Refactor security group rule create to use SDK

Refactored the 'os security group rule create' command to use the
SDK when neutron is enabled, but continue to use the nova client
when nova network is enabled.

Added a release note for the change in security group rules output
due to Network v2.

Change-Id: I8c6c99d5272ff5d410a449f73d198d834c5cd96e
Partial-Bug: #1519512
Implements: blueprint neutron-client
This commit is contained in:
Richard Theis 2016-03-11 15:30:47 -06:00
parent 4b4349ee68
commit d906507962
9 changed files with 434 additions and 345 deletions

View File

@ -32,7 +32,8 @@ Create a new security group rule
.. option:: --dst-port <port-range>
Destination port, may be a range: 137:139 (default: 0; only required for proto tcp and udp)
Destination port, may be a single port or port range: 137:139
(only required for IP protocols tcp and udp)
.. describe:: <group>

View File

@ -16,15 +16,12 @@
"""Compute v2 Security Group action implementations"""
import six
try:
from novaclient.v2 import security_group_rules
except ImportError:
from novaclient.v1_1 import security_group_rules
from openstackclient.common import command
from openstackclient.common import parseractions
from openstackclient.common import utils
@ -56,68 +53,6 @@ def _xform_security_group_rule(sgroup):
return info
class CreateSecurityGroupRule(command.ShowOne):
"""Create a new security group rule"""
def get_parser(self, prog_name):
parser = super(CreateSecurityGroupRule, self).get_parser(prog_name)
parser.add_argument(
'group',
metavar='<group>',
help='Create rule in this security group (name or ID)',
)
parser.add_argument(
"--proto",
metavar="<proto>",
default="tcp",
help="IP protocol (icmp, tcp, udp; default: tcp)",
)
source_group = parser.add_mutually_exclusive_group()
source_group.add_argument(
"--src-ip",
metavar="<ip-address>",
default="0.0.0.0/0",
help="Source IP address block (may use CIDR notation; default: "
"0.0.0.0/0)",
)
source_group.add_argument(
"--src-group",
metavar="<group>",
help="Source security group (ID only)",
)
parser.add_argument(
"--dst-port",
metavar="<port-range>",
default=(0, 0),
action=parseractions.RangeAction,
help="Destination port, may be a range: 137:139 (default: 0; "
"only required for proto tcp and udp)",
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
group = utils.find_resource(
compute_client.security_groups,
parsed_args.group,
)
if parsed_args.proto.lower() == 'icmp':
from_port, to_port = -1, -1
else:
from_port, to_port = parsed_args.dst_port
data = compute_client.security_group_rules.create(
group.id,
parsed_args.proto,
from_port,
to_port,
parsed_args.src_ip,
parsed_args.src_group,
)
info = _xform_security_group_rule(data._info)
return zip(*sorted(six.iteritems(info)))
class ListSecurityGroupRule(command.Lister):
"""List security group rules"""

View File

@ -16,6 +16,7 @@
import six
from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils
from openstackclient.network import common
from openstackclient.network import utils as network_utils
@ -34,6 +35,105 @@ def _get_columns(item):
return tuple(sorted(columns))
def _convert_to_lowercase(string):
return string.lower()
class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
"""Create a new security group rule"""
def update_parser_common(self, parser):
parser.add_argument(
'group',
metavar='<group>',
help='Create rule in this security group (name or ID)',
)
# TODO(rtheis): Add support for additional protocols for network.
# Until then, continue enforcing the compute choices.
parser.add_argument(
"--proto",
metavar="<proto>",
default="tcp",
choices=['icmp', 'tcp', 'udp'],
type=_convert_to_lowercase,
help="IP protocol (icmp, tcp, udp; default: tcp)",
)
source_group = parser.add_mutually_exclusive_group()
source_group.add_argument(
"--src-ip",
metavar="<ip-address>",
default="0.0.0.0/0",
help="Source IP address block (may use CIDR notation; default: "
"0.0.0.0/0)",
)
source_group.add_argument(
"--src-group",
metavar="<group>",
help="Source security group (ID only)",
)
parser.add_argument(
"--dst-port",
metavar="<port-range>",
default=(0, 0),
action=parseractions.RangeAction,
help="Destination port, may be a single port or port range: "
"137:139 (only required for IP protocols tcp and udp)",
)
return parser
def take_action_network(self, client, parsed_args):
# Get the security group ID to hold the rule.
security_group_id = client.find_security_group(
parsed_args.group,
ignore_missing=False
).id
# Build the create attributes.
attrs = {}
# TODO(rtheis): Add --direction option. Until then, continue
# with the default of 'ingress'.
attrs['direction'] = 'ingress'
# TODO(rtheis): Add --ethertype option. Until then, continue
# with the default of 'IPv4'
attrs['ethertype'] = 'IPv4'
# TODO(rtheis): Add port range support (type and code) for icmp
# protocol. Until then, continue ignoring the port range.
if parsed_args.proto != 'icmp':
attrs['port_range_min'] = parsed_args.dst_port[0]
attrs['port_range_max'] = parsed_args.dst_port[1]
attrs['protocol'] = parsed_args.proto
if parsed_args.src_group is not None:
attrs['remote_group_id'] = parsed_args.src_group
else:
attrs['remote_ip_prefix'] = parsed_args.src_ip
attrs['security_group_id'] = security_group_id
# Create and show the security group rule.
obj = client.create_security_group_rule(**attrs)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns)
return (columns, data)
def take_action_compute(self, client, parsed_args):
group = utils.find_resource(
client.security_groups,
parsed_args.group,
)
if parsed_args.proto == 'icmp':
from_port, to_port = -1, -1
else:
from_port, to_port = parsed_args.dst_port
obj = client.security_group_rules.create(
group.id,
parsed_args.proto,
from_port,
to_port,
parsed_args.src_ip,
parsed_args.src_group,
)
return _format_security_group_rule_show(obj._info)
class DeleteSecurityGroupRule(common.NetworkAndComputeCommand):
"""Delete a security group rule"""

