Providers/nxOMSAutomationWorker/automationworker/worker/runtime.py (105 lines of code) (raw):

#!/usr/bin/env python2 # # Copyright (C) Microsoft Corporation, All rights reserved. """Runtime module. Contains runtime base class and language specific runtime classes.""" import signal import subprocess import sys import time from distutils.spawn import find_executable import serializerfactory import subprocessfactory import tracer from runbook import * from workerexception import * json = serializerfactory.get_serializer(sys.version_info) class Runtime: """Runtime base class.""" def __init__(self, job_data, runbook): """ :type job_data : jrdsclient.JobData" :type runbook : Runbook """ # should be overwritten by language runtime self.execution_alias = None self.base_cmd = None # used for actual runtime self.runbook = runbook self.runbook_subprocess = None self.job_data = job_data def initialize(self): self.runbook.write_to_disk() def start_runbook_subprocess(self): """Creates the runbook subprocess based on the script language and using properties set by the derived class. Requires self.base_cmd & self.runbook_file_path to be set by derived class. """ cmd = self.base_cmd + [self.runbook.runbook_file_path] job_parameters = self.job_data.parameters if job_parameters is not None and len(job_parameters) > 0: for parameter in job_parameters: tracer.log_debug_trace("Parameter is: \n" + str(parameter)) if (self.runbook.definition_kind_str == "PowerShell" or self.runbook.definition_kind_str == "PowerShell7") and parameter["Name"]: # Handle named parameters for PowerShell arriving out of order cmd += ["-%s" % parameter["Name"]] try: cmd += [str(json.loads(parameter["Value"]))] except: cmd += [str(parameter["Value"])] # Do not copy current process env var to the sandbox process env = os.environ.copy() env.update({"AUTOMATION_JOB_ID": str(self.job_data.job_id), "AUTOMATION_ACTIVITY_ID": str(tracer.u_activity_id), "PYTHONPATH": str(configuration.get_source_directory_path()), "HOME": str(os.getcwd())}) # windows env have to be str (not unicode) self.runbook_subprocess = subprocessfactory.create_subprocess(cmd=cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def kill_runbook_subprocess(self): """Attempts to kill the runbook subprocess. This method will attempt to kill the runbook subprocess [max_attempt_count] and will return if successful. Throws: SandboxRuntimeException : If runbook subprocess is still alive after [max_attempt_count]. """ attempt_count = 0 max_attempt_count = 3 while attempt_count < max_attempt_count: if self.runbook_subprocess is not None and self.runbook_subprocess.poll() is None: os.kill(self.runbook_subprocess.pid, signal.SIGTERM) runbook_proc_is_alive = self.is_process_alive(self.runbook_subprocess) if runbook_proc_is_alive is False: return attempt_count += 1 time.sleep(attempt_count) else: return raise SandboxRuntimeException() @staticmethod def is_process_alive(process): """Checks if the given process is still alive. Returns: boolean : True if the process [pid] is alive, False otherwise. """ if process.poll() is None: return True else: return False def is_runtime_supported(self): """Validates that the OS supports the language runtime by testing the executable file path. Returns: True : If executable exist. False : Otherwise. """ if find_executable(self.execution_alias) is None: return False else: return True class PowerShellRuntime(Runtime): """PowerShell runtime derived class.""" def __init__(self, job_data, runbook): Runtime.__init__(self, job_data, runbook) self.execution_alias = "pwsh" if linuxutil.is_posix_host() is False: self.execution_alias = "powershell" self.base_cmd = [self.execution_alias, "-command"] class Python2Runtime(Runtime): """Python 2 runtime derived class.""" def __init__(self, job_data, runbook): Runtime.__init__(self, job_data, runbook) self.execution_alias = "python2" if get_default_python_interpreter_major_version() == 2: self.execution_alias = "python" self.base_cmd = [self.execution_alias] class Python3Runtime(Runtime): """Python 3 runtime derived class.""" def __init__(self, job_data, runbook): Runtime.__init__(self, job_data, runbook) self.execution_alias = "python3" if get_default_python_interpreter_major_version() == 3: self.execution_alias = "python" self.base_cmd = [self.execution_alias] class BashRuntime(Runtime): """Bash runtime derived class.""" def __init__(self, job_data, runbook): Runtime.__init__(self, job_data, runbook) self.execution_alias = "bash" self.base_cmd = [self.execution_alias] class PowerShell7Runtime(Runtime): """PowerShell7 runtime derived class.""" def __init__(self, job_data, runbook): Runtime.__init__(self, job_data, runbook) self.execution_alias = "pwsh" self.base_cmd = [self.execution_alias, "-command"] def get_default_python_interpreter_major_version(): """Return the default "python" alias interpreter version. Returns: int, the interpreter major version None, if the default interpreter version cannot be detected """ cmd = ["python", "-c", "import sys;print(sys.version[0])"] # need to use print() for python3 compatibility p = subprocessfactory.create_subprocess(cmd=cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) default_interpreter_version, error = p.communicate() if p.returncode == 0: return int(default_interpreter_version.strip()) else: return None