Mini Shell

Direktori : /opt/imh-python/lib/python3.9/site-packages/ngxconf/
Upload File :
Current File : //opt/imh-python/lib/python3.9/site-packages/ngxconf/masters.py

# vim: set ts=4 sw=4 expandtab syntax=python:
"""

ngxconf.masters
FPM Masters: Supervisord XML-RPC Interface & Config Management

Copyright (c) 2018-2020 InMotion Hosting, Inc.
http://www.inmotionhosting.com/

@author J. Hipps <jacobh@inmotionhosting.com>

"""
# pylint: disable=invalid-name

import os
import logging
from xmlrpc.client import ServerProxy

import arrow
try:
    from supervisor.xmlrpc import SupervisorTransport
except ImportError:
    SupervisorTransport = None

from ngxconf import __version__, __date__
from ngxconf.util import load_template, gconf

logger = logging.getLogger('ngxconf')


class Supervisor(object):
    """
    Class for wrapping supervisord communications
    and managing PHP-FPM masters
    """
    sockpath = None
    rpc = None
    connected = False
    template = None
    confpath = "/opt/ngxconf/supervisord/conf.d"

    def __init__(self, sockpath='/var/run/supervisord-fpm.sock'):
        self.sockpath = sockpath
        if SupervisorTransport is None:
            logger.error("Machine is missing the imh-python-supervisor package")
        else:
            self.connect()

    def connect(self):
        """
        Initialize supervisord XMLRPC connection via UNIX socket
        """
        try:
            self.rpc = ServerProxy('http://localhost', transport=SupervisorTransport(None, None, serverurl='unix://' + self.sockpath))
        except Exception as e:
            logger.error("Failed to spawn XMLRPC connection to supervisord: %s", str(e))
            self.connected = False
            return False

        # Check to ensure connection is established
        try:
            cstate = self.rpc.supervisor.getState()
            spid = self.rpc.supervisor.getPID()
            logger.debug("Connected to supervisord (pid %d) on %s [state: %s (%s)]", spid, self.sockpath, cstate['statename'], cstate['statecode'])
        except Exception as e:
            logger.error("Failed to connect to supervisord master at %s: %s", self.sockpath, str(e))
            self.connected = False
            return False

        if cstate['statecode'] != 1:
            logger.warning("Supervisord is in state %s (%s)", cstate['statename'], cstate['statecode'])

        self.connected = True
        return True

    def update_config(self, reload_list=None):
        """
        Reload supervisord configuration, and start/stop/restart
        process groups accordingly
        If @reload_list is set, it should be a list [] of vhosts
        that this function will ensure get restarted
        """
        if not self.connected:
            logger.error("supervisord: Not connected, unable to reload config")
            return -1

        try:
            added, changed, removed = self.rpc.supervisor.reloadConfig()[0]
            logger.debug("supervisord: reloadConfig: %d added / %d changed / %d removed",
                         len(added), len(changed), len(removed))
        except Exception as e:
            self.connected = False
            logger.error("supervisord: Failed to reload config: %s", str(e))
            return -1
        fails = 0

        # ensure we have a connection
        self.rpc.supervisor.getState()

        changeset = set(changed)
        if reload_list:
            changeset.update(reload_list)
            changeset = changeset.difference(added)

        for tgroup in removed:
            logger.info("supervisord: ProcessGroup removed: %s", tgroup)
            try:
                self.rpc.supervisor.stopProcessGroup(tgroup)
                self.rpc.supervisor.removeProcessGroup(tgroup)
                changeset.discard(tgroup)
            except Exception:
                # Not counted as a failure, since it should already be removed
                pass

        for tgroup in changeset:
            logger.info("supervisord: ProcessGroup changed: %s", tgroup)
            try:
                self.rpc.supervisor.stopProcessGroup(tgroup)
                self.rpc.supervisor.removeProcessGroup(tgroup)
                self.rpc.supervisor.addProcessGroup(tgroup)
                self.rpc.supervisor.startProcessGroup(tgroup, False)
            except Exception as e:
                logger.error("Failed to change ProcessGroup %s: %s", tgroup, str(e))
                fails += 1

        for tgroup in added:
            logger.info("supervisord: ProcessGroup added: %s", tgroup)
            try:
                self.rpc.supervisor.addProcessGroup(tgroup)
                self.rpc.supervisor.startProcessGroup(tgroup, False)
            except Exception as e:
                logger.error("Failed to add ProcessGroup %s: %s", tgroup, str(e))
                fails += 1

        logger.info("supervisord: Reload complete (%d failures)", fails)
        return fails

    def get_info(self):
        """
        Return status of all ProcessGroups
        """
        return self.rpc.supervisor.getAllProcessInfo()

    def render_config(self, pgname, binpath, confpath):
        """
        Render config file for ProcessGroup @pgname
        """
        if not self.template:
            self.template = load_template('user_supervisor')
        outconf = os.path.join(self.confpath, pgname + '.conf')
        phpconf = os.path.join(gconf.fpm_conf_path, pgname + '.conf')

        # render with Jinja2
        try:
            ttext = self.template.render(tstamp=arrow.now().format(), poolname=pgname,
                                         php_binpath=binpath, php_confpath=phpconf,
                                         ngxconf_ver="%s (%s)" % (__version__, __date__))
        except Exception as e:
            logger.error("Failed to render supervisord template for %s: %s", pgname, str(e))
            return False

        # write new include file
        try:
            with open(outconf, 'w') as f:
                f.write(ttext)
            logger.debug("Rendered supervisord config for %s to %s", pgname, outconf)
        except Exception as e:
            logger.error("Failed to render template to file %s: %s", outconf, str(e))
            return False

        return True

    def restart_group(self, pgname):
        self.stop_group(pgname)
        return self.start_group(pgname)

    def stop_group(self, pgname):
        try:
            self.rpc.stopProccessGroup(pgname)
        except Exception as e:
            logger.error("Failed to stop ProcessGroup %s: %s", pgname, str(e))
            return False
        return True

    def start_group(self, pgname):
        try:
            self.rpc.startProccessGroup(pgname)
        except Exception as e:
            logger.error("Failed to start ProcessGroup %s: %s", pgname, str(e))
            return False
        return True

Zerion Mini Shell 1.0