Firmware Interface support

Command to list firmware components of a node:
* ``openstack baremetal node firmware list <node_ident>``

The `firmware_interface` can be specified in the commands below:
* ``openstack baremetal node create``
* ``openstack baremetal node show``
* ``openstack baremetal node set``
* ``openstack baremetal node unset``
* ``openstack baremetal driver list``
* ``openstack baremetal driver show``

Bumping API to match the required version for it and added unit testing.

Change-Id: I90146439768444a92a586cb96c5ab1fca9899653
This commit is contained in:
Iury Gregory Melo Ferreira 2023-08-16 00:22:27 -03:00
parent 6a363dbff3
commit cea2c5a574
10 changed files with 195 additions and 6 deletions

View File

@ -37,7 +37,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 = 85
LAST_KNOWN_API_VERSION = 86
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__)

View File

@ -48,9 +48,9 @@ NETWORK_DATA_ARG_HELP = _(
"being used, network configuration may or may not been served to the "
"node for offline network configuration.")
SUPPORTED_INTERFACES = ['bios', 'boot', 'console', 'deploy', 'inspect',
'management', 'network', 'power', 'raid', 'rescue',
'storage', 'vendor']
SUPPORTED_INTERFACES = ['bios', 'boot', 'console', 'deploy', 'firmware',
'inspect', 'management', 'network', 'power', 'raid',
'rescue', 'storage', 'vendor']
class ProvisionStateBaremetalNode(command.Command):
@ -531,6 +531,12 @@ class CreateBaremetalNode(command.ShowOne):
'--parent-node',
metavar='<parent_node>',
help=_('Parent node for the node being created.'))
parser.add_argument(
'--firmware-interface',
metavar='<firmware_interface>',
help=_('Firmware interface used by the node\'s driver. This is '
'only applicable when the specified --driver is a '
'hardware type.'))
return parser
@ -1297,6 +1303,12 @@ class SetBaremetalNode(command.Command):
reset_help=_('Reset the deploy interface to its hardware type '
'default'),
)
self._add_interface_args(
parser, 'firmware',
set_help=_('Set the firmware interface for the node'),
reset_help=_('Reset the firmware interface for its hardware '
'type default'),
)
self._add_interface_args(
parser, 'inspect',
set_help=_('Set the inspect interface for the node'),
@ -1702,6 +1714,12 @@ class UnsetBaremetalNode(command.Command):
action='store_true',
help=_('Unset deploy interface on this baremetal node'),
)
parser.add_argument(
"--firmware-interface",
dest='firmware_interface',
action='store_true',
help=_('Unset firmware interface on this baremetal node'),
)
parser.add_argument(
"--inspect-interface",
dest='inspect_interface',
@ -1833,7 +1851,8 @@ class UnsetBaremetalNode(command.Command):
for field in ['instance_uuid', 'name', 'chassis_uuid',
'resource_class', 'conductor_group', 'automated_clean',
'bios_interface', 'boot_interface', 'console_interface',
'deploy_interface', 'inspect_interface',
'deploy_interface', 'firmware_interface',
'inspect_interface',
'management_interface', 'network_interface',
'power_interface', 'raid_interface', 'rescue_interface',
'storage_interface', 'vendor_interface',
@ -2362,3 +2381,33 @@ class NodeChildrenList(command.ShowOne):
data = baremetal_client.node.list_children_of_node(
parsed_args.node)
return (labels, [[node] for node in data])
class ListFirmwareComponentBaremetalNode(command.Lister):
"""List all Firmware Components of a node"""
log = logging.getLogger(__name__ + ".ListFirmwareComponentBaremetalNode")
def get_parser(self, prog_name):
parser = super(ListFirmwareComponentBaremetalNode, 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)
labels = res_fields.FIRMWARE_RESOURCE.labels
fields = res_fields.FIRMWARE_RESOURCE.fields
baremetal_client = self.app.client_manager.baremetal
components = baremetal_client.node.list_firmware_components(
parsed_args.node)
return (labels,
(oscutils.get_dict_properties(s, fields) for s in components))

View File

@ -72,6 +72,7 @@ baremetal_driver_default_boot_if = 'boot'
baremetal_driver_default_console_if = 'console'
baremetal_driver_default_deploy_if = 'deploy'
baremetal_driver_default_inspect_if = 'inspect'
baremetal_driver_default_firmware_if = 'firmware'
baremetal_driver_default_management_if = 'management'
baremetal_driver_default_network_if = 'network'
baremetal_driver_default_power_if = 'power'
@ -83,6 +84,7 @@ baremetal_driver_enabled_bios_ifs = ['bios', 'bios2']
baremetal_driver_enabled_boot_ifs = ['boot', 'boot2']
baremetal_driver_enabled_console_ifs = ['console', 'console2']
baremetal_driver_enabled_deploy_ifs = ['deploy', 'deploy2']
baremetal_driver_enabled_firmware_ifs = ['firmware', 'firmware2']
baremetal_driver_enabled_inspect_ifs = ['inspect', 'inspect2']
baremetal_driver_enabled_management_ifs = ['management', 'management2']
baremetal_driver_enabled_network_ifs = ['network', 'network2']
@ -100,6 +102,7 @@ BAREMETAL_DRIVER = {
'default_boot_interface': baremetal_driver_default_boot_if,
'default_console_interface': baremetal_driver_default_console_if,
'default_deploy_interface': baremetal_driver_default_deploy_if,
'default_firmware_interface': baremetal_driver_default_firmware_if,
'default_inspect_interface': baremetal_driver_default_inspect_if,
'default_management_interface': baremetal_driver_default_management_if,
'default_network_interface': baremetal_driver_default_network_if,
@ -112,6 +115,7 @@ BAREMETAL_DRIVER = {
'enabled_boot_interfaces': baremetal_driver_enabled_boot_ifs,
'enabled_console_interfaces': baremetal_driver_enabled_console_ifs,
'enabled_deploy_interfaces': baremetal_driver_enabled_deploy_ifs,
'enabled_firmware_interfaces': baremetal_driver_enabled_firmware_ifs,
'enabled_inspect_interfaces': baremetal_driver_enabled_inspect_ifs,
'enabled_management_interfaces': baremetal_driver_enabled_management_ifs,
'enabled_network_interfaces': baremetal_driver_enabled_network_ifs,
@ -261,6 +265,25 @@ NODE_INVENTORY = [
}
]
FIRMWARE_COMPONENTS = [
{
"component": "bios",
"initial_version": "v1.0.0.0 (01.02.2022)",
"current_version": "v1.2.3.4 (01.02.2023)",
"last_version_flashed": "v1.2.3.4 (01.02.2023)",
"created_at": "2023-02-01 09:00:00",
"updated_at": "2023-03-01 10:00:00"
},
{
"component": "bmc",
"initial_version": "v1.0.0",
"current_version": "v1.0.0",
"last_version_flashed": "",
"created_at": "2023-02-01 09:00:00",
"updated_at": ""
}
]
class TestBaremetal(utils.TestCommand):

View File

@ -92,6 +92,7 @@ class TestListBaremetalDriver(TestBaremetalDriver):
'Default Boot Interface',
'Default Console Interface',
'Default Deploy Interface',
'Default Firmware Interface',
'Default Inspect Interface',
'Default Management Interface',
'Default Network Interface',
@ -104,6 +105,7 @@ class TestListBaremetalDriver(TestBaremetalDriver):
'Enabled Boot Interfaces',
'Enabled Console Interfaces',
'Enabled Deploy Interfaces',
'Enabled Firmware Interfaces',
'Enabled Inspect Interfaces',
'Enabled Management Interfaces',
'Enabled Network Interfaces',
@ -123,6 +125,7 @@ class TestListBaremetalDriver(TestBaremetalDriver):
baremetal_fakes.baremetal_driver_default_boot_if,
baremetal_fakes.baremetal_driver_default_console_if,
baremetal_fakes.baremetal_driver_default_deploy_if,
baremetal_fakes.baremetal_driver_default_firmware_if,
baremetal_fakes.baremetal_driver_default_inspect_if,
baremetal_fakes.baremetal_driver_default_management_if,
baremetal_fakes.baremetal_driver_default_network_if,
@ -135,6 +138,7 @@ class TestListBaremetalDriver(TestBaremetalDriver):
', '.join(baremetal_fakes.baremetal_driver_enabled_boot_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_console_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_deploy_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_firmware_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_inspect_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_management_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_network_ifs),
@ -430,13 +434,14 @@ class TestShowBaremetalDriver(TestBaremetalDriver):
collist = ('default_bios_interface', 'default_boot_interface',
'default_console_interface', 'default_deploy_interface',
'default_inspect_interface',
'default_firmware_interface', 'default_inspect_interface',
'default_management_interface',
'default_network_interface', 'default_power_interface',
'default_raid_interface', 'default_rescue_interface',
'default_storage_interface', 'default_vendor_interface',
'enabled_bios_interfaces', 'enabled_boot_interfaces',
'enabled_console_interfaces', 'enabled_deploy_interfaces',
'enabled_firmware_interfaces',
'enabled_inspect_interfaces',
'enabled_management_interfaces',
'enabled_network_interfaces', 'enabled_power_interfaces',
@ -450,6 +455,7 @@ class TestShowBaremetalDriver(TestBaremetalDriver):
baremetal_fakes.baremetal_driver_default_boot_if,
baremetal_fakes.baremetal_driver_default_console_if,
baremetal_fakes.baremetal_driver_default_deploy_if,
baremetal_fakes.baremetal_driver_default_firmware_if,
baremetal_fakes.baremetal_driver_default_inspect_if,
baremetal_fakes.baremetal_driver_default_management_if,
baremetal_fakes.baremetal_driver_default_network_if,
@ -462,6 +468,7 @@ class TestShowBaremetalDriver(TestBaremetalDriver):
', '.join(baremetal_fakes.baremetal_driver_enabled_boot_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_console_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_deploy_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_firmware_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_inspect_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_management_ifs),
', '.join(baremetal_fakes.baremetal_driver_enabled_network_ifs),

View File

@ -650,6 +650,11 @@ class TestBaremetalCreate(TestBaremetal):
[('deploy_interface', 'deploy')],
{'deploy_interface': 'deploy'})
def test_baremetal_create_with_firmware_interface(self):
self.check_with_options(['--firmware-interface', 'firmware'],
[('firmware_interface', 'firmware')],
{'firmware_interface': 'firmware'})
def test_baremetal_create_with_inspect_interface(self):
self.check_with_options(['--inspect-interface', 'inspect'],
[('inspect_interface', 'inspect')],
@ -906,6 +911,7 @@ class TestBaremetalList(TestBaremetal):
'Driver Internal Info',
'Extra',
'Fault',
'Firmware Interface',
'Inspect Interface',
'Inspection Finished At',
'Inspection Started At',
@ -2777,6 +2783,9 @@ class TestBaremetalSet(TestBaremetal):
def test_baremetal_set_deploy_interface(self):
self._test_baremetal_set_hardware_interface('deploy')
def test_baremetal_set_firmware_interface(self):
self._test_baremetal_set_hardware_interface('firmware')
def test_baremetal_set_inspect_interface(self):
self._test_baremetal_set_hardware_interface('inspect')
@ -2833,6 +2842,9 @@ class TestBaremetalSet(TestBaremetal):
def test_baremetal_reset_deploy_interface(self):
self._test_baremetal_reset_hardware_interface('deploy')
def test_baremetal_reset_firmware_interface(self):
self._test_baremetal_reset_hardware_interface('firmware')
def test_baremetal_reset_inspect_interface(self):
self._test_baremetal_reset_hardware_interface('inspect')
@ -3832,6 +3844,9 @@ class TestBaremetalUnset(TestBaremetal):
def test_baremetal_unset_deploy_interface(self):
self._test_baremetal_unset_hw_interface('deploy')
def test_baremetal_unset_firmware_interface(self):
self._test_baremetal_unset_hw_interface('firmware')
def test_baremetal_unset_inspect_interface(self):
self._test_baremetal_unset_hw_interface('inspect')
@ -4545,3 +4560,38 @@ 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)
class TestListFirmwareComponents(TestBaremetal):
def setUp(self):
super(TestListFirmwareComponents, self).setUp()
self.baremetal_mock.node.list_firmware_components.return_value = (
baremetal_fakes.FIRMWARE_COMPONENTS)
# Get the command object to test
self.cmd = baremetal_node.ListFirmwareComponentBaremetalNode(self.app,
None)
def test_baremetal_list_firmware_components(self):
arglist = ['node_uuid']
verifylist = [('node', 'node_uuid')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.baremetal_mock.node.list_firmware_components \
.assert_called_once_with('node_uuid')
expected_columns = ('Component', 'Initial Version',
'Current Version', 'Last Version Flashed',
'Created At', 'Updated At')
self.assertEqual(expected_columns, columns)
expected_data = (
[(fw['component'], fw['initial_version'],
fw['current_version'], fw['last_version_flashed'],
fw['created_at'], fw['updated_at'])
for fw in baremetal_fakes.FIRMWARE_COMPONENTS])
self.assertEqual(tuple(expected_data), tuple(data))

View File

@ -31,6 +31,7 @@ DRIVER1 = {
'default_boot_interface': 'boot',
'default_console_interface': 'console',
'default_deploy_interface': 'deploy',
'default_firmware_interface': 'firmware',
'default_inspect_interface': 'inspect',
'default_management_interface': 'management',
'default_network_interface': 'network',
@ -41,6 +42,7 @@ DRIVER1 = {
'enabled_boot_interfaces': ['boot', 'boot2'],
'enabled_console_interfaces': ['console', 'console2'],
'enabled_deploy_interfaces': ['deploy', 'deploy2'],
'enabled_firmware_interfaces': ['firmware', 'firmware2'],
'enabled_inspect_interfaces': ['inspect', 'inspect2'],
'enabled_management_interfaces': ['management', 'management2'],
'enabled_network_interfaces': ['network', 'network2'],

View File

@ -1163,3 +1163,21 @@ class NodeManager(base.CreateManager):
return self._get_as_dict(
path, os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)
def list_firmware_components(self, node_ident,
os_ironic_api_version=None,
global_request_id=None):
"""List all firmware components from a node.
:param node_ident: node UUID or name.
:param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
"""
path = "%s/firmware" % node_ident
return self._list_primitives(
self._path(path), 'firmware',
os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)

View File

@ -52,6 +52,7 @@ class Resource(object):
'default_boot_interface': 'Default Boot Interface',
'default_console_interface': 'Default Console Interface',
'default_deploy_interface': 'Default Deploy Interface',
'default_firmware_interface': 'Default Firmware Interface',
'default_inspect_interface': 'Default Inspect Interface',
'default_management_interface': 'Default Management Interface',
'default_network_interface': 'Default Network Interface',
@ -70,6 +71,7 @@ class Resource(object):
'enabled_boot_interfaces': 'Enabled Boot Interfaces',
'enabled_console_interfaces': 'Enabled Console Interfaces',
'enabled_deploy_interfaces': 'Enabled Deploy Interfaces',
'enabled_firmware_interfaces': 'Enabled Firmware Interfaces',
'enabled_inspect_interfaces': 'Enabled Inspect Interfaces',
'enabled_management_interfaces': 'Enabled Management Interfaces',
'enabled_network_interfaces': 'Enabled Network Interfaces',
@ -154,6 +156,7 @@ class Resource(object):
'is_smartnic': 'Is Smart NIC port',
'parent_node': 'Parent Node',
'children': 'Child Nodes',
'firmware_interface': 'Firmware Interface',
}
def __init__(self, field_ids, sort_excluded=None, override_labels=None):
@ -256,6 +259,7 @@ NODE_DETAILED_RESOURCE = Resource(
'driver_internal_info',
'extra',
'fault',
'firmware_interface',
'inspect_interface',
'inspection_finished_at',
'inspection_started_at',
@ -429,6 +433,7 @@ DRIVER_DETAILED_RESOURCE = Resource(
'default_boot_interface',
'default_console_interface',
'default_deploy_interface',
'default_firmware_interface',
'default_inspect_interface',
'default_management_interface',
'default_network_interface',
@ -441,6 +446,7 @@ DRIVER_DETAILED_RESOURCE = Resource(
'enabled_boot_interfaces',
'enabled_console_interfaces',
'enabled_deploy_interfaces',
'enabled_firmware_interfaces',
'enabled_inspect_interfaces',
'enabled_management_interfaces',
'enabled_network_interfaces',
@ -459,6 +465,22 @@ DRIVER_RESOURCE = Resource(
override_labels={'name': 'Supported driver(s)'}
)
# Firmware
FIRMWARE_RESOURCE = Resource(
['component',
'initial_version',
'current_version',
'last_version_flashed',
'created_at',
'updated_at',
],
override_labels={'component': 'Component',
'initial_version': 'Initial Version',
'current_version': 'Current Version',
'last_version_flashed': 'Last Version Flashed'}
)
# Volume connectors
VOLUME_CONNECTOR_DETAILED_RESOURCE = Resource(
['uuid',

View File

@ -0,0 +1,17 @@
---
features:
- |
Adds support for the Firmware Interface feature.
Command to list firmware components of a node:
* ``openstack baremetal node firmware list <node_ident>``
The `firmware_interface` can be specified in the commands below:
* ``openstack baremetal node create``
* ``openstack baremetal node show``
* ``openstack baremetal node set``
* ``openstack baremetal node unset``
* ``openstack baremetal driver list``
* ``openstack baremetal driver show``

View File

@ -72,6 +72,7 @@ openstack.baremetal.v1 =
baremetal_node_create = ironicclient.osc.v1.baremetal_node:CreateBaremetalNode
baremetal_node_delete = ironicclient.osc.v1.baremetal_node:DeleteBaremetalNode
baremetal_node_deploy = ironicclient.osc.v1.baremetal_node:DeployBaremetalNode
baremetal_node_firmware_list = ironicclient.osc.v1.baremetal_node:ListFirmwareComponentBaremetalNode
baremetal_node_history_list = ironicclient.osc.v1.baremetal_node:NodeHistoryList
baremetal_node_history_get = ironicclient.osc.v1.baremetal_node:NodeHistoryEventGet
baremetal_node_inspect = ironicclient.osc.v1.baremetal_node:InspectBaremetalNode