support passing disable_ramdisk for clean and service state

When manually cleaning or servicing a node, it is possible to
not have it boot the ironic-python-agent. Wire this up to the
CLI as well for manual cleaning and servicing.

Change-Id: Ibcedfbc2e65756b98c9b7334c7eadfc925d70fd7
This commit is contained in:
Doug Goldstein
2024-07-24 21:35:40 -05:00
committed by Doug Goldstein
parent 6638d86baf
commit 9feda5a8e2
5 changed files with 193 additions and 24 deletions

View File

@@ -103,6 +103,13 @@ class ProvisionStateBaremetalNode(command.Command):
rescue_password = getattr(parsed_args, 'rescue_password', None)
disable_ramdisk = getattr(parsed_args, 'disable_ramdisk', None)
if runbook and disable_ramdisk:
raise exc.CommandError(
_("You cannot supply --runbook and --disable-ramdisk together")
)
for node in parsed_args.nodes:
baremetal_client.node.set_provision_state(
node,
@@ -112,7 +119,8 @@ class ProvisionStateBaremetalNode(command.Command):
deploysteps=deploy_steps,
rescue_password=rescue_password,
servicesteps=service_steps,
runbook=runbook)
runbook=runbook,
disable_ramdisk=disable_ramdisk)
class ProvisionStateWithWait(ProvisionStateBaremetalNode):
@@ -308,6 +316,13 @@ class CleanBaremetalNode(ProvisionStateWithWait):
metavar='<runbook>',
help=_("The identifier of a predefined runbook to use for "
"cleaning."))
parser.add_argument(
'--disable-ramdisk',
action='store_true',
default=None,
help=_("ironic-python-agent will not be booted for cleaning. "
"Only steps explicitly marked as not requiring "
"ironic-python-agent can be executed with this set."))
return parser
@@ -335,6 +350,13 @@ class ServiceBaremetalNode(ProvisionStateWithWait):
metavar='<runbook>',
help=_("The identifier of a predefined runbook to use for "
"servicing."))
parser.add_argument(
'--disable-ramdisk',
action='store_true',
default=None,
help=_("ironic-python-agent will not be booted for cleaning. "
"Only steps explicitly marked as not requiring "
"ironic-python-agent can be executed with this set."))
return parser

View File

