def clearProcessLogs(self, name): """ Clear the stdout and stderr logs for the named process and reopen them. @param string name The name of the process (or 'group:name') @return boolean result Always True unless error """ self._update('clearProcessLogs') group, process = self._getGroupAndProcess(name) if process is None: raise RPCError(Faults.BAD_NAME, name) try: # implies a reopen process.removelogs() except (IOError, OSError): raise RPCError(Faults.FAILED, name) return True
def _makeConfigParser(self, section_name, options): """ Populate a new UnhosedConfigParser instance with a section built from an options dict. """ config = UnhosedConfigParser() try: config.add_section(section_name) for k, v in dict(options).items(): config.set(section_name, k, v) except (TypeError, ValueError): raise RPCError(SupervisorFaults.INCORRECT_PARAMETERS) return config
def removeProcessFromGroup(self, group_name, process_name): self._update('removeProcessFromGroup') group = self._getProcessGroup(group_name) # check process exists and is running process = group.processes.get(process_name) if process is None: raise RPCError(SupervisorFaults.BAD_NAME, process_name) if process.pid or process.state not in STOPPED_STATES: raise RPCError(SupervisorFaults.STILL_RUNNING, process_name) group.transition() # del process config from group, then del process for index, config in enumerate(group.config.process_configs): if config.name == process_name: del group.config.process_configs[index] del group.processes[process_name] return True
def _tailProcessLog(self, name, offset, length, channel): group, process = self._getGroupAndProcess(name) if process is None: raise RPCError(Faults.BAD_NAME, name) logfile = getattr(process.config, '%s_logfile' % channel) if logfile is None or not os.path.exists(logfile): return ['', 0, False] return tailFile(logfile, int(offset), int(length))
def log(self, message, level=supervisor.loggers.LevelsByName.INFO): self._update('log') if isinstance(level, str): level = getattr(supervisor.loggers.LevelsByName, level.upper(), None) if supervisor.loggers.LOG_LEVELS_BY_NUM.get(level, None) is None: raise RPCError(SupervisorFaults.INCORRECT_PARAMETERS) self.supervisord.options.logger.log(level, message) return True
def killit(): if not called: if process.get_state() not in RUNNING_STATES: raise RPCError(Faults.NOT_RUNNING) # use a mutable for lexical scoping; see startProcess called.append(1) if not killed: kill(process.pid, sig) killed.append(1) return True
def sendProcessStdin(self, name, chars): """ Send a string of chars to the stdin of the process name. If non-7-bit data is sent (unicode), it is encoded to utf-8 before being sent to the process' stdin. If chars is not a string or is not unicode, raise INCORRECT_PARAMETERS. If the process is not running, raise NOT_RUNNING. If the process' stdin cannot accept input (e.g. it was closed by the child process), raise NO_FILE. @param string name The process name to send to (or 'group:name') @param string chars The character data to send to the process @return boolean result Always return True unless error """ self._update('sendProcessStdin') if isinstance(chars, unicode): chars = chars.encode('utf-8') if not isinstance(chars, basestring): raise RPCError(Faults.INCORRECT_PARAMETERS, chars) group, process = self._getGroupAndProcess(name) if process is None: raise RPCError(Faults.BAD_NAME, name) if not process.pid or process.killing: raise RPCError(Faults.NOT_RUNNING, name) try: process.write(chars) except OSError: why = sys.exc_info()[1] if why.args[0] == errno.EPIPE: raise RPCError(Faults.NO_FILE, name) else: raise return True
def clearLog(self): """ Clear the main log. @return boolean result always returns True unless error """ self._update('clearLog') logfile = self.supervisord.options.logfile if logfile is None or not self.supervisord.options.exists(logfile): raise RPCError(Faults.NO_FILE) # there is a race condition here, but ignore it. try: self.supervisord.options.remove(logfile) except (OSError, IOError): raise RPCError(Faults.FAILED) for handler in self.supervisord.options.logger.handlers: if hasattr(handler, 'reopen'): self.supervisord.options.logger.info('reopening log file') handler.reopen() return True
def fetch(self, key): """ Retrieve data from cache stored under 'key' @param string key The cache key @return string Cache data stored at key """ self._update('fetch') self._validateKey(key) data = self.cache.get(key) if data is None: raise RPCError(Faults.BAD_NAME) return data
def get_address_info(self, address_name): """ Get information about the **Supvisors** instance running on the host named address_name. *@param* ``str address_name``: the address name where the Supervisor daemon is running. *@throws* ``RPCError``: with code ``Faults.BAD_ADDRESS`` if address name is unknown to **Supvisors**. *@return* ``dict``: a structure containing data about the **Supvisors** instance. """ try: status = self.context.addresses[address_name] except KeyError: raise RPCError(Faults.BAD_ADDRESS, 'address {} unknown in Supvisors'.format(address_name)) return status.serial()
def killit(): if not called: if process.get_state() not in RUNNING_STATES: raise RPCError(Faults.NOT_RUNNING) # use a mutable for lexical scoping; see startProcess called.append(1) if not stopped: msg = process.stop() if msg is not None: raise RPCError(Faults.FAILED, msg) stopped.append(1) if wait: return NOT_DONE_YET else: return True if process.get_state() not in (ProcessStates.STOPPED, ProcessStates.EXITED): return NOT_DONE_YET else: return True
def _get_process_group(self, group_name, create_group_if_not_exists=True): """ Retrieves the process group config for a specified process group. Args: group_name (str): The name of the process group to get. Returns: (ProcessGroupConfig): The process group configuration """ if not self.hasGroup(group_name): if create_group_if_not_exists: self.addGroup(group_name) else: raise RPCError(Faults.BAD_NAME, 'group: %s' % group_name) return self.supervisord.process_groups.get(group_name)
def getProcessInfo(self, name): """ Get info about a process named name @param string name The name of the process (or 'group:name') @return struct result A structure containing data about the process """ self._update('getProcessInfo') group, process = self._getGroupAndProcess(name) if process is None: raise RPCError(Faults.BAD_NAME, name) # TODO timestamps are returned as xml-rpc integers for b/c but will # saturate the xml-rpc integer type in jan 2038 ("year 2038 problem"). # future api versions should return timestamps as a different type. start = capped_int(process.laststart) stop = capped_int(process.laststop) now = capped_int(self._now()) state = process.get_state() spawnerr = process.spawnerr or '' exitstatus = process.exitstatus or 0 stdout_logfile = process.config.stdout_logfile or '' stderr_logfile = process.config.stderr_logfile or '' info = { 'name':process.config.name, 'group':group.config.name, 'start':start, 'stop':stop, 'now':now, 'state':state, 'statename':getProcessStateDescription(state), 'spawnerr':spawnerr, 'exitstatus':exitstatus, 'logfile':stdout_logfile, # b/c alias 'stdout_logfile':stdout_logfile, 'stderr_logfile':stderr_logfile, 'pid':process.pid, } description = self._interpretProcessInfo(info) info['description'] = description return info
def reloadConfig(self): """ Reload configuration @return boolean result always return True unless error """ self._update('reloadConfig') try: self.supervisord.options.process_config(do_usage=False) except ValueError as msg: raise RPCError(Faults.CANT_REREAD, msg) added, changed, removed = self.supervisord.diff_to_active() added = [group.name for group in added] changed = [group.name for group in changed] removed = [group.name for group in removed] return [[added, changed, removed]] # cannot return len > 1, apparently
def getProcessInfo(self, name): """ Get info about a process named name @param string name The name of the process (or 'group:name') @return struct result A structure containing data about the process """ self._update('getProcessInfo') group, process = self._getGroupAndProcess(name) if process is None: return self._getGroupProcessInfo(group) if process is None: raise RPCError(Faults.BAD_NAME, name) start = int(process.laststart) stop = int(process.laststop) now = int(time.time()) state = process.get_state() spawnerr = process.spawnerr or '' exitstatus = process.exitstatus or 0 stdout_logfile = process.config.stdout_logfile or '' stderr_logfile = process.config.stderr_logfile or '' info = { 'name': process.config.name, 'group': group.config.name, 'start': start, 'stop': stop, 'now': now, 'state': state, 'statename': getProcessStateDescription(state), 'spawnerr': spawnerr, 'exitstatus': exitstatus, 'logfile': stdout_logfile, # b/c alias 'stdout_logfile': stdout_logfile, 'stderr_logfile': stderr_logfile, 'pid': process.pid, } description = self._interpretProcessInfo(info) info['description'] = description return info
def __init__(self, supervisord): """ Initialization of the attributes. """ # store this instance in supervisord to ensure persistence supervisord.supvisors = self # get options from config file server_options = SupvisorsServerOptions() server_options.realize() self.options = server_options.supvisors_options # create logger stdout = supervisord.options.nodaemon self.logger = getLogger(self.options.logfile, self.options.loglevel, Supvisors.LOGGER_FORMAT, True, self.options.logfile_maxbytes, self.options.logfile_backups, stdout) # configure supervisor info source self.info_source = SupervisordSource(supervisord) # set addresses and check local address self.address_mapper = AddressMapper(self.logger) self.address_mapper.addresses = self.options.address_list if not self.address_mapper.local_address: raise RPCError( Faults.SUPVISORS_CONF_ERROR, 'local host unexpected in address list: {}'.format( self.options.address_list)) # create context data self.context = Context(self) # create application starter and stopper self.starter = Starter(self) self.stopper = Stopper(self) # create statistics handler self.statistician = StatisticsCompiler(self) # create the failure handler of crashing processes self.failure_handler = RunningFailureHandler(self) # create state machine self.fsm = FiniteStateMachine(self) # check parsing try: self.parser = Parser(self) except: self.logger.warn('cannot parse rules file: {}'.format( self.options.rules_file)) self.parser = None # create event subscriber self.listener = SupervisorListener(self)
def stop_process(self, namespec, wait=True): """ Stop the process named namespec where it is running. *@param* ``str namespec``: the process namespec (``name``, ``group:name``, or ``group:*``). *@param* ``bool wait``: wait for process to be fully stopped. *@throws* ``RPCError``: * with code ``Faults.BAD_SUPVISORS_STATE`` if **Supvisors** is not in state ``OPERATION`` or ``CONCILIATION``, * with code ``Faults.BAD_NAME`` if namespec is unknown to **Supvisors**. * with code ``Faults.NOT_RUNNING`` if process is in a stopped state, *@return* ``bool``: always ``True`` unless error. """ self._check_operating_conciliation() # check names application, process = self._get_application_process(namespec) processes = [process] if process else application.processes.values() # check processes are not already running for process in processes: if process.stopped(): raise RPCError(Faults.NOT_RUNNING, process.namespec()) # stop all processes done = True for process in processes: self.logger.info('stopping process {}'.format(process.namespec())) done &= self.stopper.stop_process(process) # wait until processes are in STOPPED_STATES if wait and not done: def onwait(): # check stopper if self.stopper.in_progress(): return NOT_DONE_YET for process in processes: if process.running(): raise RPCError(Faults.ABNORMAL_TERMINATION, process.namespec()) return True onwait.delay = 0.1 return onwait # deferred return True
def log(self, message, level=supervisor.loggers.LevelsByName.INFO): """ Write an arbitrary message to the main supervisord log. This is useful for recording information about your twiddling. @param string message Message to write to the log @param string|int level Log level name (INFO) or code (20) @return boolean Always True unless error """ self._update('log') if isinstance(level, str): level = getattr(supervisor.loggers.LevelsByName, level.upper(), None) if supervisor.loggers.LOG_LEVELS_BY_NUM.get(level, None) is None: raise RPCError(SupervisorFaults.INCORRECT_PARAMETERS) self.supervisord.options.logger.log(level, message) return True
def startProcess(self, name, wait=True): """ Start a process @param string name Process name (or ``group:name``, or ``group:*``) @param boolean wait Wait for process to be fully started @return boolean result Always true unless error """ self._update('startProcess') group, process = self._getGroupAndProcess(name) if process is None: group_name, process_name = split_namespec(name) return self.startProcessGroup(group_name, wait) # test filespec, don't bother trying to spawn if we know it will # eventually fail try: filename, argv = process.get_execv_args() except NotFound, why: raise RPCError(Faults.NO_FILE, why.args[0])
def reloadConfig(self): """ Reload the configuration. The result contains three arrays containing names of process groups: * `added` gives the process groups that have been added * `changed` gives the process groups whose contents have changed * `removed` gives the process groups that are no longer in the configuration @return array result [[added, changed, removed]] """ self._update('reloadConfig') try: self.supervisord.options.process_config(do_usage=False) except ValueError, msg: raise RPCError(Faults.CANT_REREAD, msg)
def killProcess(self, name, signal): """ Send signal to a process @param string signal Signal identifier @param string name Process name (or 'group:name', or 'group:*') @return boolean result Always true unless error """ self._update('killProcess') group, process = self._getGroupAndProcess(name) if process is None: group_name, process_name = split_namespec(name) return self.killProcessGroup(signal, group_name) sig = self._getSignalFromString(signal) if sig is None: raise RPCError(Faults.BAD_ARGUMENTS, signal) killed = [] called = [] kill = self.supervisord.options.kill def killit(): if not called: if process.get_state() not in RUNNING_STATES: raise RPCError(Faults.NOT_RUNNING) # use a mutable for lexical scoping; see startProcess called.append(1) if not killed: kill(process.pid, sig) killed.append(1) return True killit.delay = 0.2 killit.rpcinterface = self return killit # deferred
def signalProcessGroup(self, name, signal): """ Send a signal to all processes in the group named 'name' @param string name The group name @param string signal Signal to send, as name ('HUP') or number ('1') @return array """ self._update('signalProcessGroup') group = self.supervisord.process_groups.get(name) if group is None: raise RPCError(Faults.BAD_NAME, name) processes = group.processes.values() processes.sort() processes = [(group, process) for process in processes] sendall = make_allfunc(processes, isRunning, self.signalProcess, signal=signal) result = sendall() self._update('signalProcessGroup') return result
def _make_config_parser(self, section_name, section_options): """ Populates a new UnhosedConfigParser instance with a section built from a dict of section options. Args: section_name (str) -- The name of the section section_options (dict) -- The configuration options for the section Returns: config (supervisor.options.UnhosedConfgParser) Raises: RPCError: Raises error if section parameters are invalid """ config = UnhosedConfigParser() try: config.add_section(section_name) for key, value in dict(section_options).items(): config.set(section_name, key, value) except (TypeError, ValueError): raise RPCError(Faults.INCORRECT_PARAMETERS) return config
def conciliate(self, strategy): """ Apply the conciliation strategy only if **Supvisors** is in ``CONCILIATION`` state, with a USER strategy. *@param* ``ConciliationStrategies strategy``: the strategy used to conciliate. *@throws* ``RPCError``: * with code ``Faults.BAD_SUPVISORS_STATE`` if **Supvisors** is not in state ``CONCILIATION``, * with code ``Faults.BAD_STRATEGY`` if strategy is unknown to **Supvisors**. *@return* ``bool``: ``True`` if conciliation is triggered, ``False`` when strategy is USER. """ self._check_conciliation() # check strategy if strategy not in ConciliationStrategies._values(): raise RPCError(Faults.BAD_STRATEGY, '{}'.format(strategy)) # trigger conciliation if strategy != ConciliationStrategies.USER: conciliate_conflicts(self.supvisors, strategy, self.context.conflicts()) return True return False
def killProcessGroup(self, name, signal): """ Send signal to all processes in the group named 'name' @param string signal Signal identifier @param string name The group name @return struct result A structure containing start statuses """ self._update('killProcessGroup') group = self.supervisord.process_groups.get(name) if group is None: raise RPCError(Faults.BAD_NAME, name) processes = group.processes.values() processes.sort() processes = [ (group, process) for process in processes ] killall = make_allfunc(processes, isRunning, self.killProcess, signal=signal) killall.delay = 0.05 killall.rpcinterface = self return killall # deferred
def startProcessGroup(self, name, wait=True): """ Start all processes in the group named 'name' @param string name The group name @param boolean wait Wait for each process to be fully started @return struct result A structure containing start statuses """ self._update('startProcessGroup') group = self.supervisord.process_groups.get(name) if group is None: raise RPCError(Faults.BAD_NAME, name) processes = group.processes.values() processes.sort() processes = [ (group, process) for process in processes ] startall = make_allfunc(processes, isNotRunning, self.startProcess, wait=wait) startall.delay = 0.05 startall.rpcinterface = self return startall # deferred
def stopProcessGroup(self, name, wait=True): """ Stop all processes in the process group named 'name' @param string name The group name @param boolean wait Wait for each process to be fully stopped @return array result An array of process status info structs """ self._update('stopProcessGroup') group = self.supervisord.process_groups.get(name) if group is None: raise RPCError(Faults.BAD_NAME, name) processes = list(group.processes.values()) processes.sort() processes = [ (group, process) for process in processes ] killall = make_allfunc(processes, isRunning, self.stopProcess, wait=wait) killall.delay = 0.05 killall.rpcinterface = self return killall # deferred
def inner(): raise RPCError(Faults.BAD_NAME, 'foo')
def bad_name(self): raise RPCError(Faults.BAD_NAME, 'foo')
def callback(): raise RPCError(Faults.UNKNOWN_METHOD)