From c36a01a43994f7f4a8cb0f44bd0859d41cea83cd Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Thu, 11 Apr 2019 17:02:58 +0200 Subject: [PATCH] Publish baremetal endpoint via mdns This change adds an option to publish the endpoint via mDNS on start up and clean it up on tear down. Story: #2005393 Task: #30383 Change-Id: I55d2e7718a23cde111eaac4e431588184cb16bda --- ironic/conductor/base_manager.py | 20 +++++++++++ ironic/conf/conductor.py | 3 ++ .../tests/unit/conductor/test_base_manager.py | 36 +++++++++++++++++++ lower-constraints.txt | 2 +- releasenotes/notes/mdns-a5f4034257139e31.yaml | 6 ++++ requirements.txt | 2 +- tools/config/ironic-config-generator.conf | 1 + 7 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/mdns-a5f4034257139e31.yaml diff --git a/ironic/conductor/base_manager.py b/ironic/conductor/base_manager.py index c08c8702bd..31d9471927 100644 --- a/ironic/conductor/base_manager.py +++ b/ironic/conductor/base_manager.py @@ -19,6 +19,7 @@ import eventlet import futurist from futurist import periodics from futurist import rejection +from ironic_lib import mdns from oslo_db import exception as db_exception from oslo_log import log from oslo_utils import excutils @@ -39,6 +40,7 @@ from ironic.conductor import task_manager from ironic.conf import CONF from ironic.db import api as dbapi from ironic.drivers import base as driver_base +from ironic.drivers.modules import deploy_utils from ironic import objects from ironic.objects import fields as obj_fields @@ -78,6 +80,7 @@ class BaseConductorManager(object): self.sensors_notifier = rpc.get_sensors_notifier() self._started = False self._shutdown = None + self._zeroconf = None def init_host(self, admin_context=None): """Initialize the conductor host. @@ -212,6 +215,9 @@ class BaseConductorManager(object): except exception.NoFreeConductorWorker: LOG.warning('Failed to start worker for resuming allocations.') + if CONF.conductor.enable_mdns: + self._publish_endpoint() + self._started = True def _use_groups(self): @@ -316,6 +322,11 @@ class BaseConductorManager(object): self._periodic_tasks.stop() self._periodic_tasks.wait() self._executor.shutdown(wait=True) + + if self._zeroconf is not None: + self._zeroconf.close() + self._zeroconf = None + self._started = False def _register_and_validate_hardware_interfaces(self, hardware_types): @@ -564,3 +575,12 @@ class BaseConductorManager(object): for allocation in objects.Allocation.list(context, filters=filters): LOG.debug('Resuming unfinished allocation %s', allocation.uuid) allocations.do_allocate(context, allocation) + + def _publish_endpoint(self): + params = {} + if CONF.debug: + params['ipa_debug'] = True + self._zeroconf = mdns.Zeroconf() + self._zeroconf.register_service('baremetal', + deploy_utils.get_ironic_api_url(), + params=params) diff --git a/ironic/conf/conductor.py b/ironic/conf/conductor.py index 039574e4ea..05ecd20665 100644 --- a/ironic/conf/conductor.py +++ b/ironic/conf/conductor.py @@ -221,6 +221,9 @@ opts = [ mutable=True, help=_('Allow deleting nodes which are in state ' '\'available\'. Defaults to True.')), + cfg.BoolOpt('enable_mdns', default=False, + help=_('Whether to enable publishing the ironic-inspector API ' + 'endpoint via multicast DNS.')), ] diff --git a/ironic/tests/unit/conductor/test_base_manager.py b/ironic/tests/unit/conductor/test_base_manager.py index 0c619d912b..02bd171f92 100644 --- a/ironic/tests/unit/conductor/test_base_manager.py +++ b/ironic/tests/unit/conductor/test_base_manager.py @@ -18,6 +18,7 @@ import uuid import eventlet import futurist from futurist import periodics +from ironic_lib import mdns import mock from oslo_config import cfg from oslo_db import exception as db_exception @@ -32,6 +33,7 @@ from ironic.conductor import notification_utils from ironic.conductor import task_manager from ironic.drivers import fake_hardware from ironic.drivers import generic +from ironic.drivers.modules import deploy_utils from ironic.drivers.modules import fake from ironic import objects from ironic.objects import fields @@ -247,6 +249,40 @@ class StartStopTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase): self.service.del_host() self.assertTrue(self.service._shutdown) + @mock.patch.object(deploy_utils, 'get_ironic_api_url', autospec=True) + @mock.patch.object(mdns, 'Zeroconf', autospec=True) + def test_start_with_mdns(self, mock_zc, mock_api_url): + CONF.set_override('debug', False) + CONF.set_override('enable_mdns', True, 'conductor') + self._start_service() + res = objects.Conductor.get_by_hostname(self.context, self.hostname) + self.assertEqual(self.hostname, res['hostname']) + mock_zc.return_value.register_service.assert_called_once_with( + 'baremetal', + mock_api_url.return_value, + params={}) + + @mock.patch.object(deploy_utils, 'get_ironic_api_url', autospec=True) + @mock.patch.object(mdns, 'Zeroconf', autospec=True) + def test_start_with_mdns_and_debug(self, mock_zc, mock_api_url): + CONF.set_override('debug', True) + CONF.set_override('enable_mdns', True, 'conductor') + self._start_service() + res = objects.Conductor.get_by_hostname(self.context, self.hostname) + self.assertEqual(self.hostname, res['hostname']) + mock_zc.return_value.register_service.assert_called_once_with( + 'baremetal', + mock_api_url.return_value, + params={'ipa_debug': True}) + + def test_del_host_with_mdns(self): + mock_zc = mock.Mock(spec=mdns.Zeroconf) + self.service._zeroconf = mock_zc + self._start_service() + self.service.del_host() + mock_zc.close.assert_called_once_with() + self.assertIsNone(self.service._zeroconf) + class CheckInterfacesTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase): def test__check_enabled_interfaces_success(self): diff --git a/lower-constraints.txt b/lower-constraints.txt index 3676081f11..859c210448 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -38,7 +38,7 @@ greenlet==0.4.13 hacking==1.0.0 idna==2.6 imagesize==1.0.0 -ironic-lib==2.15.0 +ironic-lib==2.17.0 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.3 diff --git a/releasenotes/notes/mdns-a5f4034257139e31.yaml b/releasenotes/notes/mdns-a5f4034257139e31.yaml new file mode 100644 index 0000000000..8bb4327e7e --- /dev/null +++ b/releasenotes/notes/mdns-a5f4034257139e31.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + A new option ``enable_mdns`` allows to enable publishing the baremetal + API endpoint via mDNS as specified in the `API SIG guideline + `_. diff --git a/requirements.txt b/requirements.txt index ab973569d8..be3c0992b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ python-cinderclient>=3.3.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0 keystoneauth1>=3.4.0 # Apache-2.0 -ironic-lib>=2.15.0 # Apache-2.0 +ironic-lib>=2.17.0 # Apache-2.0 python-swiftclient>=3.2.0 # Apache-2.0 pytz>=2013.6 # MIT stevedore>=1.20.0 # Apache-2.0 diff --git a/tools/config/ironic-config-generator.conf b/tools/config/ironic-config-generator.conf index 5412c9159f..806bf7a03e 100644 --- a/tools/config/ironic-config-generator.conf +++ b/tools/config/ironic-config-generator.conf @@ -4,6 +4,7 @@ wrap_width = 62 namespace = ironic namespace = ironic_lib.disk_utils namespace = ironic_lib.disk_partitioner +namespace = ironic_lib.mdns namespace = ironic_lib.metrics namespace = ironic_lib.metrics_statsd namespace = ironic_lib.utils