@rbac('owner')
    def execute_command(self, resource_desc):
        """
        Submit command based on `resource_desc`.

        resource_desc: dict
            Description of command and required resources.

        The '-V' `qsub` option is always used to export the current environment
        to the job. This environment is first updated with any 'job_environment'
        data. The '-sync yes' `qsub` option is used to wait for job completion.

        Other job resource keys are processed as follows:

        ========================= =========================
        Resource Key              Translation
        ========================= =========================
        ``submit_as_hold``        -h
        ------------------------- -------------------------
        ``rerunnable``            -r yes|no
        ------------------------- -------------------------
        ``working_directory``     Ignored
        ------------------------- -------------------------
        ``job_category``          Sets parallel environment
        ------------------------- -------------------------
        ``min_cpus``              Sets parallel environment
        ------------------------- -------------------------
        ``max_cpus``              Sets parallel environment
        ------------------------- -------------------------
        ``min_phys_memory``       Ignored
        ------------------------- -------------------------
        ``email``                 -M `value`
        ------------------------- -------------------------
        ``email_on_started``      -m b
        ------------------------- -------------------------
        ``email_on_terminated``   -m e
        ------------------------- -------------------------
        ``job_name``              -N `value`
        ------------------------- -------------------------
        ``input_path``            -i `value`
        ------------------------- -------------------------
        ``output_path``           -o `value`
        ------------------------- -------------------------
        ``error_path``            -e `value`
        ------------------------- -------------------------
        ``join_files``            -j yes|no
        ------------------------- -------------------------
        ``reservation_id``        -ar `value`
        ------------------------- -------------------------
        ``queue_name``            -q `value`
        ------------------------- -------------------------
        ``priority``              -p `value`
        ------------------------- -------------------------
        ``start_time``            -a `value`
        ------------------------- -------------------------
        ``deadline_time``         Ignored
        ------------------------- -------------------------
        ``accounting_id``         -A `value`
        ========================= =========================

        Where `value` is the corresponding resource value.

        The 'working_directory' key is ignored since the server has been
        started in this directory.  ``-cwd`` is used in the `qsub` command.

        If 'input_path' is not specified, add ``-i /dev/null``.
        If 'output_path' is not specified, add ``-o <remote_command>.stdout``.
        If 'error_path' is not specified, add ``-j yes``.

        If 'native_specification' is specified, it is added to the `qsub`
        command just before 'remote_command' and 'args'.

        If specified, 'job_category' is used to index into the category
        map set up during allocator configuration.  The mapped category
        name as well as the 'min_cpus' and 'max_cpus' values are used
        with the ``-pe`` qsub option.

        Some resource limits are also handled:

        ==================== =========================
        Resource Key         Translation
        ==================== =========================
        ``core_file_size``   Ignored
        -------------------- -------------------------
        ``data_seg_size``    Ignored
        -------------------- -------------------------
        ``file_size``        Ignored
        -------------------- -------------------------
        ``open_files``       Ignored
        -------------------- -------------------------
        ``stack_size``       Ignored
        -------------------- -------------------------
        ``virtual_memory``   Ignored
        -------------------- -------------------------
        ``cpu_time``         -l h_cpu= `value`
        -------------------- -------------------------
        ``wallclock_time``   -l h_rt= `value`
        ==================== =========================

        Output from `qsub` itself is routed to ``qsub.out``.
        """
        self.home_dir = os.path.expanduser('~')
        self.work_dir = os.getcwd()  # Server started in working directory.

        cmd = list(self._QSUB)
        cmd.extend(('-V', '-sync', 'yes', '-b', 'yes', '-cwd'))
        env = None
        inp, out, err = None, None, None

        # Process description in fixed, repeatable order.
        keys = ('submit_as_hold',
                'rerunnable',
                'job_environment',
                'email',
                'email_on_started',
                'email_on_terminated',
                'job_name',
                'input_path',
                'output_path',
                'error_path',
                'join_files',
                'reservation_id',
                'queue_name',
                'priority',
                'start_time',
                'accounting_id')

        email_events = ''
        for key in keys:
            try:
                value = resource_desc[key]
            except KeyError:
                continue

            if key == 'submit_as_hold':
                if value:
                    cmd.append('-h')
            elif key == 'rerunnable':
                cmd.extend(('-r', 'yes' if value else 'no'))
            elif key == 'job_environment':
                env = value
            elif key == 'email':
                cmd.extend(('-M', ','.join(value)))
            elif key == 'email_on_started':
                email_events += 'b'
            elif key == 'email_on_terminated':
                email_events += 'e'
            elif key == 'job_name':
                if value:
                    cmd.extend(('-N', self._jobname(value)))
            elif key == 'input_path':
                cmd.extend(('-i', self._fix_path(value)))
                inp = value
            elif key == 'output_path':
                cmd.extend(('-o', self._fix_path(value)))
                out = value
            elif key == 'error_path':
                cmd.extend(('-e', self._fix_path(value)))
                err = value
            elif key == 'join_files':
                cmd.extend(('-j', 'yes' if value else 'no'))
                if value:
                    err = 'yes'
            elif key == 'reservation_id':
                cmd.extend(('-ar', value))
            elif key == 'queue_name':
                cmd.extend(('-q', value))
            elif key == 'priority':
                cmd.extend(('-p', str(value)))
            elif key == 'start_time':
                cmd.extend(('-a', value.strftime('%Y%m%d%H%M.%S')))
            elif key == 'accounting_id':
                cmd.extend(('-A', value))

        if email_events:
            cmd.extend(('-m', email_events))

        # Setup parallel environment.
        if 'job_category' in resource_desc:
            job_category = resource_desc['job_category']
            try:
                parallel_environment = self.category_map[job_category]
            except __HOLE__:
                msg = 'No mapping for job_category %r' % job_category
                self._logger.error(msg)
                raise ValueError(msg)
            min_cpus = resource_desc.get('min_cpus', 1)
            max_cpus = resource_desc.get('max_cpus', min_cpus)
            cmd.extend(('-pe', parallel_environment,
                        '%d-%d' % (min_cpus, max_cpus)))

        # Set resource limits.
        if 'resource_limits' in resource_desc:
            limits = resource_desc['resource_limits']
            if 'cpu_time' in limits:
                cpu_time = limits['cpu_time']
                cmd.extend(('-l', 'h_cpu=%s' % self._timelimit(cpu_time)))
            if 'wallclock_time' in limits:
                wall_time = limits['wallclock_time']
                cmd.extend(('-l', 'h_rt=%s' % self._timelimit(wall_time)))

        # Set default command configuration.
        if inp is None:
            cmd.extend(('-i', DEV_NULL))
        if out is None:
            base = os.path.basename(resource_desc['remote_command'])
            cmd.extend(('-o', '%s.stdout' % base))
        if err is None:
            cmd.extend(('-j', 'yes'))

        # Add 'escape' clause.
        if 'native_specification' in resource_desc:
            cmd.extend(resource_desc['native_specification'])

        cmd.append(self._fix_path(resource_desc['remote_command']))

        if 'args' in resource_desc:
            for arg in resource_desc['args']:
                cmd.append(self._fix_path(arg))

        self._logger.info('%r', ' '.join(cmd))
        try:
            process = ShellProc(cmd, DEV_NULL, 'qsub.out', STDOUT, env)
        except Exception as exc:
            self._logger.error('exception creating process: %s', exc)
            raise

        self._logger.debug('    PID = %d', process.pid)
        return_code, error_msg = process.wait(1)
        self._logger.debug('    returning %s', (return_code, error_msg))
        return (return_code, error_msg)
dataset/ETHPy150Open OpenMDAO/OpenMDAO-Framework/openmdao.main/src/openmdao/main/grid_engine.py/GridEngineServer.execute_command