Adds node boot device & passthru OSC commands
Adds the following node boot device and node passthru commands to the OpenStackClient plugin: - openstack baremetal node boot device set - openstack baremetal node boot device show - openstack baremetal node passthru call - openstack baremetal node passthru list Change-Id: I803bf263c0e548019425e769f1d3ae5ae33d9940 Partial-Bug: #1526479
This commit is contained in:
parent
fd54fd076f
commit
0f005338e9
|
@ -72,6 +72,82 @@ class AdoptBaremetalNode(ProvisionStateBaremetalNode):
|
|||
PROVISION_STATE = 'adopt'
|
||||
|
||||
|
||||
class BootdeviceSetBaremetalNode(command.Command):
|
||||
"""Set the boot device for a node"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".BootdeviceSetBaremetalNode")
|
||||
|
||||
BOOT_DEVICES = ['pxe', 'disk', 'cdrom', 'bios', 'safe']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(BootdeviceSetBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help="Name or UUID of the node"
|
||||
)
|
||||
parser.add_argument(
|
||||
'device',
|
||||
metavar='<device>',
|
||||
choices=self.BOOT_DEVICES,
|
||||
help="One of %s" % (oscutils.format_list(self.BOOT_DEVICES))
|
||||
)
|
||||
parser.add_argument(
|
||||
'--persistent',
|
||||
dest='persistent',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Make changes persistent for all future boots"
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
baremetal_client.node.set_boot_device(
|
||||
parsed_args.node,
|
||||
parsed_args.device,
|
||||
parsed_args.persistent)
|
||||
|
||||
|
||||
class BootdeviceShowBaremetalNode(command.ShowOne):
|
||||
"""Show the boot device information for a node"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".BootdeviceShowBaremetalNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(BootdeviceShowBaremetalNode, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help="Name or UUID of the node"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--supported',
|
||||
dest='supported',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help="Show the supported boot devices"
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
if parsed_args.supported:
|
||||
info = baremetal_client.node.get_supported_boot_devices(
|
||||
parsed_args.node)
|
||||
boot_device_list = info.get('supported_boot_devices', [])
|
||||
info['supported_boot_devices'] = ', '.join(boot_device_list)
|
||||
else:
|
||||
info = baremetal_client.node.get_boot_device(parsed_args.node)
|
||||
return zip(*sorted(info.items()))
|
||||
|
||||
|
||||
class CleanBaremetalNode(ProvisionStateBaremetalNode):
|
||||
"""Set provision state of baremetal node to 'clean'"""
|
||||
|
||||
|
@ -535,6 +611,104 @@ class ManageBaremetalNode(ProvisionStateBaremetalNode):
|
|||
PROVISION_STATE = 'manage'
|
||||
|
||||
|
||||
class PassthruCallBaremetalNode(command.Command):
|
||||
"""Call a vendor passthu method for a node"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".PassthuCallBaremetalNode")
|
||||
|
||||
HTTP_METHODS = ['POST', 'PUT', 'GET', 'DELETE', 'PATCH']
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PassthruCallBaremetalNode, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help="Name or UUID of the node"
|
||||
)
|
||||
parser.add_argument(
|
||||
'method',
|
||||
metavar='<method>',
|
||||
help="Vendor passthru method to be executed"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--arg',
|
||||
metavar='<key=value>',
|
||||
action='append',
|
||||
help="Argument to pass to the passthru method (repeat option "
|
||||
"to specify multiple arguments)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--http-method',
|
||||
metavar='<http-method>',
|
||||
choices=self.HTTP_METHODS,
|
||||
default='POST',
|
||||
help="The HTTP method to use in the passthru request. One of "
|
||||
"%s. Defaults to POST." %
|
||||
oscutils.format_list(self.HTTP_METHODS)
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
|
||||
arguments = utils.args_array_to_dict(
|
||||
{'args': parsed_args.arg}, 'args')['args']
|
||||
|
||||
# If there were no arguments for the method, arguments will still
|
||||
# be an empty list. So make it an empty dict.
|
||||
if not arguments:
|
||||
arguments = {}
|
||||
|
||||
resp = baremetal_client.node.vendor_passthru(
|
||||
parsed_args.node,
|
||||
parsed_args.method,
|
||||
http_method=parsed_args.http_method,
|
||||
args=arguments)
|
||||
if resp:
|
||||
# Print the raw response; we don't know how it should be formatted
|
||||
print(str(resp.to_dict()))
|
||||
|
||||
|
||||
class PassthruListBaremetalNode(command.Lister):
|
||||
"""List vendor passthru methods for a node"""
|
||||
|
||||
log = logging.getLogger(__name__ + ".PassthruListBaremetalNode")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PassthruListBaremetalNode, self).get_parser(
|
||||
prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'node',
|
||||
metavar='<node>',
|
||||
help="Name or UUID of the node"
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self.log.debug("take_action(%s)", parsed_args)
|
||||
|
||||
baremetal_client = self.app.client_manager.baremetal
|
||||
methods = baremetal_client.node.get_vendor_passthru_methods(
|
||||
parsed_args.node)
|
||||
data = []
|
||||
for method, response in methods.items():
|
||||
response['name'] = method
|
||||
response['http_methods'] = oscutils.format_list(
|
||||
response['http_methods'])
|
||||
data.append(response)
|
||||
|
||||
return (
|
||||
res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.labels,
|
||||
(oscutils.get_dict_properties(
|
||||
s, res_fields.VENDOR_PASSTHRU_METHOD_RESOURCE.fields)
|
||||
for s in data))
|
||||
|
||||
|
||||
class PowerBaremetalNode(command.Command):
|
||||
"""Set power state of baremetal node"""
|
||||
|
||||
|
|
|
@ -56,6 +56,92 @@ class TestAdopt(TestBaremetal):
|
|||
'node_uuid', 'adopt')
|
||||
|
||||
|
||||
class TestBootdeviceSet(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestBootdeviceSet, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.BootdeviceSetBaremetalNode(self.app, None)
|
||||
|
||||
def test_bootdevice_set(self):
|
||||
arglist = ['node_uuid', 'bios']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('device', 'bios')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_boot_device.assert_called_once_with(
|
||||
'node_uuid', 'bios', False)
|
||||
|
||||
def test_bootdevice_set_persistent(self):
|
||||
arglist = ['node_uuid', 'bios', '--persistent']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('device', 'bios'),
|
||||
('persistent', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.set_boot_device.assert_called_once_with(
|
||||
'node_uuid', 'bios', True)
|
||||
|
||||
def test_bootdevice_set_invalid_device(self):
|
||||
arglist = ['node_uuid', 'foo']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('device', 'foo')]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
def test_bootdevice_set_device_only(self):
|
||||
arglist = ['bios']
|
||||
verifylist = [('device', 'bios')]
|
||||
|
||||
self.assertRaises(oscutils.ParserException,
|
||||
self.check_parser,
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
|
||||
class TestBootdeviceShow(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestBootdeviceShow, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.BootdeviceShowBaremetalNode(self.app, None)
|
||||
|
||||
self.baremetal_mock.node.get_boot_device.return_value = {
|
||||
"boot_device": "pxe", "persistent": False}
|
||||
|
||||
self.baremetal_mock.node.get_supported_boot_devices.return_value = {
|
||||
"supported_boot_devices": ["cdrom", "bios", "safe", "disk", "pxe"]}
|
||||
|
||||
def test_bootdevice_show(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid')]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
self.baremetal_mock.node.get_boot_device.assert_called_once_with(
|
||||
'node_uuid')
|
||||
|
||||
def test_bootdevice_supported_show(self):
|
||||
arglist = ['node_uuid', '--supported']
|
||||
verifylist = [('node', 'node_uuid'), ('supported', True)]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
|
||||
mock = self.baremetal_mock.node.get_supported_boot_devices
|
||||
mock.assert_called_once_with('node_uuid')
|
||||
|
||||
|
||||
class TestConsoleDisable(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestConsoleDisable, self).setUp()
|
||||
|
@ -677,6 +763,73 @@ class TestBaremetalMaintenanceUnset(TestBaremetal):
|
|||
False)
|
||||
|
||||
|
||||
class TestPassthruCall(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestPassthruCall, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.PassthruCallBaremetalNode(self.app, None)
|
||||
|
||||
def test_passthru_call(self):
|
||||
arglist = ['node_uuid', 'heartbeat']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('method', 'heartbeat')]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.node.vendor_passthru.assert_called_once_with(
|
||||
'node_uuid', 'heartbeat', http_method='POST', args={})
|
||||
|
||||
def test_passthru_call_http_method(self):
|
||||
arglist = ['node_uuid', 'heartbeat', '--http-method', 'PUT']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('method', 'heartbeat'),
|
||||
('http_method', 'PUT')]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.baremetal_mock.node.vendor_passthru.assert_called_once_with(
|
||||
'node_uuid', 'heartbeat', http_method='PUT', args={})
|
||||
|
||||
def test_passthru_call_args(self):
|
||||
arglist = ['node_uuid', 'heartbeat',
|
||||
'--arg', 'key1=value1', '--arg', 'key2=value2']
|
||||
verifylist = [('node', 'node_uuid'),
|
||||
('method', 'heartbeat'),
|
||||
('arg', ['key1=value1', 'key2=value2'])]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
expected_dict = {'key1': 'value1', 'key2': 'value2'}
|
||||
self.baremetal_mock.node.vendor_passthru.assert_called_once_with(
|
||||
'node_uuid', 'heartbeat', http_method='POST', args=expected_dict)
|
||||
|
||||
|
||||
class TestPassthruList(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestPassthruList, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = baremetal_node.PassthruListBaremetalNode(self.app, None)
|
||||
|
||||
self.baremetal_mock.node.get_vendor_passthru_methods.return_value = {
|
||||
"send_raw": {"require_exclusive_lock": True, "attach": False,
|
||||
"http_methods": ["POST"], "description": "",
|
||||
"async": True},
|
||||
"bmc_reset": {"require_exclusive_lock": True, "attach": False,
|
||||
"http_methods": ["POST"], "description": "",
|
||||
"async": True}}
|
||||
|
||||
def test_passthru_list(self):
|
||||
arglist = ['node_uuid']
|
||||
verifylist = [('node', 'node_uuid')]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
self.cmd.take_action(parsed_args)
|
||||
mock = self.baremetal_mock.node.get_vendor_passthru_methods
|
||||
mock.assert_called_once_with('node_uuid')
|
||||
|
||||
|
||||
class TestBaremetalPower(TestBaremetal):
|
||||
def setUp(self):
|
||||
super(TestBaremetalPower, self).setUp()
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Extends the OpenStackClient plug-in with new commands:
|
||||
|
||||
* openstack baremetal node boot device set
|
||||
* openstack baremetal node boot device show
|
||||
* openstack baremetal node passthru call
|
||||
* openstack baremetal node passthru list
|
|
@ -34,6 +34,8 @@ openstack.baremetal.v1 =
|
|||
baremetal_list = ironicclient.osc.v1.baremetal_node:ListBaremetal
|
||||
baremetal_node_abort = ironicclient.osc.v1.baremetal_node:AbortBaremetalNode
|
||||
baremetal_node_adopt = ironicclient.osc.v1.baremetal_node:AdoptBaremetalNode
|
||||
baremetal_node_boot_device_set = ironicclient.osc.v1.baremetal_node:BootdeviceSetBaremetalNode
|
||||
baremetal_node_boot_device_show = ironicclient.osc.v1.baremetal_node:BootdeviceShowBaremetalNode
|
||||
baremetal_node_clean = ironicclient.osc.v1.baremetal_node:CleanBaremetalNode
|
||||
baremetal_node_console_disable = ironicclient.osc.v1.baremetal_node:ConsoleDisableBaremetalNode
|
||||
baremetal_node_console_enable = ironicclient.osc.v1.baremetal_node:ConsoleEnableBaremetalNode
|
||||
|
@ -46,6 +48,8 @@ openstack.baremetal.v1 =
|
|||
baremetal_node_maintenance_set = ironicclient.osc.v1.baremetal_node:MaintenanceSetBaremetalNode
|
||||
baremetal_node_maintenance_unset = ironicclient.osc.v1.baremetal_node:MaintenanceUnsetBaremetalNode
|
||||
baremetal_node_manage = ironicclient.osc.v1.baremetal_node:ManageBaremetalNode
|
||||
baremetal_node_passthru_call = ironicclient.osc.v1.baremetal_node:PassthruCallBaremetalNode
|
||||
baremetal_node_passthru_list = ironicclient.osc.v1.baremetal_node:PassthruListBaremetalNode
|
||||
baremetal_node_power = ironicclient.osc.v1.baremetal_node:PowerBaremetalNode
|
||||
baremetal_node_provide = ironicclient.osc.v1.baremetal_node:ProvideBaremetalNode
|
||||
baremetal_node_reboot = ironicclient.osc.v1.baremetal_node:RebootBaremetalNode
|
||||
|
|
Loading…
Reference in New Issue