Add support for RESCUE and UNRESCUE provision states
Adding support for rescue and unrescue to OSC. Co-Authored-By: Michael Turek <mjturek@linux.vnet.ibm.com> Co-Authored-By: Dao Cong Tien <tiendc@vn.fujitsu.com> Change-Id: Id865d7c9a40e8d85242befb2a0335abe0c52dac7 Depends-On: I3953ff0b1ca000f8ae83fb7b3c663f848a149345 Partial-bug: 1526449
This commit is contained in:
parent
683b7c6e78
commit
fce885bf64
@ -44,7 +44,7 @@ from ironicclient import exc
|
||||
# http://specs.openstack.org/openstack/ironic-specs/specs/kilo/api-microversions.html # noqa
|
||||
# for full details.
|
||||
DEFAULT_VER = '1.9'
|
||||
LAST_KNOWN_API_VERSION = 37
|
||||
LAST_KNOWN_API_VERSION = 38
|
||||
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -67,12 +67,14 @@ class ProvisionStateBaremetalNode(command.Command):
|
||||
clean_steps = utils.handle_json_or_file_arg(clean_steps)
|
||||
|
||||
config_drive = getattr(parsed_args, 'config_drive', None)
|
||||
rescue_password = getattr(parsed_args, 'rescue_password', None)
|
||||
|
||||
baremetal_client.node.set_provision_state(
|
||||
parsed_args.node,
|
||||
parsed_args.provision_state,
|
||||
configdrive=config_drive,
|
||||
cleansteps=clean_steps)
|
||||
cleansteps=clean_steps,
|
||||
rescuepassword=rescue_password)
|
||||
|
||||
|
||||
class ProvisionStateWithWait(ProvisionStateBaremetalNode):
|
||||
@ -926,6 +928,25 @@ class RebuildBaremetalNode(ProvisionStateWithWait):
|
||||
return parser
|
||||
|
||||
|
||||
class RescueBaremetalNode(ProvisionStateWithWait):
|
||||
"""Set provision state of baremetal node to 'rescue'"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".RescueBaremetalNode")
|
||||
PROVISION_STATE = 'rescue'
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(RescueBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--rescue-password',
|
||||
metavar='<rescue-password>',
|
||||
required=True,
|
||||
default=None,
|
||||
help=("The password that will be used to login to the rescue "
|
||||
"ramdisk. The value should be a string."))
|
||||
return parser
|
||||
|
||||
|
||||
class SetBaremetalNode(command.Command):
|
||||
"""Set baremetal properties"""
|
||||
|
||||
@ -1221,6 +1242,13 @@ class UndeployBaremetalNode(ProvisionStateWithWait):
|
||||
PROVISION_STATE = 'deleted'
|
||||
|
||||
|
||||
class UnrescueBaremetalNode(ProvisionStateWithWait):
|
||||
"""Set provision state of baremetal node to 'unrescue'"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".UnrescueBaremetalNode")
|
||||
PROVISION_STATE = 'unrescue'
|
||||
|
||||
|
||||
class UnsetBaremetalNode(command.Command):
|
||||
"""Unset baremetal properties"""
|
||||
log = logging.getLogger(__name__ + ".UnsetBaremetalNode")
|
||||
|
@ -55,7 +55,8 @@ class TestAdopt(TestBaremetal):
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'adopt', cleansteps=None, configdrive=None)
|
||||
'node_uuid', 'adopt',
|
||||
cleansteps=None, configdrive=None, rescuepassword=None)
|
||||
|
||||
def test_adopt_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
@ -1199,7 +1200,7 @@ class TestDeployBaremetalProvisionState(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'active',
|
||||
cleansteps=None, configdrive='path/to/drive')
|
||||
cleansteps=None, configdrive='path/to/drive', rescuepassword=None)
|
||||
|
||||
def test_deploy_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
@ -1379,6 +1380,80 @@ class TestCleanBaremetalProvisionState(TestBaremetal):
|
||||
poll_interval=10, timeout=0)
|
||||
|
||||
|
||||
class TestRescueBaremetalProvisionState(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestRescueBaremetalProvisionState, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.RescueBaremetalNode(self.app, None)
|
||||
|
||||
def test_rescue_baremetal_no_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
'--rescue-password', 'supersecret']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'rescue'),
|
||||
('rescue_password', 'supersecret'),
|
||||
]
|
||||
|
||||
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', 'rescue', cleansteps=None, configdrive=None,
|
||||
rescuepassword='supersecret')
|
||||
|
||||
def test_rescue_baremetal_provision_state_rescue_and_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
'--wait', '15',
|
||||
'--rescue-password', 'supersecret']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'rescue'),
|
||||
('rescue_password', 'supersecret'),
|
||||
('wait_timeout', 15)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
test_node = self.baremetal_mock.node
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
'node_uuid', expected_state='rescue',
|
||||
poll_interval=10, timeout=15)
|
||||
|
||||
def test_rescue_baremetal_provision_state_default_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
'--wait',
|
||||
'--rescue-password', 'supersecret']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'rescue'),
|
||||
('rescue_password', 'supersecret'),
|
||||
('wait_timeout', 0)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
test_node = self.baremetal_mock.node
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
'node_uuid', expected_state='rescue',
|
||||
poll_interval=10, timeout=0)
|
||||
|
||||
def test_rescue_baremetal_no_rescue_password(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('provision_state', 'rescue')]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestInspectBaremetalProvisionState(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestInspectBaremetalProvisionState, self).setUp()
|
||||
@ -1515,7 +1590,8 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'rebuild',
|
||||
cleansteps=None, configdrive='path/to/drive')
|
||||
cleansteps=None, configdrive='path/to/drive',
|
||||
rescuepassword=None)
|
||||
|
||||
def test_rebuild_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
@ -1530,7 +1606,8 @@ class TestRebuildBaremetalProvisionState(TestBaremetal):
|
||||
|
||||
self.baremetal_mock.node.set_provision_state.assert_called_once_with(
|
||||
'node_uuid', 'rebuild',
|
||||
cleansteps=None, configdrive=None)
|
||||
cleansteps=None, configdrive=None,
|
||||
rescuepassword=None)
|
||||
|
||||
self.baremetal_mock.node.wait_for_provision_state.assert_not_called()
|
||||
|
||||
@ -1628,6 +1705,65 @@ class TestUndeployBaremetalProvisionState(TestBaremetal):
|
||||
poll_interval=10, timeout=0)
|
||||
|
||||
|
||||
class TestUnrescueBaremetalProvisionState(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestUnrescueBaremetalProvisionState, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.UnrescueBaremetalNode(self.app, None)
|
||||
|
||||
def test_unrescue_no_wait(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'unrescue'),
|
||||
]
|
||||
|
||||
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', 'unrescue', cleansteps=None, configdrive=None,
|
||||
rescuepassword=None)
|
||||
|
||||
def test_unrescue_baremetal_provision_state_active_and_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
'--wait', '15']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'unrescue'),
|
||||
('wait_timeout', 15)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
test_node = self.baremetal_mock.node
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
'node_uuid', expected_state='active',
|
||||
poll_interval=10, timeout=15)
|
||||
|
||||
def test_unrescue_baremetal_provision_state_default_wait(self):
|
||||
arglist = ['node_uuid',
|
||||
'--wait']
|
||||
verifylist = [
|
||||
('node', 'node_uuid'),
|
||||
('provision_state', 'unrescue'),
|
||||
('wait_timeout', 0)
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
test_node = self.baremetal_mock.node
|
||||
test_node.wait_for_provision_state.assert_called_once_with(
|
||||
'node_uuid', expected_state='active',
|
||||
poll_interval=10, timeout=0)
|
||||
|
||||
|
||||
class TestBaremetalReboot(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestBaremetalReboot, self).setUp()
|
||||
|
@ -1352,6 +1352,17 @@ class NodeManagerTest(testtools.TestCase):
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
|
||||
def test_node_set_provision_state_with_rescuepassword(self):
|
||||
rescuepassword = 'supersecret'
|
||||
target_state = 'rescue'
|
||||
self.mgr.set_provision_state(NODE1['uuid'], target_state,
|
||||
rescuepassword=rescuepassword)
|
||||
body = {'target': target_state, 'rescue_password': rescuepassword}
|
||||
expect = [
|
||||
('PUT', '/v1/nodes/%s/states/provision' % NODE1['uuid'], {}, body),
|
||||
]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
|
||||
def test_node_states(self):
|
||||
states = self.mgr.states(NODE1['uuid'])
|
||||
expect = [
|
||||
|
@ -478,12 +478,13 @@ class NodeManager(base.CreateManager):
|
||||
return self.get(path)
|
||||
|
||||
def set_provision_state(self, node_uuid, state, configdrive=None,
|
||||
cleansteps=None):
|
||||
cleansteps=None, rescuepassword=None):
|
||||
"""Set the provision state for the node.
|
||||
|
||||
:param node_uuid: The UUID or name of the node.
|
||||
:param state: The desired provision state. One of 'active', 'deleted',
|
||||
'rebuild', 'inspect', 'provide', 'manage', 'clean', 'abort'.
|
||||
'rebuild', 'inspect', 'provide', 'manage', 'clean', 'abort',
|
||||
'rescue', 'unrescue'.
|
||||
:param configdrive: A gzipped, base64-encoded configuration drive
|
||||
string OR the path to the configuration drive file OR the path to
|
||||
a directory containing the config drive files. In case it's a
|
||||
@ -493,6 +494,10 @@ class NodeManager(base.CreateManager):
|
||||
dictionaries; each dictionary should have keys 'interface' and
|
||||
'step', and optional key 'args'. This must be specified (and is
|
||||
only valid) when setting provision-state to 'clean'.
|
||||
:param rescuepassword: A string to be used as the login password
|
||||
inside the rescue ramdisk once a node is rescued. This must be
|
||||
specified (and is only valid) when setting provision-state to
|
||||
'rescue'.
|
||||
:raises: InvalidAttribute if there was an error with the clean steps
|
||||
:returns: The status of the request
|
||||
"""
|
||||
@ -509,6 +514,8 @@ class NodeManager(base.CreateManager):
|
||||
body['configdrive'] = configdrive
|
||||
elif cleansteps:
|
||||
body['clean_steps'] = cleansteps
|
||||
elif rescuepassword:
|
||||
body['rescue_password'] = rescuepassword
|
||||
|
||||
return self.update(path, body, http_method='PUT')
|
||||
|
||||
|
@ -43,6 +43,10 @@ PROVISION_ACTIONS = {
|
||||
'adopt': {'expected_state': 'active',
|
||||
'poll_interval': _SHORT_ACTION_POLL_INTERVAL},
|
||||
'abort': None, # no support for --wait in abort
|
||||
'rescue': {'expected_state': 'rescue',
|
||||
'poll_interval': _LONG_ACTION_POLL_INTERVAL},
|
||||
'unrescue': {'expected_state': 'active',
|
||||
'poll_interval': _LONG_ACTION_POLL_INTERVAL},
|
||||
}
|
||||
|
||||
PROVISION_STATES = list(PROVISION_ACTIONS)
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds the below commands to OSC to support rescue mode for ironic
|
||||
available starting with API version 1.38:
|
||||
|
||||
* ``openstack baremetal node rescue``
|
||||
* ``openstack baremetal node unrescue``
|
@ -66,10 +66,12 @@ openstack.baremetal.v1 =
|
||||
baremetal_node_reboot = ironicclient.osc.v1.baremetal_node:RebootBaremetalNode
|
||||
baremetal_node_rebuild = ironicclient.osc.v1.baremetal_node:RebuildBaremetalNode
|
||||
baremetal_node_remove_trait = ironicclient.osc.v1.baremetal_node:RemoveTraitBaremetalNode
|
||||
baremetal_node_rescue = ironicclient.osc.v1.baremetal_node:RescueBaremetalNode
|
||||
baremetal_node_set = ironicclient.osc.v1.baremetal_node:SetBaremetalNode
|
||||
baremetal_node_show = ironicclient.osc.v1.baremetal_node:ShowBaremetalNode
|
||||
baremetal_node_trait_list = ironicclient.osc.v1.baremetal_node:ListTraitsBaremetalNode
|
||||
baremetal_node_undeploy = ironicclient.osc.v1.baremetal_node:UndeployBaremetalNode
|
||||
baremetal_node_unrescue = ironicclient.osc.v1.baremetal_node:UnrescueBaremetalNode
|
||||
baremetal_node_unset = ironicclient.osc.v1.baremetal_node:UnsetBaremetalNode
|
||||
baremetal_node_validate = ironicclient.osc.v1.baremetal_node:ValidateBaremetalNode
|
||||
baremetal_node_vif_attach = ironicclient.osc.v1.baremetal_node:VifAttachBaremetalNode
|
||||
|
Loading…
Reference in New Issue
Block a user