diff --git a/bin/heat-api b/bin/heat-api index 3f9eeeabd..3bfa8e757 100755 --- a/bin/heat-api +++ b/bin/heat-api @@ -39,6 +39,7 @@ from oslo.config import cfg from heat.common import config from heat.common import wsgi +from heat.common import notify from heat.openstack.common import log as logging @@ -61,6 +62,7 @@ if __name__ == '__main__': LOG.info('Starting Heat ReST API on %s:%s' % (host, port)) server = wsgi.Server() server.start(app, cfg.CONF.heat_api, default_port=port) + notify.startup_notify(cfg.CONF.onready) server.wait() except RuntimeError as e: sys.exit("ERROR: %s" % e) diff --git a/bin/heat-api-cfn b/bin/heat-api-cfn index 03a14028b..8d0371c3e 100755 --- a/bin/heat-api-cfn +++ b/bin/heat-api-cfn @@ -41,6 +41,7 @@ from oslo.config import cfg from heat.common import config from heat.common import wsgi +from heat.common import notify from heat.openstack.common import log as logging @@ -63,6 +64,7 @@ if __name__ == '__main__': LOG.info('Starting Heat API on %s:%s' % (host, port)) server = wsgi.Server() server.start(app, cfg.CONF.heat_api_cfn, default_port=port) + notify.startup_notify(cfg.CONF.onready) server.wait() except RuntimeError as e: sys.exit("ERROR: %s" % e) diff --git a/bin/heat-api-cloudwatch b/bin/heat-api-cloudwatch index 9100e878e..76f123cf5 100755 --- a/bin/heat-api-cloudwatch +++ b/bin/heat-api-cloudwatch @@ -41,6 +41,7 @@ from oslo.config import cfg from heat.common import config from heat.common import wsgi +from heat.common import notify from heat.openstack.common import log as logging @@ -63,6 +64,7 @@ if __name__ == '__main__': LOG.info('Starting Heat CloudWatch API on %s:%s' % (host, port)) server = wsgi.Server() server.start(app, cfg.CONF.heat_api_cloudwatch, default_port=port) + notify.startup_notify(cfg.CONF.onready) server.wait() except RuntimeError as e: sys.exit("ERROR: %s" % e) diff --git a/bin/heat-engine b/bin/heat-engine index 6f05e167c..c25ec5d26 100755 --- a/bin/heat-engine +++ b/bin/heat-engine @@ -40,6 +40,8 @@ gettextutils.install('heat', lazy=False) from oslo.config import cfg +from heat.common import notify + from heat.openstack.common import log as logging from heat.openstack.common import service @@ -63,4 +65,5 @@ if __name__ == '__main__': srv = engine.EngineService(cfg.CONF.host, rpc_api.ENGINE_TOPIC) launcher = service.launch(srv) + notify.startup_notify(cfg.CONF.onready) launcher.wait() diff --git a/etc/heat/heat.conf.sample b/etc/heat/heat.conf.sample index 896925ba8..7f1663c24 100644 --- a/etc/heat/heat.conf.sample +++ b/etc/heat/heat.conf.sample @@ -58,6 +58,14 @@ # stack locking. (integer value) #engine_life_check_timeout=2 +# onready allows you to send a notification when the heat +# processes are ready to serve. This is either a module with +# the notify() method or a shell command. To enable +# notifications with systemd, one may use the 'systemd-notify +# --ready' shell command or the 'heat.common.systemd' +# notification module. (string value) +#onready= + # Name of the engine node. This can be an opaque identifier. # It is not necessarily a hostname, FQDN, or IP address. # (string value) diff --git a/heat/common/config.py b/heat/common/config.py index 1cfb50fdd..31d348e50 100644 --- a/heat/common/config.py +++ b/heat/common/config.py @@ -118,7 +118,14 @@ engine_opts = [ cfg.IntOpt('engine_life_check_timeout', default=2, help=_('RPC timeout for the engine liveness check that is used' - ' for stack locking.'))] + ' for stack locking.')), + cfg.StrOpt('onready', + help=_('onready allows you to send a notification when the' + ' heat processes are ready to serve. This is either a' + ' module with the notify() method or a shell command. ' + ' To enable notifications with systemd, one may use' + ' the \'systemd-notify --ready\' shell command or' + ' the \'heat.common.systemd\' notification module.'))] rpc_opts = [ cfg.StrOpt('host', diff --git a/heat/common/notify.py b/heat/common/notify.py new file mode 100644 index 000000000..d168482c4 --- /dev/null +++ b/heat/common/notify.py @@ -0,0 +1,38 @@ +# Copyright 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. + +""" +Startup notification using a shell script or systemd NOTIFY_SOCKET +style notification +""" + +from heat.openstack.common import log as logging +from heat.openstack.common import importutils +from heat.openstack.common import processutils + +logger = logging.getLogger(__name__) + + +def startup_notify(notify_param): + if not notify_param or notify_param == "": + return + try: + notifier = importutils.import_module(notify_param) + except ImportError: + try: + processutils.execute(notify_param, shell=True) + except Exception as e: + logger.error(_('Failed to execute onready command: %s') % str(e)) + else: + notifier.notify() diff --git a/heat/common/systemd.py b/heat/common/systemd.py new file mode 100644 index 000000000..f094dd777 --- /dev/null +++ b/heat/common/systemd.py @@ -0,0 +1,44 @@ +# 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 heat.conf +""" + +import os +import socket + +from heat.openstack.common import log as logging + +logger = logging.getLogger(__name__) + + +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() + else: + logger.warning(_('Unable to notify systemd of startup completion:' + ' NOTIFY_SOCKET not set')) + + +def notify(): + _sd_notify('READY=1')