Skip to content

Commit dccde70

Browse files
rtheisSteve Martinelli
authored andcommitted
Add "security group rule show" command
Add the "os security group rule show" command which will use the SDK when neutron is enabled, and use the nova client when nova network is enabled. Change-Id: I41efaa4468ec15e4e86d74144cc72edc25a29024 Partial-Bug: #1519512 Implements: blueprint neutron-client
1 parent 20f8646 commit dccde70

7 files changed

Lines changed: 236 additions & 4 deletions

File tree

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,18 @@ List security group rules
6767
.. describe:: <group>
6868

6969
List all rules in this security group (name or ID)
70+
71+
security group rule show
72+
------------------------
73+
74+
Display security group rule details
75+
76+
.. program:: security group rule show
77+
.. code:: bash
78+
79+
os security group rule show
80+
<rule>
81+
82+
.. describe:: <rule>
83+
84+
Security group rule to display (ID only)

functional/tests/network/v2/test_security_group_rule.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,10 @@ def test_security_group_rule_list(self):
5757
self.SECURITY_GROUP_NAME +
5858
opts)
5959
self.assertIn(self.SECURITY_GROUP_RULE_ID, raw_output)
60+
61+
def test_security_group_rule_show(self):
62+
opts = self.get_show_opts(self.ID_FIELD)
63+
raw_output = self.openstack('security group rule show ' +
64+
self.SECURITY_GROUP_RULE_ID +
65+
opts)
66+
self.assertEqual(self.SECURITY_GROUP_RULE_ID + "\n", raw_output)

openstackclient/network/v2/security_group_rule.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,54 @@
1313

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

16+
import six
17+
18+
from openstackclient.common import exceptions
19+
from openstackclient.common import utils
1620
from openstackclient.network import common
1721

1822

23+
def _xform_security_group_rule(sgroup):
24+
info = {}
25+
info.update(sgroup)
26+
from_port = info.pop('from_port')
27+
to_port = info.pop('to_port')
28+
if isinstance(from_port, int) and isinstance(to_port, int):
29+
port_range = {'port_range': "%u:%u" % (from_port, to_port)}
30+
elif from_port is None and to_port is None:
31+
port_range = {'port_range': ""}
32+
else:
33+
port_range = {'port_range': "%s:%s" % (from_port, to_port)}
34+
info.update(port_range)
35+
if 'cidr' in info['ip_range']:
36+
info['ip_range'] = info['ip_range']['cidr']
37+
else:
38+
info['ip_range'] = ''
39+
if info['ip_protocol'] is None:
40+
info['ip_protocol'] = ''
41+
elif info['ip_protocol'].lower() == 'icmp':
42+
info['port_range'] = ''
43+
group = info.pop('group')
44+
if 'name' in group:
45+
info['remote_security_group'] = group['name']
46+
else:
47+
info['remote_security_group'] = ''
48+
return info
49+
50+
51+
def _format_security_group_rule_show(obj):
52+
data = _xform_security_group_rule(obj)
53+
return zip(*sorted(six.iteritems(data)))
54+
55+
56+
def _get_columns(item):
57+
columns = item.keys()
58+
if 'tenant_id' in columns:
59+
columns.remove('tenant_id')
60+
columns.append('project_id')
61+
return tuple(sorted(columns))
62+
63+
1964
class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
2065
"""Delete a security group rule"""
2166

@@ -33,3 +78,44 @@ def take_action_network(self, client, parsed_args):
3378

