From 3514f052dcdc4eae706939dd7297623f441558d2 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Wed, 21 Jan 2026 09:34:56 -0600 Subject: [PATCH] feat: add 'physical_network' and 'category' to portgroup object The portgroup object added 'physical_network' and 'category' fields in API 1.102 and 1.103 respectively. Change-Id: Id12c56affb599ef98d102cf437bceffd993bea5f Signed-off-by: Doug Goldstein --- ironicclient/osc/v1/baremetal_portgroup.py | 51 ++++++- ironicclient/tests/unit/osc/v1/fakes.py | 3 + .../unit/osc/v1/test_baremetal_portgroup.py | 128 +++++++++++++++++- ironicclient/v1/portgroup.py | 2 +- ironicclient/v1/resource_fields.py | 2 + ...cal-network-category-53508695ce5f5dc5.yaml | 7 + 6 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/add-portgroup-physical-network-category-53508695ce5f5dc5.yaml diff --git a/ironicclient/osc/v1/baremetal_portgroup.py b/ironicclient/osc/v1/baremetal_portgroup.py index a1dc40486..b8425ba3b 100644 --- a/ironicclient/osc/v1/baremetal_portgroup.py +++ b/ironicclient/osc/v1/baremetal_portgroup.py @@ -81,6 +81,17 @@ class CreateBaremetalPortGroup(command.ShowOne): action='store_true', help=_("Ports that are members of this port group " "cannot be used as stand-alone ports.")) + parser.add_argument( + '--physical-network', + dest='physical_network', + metavar='', + help=_("Name of the physical network to which the ports in " + "this port group are connected.")) + parser.add_argument( + '--category', + dest='category', + metavar='', + help=_("An optional category for the port group.")) return parser @@ -89,7 +100,7 @@ class CreateBaremetalPortGroup(command.ShowOne): baremetal_client = self.app.client_manager.baremetal field_list = ['node_uuid', 'address', 'name', 'uuid', 'extra', 'mode', - 'properties'] + 'properties', 'physical_network', 'category'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) if parsed_args.support_standalone_ports: @@ -352,6 +363,17 @@ class SetBaremetalPortGroup(command.Command): help=_("Ports that are members of this port group " "cannot be used as stand-alone ports.") ) + parser.add_argument( + '--physical-network', + metavar='', + dest='physical_network', + help=_("Set a physical network for the ports in this port group")) + + parser.add_argument( + '--category', + metavar='', + dest='category', + help=_("Set a category for this port group")) return parser @@ -371,6 +393,16 @@ class SetBaremetalPortGroup(command.Command): name = ["name=%s" % parsed_args.name] properties.extend(utils.args_array_to_patch( 'add', name)) + if parsed_args.physical_network: + physical_network = [ + f"physical_network={parsed_args.physical_network}" + ] + properties.extend(utils.args_array_to_patch( + 'add', physical_network)) + if parsed_args.category: + category = [f"category={parsed_args.category}"] + properties.extend(utils.args_array_to_patch( + 'add', category)) if parsed_args.support_standalone_ports: properties.extend(utils.args_array_to_patch( 'add', ["standalone_ports_supported=True"])) @@ -432,6 +464,17 @@ class UnsetBaremetalPortGroup(command.Command): help=_('Property to unset on this baremetal port group ' '(repeat option to unset multiple properties).'), ) + parser.add_argument( + '--physical-network', + action='store_true', + dest='physical_network', + help=_("Unset the physical network on this baremetal port group.")) + + parser.add_argument( + '--category', + dest='category', + action='store_true', + help=_("Unset the category for this port group.")) return parser @@ -447,6 +490,12 @@ class UnsetBaremetalPortGroup(command.Command): if parsed_args.address: properties.extend(utils.args_array_to_patch('remove', ['address'])) + if parsed_args.physical_network: + properties.extend(utils.args_array_to_patch('remove', + ['physical_network'])) + if parsed_args.category: + properties.extend(utils.args_array_to_patch('remove', + ['category'])) if parsed_args.extra: properties.extend(utils.args_array_to_patch('remove', ['extra/' + x for x in parsed_args.extra])) diff --git a/ironicclient/tests/unit/osc/v1/fakes.py b/ironicclient/tests/unit/osc/v1/fakes.py index 3277d00dc..e3ee09b92 100644 --- a/ironicclient/tests/unit/osc/v1/fakes.py +++ b/ironicclient/tests/unit/osc/v1/fakes.py @@ -141,6 +141,7 @@ baremetal_portgroup_extra = {'key1': 'value1', 'key2': 'value2'} baremetal_portgroup_properties = {'key1': 'value11', 'key2': 'value22'} +baremetal_portgroup_category = 'Green' PORTGROUP = {'uuid': baremetal_portgroup_uuid, 'name': baremetal_portgroup_name, @@ -149,6 +150,8 @@ PORTGROUP = {'uuid': baremetal_portgroup_uuid, 'extra': baremetal_portgroup_extra, 'mode': baremetal_portgroup_mode, 'properties': baremetal_portgroup_properties, + 'physical_network': baremetal_port_physical_network, + 'category': baremetal_portgroup_category, } VIFS = {'vifs': [{'id': 'aaa-aa'}]} diff --git a/ironicclient/tests/unit/osc/v1/test_baremetal_portgroup.py b/ironicclient/tests/unit/osc/v1/test_baremetal_portgroup.py index b8cabce1f..4acaad8bf 100644 --- a/ironicclient/tests/unit/osc/v1/test_baremetal_portgroup.py +++ b/ironicclient/tests/unit/osc/v1/test_baremetal_portgroup.py @@ -219,6 +219,63 @@ class TestCreateBaremetalPortGroup(TestBaremetalPortGroup): self.check_parser, self.cmd, arglist, verifylist) + def test_baremetal_portgroup_create_physical_network(self): + arglist = [ + '--address', baremetal_fakes.baremetal_portgroup_address, + '--node', baremetal_fakes.baremetal_uuid, + '--physical-network', + baremetal_fakes.baremetal_port_physical_network, + ] + + verifylist = [ + ('node_uuid', baremetal_fakes.baremetal_uuid), + ('address', baremetal_fakes.baremetal_portgroup_address), + ('physical_network', + baremetal_fakes.baremetal_port_physical_network), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + self.cmd.take_action(parsed_args) + + # Set expected values + args = { + 'address': baremetal_fakes.baremetal_portgroup_address, + 'node_uuid': baremetal_fakes.baremetal_uuid, + 'physical_network': + baremetal_fakes.baremetal_port_physical_network, + } + + self.baremetal_mock.portgroup.create.assert_called_once_with(**args) + + def test_baremetal_portgroup_create_category(self): + arglist = [ + '--address', baremetal_fakes.baremetal_portgroup_address, + '--node', baremetal_fakes.baremetal_uuid, + '--category', baremetal_fakes.baremetal_portgroup_category, + ] + + verifylist = [ + ('node_uuid', baremetal_fakes.baremetal_uuid), + ('address', baremetal_fakes.baremetal_portgroup_address), + ('category', baremetal_fakes.baremetal_portgroup_category), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + self.cmd.take_action(parsed_args) + + # Set expected values + args = { + 'address': baremetal_fakes.baremetal_portgroup_address, + 'node_uuid': baremetal_fakes.baremetal_uuid, + 'category': baremetal_fakes.baremetal_portgroup_category, + } + + self.baremetal_mock.portgroup.create.assert_called_once_with(**args) + class TestShowBaremetalPortGroup(TestBaremetalPortGroup): @@ -251,16 +308,19 @@ class TestShowBaremetalPortGroup(TestBaremetalPortGroup): self.baremetal_mock.portgroup.get.assert_called_with(*args, fields=None) - collist = ('address', 'extra', 'mode', 'name', 'node_uuid', - 'properties', 'uuid') + collist = ('address', 'category', 'extra', 'mode', 'name', + 'node_uuid', 'physical_network', 'properties', + 'uuid') self.assertEqual(collist, columns) datalist = ( baremetal_fakes.baremetal_portgroup_address, + baremetal_fakes.baremetal_portgroup_category, baremetal_fakes.baremetal_portgroup_extra, baremetal_fakes.baremetal_portgroup_mode, baremetal_fakes.baremetal_portgroup_name, baremetal_fakes.baremetal_uuid, + baremetal_fakes.baremetal_port_physical_network, baremetal_fakes.baremetal_portgroup_properties, baremetal_fakes.baremetal_portgroup_uuid, ) @@ -385,7 +445,8 @@ class TestBaremetalPortGroupList(TestBaremetalPortGroup): collist = ('UUID', 'Address', 'Created At', 'Extra', 'Standalone Ports Supported', 'Node UUID', 'Name', - 'Updated At', 'Internal Info', 'Mode', 'Properties') + 'Updated At', 'Internal Info', 'Mode', 'Properties', + 'Physical Network', 'Category') self.assertEqual(collist, columns) datalist = ((baremetal_fakes.baremetal_portgroup_uuid, @@ -398,7 +459,10 @@ class TestBaremetalPortGroupList(TestBaremetalPortGroup): '', '', baremetal_fakes.baremetal_portgroup_mode, - baremetal_fakes.baremetal_portgroup_properties),) + baremetal_fakes.baremetal_portgroup_properties, + baremetal_fakes.baremetal_port_physical_network, + baremetal_fakes.baremetal_portgroup_category, + ),) self.assertEqual(datalist, tuple(data)) def test_baremetal_portgroup_list_fields(self): @@ -670,6 +734,40 @@ class TestBaremetalPortGroupSet(TestBaremetalPortGroup): self.cmd.take_action(parsed_args) self.assertFalse(self.baremetal_mock.portgroup.update.called) + def test_baremetal_portgroup_set_physical_network(self): + new_portgroup_physnet = 'physnet2' + arglist = [ + baremetal_fakes.baremetal_portgroup_uuid, + '--physical-network', new_portgroup_physnet] + verifylist = [ + ('portgroup', baremetal_fakes.baremetal_portgroup_uuid), + ('physical_network', new_portgroup_physnet)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.baremetal_mock.portgroup.update.assert_called_once_with( + baremetal_fakes.baremetal_portgroup_uuid, + [{'path': '/physical_network', 'value': new_portgroup_physnet, + 'op': 'add'}]) + + def test_baremetal_portgroup_set_category(self): + new_portgroup_category = 'Purple' + arglist = [ + baremetal_fakes.baremetal_portgroup_uuid, + '--category', new_portgroup_category] + verifylist = [ + ('portgroup', baremetal_fakes.baremetal_portgroup_uuid), + ('category', new_portgroup_category)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.baremetal_mock.portgroup.update.assert_called_once_with( + baremetal_fakes.baremetal_portgroup_uuid, + [{'path': '/category', 'value': new_portgroup_category, + 'op': 'add'}]) + class TestBaremetalPortGroupUnset(TestBaremetalPortGroup): def setUp(self): @@ -762,3 +860,25 @@ class TestBaremetalPortGroupUnset(TestBaremetalPortGroup): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.assertFalse(self.baremetal_mock.portgroup.update.called) + + def test_baremetal_portgroup_unset_physical_network(self): + arglist = ['portgroup', '--physical-network'] + verifylist = [('physical_network', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.baremetal_mock.portgroup.update.assert_called_once_with( + 'portgroup', + [{'path': '/physical_network', 'op': 'remove'}]) + + def test_baremetal_portgroup_unset_category(self): + arglist = ['portgroup', '--category'] + verifylist = [('category', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.baremetal_mock.portgroup.update.assert_called_once_with( + 'portgroup', + [{'path': '/category', 'op': 'remove'}]) diff --git a/ironicclient/v1/portgroup.py b/ironicclient/v1/portgroup.py index d5636aec9..96bd30ccc 100644 --- a/ironicclient/v1/portgroup.py +++ b/ironicclient/v1/portgroup.py @@ -28,7 +28,7 @@ class PortgroupManager(base.CreateManager): _resource_name = 'portgroups' _creation_attributes = ['address', 'extra', 'name', 'node_uuid', 'standalone_ports_supported', 'mode', 'properties', - 'uuid'] + 'uuid', 'physical_network', 'category'] def list(self, node=None, address=None, limit=None, marker=None, sort_key=None, sort_dir=None, detail=False, fields=None, diff --git a/ironicclient/v1/resource_fields.py b/ironicclient/v1/resource_fields.py index 744b91e2c..0e8a4f43e 100644 --- a/ironicclient/v1/resource_fields.py +++ b/ironicclient/v1/resource_fields.py @@ -390,6 +390,8 @@ PORTGROUP_DETAILED_RESOURCE = Resource( 'internal_info', 'mode', 'properties', + 'physical_network', + 'category', ], sort_excluded=[ 'extra', diff --git a/releasenotes/notes/add-portgroup-physical-network-category-53508695ce5f5dc5.yaml b/releasenotes/notes/add-portgroup-physical-network-category-53508695ce5f5dc5.yaml new file mode 100644 index 000000000..97a0a42e3 --- /dev/null +++ b/releasenotes/notes/add-portgroup-physical-network-category-53508695ce5f5dc5.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Adds ``physical_network`` field and ``category`` field support, which is + introduced in ironic API 1.102 and 1.103 respectively. The ``physical_network`` + field is reflected into the port objects that are part of the port group + while the ``category`` is informational text for trait based scheduling.