View File

@ -393,13 +393,13 @@ class FakeSecurityGroupRule(object):
# Set default attributes.
security_group_rule_attrs = {
'from_port': -1,
'from_port': 0,
'group': {},
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
'ip_protocol': 'icmp',
'ip_protocol': 'tcp',
'ip_range': {'cidr': '0.0.0.0/0'},
'parent_group_id': 'security-group-id-' + uuid.uuid4().hex,
'to_port': -1,
'to_port': 0,
}
# Overwrite default attributes.

View File

@ -17,7 +17,6 @@ from openstackclient.compute.v2 import security_group
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests import utils
security_group_id = '11'
@ -111,271 +110,6 @@ class TestSecurityGroupRule(compute_fakes.TestComputev2):
self.sg_rules_mock.reset_mock()
class TestSecurityGroupRuleCreate(TestSecurityGroupRule):
columns = (
'id',
'ip_protocol',
'ip_range',
'parent_group_id',
'port_range',
'remote_security_group',
)
def setUp(self):
super(TestSecurityGroupRuleCreate, self).setUp()
self.secgroups_mock.get.return_value = FakeSecurityGroupRuleResource(
None,
copy.deepcopy(SECURITY_GROUP),
loaded=True,
)
# Get the command object to test
self.cmd = security_group.CreateSecurityGroupRule(self.app, None)
def test_security_group_rule_create_no_options(self):
self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
None,
copy.deepcopy(SECURITY_GROUP_RULE),
loaded=True,
)
arglist = [
security_group_name,
]
verifylist = [
('group', security_group_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
# SecurityGroupManager.create(name, description)
self.sg_rules_mock.create.assert_called_with(
security_group_id,
'tcp',
0,
0,
security_group_rule_cidr,
None,
)
self.assertEqual(self.columns, columns)
datalist = (
security_group_rule_id,
'tcp',
security_group_rule_cidr,
security_group_id,
'0:0',
'',
)
self.assertEqual(datalist, data)
def test_security_group_rule_create_ftp(self):
sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
sg_rule['from_port'] = 20
sg_rule['to_port'] = 21
self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
None,
sg_rule,
loaded=True,
)
arglist = [
security_group_name,
'--dst-port', '20:21',
]
verifylist = [
('group', security_group_name),
('dst_port', (20, 21)),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
# SecurityGroupManager.create(name, description)
self.sg_rules_mock.create.assert_called_with(
security_group_id,
'tcp',
20,
21,
security_group_rule_cidr,
None,
)
self.assertEqual(self.columns, columns)
datalist = (
security_group_rule_id,
'tcp',
security_group_rule_cidr,
security_group_id,
'20:21',
'',
)
self.assertEqual(datalist, data)
def test_security_group_rule_create_ssh(self):
sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
sg_rule['from_port'] = 22
sg_rule['to_port'] = 22
sg_rule['ip_range'] = {}
sg_rule['group'] = {'name': security_group_name}
self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
None,
sg_rule,
loaded=True,
)
arglist = [
security_group_name,
'--dst-port', '22',
'--src-group', security_group_id,
]
verifylist = [
('group', security_group_name),
('dst_port', (22, 22)),
('src_group', security_group_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
# SecurityGroupManager.create(name, description)
self.sg_rules_mock.create.assert_called_with(
security_group_id,
'tcp',
22,
22,
security_group_rule_cidr,
security_group_id,
)
self.assertEqual(self.columns, columns)
datalist = (
security_group_rule_id,
'tcp',
'',
security_group_id,
'22:22',
security_group_name,
)
self.assertEqual(datalist, data)
def test_security_group_rule_create_udp(self):
sg_rule = copy.deepcopy(SECURITY_GROUP_RULE)
sg_rule['ip_protocol'] = 'udp'
self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
None,
sg_rule,
loaded=True,
)
arglist = [
security_group_name,
'--proto', 'udp',
]
verifylist = [
('group', security_group_name),
('proto', 'udp'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
# SecurityGroupManager.create(name, description)
self.sg_rules_mock.create.assert_called_with(
security_group_id,
'udp',
0,
0,
security_group_rule_cidr,
None,
)
self.assertEqual(self.columns, columns)
datalist = (
security_group_rule_id,
'udp',
security_group_rule_cidr,
security_group_id,
'0:0',
'',
)
self.assertEqual(datalist, data)
def test_security_group_rule_create_icmp(self):
sg_rule_cidr = '10.0.2.0/24'
sg_rule = copy.deepcopy(SECURITY_GROUP_RULE_ICMP)
sg_rule['ip_range'] = {'cidr': sg_rule_cidr}
self.sg_rules_mock.create.return_value = FakeSecurityGroupRuleResource(
None,
sg_rule,
loaded=True,
)
arglist = [
security_group_name,
'--proto', 'ICMP',
'--src-ip', sg_rule_cidr,
]
verifylist = [
('group', security_group_name),
('proto', 'ICMP'),
('src_ip', sg_rule_cidr)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# In base command class ShowOne in cliff, abstract method take_action()
# returns a two-part tuple with a tuple of column names and a tuple of
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
# SecurityGroupManager.create(name, description)
self.sg_rules_mock.create.assert_called_with(
security_group_id,
'ICMP',
-1,
-1,
sg_rule_cidr,
None,
)
self.assertEqual(self.columns, columns)
datalist = (
security_group_rule_id,
'icmp',
sg_rule_cidr,
security_group_id,
'',
'',
)
self.assertEqual(datalist, data)
def test_security_group_rule_create_src_invalid(self):
arglist = [
security_group_name,
'--proto', 'ICMP',
'--src-ip', security_group_rule_cidr,
'--src-group', security_group_id,
]
self.assertRaises(utils.ParserException,
self.check_parser, self.cmd, arglist, [])
class TestSecurityGroupRuleList(TestSecurityGroupRule):
def setUp(self):

View File

@ -510,11 +510,11 @@ class FakeSecurityGroupRule(object):
'direction': 'ingress',
'ethertype': 'IPv4',
'id': 'security-group-rule-id-' + uuid.uuid4().hex,
'port_range_max': None,
'port_range_min': None,
'protocol': None,
'remote_group_id': 'remote-security-group-id-' + uuid.uuid4().hex,
'remote_ip_prefix': None,
'port_range_max': 0,
'port_range_min': 0,
'protocol': 'tcp',
'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'security_group_id': 'security-group-id-' + uuid.uuid4().hex,
'tenant_id': 'project-id-' + uuid.uuid4().hex,
}

View File

@ -39,6 +39,317 @@ class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2):
self.compute = self.app.client_manager.compute
class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
# The security group rule to be created.
_security_group_rule = None
# The security group that will contain the rule created.
_security_group = \
network_fakes.FakeSecurityGroup.create_one_security_group()
expected_columns = (
'direction',
'ethertype',
'id',
'port_range_max',
'port_range_min',
'project_id',
'protocol',
'remote_group_id',
'remote_ip_prefix',
'security_group_id',
)
expected_data = None
def _setup_security_group_rule(self, attrs=None):
self._security_group_rule = \
network_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
attrs)
self.network.create_security_group_rule = mock.Mock(
return_value=self._security_group_rule)
self.expected_data = (
self._security_group_rule.direction,
self._security_group_rule.ethertype,
self._security_group_rule.id,
self._security_group_rule.port_range_max,
self._security_group_rule.port_range_min,
self._security_group_rule.project_id,
self._security_group_rule.protocol,
self._security_group_rule.remote_group_id,
self._security_group_rule.remote_ip_prefix,
self._security_group_rule.security_group_id,
)
def setUp(self):
super(TestCreateSecurityGroupRuleNetwork, self).setUp()
self.network.find_security_group = mock.Mock(
return_value=self._security_group)
# Get the command object to test
self.cmd = security_group_rule.CreateSecurityGroupRule(
self.app, self.namespace)
def test_create_no_options(self):
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, [], [])
def test_create_source_group_and_ip(self):
arglist = [
'--src-ip', '10.10.0.0/24',
'--src-group', self._security_group.id,
self._security_group.id,
]
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, arglist, [])
def test_create_bad_protocol(self):
arglist = [
'--protocol', 'foo',
self._security_group.id,
]
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, arglist, [])
def test_create_default_rule(self):
self._setup_security_group_rule({
'port_range_max': 443,
'port_range_min': 443,
})
arglist = [
'--dst-port', str(self._security_group_rule.port_range_min),
self._security_group.id,
]
verifylist = [
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.create_security_group_rule.assert_called_once_with(**{
'direction': self._security_group_rule.direction,
'ethertype': self._security_group_rule.ethertype,
'port_range_max': self._security_group_rule.port_range_max,
'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
self.assertEqual(tuple(self.expected_columns), columns)
self.assertEqual(self.expected_data, data)
def test_create_source_group(self):
self._setup_security_group_rule({
'port_range_max': 22,
'port_range_min': 22,
'remote_group_id': self._security_group.id,
})
arglist = [
'--dst-port', str(self._security_group_rule.port_range_min),
'--src-group', self._security_group.id,
self._security_group.id,
]
verifylist = [
('dst_port', (self._security_group_rule.port_range_min,
self._security_group_rule.port_range_max)),
('src_group', self._security_group.id),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.create_security_group_rule.assert_called_once_with(**{
'direction': self._security_group_rule.direction,
'ethertype': self._security_group_rule.ethertype,
'port_range_max': self._security_group_rule.port_range_max,
'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol,
'remote_group_id': self._security_group_rule.remote_group_id,
'security_group_id': self._security_group.id,
})
self.assertEqual(tuple(self.expected_columns), columns)
self.assertEqual(self.expected_data, data)
def test_create_source_ip(self):
self._setup_security_group_rule({
'protocol': 'icmp',
'port_range_max': -1,
'port_range_min': -1,
'remote_ip_prefix': '10.0.2.0/24',
})
arglist = [
'--proto', self._security_group_rule.protocol,
'--src-ip', self._security_group_rule.remote_ip_prefix,
self._security_group.id,
]
verifylist = [
('proto', self._security_group_rule.protocol),
('src_ip', self._security_group_rule.remote_ip_prefix),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network.create_security_group_rule.assert_called_once_with(**{
'direction': self._security_group_rule.direction,
'ethertype': self._security_group_rule.ethertype,
'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
self.assertEqual(tuple(self.expected_columns), columns)
self.assertEqual(self.expected_data, data)
class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
# The security group rule to be created.
_security_group_rule = None
# The security group that will contain the rule created.
_security_group = \
compute_fakes.FakeSecurityGroup.create_one_security_group()
def _setup_security_group_rule(self, attrs=None):
self._security_group_rule = \
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule(
attrs)
self.compute.security_group_rules.create.return_value = \
self._security_group_rule
expected_columns, expected_data = \
security_group_rule._format_security_group_rule_show(
self._security_group_rule._info)
return expected_columns, expected_data
def setUp(self):
super(TestCreateSecurityGroupRuleCompute, self).setUp()
self.app.client_manager.network_endpoint_enabled = False
self.compute.security_groups.get.return_value = self._security_group
# Get the command object to test
self.cmd = security_group_rule.CreateSecurityGroupRule(self.app, None)
def test_create_no_options(self):
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, [], [])
def test_create_source_group_and_ip(self):
arglist = [
'--src-ip', '10.10.0.0/24',
'--src-group', self._security_group.id,
self._security_group.id,
]
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, arglist, [])
def test_create_bad_protocol(self):
arglist = [
'--protocol', 'foo',
self._security_group.id,
]
self.assertRaises(tests_utils.ParserException,
self.check_parser, self.cmd, arglist, [])
def test_create_default_rule(self):
expected_columns, expected_data = self._setup_security_group_rule()
dst_port = str(self._security_group_rule.from_port) + ':' + \
str(self._security_group_rule.to_port)
arglist = [
'--dst-port', dst_port,
self._security_group.id,
]
verifylist = [
('dst_port', (self._security_group_rule.from_port,
self._security_group_rule.to_port)),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute.security_group_rules.create.assert_called_once_with(
self._security_group.id,
self._security_group_rule.ip_protocol,
self._security_group_rule.from_port,
self._security_group_rule.to_port,
self._security_group_rule.ip_range['cidr'],
None,
)
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
def test_create_source_group(self):
expected_columns, expected_data = self._setup_security_group_rule({
'from_port': 22,
'to_port': 22,
'group': {'name': self._security_group.id},
})
arglist = [
'--dst-port', str(self._security_group_rule.from_port),
'--src-group', self._security_group.id,
self._security_group.id,
]
verifylist = [
('dst_port', (self._security_group_rule.from_port,
self._security_group_rule.to_port)),
('src_group', self._security_group.id),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute.security_group_rules.create.assert_called_once_with(
self._security_group.id,
self._security_group_rule.ip_protocol,
self._security_group_rule.from_port,
self._security_group_rule.to_port,
self._security_group_rule.ip_range['cidr'],
self._security_group.id,
)
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
def test_create_source_ip(self):
expected_columns, expected_data = self._setup_security_group_rule({
'ip_protocol': 'icmp',
'from_port': -1,
'to_port': -1,
'ip_range': {'cidr': '10.0.2.0/24'},
})
arglist = [
'--proto', self._security_group_rule.ip_protocol,
'--src-ip', self._security_group_rule.ip_range['cidr'],
self._security_group.id,
]
verifylist = [
('proto', self._security_group_rule.ip_protocol),
('src_ip', self._security_group_rule.ip_range['cidr']),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.compute.security_group_rules.create.assert_called_once_with(
self._security_group.id,
self._security_group_rule.ip_protocol,
self._security_group_rule.from_port,
self._security_group_rule.to_port,
self._security_group_rule.ip_range['cidr'],
None,
)
self.assertEqual(expected_columns, columns)
self.assertEqual(expected_data, data)
class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
# The security group rule to be deleted.
@ -68,7 +379,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
result = self.cmd.take_action(parsed_args)
self.network.delete_security_group_rule.assert_called_with(
self.network.delete_security_group_rule.assert_called_once_with(
self._security_group_rule)
self.assertIsNone(result)
@ -98,7 +409,7 @@ class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
result = self.cmd.take_action(parsed_args)
self.compute.security_group_rules.delete.assert_called_with(
self.compute.security_group_rules.delete.assert_called_once_with(
self._security_group_rule.id)
self.assertIsNone(result)
@ -160,7 +471,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.find_security_group_rule.assert_called_with(
self.network.find_security_group_rule.assert_called_once_with(
self._security_group_rule.id, ignore_missing=False)
self.assertEqual(tuple(self.columns), columns)
self.assertEqual(self.data, data)
@ -207,6 +518,6 @@ class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
columns, data = self.cmd.take_action(parsed_args)
self.compute.security_groups.list.assert_called_with()
self.compute.security_groups.list.assert_called_once_with()
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)

View File

@ -0,0 +1,7 @@
---
upgrade:
- The ``security group rule create`` command now uses Network v2
when enabled which results in a more detailed output for network
security group rules that matches the ``security group rule show``
command.
[Bug `1519512 <https://bugs.launchpad.net/bugs/1519512>`_]

View File

@ -100,7 +100,6 @@ openstack.compute.v2 =
keypair_list = openstackclient.compute.v2.keypair:ListKeypair
keypair_show = openstackclient.compute.v2.keypair:ShowKeypair
security_group_rule_create = openstackclient.compute.v2.security_group:CreateSecurityGroupRule
security_group_rule_list = openstackclient.compute.v2.security_group:ListSecurityGroupRule
server_add_security_group = openstackclient.compute.v2.server:AddServerSecurityGroup
@ -349,6 +348,8 @@ openstack.network.v2 =
security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup
security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup
security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup
security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule
security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule
security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule