Skip to content

Instantly share code, notes, and snippets.

@corystone
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save corystone/92714263f55bb60009a6 to your computer and use it in GitHub Desktop.
Save corystone/92714263f55bb60009a6 to your computer and use it in GitHub Desktop.
Cinder service maintenance mode patch
diff --git a/cinder/service.py b/cinder/service.py
index 6c557e8..60b4ce8 100644
--- a/cinder/service.py
+++ b/cinder/service.py
@@ -153,11 +153,23 @@ class ProcessLauncher(object):
self.totalwrap = 0
self.failedwrap = 0
self.running = True
+ self.maintenance = False
rfd, self.writepipe = os.pipe()
self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
signal.signal(signal.SIGTERM, self._handle_signal)
signal.signal(signal.SIGINT, self._handle_signal)
+ signal.signal(signal.SIGUSR1, self._handle_sigusr1)
+
+ def _handle_sigusr1(self, signo, frame):
+ self.maintenance = True
+ for pid in self.children:
+ # Pass USR1 on to our children.
+ try:
+ os.kill(pid, signal.SIGUSR1)
+ except OSError as exc:
+ if exc.errno != errno.ESRCH:
+ raise
def _handle_signal(self, signo, frame):
self.sigcaught = signo
@@ -302,6 +314,9 @@ class ProcessLauncher(object):
eventlet.greenthread.sleep(.01)
continue
+ if self.maintenance:
+ break
+
LOG.info(_('wait wrap.failed %s'), wrap.failed)
while (self.running and len(wrap.children) < wrap.workers
and not wrap.failed):
@@ -312,12 +327,12 @@ class ProcessLauncher(object):
signal.SIGINT: 'SIGINT'}[self.sigcaught]
LOG.info(_('Caught %s, stopping children'), signame)
- for pid in self.children:
- try:
- os.kill(pid, signal.SIGTERM)
- except OSError as exc:
- if exc.errno != errno.ESRCH:
- raise
+ for pid in self.children:
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except OSError as exc:
+ if exc.errno != errno.ESRCH:
+ raise
# Wait for children to die
if self.children:
@@ -352,6 +367,35 @@ class Service(object):
self.saved_args, self.saved_kwargs = args, kwargs
self.timers = []
+ def maintenance_periodic(self):
+ """If no eventlet timers exist, assume done, so shut down."""
+ LOG.info(_('Maintenance, waiting for all eventlets to finish'))
+ hub = eventlet.hubs.get_hub()
+ if len(hub.timers) == 0:
+ LOG.info(_('No eventlet timers, exiting'))
+ self.stop()
+
+ def _handle_sigusr1(self, signo, frame):
+ """Handle SIGUSR1 to enter maintenance mode.
+
+ Stop processing new rpc messages. Stop running periodic tasks. The
+ service will keep running until it has no more RPC calls executing,
+ then it will cleanly exit.
+ """
+ try:
+ self.conn.close()
+ except Exception:
+ pass
+ for x in self.timers:
+ try:
+ x.stop()
+ except Exception:
+ pass
+ # This periodic task just exists to keep the manager running.
+ periodic = utils.LoopingCall(self.maintenance_periodic)
+ periodic.start(interval=10, initial_delay=0)
+ self.timers.append(periodic)
+
def start(self):
version_string = version.version_string()
LOG.audit(_('Starting %(topic)s node (version %(version_string)s)'),
@@ -401,6 +445,8 @@ class Service(object):
initial_delay=initial_delay)
self.timers.append(periodic)
+ signal.signal(signal.SIGUSR1, self._handle_sigusr1)
+
def _create_service_ref(self, context):
zone = CONF.storage_availability_zone
service_ref = db.service_create(context,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment