Exemplo n.º 1
0
    def process_prepare_co(self, environ):
        if not self._listener:
            self._listener = NotifyListener('@/chaperone/' + self.service.name,
                                            onNotify=self._notify_received)
            yield from self._listener.run()

        environ['NOTIFY_SOCKET'] = self._listener.socket_name

        # Now, set up an event which is triggered upon ready
        self._ready_event = asyncio.Event()
Exemplo n.º 2
0
    def __init__(self, options):
        self.sockname = options['--socket']
        if not self.sockname:
            self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid())

        self.proxy_enabled = parent_socket and not options['--noproxy']
        if options['--wait-stop']:
            self.wait_mode = 'stop'
        elif options['--wait-ready']:
            self.wait_mode = 'ready'

        if options['--timeout'] and self.wait_mode:
            self.timeout = float(options['--timeout'])

        self.verbose = options['--verbose']

        # Modify original environment

        os.environ['NOTIFY_SOCKET'] = self.sockname

        # Set up the environment, reparse the options, build the final command
        Environment.set_parse_parameters('%', '{')
        env = Environment()

        env['PID'] = str(os.getpid())
        env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE
        if parent_socket:
            env['ORIG_NOTIFY_SOCKET'] = parent_socket

        env = env.expanded()

        self.proc_args = shlex.split(
            env.expand(' '.join(
                maybe_quote(arg)
                for arg in [options['COMMAND']] + options['ARGS'])))

        self.listener = NotifyListener(self.sockname,
                                       onNotify=self.notify_received,
                                       onClose=self._parent_closed)
        loop.add_signal_handler(signal.SIGTERM, self._got_sig)
        loop.add_signal_handler(signal.SIGINT, self._got_sig)

        proctitle = '[sdnotify-exec]'

        try:
            from setproctitle import setproctitle
            setproctitle(proctitle)
        except ImportError:
            pass
Exemplo n.º 3
0
    def process_prepare_co(self, environ):
        if not self._listener:
            self._listener = NotifyListener('@/chaperone/' + self.service.name,
                                            onNotify = self._notify_received)
            yield from self._listener.run()

        environ['NOTIFY_SOCKET'] = self._listener.socket_name

        # Now, set up an event which is triggered upon ready
        self._ready_event = asyncio.Event()
Exemplo n.º 4
0
    def __init__(self, options):
        self.sockname = options['--socket']
        if not self.sockname:
            self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid())

        self.proxy_enabled = parent_socket and not options['--noproxy']
        if options['--wait-stop']:
            self.wait_mode = 'stop'
        elif options['--wait-ready']:
            self.wait_mode = 'ready'

        if options['--timeout'] and self.wait_mode:
            self.timeout = float(options['--timeout'])

        self.verbose = options['--verbose']

        # Modify original environment

        os.environ['NOTIFY_SOCKET'] = self.sockname

        # Set up the environment, reparse the options, build the final command
        Environment.set_parse_parameters('%', '{')
        env = Environment()

        env['PID'] = str(os.getpid())
        env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE
        if parent_socket:
            env['ORIG_NOTIFY_SOCKET'] = parent_socket
        
        env = env.expanded()

        self.proc_args = shlex.split(env.expand(' '.join(maybe_quote(arg) 
                                                         for arg in [options['COMMAND']] + options['ARGS'])))

        self.listener = NotifyListener(self.sockname, 
                                       onNotify = self.notify_received,
                                       onClose = self._parent_closed)
        loop.add_signal_handler(signal.SIGTERM, self._got_sig)
        loop.add_signal_handler(signal.SIGINT, self._got_sig)

        proctitle = '[sdnotify-exec]'

        try:
            from setproctitle import setproctitle
            setproctitle(proctitle)
        except ImportError:
            pass
