Skip to content

Commit 842882f

Browse files
committed
Refactor security group list to use SDK
Refactored the 'os security group list' command to use the SDK when neutron is enabled, but continue to use the nova client when nova network is enabled. This refactor also removes the logic for displaying project names instead of project IDs when the --all-projects option is specified. This logic was removed because it is inconsistent with the other network commands. Since neutron will always display security groups across all projects for an admin, the --all-projects option is now hidden when neutron is enabled and the Project column is always displayed. Change-Id: I934a1f5084ef3c5f929d0ffd38ebf5064d799941 Partial-Bug: #1519511 Related-to: blueprint neutron-client
1 parent 5310cfb commit 842882f

8 files changed

Lines changed: 267 additions & 126 deletions

File tree

doc/source/command-objects/security-group.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ List security groups
5454

5555
Display information from all projects (admin only)
5656

57+
*Network version 2 ignores this option and will always display information*
58+
*for all projects.*
59+
5760
security group set
5861
------------------
5962

openstackclient/compute/v2/security_group.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import six
2020

21-
from keystoneauth1 import exceptions as ks_exc
22-
2321
try:
2422
from novaclient.v2 import security_group_rules
2523
except ImportError:
@@ -169,58 +167,6 @@ def take_action(self, parsed_args):
169167
return zip(*sorted(six.iteritems(info)))
170168

171169

172-
class ListSecurityGroup(command.Lister):
173-
"""List security groups"""
174-
175-
def get_parser(self, prog_name):
176-
parser = super(ListSecurityGroup, self).get_parser(prog_name)
177-
parser.add_argument(
178-
'--all-projects',
179-
action='store_true',
180-
default=False,
181-
help='Display information from all projects (admin only)',
182-
)
183-
return parser
184-
185-
def take_action(self, parsed_args):
186-
187-
def _get_project(project_id):
188-
try:
189-
return getattr(project_hash[project_id], 'name', project_id)
190-
except KeyError:
191-
return project_id
192-
193-
compute_client = self.app.client_manager.compute
194-
columns = (
195-
"ID",
196-
"Name",
197-
"Description",
198-
)
199-
column_headers = columns
200-
if parsed_args.all_projects:
201-
# TODO(dtroyer): Translate Project_ID to Project (name)
202-
columns = columns + ('Tenant ID',)
203-
column_headers = column_headers + ('Project',)
204-
search = {'all_tenants': parsed_args.all_projects}
205-
data = compute_client.security_groups.list(search_opts=search)
206-
207-
project_hash = {}
208-
try:
209-
projects = self.app.client_manager.identity.projects.list()
210-
except ks_exc.ClientException:
211-
# This fails when the user is not an admin, just move along
212-
pass
213-
else:
214-
for project in projects:
215-
project_hash[project.id] = project
216-
217-
return (column_headers,
218-
(utils.get_item_properties(
219-
s, columns,
220-
formatters={'Tenant ID': _get_project},
221-
) for s in data))
222-
223-
224170
class ListSecurityGroupRule(command.Lister):
225171
"""List security group rules"""
226172

openstackclient/network/v2/security_group.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
"""Security Group action implementations"""
1515

16+
import argparse
17+
1618
from openstackclient.common import utils
1719
from openstackclient.network import common
1820

@@ -38,3 +40,51 @@ def take_action_compute(self, client, parsed_args):
3840
parsed_args.group,
3941
)
4042
client.security_groups.delete(data.id)
43+
44+
45+
class ListSecurityGroup(common.NetworkAndComputeLister):
46+
"""List security groups"""
47+
48+
def update_parser_network(self, parser):
49+
# Maintain and hide the argument for backwards compatibility.
50+
# Network will always return all projects for an admin.
51+
parser.add_argument(
52+
'--all-projects',
53+
action='store_true',
54+
default=False,
55+
help=argparse.SUPPRESS,
56+
)
57+
return parser
58+
59+
def update_parser_compute(self, parser):
60+
parser.add_argument(
61+
'--all-projects',
62+
action='store_true',
63+
default=False,
64+
help='Display information from all projects (admin only)',
65+
)
66+
return parser
67+
68+
def _get_return_data(self, data, include_project=True):
69+
columns = (
70+
"ID",
71+
"Name",
72+
"Description",
73+
)
74+
column_headers = columns
75+
if include_project:
76+
columns = columns + ('Tenant ID',)
77+
column_headers = column_headers + ('Project',)
78+
return (column_headers,
79+
(utils.get_item_properties(
80+
s, columns,
81+
) for s in data))
82+
83+
def take_action_network(self, client, parsed_args):
84+
return self._get_return_data(client.security_groups())
85+
86+
def take_action_compute(self, client, parsed_args):
87+
search = {'all_tenants': parsed_args.all_projects}
88+
data = client.security_groups.list(search_opts=search)
89+
return self._get_return_data(data,
90+
include_project=parsed_args.all_projects)

