def run(self):
        '''
        Launch the Ansible task configured in self.config (A RunnerConfig object), returns once the
        invocation is complete
        '''
        self.status_callback('starting')
        stdout_filename = os.path.join(self.config.artifact_dir, 'stdout')
        command_filename = os.path.join(self.config.artifact_dir, 'command')

        try:
            os.makedirs(self.config.artifact_dir, mode=0o700)
        except OSError as exc:
            if exc.errno == errno.EEXIST and os.path.isdir(self.config.artifact_dir):
                pass
            else:
                raise
        os.close(os.open(stdout_filename, os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR))

        command = [a.decode('utf-8') if six.PY2 else a for a in self.config.command]
        with codecs.open(command_filename, 'w', encoding='utf-8') as f:
            os.chmod(command_filename, stat.S_IRUSR | stat.S_IWUSR)
            json.dump(
                {'command': command,
                 'cwd': self.config.cwd,
                 'env': self.config.env}, f, ensure_ascii=False
            )

        if self.config.ident is not None:
            cleanup_artifact_dir(os.path.join(self.config.artifact_dir, ".."), self.config.rotate_artifacts)

        stdout_handle = codecs.open(stdout_filename, 'w', encoding='utf-8')
        stdout_handle = OutputEventFilter(stdout_handle, self.event_callback, self.config.suppress_ansible_output, output_json=self.config.json_mode)

        if not isinstance(self.config.expect_passwords, collections.OrderedDict):
            # We iterate over `expect_passwords.keys()` and
            # `expect_passwords.values()` separately to map matched inputs to
            # patterns and choose the proper string to send to the subprocess;
            # enforce usage of an OrderedDict so that the ordering of elements in
            # `keys()` matches `values()`.
            expect_passwords = collections.OrderedDict(self.config.expect_passwords)
        password_patterns = list(expect_passwords.keys())
        password_values = list(expect_passwords.values())

        # pexpect needs all env vars to be utf-8 encoded bytes
        # https://github.com/pexpect/pexpect/issues/512

        # Use a copy so as not to cause problems when serializing the job_env.
        env = {
            ensure_str(k): ensure_str(v) if k != 'PATH' and isinstance(v, six.text_type) else v
            for k, v in self.config.env.items()
        }

        self.status_callback('running')
        self.last_stdout_update = time.time()
        try:
            child = pexpect.spawn(
                command[0],
                command[1:],
                cwd=self.config.cwd,
                env=env,
                ignore_sighup=True,
                encoding='utf-8',
                echo=False,
                use_poll=self.config.pexpect_use_poll,
            )
            child.logfile_read = stdout_handle
        except pexpect.exceptions.ExceptionPexpect as e:
            child = collections.namedtuple(
                'MissingProcess', 'exitstatus isalive'
            )(
                exitstatus=127,
                isalive=lambda: False
            )

            def _decode(x):
                return x.decode('utf-8') if six.PY2 else x

            # create the events directory (the callback plugin won't run, so it
            # won't get created)
            events_directory = os.path.join(self.config.artifact_dir, 'job_events')
            if not os.path.exists(events_directory):
                os.mkdir(events_directory, 0o700)
            stdout_handle.write(_decode(str(e)))
            stdout_handle.write(_decode('\n'))

        job_start = time.time()
        while child.isalive():
            result_id = child.expect(password_patterns,
                                     timeout=self.config.pexpect_timeout,
                                     searchwindowsize=100)
            password = password_values[result_id]
            if password is not None:
                child.sendline(password)
                self.last_stdout_update = time.time()
            if self.cancel_callback:
                try:
                    self.canceled = self.cancel_callback()
                except Exception as e:
                    # TODO: logger.exception('Could not check cancel callback - cancelling immediately')
                    #if isinstance(extra_update_fields, dict):
                    #    extra_update_fields['job_explanation'] = "System error during job execution, check system logs"
                    raise CallbackError("Exception in Cancel Callback: {}".format(e))
            if self.config.job_timeout and not self.canceled and (time.time() - job_start) > self.config.job_timeout:
                self.timed_out = True
                # if isinstance(extra_update_fields, dict):
                #     extra_update_fields['job_explanation'] = "Job terminated due to timeout"
            if self.canceled or self.timed_out or self.errored:
                Runner.handle_termination(child.pid, is_cancel=self.canceled)
            if self.config.idle_timeout and (time.time() - self.last_stdout_update) > self.config.idle_timeout:
                Runner.handle_termination(child.pid, is_cancel=False)
                self.timed_out = True

        stdout_handle.flush()
        stdout_handle.close()

        if self.canceled:
            self.status_callback('canceled')
        elif child.exitstatus == 0 and not self.timed_out:
            self.status_callback('successful')
        elif self.timed_out:
            self.status_callback('timeout')
        else:
            self.status_callback('failed')
        self.rc = child.exitstatus if not (self.timed_out or self.canceled) else 254
        for filename, data in [
            ('status', self.status),
            ('rc', self.rc),
        ]:
            artifact_path = os.path.join(self.config.artifact_dir, filename)
            if not os.path.exists(artifact_path):
                os.close(os.open(artifact_path, os.O_CREAT, stat.S_IRUSR | stat.S_IWUSR))
            with open(artifact_path, 'w') as f:
                f.write(str(data))
        if self.config.directory_isolation_path and self.config.directory_isolation_cleanup:
            shutil.rmtree(self.config.directory_isolation_path)
        if self.finished_callback is not None:
            try:
                self.finished_callback(self)
            except Exception as e:
                raise CallbackError("Exception in Finished Callback: {}".format(e))
        return self.status, self.rc