Exemplo n.º 5
0
class NotifyProcess(SubProcess):

    process_timeout = 300

    _fut_monitor = None
    _listener = None
    _ready_event = None

    def _close_listener(self):
        if self._listener:
            self._listener.close()
            self._listener = None

    @asyncio.coroutine
    def process_prepare_co(self, environ):
        if not self._listener:
            self._listener = NotifyListener('@/chaperone/' + self.service.name,
                                            onNotify=self._notify_received)
            yield from self._listener.run()

        environ['NOTIFY_SOCKET'] = self._listener.socket_name

        # Now, set up an event which is triggered upon ready
        self._ready_event = asyncio.Event()

    def _notify_timeout(self):
        service = self.service
        message = "notify service '{1}' did not receive ready notification after {2} second(s), {3}".format(
            service.type, service.name, self.process_timeout,
            "proceeding due to 'ignore_failures=True'"
            if service.ignore_failures else
            "terminating due to 'ignore_failures=False'")
        if not service.ignore_failures:
            self.terminate()
        raise ChProcessError(message)

    @asyncio.coroutine
    def reset(self, dependents=False, enable=False, restarts_ok=False):
        yield from super().reset(dependents, enable, restarts_ok)
        self._close_listener()

    @asyncio.coroutine
    def final_stop(self):
        yield from super().final_stop()
        self._close_listener()

    @asyncio.coroutine
    def process_started_co(self):
        if self._fut_monitor and not self._fut_monitor.cancelled():
            self._fut_monitor.cancel()
            self._fut_monitor = None

        yield from self.do_startup_pause()

        self._fut_monitor = asyncio. async (self._monitor_service())
        self.add_pending(self._fut_monitor)

        if self._ready_event:
            try:
                if not self.process_timeout:
                    raise asyncio.TimeoutError()
                yield from asyncio.wait_for(self._ready_event.wait(),
                                            self.process_timeout)
            except asyncio.TimeoutError:
                self._ready_event = None
                self._notify_timeout()
            else:
                if self._ready_event:
                    self._ready_event = None
                    rc = self.returncode
                    if rc is not None and not rc.normal_exit:
                        if self.ignore_failures:
                            warn(
                                "{0} (ignored) failure on start-up with result '{1}'"
                                .format(self.name, rc))
                        else:
                            raise ChProcessError(
                                "{0} failed with reported error {1}".format(
                                    self.name, rc),
                                resultcode=rc)

    @asyncio.coroutine
    def _monitor_service(self):
        """
        We only care about errors here.  The rest is dealt with by having notifications
        occur.
        """
        result = yield from self.wait()
        if isinstance(result, int) and result > 0:
            self._setready()  # simulate ready
            self._ready_event = None
            self._close_listener()
            yield from self._abnormal_exit(result)

    def _notify_received(self, which, var, value):
        callfunc = getattr(self, "notify_" + var.upper(), None)
        #print("NOTIFY RECEIVED", var, value)
        if callfunc:
            callfunc(value)

    def _setready(self):
        if self._ready_event:
            self._ready_event.set()
            return True
        return False

    def notify_MAINPID(self, value):
        try:
            pid = int(value)
        except ValueError:
            self.logdebug("{0} got MAINPID={1}, but not a valid pid#",
                          self.name, value)
            return
        self.pid = pid

    def notify_BUSERROR(self, value):
        code = ProcStatus(value)
        if not self._setready():
            self.process_exit(code)
        else:
            self.returncode = code

    def notify_ERRNO(self, value):
        try:
            intval = int(value)
        except ValueError:
            self.logdebug("{0} got ERROR={1}, not a valid error code",
                          self.name, value)
            return
        code = ProcStatus(intval << 8)
        if not self._setready():
            self.process_exit(code)
        else:
            self.returncode = code

    def notify_READY(self, value):
        if value == "1":
            self._setready()

    def notify_STATUS(self, value):
        self.note = value

    @property
    def status(self):
        if self._ready_event:
            return "activating"
        return super().status