openstackclient/tests/compute/v2/fakes.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,70 @@ def create_hypervisors_stats(attrs={}, count=2):
298298
return hypervisors
299299

300300

301+
class FakeSecurityGroup(object):
302+
"""Fake one or more security groups."""
303+
304+
@staticmethod
305+
def create_one_security_group(attrs=None, methods=None):
306+
"""Create a fake security group.
307+
308+
:param Dictionary attrs:
309+
A dictionary with all attributes
310+
:param Dictionary methods:
311+
A dictionary with all methods
312+
:return:
313+
A FakeResource object, with id, name, etc.
314+
"""
315+
if attrs is None:
316+
attrs = {}
317+
if methods is None:
318+
methods = {}
319+
320+
# Set default attributes.
321+
security_group_attrs = {
322+
'id': 'security-group-id-' + uuid.uuid4().hex,
323+
'name': 'security-group-name-' + uuid.uuid4().hex,
324+
'description': 'security-group-description-' + uuid.uuid4().hex,
325+
'tenant_id': 'project-id-' + uuid.uuid4().hex,
326+
'rules': [],
327+
}
328+
329+
# Overwrite default attributes.
330+
security_group_attrs.update(attrs)
331+
332+
# Set default methods.
333+
security_group_methods = {}
334+
335+
# Overwrite default methods.
336+
security_group_methods.update(methods)
337+
338+
security_group = fakes.FakeResource(
339+
info=copy.deepcopy(security_group_attrs),
340+
methods=copy.deepcopy(security_group_methods),
341+
loaded=True)
342+
return security_group
343+
344+
@staticmethod
345+
def create_security_groups(attrs=None, methods=None, count=2):
346+
"""Create multiple fake security groups.
347+
348+
:param Dictionary attrs:
349+
A dictionary with all attributes
350+
:param Dictionary methods:
351+
A dictionary with all methods
352+
:param int count:
353+
The number of security groups to fake
354+
:return:
355+
A list of FakeResource objects faking the security groups
356+
"""
357+
security_groups = []
358+
for i in range(0, count):
359+
security_groups.append(
360+
FakeSecurityGroup.create_one_security_group(attrs, methods))
361+
362+
return security_groups
363+
364+
301365
class FakeSecurityGroupRule(object):
302366
"""Fake one or more security group rules."""
303367

openstackclient/tests/compute/v2/test_security_group.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -125,65 +125,3 @@ def test_security_group_create_description(self):
125125

126126
self.assertEqual(self.columns, columns)
127127
self.assertEqual(self.data, data)
128-
129-
130-
class TestSecurityGroupList(TestSecurityGroup):
131-
132-
def setUp(self):
133-
super(TestSecurityGroupList, self).setUp()
134-
135-
self.secgroups_mock.list.return_value = [
136-
FakeSecurityGroupResource(
137-
None,
138-
copy.deepcopy(SECURITY_GROUP),
139-
loaded=True,
140-
),
141-
]
142-
143-
# Get the command object to test
144-
self.cmd = security_group.ListSecurityGroup(self.app, None)
145-
146-
def test_security_group_list_no_options(self):
147-
self.projects_mock.list.return_value = [
148-
fakes.FakeResource(
149-
None,
150-
copy.deepcopy(identity_fakes.PROJECT),
151-
loaded=True,
152-
),
153-
]
154-
155-
arglist = []
156-
verifylist = [
157-
('all_projects', False),
158-
]
159-
160-
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
161-
162-
# In base command class Lister in cliff, abstract method take_action()
163-
# returns a tuple containing the column names and an iterable
164-
# containing the data to be listed.
165-
columns, data = self.cmd.take_action(parsed_args)
166-
167-
# Set expected values
168-
kwargs = {
169-
'search_opts': {
170-
'all_tenants': False,
171-
},
172-
}
173-
174-
self.secgroups_mock.list.assert_called_with(
175-
**kwargs
176-
)
177-
178-
collist = (
179-
'ID',
180-
'Name',
181-
'Description',
182-
)
183-
self.assertEqual(collist, columns)
184-
datalist = ((
185-
security_group_id,
186-
security_group_name,
187-
security_group_description,
188-
), )
189-
self.assertEqual(datalist, tuple(data))

0 commit comments

Comments
 (0)