示例#1
0
    def memusage(self, name):
        info = self.getProcessInfo(name)

        if info['pid'] == 0:
            raise RPCError(Faults.NOT_RUNNING)

        return get_rss(info['pid'])
示例#2
0
 def onwait():
     # check stopper
     if self.stopper.in_progress():
         return NOT_DONE_YET
     if application.state != ApplicationStates.STOPPED:
         raise RPCError(Faults.ABNORMAL_TERMINATION, application_name)
     return True
    def UpdateNumprocs(self, group_name):
        """ graceful process_group numprocs without restart all process when only numprocs changed.
            if numprocs increased, the operation will start (new_num - old_num) processes,
            if numprocs reduced, the operation will stop the last (new_num - old_num) processes
            @param string group_name Name of an existing process group
        """

        try:
            self.supervisord.options.process_config(do_usage=False)
        except ValueError as msg:
            raise RPCError(Faults.CANT_REREAD, msg)

        group = self._getProcessGroup(group_name)
        old_config = self.supervisord.process_groups[group_name].config
        new_config = [
            cfg for cfg in self.supervisord.options.process_group_configs
            if cfg.name == group_name
        ][0]
        if old_config == new_config:
            return json.dumps({"msg": "No need to update", "type": "error"})
        else:
            if old_config.name != new_config.name or old_config.priority != new_config.priority:
                return json.dumps({
                    "msg":
                    "Not only numprocs has changed: priority is difference",
                    "type": "error"
                })
            new_process_configs = new_config.process_configs
            old_process_configs = old_config.process_configs
            if len(old_process_configs) < len(new_process_configs):
                if self._issubset(old_process_configs, new_process_configs):
                    return self._add_num(
                        group_name,
                        self._difference(new_process_configs,
                                         old_process_configs))
                else:
                    return json.dumps({
                        "msg": "Not only numprocs has changed",
                        "type": "error"
                    })
            elif len(old_process_configs) > len(new_process_configs):
                if self._issubset(new_process_configs, old_process_configs):
                    return self._reduce_num(
                        group_name,
                        self._difference(old_process_configs,
                                         new_process_configs))
                else:
                    return json.dumps({
                        "msg": "Not only numprocs has changed",
                        "type": "error"
                    })
            # If other not only name, priority or numprocs has changed
            # Return an error
            else:
                return json.dumps({
                    "msg": "Other settings has changed, please use update",
                    "type": "error"
                })
        # Return something for xmlrpc lib
        return True
示例#4
0
    def _update(self, text):
        self.update_text = text  # for unit tests, mainly

        state = self.supervisord.get_state()

        if state == SupervisorStates.SHUTDOWN:
            raise RPCError(Faults.SHUTDOWN_STATE)
示例#5
0
    def _getGroupAndProcess(self, name):
        # get process to start from name
        group_name, process_name = split_namespec(name)

        group = self.supervisord.process_groups.get(group_name)
        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        if process_name is None:
            return group, None

        process = group.processes.get(process_name)
        if process is None:
            raise RPCError(Faults.BAD_NAME, name)

        return group, process
示例#6
0
 def _getProcessGroup(self, name):
     """ Find a process group by its name """
     group = self.supervisord.process_groups.get(name)
     if group is None:
         raise RPCError(SupervisorFaults.BAD_NAME,
                        'Unknown group: %s' % name)
     return group
