Exemple #1
0
class ApplicationsSetup(object):
    """Manage applications that make up the master run workflow."""

    def __init__(
        self,
        aCmdLineParameters,
        aSysArgs,
        aConfigPath=None,
        aSingleRun=False,
        aAddOpts=False,
    ):
        """Initialize ApplicationSetup object."""
        lsf_info = self._getAppConfig("lsf")
        self._mAppsInfo = ApplicationsInfo(lsf_info)

        # Setting the config path for access in master run (so we can pass it
        # to forrest run)
        self._mAppsInfo.mConfigPath = aConfigPath

        # Getting workflow lists
        (single_run_app_opts, sequence_app_opts) = self._getWorkflow(aConfigPath)

        # Populate single run applications options
        if aSingleRun:
            for app_name, app_opts in single_run_app_opts:
                app_info = self._getAppConfig(app_name)
                self._mAppsInfo.addSingleRunApplication(app_info)
                self._addWorkflowToSysArgs(aSysArgs, app_name, app_opts)

        # Populate sequence applications options
        for app_name, app_opts in sequence_app_opts:
            app_info = self._getAppConfig(app_name)
            self._mAppsInfo.addSequenceApplication(app_info)
            if aSingleRun:  # We don't need to add these to forrest run's command line
                self._addWorkflowToSysArgs(aSysArgs, app_name, app_opts)

        # Add options from applications if necessary
        if aAddOpts:
            self._addAppOptions(aCmdLineParameters)

        self._mAppsInfo.mCmdLineOpts = CmdLineOpts(aCmdLineParameters, aSysArgs)

        # Let applications that have added parameters receive the parsed
        # result.
        if aAddOpts:
            self._resolveParameters()

    # Adds options to provided system arguments to resolve later
    #
    def _addWorkflowToSysArgs(self, aSysArgs, aAppName, aAppOpts):
        """Adds options to provided system arguments to resolve later"""
        for option, value in aAppOpts.items():
            argument = "--%s.%s" % (aAppName, option)
            if argument in aSysArgs:
                if value is not None:
                    option_index = aSysArgs.index(argument) + 1
                    new_argument = [aSysArgs[option_index]]
                    # Add new argument to existing options
                    if isinstance(value, list):
                        new_argument.extend(value)
                        aSysArgs[option_index] = new_argument
                    else:
                        new_argument.append(value)
                        aSysArgs[option_index] = new_argument
            else:  # Argument does not exist yet
                aSysArgs.append(argument)
                if value is not None:
                    aSysArgs.append(value)

    # Retrieve workflow from the provided path to add to aSysArgs to parse
    # when building mCmdLineOpts later
    #
    def _getWorkflow(self, aConfigPath):
        """Retrieve workflow from the provided path to add to aSysArgs to
        parse when building mCmdLineOpts later"""
        # Check workflow in config file first
        try:
            Msg.info("Using workflow from: %s" % aConfigPath)
            config_module = SourceFileLoader("config", aConfigPath).load_module()
            return (
                config_module.single_run_app_opts,
                config_module.sequence_app_opts,
            )
        except AttributeError:
            # aConfigPath is None or the specified config file does not
            # contain workflow
            if aConfigPath:
                Msg.info("Workflow improperly defined in %s" % aConfigPath)
            else:
                Msg.info("Config not specified.")

        # Check environment variable next
        try:
            Msg.info("Attempting to use MASTER_RUN_CONFIG environment variable.")
            config_module = SourceFileLoader(
                "config", os.environ.get("MASTER_RUN_CONFIG")
            ).load_module()
            return (
                config_module.single_run_app_opts,
                config_module.sequence_app_opts,
            )
        except AttributeError:
            if os.environ.get("MASTER_RUN_CONFIG"):
                Msg.info(
                    "Workflow improperly defined in MASTER_RUN_CONFIG: "
                    "%s" % os.environ.get("MASTER_RUN_CONFIG")
                )
            else:
                Msg.info("MASTER_RUN_CONFIG environment variable is not set.")
        except FileNotFoundError:  # MASTER_RUN_CONFIG environment variable
            # is set, but cannot be found
            Msg.err(
                "MASTER_RUN_CONFIG is currently set to %s. "
                "Please ensure that it exists." % os.environ.get("MASTER_RUN_CONFIG")
            )
            sys.exit(1)  # Assume typo so quit

        # Use default last
        try:
            default_config_file = "%s/config/%s" % (
                sys.path[0],
                Defaults.fcfg_name,
            )
            Msg.info("Using workflow from default config file: %s" % default_config_file)
            config_module = SourceFileLoader("config", default_config_file).load_module()
            return (
                config_module.single_run_app_opts,
                config_module.sequence_app_opts,
            )
        except FileNotFoundError:  # default config file cannot be found
            Msg.err("Please ensure the default config file exists.")
            sys.exit(1)  # Assume typo so quit

    # Return the ApplicationsInfo object
    #
    def getApplicationsInfo(self):
        """Return the ApplicationsInfo object"""
        return self._mAppsInfo

    # Import and create instance of ApplicationConfig for the requested
    # application
    #
    def _getAppConfig(self, aAppName):
        """Import and create instance of ApplicationConfig for the requested
        application"""
        app_module = importlib.import_module("." + aAppName, "applications")
        app_cfg = ApplicationConfig(aAppName, app_module)
        return app_cfg

    # Iterate through all ApplicationConfig objects to add application
    # specific command line parameters
    #
    def _addAppOptions(self, aCmdLineParameters):
        """Iterate through all ApplicationConfig objects to add application
        specific command line parameters"""
        for app_cfg in self._mAppsInfo.mAllAppsOrder:
            app_cmdline_opts = app_cfg.getCmdLineOptions()
            if app_cmdline_opts is not None:
                aCmdLineParameters.group_names.append(app_cmdline_opts.cGroupName)
                aCmdLineParameters.group_descriptions.append(app_cmdline_opts.cGroupDescription)
                app_options_expanded = list()
                for app_option in app_cmdline_opts.cOptions:
                    app_option.mAppName = app_cfg.mName
                    app_options_expanded.append(app_option.getExpandedList())
                aCmdLineParameters.group_parameters.append(app_options_expanded)
                aCmdLineParameters.parameters.extend(app_options_expanded)

    # Iterate through all ApplicationConfig objects to pass back parsed
    # command line options and resolve application parameters.
    #
    def _resolveParameters(self):
        """Iterate through all ApplicationConfig objects to pass back parsed
        command line options and resolve application parameters."""
        for app_cfg in self._mAppsInfo.mAllAppsOrder:
            app_parms_processor_cls = app_cfg.getParametersProcessorClass()
            if app_parms_processor_cls is not None:
                app_parms_processor = app_parms_processor_cls(self._mAppsInfo.mCmdLineOpts)
                app_cfg.mAppParameters = app_parms_processor.mAppParameters