Exemplo n.º 6
0
class NotifyProcess(SubProcess):

    process_timeout = 300

    _fut_monitor = None
    _listener = None
    _ready_event = None
    
    def _close_listener(self):
        if self._listener:
            self._listener.close()
            self._listener = None

    @asyncio.coroutine
    def process_prepare_co(self, environ):
        if not self._listener:
            self._listener = NotifyListener('@/chaperone/' + self.service.name,
                                            onNotify = self._notify_received)
            yield from self._listener.run()

        environ['NOTIFY_SOCKET'] = self._listener.socket_name

        # Now, set up an event which is triggered upon ready
        self._ready_event = asyncio.Event()

    def _notify_timeout(self):
        service = self.service
        message = "notify service '{1}' did not receive ready notification after {2} second(s), {3}".format(
            service.type,
            service.name, self.process_timeout, 
            "proceeding due to 'ignore_failures=True'" if service.ignore_failures else
            "terminating due to 'ignore_failures=False'")
        if not service.ignore_failures:
            self.terminate()
        raise ChProcessError(message)

    @asyncio.coroutine
    def reset(self, dependents = False, enable = False, restarts_ok = False):
        yield from super().reset(dependents, enable, restarts_ok)
        self._close_listener()

    @asyncio.coroutine
    def final_stop(self):
        yield from super().final_stop()
        self._close_listener()

    @asyncio.coroutine
    def process_started_co(self):
        if self._fut_monitor and not self._fut_monitor.cancelled():
            self._fut_monitor.cancel()
            self._fut_monitor = None

        yield from self.do_startup_pause()

        self._fut_monitor = asyncio.async(self._monitor_service())
        self.add_pending(self._fut_monitor)

        if self._ready_event:
            try:
                if not self.process_timeout:
                    raise asyncio.TimeoutError()
                yield from asyncio.wait_for(self._ready_event.wait(), self.process_timeout)
            except asyncio.TimeoutError:
                self._ready_event = None
                self._notify_timeout()
            else:
                if self._ready_event:
                    self._ready_event = None
                    rc = self.returncode
                    if rc is not None and not rc.normal_exit:
                        if self.ignore_failures:
                            warn("{0} (ignored) failure on start-up with result '{1}'".format(self.name, rc))
                        else:
                            raise ChProcessError("{0} failed with reported error {1}".format(self.name, rc), resultcode = rc)

    @asyncio.coroutine
    def _monitor_service(self):
        """
        We only care about errors here.  The rest is dealt with by having notifications
        occur.
        """
        result = yield from self.wait()
        if isinstance(result, int) and result > 0:
            self._setready()    # simulate ready
            self._ready_event = None
            self._close_listener()
            yield from self._abnormal_exit(result)
            
    def _notify_received(self, which, var, value):
        callfunc = getattr(self, "notify_" + var.upper(), None)
        #print("NOTIFY RECEIVED", var, value)
        if callfunc:
            callfunc(value)

    def _setready(self):
        if self._ready_event:
            self._ready_event.set()
            return True
        return False

    def notify_MAINPID(self, value):
        try:
            pid = int(value)
        except ValueError:
            self.logdebug("{0} got MAINPID={1}, but not a valid pid#", self.name, value)
            return
        self.pid = pid

    def notify_BUSERROR(self, value):
        code = ProcStatus(value)
        if not self._setready():
            self.process_exit(code)
        else:
            self.returncode = code

    def notify_ERRNO(self, value):
        try:
            intval = int(value)
        except ValueError:
            self.logdebug("{0} got ERROR={1}, not a valid error code", self.name, value)
            return
        code = ProcStatus(intval << 8)
        if not self._setready():
            self.process_exit(code)
        else:
            self.returncode = code

    def notify_READY(self, value):
        if value == "1":
            self._setready()

    def notify_STATUS(self, value):
        self.note = value

    @property
    def status(self):
        if self._ready_event:
            return "activating"
        return super().status
