Add reporting provisioning status to the metadata service
Add support for reporting the provisioning status to the metadata service. The following config options have been added: - metadata_report_provisioning_started: Reports to the metadata service that provisioning has started type=bool, default=False - metadata_report_provisioning_completed: Reports to the metadata service that provisioning completed or failed type=bool, default=False Change-Id: I23a5a8e5473dd103ce5e0acd0c4647cdec86f97f Implements: blueprint add-reporting-provisioning-status-to-metadata-service Co-Authored-By: Paula Madalina Crismaru <pcrismaru@cloudbasesolutions.com>
This commit is contained in:
parent
18f4b25972
commit
834f19058c
@ -271,6 +271,14 @@ class GlobalOptions(conf_base.Options):
|
|||||||
'enable_automatic_updates', default=None,
|
'enable_automatic_updates', default=None,
|
||||||
help='If set, enables or disables automatic operating '
|
help='If set, enables or disables automatic operating '
|
||||||
'system updates.'),
|
'system updates.'),
|
||||||
|
cfg.BoolOpt(
|
||||||
|
'metadata_report_provisioning_started', default=False,
|
||||||
|
help='Reports to the metadata service that provisioning has '
|
||||||
|
'started'),
|
||||||
|
cfg.BoolOpt(
|
||||||
|
'metadata_report_provisioning_completed', default=False,
|
||||||
|
help='Reports to the metadata service that provisioning '
|
||||||
|
'completed successfully or failed'),
|
||||||
]
|
]
|
||||||
|
|
||||||
self._cli_options = [
|
self._cli_options = [
|
||||||
|
@ -52,6 +52,8 @@ class InitManager(object):
|
|||||||
def _exec_plugin(self, osutils, service, plugin, instance_id, shared_data):
|
def _exec_plugin(self, osutils, service, plugin, instance_id, shared_data):
|
||||||
plugin_name = plugin.get_name()
|
plugin_name = plugin.get_name()
|
||||||
|
|
||||||
|
reboot_required = None
|
||||||
|
success = True
|
||||||
status = None
|
status = None
|
||||||
if instance_id is not None:
|
if instance_id is not None:
|
||||||
status = self._get_plugin_status(osutils, instance_id, plugin_name)
|
status = self._get_plugin_status(osutils, instance_id, plugin_name)
|
||||||
@ -66,11 +68,12 @@ class InitManager(object):
|
|||||||
if instance_id is not None:
|
if instance_id is not None:
|
||||||
self._set_plugin_status(osutils, instance_id, plugin_name,
|
self._set_plugin_status(osutils, instance_id, plugin_name,
|
||||||
status)
|
status)
|
||||||
return reboot_required
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.error('plugin \'%(plugin_name)s\' failed with error '
|
LOG.error('plugin \'%(plugin_name)s\' failed with error '
|
||||||
'\'%(ex)s\'', {'plugin_name': plugin_name, 'ex': ex})
|
'\'%(ex)s\'', {'plugin_name': plugin_name, 'ex': ex})
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
|
success = False
|
||||||
|
return success, reboot_required
|
||||||
|
|
||||||
def _check_plugin_os_requirements(self, osutils, plugin):
|
def _check_plugin_os_requirements(self, osutils, plugin):
|
||||||
supported = False
|
supported = False
|
||||||
@ -102,19 +105,22 @@ class InitManager(object):
|
|||||||
def _handle_plugins_stage(self, osutils, service, instance_id, stage):
|
def _handle_plugins_stage(self, osutils, service, instance_id, stage):
|
||||||
plugins_shared_data = {}
|
plugins_shared_data = {}
|
||||||
reboot_required = False
|
reboot_required = False
|
||||||
|
stage_success = True
|
||||||
plugins = plugins_factory.load_plugins(stage)
|
plugins = plugins_factory.load_plugins(stage)
|
||||||
|
|
||||||
LOG.info('Executing plugins for stage %r:', stage)
|
LOG.info('Executing plugins for stage %r:', stage)
|
||||||
|
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
if self._check_plugin_os_requirements(osutils, plugin):
|
if self._check_plugin_os_requirements(osutils, plugin):
|
||||||
if self._exec_plugin(osutils, service, plugin,
|
success, reboot_required = self._exec_plugin(
|
||||||
instance_id, plugins_shared_data):
|
osutils, service, plugin, instance_id,
|
||||||
reboot_required = True
|
plugins_shared_data)
|
||||||
if CONF.allow_reboot:
|
if not success:
|
||||||
|
stage_success = False
|
||||||
|
if reboot_required and CONF.allow_reboot:
|
||||||
break
|
break
|
||||||
|
|
||||||
return reboot_required
|
return stage_success, reboot_required
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _reset_service_password_and_respawn(osutils):
|
def _reset_service_password_and_respawn(osutils):
|
||||||
@ -168,14 +174,14 @@ class InitManager(object):
|
|||||||
LOG.info('Cloudbase-Init version: %s', version.get_version())
|
LOG.info('Cloudbase-Init version: %s', version.get_version())
|
||||||
osutils.wait_for_boot_completion()
|
osutils.wait_for_boot_completion()
|
||||||
|
|
||||||
reboot_required = self._handle_plugins_stage(
|
stage_success, reboot_required = self._handle_plugins_stage(
|
||||||
osutils, None, None,
|
osutils, None, None,
|
||||||
plugins_base.PLUGIN_STAGE_PRE_NETWORKING)
|
plugins_base.PLUGIN_STAGE_PRE_NETWORKING)
|
||||||
|
|
||||||
self._check_latest_version()
|
self._check_latest_version()
|
||||||
|
|
||||||
if not (reboot_required and CONF.allow_reboot):
|
if not (reboot_required and CONF.allow_reboot):
|
||||||
reboot_required = self._handle_plugins_stage(
|
stage_success, reboot_required = self._handle_plugins_stage(
|
||||||
osutils, None, None,
|
osutils, None, None,
|
||||||
plugins_base.PLUGIN_STAGE_PRE_METADATA_DISCOVERY)
|
plugins_base.PLUGIN_STAGE_PRE_METADATA_DISCOVERY)
|
||||||
|
|
||||||
@ -188,16 +194,28 @@ class InitManager(object):
|
|||||||
LOG.info('Metadata service loaded: \'%s\'' %
|
LOG.info('Metadata service loaded: \'%s\'' %
|
||||||
service.get_name())
|
service.get_name())
|
||||||
|
|
||||||
|
if CONF.metadata_report_provisioning_started:
|
||||||
|
LOG.info("Reporting provisioning started")
|
||||||
|
service.provisioning_started()
|
||||||
|
|
||||||
instance_id = service.get_instance_id()
|
instance_id = service.get_instance_id()
|
||||||
LOG.debug('Instance id: %s', instance_id)
|
LOG.debug('Instance id: %s', instance_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reboot_required = self._handle_plugins_stage(
|
stage_success, reboot_required = self._handle_plugins_stage(
|
||||||
osutils, service, instance_id,
|
osutils, service, instance_id,
|
||||||
plugins_base.PLUGIN_STAGE_MAIN)
|
plugins_base.PLUGIN_STAGE_MAIN)
|
||||||
finally:
|
finally:
|
||||||
service.cleanup()
|
service.cleanup()
|
||||||
|
|
||||||
|
if (CONF.metadata_report_provisioning_completed and
|
||||||
|
not stage_success):
|
||||||
|
try:
|
||||||
|
LOG.info("Reporting provisioning failed")
|
||||||
|
service.provisioning_failed()
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.exception(ex)
|
||||||
|
|
||||||
if reboot_required and CONF.allow_reboot:
|
if reboot_required and CONF.allow_reboot:
|
||||||
try:
|
try:
|
||||||
LOG.info("Rebooting")
|
LOG.info("Rebooting")
|
||||||
@ -206,6 +224,15 @@ class InitManager(object):
|
|||||||
LOG.error('reboot failed with error \'%s\'' % ex)
|
LOG.error('reboot failed with error \'%s\'' % ex)
|
||||||
else:
|
else:
|
||||||
LOG.info("Plugins execution done")
|
LOG.info("Plugins execution done")
|
||||||
|
|
||||||
|
if (service and CONF.metadata_report_provisioning_completed and
|
||||||
|
stage_success):
|
||||||
|
try:
|
||||||
|
LOG.info("Reporting provisioning completed")
|
||||||
|
service.provisioning_completed()
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.exception(ex)
|
||||||
|
|
||||||
if CONF.stop_service_on_exit:
|
if CONF.stop_service_on_exit:
|
||||||
LOG.info("Stopping Cloudbase-Init service")
|
LOG.info("Stopping Cloudbase-Init service")
|
||||||
osutils.terminate()
|
osutils.terminate()
|
||||||
|
@ -189,6 +189,15 @@ class BaseMetadataService(object):
|
|||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def provisioning_started(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def provisioning_completed(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def provisioning_failed(self):
|
||||||
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_post_rdp_cert_thumbprint(self):
|
def can_post_rdp_cert_thumbprint(self):
|
||||||
return False
|
return False
|
||||||
|
@ -186,12 +186,13 @@ class TestInitManager(unittest.TestCase):
|
|||||||
def _test_handle_plugins_stage(self, mock_load_plugins,
|
def _test_handle_plugins_stage(self, mock_load_plugins,
|
||||||
mock_check_plugin_os_requirements,
|
mock_check_plugin_os_requirements,
|
||||||
mock_exec_plugin,
|
mock_exec_plugin,
|
||||||
reboot=True, fast_reboot=True):
|
reboot=True, fast_reboot=True,
|
||||||
|
success=True):
|
||||||
stage = "fake stage"
|
stage = "fake stage"
|
||||||
service, instance_id = mock.Mock(), mock.Mock()
|
service, instance_id = mock.Mock(), mock.Mock()
|
||||||
plugins = [mock.Mock() for _ in range(3)]
|
plugins = [mock.Mock() for _ in range(3)]
|
||||||
mock_check_plugin_os_requirements.return_value = True
|
mock_check_plugin_os_requirements.return_value = True
|
||||||
mock_exec_plugin.return_value = reboot
|
mock_exec_plugin.return_value = success, reboot
|
||||||
mock_load_plugins.return_value = plugins
|
mock_load_plugins.return_value = plugins
|
||||||
requirements_calls = [mock.call(self.osutils, plugin)
|
requirements_calls = [mock.call(self.osutils, plugin)
|
||||||
for plugin in plugins]
|
for plugin in plugins]
|
||||||
@ -210,7 +211,7 @@ class TestInitManager(unittest.TestCase):
|
|||||||
mock_check_plugin_os_requirements.assert_has_calls(
|
mock_check_plugin_os_requirements.assert_has_calls(
|
||||||
requirements_calls[:idx])
|
requirements_calls[:idx])
|
||||||
mock_exec_plugin.assert_has_calls(exec_plugin_calls[:idx])
|
mock_exec_plugin.assert_has_calls(exec_plugin_calls[:idx])
|
||||||
self.assertEqual(reboot, response)
|
self.assertEqual((success, reboot), response)
|
||||||
|
|
||||||
def test_handle_plugins_stage(self):
|
def test_handle_plugins_stage(self):
|
||||||
self._test_handle_plugins_stage()
|
self._test_handle_plugins_stage()
|
||||||
@ -222,6 +223,9 @@ class TestInitManager(unittest.TestCase):
|
|||||||
def test_handle_plugins_stage_no_fast_reboot(self):
|
def test_handle_plugins_stage_no_fast_reboot(self):
|
||||||
self._test_handle_plugins_stage(fast_reboot=False)
|
self._test_handle_plugins_stage(fast_reboot=False)
|
||||||
|
|
||||||
|
def test_handle_plugins_stage_stage_fails(self):
|
||||||
|
self._test_handle_plugins_stage(success=False)
|
||||||
|
|
||||||
@mock.patch('cloudbaseinit.init.InitManager.'
|
@mock.patch('cloudbaseinit.init.InitManager.'
|
||||||
'_reset_service_password_and_respawn')
|
'_reset_service_password_and_respawn')
|
||||||
@mock.patch('cloudbaseinit.init.InitManager'
|
@mock.patch('cloudbaseinit.init.InitManager'
|
||||||
@ -236,7 +240,8 @@ class TestInitManager(unittest.TestCase):
|
|||||||
mock_get_version, mock_check_latest_version,
|
mock_get_version, mock_check_latest_version,
|
||||||
mock_handle_plugins_stage, mock_reset_service,
|
mock_handle_plugins_stage, mock_reset_service,
|
||||||
expected_logging,
|
expected_logging,
|
||||||
version, name, instance_id, reboot=True):
|
version, name, instance_id, reboot=True,
|
||||||
|
last_stage=False):
|
||||||
sys.platform = 'win32'
|
sys.platform = 'win32'
|
||||||
mock_get_version.return_value = version
|
mock_get_version.return_value = version
|
||||||
fake_service = mock.MagicMock()
|
fake_service = mock.MagicMock()
|
||||||
@ -246,7 +251,8 @@ class TestInitManager(unittest.TestCase):
|
|||||||
mock_get_metadata_service.return_value = fake_service
|
mock_get_metadata_service.return_value = fake_service
|
||||||
fake_service.get_name.return_value = name
|
fake_service.get_name.return_value = name
|
||||||
fake_service.get_instance_id.return_value = instance_id
|
fake_service.get_instance_id.return_value = instance_id
|
||||||
mock_handle_plugins_stage.side_effect = [False, False, True]
|
mock_handle_plugins_stage.side_effect = [(True, False), (True, False),
|
||||||
|
(last_stage, True)]
|
||||||
stages = [
|
stages = [
|
||||||
base.PLUGIN_STAGE_PRE_NETWORKING,
|
base.PLUGIN_STAGE_PRE_NETWORKING,
|
||||||
base.PLUGIN_STAGE_PRE_METADATA_DISCOVERY,
|
base.PLUGIN_STAGE_PRE_METADATA_DISCOVERY,
|
||||||
@ -256,13 +262,14 @@ class TestInitManager(unittest.TestCase):
|
|||||||
stage_calls_list[2][1] = fake_service
|
stage_calls_list[2][1] = fake_service
|
||||||
stage_calls_list[2][2] = instance_id
|
stage_calls_list[2][2] = instance_id
|
||||||
stage_calls = [mock.call(*args) for args in stage_calls_list]
|
stage_calls = [mock.call(*args) for args in stage_calls_list]
|
||||||
|
|
||||||
with testutils.LogSnatcher('cloudbaseinit.init') as snatcher:
|
with testutils.LogSnatcher('cloudbaseinit.init') as snatcher:
|
||||||
self._init.configure_host()
|
self._init.configure_host()
|
||||||
self.assertEqual(expected_logging, snatcher.output)
|
self.assertEqual(expected_logging, snatcher.output)
|
||||||
mock_check_latest_version.assert_called_once_with()
|
mock_check_latest_version.assert_called_once_with()
|
||||||
if CONF.reset_service_password:
|
if CONF.reset_service_password:
|
||||||
mock_reset_service.assert_called_once_with(self.osutils)
|
mock_reset_service.assert_called_once_with(self.osutils)
|
||||||
|
if last_stage:
|
||||||
|
fake_service.provisioning_completed.assert_called_once_with()
|
||||||
|
|
||||||
self.osutils.wait_for_boot_completion.assert_called_once_with()
|
self.osutils.wait_for_boot_completion.assert_called_once_with()
|
||||||
mock_get_metadata_service.assert_called_once_with()
|
mock_get_metadata_service.assert_called_once_with()
|
||||||
@ -275,7 +282,8 @@ class TestInitManager(unittest.TestCase):
|
|||||||
else:
|
else:
|
||||||
self.assertFalse(self.osutils.reboot.called)
|
self.assertFalse(self.osutils.reboot.called)
|
||||||
|
|
||||||
def _test_configure_host_with_logging(self, extra_logging, reboot=True):
|
def _test_configure_host_with_logging(self, extra_logging, reboot=True,
|
||||||
|
last_stage=False):
|
||||||
instance_id = 'fake id'
|
instance_id = 'fake id'
|
||||||
name = 'fake name'
|
name = 'fake name'
|
||||||
version = 'version'
|
version = 'version'
|
||||||
@ -284,17 +292,23 @@ class TestInitManager(unittest.TestCase):
|
|||||||
'Metadata service loaded: %r' % name,
|
'Metadata service loaded: %r' % name,
|
||||||
'Instance id: %s' % instance_id,
|
'Instance id: %s' % instance_id,
|
||||||
]
|
]
|
||||||
|
if CONF.metadata_report_provisioning_started:
|
||||||
|
expected_logging.insert(2, 'Reporting provisioning started')
|
||||||
|
|
||||||
self._test_configure_host(
|
self._test_configure_host(
|
||||||
expected_logging=expected_logging + extra_logging,
|
expected_logging=expected_logging + extra_logging,
|
||||||
version=version, name=name, instance_id=instance_id,
|
version=version, name=name, instance_id=instance_id,
|
||||||
reboot=reboot)
|
reboot=reboot, last_stage=last_stage)
|
||||||
|
|
||||||
|
@testutils.ConfPatcher('metadata_report_provisioning_completed', True)
|
||||||
@testutils.ConfPatcher('allow_reboot', False)
|
@testutils.ConfPatcher('allow_reboot', False)
|
||||||
@testutils.ConfPatcher('stop_service_on_exit', False)
|
@testutils.ConfPatcher('stop_service_on_exit', False)
|
||||||
def test_configure_host_no_reboot_no_service_stopping(self):
|
def test_configure_host_no_reboot_no_service_stopping_reporting_done(self):
|
||||||
self._test_configure_host_with_logging(
|
self._test_configure_host_with_logging(
|
||||||
reboot=False,
|
reboot=False,
|
||||||
extra_logging=['Plugins execution done'])
|
extra_logging=['Plugins execution done',
|
||||||
|
'Reporting provisioning completed'],
|
||||||
|
last_stage=True)
|
||||||
|
|
||||||
@testutils.ConfPatcher('allow_reboot', False)
|
@testutils.ConfPatcher('allow_reboot', False)
|
||||||
@testutils.ConfPatcher('stop_service_on_exit', True)
|
@testutils.ConfPatcher('stop_service_on_exit', True)
|
||||||
@ -305,10 +319,11 @@ class TestInitManager(unittest.TestCase):
|
|||||||
'Stopping Cloudbase-Init service'])
|
'Stopping Cloudbase-Init service'])
|
||||||
self.osutils.terminate.assert_called_once_with()
|
self.osutils.terminate.assert_called_once_with()
|
||||||
|
|
||||||
|
@testutils.ConfPatcher('metadata_report_provisioning_completed', True)
|
||||||
@testutils.ConfPatcher('allow_reboot', True)
|
@testutils.ConfPatcher('allow_reboot', True)
|
||||||
def test_configure_host_reboot(self):
|
def test_configure_host_reboot_reporting_started_and_failed(self):
|
||||||
self._test_configure_host_with_logging(
|
self._test_configure_host_with_logging(
|
||||||
extra_logging=['Rebooting'])
|
extra_logging=['Reporting provisioning failed', 'Rebooting'])
|
||||||
|
|
||||||
@testutils.ConfPatcher('check_latest_version', False)
|
@testutils.ConfPatcher('check_latest_version', False)
|
||||||
@mock.patch('cloudbaseinit.version.check_latest_version')
|
@mock.patch('cloudbaseinit.version.check_latest_version')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user