@@ -60,7 +60,7 @@ class TestAbort(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'abort', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestAdopt(TestBaremetal):
@@ -84,7 +84,8 @@ class TestAdopt(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'adopt',
cleansteps=None, deploysteps=None, configdrive=None,
rescue_password=None, servicesteps=None, runbook=None)
rescue_password=None, servicesteps=None, runbook=None,
disable_ramdisk=None)
self.baremetal_mock.node.wait_for_provision_state.assert_not_called()
def test_adopt_baremetal_provision_state_active_and_wait(self):
@@ -104,7 +105,8 @@ class TestAdopt(TestBaremetal):
test_node.set_provision_state.assert_called_once_with(
'node_uuid', 'adopt',
cleansteps=None, deploysteps=None, configdrive=None,
rescue_password=None, servicesteps=None, runbook=None)
rescue_password=None, servicesteps=None, runbook=None,
disable_ramdisk=None)
test_node.wait_for_provision_state.assert_called_once_with(
['node_uuid'], expected_state='active',
poll_interval=2, timeout=15)
@@ -126,7 +128,8 @@ class TestAdopt(TestBaremetal):
test_node.set_provision_state.assert_called_once_with(
'node_uuid', 'adopt',
cleansteps=None, deploysteps=None, configdrive=None,
rescue_password=None, servicesteps=None, runbook=None)
rescue_password=None, servicesteps=None, runbook=None,
disable_ramdisk=None)
test_node.wait_for_provision_state.assert_called_once_with(
['node_uuid'], expected_state='active',
poll_interval=2, timeout=0)
@@ -176,7 +179,7 @@ class TestClean(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'clean', cleansteps=steps_dict, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
def test_clean_with_runbook(self):
runbook_name = 'runbook_name'
@@ -194,7 +197,7 @@ class TestClean(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'clean', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=runbook_name)
runbook=runbook_name, disable_ramdisk=None)
def test_clean_with_runbook_and_steps(self):
runbook_name = 'runbook_name'
@@ -225,6 +228,58 @@ class TestClean(TestBaremetal):
self.check_parser,
self.cmd, arglist, verifylist)
def test_clean_with_steps_and_disable_ramdisk(self):
steps_dict = {
"clean_steps": [{
"interface": "raid",
"step": "delete_configuration"
}, {
"interface": "raid",
"step": "create_configuration",
"args": {"create_nonroot_volumes": False}
}]
}
steps_json = json.dumps(steps_dict)
arglist = ['--clean-steps',
steps_json,
'--disable-ramdisk',
'node_uuid']
verifylist = [
('clean_steps', steps_json),
('provision_state', 'clean'),
('disable_ramdisk', True),
('nodes', ['node_uuid']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'clean', cleansteps=steps_dict, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None, disable_ramdisk=True)
def test_clean_with_runbook_and_disable_ramdisk(self):
runbook_name = 'runbook_name'
arglist = ['--runbook',
runbook_name,
'--disable-ramdisk',
'node_uuid']
verifylist = [
('runbook', runbook_name),
('provision_state', 'clean'),
('disable_ramdisk', True),
('nodes', ['node_uuid']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exc.CommandError,
self.cmd.take_action, parsed_args)
self.assertFalse(self.baremetal_mock.node.set_provision_state.called)
class TestService(TestBaremetal):
def setUp(self):
@@ -260,7 +315,7 @@ class TestService(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'service', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=steps_dict,
runbook=None)
runbook=None, disable_ramdisk=None)
def test_service_with_runbook(self):
runbook_name = 'runbook_name'
@@ -276,9 +331,9 @@ class TestService(TestBaremetal):
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'service', cleansteps=None, configdrive=None,
'node_uuid', 'service', configdrive=None, cleansteps=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=runbook_name)
runbook=runbook_name, disable_ramdisk=None)
def test_service_with_runbook_and_steps(self):
runbook_name = 'runbook_name'
@@ -308,6 +363,58 @@ class TestService(TestBaremetal):
self.check_parser,
self.cmd, arglist, verifylist)
def test_service_with_steps_and_disable_ramdisk(self):
steps_dict = {
"service_steps": [{
"interface": "raid",
"step": "delete_configuration"
}, {
"interface": "raid",
"step": "create_configuration",
"args": {"create_nonroot_volumes": False}
}]
}
steps_json = json.dumps(steps_dict)
arglist = ['--service-steps',
steps_json,
'--disable-ramdisk',
'node_uuid']
verifylist = [
('service_steps', steps_json),
('provision_state', 'service'),
('disable_ramdisk', True),
('nodes', ['node_uuid']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'service', configdrive=None, cleansteps=None,
deploysteps=None, rescue_password=None, servicesteps=steps_dict,
runbook=None, disable_ramdisk=True)
def test_service_with_runbook_and_disable_ramdisk(self):
runbook_name = 'runbook_name'
arglist = ['--runbook',
runbook_name,
'--disable-ramdisk',
'node_uuid']
verifylist = [
('runbook', runbook_name),
('provision_state', 'service'),
('disable_ramdisk', True),
('nodes', ['node_uuid']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(exc.CommandError,
self.cmd.take_action, parsed_args)
self.assertFalse(self.baremetal_mock.node.set_provision_state.called)
class TestInspect(TestBaremetal):
def setUp(self):
@@ -330,7 +437,7 @@ class TestInspect(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'inspect', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestManage(TestBaremetal):
@@ -354,7 +461,7 @@ class TestManage(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'manage', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestProvide(TestBaremetal):
@@ -378,7 +485,7 @@ class TestProvide(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'provide', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestRebuild(TestBaremetal):
@@ -402,7 +509,7 @@ class TestRebuild(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'rebuild', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestUndeploy(TestBaremetal):
@@ -426,7 +533,7 @@ class TestUndeploy(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'deleted', cleansteps=None, configdrive=None,
deploysteps=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestBootdeviceSet(TestBaremetal):
@@ -2013,7 +2120,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
'node_uuid', 'active',
cleansteps=None, deploysteps=[{"interface": "deploy"}],
configdrive='path/to/drive', rescue_password=None,
servicesteps=None, runbook=None)
servicesteps=None, runbook=None, disable_ramdisk=None)
def test_deploy_baremetal_provision_state_active_and_configdrive_dict(
self):
@@ -2032,7 +2139,8 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'active',
cleansteps=None, deploysteps=None, configdrive={'meta_data': {}},
rescue_password=None, servicesteps=None, runbook=None)
rescue_password=None, servicesteps=None, runbook=None,
disable_ramdisk=None)
def test_deploy_no_wait(self):
arglist = ['node_uuid']
@@ -2097,7 +2205,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
test_node.set_provision_state.assert_has_calls([
mock.call(n, 'active', cleansteps=None, deploysteps=None,
configdrive=None, rescue_password=None,
servicesteps=None, runbook=None)
servicesteps=None, runbook=None, disable_ramdisk=None)
for n in ['node_uuid', 'node_name']
])
test_node.wait_for_provision_state.assert_called_once_with(
@@ -2321,7 +2429,7 @@ class TestRescueBaremetalProvisionState(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'rescue', cleansteps=None, deploysteps=None,
configdrive=None, rescue_password='supersecret',
servicesteps=None, runbook=None)
servicesteps=None, runbook=None, disable_ramdisk=None)
def test_rescue_baremetal_provision_state_rescue_and_wait(self):
arglist = ['node_uuid',
@@ -2513,7 +2621,7 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
'node_uuid', 'rebuild',
cleansteps=None, deploysteps=[{"interface": "deploy"}],
configdrive='path/to/drive', rescue_password=None,
servicesteps=None, runbook=None)
servicesteps=None, runbook=None, disable_ramdisk=None)
def test_rebuild_no_wait(self):
arglist = ['node_uuid']
@@ -2529,7 +2637,8 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'rebuild',
cleansteps=None, deploysteps=None, configdrive=None,
rescue_password=None, servicesteps=None, runbook=None)
rescue_password=None, servicesteps=None, runbook=None,
disable_ramdisk=None)
self.baremetal_mock.node.wait_for_provision_state.assert_not_called()
@@ -2648,7 +2757,7 @@ class TestUnrescueBaremetalProvisionState(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'unrescue', cleansteps=None, deploysteps=None,
configdrive=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
def test_unrescue_baremetal_provision_state_active_and_wait(self):
arglist = ['node_uuid',
@@ -4764,7 +4873,7 @@ class TestUnholdBaremetalProvisionState(TestBaremetal):
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
'node_uuid', 'unhold', cleansteps=None, deploysteps=None,
configdrive=None, rescue_password=None, servicesteps=None,
runbook=None)
runbook=None, disable_ramdisk=None)
class TestListFirmwareComponents(TestBaremetal):

