From 0ba77e672756bffc31c1e94eae7baaad556b17e3 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Date: Wed, 19 Feb 2025 21:21:52 +0000 Subject: [PATCH] [Neutron] Fix the "port show" command for trunk details In [1], the "port list --long" command received a new column, showing the trunk subports related to a parent port. The problem of this patch is that the ``_formatters`` definition, that is shared with the "port show" command too, is changed. The "trunk_details" information presented in both commands differ: * In the "port list" command, only the subports are presented, in order to print a list of dictionaries without showing the ``trunk_id``. * In the "port show" command, it is presented all the trunk information, including the ``trunk_id``. This patch includes functional tests to validate the fix. [1]https://review.opendev.org/c/openstack/python-openstackclient/+/926611 Closes-Bug: #2098950 Change-Id: Ib1107fb3dbb025b39a7c55f90f5fe51ae433a72f --- openstackclient/network/v2/port.py | 5 +- .../tests/functional/network/v2/test_port.py | 79 +++++++++++++++++++ .../tests/unit/network/v2/test_port.py | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index cf334bcddb..91573ffbd3 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -65,8 +65,9 @@ _formatters = { 'fixed_ips': format_columns.ListDictColumn, 'security_group_ids': format_columns.ListColumn, 'tags': format_columns.ListColumn, - 'trunk_details': SubPortColumn, } +_list_formatters = copy.deepcopy(_formatters) +_list_formatters.update({'trunk_details': SubPortColumn}) def _get_columns(item): @@ -952,7 +953,7 @@ class ListPort(command.Lister): utils.get_item_properties( s, attrs, - formatters=_formatters, + formatters=_list_formatters, ) for s in data ), diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index ef46b1f42e..4530395507 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -289,3 +289,82 @@ class PortTests(common.NetworkTagTests): f'{self.base_command} create --network {self.NETWORK_NAME} {args} {name}', parse_output=True, ) + + def _trunk_creation(self): + pport = uuid.uuid4().hex + sport1 = uuid.uuid4().hex + sport2 = uuid.uuid4().hex + trunk = uuid.uuid4().hex + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {pport}', + parse_output=True, + ) + pport_id = json_output.get('id') + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {sport1}', + parse_output=True, + ) + sport1_id = json_output.get('id') + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {sport2}', + parse_output=True, + ) + sport2_id = json_output.get('id') + + self.openstack( + f'network trunk create --parent-port {pport} {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport1},' + f'segmentation-type=vlan,segmentation-id=100 {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport2},' + f'segmentation-type=vlan,segmentation-id=101 {trunk}', + ) + + # NOTE(ralonsoh): keep this order to first delete the trunk and then + # the ports. + self.addCleanup(self.openstack, f'port delete {pport_id}') + self.addCleanup(self.openstack, f'port delete {sport1_id}') + self.addCleanup(self.openstack, f'port delete {sport2_id}') + self.addCleanup(self.openstack, f'network trunk delete {trunk}') + + return pport_id, sport1_id, sport2_id + + def check_subports(self, subports, pport_id, sport1_id, sport2_id): + self.assertEqual(2, len(subports)) + for subport in subports: + if subport['port_id'] == sport1_id: + self.assertEqual(100, subport['segmentation_id']) + elif subport['port_id'] == sport2_id: + self.assertEqual(101, subport['segmentation_id']) + else: + self.fail( + f'Port {pport_id} does not have subport ' + f'{subport["port_id"]}' + ) + self.assertEqual('vlan', subport['segmentation_type']) + + def test_port_list_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + json_output = self.openstack( + 'port list --long', + parse_output=True, + ) + port = next(port for port in json_output if port['ID'] == pport_id) + subports = port['Trunk subports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) + + def test_port_show_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + port = self.openstack( + f'port show {pport_id}', + parse_output=True, + ) + subports = port['trunk_details']['sub_ports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 77561f78c2..a934593cf2 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -122,7 +122,7 @@ class TestPort(network_fakes.TestNetworkV2): fake_port.status, format_columns.ListColumn(fake_port.tags), fake_port.trusted, - port.SubPortColumn(fake_port.trunk_details), + fake_port.trunk_details, fake_port.updated_at, )