diff --git a/bin/keystone-all b/bin/keystone-all index fa14bbb819..3214d13547 100755 --- a/bin/keystone-all +++ b/bin/keystone-all @@ -48,7 +48,7 @@ from keystone.common import sql from keystone.common import utils from keystone import config from keystone.openstack.common.gettextutils import _ -from keystone.openstack.common import importutils +from keystone.openstack.common import systemd CONF = config.CONF @@ -83,15 +83,7 @@ def serve(*servers): raise # notify calling process we are ready to serve - if CONF.onready: - try: - notifier = importutils.import_module(CONF.onready) - notifier.notify() - except ImportError: - try: - utils.check_output(CONF.onready.split()) - except Exception: - logging.exception('Failed to execute onready command') + systemd.notify_once() for name, server in servers: server.wait() diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample index 14a0ef0130..96369b580c 100644 --- a/etc/keystone.conf.sample +++ b/etc/keystone.conf.sample @@ -54,13 +54,6 @@ # server. (string value) #admin_endpoint= -# onready allows you to send a notification when the process -# is ready to serve. For example, to have it notify using -# systemd, one could set shell command: "onready = systemd- -# notify --ready" or a module with notify() method: "onready = -# keystone.common.systemd". (string value) -#onready= - # Enforced by optional sizelimit middleware # (keystone.middleware:RequestBodySizeLimiter). (integer # value) diff --git a/keystone/common/config.py b/keystone/common/config.py index 830bf7e7e7..f7d6635e66 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -69,13 +69,6 @@ FILE_OPTIONS = { 'to set this value if the base URL contains a path ' '(e.g. /prefix/v2.0) or the endpoint should be found ' 'on a different server.'), - cfg.StrOpt('onready', - help='onready allows you to send a notification when the ' - 'process is ready to serve. For example, to have it ' - 'notify using systemd, one could set shell command: ' - '"onready = systemd-notify --ready" or a module ' - 'with notify() method: ' - '"onready = keystone.common.systemd".'), # default max request size is 112k cfg.IntOpt('max_request_body_size', default=114688, help='Enforced by optional sizelimit middleware ' diff --git a/keystone/common/systemd.py b/keystone/common/systemd.py deleted file mode 100644 index 807b24184d..0000000000 --- a/keystone/common/systemd.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Helper module for systemd start-up completion notification. -Used for "onready" configuration parameter in keystone.conf -""" - -import os -import socket - - -def _sd_notify(msg): - sysd = os.getenv('NOTIFY_SOCKET') - if sysd: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - if sysd.startswith('@'): - # abstract namespace socket - sysd = '\0%s' % sysd[1:] - sock.connect(sysd) - sock.sendall(msg) - sock.close() - - -def notify(): - _sd_notify('READY=1') - - -if __name__ == '__main__': - notify() diff --git a/keystone/openstack/common/systemd.py b/keystone/openstack/common/systemd.py new file mode 100644 index 0000000000..d08548b1a5 --- /dev/null +++ b/keystone/openstack/common/systemd.py @@ -0,0 +1,104 @@ +# Copyright 2012-2014 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Helper module for systemd service readiness notification. +""" + +import os +import socket +import sys + +from keystone.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) + + +def _abstractify(socket_name): + if socket_name.startswith('@'): + # abstract namespace socket + socket_name = '\0%s' % socket_name[1:] + return socket_name + + +def _sd_notify(unset_env, msg): + notify_socket = os.getenv('NOTIFY_SOCKET') + if notify_socket: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + try: + sock.connect(_abstractify(notify_socket)) + sock.sendall(msg) + if unset_env: + del os.environ['NOTIFY_SOCKET'] + except EnvironmentError: + LOG.debug("Systemd notification failed", exc_info=True) + finally: + sock.close() + + +def notify(): + """Send notification to Systemd that service is ready. + For details see + http://www.freedesktop.org/software/systemd/man/sd_notify.html + """ + _sd_notify(False, 'READY=1') + + +def notify_once(): + """Send notification once to Systemd that service is ready. + Systemd sets NOTIFY_SOCKET environment variable with the name of the + socket listening for notifications from services. + This method removes the NOTIFY_SOCKET environment variable to ensure + notification is sent only once. + """ + _sd_notify(True, 'READY=1') + + +def onready(notify_socket, timeout): + """Wait for systemd style notification on the socket. + + :param notify_socket: local socket address + :type notify_socket: string + :param timeout: socket timeout + :type timeout: float + :returns: 0 service ready + 1 service not ready + 2 timeout occured + """ + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock.settimeout(timeout) + sock.bind(_abstractify(notify_socket)) + try: + msg = sock.recv(512) + except socket.timeout: + return 2 + finally: + sock.close() + if 'READY=1' in msg: + return 0 + else: + return 1 + + +if __name__ == '__main__': + # simple CLI for testing + if len(sys.argv) == 1: + notify() + elif len(sys.argv) >= 2: + timeout = float(sys.argv[1]) + notify_socket = os.getenv('NOTIFY_SOCKET') + if notify_socket: + retval = onready(notify_socket, timeout) + sys.exit(retval) diff --git a/openstack-common.conf b/openstack-common.conf index ec9ef2a299..9f24bc3404 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -13,6 +13,7 @@ module=jsonutils module=log module=policy module=strutils +module=systemd module=timeutils module=versionutils