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)
def wait_for_pidfile(self): """ If the pidfile option was specified, then wait until we find a valid pidfile, and register the new PID. This is not done automatically, but is implemented here as a utility for process types that need it. """ if not self.pidfile: return self.logdebug("{0} waiting for PID file: {1}".format( self.name, self.pidfile)) pidsleep = 0.02 # work incrementally up to no more than process_timeout minsleep = 3 expires = time() + self.process_timeout last_ex = None while time() < expires: if not self.family.system_alive: return yield from asyncio.sleep(pidsleep) # ramp up until we hit the minsleep ceiling pidsleep = min(pidsleep * 2, minsleep) try: newpid = int(open(self.pidfile, 'r').read().strip()) except FileNotFoundError: continue except Exception as ex: # Don't raise this immediately. The service may create the file before writing the PID. last_ex = ChProcessError( "{0} found pid file '{1}' but contents did not contain an integer" .format(self.name, self.pidfile), errno=errno.EINVAL) continue self.pid = newpid return if last_ex is not None: raise last_ex raise ChProcessError( "{0} did not find pid file '{1}' before {2}sec process_timeout expired" .format(self.name, self.pidfile, self.process_timeout), errno=errno.ENOENT)
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)
def process_started_co(self): result = yield from self.timed_wait(self.process_timeout, self._exit_timeout) if result is not None and not result.normal_exit: if self.ignore_failures: warn("{0} (ignored) failure on start-up with result '{1}'". format(self.name, result)) else: raise ChProcessError( "{0} failed on start-up with result '{1}'".format( self.name, result), resultcode=result)
def pid(self, newpid): if self._pid is not None and newpid is not None and self._pid is not newpid: self.logdebug("{0} changing PID to {1} (from {2})", self.name, newpid, self._pid) try: pgid = os.getpgid(newpid) except ProcessLookupError as ex: raise ChProcessError( "{0} attempted to attach the process with PID={1} but there is no such process" .format(self.name, newpid), errno=ex.errno) self._attach_pid(newpid) self._pid = newpid
def do_startup_pause(self): """ Wait a short time just to see if the process errors out immediately. This avoids a retry loop and catches any immediate failures now. Can be used by process implementations if needed. """ if not self.startup_pause: return try: result = yield from self.timed_wait(self.startup_pause) except asyncio.TimeoutError: result = None if result is not None and not result.normal_exit: if self.ignore_failures: warn("{0} (ignored) failure on start-up with result '{1}'". format(self.name, result)) else: raise ChProcessError( "{0} failed on start-up with result '{1}'".format( self.name, result), resultcode=result)