def __init__(self): self.conf = Conf() self.job = Job() self.pids = set() # bg processes self.tmpfiles = set() self.user_logfiles = LogFileSet() self.password = gen_password() if 'SSH_CONNECTION' in os.environ: self.srcip = os.environ['SSH_CONNECTION'].split(' ')[0] else: logger.warning("unable to extract source IP from SSH_CONNECTION, " "fallback to 127.0.0.1") self.srcip = '127.0.0.1' try: self.rinip = socket.gethostbyname(self.conf.wan_prefix + socket.gethostname()) except socket.gaierror: logger.error("failed to get wan hostname, fallback to localhost") self.rinip = '127.0.0.1' self.opts = ScenarioOpts() self.declare_opts() if self.conf.log: self.ensure_dir(self.opts.logfile) logger.debug("opening logfile %s", self.opts.logfile) self.logfile = open(self.opts.logfile, 'w+') else: self.logfile = None
class Scenario(object): OPTS = ['logfile:str:${BASEDIR}/Xlog_${JOBID}'] def __init__(self): self.conf = Conf() self.job = Job() self.pids = set() # bg processes self.tmpfiles = set() self.user_logfiles = LogFileSet() self.password = gen_password() if 'SSH_CONNECTION' in os.environ: self.srcip = os.environ['SSH_CONNECTION'].split(' ')[0] else: logger.warning("unable to extract source IP from SSH_CONNECTION, " "fallback to 127.0.0.1") self.srcip = '127.0.0.1' try: self.rinip = socket.gethostbyname(self.conf.wan_prefix + socket.gethostname()) except socket.gaierror: logger.error("failed to get wan hostname, fallback to localhost") self.rinip = '127.0.0.1' self.opts = ScenarioOpts() self.declare_opts() if self.conf.log: self.ensure_dir(self.opts.logfile) logger.debug("opening logfile %s", self.opts.logfile) self.logfile = open(self.opts.logfile, 'w+') else: self.logfile = None def declare_opts(self): logger.debug("scenario %s: declaring opts", self.NAME) self.declare_opts_cls(type(self)) def declare_opts_cls(self, cls): if cls == object: return if hasattr(cls, 'OPTS'): for opt_s in cls.OPTS: logger.debug("class %s: opt %s", cls.__name__, opt_s) self.opts.add(opt_s) for xcls in cls.__bases__: self.declare_opts_cls(xcls) def set_opts(self): """Set scenario opts with values given by user in parameters.""" if self.conf.opts is not None: for opt_s in self.conf.opts: self.opts.set(opt_s) def ensure_dir(self, filename): """Ensure the parent directory of the filename in parameter exists so that the file can be created witout problem then.""" dirname = os.path.dirname(os.path.abspath( os.path.expanduser(filename))) if not os.path.isdir(dirname): logger.debug("creating directory %s", dirname) os.makedirs(dirname) def create_file(self, filename): """Create empty file if not existing""" # first ensure directory exists self.ensure_dir(filename) absfile = os.path.abspath(os.path.expanduser(filename)) if not os.path.exists(absfile): logger.debug("creating empty file %s", absfile) fh = open(absfile, 'w+') fh.close() def sleep(self, time): """Sleep if not in dry-run mode.""" if self.conf.dryrun: sleep(time) def register_tmpfile(self, filename): if filename not in self.tmpfiles: logger.debug("register tmp file %s", filename) self.tmpfiles.add(filename) def _check_open_logfile(self, path): """Check path type (must be string), search for path in logfiles set, and try opening fd. Returns the fd if everything goes well, None on error.""" if type(path) is not str: logger.warning("logfile path type is invalid, cannot " "use it") return None # search for path into user_logfiles if path in self.user_logfiles: logfile = self.user_logfiles.get(path) else: logfile = self.user_logfiles.add(path) fd = logfile.open() # returns None on error return fd def _output_streams(self, stdout, stderr): """Returns the files handlers for the stdout and stderr of the command based on user input and --log argument. By default, the neos stdout/stderr are selected, or --log file if set. Then, if the user has given either a stdout and a stderr for the command, check and open them before using them.""" # use neos stdout/stderr by default or --log file if set if self.conf.log: p_stdout = self.logfile p_stderr = self.logfile else: p_stdout = sys.stdout p_stderr = sys.stderr # if the user either give a stdout or a stderr if stdout is not None or stderr is not None: if stdout is not None: fd = self._check_open_logfile(stdout) if fd is not None: p_stdout = fd if stderr is not None: fd = self._check_open_logfile(stderr) if fd is not None: p_stderr = fd return (p_stdout, p_stderr) def cmd_run_bg(self, cmd, shell=False, stdout=None, stderr=None): if self.conf.dryrun: logger.info("run cmd: %s", ' '.join(cmd)) return FakePopen() else: logger.debug("run cmd: %s", ' '.join(cmd)) (p_stdout, p_stderr) = self._output_streams(stdout, stderr) process = Popen(cmd, shell=shell, stdout=p_stdout, stderr=p_stderr) self.pids.add(process) logger.debug("Process added to monitoring: %s", process.pid) def cmd_wait(self, cmd, shell=False, stdout=None, stderr=None): if self.conf.dryrun: logger.info("run and wait cmd : %s", ' '.join(cmd)) return 0 else: logger.debug("run and wait cmd: %s", ' '.join(cmd)) (p_stdout, p_stderr) = self._output_streams(stdout, stderr) return call(cmd, shell=shell, stdout=p_stdout, stderr=p_stderr) def cmd_output(self, cmd, shell=False): if self.conf.dryrun: logger.info("run cmd: %s", ' '.join(cmd)) return 'drynrunoutput' else: logger.debug("run cmd: %s", ' '.join(cmd)) return check_output(cmd, shell=shell) def kill(self, exception): for process in self.pids: if process is not exception: logger.debug("killing process %s", process.pid) process.kill() process.wait() def wait(self): # do not wait in dry-run mode if self.conf.dryrun: return # do not wait if nothing to wait for if len(self.pids) == 0: return logger.debug("now waiting for processes to end") try: while True: for process in self.pids: returncode = process.poll() if returncode is not None: logger.info("process pid %d ends with return code: %d", process.pid, returncode) logger.info("now killing other processes") self.kill(process) return else: sleep(0.5) except KeyboardInterrupt, e: logger.info("SIGINT received, killing all processes") self.kill(None)
class Scenario(object): OPTS = ['logfile:str:${BASEDIR}/Xlog_${JOBID}'] def __init__(self): self.conf = Conf() self.job = Job() self.pids = set() # bg processes self.tmpfiles = set() self.user_logfiles = LogFileSet() self.password = gen_password() if 'SSH_CONNECTION' in os.environ: self.srcip = os.environ['SSH_CONNECTION'].split(' ')[0] else: logger.warning("unable to extract source IP from SSH_CONNECTION, " "fallback to 127.0.0.1") self.srcip = '127.0.0.1' try: self.rinip = socket.gethostbyname(self.conf.wan_prefix + socket.gethostname()) except socket.gaierror: logger.error("failed to get wan hostname, fallback to localhost") self.rinip = '127.0.0.1' self.opts = ScenarioOpts() self.declare_opts() if self.conf.log: self.ensure_dir(self.opts.logfile) logger.debug("opening logfile %s", self.opts.logfile) self.logfile = open(self.opts.logfile, 'w+') else: self.logfile = None def declare_opts(self): logger.debug("scenario %s: declaring opts", self.NAME) self.declare_opts_cls(type(self)) def declare_opts_cls(self, cls): if cls == object: return if hasattr(cls, 'OPTS'): for opt_s in cls.OPTS: logger.debug("class %s: opt %s", cls.__name__, opt_s) self.opts.add(opt_s) for xcls in cls.__bases__: self.declare_opts_cls(xcls) def set_opts(self): """Set scenario opts with values given by user in parameters.""" if self.conf.opts is not None: for opt_s in self.conf.opts: self.opts.set(opt_s) def ensure_dir(self, filename): """Ensure the parent directory of the filename in parameter exists so that the file can be created witout problem then.""" dirname = os.path.dirname( os.path.abspath(os.path.expanduser(filename))) if not os.path.isdir(dirname): logger.debug("creating directory %s", dirname) os.makedirs(dirname) def create_file(self, filename): """Create empty file if not existing""" # first ensure directory exists self.ensure_dir(filename) absfile = os.path.abspath(os.path.expanduser(filename)) if not os.path.exists(absfile): logger.debug("creating empty file %s", absfile) fh = open(absfile, 'w+') fh.close() def sleep(self, time): """Sleep if not in dry-run mode.""" if self.conf.dryrun: sleep(time) def register_tmpfile(self, filename): if filename not in self.tmpfiles: logger.debug("register tmp file %s", filename) self.tmpfiles.add(filename) def _check_open_logfile(self, path): """Check path type (must be string), search for path in logfiles set, and try opening fd. Returns the fd if everything goes well, None on error.""" if type(path) is not str: logger.warning("logfile path type is invalid, cannot " "use it") return None # search for path into user_logfiles if path in self.user_logfiles: logfile = self.user_logfiles.get(path) else: logfile = self.user_logfiles.add(path) fd = logfile.open() # returns None on error return fd def _output_streams(self, stdout, stderr): """Returns the files handlers for the stdout and stderr of the command based on user input and --log argument. By default, the neos stdout/stderr are selected, or --log file if set. Then, if the user has given either a stdout and a stderr for the command, check and open them before using them.""" # use neos stdout/stderr by default or --log file if set if self.conf.log: p_stdout = self.logfile p_stderr = self.logfile else: p_stdout = sys.stdout p_stderr = sys.stderr # if the user either give a stdout or a stderr if stdout is not None or stderr is not None: if stdout is not None: fd = self._check_open_logfile(stdout) if fd is not None: p_stdout = fd if stderr is not None: fd = self._check_open_logfile(stderr) if fd is not None: p_stderr = fd return (p_stdout, p_stderr) def cmd_run_bg(self, cmd, shell=False, stdout=None, stderr=None): if self.conf.dryrun: logger.info("run cmd: %s", ' '.join(cmd)) return FakePopen() else: logger.debug("run cmd: %s", ' '.join(cmd)) (p_stdout, p_stderr) = self._output_streams(stdout, stderr) process = Popen(cmd, shell=shell, stdout=p_stdout, stderr=p_stderr) self.pids.add(process) logger.debug("Process added to monitoring: %s", process.pid) def cmd_wait(self, cmd, shell=False, stdout=None, stderr=None): if self.conf.dryrun: logger.info("run and wait cmd : %s", ' '.join(cmd)) return 0 else: logger.debug("run and wait cmd: %s", ' '.join(cmd)) (p_stdout, p_stderr) = self._output_streams(stdout, stderr) return call(cmd, shell=shell, stdout=p_stdout, stderr=p_stderr) def cmd_output(self, cmd, shell=False): if self.conf.dryrun: logger.info("run cmd: %s", ' '.join(cmd)) return 'drynrunoutput' else: logger.debug("run cmd: %s", ' '.join(cmd)) return check_output(cmd, shell=shell) def kill(self, exception): for process in self.pids: if process is not exception: logger.debug("killing process %s", process.pid) process.kill() process.wait() def wait(self): # do not wait in dry-run mode if self.conf.dryrun: return # do not wait if nothing to wait for if len(self.pids) == 0: return logger.debug("now waiting for processes to end") try: while True: for process in self.pids: returncode = process.poll() if returncode is not None: logger.info("process pid %d ends with return code: %d", process.pid, returncode) logger.info("now killing other processes") self.kill(process) return else: sleep(0.5) except KeyboardInterrupt, e: logger.info("SIGINT received, killing all processes") self.kill(None)