1 From f0bbb2c80e84848967885599d8b57f0710d7d44c Mon Sep 17 00:00:00 2001
2 From: Jakub Libosvar <libosvar@redhat.com>
3 Date: Mon, 17 Mar 2014 16:36:01 +0100
4 Subject: [PATCH] Sync service and systemd modules from oslo-incubator
6 This patch make systemd know when neutron-service was started. This is
7 needed in HA environment, previously systemd returned success even
8 before neutron-server was able to handle requests.
10 Current oslo-incubator commit on HEAD:
11 b7ad6ddab8b1d61bf4f52ccaa461a9d68809747b
13 Implements: blueprint service-readiness
14 Change-Id: Ic9e4abd11b614a896fbd7454b9a604a69a248d0f
16 neutron/openstack/common/service.py | 72 +++++++++++++------------
17 neutron/openstack/common/systemd.py | 104 ++++++++++++++++++++++++++++++++++++
18 openstack-common.conf | 1 +
19 3 files changed, 144 insertions(+), 33 deletions(-)
20 create mode 100644 neutron/openstack/common/systemd.py
22 diff --git a/neutron/openstack/common/service.py b/neutron/openstack/common/service.py
23 index b8144bb..627dda4 100644
24 --- a/neutron/openstack/common/service.py
25 +++ b/neutron/openstack/common/service.py
26 @@ -38,9 +38,10 @@ from eventlet import event
27 from oslo.config import cfg
29 from neutron.openstack.common import eventlet_backdoor
30 -from neutron.openstack.common.gettextutils import _
31 +from neutron.openstack.common.gettextutils import _LE, _LI, _LW
32 from neutron.openstack.common import importutils
33 from neutron.openstack.common import log as logging
34 +from neutron.openstack.common import systemd
35 from neutron.openstack.common import threadgroup
38 @@ -163,7 +164,7 @@ class ServiceLauncher(Launcher):
42 - LOG.debug(_('Full set of CONF:'))
43 + LOG.debug('Full set of CONF:')
44 CONF.log_opt_values(LOG, std_logging.DEBUG)
47 @@ -172,7 +173,7 @@ class ServiceLauncher(Launcher):
48 super(ServiceLauncher, self).wait()
49 except SignalExit as exc:
50 signame = _signo_to_signame(exc.signo)
51 - LOG.info(_('Caught %s, exiting'), signame)
52 + LOG.info(_LI('Caught %s, exiting'), signame)
55 except SystemExit as exc:
56 @@ -184,7 +185,7 @@ class ServiceLauncher(Launcher):
59 # We're shutting down, so it doesn't matter at this point.
60 - LOG.exception(_('Exception during rpc cleanup.'))
61 + LOG.exception(_LE('Exception during rpc cleanup.'))
65 @@ -235,7 +236,7 @@ class ProcessLauncher(object):
69 - LOG.info(_('Parent process has died unexpectedly, exiting'))
70 + LOG.info(_LI('Parent process has died unexpectedly, exiting'))
74 @@ -266,13 +267,13 @@ class ProcessLauncher(object):
76 except SignalExit as exc:
77 signame = _signo_to_signame(exc.signo)
78 - LOG.info(_('Caught %s, exiting'), signame)
79 + LOG.info(_LI('Caught %s, exiting'), signame)
82 except SystemExit as exc:
85 - LOG.exception(_('Unhandled exception'))
86 + LOG.exception(_LE('Unhandled exception'))
90 @@ -305,7 +306,7 @@ class ProcessLauncher(object):
91 # start up quickly but ensure we don't fork off children that
92 # die instantly too quickly.
93 if time.time() - wrap.forktimes[0] < wrap.workers:
94 - LOG.info(_('Forking too fast, sleeping'))
95 + LOG.info(_LI('Forking too fast, sleeping'))
99 @@ -324,7 +325,7 @@ class ProcessLauncher(object):
103 - LOG.info(_('Started child %d'), pid)
104 + LOG.info(_LI('Started child %d'), pid)
106 wrap.children.add(pid)
107 self.children[pid] = wrap
108 @@ -334,7 +335,7 @@ class ProcessLauncher(object):
109 def launch_service(self, service, workers=1):
110 wrap = ServiceWrapper(service, workers)
112 - LOG.info(_('Starting %d workers'), wrap.workers)
113 + LOG.info(_LI('Starting %d workers'), wrap.workers)
114 while self.running and len(wrap.children) < wrap.workers:
115 self._start_child(wrap)
117 @@ -351,15 +352,15 @@ class ProcessLauncher(object):
119 if os.WIFSIGNALED(status):
120 sig = os.WTERMSIG(status)
121 - LOG.info(_('Child %(pid)d killed by signal %(sig)d'),
122 + LOG.info(_LI('Child %(pid)d killed by signal %(sig)d'),
123 dict(pid=pid, sig=sig))
125 code = os.WEXITSTATUS(status)
126 - LOG.info(_('Child %(pid)s exited with status %(code)d'),
127 + LOG.info(_LI('Child %(pid)s exited with status %(code)d'),
128 dict(pid=pid, code=code))
130 if pid not in self.children:
131 - LOG.warning(_('pid %d not in child list'), pid)
132 + LOG.warning(_LW('pid %d not in child list'), pid)
135 wrap = self.children.pop(pid)
136 @@ -381,22 +382,25 @@ class ProcessLauncher(object):
138 """Loop waiting on children to die and respawning as necessary."""
140 - LOG.debug(_('Full set of CONF:'))
141 + LOG.debug('Full set of CONF:')
142 CONF.log_opt_values(LOG, std_logging.DEBUG)
145 - self.handle_signal()
146 - self._respawn_children()
148 - signame = _signo_to_signame(self.sigcaught)
149 - LOG.info(_('Caught %s, stopping children'), signame)
150 - if not _is_sighup_and_daemon(self.sigcaught):
153 - for pid in self.children:
154 - os.kill(pid, signal.SIGHUP)
155 - self.running = True
156 - self.sigcaught = None
159 + self.handle_signal()
160 + self._respawn_children()
162 + signame = _signo_to_signame(self.sigcaught)
163 + LOG.info(_LI('Caught %s, stopping children'), signame)
164 + if not _is_sighup_and_daemon(self.sigcaught):
167 + for pid in self.children:
168 + os.kill(pid, signal.SIGHUP)
169 + self.running = True
170 + self.sigcaught = None
171 + except eventlet.greenlet.GreenletExit:
172 + LOG.info(_LI("Wait called after thread killed. Cleaning up."))
174 for pid in self.children:
176 @@ -407,7 +411,7 @@ class ProcessLauncher(object):
178 # Wait for children to die
180 - LOG.info(_('Waiting on %d children to exit'), len(self.children))
181 + LOG.info(_LI('Waiting on %d children to exit'), len(self.children))
185 @@ -484,14 +488,16 @@ class Services(object):
189 + systemd.notify_once()
193 -def launch(service, workers=None):
195 - launcher = ProcessLauncher()
196 - launcher.launch_service(service, workers=workers)
198 +def launch(service, workers=1):
199 + if workers is None or workers == 1:
200 launcher = ServiceLauncher()
201 launcher.launch_service(service)
203 + launcher = ProcessLauncher()
204 + launcher.launch_service(service, workers=workers)
207 diff --git a/neutron/openstack/common/systemd.py b/neutron/openstack/common/systemd.py
209 index 0000000..e1ba656
211 +++ b/neutron/openstack/common/systemd.py
213 +# Copyright 2012-2014 Red Hat, Inc.
215 +# Licensed under the Apache License, Version 2.0 (the "License"); you may
216 +# not use this file except in compliance with the License. You may obtain
217 +# a copy of the License at
219 +# http://www.apache.org/licenses/LICENSE-2.0
221 +# Unless required by applicable law or agreed to in writing, software
222 +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
223 +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
224 +# License for the specific language governing permissions and limitations
225 +# under the License.
228 +Helper module for systemd service readiness notification.
235 +from neutron.openstack.common import log as logging
238 +LOG = logging.getLogger(__name__)
241 +def _abstractify(socket_name):
242 + if socket_name.startswith('@'):
243 + # abstract namespace socket
244 + socket_name = '\0%s' % socket_name[1:]
248 +def _sd_notify(unset_env, msg):
249 + notify_socket = os.getenv('NOTIFY_SOCKET')
251 + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
253 + sock.connect(_abstractify(notify_socket))
256 + del os.environ['NOTIFY_SOCKET']
257 + except EnvironmentError:
258 + LOG.debug("Systemd notification failed", exc_info=True)
264 + """Send notification to Systemd that service is ready.
266 + http://www.freedesktop.org/software/systemd/man/sd_notify.html
268 + _sd_notify(False, 'READY=1')
272 + """Send notification once to Systemd that service is ready.
273 + Systemd sets NOTIFY_SOCKET environment variable with the name of the
274 + socket listening for notifications from services.
275 + This method removes the NOTIFY_SOCKET environment variable to ensure
276 + notification is sent only once.
278 + _sd_notify(True, 'READY=1')
281 +def onready(notify_socket, timeout):
282 + """Wait for systemd style notification on the socket.
284 + :param notify_socket: local socket address
285 + :type notify_socket: string
286 + :param timeout: socket timeout
287 + :type timeout: float
288 + :returns: 0 service ready
289 + 1 service not ready
292 + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
293 + sock.settimeout(timeout)
294 + sock.bind(_abstractify(notify_socket))
296 + msg = sock.recv(512)
297 + except socket.timeout:
301 + if 'READY=1' in msg:
307 +if __name__ == '__main__':
308 + # simple CLI for testing
309 + if len(sys.argv) == 1:
311 + elif len(sys.argv) >= 2:
312 + timeout = float(sys.argv[1])
313 + notify_socket = os.getenv('NOTIFY_SOCKET')
315 + retval = onready(notify_socket, timeout)
317 diff --git a/openstack-common.conf b/openstack-common.conf
318 index 9523f9c..395576f 100644
319 --- a/openstack-common.conf
320 +++ b/openstack-common.conf
321 @@ -26,6 +26,7 @@ module=processutils