def run ( self ) : self . status_callback ( 'starting' ) stdout_filename = os . path . join ( self . config . artifact_dir , 'stdout' ) command_filename = os . path . join ( self . config . artifact_dir , 'command' ) try : os . makedirs ( self . config . artifact_dir , mode = 0o700 ) except OSError as exc : if exc . errno == errno . EEXIST and os . path . isdir ( self . config . artifact_dir ) : pass else : raise os . close ( os . open ( stdout_filename , os . O_CREAT , stat . S_IRUSR | stat . S_IWUSR ) ) command = [ a . decode ( 'utf-8' ) if six . PY2 else a for a in self . config . command ] with codecs . open ( command_filename , 'w' , encoding = 'utf-8' ) as f : os . chmod ( command_filename , stat . S_IRUSR | stat . S_IWUSR ) json . dump ( { 'command' : command , 'cwd' : self . config . cwd , 'env' : self . config . env } , f , ensure_ascii = False ) if self . config . ident is not None : cleanup_artifact_dir ( os . path . join ( self . config . artifact_dir , ".." ) , self . config . rotate_artifacts ) stdout_handle = codecs . open ( stdout_filename , 'w' , encoding = 'utf-8' ) stdout_handle = OutputEventFilter ( stdout_handle , self . event_callback , self . config . suppress_ansible_output , output_json = self . config . json_mode ) if not isinstance ( self . config . expect_passwords , collections . OrderedDict ) : expect_passwords = collections . OrderedDict ( self . config . expect_passwords ) password_patterns = list ( expect_passwords . keys ( ) ) password_values = list ( expect_passwords . values ( ) ) env = { ensure_str ( k ) : ensure_str ( v ) if k != 'PATH' and isinstance ( v , six . text_type ) else v for k , v in self . config . env . items ( ) } self . status_callback ( 'running' ) self . last_stdout_update = time . time ( ) try : child = pexpect . spawn ( command [ 0 ] , command [ 1 : ] , cwd = self . config . cwd , env = env , ignore_sighup = True , encoding = 'utf-8' , echo = False , use_poll = self . config . pexpect_use_poll , ) child . logfile_read = stdout_handle except pexpect . exceptions . ExceptionPexpect as e : child = collections . namedtuple ( 'MissingProcess' , 'exitstatus isalive' ) ( exitstatus = 127 , isalive = lambda : False ) def _decode ( x ) : return x . decode ( 'utf-8' ) if six . PY2 else x events_directory = os . path . join ( self . config . artifact_dir , 'job_events' ) if not os . path . exists ( events_directory ) : os . mkdir ( events_directory , 0o700 ) stdout_handle . write ( _decode ( str ( e ) ) ) stdout_handle . write ( _decode ( '\n' ) ) job_start = time . time ( ) while child . isalive ( ) : result_id = child . expect ( password_patterns , timeout = self . config . pexpect_timeout , searchwindowsize = 100 ) password = password_values [ result_id ] if password is not None : child . sendline ( password ) self . last_stdout_update = time . time ( ) if self . cancel_callback : try : self . canceled = self . cancel_callback ( ) except Exception as e : raise CallbackError ( "Exception in Cancel Callback: {}" . format ( e ) ) if self . config . job_timeout and not self . canceled and ( time . time ( ) - job_start ) > self . config . job_timeout : self . timed_out = True if self . canceled or self . timed_out or self . errored : Runner . handle_termination ( child . pid , is_cancel = self . canceled ) if self . config . idle_timeout and ( time . time ( ) - self . last_stdout_update ) > self . config . idle_timeout : Runner . handle_termination ( child . pid , is_cancel = False ) self . timed_out = True stdout_handle . flush ( ) stdout_handle . close ( ) if self . canceled : self . status_callback ( 'canceled' ) elif child . exitstatus == 0 and not self . timed_out : self . status_callback ( 'successful' ) elif self . timed_out : self . status_callback ( 'timeout' ) else : self . status_callback ( 'failed' ) self . rc = child . exitstatus if not ( self . timed_out or self . canceled ) else 254 for filename , data in [ ( 'status' , self . status ) , ( 'rc' , self . rc ) , ] : artifact_path = os . path . join ( self . config . artifact_dir , filename ) if not os . path . exists ( artifact_path ) : os . close ( os . open ( artifact_path , os . O_CREAT , stat . S_IRUSR | stat . S_IWUSR ) ) with open ( artifact_path , 'w' ) as f : f . write ( str ( data ) ) if self . config . directory_isolation_path and self . config . directory_isolation_cleanup : shutil . rmtree ( self . config . directory_isolation_path ) if self . finished_callback is not None : try : self . finished_callback ( self ) except Exception as e : raise CallbackError ( "Exception in Finished Callback: {}" . format ( e ) ) return self . status , self . rc
Launch the Ansible task configured in self.config (A RunnerConfig object), returns once the
        invocation is complete