Get "security_groups" when port list

Neutron API is accepting 'security_groups' field  in
order to return the list of security_groups attached
to a port, but openstackclient is parsing the output
over a Openstack Port object that has security_group_ids
to map. This patch sends to the Neutron API the expected
field value and replace the output key to allow the
mapping just in case '--long' argument is passed.

Closes-Bug: #2095414
Change-Id: I188edc3c620ce29d7b16497ca24fd7d972a06618
This commit is contained in:
Fernando Royo 2025-01-21 14:23:07 +01:00
parent 146a1814b6
commit d2d7219231
3 changed files with 51 additions and 23 deletions
openstackclient
network/v2
tests
functional/network/v2
unit/network/v2

@ -866,35 +866,30 @@ class ListPort(command.Lister):
network_client = self.app.client_manager.network
identity_client = self.app.client_manager.identity
columns = (
columns = [
'id',
'name',
'mac_address',
'fixed_ips',
'status',
)
column_headers = (
]
column_headers = [
'ID',
'Name',
'MAC Address',
'Fixed IP Addresses',
'Status',
)
]
filters = {}
if parsed_args.long:
columns += (
'security_group_ids',
'device_owner',
'tags',
'trunk_details',
columns.extend(
['security_groups', 'device_owner', 'tags', 'trunk_details']
)
column_headers += (
'Security Groups',
'Device Owner',
'Tags',
'Trunk subports',
column_headers.extend(
['Security Groups', 'Device Owner', 'Tags', 'Trunk subports']
)
if parsed_args.device_owner is not None:
filters['device_owner'] = parsed_args.device_owner
if parsed_args.device_id is not None:
@ -942,6 +937,12 @@ class ListPort(command.Lister):
data = network_client.ports(fields=columns, **filters)
if parsed_args.long:
columns = [
'security_group_ids' if item == 'security_groups' else item
for item in columns
]
headers, attrs = utils.calculate_header_and_attrs(
column_headers, columns, parsed_args
)

@ -81,10 +81,20 @@ class PortTests(common.NetworkTagTests):
self.addCleanup(self.openstack, f'port delete {id1}')
self.assertEqual(self.NAME, json_output.get('name'))
# sg for port2
sg_name1 = uuid.uuid4().hex
json_output = self.openstack(
f'port create --network {self.NETWORK_NAME} {self.NAME}x',
f'security group create {sg_name1}',
parse_output=True,
)
sg_id1 = json_output.get('id')
self.addCleanup(self.openstack, f'security group delete {sg_id1}')
json_output = self.openstack(
f'port create --network {self.NETWORK_NAME} '
f'--security-group {sg_name1} {self.NAME}x',
parse_output=True,
)
id2 = json_output.get('id')
self.assertIsNotNone(id2)
mac2 = json_output.get('mac_address')
@ -113,6 +123,12 @@ class PortTests(common.NetworkTagTests):
id_list = [item.get('ID') for item in json_output]
self.assertIn(id1, id_list)
self.assertIn(id2, id_list)
item_sg_map = {
item.get('ID'): item.get('Security Groups') for item in json_output
}
self.assertIn(id1, item_sg_map.keys())
self.assertIn(id2, item_sg_map.keys())
self.assertIn([sg_id1], item_sg_map.values())
# Test list --mac-address
json_output = self.openstack(
@ -127,6 +143,17 @@ class PortTests(common.NetworkTagTests):
self.assertNotIn(mac1, item_map.values())
self.assertIn(mac2, item_map.values())
# Test list --security-group
json_output = self.openstack(
f'port list --security-group {sg_id1}',
parse_output=True,
)
item_map = {
item.get('ID'): item.get('Security Groups') for item in json_output
}
self.assertNotIn(id1, item_map.keys())
self.assertIn(id2, item_map.keys())
# Test list with unknown fields
json_output = self.openstack(
'port list -c ID -c Name -c device_id',

@ -24,13 +24,13 @@ from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as test_utils
LIST_FIELDS_TO_RETRIEVE = ('id', 'name', 'mac_address', 'fixed_ips', 'status')
LIST_FIELDS_TO_RETRIEVE_LONG = (
'security_group_ids',
LIST_FIELDS_TO_RETRIEVE = ['id', 'name', 'mac_address', 'fixed_ips', 'status']
LIST_FIELDS_TO_RETRIEVE_LONG = [
'security_groups',
'device_owner',
'tags',
'trunk_details',
)
]
class TestPort(network_fakes.TestNetworkV2):
@ -1274,15 +1274,15 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort):
)
_ports = (_pport, _sport1, _sport2)
columns = (
columns = [
'ID',
'Name',
'MAC Address',
'Fixed IP Addresses',
'Status',
)
]
columns_long = (
columns_long = [
'ID',
'Name',
'MAC Address',
@ -1292,7 +1292,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort):
'Device Owner',
'Tags',
'Trunk subports',
)
]
data = []
for prt in _ports: