Skip to content

Commit ef933db

Browse files
committed
network: Add PVLAN filters to ports and networks
It is useful for users to be able to filter only pvlan networks or to exclude them. For ports, there is now a filter per pvlan-type, per pvlan-community, and to only get the ports that have pvlan attributes enabled or disabled. The new filtering attributes are: - Network: --pvlan, --no-pvlan - Port: --pvlan, --no-pvlan, --pvlan-type, --pvlan-community Unit tests listing these attributes have been added. Assisted-by: Claude Opus 4.6 Change-Id: I4124f10e130d5f12fba8c597525c22bf3c5572d1 Signed-off-by: Elvira Garcia <egarciar@redhat.com>
1 parent caec5c3 commit ef933db

4 files changed

Lines changed: 211 additions & 1 deletion

File tree

openstackclient/network/v2/network.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,17 @@ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
504504
"List only networks with the specified project (name or ID)"
505505
),
506506
)
507+
pvlan_group = parser.add_mutually_exclusive_group()
508+
pvlan_group.add_argument(
509+
'--pvlan',
510+
action='store_true',
511+
help=_("List only networks with PVLAN enabled"),
512+
)
513+
pvlan_group.add_argument(
514+
'--no-pvlan',
515+
action='store_true',
516+
help=_("List only networks with PVLAN disabled"),
517+
)
507518
identity_common.add_project_domain_option_to_parser(parser)
508519
shared_group = parser.add_mutually_exclusive_group()
509520
shared_group.add_argument(
@@ -672,7 +683,10 @@ def take_action(
672683
if parsed_args.segmentation_id:
673684
args['provider:segmentation_id'] = parsed_args.segmentation_id
674685
args['provider_segmentation_id'] = parsed_args.segmentation_id
675-
686+
if parsed_args.pvlan:
687+
args['pvlan'] = True
688+
elif parsed_args.no_pvlan:
689+
args['pvlan'] = False
676690
if parsed_args.marker is not None:
677691
args['marker'] = parsed_args.marker
678692
if parsed_args.limit is not None:

openstackclient/network/v2/port.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,29 @@ def get_parser(self, prog_name: str) -> argparse.ArgumentParser:
905905
metavar='<project>',
906906
help=_("List only ports with the specified project (name or ID)"),
907907
)
908+
parser.add_argument(
909+
'--pvlan-type',
910+
metavar='<type>',
911+
help=_("List only ports with the specified PVLAN type"),
912+
)
913+
parser.add_argument(
914+
'--pvlan-community',
915+
metavar='<community-name>',
916+
help=_("List only ports within a specified PVLAN community"),
917+
)
918+
pvlan_group = parser.add_mutually_exclusive_group()
919+
pvlan_group.add_argument(
920+
'--pvlan',
921+
action='store_true',
922+
default=False,
923+
help=_("List only ports with PVLAN enabled"),
924+
)
925+
pvlan_group.add_argument(
926+
'--no-pvlan',
927+
action='store_true',
928+
default=False,
929+
help=_("List only ports with PVLAN disabled"),
930+
)
908931
parser.add_argument(
909932
'--name',
910933
metavar='<name>',
@@ -971,6 +994,14 @@ def take_action(
971994
]
972995

973996
filters = {}
997+
if (
998+
parsed_args.pvlan
999+
or parsed_args.no_pvlan
1000+
or parsed_args.pvlan_type is not None
1001+
or parsed_args.pvlan_community is not None
1002+
):
1003+
columns.extend(['pvlan_type', 'pvlan_community'])
1004+
column_headers.extend(['PVLAN Type', 'PVLAN Community'])
9741005
if parsed_args.long:
9751006
columns.extend(
9761007
['security_groups', 'device_owner', 'tags', 'trunk_details']
@@ -1021,6 +1052,10 @@ def take_action(
10211052
)
10221053
if parsed_args.security_groups:
10231054
filters['security_group_ids'] = parsed_args.security_groups
1055+
if parsed_args.pvlan_type is not None:
1056+
filters['pvlan_type'] = parsed_args.pvlan_type
1057+
if parsed_args.pvlan_community is not None:
1058+
filters['pvlan_community'] = parsed_args.pvlan_community
10241059
if parsed_args.marker is not None:
10251060
filters['marker'] = parsed_args.marker
10261061
if parsed_args.limit is not None:
@@ -1032,6 +1067,11 @@ def take_action(
10321067

10331068
data = network_client.ports(fields=columns, **filters)
10341069

1070+
if parsed_args.pvlan:
1071+
data = (p for p in data if p.pvlan_type is not None)
1072+
elif parsed_args.no_pvlan:
1073+
data = (p for p in data if p.pvlan_type is None)
1074+
10351075
if parsed_args.long:
10361076
columns = [
10371077
'security_group_ids' if item == 'security_groups' else item

openstackclient/tests/unit/network/v2/test_network.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,38 @@ def test_network_list_provider_segment(self):
814814
self.assertEqual(self.columns, columns)
815815
self.assertCountEqual(self.data, list(data))
816816

817+
def test_network_list_pvlan(self):
818+
arglist = [
819+
'--pvlan',
820+
]
821+
verifylist = [
822+
('long', False),
823+
('pvlan', True),
824+
]
825+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
826+
columns, data = self.cmd.take_action(parsed_args)
827+
828+
self.network_client.networks.assert_called_once_with(**{'pvlan': True})
829+
self.assertEqual(self.columns, columns)
830+
self.assertCountEqual(self.data, list(data))
831+
832+
def test_network_list_no_pvlan(self):
833+
arglist = [
834+
'--no-pvlan',
835+
]
836+
verifylist = [
837+
('long', False),
838+
('no_pvlan', True),
839+
]
840+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
841+
columns, data = self.cmd.take_action(parsed_args)
842+
843+
self.network_client.networks.assert_called_once_with(
844+
**{'pvlan': False}
845+
)
846+
self.assertEqual(self.columns, columns)
847+
self.assertCountEqual(self.data, list(data))
848+
817849
def test_network_list_dhcp_agent(self):
818850
arglist = ['--agent', self._agent.id]
819851
verifylist = [

openstackclient/tests/unit/network/v2/test_port.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,130 @@ def test_port_list_security_group(self):
18901890
self.assertEqual(self.columns, columns)
18911891
self.assertCountEqual(self.data, list(data))
18921892

1893+
def test_port_list_pvlan_type(self):
1894+
arglist = [
1895+
'--pvlan-type',
1896+
'community',
1897+
]
1898+
verifylist = [
1899+
('pvlan_type', 'community'),
1900+
]
1901+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1902+
1903+
columns, _data = self.cmd.take_action(parsed_args)
1904+
expected_fields = [
1905+
*LIST_FIELDS_TO_RETRIEVE,
1906+
'pvlan_type',
1907+
'pvlan_community',
1908+
]
1909+
filters = {
1910+
'pvlan_type': 'community',
1911+
'fields': expected_fields,
1912+
}
1913+
1914+
self.network_client.ports.assert_called_once_with(**filters)
1915+
expected_columns = [*self.columns, 'PVLAN Type', 'PVLAN Community']
1916+
self.assertEqual(expected_columns, columns)
1917+
1918+
def test_port_list_pvlan_community(self):
1919+
arglist = [
1920+
'--pvlan-community',
1921+
'community_1',
1922+
]
1923+
verifylist = [
1924+
('pvlan_community', 'community_1'),
1925+
]
1926+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1927+
1928+
columns, _data = self.cmd.take_action(parsed_args)
1929+
expected_fields = [
1930+
*LIST_FIELDS_TO_RETRIEVE,
1931+
'pvlan_type',
1932+
'pvlan_community',
1933+
]
1934+
filters = {
1935+
'pvlan_community': 'community_1',
1936+
'fields': expected_fields,
1937+
}
1938+
1939+
self.network_client.ports.assert_called_once_with(**filters)
1940+
expected_columns = [*self.columns, 'PVLAN Type', 'PVLAN Community']
1941+
self.assertEqual(expected_columns, columns)
1942+
1943+
def test_port_list_pvlan(self):
1944+
port_pvlan = network_fakes.create_one_port(
1945+
attrs={
1946+
'pvlan_type': 'community',
1947+
'pvlan_community': 'community_1',
1948+
}
1949+
)
1950+
port_no_pvlan = network_fakes.create_one_port(
1951+
attrs={'pvlan_type': None, 'pvlan_community': None}
1952+
)
1953+
self.network_client.ports.return_value = [port_pvlan, port_no_pvlan]
1954+
1955+
arglist = [
1956+
'--pvlan',
1957+
]
1958+
verifylist = [
1959+
('pvlan', True),
1960+
]
1961+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1962+
1963+
columns, data = self.cmd.take_action(parsed_args)
1964+
expected_fields = [
1965+
*LIST_FIELDS_TO_RETRIEVE,
1966+
'pvlan_type',
1967+
'pvlan_community',
1968+
]
1969+
1970+
self.network_client.ports.assert_called_once_with(
1971+
fields=expected_fields
1972+
)
1973+
expected_columns = [*self.columns, 'PVLAN Type', 'PVLAN Community']
1974+
self.assertEqual(expected_columns, columns)
1975+
result = list(data)
1976+
self.assertEqual(1, len(result))
1977+
# First column is the port ID
1978+
self.assertEqual(port_pvlan.id, result[0][0])
1979+
1980+
def test_port_list_no_pvlan(self):
1981+
port_pvlan = network_fakes.create_one_port(
1982+
attrs={
1983+
'pvlan_type': 'community',
1984+
'pvlan_community': 'community_1',
1985+
}
1986+
)
1987+
port_no_pvlan = network_fakes.create_one_port(
1988+
attrs={'pvlan_type': None, 'pvlan_community': None}
1989+
)
1990+
self.network_client.ports.return_value = [port_pvlan, port_no_pvlan]
1991+
1992+
arglist = [
1993+
'--no-pvlan',
1994+
]
1995+
verifylist = [
1996+
('no_pvlan', True),
1997+
]
1998+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1999+
2000+
columns, data = self.cmd.take_action(parsed_args)
2001+
expected_fields = [
2002+
*LIST_FIELDS_TO_RETRIEVE,
2003+
'pvlan_type',
2004+
'pvlan_community',
2005+
]
2006+
2007+
self.network_client.ports.assert_called_once_with(
2008+
fields=expected_fields
2009+
)
2010+
expected_columns = [*self.columns, 'PVLAN Type', 'PVLAN Community']
2011+
self.assertEqual(expected_columns, columns)
2012+
result = list(data)
2013+
self.assertEqual(1, len(result))
2014+
# First column is the port ID
2015+
self.assertEqual(port_no_pvlan.id, result[0][0])
2016+
18932017
def test_port_list_status(self):
18942018
arglist = [
18952019
'--status',

0 commit comments

Comments
 (0)