class WrapperContextImpl(ArgRegistrar): ''' WrapperContextImpl implements the WrapperContext interface.''' def __init__(self, wrappername, wrapperinstance, trialargs, testargs, config, testdir): self._trialargs = trialargs self._testargs = testargs self._config = config self._testdir = testdir self._platform = Platform() self._wrappername = wrappername self._sudo = False self._default_pidfilename = '%s/etce.%s.%s.pid' \ % (os.path.join(self._config.get('etce', 'WORK_DIRECTORY'), 'lock'), self.platform.hostname(), self._wrappername) self._description = wrapperinstance.__doc__ # start with reserved args set here ... self._args = { 'default_pidfilename': self._default_pidfilename, 'nodename': self._testdir.nodename(), 'nodeid': self._testdir.nodeid(), 'testname': self._testdir.name(), 'wrappername': self._wrappername, 'infile': None, 'outfile': None } # ... and the ones passed in self._args.update(trialargs) # these are the reserved args that cannot be overwritten self._reserved_args = set(self._args) # fill in the arguments registered by the wrapper wrapperinstance.register(self) storefile = os.path.join(self._trialargs['logdirectory'], 'etce.store') self._wrapperstore = WrapperStore(storefile) self._wrapperstore.update( {'etce': { 'starttime': self._trialargs['starttime'] }}, self._args['nodename']) def register_argument(self, argname, defaultval, description): if argname in self._reserved_args: raise ValueError('Wrapper "%s" attempting to register a ' \ 'reserved argument "%s". Quitting.' % \ (self._args['wrappername'], argname)) if self._testdir.hasconfig(self._wrappername, argname): self._args[argname] = \ self._testdir.getconfig(self._wrappername, argname, defaultval) elif argname in self._testargs: self._args[argname] = self._testargs[argname] else: self._args[argname] = defaultval def register_infile_name(self, name): self._args['infile'] = self._testdir.getfile(name) def register_outfile_name(self, name): self._args['outfile'] = os.path.join(self._trialargs['logdirectory'], name) def run_with_sudo(self): # ignore run with sudo requests when configured to do so if self._config.get('etce', 'IGNORE_RUN_WITH_SUDO').lower() == 'yes': return self._sudo = True def store(self, namevaldict): self._wrapperstore.update({self._args['wrappername']: namevaldict}, self._args['nodename']) @property def platform(self): return self._platform @property def args(self): return ArgProxy(self._args) def daemonize(self, command, argstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0, starttime=None, extra_paths=[]): commandstr = self._build_commandstr(command, argstr, extra_paths) # print the commandstr and return on a dryrun if self._trialargs['dryrun']: print(commandstr) sys.stdout.flush() return # 1. call self.stop(pidfilename) self.stop(pidfilename) # run the command pid, subprocess = etce.utils.daemonize_command(commandstr, stdout, stderr, starttime) # return on parent if pid > 0: return # 2. if genpidfile is True, and pidfilename is None, # generate the pidfilename if genpidfile and pidfilename is None: pidfilename = self._default_pidfilename # 3. write the pid to pidfilename if genpidfile: with open(pidfilename, 'w') as pidfile: pidfile.write(str(subprocess.pid + pidincrement)) # 4. wait on subprocess subprocess.wait() # 5. exit, do not return, because returning # will cause any subsequent wrappers in this # step to be rerun sys.exit(0) def run(self, command, argstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0, extra_paths=[]): commandstr = self._build_commandstr(command, argstr, extra_paths) # print the commandstr and return on a dryrun if self._trialargs['dryrun']: print(commandstr) sys.stdout.flush() return self.stop(pidfilename) print(commandstr) sys.stdout.flush() stdoutfd = None stderrfd = None if not stdout is None: stdoutfd = open(stdout, 'w') if not stderr is None: if stdout == stderr: stderrfd = stdoutfd else: stderrfd = open(stderr, 'w') # generate the pidfilename if genpidfile and pidfilename is None: pidfilename = self._default_pidfilename # create the Popen subprocess sp = subprocess.Popen(shlex.split(commandstr), stdout=stdoutfd, stderr=stderrfd) # write the pid to pidfilename if genpidfile: with open(pidfilename, 'w') as pidfile: pidfile.write(str(sp.pid + pidincrement)) # wait on subprocess sp.wait() def stop(self, pidfilename=None, signal=signal.SIGQUIT, sudo=True): # use default pidfilename if None specified if pidfilename is None: pidfilename = self._default_pidfilename # if found a pid, kill the process and remove the file self._platform.kill(pidfilename, signal, sudo) def _build_commandstr(self, command, argstr, extra_paths): all_paths = os.environ['PATH'].split(':') + list(extra_paths) existing_paths = filter(os.path.isdir, all_paths) found_paths = [] for existing_path in existing_paths: if command in os.listdir(existing_path): found_paths.append(existing_path) if not found_paths: raise WrapperError('Cannot find command "%s" in system paths {%s}. Quitting.' \ % (command, ','.join(all_paths))) commandstr = ' '.join([os.path.join(found_paths[0], command), argstr]) # run with sudo if wrapper requested it if self._sudo: commandstr = 'sudo ' + commandstr return commandstr
class WrapperContextImpl(ArgRegistrar): ''' WrapperContextImpl implements the WrapperContext interface.''' def __init__(self, wrappername, wrapperinstance, trialargs, testargs, config, testdir): self._trialargs = trialargs self._testargs = testargs self._config = config self._testdir = testdir self._platform = Platform() self._wrappername = wrappername self._default_pidfilename = '%s/etce.%s.%s.pid' \ % (os.path.join(self._config.get('etce', 'WORK_DIRECTORY'), 'lock'), self.platform.hostname(), self._wrappername) self._description = wrapperinstance.__doc__ # start with reserved args set here ... self._args = { 'default_pidfilename': self._default_pidfilename, 'nodename': self._testdir.nodename(), 'nodeid': self._testdir.nodeid(), 'testname': self._testdir.name(), 'wrappername': self._wrappername, 'infile': None, 'outfile': None } # ... and the ones passed in self._args.update(trialargs) # these are the reserved args that cannot be overwritten self._reserved_args = set(self._args) # fill in the arguments registered by the wrapper wrapperinstance.register(self) storefile = os.path.join(self._trialargs['logdirectory'], 'etce.store') self._wrapperstore = WrapperStore(storefile) self._wrapperstore.update( {'etce': { 'starttime': self._trialargs['starttime'] }}, self._args['nodename']) def register_argument(self, argname, defaultval, description): if argname in self._reserved_args: raise ValueError('Wrapper "%s" attempting to register a ' \ 'reserved argument "%s". Quitting.' % \ (self._args['wrappername'], argname)) if self._testdir.hasconfig(self._wrappername, argname): self._args[argname] = \ self._testdir.getconfig(self._wrappername, argname, defaultval) elif argname in self._testargs: self._args[argname] = self._testargs[argname] else: self._args[argname] = defaultval def register_infile_name(self, name): self._args['infile'] = self._testdir.getfile(name) def register_outfile_name(self, name): self._args['outfile'] = os.path.join(self._trialargs['logdirectory'], name) def store(self, namevaldict): self._wrapperstore.update({self._args['wrappername']: namevaldict}, self._args['nodename']) @property def platform(self): return self._platform @property def args(self): return ArgProxy(self._args) def daemonize(self, commandstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0, starttime=None): # print the commandstr and return on a dryrun if self._trialargs['dryrun']: print commandstr return # 1. call self.stop(pidfilename) self.stop(pidfilename) # run the command pid,subprocess = \ etce.utils.daemonize_command(commandstr, stdout, stderr, starttime) # return on parent if pid > 0: return # 2. if genpidfile is True, and pidfilename is None, # generate the pidfilename if genpidfile and pidfilename is None: pidfilename = self._default_pidfilename # 3. write the pid to pidfilename if genpidfile: with open(pidfilename, 'w') as pidfile: pidfile.write(str(subprocess.pid + pidincrement)) # 4. wait on subprocess subprocess.wait() # 5. exit, do not return, because returning # will cause any subsequent wrappers in this # step to be rerun sys.exit(0) def run(self, commandstr, stdout=None, stderr=None, pidfilename=None, genpidfile=True, pidincrement=0): # print the commandstr and return on a dryrun if self._trialargs['dryrun']: print commandstr return self.stop(pidfilename) print commandstr command = shlex.split(commandstr) stdoutfd = None stderrfd = None if not stdout is None: stdoutfd = open(stdout, 'w') if not stderr is None: if stdout == stderr: stderrfd = stdoutfd else: stderrfd = open(stderr, 'w') # generate the pidfilename if genpidfile and pidfilename is None: pidfilename = self._default_pidfilename # create the Popen subprocess sp = subprocess.Popen(command, stdout=stdoutfd, stderr=stderrfd) # write the pid to pidfilename if genpidfile: with open(pidfilename, 'w') as pidfile: pidfile.write(str(sp.pid + pidincrement)) # wait on subprocess sp.wait() def stop(self, pidfilename=None, signal=signal.SIGQUIT, decorator=''): # use default pidfilename if None specified if pidfilename is None: pidfilename = self._default_pidfilename pid = self._platform.readpid(pidfilename) # if found a pid, kill the process and remove the file if pid: try: print 'killing pid %d found in %s' % (pid, pidfilename) command = '%s kill -%d %d' % (pid, signal) sp = subprocess.Popen(command) sp.wait() #os.kill(pid, signal.SIGQUIT) except OSError as e: # orphaned pidfile - process already dead pass finally: os.remove(pidfilename)