From 1c93c84d1c16d9ede1950f9756b8bd04302fdc68 Mon Sep 17 00:00:00 2001 From: Maksim Kostrow Date: Wed, 2 Apr 2025 11:47:59 +0300 Subject: [PATCH] Correction of a typo in the 'sub_ports' field Problem: In the "_extend_port_trunk_details_bulk" method for the trunk plugin, the search of subports takes place using the key 'subports' instead 'sub_ports'. That cause the mac address field has never added to the subports. It also leads to consistency with the documented API: https://docs.openstack.org/api-ref/network/v2/index.html#show-trunk-details Closes-Bug: #2020552 Change-Id: I113714a21692bfb6be9a21586de172a62191619f --- neutron/services/trunk/plugin.py | 25 +++++++++++-------- .../tests/unit/services/trunk/test_plugin.py | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/neutron/services/trunk/plugin.py b/neutron/services/trunk/plugin.py index d2f005171fb..005fe97585b 100644 --- a/neutron/services/trunk/plugin.py +++ b/neutron/services/trunk/plugin.py @@ -93,7 +93,7 @@ class TrunkPlugin(service_base.ServicePluginBase): for port in ports: subports[port['id']]['mac_address'] = port['mac_address'] trunk_details = {'trunk_id': port_db.trunk_port.id, - 'sub_ports': list(subports.values())} + trunk_apidef.SUB_PORTS: list(subports.values())} port_res['trunk_details'] = trunk_details return port_res @@ -112,9 +112,10 @@ class TrunkPlugin(service_base.ServicePluginBase): subport_ids = [] trunk_ports = [] for p in ports_res: - if 'trunk_details' in p and 'subports' in p['trunk_details']: + if 'trunk_details' in p and \ + trunk_apidef.SUB_PORTS in p['trunk_details']: trunk_ports.append(p) - for subp in p['trunk_details']['subports']: + for subp in p['trunk_details'][trunk_apidef.SUB_PORTS]: subport_ids.append(subp['port_id']) if not subport_ids: return ports_res @@ -125,7 +126,7 @@ class TrunkPlugin(service_base.ServicePluginBase): subport_macs = {p['id']: p['mac_address'] for p in subports} for tp in trunk_ports: - for subp in tp['trunk_details']['subports']: + for subp in tp['trunk_details'][trunk_apidef.SUB_PORTS]: subp['mac_address'] = subport_macs[subp['port_id']] return ports_res @@ -203,8 +204,10 @@ class TrunkPlugin(service_base.ServicePluginBase): trunk_details['port_id'] = trunk_validator.validate(context) subports_validator = rules.SubPortsValidator( - self._segmentation_types, trunk['sub_ports'], trunk['port_id']) - trunk_details['sub_ports'] = subports_validator.validate(context) + self._segmentation_types, trunk[trunk_apidef.SUB_PORTS], + trunk['port_id']) + trunk_details[trunk_apidef.SUB_PORTS] = ( + subports_validator.validate(context)) return trunk_details def get_plugin_description(self): @@ -240,7 +243,7 @@ class TrunkPlugin(service_base.ServicePluginBase): port_id=p['port_id'], segmentation_id=p['segmentation_id'], segmentation_type=p['segmentation_type']) - for p in trunk['sub_ports']] + for p in trunk[trunk_apidef.SUB_PORTS]] admin_state_up = trunk.get('admin_state_up', True) # NOTE(status_police): a trunk is created in DOWN status. Depending # on the nature of the create request, a driver may set the status @@ -334,7 +337,7 @@ class TrunkPlugin(service_base.ServicePluginBase): # Check for basic validation since the request body here is not # automatically validated by the API layer. - subports = subports['sub_ports'] + subports = subports[trunk_apidef.SUB_PORTS] subports_validator = rules.SubPortsValidator( self._segmentation_types, subports, trunk['port_id']) subports = subports_validator.validate( @@ -364,7 +367,7 @@ class TrunkPlugin(service_base.ServicePluginBase): segmentation_type=subport['segmentation_type'], segmentation_id=subport['segmentation_id']) obj.create() - trunk['sub_ports'].append(obj) + trunk[trunk_apidef.SUB_PORTS].append(obj) added_subports.append(obj) payload = events.DBEventPayload(context, resource_id=trunk_id, states=(original_trunk, trunk,), @@ -387,7 +390,7 @@ class TrunkPlugin(service_base.ServicePluginBase): @db_base_plugin_common.convert_result_to_dict def remove_subports(self, context, trunk_id, subports): """Remove one or more subports from trunk.""" - subports = subports['sub_ports'] + subports = subports[trunk_apidef.SUB_PORTS] with db_api.CONTEXT_WRITER.using(context): trunk = self._get_trunk(context, trunk_id) original_trunk = copy.deepcopy(trunk) @@ -446,7 +449,7 @@ class TrunkPlugin(service_base.ServicePluginBase): def get_subports(self, context, trunk_id, fields=None): """Return subports for the specified trunk.""" trunk = self.get_trunk(context, trunk_id) - return {'sub_ports': trunk['sub_ports']} + return {trunk_apidef.SUB_PORTS: trunk[trunk_apidef.SUB_PORTS]} def _get_trunk(self, context, trunk_id): """Return the trunk object or raise if not found.""" diff --git a/neutron/tests/unit/services/trunk/test_plugin.py b/neutron/tests/unit/services/trunk/test_plugin.py index f5598260e9a..8cf33aa6090 100644 --- a/neutron/tests/unit/services/trunk/test_plugin.py +++ b/neutron/tests/unit/services/trunk/test_plugin.py @@ -367,6 +367,31 @@ class TrunkPluginTestCase(test_plugin.Ml2PluginV2TestCase): self.assertEqual(final_trunk_status, current_trunk.status) return trunk, current_trunk + def test_get_trunk_subport(self): + + with self.port() as parent_port, self.port() as child_port: + trunk = self._create_test_trunk(parent_port) + subport = create_subport_dict(child_port['port']['id']) + self.trunk_plugin.add_subports( + self.context, trunk['id'], {'sub_ports': [subport]}) + + portid = parent_port['port']['id'] + pl = directory.get_plugin() + res = pl.get_ports(self.context, filters={'id': [portid]}) + + expected_trunk_details = { + 'trunk_id': trunk['id'], + 'sub_ports': [ + { + 'segmentation_id': subport['segmentation_id'], + 'port_id': subport['port_id'], + 'segmentation_type': subport['segmentation_type'], + 'mac_address': child_port['port']['mac_address'], + } + ] + } + self.assertEqual(expected_trunk_details, res[0]['trunk_details']) + class TrunkPluginCompatDriversTestCase(test_plugin.Ml2PluginV2TestCase):