3479
def take_action_compute(self, client, parsed_args):
3580
client.security_group_rules.delete(parsed_args.rule)
81+
82+
83+
class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
84+
"""Display security group rule details"""
85+
86+
def update_parser_common(self, parser):
87+
parser.add_argument(
88+
'rule',
89+
metavar="<rule>",
90+
help="Security group rule to display (ID only)"
91+
)
92+
return parser
93+
94+
def take_action_network(self, client, parsed_args):
95+
obj = client.find_security_group_rule(parsed_args.rule,
96+
ignore_missing=False)
97+
columns = _get_columns(obj)
98+
data = utils.get_item_properties(obj, columns)
99+
return (columns, data)
100+
101+
def take_action_compute(self, client, parsed_args):
102+
# NOTE(rtheis): Unfortunately, compute does not have an API
103+
# to get or list security group rules so parse through the
104+
# security groups to find all accessible rules in search of
105+
# the requested rule.
106+
obj = None
107+
security_group_rules = []
108+
for security_group in client.security_groups.list():
109+
security_group_rules.extend(security_group.rules)
110+
for security_group_rule in security_group_rules:
111+
if parsed_args.rule == str(security_group_rule.get('id')):
112+
obj = security_group_rule
113+
break
114+
115+
if obj is None:
116+
msg = "Could not find security group rule " \
117+
"with ID %s" % parsed_args.rule
118+
raise exceptions.CommandError(msg)
119+
120+
# NOTE(rtheis): Format security group rule
121+
return _format_security_group_rule_show(obj)