Exemplo n.º 7
0
class SDNotifyExec:

    exitcode = 0
    sockname = None
    listener = None
    parent = None
    timeout = None
    wait_mode = None
    verbose = False

    parent_client = None
    proxy_enabled = True

    INFO_MESSAGE = {
        'READY': "READY={1}{2}",
        'MAINPID': "Process PID (={1}) notification{2}",
        'ERRNO': "Process ERROR (={1}) notification{2}",
        'STATUS': "Status message = '{1}'{2}",
        'default': "{0}={1}{2}",
    }

    def __init__(self, options):
        self.sockname = options['--socket']
        if not self.sockname:
            self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid())

        self.proxy_enabled = parent_socket and not options['--noproxy']
        if options['--wait-stop']:
            self.wait_mode = 'stop'
        elif options['--wait-ready']:
            self.wait_mode = 'ready'

        if options['--timeout'] and self.wait_mode:
            self.timeout = float(options['--timeout'])

        self.verbose = options['--verbose']

        # Modify original environment

        os.environ['NOTIFY_SOCKET'] = self.sockname

        # Set up the environment, reparse the options, build the final command
        Environment.set_parse_parameters('%', '{')
        env = Environment()

        env['PID'] = str(os.getpid())
        env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE
        if parent_socket:
            env['ORIG_NOTIFY_SOCKET'] = parent_socket
        
        env = env.expanded()

        self.proc_args = shlex.split(env.expand(' '.join(maybe_quote(arg) 
                                                         for arg in [options['COMMAND']] + options['ARGS'])))

        self.listener = NotifyListener(self.sockname, 
                                       onNotify = self.notify_received,
                                       onClose = self._parent_closed)
        loop.add_signal_handler(signal.SIGTERM, self._got_sig)
        loop.add_signal_handler(signal.SIGINT, self._got_sig)

        proctitle = '[sdnotify-exec]'

        try:
            from setproctitle import setproctitle
            setproctitle(proctitle)
        except ImportError:
            pass

    def info(self, msg):
        if self.verbose:
            print("info: " + msg)

    def _got_sig(self):
        self.kill_program()

    def kill_program(self, exitcode = None):
        if exitcode is not None:
            self.exitcode = exitcode
        loop.call_soon(self._really_kill)

    def _really_kill(self):
        self.listener.close()
        loop.stop()

    def _parent_closed(self, which, ex):
        if which == self.parent_client:
            self.proxy_enabled = False
            self.parent_client = None

    @asyncio.coroutine
    def _do_proxy_send(self, name, value):
        if not (parent_socket and self.proxy_enabled):
            return

        if not self.parent_client:
            self.parent_client = NotifyClient(parent_socket, onClose = self._parent_closed)
            yield from self.parent_client.run()

        yield from self.parent_client.send("{0}={1}".format(name, value))

    def send_to_proxy(self, name, value):
        asyncio.ensure_future(self._do_proxy_send(name, value))

    def notify_received(self, which, name, value):
        self.send_to_proxy(name, value)

        sent_info = False

        if self.wait_mode:
            if name == "READY" and value == "1":
                if self.wait_mode == 'ready':
                    sent_info = True
                    self.info("ready notification received (will exit)")
                    self.kill_program(0)
            elif name == "ERRNO":
                sent_info = True
                self.info("error notification ({0}) received from {1}".format(value, self.proc_args[0]))
                self.kill_program(int(value))
            elif name == "STOPPING" and value == "1":
                sent_info = True
                self.info("STOP notification received from {0} (will exit)".format(self.proc_args[0]))
                self.kill_program()

        if not sent_info:
            self.info(self.INFO_MESSAGE.get(name, self.INFO_MESSAGE['default']).
                      format(name, value, ' (ignored but passed on)' if self.proxy_enabled else ' (ignored)'))
                                                                                  
    @asyncio.coroutine
    def _notify_timeout(self):
        self.info("waiting {0} seconds for notification".format(self.timeout))
        yield from asyncio.sleep(self.timeout)
        print("ERROR: Timeout exceeded while waiting for notification from '{0}'".format(self.proc_args[0]))
        self.kill_program(1)

    @asyncio.coroutine
    def _run_process(self):

        self.info('running: {0}'.format(self.proc_args[0]))

        create = asyncio.create_subprocess_exec(*self.proc_args, start_new_session=bool(self.wait_mode))
        proc = yield from create

        if self.timeout:
            asyncio.ensure_future(self._notify_timeout())

        exitcode = yield from proc.wait()
        if not self.exitcode:   # may have arrived from ERRNO
            self.exitcode = exitcode

    @asyncio.coroutine
    def run(self):

        try:
            yield from self.listener.run()
        except ValueError as ex:
            print("Error while trying to create socket: " + str(ex))
            self.kill_program()
        else:
            try:
                yield from self._run_process()
            except Exception as ex:
                print("Error running command: " + str(ex))
                self.kill_program()

        # Command has executed, now determine our exit and proxy disposition

        if not self.wait_mode:
            self.info("program {0} exit({1}), terminating since --wait not specified".format(self.proc_args[0], self.exitcode))
            self.kill_program()