示例#7
0
    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 boolean result Always return true unless error.
        """
        self._update('stopProcessGroup')

        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.stopProcess,
                               wait=wait)

        killall.delay = 0.05
        killall.rpcinterface = self
        return killall  # deferred
示例#8
0
    def stop_application(self, application_name, wait=True):
        """ Stop the application named application_name.

        *@param* ``str application_name``: the name of the application.

        *@param* ``bool wait``: wait for the application 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 application_name is unknown to **Supvisors**.

        *@return* ``bool``: always ``True`` unless error.
        """
        self._check_operating_conciliation()
        # check application is known
        if application_name not in self.context.applications.keys():
            raise RPCError(Faults.BAD_NAME, application_name)
        # do NOT check application state as there may be processes RUNNING although the application is declared STOPPED
        application = self.context.applications[application_name]
        done = self.stopper.stop_application(application)
        self.logger.debug('stop_application {} done={}'.format(application_name, done))
        # wait until application fully STOPPED
        if wait and not done:
            def onwait():
                # check stopper
                if self.stopper.in_progress():
                    return NOT_DONE_YET
                if application.state != ApplicationStates.STOPPED:
                    raise RPCError(Faults.ABNORMAL_TERMINATION, application_name)
                return True
            onwait.delay = 0.5
            return onwait # deferred
        # if done is True, nothing to do
        return not done
示例#9
0
    def addGroup(self, group_name, priority=999):
        """
        Adds a new process group to the supervisor configuration.
        
        Args:
            group_name (str): The name of the group to add.
            priority (int, optional): The group priority. Defaults to 999.
            
        Returns:
            (boolean) True, unless an error is raised.
        """
        self._update('addGroup')

        # make sure the group doesn't already exist
        if self.supervisord.process_groups.get(group_name) is not None:
            raise RPCError(Faults.ALREADY_ADDED, group_name)
        
        options = self.supervisord.options
        group_config = ProcessGroupConfig(
            options, group_name, priority, process_configs=[])
        self.supervisord.options.process_group_configs.append(group_config)
        group_config.after_setuid()
        self.supervisord.process_groups[group_name] = group_config.make_group()

        return True
示例#10
0
    def log(self, message, level=supervisor.loggers.LevelsByName.INFO):
        """
        Writes a message to the main supervisor log.

        Args:
            message (str): The message to write.
            level (str, optional): The log level to assign to the message.
                Defaults to supervisor.loggers.LevelsByName.INFO.

        Raises:
            RPCError: INCORRECT_PARAMETERS if an invalid log level is provided

        Returns:
            boolean: True, unless an error was raised.
        """
        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(Faults.INCORRECT_PARAMETERS)

        self.supervisord.options.logger.log(level, message)
        return True
示例#11
0
    def _readProcessLog(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):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return as_string(readFile(logfile, int(offset), int(length)))
        except ValueError as inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))
示例#12
0
    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
        """

        group = self.supervisord.process_groups.get(name)
        self._update('signalProcessGroup')

        if group is None:
            raise RPCError(Faults.BAD_NAME, name)

        processes = list(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
示例#13
0
    def _extract_params(self, proc_name, proc_section):
        configfile = self.supervisord.options.configfile
        config = ConfigParser.RawConfigParser()
        config.read(configfile)

        req_params = ('workernames', 'tasks')
        opt_params = ('concurrency', 'loglevel', 'logfile', 'pidfile')
        params_dict = {}
        for param in req_params:
            try:
                params_dict[param] = config.get(proc_section, param)
            except ConfigParser.NoOptionError:
                raise RPCError(SupervisorFaults.BAD_ARGUMENTS)

        req_command = '{workernames} -A {tasks} '.format(**params_dict)
        opt_command = ''
        for param in opt_params:
            param_value = None
            try:
                param_value = config.get(proc_section, param)
            except ConfigParser.NoOptionError:
                pass

            if param_value is not None:
                opt_command += '--{param}={param_value} '.format(
                    param=param, param_value=param_value)

        # Replace supervisor special chars to use celery wildcards
        command = req_command + opt_command.replace('%%', '%')
        return command
示例#14
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 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
示例#15
0
    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
示例#16
0
    def addProgramToGroup(self, group_name, program_name, program_options):
        """
        Adds a new program to an existing process group.
        
        Args:
            group_name (str): The name of the group to add this program to.
            program_name (str): The name of the program to add to supervisor.
            program_options (dict): The program configuration options.
        
        Returns:
            (boolean): True, unless an error is raised
        """
        self._update('addProgramToGroup')
        group = self._get_process_group(group_name, True)

        # determine the process name based on the current instances
        # of this program.
        current_program_instances = self.numprocs.get(program_name)
        process_num = '%d' % (current_program_instances + 1)
        self._apply_process_num(program_options, process_num)
        
        # make a configparser instance for the program
        section_name = 'program:%s' % program_name
        parser = self._make_config_parser(section_name, program_options)
        
        # make the process configs from the parser instance
        options = self.supervisord.options
        try:
            new_configs = options.processes_from_section(
                parser, section_name, group_name)
        except ValueError as e:
            raise RPCError(Faults.INCORRECT_PARAMETERS, e)
        
        # make sure the new program doesn't already exist in the group
        for new_config in new_configs:
            for existing_config in group.config.process_configs:
                if new_config.name == existing_config.name:
                    raise RPCError(Faults.BAD_NAME, new_config.name)

        # add the new program configuration(s) to the group
        group.config.process_configs.extend(new_configs)
        for new_config in new_configs:
            new_config.create_autochildlogs()
            group.processes[new_config.name] = new_config.make_process(group)
        
        self.numprocs.increment(program_name)
        return True
示例#17
0
    def start_args(self, namespec, extra_args='', wait=True):
        """ Start a local process.
        The behaviour is different from ``supervisor.startProcess`` as it sets the process state to ``FATAL``
        instead of throwing an exception to the RPC client.
        This RPC makes it also possible to pass extra arguments to the program command line.

        *@param* ``str namespec``: the process namespec.

        *@param* ``str extra_args``: extra arguments to be passed to the command line of the program.

        *@param* ``bool wait``: wait for the process to be fully started.

        *@throws* ``RPCError``:

            * with code ``Faults.BAD_NAME`` if namespec is unknown to the local Supervisor,
            * with code ``Faults.BAD_EXTRA_ARGUMENTS`` if program is required or has a start sequence,
            * with code ``Faults.ALREADY_STARTED`` if process is ``RUNNING``,
            * with code ``Faults.ABNORMAL_TERMINATION`` if process could not be started.

        *@return* ``bool``: always ``True`` unless error.
        """
        # WARN: do NOT check OPERATION (it is used internally in DEPLOYMENT)
        application, process = self._get_application_process(namespec)
        if extra_args and not process.accept_extra_arguments():
            raise RPCError(Faults.BAD_EXTRA_ARGUMENTS, 'rules for namespec {} are not compatible with extra arguments in command line'.format(namespec))
        # update command line in process config with extra_args
        try:
            self.info_source.update_extra_args(namespec, extra_args)
        except KeyError:
            # process is unknown to the local Supervisor
            # this should not happen as Supvisors checks the configuration before it sends this request
            self.logger.error('could not find {} in supervisord processes'.format(namespec))
            raise RPCError(Faults.BAD_NAME, 'namespec {} unknown in this Supervisor instance'.format(namespec))
        # start process with Supervisor internal RPC
        try:
            cb = self.info_source.supervisor_rpc_interface.startProcess(namespec, wait)
        except RPCError, why:
            self.logger.error('start_process {} failed: {}'.format(namespec, why))
            if why.code in [Faults.NO_FILE, Faults.NOT_EXECUTABLE]:
                self.logger.warn('force supervisord internal state of {} to FATAL'.format(namespec))
                # at this stage, process is known to the local Supervisor
                self.info_source.force_process_fatal(namespec, why.text)
            # else process is already started
            # this should not happen as Supvisors checks the process state before it sends this request
            # anyway raise exception again
            raise
示例#18
0
 def _get_process(self, namespec):
     """ Return the ProcessStatus corresponding to the namespec.
     A BAD_NAME exception is raised if the process is not found. """
     try:
         process = self.context.processes[namespec]
     except KeyError:
         raise RPCError(Faults.BAD_NAME, 'process {} unknown in Supvisors'.format(namespec))
     return process
示例#19
0
 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
示例#20
0
    def addProgramToGroup(self, group_name, program_name, program_options):
        """ Add a new program to an existing process group.  Depending on the
            numprocs option, this will result in one or more processes being
            added to the group.

        @param string  group_name       Name of an existing process group
        @param string  program_name     Name of the new process in the process table
        @param struct  program_options  Program options, same as in supervisord.conf
        @return boolean                 Always True unless error
        """
        self._update('addProgramToGroup')

        group = self._getProcessGroup(group_name)

        # make configparser instance for program options
        section_name = 'program:%s' % program_name
        parser = self._makeConfigParser(section_name, program_options)

        # make process configs from parser instance
        options = self.supervisord.options
        try:
            new_configs = options.processes_from_section(
                parser, section_name, group_name)
        except ValueError as e:
            raise RPCError(SupervisorFaults.INCORRECT_PARAMETERS, e)

        # check new process names don't already exist in the config
        for new_config in new_configs:
            for existing_config in group.config.process_configs:
                if new_config.name == existing_config.name:
                    raise RPCError(
                        SupervisorFaults.BAD_NAME,
                        "Duplicated process name:" + new_config.name)

        # add process configs to group
        group.config.process_configs.extend(new_configs)

        for new_config in new_configs:
            # the process group config already exists and its after_setuid hook
            # will not be called again to make the auto child logs for this process.
            new_config.create_autochildlogs()

            # add process instance
            group.processes[new_config.name] = new_config.make_process(group)

        return True
示例#21
0
 def _get_application(self, application_name):
     """ Return the ApplicationStatus corresponding to the application name.
     A BAD_NAME exception is raised if the application is not found. """
     try:
         application = self.context.applications[application_name]
     except KeyError:
         raise RPCError(Faults.BAD_NAME, 'application {} unknown in Supvisors'.format(application_name))
     return application
 def _makeConfigParser(self, section_name, options):
     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 addGroup(self, name, priority=999):
        """ Add a new, empty process group.

        @param string   name         Name for the new process group
        @param integer  priority     Group priority (same as supervisord.conf)
        @return boolean              Always True unless error
        """
        self._update('addGroup')

        # check group_name does not already exist
        if self.supervisord.process_groups.get(name) is not None:
            raise RPCError(SupervisorFaults.BAD_NAME, name)

        # check priority is sane
        try:
            int(priority)
        except ValueError, why:
            raise RPCError(SupervisorFaults.INCORRECT_PARAMETERS, why[0])
示例#24
0
 def _check_state(self, states):
     """ Raises a BAD_SUPVISORS_STATE exception if Supvisors' state is NOT in one of the states. """
     if self.supvisors.fsm.state not in states:
         raise RPCError(
             Faults.BAD_SUPVISORS_STATE,
             'Supvisors (state={}) not in state {} to perform request'.
             format(SupvisorsStates._to_string(self.supvisors.fsm.state),
                    [SupvisorsStates._to_string(state)
                     for state in states]))
示例#25
0
    def readLog(self, offset, length):
        """ Read length bytes from the main log starting at offset

        @param int offset         offset to start reading from.
        @param int length         number of bytes to read from the log.
        @return string result     Bytes of log
        """
        self._update('readLog')

        logfile = self.supervisord.options.logfile

        if logfile is None or not os.path.exists(logfile):
            raise RPCError(Faults.NO_FILE, logfile)

        try:
            return readFile(logfile, int(offset), int(length))
        except ValueError, inst:
            why = inst.args[0]
            raise RPCError(getattr(Faults, why))
示例#26
0
    def _make_config_parser(self, section_name, options):
        config_parser = UnhosedConfigParser()
        try:
            config_parser.add_section(section_name)
            for key, value in dict(options).items():
                config_parser.set(section_name, key, value)
        except (TypeError, ValueError) as e:
            return RPCError(SupervisorFaults.INCORRECT_PARAMETERS)

        return config_parser
    def UpdateNumprocs(self, group_name):
        self._update('UpdateNumprocs')
        try:
            self.supervisord.options.process_config(do_usage=False)
        except ValueError as msg:
            raise RPCError(Faults.CANT_REREAD, msg)

        group = self._getProcessGroup(group_name)
        old_config = self.supervisord.process_groups[group_name].config
        new_config = [
            cfg for cfg in self.supervisord.options.process_group_configs
            if cfg.name == group_name
        ][0]
        if old_config == new_config:
            return json.dumps({"msg": "No need to update", "type": "error"})
        else:
            if old_config.name != new_config.name or old_config.priority != new_config.priority:
                return json.dumps({
                    "msg":
                    "Not only numprocs has changed: priority is difference",
                    "type": "error"
                })
            new_process_configs = new_config.process_configs
            old_process_configs = old_config.process_configs
            if len(old_process_configs) < len(new_process_configs):
                if self._issubset(old_process_configs, new_process_configs):
                    return self._add_num(
                        group_name,
                        self._difference(new_process_configs,
                                         old_process_configs))
                else:
                    return json.dumps({
                        "msg": "Not only numprocs has changed",
                        "type": "error"
                    })
            elif len(old_process_configs) > len(new_process_configs):
                if self._issubset(new_process_configs, old_process_configs):
                    return self._reduce_num(
                        group_name,
                        self._difference(old_process_configs,
                                         new_process_configs))
                else:
                    return json.dumps({
                        "msg": "Not only numprocs has changed",
                        "type": "error"
                    })
            # If other not only name, priority or numprocs has changed
            # Return an error
            else:
                return json.dumps({
                    "msg": "Other settings has changed, please use update",
                    "type": "error"
                })
        # Return something for xmlrpc lib
        return True
示例#28
0
    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, msg:
            raise RPCError(Faults.CANT_REREAD, msg)
示例#29
0
    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
示例#30
0
        def startit():
            if not started:

                if process.get_state() in RUNNING_STATES:
                    raise RPCError(Faults.ALREADY_STARTED, name)

                process.spawn()

                if process.spawnerr:
                    raise RPCError(Faults.SPAWN_ERROR, name)

                # we use a list here to fake out lexical scoping;
                # using a direct assignment to 'started' in the
                # function appears to not work (symptom: 2nd or 3rd
                # call through, it forgets about 'started', claiming
                # it's undeclared).
                started.append(time.time())

            if not wait or not startsecs:
                return True

            t = time.time()
            if t < started[0]:
                self.supervisord.options.logger.warn(
                    "startProcess: Abnormal system time detected, assuming start OK"
                )
                t = started[0] + startsecs

            runtime = (t - started[0])
            state = process.get_state()

            if state not in (ProcessStates.STARTING, ProcessStates.RUNNING):
                raise RPCError(Faults.ABNORMAL_TERMINATION, name)

            if runtime < startsecs:
                return NOT_DONE_YET

            if state == ProcessStates.RUNNING:
                return True

            raise RPCError(Faults.ABNORMAL_TERMINATION, name)