From 116d875c04fdf4e922ff428920ce55ace3d52254 Mon Sep 17 00:00:00 2001 From: Cenne Date: Fri, 13 Aug 2021 16:34:58 +0200 Subject: [PATCH] Add support for changing baremetal node's boot_mode and secure_boot states depends-on: https://review.opendev.org/c/openstack/ironic/+/800084 Story: 2008567 Task: 41709 Change-Id: I941a84f36ce79c4cbd0968f4120c66d59091fdd9 --- openstack/baremetal/v1/_common.py | 3 + openstack/baremetal/v1/_proxy.py | 21 ++++++ openstack/baremetal/v1/node.py | 65 ++++++++++++++++++- .../tests/unit/baremetal/v1/test_node.py | 48 ++++++++++++++ ...d-node-boot-mode-set-5718a8d6511b4826.yaml | 5 ++ 5 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/add-node-boot-mode-set-5718a8d6511b4826.yaml diff --git a/openstack/baremetal/v1/_common.py b/openstack/baremetal/v1/_common.py index c2cdaac55..7981c95f5 100644 --- a/openstack/baremetal/v1/_common.py +++ b/openstack/baremetal/v1/_common.py @@ -79,6 +79,9 @@ CONFIG_DRIVE_DICT_VERSION = '1.56' DEPLOY_STEPS_VERSION = '1.69' """API version in which deploy_steps was added to node provisioning.""" +CHANGE_BOOT_MODE_VERSION = '1.76' +"""API version in which boot_mode and secure_boot states can be changed""" + class ListMixin: diff --git a/openstack/baremetal/v1/_proxy.py b/openstack/baremetal/v1/_proxy.py index 0b9e21413..f5a888106 100644 --- a/openstack/baremetal/v1/_proxy.py +++ b/openstack/baremetal/v1/_proxy.py @@ -408,6 +408,27 @@ class Proxy(proxy.Proxy): res = self._get_resource(_node.Node, node) return res.set_boot_device(self, boot_device, persistent=persistent) + def set_node_boot_mode(self, node, target): + """Make a request to change node's boot mode + + :param node: The value can be the name or ID of a node or a + :class:`~openstack.baremetal.v1.node.Node` instance. + :param target: Boot mode to set for node, one of either 'uefi'/'bios'. + """ + res = self._get_resource(_node.Node, node) + return res.set_boot_mode(self, target) + + def set_node_secure_boot(self, node, target): + """Make a request to change node's secure boot state + + :param node: The value can be the name or ID of a node or a + :class:`~openstack.baremetal.v1.node.Node` instance. + :param target: Boolean indicating secure boot state to set. + True/False corresponding to 'on'/'off' respectively. + """ + res = self._get_resource(_node.Node, node) + return res.set_secure_boot(self, target) + def wait_for_nodes_provision_state(self, nodes, expected_state, timeout=None, abort_on_failed_state=True, diff --git a/openstack/baremetal/v1/node.py b/openstack/baremetal/v1/node.py index 66cf2cd22..75e407f70 100644 --- a/openstack/baremetal/v1/node.py +++ b/openstack/baremetal/v1/node.py @@ -91,8 +91,8 @@ class Node(_common.ListMixin, resource.Resource): is_maintenance='maintenance', ) - # Node states boot_mode and secure_boot introduced in 1.75 (Xena). - _max_microversion = '1.75' + # Ability to change boot_mode and secure_boot, introduced in 1.76 (Xena). + _max_microversion = '1.76' # Properties #: The UUID of the allocation associated with this node. Added in API @@ -853,6 +853,67 @@ class Node(_common.ListMixin, resource.Resource): .format(node=self.id)) exceptions.raise_from_response(response, error_message=msg) + def set_boot_mode(self, session, target): + """Make a request to change node's boot mode + + This call is asynchronous, it will return success as soon as the Bare + Metal service acknowledges the request. + + :param session: The session to use for making this request. + :param target: Boot mode to set for node, one of either 'uefi'/'bios'. + :raises: ValueError if ``target`` is not one of 'uefi or 'bios'. + """ + session = self._get_session(session) + version = utils.pick_microversion(session, + _common.CHANGE_BOOT_MODE_VERSION) + request = self._prepare_request(requires_id=True) + request.url = utils.urljoin(request.url, 'states', 'boot_mode') + if target not in ('uefi', 'bios'): + raise ValueError("Unrecognized boot mode %s." + "Boot mode should be one of 'uefi' or 'bios'." + % target) + body = {'target': target} + + response = session.put( + request.url, json=body, + headers=request.headers, microversion=version, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + msg = ("Failed to change boot mode for node {node}" + .format(node=self.id)) + exceptions.raise_from_response(response, error_message=msg) + + def set_secure_boot(self, session, target): + """Make a request to change node's secure boot state + + This call is asynchronous, it will return success as soon as the Bare + Metal service acknowledges the request. + + :param session: The session to use for making this request. + :param bool target: Boolean indicating secure boot state to set. + True/False corresponding to 'on'/'off' respectively. + :raises: ValueError if ``target`` is not boolean. + """ + session = self._get_session(session) + version = utils.pick_microversion(session, + _common.CHANGE_BOOT_MODE_VERSION) + request = self._prepare_request(requires_id=True) + request.url = utils.urljoin(request.url, 'states', 'secure_boot') + if not isinstance(target, bool): + raise ValueError("Invalid target %s. It should be True or False " + "corresponding to secure boot state 'on' or 'off'" + % target) + body = {'target': target} + + response = session.put( + request.url, json=body, + headers=request.headers, microversion=version, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + msg = ("Failed to change secure boot state for {node}" + .format(node=self.id)) + exceptions.raise_from_response(response, error_message=msg) + def add_trait(self, session, trait): """Add a trait to a node. diff --git a/openstack/tests/unit/baremetal/v1/test_node.py b/openstack/tests/unit/baremetal/v1/test_node.py index 27a5012dc..99c8835ad 100644 --- a/openstack/tests/unit/baremetal/v1/test_node.py +++ b/openstack/tests/unit/baremetal/v1/test_node.py @@ -764,6 +764,54 @@ class TestNodeSetBootDevice(base.TestCase): retriable_status_codes=_common.RETRIABLE_STATUS_CODES) +@mock.patch.object(utils, 'pick_microversion', lambda session, v: v) +@mock.patch.object(node.Node, 'fetch', lambda self, session: self) +@mock.patch.object(exceptions, 'raise_from_response', mock.Mock()) +class TestNodeSetBootMode(base.TestCase): + + def setUp(self): + super(TestNodeSetBootMode, self).setUp() + self.node = node.Node(**FAKE) + self.session = mock.Mock(spec=adapter.Adapter, + default_microversion='1.1') + + def test_node_set_boot_mode(self): + self.node.set_boot_mode(self.session, 'uefi') + self.session.put.assert_called_once_with( + 'nodes/%s/states/boot_mode' % self.node.id, + json={'target': 'uefi'}, + headers=mock.ANY, microversion=mock.ANY, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_node_set_boot_mode_invalid_mode(self): + self.assertRaises(ValueError, + self.node.set_boot_mode, self.session, 'invalid-efi') + + +@mock.patch.object(utils, 'pick_microversion', lambda session, v: v) +@mock.patch.object(node.Node, 'fetch', lambda self, session: self) +@mock.patch.object(exceptions, 'raise_from_response', mock.Mock()) +class TestNodeSetSecureBoot(base.TestCase): + + def setUp(self): + super(TestNodeSetSecureBoot, self).setUp() + self.node = node.Node(**FAKE) + self.session = mock.Mock(spec=adapter.Adapter, + default_microversion='1.1') + + def test_node_set_secure_boot(self): + self.node.set_secure_boot(self.session, True) + self.session.put.assert_called_once_with( + 'nodes/%s/states/secure_boot' % self.node.id, + json={'target': True}, + headers=mock.ANY, microversion=mock.ANY, + retriable_status_codes=_common.RETRIABLE_STATUS_CODES) + + def test_node_set_secure_boot_invalid_none(self): + self.assertRaises(ValueError, + self.node.set_secure_boot, self.session, None) + + @mock.patch.object(utils, 'pick_microversion', lambda session, v: v) @mock.patch.object(node.Node, 'fetch', lambda self, session: self) @mock.patch.object(exceptions, 'raise_from_response', mock.Mock()) diff --git a/releasenotes/notes/add-node-boot-mode-set-5718a8d6511b4826.yaml b/releasenotes/notes/add-node-boot-mode-set-5718a8d6511b4826.yaml new file mode 100644 index 000000000..23d6e439a --- /dev/null +++ b/releasenotes/notes/add-node-boot-mode-set-5718a8d6511b4826.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add support for changing node states ``boot_mode`` and ``secure_boot`` + in sync with functionality introduced in API 1.76.