Minor changes for deploy_steps framework

This addresses nits from the two reviews related to the
deploy_steps framework:
- I5feac3856cc4b87a850180b7fd0b3b9805f9225f
- I1baeeaaa6ed521e4189958fd7624cd6c5de96707

It also updates the release note to:
- indicate that support for drivers with no deploy steps will
  be removed in the Stein release (as opposed to the T* release),
  based on discussions in [1].
- mention that node.deploy_step is available in REST API version 1.44.

[1] http://eavesdrop.openstack.org/meetings/ironic/2018/ironic.2018-07-09-15.00.log.html#l-64

Change-Id: I97ab00cab21814287d1b8344b3e4ca0c093fb6ad
Story: #1753128
Task: #22592
This commit is contained in:
Ruby Loo 2018-07-16 20:28:05 +00:00
parent 67045c3a55
commit f838a14bcf
6 changed files with 141 additions and 55 deletions

View File

@ -89,9 +89,9 @@ SYNC_EXCLUDED_STATES = (states.DEPLOYWAIT, states.CLEANWAIT, states.ENROLL)
# agent_version parameter and need updating.
_SEEN_AGENT_VERSION_DEPRECATIONS = []
# NOTE(rloo) This list is used to keep track of deprecation warnings that
# have already been issued for deploy drivers that do not use deploy steps.
_SEEN_NO_DEPLOY_STEP_DEPRECATIONS = []
# NOTE(rloo) This is used to keep track of deprecation warnings that have
# already been issued for deploy drivers that do not use deploy steps.
_SEEN_NO_DEPLOY_STEP_DEPRECATIONS = set()
class ConductorManager(base_manager.BaseConductorManager):
@ -1001,6 +1001,13 @@ class ConductorManager(base_manager.BaseConductorManager):
:returns: index of the next step; None if there are none to execute.
"""
valid_types = set(['clean', 'deploy'])
if step_type not in valid_types:
# NOTE(rloo): No need to i18n this, since this would be a
# developer error; it isn't user-facing.
raise exception.Invalid(
'step_type must be one of %(valid)s, not %(step)s'
% {'valid': valid_types, 'step': step_type})
node = task.node
if not getattr(node, '%s_step' % step_type):
# first time through, all steps need to be done. Return the
@ -3488,12 +3495,12 @@ def _old_rest_of_do_node_deploy(task, conductor_id, no_deploy_steps):
# for supporting drivers with no deploy steps.
if no_deploy_steps:
global _SEEN_NO_DEPLOY_STEP_DEPRECATIONS
deploy_driver_name = task.driver.deploy.__class__.__name__
if deploy_driver_name not in _SEEN_NO_DEPLOY_STEP_DEPRECATIONS:
LOG.warning('Deploy driver %s does not support deploy steps; this '
'will be required after Stein.', deploy_driver_name)
_SEEN_NO_DEPLOY_STEP_DEPRECATIONS.append(deploy_driver_name)
'will be required starting with the Stein release.',
deploy_driver_name)
_SEEN_NO_DEPLOY_STEP_DEPRECATIONS.add(deploy_driver_name)
node = task.node
try:

View File

@ -466,7 +466,7 @@ def deploying_error_handler(task, logmsg, errmsg, traceback=False,
addl = _('An unhandled exception was encountered while '
'aborting. More information may be found in the log '
'file.')
cleanup_err = _('%(err)s. %(add)s') % {'err': errmsg, 'add': addl}
cleanup_err = '%(err)s. %(add)s' % {'err': errmsg, 'add': addl}
node.refresh()
if node.provision_state in (
@ -686,9 +686,7 @@ def _get_cleaning_steps(task, enabled=False, sort=True):
clean steps.
:returns: A list of clean step dictionaries
"""
sort_key = None
if sort:
sort_key = _clean_step_key
sort_key = _clean_step_key if sort else None
return _get_steps(task, CLEANING_INTERFACE_PRIORITY, 'get_clean_steps',
enabled=enabled, sort_step_key=sort_key)
@ -706,9 +704,7 @@ def _get_deployment_steps(task, enabled=False, sort=True):
deploy steps.
:returns: A list of deploy step dictionaries
"""
sort_key = None
if sort:
sort_key = _deploy_step_key
sort_key = _deploy_step_key if sort else None
return _get_steps(task, DEPLOYING_INTERFACE_PRIORITY, 'get_deploy_steps',
enabled=enabled, sort_step_key=sort_key)

View File

@ -2432,7 +2432,7 @@ class TestPatch(test_api_base.BaseApiTest):
[{'path': '/deploy_step',
'op': 'replace',
'value': 'deploy this'}],
headers={api_base.Version.string: "1.43"},
headers={api_base.Version.string: "1.44"},
expect_errors=True)
self.assertEqual('application/json', response.content_type)
self.assertEqual(http_client.BAD_REQUEST, response.status_code)

View File

@ -1342,6 +1342,12 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
# when getting an unexpected state returned from a deploy_step.
mock_iwdi.return_value = True
self._start_service()
# NOTE(rloo): We have to mock this here as opposed to using a
# decorator. With a decorator, when initialization is done, the
# mocked deploy() method isn't considered a deploy step, causing
# manager._old_rest_of_do_node_deploy() to be run instead of
# manager._do_next_deploy_step(). So we defer mock'ing until after
# the init is done.
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
mock_deploy.return_value = states.DEPLOYING
@ -1368,10 +1374,17 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
# Verify is_whole_disk_image reflects correct value on rebuild.
self.assertTrue(node.driver_internal_info['is_whole_disk_image'])
self.assertEqual(1, len(node.driver_internal_info['deploy_steps']))
def test_do_node_deploy_rebuild_active_state_waiting(self, mock_iwdi):
mock_iwdi.return_value = False
self._start_service()
# NOTE(rloo): We have to mock this here as opposed to using a
# decorator. With a decorator, when initialization is done, the
# mocked deploy() method isn't considered a deploy step, causing
# manager._old_rest_of_do_node_deploy() to be run instead of
# manager._do_next_deploy_step(). So we defer mock'ing until after
# the init is done.
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
mock_deploy.return_value = states.DEPLOYWAIT
@ -1393,10 +1406,17 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
self.assertEqual(1, len(node.driver_internal_info['deploy_steps']))
def test_do_node_deploy_rebuild_active_state_done(self, mock_iwdi):
mock_iwdi.return_value = False
self._start_service()
# NOTE(rloo): We have to mock this here as opposed to using a
# decorator. With a decorator, when initialization is done, the
# mocked deploy() method isn't considered a deploy step, causing
# manager._old_rest_of_do_node_deploy() to be run instead of
# manager._do_next_deploy_step(). So we defer mock'ing until after
# the init is done.
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
mock_deploy.return_value = None
@ -1417,10 +1437,17 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
self.assertIsNone(node.driver_internal_info['deploy_steps'])
def test_do_node_deploy_rebuild_deployfail_state(self, mock_iwdi):
mock_iwdi.return_value = False
self._start_service()
# NOTE(rloo): We have to mock this here as opposed to using a
# decorator. With a decorator, when initialization is done, the
# mocked deploy() method isn't considered a deploy step, causing
# manager._old_rest_of_do_node_deploy() to be run instead of
# manager._do_next_deploy_step(). So we defer mock'ing until after
# the init is done.
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
mock_deploy.return_value = None
@ -1441,10 +1468,17 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
self.assertIsNone(node.driver_internal_info['deploy_steps'])
def test_do_node_deploy_rebuild_error_state(self, mock_iwdi):
mock_iwdi.return_value = False
self._start_service()
# NOTE(rloo): We have to mock this here as opposed to using a
# decorator. With a decorator, when initialization is done, the
# mocked deploy() method isn't considered a deploy step, causing
# manager._old_rest_of_do_node_deploy() to be run instead of
# manager._do_next_deploy_step(). So we defer mock'ing until after
# the init is done.
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
mock_deploy.return_value = None
@ -1465,6 +1499,7 @@ class ServiceDoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
mock_deploy.assert_called_once_with(mock.ANY, mock.ANY)
mock_iwdi.assert_called_once_with(self.context, node.instance_info)
self.assertFalse(node.driver_internal_info['is_whole_disk_image'])
self.assertIsNone(node.driver_internal_info['deploy_steps'])
def test_do_node_deploy_rebuild_from_available_state(self, mock_iwdi):
mock_iwdi.return_value = False
@ -1527,7 +1562,8 @@ class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
'step': 'deploy_end', 'priority': 20, 'interface': 'deploy'}
self.deploy_steps = [self.deploy_start, self.deploy_end]
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
autospec=True)
def test_continue_node_deploy_worker_pool_full(self, mock_spawn):
# Test the appropriate exception is raised if the worker pool is full
prv_state = states.DEPLOYWAIT
@ -1544,7 +1580,8 @@ class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
self.service.continue_node_deploy,
self.context, node.uuid)
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
autospec=True)
def test_continue_node_deploy_wrong_state(self, mock_spawn):
# Test the appropriate exception is raised if node isn't already
# in DEPLOYWAIT state
@ -1568,7 +1605,8 @@ class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
# Verify reservation has been cleared.
self.assertIsNone(node.reservation)
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
autospec=True)
def test_continue_node_deploy(self, mock_spawn):
# test a node can continue deploying via RPC
prv_state = states.DEPLOYWAIT
@ -1587,12 +1625,13 @@ class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(tgt_prv_state, node.target_provision_state)
mock_spawn.assert_called_with(manager._do_next_deploy_step, mock.ANY,
1, mock.ANY)
mock_spawn.assert_called_with(mock.ANY, manager._do_next_deploy_step,
mock.ANY, 1, mock.ANY)
@mock.patch.object(task_manager.TaskManager, 'process_event',
autospec=True)
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker')
@mock.patch('ironic.conductor.manager.ConductorManager._spawn_worker',
autospec=True)
def test_continue_node_deploy_deprecated(self, mock_spawn, mock_event):
# TODO(rloo): delete this when we remove support for handling
# deploy steps; node will always be in DEPLOYWAIT then.
@ -1614,8 +1653,8 @@ class ContinueNodeDeployTestCase(mgr_utils.ServiceSetUpMixin,
node.refresh()
self.assertEqual(states.DEPLOYING, node.provision_state)
self.assertEqual(tgt_prv_state, node.target_provision_state)
mock_spawn.assert_called_with(manager._do_next_deploy_step, mock.ANY,
1, mock.ANY)
mock_spawn.assert_called_with(mock.ANY, manager._do_next_deploy_step,
mock.ANY, 1, mock.ANY)
self.assertFalse(mock_event.called)
@ -1944,7 +1983,9 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
def test__do_node_deploy_ok_2(self):
# NOTE(rloo): a different way of testing for the same thing as in
# test__do_node_deploy_ok()
# test__do_node_deploy_ok(). Instead of specifying the provision &
# target_provision_states when creating the node, we call
# task.process_event() to "set the stage" (err "states").
self._start_service()
with mock.patch.object(fake.FakeDeploy,
'deploy', autospec=True) as mock_deploy:
@ -2044,12 +2085,15 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
self.assertFalse(mock_deploy_step.called)
self.assertNotIn('deploy_steps', task.node.driver_internal_info)
@mock.patch.object(manager, '_SEEN_NO_DEPLOY_STEP_DEPRECATIONS',
autospec=True)
@mock.patch.object(manager, 'LOG', autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy', autospec=True)
def test__old_rest_of_do_node_deploy_no_steps(self, mock_deploy, mock_log):
def test__old_rest_of_do_node_deploy_no_steps(self, mock_deploy, mock_log,
mock_deprecate):
# TODO(rloo): no deploy steps; delete this when we remove support
# for handling no deploy steps.
manager._SEEN_NO_DEPLOY_STEP_DEPRECATIONS = []
mock_deprecate.__contains__.side_effect = [False, True]
self._start_service()
node = obj_utils.create_test_node(self.context, driver='fake-hardware')
task = task_manager.TaskManager(self.context, node.uuid)
@ -2061,12 +2105,18 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
self.assertTrue(mock_log.warning.called)
self.assertEqual(self.service.conductor.id,
task.node.conductor_affinity)
mock_deprecate.__contains__.assert_called_once_with('FakeDeploy')
mock_deprecate.add.assert_called_once_with('FakeDeploy')
# Make sure the deprecation warning isn't logged again
mock_log.reset_mock()
mock_deprecate.add.reset_mock()
manager._old_rest_of_do_node_deploy(task, self.service.conductor.id,
True)
self.assertFalse(mock_log.warning.called)
mock_deprecate.__contains__.assert_has_calls(
[mock.call('FakeDeploy'), mock.call('FakeDeploy')])
self.assertFalse(mock_deprecate.add.called)
@mock.patch.object(manager, 'LOG', autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.deploy', autospec=True)
@ -2074,7 +2124,7 @@ class DoNodeDeployTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):
mock_log):
# TODO(rloo): has steps but old RPC; delete this when we remove support
# for handling no deploy steps.
manager._SEEN_NO_DEPLOY_STEP_DEPRECATIONS = []
manager._SEEN_NO_DEPLOY_STEP_DEPRECATIONS = set()
self._start_service()
node = obj_utils.create_test_node(self.context, driver='fake-hardware')
task = task_manager.TaskManager(self.context, node.uuid)
@ -2166,7 +2216,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
mock_execute.assert_called_once_with(mock.ANY, task,
self.deploy_steps[1])
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step',
autospec=True)
def test__do_next_deploy_step_last_step_done(self, mock_execute):
# Resume where last_step is the last deploy step that was executed
driver_internal_info = {'deploy_step_index': 1,
@ -2193,7 +2244,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
self.assertIsNone(node.driver_internal_info['deploy_steps'])
self.assertFalse(mock_execute.called)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step',
autospec=True)
def test__do_next_deploy_step_all(self, mock_execute):
# Run all steps from start to finish (all synchronous)
driver_internal_info = {'deploy_step_index': None,
@ -2221,7 +2273,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
mock.call(self.deploy_steps[1])]
@mock.patch.object(conductor_utils, 'LOG', autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step',
autospec=True)
def _do_next_deploy_step_execute_fail(self, exc, traceback,
mock_execute, mock_log):
# When a deploy step fails, go to DEPLOYFAIL
@ -2247,7 +2300,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
self.assertNotIn('deploy_step_index', node.driver_internal_info)
self.assertIsNotNone(node.last_error)
self.assertFalse(node.maintenance)
mock_execute.assert_called_once_with(mock.ANY, self.deploy_steps[0])
mock_execute.assert_called_once_with(mock.ANY, mock.ANY,
self.deploy_steps[0])
mock_log.error.assert_called_once_with(mock.ANY, exc_info=traceback)
def test_do_next_deploy_step_execute_ironic_exception(self):
@ -2257,7 +2311,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
def test_do_next_deploy_step_execute_exception(self):
self._do_next_deploy_step_execute_fail(Exception('foo'), True)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step',
autospec=True)
def test_do_next_deploy_step_no_steps(self, mock_execute):
self._start_service()
@ -2285,7 +2340,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
self.assertFalse(mock_execute.called)
mock_execute.reset_mock()
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.execute_deploy_step',
autospec=True)
def test_do_next_deploy_step_bad_step_return_value(self, mock_execute):
# When a deploy step fails, go to DEPLOYFAIL
self._start_service()
@ -2309,7 +2365,8 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
self.assertNotIn('deploy_step_index', node.driver_internal_info)
self.assertIsNotNone(node.last_error)
self.assertFalse(node.maintenance)
mock_execute.assert_called_once_with(mock.ANY, self.deploy_steps[0])
mock_execute.assert_called_once_with(mock.ANY, mock.ANY,
self.deploy_steps[0])
def test__get_node_next_deploy_steps(self):
driver_internal_info = {'deploy_steps': self.deploy_steps,
@ -2341,6 +2398,13 @@ class DoNextDeployStepTestCase(mgr_utils.ServiceSetUpMixin,
step_index = self.service._get_node_next_deploy_steps(task)
self.assertEqual(0, step_index)
def test__get_node_next_steps_exception(self):
node = obj_utils.create_test_node(self.context)
with task_manager.acquire(self.context, node.uuid) as task:
self.assertRaises(exception.Invalid,
self.service._get_node_next_steps, task, 'foo')
@mgr_utils.mock_record_keepalive
class CheckTimeoutsTestCase(mgr_utils.ServiceSetUpMixin, db_base.DbTestCase):

View File

@ -983,9 +983,12 @@ class NodeDeployStepsTestCase(db_base.DbTestCase):
self.node = obj_utils.create_test_node(
self.context, driver='fake-hardware')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps',
autospec=True)
def test__get_deployment_steps(self, mock_mgt_steps, mock_power_steps,
mock_deploy_steps):
# Test getting deploy steps, with one driver returning None, two
@ -1001,13 +1004,16 @@ class NodeDeployStepsTestCase(db_base.DbTestCase):
steps = conductor_utils._get_deployment_steps(task, enabled=False)
self.assertEqual(expected, steps)
mock_mgt_steps.assert_called_once_with(task)
mock_power_steps.assert_called_once_with(task)
mock_deploy_steps.assert_called_once_with(task)
mock_mgt_steps.assert_called_once_with(mock.ANY, task)
mock_power_steps.assert_called_once_with(mock.ANY, task)
mock_deploy_steps.assert_called_once_with(mock.ANY, task)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps',
autospec=True)
def test__get_deploy_steps_unsorted(self, mock_mgt_steps, mock_power_steps,
mock_deploy_steps):
@ -1019,13 +1025,16 @@ class NodeDeployStepsTestCase(db_base.DbTestCase):
steps = conductor_utils._get_deployment_steps(task, enabled=False,
sort=False)
self.assertEqual(mock_deploy_steps.return_value, steps)
mock_mgt_steps.assert_called_once_with(task)
mock_power_steps.assert_called_once_with(task)
mock_deploy_steps.assert_called_once_with(task)
mock_mgt_steps.assert_called_once_with(mock.ANY, task)
mock_power_steps.assert_called_once_with(mock.ANY, task)
mock_deploy_steps.assert_called_once_with(mock.ANY, task)
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps')
@mock.patch('ironic.drivers.modules.fake.FakeDeploy.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakePower.get_deploy_steps',
autospec=True)
@mock.patch('ironic.drivers.modules.fake.FakeManagement.get_deploy_steps',
autospec=True)
def test__get_deployment_steps_only_enabled(
self, mock_mgt_steps, mock_power_steps, mock_deploy_steps):
# Test getting only deploy steps, with one driver returning None, two
@ -1042,11 +1051,12 @@ class NodeDeployStepsTestCase(db_base.DbTestCase):
steps = conductor_utils._get_deployment_steps(task, enabled=True)
self.assertEqual(self.deploy_steps, steps)
mock_mgt_steps.assert_called_once_with(task)
mock_power_steps.assert_called_once_with(task)
mock_deploy_steps.assert_called_once_with(task)
mock_mgt_steps.assert_called_once_with(mock.ANY, task)
mock_power_steps.assert_called_once_with(mock.ANY, task)
mock_deploy_steps.assert_called_once_with(mock.ANY, task)
@mock.patch.object(conductor_utils, '_get_deployment_steps')
@mock.patch.object(conductor_utils, '_get_deployment_steps',
autospec=True)
def test_set_node_deployment_steps(self, mock_steps):
mock_steps.return_value = self.deploy_steps
@ -1441,7 +1451,7 @@ class ErrorHandlersTestCase(tests_base.TestCase):
self.assertFalse(self.node.save.called)
self.assertFalse(log_mock.warning.called)
@mock.patch.object(conductor_utils, 'LOG')
@mock.patch.object(conductor_utils, 'LOG', autospec=True)
def test_spawn_deploying_error_handler_no_worker(self, log_mock):
exc = exception.NoFreeConductorWorker()
conductor_utils.spawn_deploying_error_handler(exc, self.node)
@ -1449,7 +1459,7 @@ class ErrorHandlersTestCase(tests_base.TestCase):
self.assertIn('No free conductor workers', self.node.last_error)
self.assertTrue(log_mock.warning.called)
@mock.patch.object(conductor_utils, 'LOG')
@mock.patch.object(conductor_utils, 'LOG', autospec=True)
def test_spawn_deploying_error_handler_other_error(self, log_mock):
exc = Exception('foo')
conductor_utils.spawn_deploying_error_handler(exc, self.node)

View File

@ -5,9 +5,18 @@ features:
<https://specs.openstack.org/openstack/ironic-specs/specs/approved/deployment-steps-framework.html>`_
is in place. All in-tree drivers (DeployInterfaces) have one (big) deploy
step; the conductor executes this step when deploying a node.
Starting with the Bare Metal REST API version 1.44, the current deploy
step (if any) being executed is available in a node's ``deploy_step``
field in the responses for the following queries:
* ``GET /v1/nodes/<node identifier>``
* ``GET /v1/nodes/detail``
* ``GET /v1/nodes?fields=deploy_step,...``
deprecations:
- |
All drivers must implement their deployment process using `deploy steps`.
Out-of-tree drivers without deploy steps will be supported until the T* release.
Out-of-tree drivers without deploy steps will be supported until the
Stein release.
For more details, see
`story 1753128 <https://storyboard.openstack.org/#!/story/1753128>`_.