Exemplo n.º 8
0
class SDNotifyExec:

    exitcode = 0
    sockname = None
    listener = None
    parent = None
    timeout = None
    wait_mode = None
    verbose = False

    parent_client = None
    proxy_enabled = True

    INFO_MESSAGE = {
        'READY': "READY={1}{2}",
        'MAINPID': "Process PID (={1}) notification{2}",
        'ERRNO': "Process ERROR (={1}) notification{2}",
        'STATUS': "Status message = '{1}'{2}",
        'default': "{0}={1}{2}",
    }

    def __init__(self, options):
        self.sockname = options['--socket']
        if not self.sockname:
            self.sockname = "/tmp/sdnotify-proxy-{0}.sock".format(os.getpid())

        self.proxy_enabled = parent_socket and not options['--noproxy']
        if options['--wait-stop']:
            self.wait_mode = 'stop'
        elif options['--wait-ready']:
            self.wait_mode = 'ready'

        if options['--timeout'] and self.wait_mode:
            self.timeout = float(options['--timeout'])

        self.verbose = options['--verbose']

        # Modify original environment

        os.environ['NOTIFY_SOCKET'] = self.sockname

        # Set up the environment, reparse the options, build the final command
        Environment.set_parse_parameters('%', '{')
        env = Environment()

        env['PID'] = str(os.getpid())
        env['SOCKET_ARGS'] = options['--template'] or DEFAULT_TEMPLATE
        if parent_socket:
            env['ORIG_NOTIFY_SOCKET'] = parent_socket
        
        env = env.expanded()

        self.proc_args = shlex.split(env.expand(' '.join(maybe_quote(arg) 
                                                         for arg in [options['COMMAND']] + options['ARGS'])))

        self.listener = NotifyListener(self.sockname, 
                                       onNotify = self.notify_received,
                                       onClose = self._parent_closed)
        loop.add_signal_handler(signal.SIGTERM, self._got_sig)
        loop.add_signal_handler(signal.SIGINT, self._got_sig)

        proctitle = '[sdnotify-exec]'

        try:
            from setproctitle import setproctitle
            setproctitle(proctitle)
        except ImportError:
            pass

    def info(self, msg):
        if self.verbose:
            print("info: " + msg)

    def _got_sig(self):
        self.kill_program()

    def kill_program(self, exitcode = None):
        if exitcode is not None:
            self.exitcode = exitcode
        loop.call_soon(self._really_kill)

    def _really_kill(self):
        self.listener.close()
        loop.stop()

    def _parent_closed(self, which, ex):
        if which == self.parent_client:
            self.proxy_enabled = False
            self.parent_client = None

    @asyncio.coroutine
    def _do_proxy_send(self, name, value):
        if not (parent_socket and self.proxy_enabled):
            return

        if not self.parent_client:
            self.parent_client = NotifyClient(parent_socket, onClose = self._parent_closed)
            yield from self.parent_client.run()

        yield from self.parent_client.send("{0}={1}".format(name, value))

    def send_to_proxy(self, name, value):
        asyncio.async(self._do_proxy_send(name, value))

    def notify_received(self, which, name, value):
        self.send_to_proxy(name, value)

        sent_info = False

        if self.wait_mode:
            if name == "READY" and value == "1":
                if self.wait_mode == 'ready':
                    sent_info = True
                    self.info("ready notification received (will exit)")
                    self.kill_program(0)
            elif name == "ERRNO":
                sent_info = True
                self.info("error notification ({0}) received from {1}".format(value, self.proc_args[0]))
                self.kill_program(int(value))
            elif name == "STOPPING" and value == "1":
                sent_info = True
                self.info("STOP notification received from {0} (will exit)".format(self.proc_args[0]))
                self.kill_program()

        if not sent_info:
            self.info(self.INFO_MESSAGE.get(name, self.INFO_MESSAGE['default']).
                      format(name, value, ' (ignored but passed on)' if self.proxy_enabled else ' (ignored)'))
                                                                                  
    @asyncio.coroutine
    def _notify_timeout(self):
        self.info("waiting {0} seconds for notification".format(self.timeout))
        yield from asyncio.sleep(self.timeout)
        print("ERROR: Timeout exceeded while waiting for notification from '{0}'".format(self.proc_args[0]))
        self.kill_program(1)

    @asyncio.coroutine
    def _run_process(self):

        self.info('running: {0}'.format(self.proc_args[0]))

        create = asyncio.create_subprocess_exec(*self.proc_args, start_new_session=bool(self.wait_mode))
        proc = yield from create

        if self.timeout:
            asyncio.async(self._notify_timeout())

        exitcode = yield from proc.wait()
        if not self.exitcode:   # may have arrived from ERRNO
            self.exitcode = exitcode

    @asyncio.coroutine
    def run(self):

        try:
            yield from self.listener.run()
        except ValueError as ex:
            print("Error while trying to create socket: " + str(ex))
            self.kill_program()
        else:
            try:
                yield from self._run_process()
            except Exception as ex:
                print("Error running command: " + str(ex))
                self.kill_program()

        # Command has executed, now determine our exit and proxy disposition

        if not self.wait_mode:
            self.info("program {0} exit({1}), terminating since --wait not specified".format(self.proc_args[0], self.exitcode))
            self.kill_program()