summaryrefslogtreecommitdiffstats
path: root/openstack/common
diff options
context:
space:
mode:
authorFengqian.Gao <fengqian.gao@intel.com>2013-06-17 17:17:35 +0800
committerFengqian.Gao <fengqian.gao@intel.com>2013-07-25 17:59:36 +0800
commit825ace5581fbb416944acae62f51c489ed93b9c9 (patch)
tree16f22e5d54e2343d6ac8dd5bb8519c8accd266aa /openstack/common
parentaafc227283042592b560b0a38d0ea7c39b402c50 (diff)
downloadoslo-825ace5581fbb416944acae62f51c489ed93b9c9.tar.gz
oslo-825ace5581fbb416944acae62f51c489ed93b9c9.tar.xz
oslo-825ace5581fbb416944acae62f51c489ed93b9c9.zip
Add service restart function in oslo-incubator
Add signal handler of SIGHUP and restart function for service to restart. It depends on blueprint cfg-reload-config-files of oslo.config. Implements blueprint service-restart Change-Id: Ib68fc5fb6038c4855d35464573c8866776493f6c
Diffstat (limited to 'openstack/common')
-rw-r--r--openstack/common/service.py138
1 files changed, 106 insertions, 32 deletions
diff --git a/openstack/common/service.py b/openstack/common/service.py
index cb71af2..6da9751 100644
--- a/openstack/common/service.py
+++ b/openstack/common/service.py
@@ -81,6 +81,15 @@ class Launcher(object):
"""
self.services.wait()
+ def restart(self):
+ """Reload config files and restart service.
+
+ :returns: None
+
+ """
+ cfg.CONF.reload_config_files()
+ self.services.restart()
+
class SignalExit(SystemExit):
def __init__(self, signo, exccode=1):
@@ -93,24 +102,31 @@ class ServiceLauncher(Launcher):
# Allow the process to be killed again and die from natural causes
signal.signal(signal.SIGTERM, signal.SIG_DFL)
signal.signal(signal.SIGINT, signal.SIG_DFL)
+ signal.signal(signal.SIGHUP, signal.SIG_DFL)
raise SignalExit(signo)
- def wait(self):
+ def handle_signal(self):
signal.signal(signal.SIGTERM, self._handle_signal)
signal.signal(signal.SIGINT, self._handle_signal)
+ signal.signal(signal.SIGHUP, self._handle_signal)
+
+ def _wait_for_exit_or_signal(self):
+ status = None
+ signo = 0
LOG.debug(_('Full set of CONF:'))
CONF.log_opt_values(LOG, std_logging.DEBUG)
- status = None
try:
super(ServiceLauncher, self).wait()
except SignalExit as exc:
signame = {signal.SIGTERM: 'SIGTERM',
- signal.SIGINT: 'SIGINT'}[exc.signo]
+ signal.SIGINT: 'SIGINT',
+ signal.SIGHUP: 'SIGHUP'}[exc.signo]
LOG.info(_('Caught %s, exiting'), signame)
status = exc.code
+ signo = exc.signo
except SystemExit as exc:
status = exc.code
finally:
@@ -121,7 +137,16 @@ class ServiceLauncher(Launcher):
except Exception:
# We're shutting down, so it doesn't matter at this point.
LOG.exception(_('Exception during rpc cleanup.'))
- return status
+
+ return status, signo
+
+ def wait(self):
+ while True:
+ self.handle_signal()
+ status, signo = self._wait_for_exit_or_signal()
+ if signo != signal.SIGHUP:
+ return status
+ self.restart()
class ServiceWrapper(object):
@@ -139,9 +164,12 @@ class ProcessLauncher(object):
self.running = True
rfd, self.writepipe = os.pipe()
self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
+ self.handle_signal()
+ def handle_signal(self):
signal.signal(signal.SIGTERM, self._handle_signal)
signal.signal(signal.SIGINT, self._handle_signal)
+ signal.signal(signal.SIGHUP, self._handle_signal)
def _handle_signal(self, signo, frame):
self.sigcaught = signo
@@ -150,6 +178,7 @@ class ProcessLauncher(object):
# Allow the process to be killed again and die from natural causes
signal.signal(signal.SIGTERM, signal.SIG_DFL)
signal.signal(signal.SIGINT, signal.SIG_DFL)
+ signal.signal(signal.SIGHUP, signal.SIG_DFL)
def _pipe_watcher(self):
# This will block until the write end is closed when the parent
@@ -160,16 +189,47 @@ class ProcessLauncher(object):
sys.exit(1)
- def _child_process(self, service):
+ def _child_process_handle_signal(self):
# Setup child signal handlers differently
def _sigterm(*args):
signal.signal(signal.SIGTERM, signal.SIG_DFL)
raise SignalExit(signal.SIGTERM)
+ def _sighup(*args):
+ signal.signal(signal.SIGHUP, signal.SIG_DFL)
+ raise SignalExit(signal.SIGHUP)
+
signal.signal(signal.SIGTERM, _sigterm)
+ signal.signal(signal.SIGHUP, _sighup)
# Block SIGINT and let the parent send us a SIGTERM
signal.signal(signal.SIGINT, signal.SIG_IGN)
+ def _child_wait_for_exit_or_signal(self, launcher):
+ status = None
+ signo = 0
+
+ try:
+ launcher.wait()
+ except SignalExit as exc:
+ signame = {signal.SIGTERM: 'SIGTERM',
+ signal.SIGINT: 'SIGINT',
+ signal.SIGHUP: 'SIGHUP'}[exc.signo]
+ LOG.info(_('Caught %s, exiting'), signame)
+ status = exc.code
+ signo = exc.signo
+ except SystemExit as exc:
+ status = exc.code
+ except BaseException:
+ LOG.exception(_('Unhandled exception'))
+ status = 2
+ finally:
+ launcher.stop()
+
+ return status, signo
+
+ def _child_process(self, service):
+ self._child_process_handle_signal()
+
# Reopen the eventlet hub to make sure we don't share an epoll
# fd with parent and/or siblings, which would be bad
eventlet.hubs.use_hub()
@@ -184,7 +244,7 @@ class ProcessLauncher(object):
launcher = Launcher()
launcher.launch_service(service)
- launcher.wait()
+ return launcher
def _start_child(self, wrap):
if len(wrap.forktimes) > wrap.workers:
@@ -205,21 +265,13 @@ class ProcessLauncher(object):
# NOTE(johannes): All exceptions are caught to ensure this
# doesn't fallback into the loop spawning children. It would
# be bad for a child to spawn more children.
- status = 0
- try:
- self._child_process(wrap.service)
- except SignalExit as exc:
- signame = {signal.SIGTERM: 'SIGTERM',
- signal.SIGINT: 'SIGINT'}[exc.signo]
- LOG.info(_('Caught %s, exiting'), signame)
- status = exc.code
- except SystemExit as exc:
- status = exc.code
- except BaseException:
- LOG.exception(_('Unhandled exception'))
- status = 2
- finally:
- wrap.service.stop()
+ launcher = self._child_process(wrap.service)
+ while True:
+ self._child_process_handle_signal()
+ status, signo = self._child_wait_for_exit_or_signal(launcher)
+ if signo != signal.SIGHUP:
+ break
+ launcher.restart()
os._exit(status)
@@ -265,12 +317,7 @@ class ProcessLauncher(object):
wrap.children.remove(pid)
return wrap
- def wait(self):
- """Loop waiting on children to die and respawning as necessary."""
-
- LOG.debug(_('Full set of CONF:'))
- CONF.log_opt_values(LOG, std_logging.DEBUG)
-
+ def _respawn_children(self):
while self.running:
wrap = self._wait_child()
if not wrap:
@@ -279,14 +326,30 @@ class ProcessLauncher(object):
# (see bug #1095346)
eventlet.greenthread.sleep(.01)
continue
-
while self.running and len(wrap.children) < wrap.workers:
self._start_child(wrap)
- if self.sigcaught:
- signame = {signal.SIGTERM: 'SIGTERM',
- signal.SIGINT: 'SIGINT'}[self.sigcaught]
- LOG.info(_('Caught %s, stopping children'), signame)
+ def wait(self):
+ """Loop waiting on children to die and respawning as necessary."""
+
+ LOG.debug(_('Full set of CONF:'))
+ CONF.log_opt_values(LOG, std_logging.DEBUG)
+
+ while True:
+ self.handle_signal()
+ self._respawn_children()
+ if self.sigcaught:
+ signame = {signal.SIGTERM: 'SIGTERM',
+ signal.SIGINT: 'SIGINT',
+ signal.SIGHUP: 'SIGHUP'}[self.sigcaught]
+ LOG.info(_('Caught %s, stopping children'), signame)
+ if self.sigcaught != signal.SIGHUP:
+ break
+
+ for pid in self.children:
+ os.kill(pid, signal.SIGHUP)
+ self.running = True
+ self.sigcaught = None
for pid in self.children:
try:
@@ -311,6 +374,10 @@ class Service(object):
# signal that the service is done shutting itself down:
self._done = event.Event()
+ def reset(self):
+ # NOTE(Fengqian): docs for Event.reset() recommend against using it
+ self._done = event.Event()
+
def start(self):
pass
@@ -353,6 +420,13 @@ class Services(object):
def wait(self):
self.tg.wait()
+ def restart(self):
+ self.stop()
+ self.done = event.Event()
+ for restart_service in self.services:
+ restart_service.reset()
+ self.tg.add_thread(self.run_service, restart_service, self.done)
+
@staticmethod
def run_service(service, done):
"""Service start wrapper.