Support disable_ramdisk during servicing
The node provision API rejects ``disable_ramdisk`` for servicing requests, even though the servicing backend supports skipping the ramdisk for steps marked as not requiring it and the client-side interface exposes the option. This change adds a new microversion to allow ``disable_ramdisk`` with ``target=service``. Closes-Bug: #2137065 Change-Id: I7e41ec27ac2cfc0255732bd14cad624ea612bd65 Signed-off-by: Afonne-CID <afonnepaulc@gmail.com>
This commit is contained in:
@@ -933,9 +933,11 @@ disable_power_off:
|
||||
type: boolean
|
||||
disable_ramdisk:
|
||||
description: |
|
||||
If set to ``true``, the ironic-python-agent ramdisk will not be booted for
|
||||
cleaning. Only clean steps explicitly marked as not requiring ramdisk can
|
||||
be executed in this mode. Only allowed for manual cleaning.
|
||||
If set to ``true``, the ironic-python-agent ramdisk will not be booted.
|
||||
Only steps explicitly marked as not requiring ramdisk can be executed in
|
||||
this mode.
|
||||
Allowed for manual cleaning (starting with API microversion 1.70) and for
|
||||
manual servicing (starting with API microversion 1.108).
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
REST API Version History
|
||||
========================
|
||||
|
||||
1.108 (Gazpacho)
|
||||
------------------------
|
||||
Add support for ``disable_ramdisk`` parameter to provisioning endpoint
|
||||
``/v1/nodes/{node_ident}/states/provision`` for the 'service' verb.
|
||||
|
||||
1.107 (Gazpacho)
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -1266,7 +1266,7 @@ class NodeStatesController(rest.RestController):
|
||||
rpc_node, target, runbook, clean_steps, service_steps
|
||||
)
|
||||
|
||||
api_utils.check_allow_clean_disable_ramdisk(target, disable_ramdisk)
|
||||
api_utils.check_allow_disable_ramdisk(target, disable_ramdisk)
|
||||
|
||||
if clean_steps and target != ir_states.VERBS['clean']:
|
||||
msg = (_('"clean_steps" is only valid when setting target '
|
||||
|
||||
@@ -2223,15 +2223,30 @@ def check_allow_boot_mode(node_capabilities, disallowed_boot_modes):
|
||||
op=_('provisioning'))
|
||||
|
||||
|
||||
def check_allow_clean_disable_ramdisk(target, disable_ramdisk):
|
||||
def check_allow_disable_ramdisk(target, disable_ramdisk):
|
||||
if disable_ramdisk is None:
|
||||
return
|
||||
elif api.request.version.minor < versions.MINOR_70_CLEAN_DISABLE_RAMDISK:
|
||||
raise exception.NotAcceptable(
|
||||
_("disable_ramdisk is not acceptable in this API version"))
|
||||
elif target != "clean":
|
||||
raise exception.BadRequest(
|
||||
_("disable_ramdisk is supported only with manual cleaning"))
|
||||
|
||||
minor = api.request.version.minor
|
||||
if target == states.VERBS['clean']:
|
||||
if minor < versions.MINOR_70_CLEAN_DISABLE_RAMDISK:
|
||||
raise exception.NotAcceptable(
|
||||
_("disable_ramdisk is not acceptable in this API version"))
|
||||
return
|
||||
|
||||
if target == states.VERBS['service']:
|
||||
if minor < versions.MINOR_108_SERVICE_DISABLE_RAMDISK:
|
||||
raise exception.NotAcceptable(
|
||||
_("disable_ramdisk is not acceptable in this API version"))
|
||||
return
|
||||
|
||||
raise exception.BadRequest(
|
||||
_("disable_ramdisk is supported only with manual cleaning "
|
||||
"or servicing"))
|
||||
|
||||
|
||||
def check_allow_clean_disable_ramdisk(target, disable_ramdisk):
|
||||
return check_allow_disable_ramdisk(target, disable_ramdisk)
|
||||
|
||||
|
||||
def allow_shards_endpoint():
|
||||
|
||||
@@ -145,6 +145,7 @@ BASE_VERSION = 1
|
||||
# v1.105: Remove broken ovn vtep metadata support
|
||||
# v1.106: Add shard filtering support for portgroups
|
||||
# v1.107: Add X-OpenStack-Request-Id header.
|
||||
# v1.108: Add disable_ramdisk support for servicing
|
||||
|
||||
MINOR_0_JUNO = 0
|
||||
MINOR_1_INITIAL_VERSION = 1
|
||||
@@ -254,6 +255,7 @@ MINOR_104_NODE_INSTANCE_NAME = 104
|
||||
MINOR_105_REMOVE_OVN_VTEP = 105
|
||||
MINOR_106_PORTGROUP_SHARD = 106
|
||||
MINOR_107_X_OPENSTACK_REQUEST_ID = 107
|
||||
MINOR_108_SERVICE_DISABLE_RAMDISK = 108
|
||||
|
||||
# When adding another version, update:
|
||||
# - MINOR_MAX_VERSION
|
||||
@@ -263,7 +265,7 @@ MINOR_107_X_OPENSTACK_REQUEST_ID = 107
|
||||
# - Add a comment describing the change above the list of consts
|
||||
|
||||
|
||||
MINOR_MAX_VERSION = MINOR_107_X_OPENSTACK_REQUEST_ID
|
||||
MINOR_MAX_VERSION = MINOR_108_SERVICE_DISABLE_RAMDISK
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_1_INITIAL_VERSION)
|
||||
|
||||
@@ -944,7 +944,7 @@ RELEASE_MAPPING = {
|
||||
# make it below. To release, we will preserve a version matching
|
||||
# the release as a separate block of text, like above.
|
||||
'master': {
|
||||
'api': '1.107',
|
||||
'api': '1.108',
|
||||
'rpc': '1.62',
|
||||
'networking_rpc': '1.0',
|
||||
'objects': {
|
||||
|
||||
@@ -6686,36 +6686,83 @@ ORHMKeXMO8fcK0By7CiMKwHSXCoEQgfQhWwpMdSsO8LgHCjh87DQc= """
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, ret.status_code)
|
||||
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'do_node_service', autospec=True)
|
||||
@mock.patch.object(api_node, '_check_service_steps', autospec=True)
|
||||
def test_service_disable_ramdisk(self, mock_check, mock_rpcapi):
|
||||
self.node.provision_state = states.SERVICEHOLD
|
||||
self.node.save()
|
||||
service_steps = [{"step": "upgrade_firmware", "interface": "deploy"}]
|
||||
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
|
||||
{'target': states.VERBS['service'],
|
||||
'service_steps': service_steps,
|
||||
'disable_ramdisk': True},
|
||||
headers={api_base.Version.string: "1.108"})
|
||||
self.assertEqual(http_client.ACCEPTED, ret.status_code)
|
||||
self.assertEqual(b'', ret.body)
|
||||
mock_check.assert_called_once_with(service_steps)
|
||||
mock_rpcapi.assert_called_once_with(mock.ANY, mock.ANY,
|
||||
self.node.uuid,
|
||||
service_steps, True,
|
||||
topic='test-topic')
|
||||
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'do_node_service', autospec=True)
|
||||
@mock.patch.object(api_node, '_check_service_steps', autospec=True)
|
||||
def test_service_disable_ramdisk_old_api(self, mock_check, mock_rpcapi):
|
||||
self.node.provision_state = states.SERVICEHOLD
|
||||
self.node.save()
|
||||
service_steps = [{"step": "upgrade_firmware", "interface": "deploy"}]
|
||||
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
|
||||
{'target': states.VERBS['service'],
|
||||
'service_steps': service_steps,
|
||||
'disable_ramdisk': True},
|
||||
headers={api_base.Version.string: "1.106"},
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, ret.status_code)
|
||||
mock_rpcapi.assert_not_called()
|
||||
|
||||
@mock.patch.object(api_utils, 'check_runbook_policy_and_retrieve',
|
||||
autospec=True)
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'do_node_clean', autospec=True)
|
||||
@mock.patch.object(api_node, '_check_clean_steps', autospec=True)
|
||||
def test_clean_with_runbook_disable_ramdisk(self, mock_check,
|
||||
mock_rpcapi, mock_policy):
|
||||
@mock.patch.object(rpcapi.ConductorAPI, 'do_node_service', autospec=True)
|
||||
@mock.patch.object(api_node, '_check_service_steps', autospec=True)
|
||||
def test_service_with_runbook_disable_ramdisk(self, mock_check,
|
||||
mock_rpcapi, mock_policy):
|
||||
objects.TraitList.create(self.context, self.node.id, ['CUSTOM_1'])
|
||||
self.node.refresh()
|
||||
|
||||
self.node.provision_state = states.MANAGEABLE
|
||||
self.node.provision_state = states.SERVICEHOLD
|
||||
self.node.save()
|
||||
|
||||
runbook = mock.Mock()
|
||||
runbook.name = 'CUSTOM_1'
|
||||
runbook.steps = [{"step": "erase_devices", "interface": "deploy",
|
||||
runbook.steps = [{"step": "upgrade_firmware", "interface": "deploy",
|
||||
"args": {}}]
|
||||
runbook.disable_ramdisk = True
|
||||
mock_policy.return_value = runbook
|
||||
|
||||
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
|
||||
{'target': states.VERBS['clean'],
|
||||
{'target': states.VERBS['service'],
|
||||
'runbook': runbook.name},
|
||||
headers={api_base.Version.string: "1.106"})
|
||||
headers={api_base.Version.string: "1.108"})
|
||||
self.assertEqual(http_client.ACCEPTED, ret.status_code)
|
||||
self.assertEqual(b'', ret.body)
|
||||
mock_policy.assert_has_calls([mock.call('baremetal:runbook:use',
|
||||
runbook.name)])
|
||||
mock_check.assert_called_once_with(runbook.steps)
|
||||
mock_rpcapi.assert_called_once_with(mock.ANY, mock.ANY,
|
||||
self.node.uuid, runbook.steps,
|
||||
True, topic='test-topic')
|
||||
|
||||
def test_disable_ramdisk_wrong_target(self):
|
||||
self.node.provision_state = states.AVAILABLE
|
||||
self.node.save()
|
||||
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
|
||||
{'target': states.VERBS['active'],
|
||||
'disable_ramdisk': True},
|
||||
headers={api_base.Version.string: "1.108"},
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.BAD_REQUEST, ret.status_code)
|
||||
self.assertIn('disable_ramdisk is supported only with manual '
|
||||
'cleaning or servicing', ret.json['error_message'])
|
||||
|
||||
def test_adopt_raises_error_before_1_17(self):
|
||||
"""Test that a lower API client cannot use the adopt verb"""
|
||||
ret = self.put_json('/nodes/%s/states/provision' % self.node.uuid,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support for using ``disable_ramdisk`` with manual servicing
|
||||
(``target=service``) starting with Bare Metal API microversion ``1.108``.
|
||||
This enables running service steps that are explicitly marked as not
|
||||
requiring a ramdisk.
|
||||
Reference in New Issue
Block a user