openstackclient/tests/network/v2/fakes.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,13 @@ def create_one_security_group_rule(attrs={}, methods={}):
479479
:param Dictionary methods:
480480
A dictionary with all methods
481481
:return:
482-
A FakeResource object, with id, name, etc.
482+
A FakeResource object, with id, etc.
483483
"""
484484
# Set default attributes.
485485
security_group_rule_attrs = {
486-
'description': 'security-group-rule-desc-' + uuid.uuid4().hex,
487486
'direction': 'ingress',
488487
'ethertype': 'IPv4',
489488
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
490-
'name': 'security-group-rule-name-' + uuid.uuid4().hex,
491489
'port_range_max': None,
492490
'port_range_min': None,
493491
'protocol': None,
@@ -501,7 +499,11 @@ def create_one_security_group_rule(attrs={}, methods={}):
501499
security_group_rule_attrs.update(attrs)
502500

503501
# Set default methods.
504-
security_group_rule_methods = {}
502+
security_group_rule_methods = {
503+
'keys': ['direction', 'ethertype', 'id', 'port_range_max',
504+
'port_range_min', 'protocol', 'remote_group_id',
505+
'remote_ip_prefix', 'security_group_id', 'tenant_id'],
506+
}
505507

506508
# Overwrite default methods.
507509
security_group_rule_methods.update(methods)
@@ -510,6 +512,10 @@ def create_one_security_group_rule(attrs={}, methods={}):
510512
info=copy.deepcopy(security_group_rule_attrs),
511513
methods=copy.deepcopy(security_group_rule_methods),
512514
loaded=True)
515+
516+
# Set attributes with special mappings.
517+
security_group_rule.project_id = security_group_rule_attrs['tenant_id']
518+
513519
return security_group_rule
514520

515521
@staticmethod

openstackclient/tests/network/v2/test_security_group_rule.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
# under the License.
1212
#
1313

14+
import copy
1415
import mock
1516

1617
from openstackclient.network.v2 import security_group_rule
1718
from openstackclient.tests.compute.v2 import fakes as compute_fakes
19+
from openstackclient.tests import fakes
1820
from openstackclient.tests.network.v2 import fakes as network_fakes
21+
from openstackclient.tests import utils as tests_utils
1922

2023

2124
class TestSecurityGroupRuleNetwork(network_fakes.TestNetworkV2):
@@ -98,3 +101,112 @@ def test_security_group_rule_delete(self):
98101
self.compute.security_group_rules.delete.assert_called_with(
99102
self._security_group_rule.id)
100103
self.assertIsNone(result)
104+
105+
106+
class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
107+
108+
# The security group rule to be shown.
109+
_security_group_rule = \
110+
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
111+
112+
columns = (
113+
'direction',
114+
'ethertype',
115+
'id',
116+
'port_range_max',
117+
'port_range_min',
118+
'project_id',
119+
'protocol',
120+
'remote_group_id',
121+
'remote_ip_prefix',
122+
'security_group_id',
123+
)
124+
125+
data = (
126+
_security_group_rule.direction,
127+
_security_group_rule.ethertype,
128+
_security_group_rule.id,
129+
_security_group_rule.port_range_max,
130+
_security_group_rule.port_range_min,
131+
_security_group_rule.project_id,
132+
_security_group_rule.protocol,
133+
_security_group_rule.remote_group_id,
134+
_security_group_rule.remote_ip_prefix,
135+
_security_group_rule.security_group_id,
136+
)
137+
138+
def setUp(self):
139+
super(TestShowSecurityGroupRuleNetwork, self).setUp()
140+
141+
self.network.find_security_group_rule = mock.Mock(
142+
return_value=self._security_group_rule)
143+
144+
# Get the command object to test
145+
self.cmd = security_group_rule.ShowSecurityGroupRule(
146+
self.app, self.namespace)
147+
148+
def test_show_no_options(self):
149+
self.assertRaises(tests_utils.ParserException,
150+
self.check_parser, self.cmd, [], [])
151+
152+
def test_show_all_options(self):
153+
arglist = [
154+
self._security_group_rule.id,
155+
]
156+
verifylist = [
157+
('rule', self._security_group_rule.id),
158+
]
159+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
160+
161+
columns, data = self.cmd.take_action(parsed_args)
162+
163+
self.network.find_security_group_rule.assert_called_with(
164+
self._security_group_rule.id, ignore_missing=False)
165+
self.assertEqual(tuple(self.columns), columns)
166+
self.assertEqual(self.data, data)
167+
168+
169+
class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
170+
171+
# The security group rule to be shown.
172+
_security_group_rule = \
173+
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule()
174+
175+
columns, data = \
176+
security_group_rule._format_security_group_rule_show(
177+
_security_group_rule._info)
178+
179+
def setUp(self):
180+
super(TestShowSecurityGroupRuleCompute, self).setUp()
181+
182+
self.app.client_manager.network_endpoint_enabled = False
183+
184+
# Build a security group fake customized for this test.
185+
security_group_rules = [self._security_group_rule._info]
186+
security_group = fakes.FakeResource(
187+
info=copy.deepcopy({'rules': security_group_rules}),
188+
loaded=True)
189+
security_group.rules = security_group_rules
190+
self.compute.security_groups.list.return_value = [security_group]
191+
192+
# Get the command object to test
193+
self.cmd = security_group_rule.ShowSecurityGroupRule(self.app, None)
194+
195+
def test_show_no_options(self):
196+
self.assertRaises(tests_utils.ParserException,
197+
self.check_parser, self.cmd, [], [])
198+
199+
def test_show_all_options(self):
200+
arglist = [
201+
self._security_group_rule.id,
202+
]
203+
verifylist = [
204+
('rule', self._security_group_rule.id),
205+
]
206+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
207+
208+
columns, data = self.cmd.take_action(parsed_args)
209+
210+
self.compute.security_groups.list.assert_called_with()
211+
self.assertEqual(self.columns, columns)
212+
self.assertEqual(self.data, data)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
Add support for ``security group rule show`` command.
5+
[Bug `1519512 <https://bugs.launchpad.net/bugs/1519512>`_]

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ openstack.network.v2 =
340340
router_show = openstackclient.network.v2.router:ShowRouter
341341
security_group_delete = openstackclient.network.v2.security_group:DeleteSecurityGroup
342342
security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
343+
security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule
343344
subnet_list = openstackclient.network.v2.subnet:ListSubnet
344345
subnet_pool_delete = openstackclient.network.v2.subnet_pool:DeleteSubnetPool
345346
subnet_pool_list = openstackclient.network.v2.subnet_pool:ListSubnetPool

0 commit comments

Comments
 (0)