Exemple #2
0
class ApplicationsSetup(object):
    def __init__(self,
                 aCmdLineParameters,
                 aSysArgs,
                 aConfigPath=None,
                 aSingleRun=False,
                 aAddOpts=False):
        lsf_info = self._getAppConfig('lsf')
        self._mAppsInfo = ApplicationsInfo(lsf_info)

        # Setting the config path for access in master run (so we can pass it to forrest run)
        self._mAppsInfo.mConfigPath = aConfigPath

        # Getting workflow lists
        (single_run_app_opts,
         sequence_app_opts) = self._getWorkflow(aConfigPath)

        # Populate single run applications options
        if aSingleRun:
            # TODO: The output directory hasn't been initialized when we get to this point, so we aren't able to determine if the workflow file should be written
            #  to output/regression/ or output/performance/ yet. Ideally we would set up all of master run before doing any application setup, but the current
            #  implementation is the other way around. Fixing this issue is too large an effort for something this small, but this should be revisited when 1) we
            #  decide to knock out some of the major technical debt that master run has accrued, 2) we decide on being able to include workflow in control files,
            #  or 3) we decide to start explicitly logging workflow files. For now, workflow is being passed into forrest run via the config file.
            #self._mAppsInfo.mConfigPath = self._buildTemporaryWorkflowFile(single_run_app_opts, sequence_app_opts)

            for app_name, app_opts in single_run_app_opts:
                app_info = self._getAppConfig(app_name)
                self._mAppsInfo.addSingleRunApplication(app_info)
                self._addWorkflowToSysArgs(aSysArgs, app_name, app_opts)

        # Populate sequence applications options
        for app_name, app_opts in sequence_app_opts:
            app_info = self._getAppConfig(app_name)
            self._mAppsInfo.addSequenceApplication(app_info)
            if aSingleRun:  # We don't need to add these to forrest run's command line
                self._addWorkflowToSysArgs(aSysArgs, app_name, app_opts)

        # Add options from applications if necessary
        if aAddOpts:
            self._addAppOptions(aCmdLineParameters)

        self._mAppsInfo.mCmdLineOpts = CmdLineOpts(aCmdLineParameters,
                                                   aSysArgs)

        # Let applications that have added parameters receive the parsed result.
        if aAddOpts:
            self._resolveParameters()

    ## Adds options to provided system arguments to resolve later
    #
    def _addWorkflowToSysArgs(
        self, aSysArgs, aAppName, aAppOpts
    ):  # TODO: Currently, the workflow file is arranged as a list of tuples: (application name, list of
        for option, value in aAppOpts.items(
        ):  #  dictionaries associated with the app). The way workflow parsing works right now is to split
            argument = '--%s.%s' % (
                aAppName, option
            )  #  each dictionary into a key and value and append them to existing lists of values for the
            if argument in aSysArgs:  #  associated key. It is the application's responsibility to handle the list of options for each
                if value is not None:  #  key. Since this feature was originally intended for use with the Iss application and Iss has
                    option_index = aSysArgs.index(
                        argument
                    ) + 1  #  only 1 option (path), it works perfectly. Issues arise when this functionality is adapted for
                    new_argument = [
                        aSysArgs[option_index]
                    ]  #  future applications which may have more than 1 optional option: if the first dictionary for
                    # Add new argument to existing options         #  app X contained 2 key-value pairs (a:'a', b:'b') and the second contained a single (a:'a2'),
                    if isinstance(
                            value, list
                    ):  #  the application would get 2 lists of different sizes (a:['a', 'a2'], b:['b']) and would be
                        new_argument.extend(
                            value
                        )  #  unable to determine which a-value the single b-value maps to.
                        aSysArgs[option_index] = new_argument  #
                    else:  #  There are 2 clear solutions available. The first is to change master run's behavior: how it handles
                        new_argument.append(
                            value
                        )  #  options, and loading them into their respective applications. Doing this would not require each
                        aSysArgs[
                            option_index] = new_argument  #  application having its own custom handling of multiple dictionaries, however would change some core
            else:  # Argument does not exist yet                    #  functionality of master run. The second is to modify this current workflow parser to pass entire
                aSysArgs.append(
                    argument
                )  #  dictionaries into applications instead of a list for each option. This does not change any major
                if value is not None:  #  master run functionality but requires custom handling in each application.
                    aSysArgs.append(value)

    ## Retrieve workflow from the provided path to add to aSysArgs to parse when building mCmdLineOpts later
    #
    def _getWorkflow(self, aConfigPath):
        # Check workflow in config file first
        try:
            Msg.info('Using workflow from: %s' % aConfigPath)
            config_module = SourceFileLoader('config',
                                             aConfigPath).load_module()
            return (config_module.single_run_app_opts,
                    config_module.sequence_app_opts)
        except AttributeError:  # aConfigPath is None or the specified config file does not contain workflow
            if aConfigPath:
                Msg.info('Workflow improperly defined in %s' % aConfigPath)
            else:
                Msg.info('Config not specified.')

        # Check environment variable next
        try:
            Msg.info(
                'Attempting to use MASTER_RUN_CONFIG environment variable.')
            config_module = SourceFileLoader(
                'config', os.environ.get('MASTER_RUN_CONFIG')).load_module()
            return (config_module.single_run_app_opts,
                    config_module.sequence_app_opts)
        except AttributeError:
            if os.environ.get('MASTER_RUN_CONFIG'):
                Msg.info(
                    'Workflow improperly defined in MASTER_RUN_CONFIG: %s' %
                    os.environ.get('MASTER_RUN_CONFIG'))
            else:
                Msg.info('MASTER_RUN_CONFIG environment variable is not set.')
        except FileNotFoundError:  # MASTER_RUN_CONFIG environment variable is set, but cannot be found
            Msg.err(
                'MASTER_RUN_CONFIG is currently set to %s. Please ensure that it exists.'
                % os.environ.get('MASTER_RUN_CONFIG'))
            sys.exit(1)  # Assume typo so quit

        # Use default last
        try:
            default_config_file = '%s/config/%s' % (sys.path[0],
                                                    Defaults.fcfg_name)
            Msg.info('Using workflow from default config file: %s' %
                     default_config_file)
            config_module = SourceFileLoader(
                'config', default_config_file).load_module()
            return (config_module.single_run_app_opts,
                    config_module.sequence_app_opts)
        except FileNotFoundError:  # default config file cannot be found
            Msg.err('Please ensure the default config file exists.')
            sys.exit(1)  # Assume typo so quit

    ## Return the ApplicationsInfo object
    #
    def getApplicationsInfo(self):
        return self._mAppsInfo

    ## Import and create instance of ApplicationConfig for the requested application
    #
    def _getAppConfig(self, aAppName):
        app_module = importlib.import_module('.' + aAppName, 'applications')
        app_cfg = ApplicationConfig(aAppName, app_module)
        return app_cfg

    ## Iterate through all ApplicationConfig objects to add application specific command line parameters
    #
    def _addAppOptions(self, aCmdLineParameters):
        for app_cfg in self._mAppsInfo.mAllAppsOrder:
            app_cmdline_opts = app_cfg.getCmdLineOptions()
            if app_cmdline_opts is not None:
                aCmdLineParameters.group_names.append(
                    app_cmdline_opts.cGroupName)
                aCmdLineParameters.group_descriptions.append(
                    app_cmdline_opts.cGroupDescription)
                app_options_expanded = list()
                for app_option in app_cmdline_opts.cOptions:
                    app_option.mAppName = app_cfg._mName
                    app_options_expanded.append(app_option.getExpandedList())
                aCmdLineParameters.group_parameters.append(
                    app_options_expanded)
                aCmdLineParameters.parameters.extend(app_options_expanded)

    ## Iterate through all ApplicationConfig objects to pass back parsed command line options and resolve application parameters.
    #
    def _resolveParameters(self):
        for app_cfg in self._mAppsInfo.mAllAppsOrder:
            app_parms_processor_cls = app_cfg.getParametersProcessorClass()
            if app_parms_processor_cls is not None:
                app_parms_processor = app_parms_processor_cls(
                    self._mAppsInfo.mCmdLineOpts)
                app_cfg.mAppParameters = app_parms_processor.mAppParameters