View File

@@ -1771,6 +1771,20 @@ class NodeManagerTest(testtools.TestCase):
]
self.assertEqual(expect, self.api.calls)
def test_node_set_provision_state_with_cleansteps_disable_ramdisk(self):
cleansteps = [{"step": "delete_configuration", "interface": "raid"}]
target_state = 'clean'
self.mgr.set_provision_state(NODE1['uuid'], target_state,
cleansteps=cleansteps,
disable_ramdisk=True)
body = {'target': target_state,
'clean_steps': cleansteps,
'disable_ramdisk': True}
expect = [
('PUT', '/v1/nodes/%s/states/provision' % NODE1['uuid'], {}, body),
]
self.assertEqual(expect, self.api.calls)
def test_node_set_provision_state_with_deploysteps(self):
deploysteps = [{"step": "upgrade", "interface": "deploy"}]
target_state = 'active'

View File

@@ -729,7 +729,7 @@ class NodeManager(base.CreateManager):
self, node_uuid, state, configdrive=None, cleansteps=None,
rescue_password=None, os_ironic_api_version=None,
global_request_id=None, deploysteps=None,
servicesteps=None, runbook=None):
servicesteps=None, runbook=None, disable_ramdisk=None):
"""Set the provision state for the node.
:param node_uuid: The UUID or name of the node.
@@ -769,6 +769,10 @@ class NodeManager(base.CreateManager):
'service'.
:param runbook: The identifier of a predefined runbook to use for
provisioning.
:param disable_ramdisk: Boolean if set to true will not boot the
ironic-python-agent for cleaning. Only valid when setting 'state'
to 'clean' or 'service' and only for steps explicitly marked as
not requiring the ironic-python-agent can use this.
:raises: InvalidAttribute if there was an error with the clean steps or
deploy steps
:returns: The status of the request
@@ -812,6 +816,16 @@ class NodeManager(base.CreateManager):
if runbook:
body['runbook'] = runbook
if isinstance(disable_ramdisk, bool):
body['disable_ramdisk'] = disable_ramdisk
elif disable_ramdisk:
try:
body['disable_ramdisk'] = strutils.bool_from_string(state,
True)
except ValueError as e:
raise exc.InvalidAttribute(_("Argument 'disable_ramdisk': "
"%(err)s") % {'err': e})
return self.update(path, body, http_method='PUT',
os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)

View File

@@ -0,0 +1,10 @@
---
features:
- |
Adds support for ``NodeManager.set_provision_state``,
to accept ``disable_ramdisk`` keyword argument to disable booting
ironic-python-agent for clean and service steps that support it.
- |
Add ``--disable-ramdisk`` CLI option to `node clean` and
`node service` to disable booting ironic-python-agent for steps
that support it.