Add deploy_steps to baremetal node provisioning

Story: 2008043
Task: 41825
Change-Id: Ic44d3dfeb6d68875951feabbc480461f5db5d22a
This commit is contained in:
Aija Jauntēva
2021-04-09 09:14:54 -04:00
parent 2c5200748d
commit c206d4b7f2
5 changed files with 60 additions and 9 deletions

View File

@@ -76,6 +76,9 @@ RESET_INTERFACES_VERSION = '1.45'
CONFIG_DRIVE_DICT_VERSION = '1.56' CONFIG_DRIVE_DICT_VERSION = '1.56'
"""API version in which configdrive can be a dictionary.""" """API version in which configdrive can be a dictionary."""
DEPLOY_STEPS_VERSION = '1.69'
"""API version in which deploy_steps was added to node provisioning."""
class ListMixin: class ListMixin:

View File

@@ -327,7 +327,7 @@ class Proxy(proxy.Proxy):
def set_node_provision_state(self, node, target, config_drive=None, def set_node_provision_state(self, node, target, config_drive=None,
clean_steps=None, rescue_password=None, clean_steps=None, rescue_password=None,
wait=False, timeout=None): wait=False, timeout=None, deploy_steps=None):
"""Run an action modifying node's provision state. """Run an action modifying node's provision state.
This call is asynchronous, it will return success as soon as the Bare This call is asynchronous, it will return success as soon as the Bare
@@ -350,16 +350,20 @@ class Proxy(proxy.Proxy):
:param timeout: If ``wait`` is set to ``True``, specifies how much (in :param timeout: If ``wait`` is set to ``True``, specifies how much (in
seconds) to wait for the expected state to be reached. The value of seconds) to wait for the expected state to be reached. The value of
``None`` (the default) means no client-side timeout. ``None`` (the default) means no client-side timeout.
:param deploy_steps: Deploy steps to execute, only valid for ``active``
and ``rebuild`` target.
:returns: The updated :class:`~openstack.baremetal.v1.node.Node` :returns: The updated :class:`~openstack.baremetal.v1.node.Node`
:raises: ValueError if ``config_drive``, ``clean_steps`` or :raises: ValueError if ``config_drive``, ``clean_steps``,
``rescue_password`` are provided with an invalid ``target``. ``deploy_steps`` or ``rescue_password`` are provided with an
invalid ``target``.
""" """
res = self._get_resource(_node.Node, node) res = self._get_resource(_node.Node, node)
return res.set_provision_state(self, target, config_drive=config_drive, return res.set_provision_state(self, target, config_drive=config_drive,
clean_steps=clean_steps, clean_steps=clean_steps,
rescue_password=rescue_password, rescue_password=rescue_password,
wait=wait, timeout=timeout) wait=wait, timeout=timeout,
deploy_steps=deploy_steps)
def set_node_boot_device(self, node, boot_device, persistent=False): def set_node_boot_device(self, node, boot_device, persistent=False):
"""Set node boot device """Set node boot device

View File

