summaryrefslogtreecommitdiff
path: root/ironic/api
diff options
context:
space:
mode:
authorCenne <cennedee+opendev@protonmail.com>2021-07-08 18:37:45 +0200
committerCenne <cennedee+opendev@protonmail.com>2021-08-23 19:38:58 +0200
commitbc95c92f7c122b1217459a1d7a125fae47749e6e (patch)
tree6ffd3cc271ac841e2a2a5fc72524ee2f1f625a37 /ironic/api
parent9f32ceda1a87ac83d4ac84faec16c01ba27c549e (diff)
downloadironic-bc95c92f7c122b1217459a1d7a125fae47749e6e.tar.gz
Add api endpoints for changing boot_mode and secure_boot state
Done: - Node API endpoints expose - RPC methods - Conductor Manager methods - Conductor utils new methods - RBAC new policies - Node API tests - Manager Tests (+ some testing for utils methods) - RBAC tests - Docs (api-ref) - REST API version history - Releasenotes Story: 2008567 Task: 41709 Change-Id: I2d72389edf546b99c536c6b130ca85ababf80591
Diffstat (limited to 'ironic/api')
-rw-r--r--ironic/api/controllers/v1/node.py107
-rw-r--r--ironic/api/controllers/v1/versions.py4
2 files changed, 110 insertions, 1 deletions
diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py
index 8a53c4fbb..e40743617 100644
--- a/ironic/api/controllers/v1/node.py
+++ b/ironic/api/controllers/v1/node.py
@@ -40,6 +40,7 @@ from ironic.api.controllers.v1 import versions
from ironic.api.controllers.v1 import volume
from ironic.api import method
from ironic.common import args
+from ironic.common import boot_modes
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import policy
@@ -120,6 +121,9 @@ ALLOWED_TARGET_POWER_STATES = (ir_states.POWER_ON,
ir_states.SOFT_REBOOT,
ir_states.SOFT_POWER_OFF)
+ALLOWED_TARGET_BOOT_MODES = (boot_modes.LEGACY_BIOS,
+ boot_modes.UEFI)
+
_NODE_DESCRIPTION_MAX_LENGTH = 4096
_NETWORK_DATA_SCHEMA = None
@@ -710,6 +714,8 @@ def node_states_convert(rpc_node):
class NodeStatesController(rest.RestController):
_custom_actions = {
+ 'boot_mode': ['PUT'],
+ 'secure_boot': ['PUT'],
'power': ['PUT'],
'provision': ['PUT'],
'raid': ['PUT'],
@@ -822,6 +828,107 @@ class NodeStatesController(rest.RestController):
url_args = '/'.join([node_ident, 'states'])
api.response.location = link.build_url('nodes', url_args)
+ @METRICS.timer('NodeStatesController.boot_mode')
+ @method.expose(status_code=http_client.ACCEPTED)
+ @args.validate(node_ident=args.uuid_or_name, target=args.string)
+ def boot_mode(self, node_ident, target):
+ """Asynchronous set the boot mode of the node.
+
+ :param node_ident: the UUID or logical name of a node.
+ :param target: The desired boot_mode for the node. (uefi/bios)
+ :raises: NotFound (HTTP 404) if requested version of the API
+ is less than 1.76.
+ :raises: InvalidParameterValue (HTTP 400) if the requested target
+ state is not valid.
+ :raises: Conflict (HTTP 409) if a node is in adopting state or
+ another transient state.
+
+ """
+ rpc_node = api_utils.check_node_policy_and_retrieve(
+ 'baremetal:node:set_boot_mode', node_ident)
+ topic = api.request.rpcapi.get_topic_for(rpc_node)
+
+ if (api.request.version.minor
+ < versions.MINOR_76_NODE_CHANGE_BOOT_MODE):
+ raise exception.NotFound(
+ (_("This endpoint is supported starting with the API version "
+ "1.%(min_version)s") %
+ {'min_version': versions.MINOR_76_NODE_CHANGE_BOOT_MODE}))
+
+ if target not in ALLOWED_TARGET_BOOT_MODES:
+ msg = (_("Invalid boot mode %(mode)s requested for node. "
+ "Allowed boot modes are: "
+ "%(modes)s") %
+ {'mode': target,
+ 'modes': ', '.join(ALLOWED_TARGET_BOOT_MODES)})
+ raise exception.InvalidParameterValue(msg)
+
+ # NOTE(cenne): This currenly includes the ADOPTING state
+ if rpc_node.provision_state in ir_states.UNSTABLE_STATES:
+ msg = _("Node is in %(state)s state. Since node is transitioning, "
+ "the boot mode will not be set as this may interfere "
+ "with ongoing changes and result in erroneous modification"
+ ". Try again later.")
+ raise exception.Conflict(msg,
+ action=target, node=node_ident,
+ state=rpc_node.provision_state
+ )
+ api.request.rpcapi.change_node_boot_mode(api.request.context,
+ rpc_node.uuid, target,
+ topic=topic)
+ # Set the HTTP Location Header
+ url_args = '/'.join([node_ident, 'states'])
+ api.response.location = link.build_url('nodes', url_args)
+
+ @METRICS.timer('NodeStatesController.secure_boot')
+ @method.expose(status_code=http_client.ACCEPTED)
+ @args.validate(node_ident=args.uuid_or_name, target=args.boolean)
+ def secure_boot(self, node_ident, target):
+ """Asynchronous set the secure_boot state of the node.
+
+ :param node_ident: the UUID or logical name of a node.
+ :param target: The desired secure_boot for the node. (True/False)
+ :raises: NotFound (HTTP 404) if requested version of the API
+ is less than 1.76.
+ :raises: InvalidParameterValue (HTTP 400) if the requested target
+ state is not valid.
+ :raises: Conflict (HTTP 409) if a node is in adopting state.
+
+ """
+ rpc_node = api_utils.check_node_policy_and_retrieve(
+ 'baremetal:node:set_secure_boot', node_ident)
+ topic = api.request.rpcapi.get_topic_for(rpc_node)
+
+ if (api.request.version.minor
+ < versions.MINOR_76_NODE_CHANGE_BOOT_MODE):
+ raise exception.NotFound(
+ (_("This endpoint is supported starting with the API version "
+ "1.%(min_version)s") %
+ {'min_version': versions.MINOR_76_NODE_CHANGE_BOOT_MODE}))
+ # NOTE(cenne): This is to exclude target=None or other invalid values
+ if target not in (True, False):
+ msg = (_("Invalid secure_boot %(state)s requested for node. "
+ "Allowed secure_boot states are: True, False) ") %
+ {'state': target})
+ raise exception.InvalidParameterValue(msg)
+
+ # NOTE(cenne): This currenly includes the ADOPTING state
+ if rpc_node.provision_state in ir_states.UNSTABLE_STATES:
+ msg = _("Node is in %(state)s state. Since node is transitioning, "
+ "the boot mode will not be set as this may interfere "
+ "with ongoing changes and result in erroneous modification"
+ ". Try again later.")
+ raise exception.Conflict(msg,
+ action=target, node=node_ident,
+ state=rpc_node.provision_state
+ )
+ api.request.rpcapi.change_node_secure_boot(api.request.context,
+ rpc_node.uuid, target,
+ topic=topic)
+ # Set the HTTP Location Header
+ url_args = '/'.join([node_ident, 'states'])
+ api.response.location = link.build_url('nodes', url_args)
+
def _do_provision_action(self, rpc_node, target, configdrive=None,
clean_steps=None, deploy_steps=None,
rescue_password=None, disable_ramdisk=None):
diff --git a/ironic/api/controllers/v1/versions.py b/ironic/api/controllers/v1/versions.py
index 1af83c431..9cd890e8f 100644
--- a/ironic/api/controllers/v1/versions.py
+++ b/ironic/api/controllers/v1/versions.py
@@ -113,6 +113,7 @@ BASE_VERSION = 1
# v1.73: Add support for deploy and undeploy verbs
# v1.74: Add bios registry to /v1/nodes/{node}/bios/{setting}
# v1.75: Add boot_mode, secure_boot fields to node object.
+# v1.76: Add support for changing boot_mode and secure_boot state
MINOR_0_JUNO = 0
MINOR_1_INITIAL_VERSION = 1
@@ -190,6 +191,7 @@ MINOR_72_HEARTBEAT_STATUS = 72
MINOR_73_DEPLOY_UNDEPLOY_VERBS = 73
MINOR_74_BIOS_REGISTRY = 74
MINOR_75_NODE_BOOT_MODE = 75
+MINOR_76_NODE_CHANGE_BOOT_MODE = 76
# When adding another version, update:
# - MINOR_MAX_VERSION
@@ -197,7 +199,7 @@ MINOR_75_NODE_BOOT_MODE = 75
# explanation of what changed in the new version
# - common/release_mappings.py, RELEASE_MAPPING['master']['api']
-MINOR_MAX_VERSION = MINOR_75_NODE_BOOT_MODE
+MINOR_MAX_VERSION = MINOR_76_NODE_CHANGE_BOOT_MODE
# String representations of the minor and maximum versions
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)