Blob Blame History Raw
From bacd0815c3f778507090a045f0bf8188933653b2 Mon Sep 17 00:00:00 2001
From: Alan Pevec <apevec@redhat.com>
Date: Sun, 9 Mar 2014 23:27:46 +0100
Subject: [PATCH] Refactor service readiness notification

import and apply Oslo systemd module
drop old keystone.common.systemd
drop onready configuration parameter
 - systemd notification is no-op when not running inside systemd

Oslo commit 53e1214c092f09e3851b1a1b55289a93a72b09ec

Conflicts:
        bin/keystone-all
        etc/keystone.conf.sample
        keystone/common/config.py

Change-Id: I80f325c9be9c171c2dc8d5526570bf64f0f87c78
(cherry picked from commit c045ea108d7246ccddcc8c540b6057b2105bfd07)
---
 bin/keystone-all                     |  12 +---
 etc/keystone.conf.sample             |   7 ---
 keystone/common/config.py            |   7 ---
 keystone/common/systemd.py           |  41 --------------
 keystone/openstack/common/systemd.py | 104 +++++++++++++++++++++++++++++++++++
 openstack-common.conf                |   1 +
 6 files changed, 107 insertions(+), 65 deletions(-)
 delete mode 100644 keystone/common/systemd.py
 create mode 100644 keystone/openstack/common/systemd.py

diff --git a/bin/keystone-all b/bin/keystone-all
index 315f3c1..e541f74 100755
--- a/bin/keystone-all
+++ b/bin/keystone-all
@@ -46,7 +46,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
 from keystone import service
 
 
@@ -82,15 +82,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 3aa1314..4ef5d91 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -56,13 +56,6 @@
 # a different server.
 #admin_endpoint=http://localhost:%(admin_port)s/
 
-# 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=<None>
-
 # enforced by optional sizelimit middleware
 # (keystone.middleware:RequestBodySizeLimiter). (integer
 # value)
diff --git a/keystone/common/config.py b/keystone/common/config.py
index 85c49f8..a89f7e2 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 '
                         '(eg /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 807b241..0000000
--- 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 0000000..d08548b
--- /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 0823b23..f9785ab 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -13,6 +13,7 @@ module=log
 module=log_handler
 module=policy
 module=strutils
+module=systemd
 module=timeutils
 
 # The base module to hold the copy of openstack.common