@@ -91,8 +91,8 @@ class Node(_common.ListMixin, resource.Resource):
is_maintenance='maintenance', is_maintenance='maintenance',
) )
# The retired and retired_reason fields introduced in 1.61 (Ussuri). # Provision state deploy_steps introduced in 1.69 (Wallaby).
_max_microversion = '1.61' _max_microversion = '1.69'
# Properties # Properties
#: The UUID of the allocation associated with this node. Added in API #: The UUID of the allocation associated with this node. Added in API
@@ -346,7 +346,7 @@ class Node(_common.ListMixin, resource.Resource):
def set_provision_state(self, session, target, config_drive=None, def set_provision_state(self, session, target, config_drive=None,
clean_steps=None, rescue_password=None, clean_steps=None, rescue_password=None,
wait=False, timeout=None): wait=False, timeout=None, deploy_steps=None):
"""Run an action modifying this node's provision state. """Run an action modifying this node's provision state.
This call is asynchronous, it will return success as soon as the Bare This call is asynchronous, it will return success as soon as the Bare
@@ -366,10 +366,13 @@ class Node(_common.ListMixin, resource.Resource):
:param wait: Whether to wait for the target state to be reached. :param wait: Whether to wait for the target state to be reached.
:param timeout: Timeout (in seconds) to wait for the target state to be :param timeout: Timeout (in seconds) to wait for the target state to be
reached. If ``None``, wait without timeout. reached. If ``None``, wait without timeout.
:param deploy_steps: Deploy steps to execute, only valid for ``active``
and ``rebuild`` target.
:return: This :class:`Node` instance. :return: This :class:`Node` instance.
:raises: ValueError if ``config_drive``, ``clean_steps`` or :raises: ValueError if ``config_drive``, ``clean_steps``,
``rescue_password`` are provided with an invalid ``target``. ``deploy_steps`` or ``rescue_password`` are provided with an
invalid ``target``.
:raises: :class:`~openstack.exceptions.ResourceFailure` if the node :raises: :class:`~openstack.exceptions.ResourceFailure` if the node
reaches an error state while waiting for the state. reaches an error state while waiting for the state.
:raises: :class:`~openstack.exceptions.ResourceTimeout` if timeout :raises: :class:`~openstack.exceptions.ResourceTimeout` if timeout
@@ -388,6 +391,9 @@ class Node(_common.ListMixin, resource.Resource):
elif target == 'rebuild': elif target == 'rebuild':
version = _common.CONFIG_DRIVE_REBUILD_VERSION version = _common.CONFIG_DRIVE_REBUILD_VERSION
if deploy_steps:
version = _common.DEPLOY_STEPS_VERSION
version = self._assert_microversion_for(session, 'commit', version) version = self._assert_microversion_for(session, 'commit', version)
body = {'target': target} body = {'target': target}
@@ -404,6 +410,12 @@ class Node(_common.ListMixin, resource.Resource):
'"clean" target') '"clean" target')
body['clean_steps'] = clean_steps body['clean_steps'] = clean_steps
if deploy_steps is not None:
if target not in ('active', 'rebuild'):
raise ValueError('Deploy steps can only be provided with '
'"deploy" and "rebuild" target')
body['deploy_steps'] = deploy_steps
if rescue_password is not None: if rescue_password is not None:
if target != 'rescue': if target != 'rescue':
raise ValueError('Rescue password can only be provided with ' raise ValueError('Rescue password can only be provided with '

View File

@@ -296,6 +296,34 @@ class TestNodeSetProvisionState(base.TestCase):
headers=mock.ANY, microversion='1.56', headers=mock.ANY, microversion='1.56',
retriable_status_codes=_common.RETRIABLE_STATUS_CODES) retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
def test_deploy_with_deploy_steps(self):
deploy_steps = [{'interface': 'deploy', 'step': 'upgrade_fw'}]
result = self.node.set_provision_state(
self.session, 'active',
deploy_steps=deploy_steps)
self.assertIs(result, self.node)
self.session.put.assert_called_once_with(
'nodes/%s/states/provision' % self.node.id,
json={'target': 'active', 'deploy_steps': deploy_steps},
headers=mock.ANY, microversion='1.69',
retriable_status_codes=_common.RETRIABLE_STATUS_CODES
)
def test_rebuild_with_deploy_steps(self):
deploy_steps = [{'interface': 'deploy', 'step': 'upgrade_fw'}]
result = self.node.set_provision_state(
self.session, 'rebuild',
deploy_steps=deploy_steps)
self.assertIs(result, self.node)
self.session.put.assert_called_once_with(
'nodes/%s/states/provision' % self.node.id,
json={'target': 'rebuild', 'deploy_steps': deploy_steps},
headers=mock.ANY, microversion='1.69',
retriable_status_codes=_common.RETRIABLE_STATUS_CODES
)
@mock.patch.object(node.Node, '_translate_response', mock.Mock()) @mock.patch.object(node.Node, '_translate_response', mock.Mock())
@mock.patch.object(node.Node, '_get_session', lambda self, x: x) @mock.patch.object(node.Node, '_get_session', lambda self, x: x)

View File

@@ -0,0 +1,4 @@
---
features:
- |
Adds ``deploy_steps`` to baremetal node provisioning.