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:
Ruby Loo 2016-08-18 14:09:05 -04:00
parent fd54fd076f
commit 0f005338e9
4 changed files with 340 additions and 0 deletions

View File

@ -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"""

View File

@ -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()

View File

@ -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

View File

@ -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