Add support for node inventory

Adds REST API version 1.81, which supports querying a node's hardware
inventory using the python client library, and also using the baremetal
CLI using the
`baremetal node inventory save [--file <filename>] <node>` command.

Change-Id: Idf6ba4cbf7035e0617edc67f55c93b434d9a76aa
This commit is contained in:
Mahnoor Asghar 2023-05-22 08:31:31 -04:00
parent a01ba938c2
commit 0b6b282d40
8 changed files with 137 additions and 1 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 = 78
LAST_KNOWN_API_VERSION = 81
LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION)
LOG = logging.getLogger(__name__)

View File

@ -18,6 +18,7 @@ import argparse
import itertools
import json
import logging
import sys
from osc_lib.command import command
from osc_lib import utils as oscutils
@ -2233,3 +2234,33 @@ class NodeHistoryEventGet(command.ShowOne):
data.pop('links')
return self.dict2columns(data)
class NodeInventorySave(command.Command):
"""Get hardware inventory of a node (in JSON format) or save it to file."""
log = logging.getLogger(__name__ + ".NodeInventorySave")
def get_parser(self, prog_name):
parser = super(NodeInventorySave, self).get_parser(prog_name)
parser.add_argument(
"node",
metavar="<node>",
help=_("Name or UUID of the node"))
parser.add_argument("--file",
metavar="<filename>",
help="Save inspection data to file with name "
"(default: stdout).")
return parser
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
baremetal_client = self.app.client_manager.baremetal
inventory = baremetal_client.node.get_inventory(parsed_args.node)
if parsed_args.file:
with open(parsed_args.file, 'w') as fp:
json.dump(inventory, fp)
else:
json.dump(inventory, sys.stdout)

View File

@ -241,6 +241,23 @@ NODE_HISTORY = [
'links': {'href': 'url', 'rel': 'self'},
}
]
NODE_INVENTORY = [
{
'inventory':
{
'memory': {'physical_mb': 3072},
'cpu': {'count': 1,
'model_name': 'qemu64',
'architecture': 'x86_64'},
'disks': [{'name': 'testvm2.qcow2',
'size': 11811160064}],
'interfaces': [{'mac_address': '52:54:00:11:2d:26'}],
'system_vendor': {'product_name': 'testvm2',
'manufacturer': 'Sushy Emulator'},
'boot': {'current_boot_mode': 'uefi'}
}
}
]
class TestBaremetal(utils.TestCommand):

View File

@ -15,7 +15,9 @@
#
import copy
import io
import json
import sys
from unittest import mock
from osc_lib.tests import utils as oscutils
@ -4302,3 +4304,40 @@ class TestNodeHistoryEventGet(TestBaremetal):
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, tuple(data))
class TestNodeInventorySave(TestBaremetal):
def setUp(self):
super(TestNodeInventorySave, self).setUp()
self.baremetal_mock.node.get_inventory.return_value = (
baremetal_fakes.NODE_INVENTORY[0])
# Get the command object to test
self.cmd = baremetal_node.NodeInventorySave(self.app, None)
def test_baremetal_node_inventory_save(self):
arglist = ['node_uuid']
verifylist = [('node', 'node_uuid')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
buf = io.StringIO()
with mock.patch.object(sys, 'stdout', buf):
self.cmd.take_action(parsed_args)
self.baremetal_mock.node.get_inventory.assert_called_once_with(
'node_uuid')
expected_data = {'memory': {'physical_mb': 3072},
'cpu': {'count': 1,
'model_name': 'qemu64',
'architecture': 'x86_64'},
'disks': [{'name': 'testvm2.qcow2',
'size': 11811160064}],
'interfaces': [{'mac_address': '52:54:00:11:2d:26'}],
'system_vendor': {'product_name': 'testvm2',
'manufacturer': 'Sushy Emulator'},
'boot': {'current_boot_mode': 'uefi'}}
inventory = json.loads(buf.getvalue())
self.assertEqual(expected_data, inventory['inventory'])

View File

@ -108,6 +108,13 @@ NODE_VENDOR_PASSTHRU_METHOD = {"heartbeat": {"attach": "false",
VIFS = {'vifs': [{'id': 'aaa-aaa'}]}
TRAITS = {'traits': ['CUSTOM_FOO', 'CUSTOM_BAR']}
INVENTORY = {'inventory': [{'memory': {'physical_mb': '3072'},
'cpu': {'count': 1, 'architecture': 'x86_64',
'model_name': 'qemu64'},
'disks': [{'name': 'testvm2.qcow2',
'size': 11811160064}],
'interfaces':
[{'mac_address': '52:54:00:c7:02:45'}]}]}
CREATE_NODE = copy.deepcopy(NODE1)
del CREATE_NODE['uuid']
@ -543,6 +550,13 @@ fake_responses = {
{},
None,
),
},
'/v1/nodes/%s/inventory' % NODE1['uuid']:
{
'GET': (
{},
INVENTORY,
),
}
}
@ -2148,3 +2162,11 @@ class NodeManagerTest(testtools.TestCase):
]
self.assertEqual(expect, self.api.calls)
self.assertIsNone(resp)
def test_node_get_inventory(self):
inventory = self.mgr.get_inventory(NODE1['uuid'])
expect = [
('GET', '/v1/nodes/%s/inventory' % NODE1['uuid'], {}, None),
]
self.assertEqual(expect, self.api.calls)
self.assertEqual(INVENTORY, inventory)

View File

@ -1099,3 +1099,22 @@ 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 get_inventory(self,
node_ident,
os_ironic_api_version=None,
global_request_id=None):
"""Get the hardware inventory of the node.
Requires API version 1.81.
:param node_ident: The name or UUID of the node.
:param os_ironic_api_version: String version (e.g. "1.81") 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/inventory" % node_ident
return self._get_as_dict(
path, os_ironic_api_version=os_ironic_api_version,
global_request_id=global_request_id)

View File

@ -0,0 +1,7 @@
features:
- |
Adds support for API version ``1.81``, allowing users to retrieve a node's
hardware inventory using the
``baremetal node inventory [--file <filename>] save <node>`` command.
- Adds a ``get_inventory`` method to the python client library. This method
allows operators to query recorded node inventory from an ironic API.

View File

@ -73,6 +73,7 @@ openstack.baremetal.v1 =
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
baremetal_node_inventory_save = ironicclient.osc.v1.baremetal_node:NodeInventorySave
baremetal_node_list = ironicclient.osc.v1.baremetal_node:ListBaremetalNode
baremetal_node_maintenance_set = ironicclient.osc.v1.baremetal_node:MaintenanceSetBaremetalNode
baremetal_node_maintenance_unset = ironicclient.osc.v1.baremetal_node:MaintenanceUnsetBaremetalNode