Merge "Support IPv6 addresses better"

This commit is contained in:
Zuul 2019-08-01 21:54:02 +00:00 committed by Gerrit Code Review
commit daadcd79c5
5 changed files with 79 additions and 15 deletions

@ -27,8 +27,9 @@ Create a new security group rule
.. option:: --remote-ip <ip-address> .. option:: --remote-ip <ip-address>
Remote IP address block Remote IP address block (may use CIDR notation;
(may use CIDR notation; default for IPv4 rule: 0.0.0.0/0) default for IPv4 rule: 0.0.0.0/0,
default for IPv6 rule: ::/0)
.. option:: --remote-group <group> .. option:: --remote-group <group>
@ -134,6 +135,7 @@ List security group rules
openstack security group rule list openstack security group rule list
[--all-projects] [--all-projects]
[--protocol <protocol>] [--protocol <protocol>]
[--ethertype <ethertype>]
[--ingress | --egress] [--ingress | --egress]
[--long] [--long]
[<group>] [<group>]
@ -151,7 +153,6 @@ List security group rules
*Compute version 2 does not have additional fields to display.* *Compute version 2 does not have additional fields to display.*
.. option:: --protocol .. option:: --protocol
List rules by the IP protocol (ah, dhcp, egp, esp, gre, icmp, igmp, List rules by the IP protocol (ah, dhcp, egp, esp, gre, icmp, igmp,
@ -161,6 +162,12 @@ List security group rules
*Network version 2* *Network version 2*
.. option:: --ethertype
List rules by the Ethertype (IPv4 or IPv6)
*Network version 2*
.. option:: --ingress .. option:: --ingress
List rules applied to incoming network traffic List rules applied to incoming network traffic

@ -62,6 +62,17 @@ def _format_network_port_range(rule):
return port_range return port_range
def _format_remote_ip_prefix(rule):
remote_ip_prefix = rule['remote_ip_prefix']
if remote_ip_prefix is None:
ethertype = rule['ether_type']
if ethertype == 'IPv4':
remote_ip_prefix = '0.0.0.0/0'
elif ethertype == 'IPv6':
remote_ip_prefix = '::/0'
return remote_ip_prefix
def _get_columns(item): def _get_columns(item):
column_map = { column_map = {
'tenant_id': 'project_id', 'tenant_id': 'project_id',
@ -108,7 +119,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
"--remote-ip", "--remote-ip",
metavar="<ip-address>", metavar="<ip-address>",
help=_("Remote IP address block (may use CIDR notation; " help=_("Remote IP address block (may use CIDR notation; "
"default for IPv4 rule: 0.0.0.0/0)"), "default for IPv4 rule: 0.0.0.0/0, "
"default for IPv6 rule: ::/0)"),
) )
remote_group.add_argument( remote_group.add_argument(
"--remote-group", "--remote-group",
@ -230,6 +242,14 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
protocol = None protocol = None
return protocol return protocol
def _get_ethertype(self, parsed_args, protocol):
ethertype = 'IPv4'
if parsed_args.ethertype is not None:
ethertype = parsed_args.ethertype
elif self._is_ipv6_protocol(protocol):
ethertype = 'IPv6'
return ethertype
def _is_ipv6_protocol(self, protocol): def _is_ipv6_protocol(self, protocol):
# NOTE(rtheis): Neutron has deprecated protocol icmpv6. # NOTE(rtheis): Neutron has deprecated protocol icmpv6.
# However, while the OSC CLI doesn't document the protocol, # However, while the OSC CLI doesn't document the protocol,
@ -264,12 +284,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
# NOTE(rtheis): Use ethertype specified else default based # NOTE(rtheis): Use ethertype specified else default based
# on IP protocol. # on IP protocol.
if parsed_args.ethertype: attrs['ethertype'] = self._get_ethertype(parsed_args,
attrs['ethertype'] = parsed_args.ethertype attrs['protocol'])
elif self._is_ipv6_protocol(attrs['protocol']):
attrs['ethertype'] = 'IPv6'
else:
attrs['ethertype'] = 'IPv4'
# NOTE(rtheis): Validate the port range and ICMP type and code. # NOTE(rtheis): Validate the port range and ICMP type and code.
# It would be ideal if argparse could do this. # It would be ideal if argparse could do this.
@ -306,6 +322,8 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne):
attrs['remote_ip_prefix'] = parsed_args.remote_ip attrs['remote_ip_prefix'] = parsed_args.remote_ip
elif attrs['ethertype'] == 'IPv4': elif attrs['ethertype'] == 'IPv4':
attrs['remote_ip_prefix'] = '0.0.0.0/0' attrs['remote_ip_prefix'] = '0.0.0.0/0'
elif attrs['ethertype'] == 'IPv6':
attrs['remote_ip_prefix'] = '::/0'
attrs['security_group_id'] = security_group_id attrs['security_group_id'] = security_group_id
if parsed_args.project is not None: if parsed_args.project is not None:
identity_client = self.app.client_manager.identity identity_client = self.app.client_manager.identity
@ -387,6 +405,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
""" """
rule = rule.to_dict() rule = rule.to_dict()
rule['port_range'] = _format_network_port_range(rule) rule['port_range'] = _format_network_port_range(rule)
rule['remote_ip_prefix'] = _format_remote_ip_prefix(rule)
return rule return rule
def update_parser_common(self, parser): def update_parser_common(self, parser):
@ -418,6 +437,12 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
"udp, udplite, vrrp and integer representations [0-255] " "udp, udplite, vrrp and integer representations [0-255] "
"or any; default: any (all protocols))") "or any; default: any (all protocols))")
) )
parser.add_argument(
'--ethertype',
metavar='<ethertype>',
type=_convert_to_lowercase,
help=_("List rules by the Ethertype (IPv4 or IPv6)")
)
direction_group = parser.add_mutually_exclusive_group() direction_group = parser.add_mutually_exclusive_group()
direction_group.add_argument( direction_group.add_argument(
'--ingress', '--ingress',
@ -458,11 +483,12 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
column_headers = ( column_headers = (
'ID', 'ID',
'IP Protocol', 'IP Protocol',
'Ethertype',
'IP Range', 'IP Range',
'Port Range', 'Port Range',
) )
if parsed_args.long: if parsed_args.long:
column_headers = column_headers + ('Direction', 'Ethertype',) column_headers = column_headers + ('Direction',)
column_headers = column_headers + ('Remote Security Group',) column_headers = column_headers + ('Remote Security Group',)
if parsed_args.group is None: if parsed_args.group is None:
column_headers = column_headers + ('Security Group',) column_headers = column_headers + ('Security Group',)
@ -473,11 +499,12 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
columns = ( columns = (
'id', 'id',
'protocol', 'protocol',
'ether_type',
'remote_ip_prefix', 'remote_ip_prefix',
'port_range', 'port_range',
) )
if parsed_args.long: if parsed_args.long:
columns = columns + ('direction', 'ether_type',) columns = columns + ('direction',)
columns = columns + ('remote_group_id',) columns = columns + ('remote_group_id',)
# Get the security group rules using the requested query. # Get the security group rules using the requested query.
@ -516,6 +543,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
columns = ( columns = (
"ID", "ID",
"IP Protocol", "IP Protocol",
"Ethertype",
"IP Range", "IP Range",
"Port Range", "Port Range",
"Remote Security Group", "Remote Security Group",
@ -564,6 +592,9 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne):
def take_action_network(self, client, parsed_args): def take_action_network(self, client, parsed_args):
obj = client.find_security_group_rule(parsed_args.rule, obj = client.find_security_group_rule(parsed_args.rule,
ignore_missing=False) ignore_missing=False)
# necessary for old rules that have None in this field
if not obj['remote_ip_prefix']:
obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj)
display_columns, columns = _get_columns(obj) display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns) data = utils.get_item_properties(obj, columns)
return (display_columns, data) return (display_columns, data)

@ -337,6 +337,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
_security_group_rule_tcp = \ _security_group_rule_tcp = \
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({
'ip_protocol': 'tcp', 'ip_protocol': 'tcp',
'ethertype': 'IPv4',
'from_port': 80, 'from_port': 80,
'to_port': 80, 'to_port': 80,
'group': {'name': _security_group['name']}, 'group': {'name': _security_group['name']},
@ -344,6 +345,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
_security_group_rule_icmp = \ _security_group_rule_icmp = \
compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({ compute_fakes.FakeSecurityGroupRule.create_one_security_group_rule({
'ip_protocol': 'icmp', 'ip_protocol': 'icmp',
'ethertype': 'IPv4',
'from_port': -1, 'from_port': -1,
'to_port': -1, 'to_port': -1,
'ip_range': {'cidr': '10.0.2.0/24'}, 'ip_range': {'cidr': '10.0.2.0/24'},
@ -357,6 +359,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
expected_columns_with_group = ( expected_columns_with_group = (
'ID', 'ID',
'IP Protocol', 'IP Protocol',
'Ethertype',
'IP Range', 'IP Range',
'Port Range', 'Port Range',
'Remote Security Group', 'Remote Security Group',
@ -373,6 +376,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
expected_rule_with_group = ( expected_rule_with_group = (
rule['id'], rule['id'],
rule['ip_protocol'], rule['ip_protocol'],
rule['ethertype'],
rule['ip_range'], rule['ip_range'],
rule['port_range'], rule['port_range'],
rule['remote_security_group'], rule['remote_security_group'],

@ -388,7 +388,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': 443, 'port_range_min': 443,
'protocol': '6', 'protocol': '6',
'remote_group_id': None, 'remote_group_id': None,
'remote_ip_prefix': None, 'remote_ip_prefix': '::/0',
}) })
arglist = [ arglist = [
'--dst-port', str(self._security_group_rule.port_range_min), '--dst-port', str(self._security_group_rule.port_range_min),
@ -419,6 +419,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_max': self._security_group_rule.port_range_max, 'port_range_max': self._security_group_rule.port_range_max,
'port_range_min': self._security_group_rule.port_range_min, 'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol, 'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id, 'security_group_id': self._security_group.id,
'tenant_id': self.project.id, 'tenant_id': self.project.id,
}) })
@ -664,6 +665,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': 139, 'port_range_min': 139,
'port_range_max': 2, 'port_range_max': 2,
'protocol': 'ipv6-icmp', 'protocol': 'ipv6-icmp',
'remote_ip_prefix': '::/0',
}) })
arglist = [ arglist = [
'--icmp-type', str(self._security_group_rule.port_range_min), '--icmp-type', str(self._security_group_rule.port_range_min),
@ -688,6 +690,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'port_range_min': self._security_group_rule.port_range_min, 'port_range_min': self._security_group_rule.port_range_min,
'port_range_max': self._security_group_rule.port_range_max, 'port_range_max': self._security_group_rule.port_range_max,
'protocol': self._security_group_rule.protocol, 'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id, 'security_group_id': self._security_group.id,
}) })
self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_columns, columns)
@ -698,6 +701,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'ether_type': 'IPv6', 'ether_type': 'IPv6',
'port_range_min': 139, 'port_range_min': 139,
'protocol': 'icmpv6', 'protocol': 'icmpv6',
'remote_ip_prefix': '::/0',
}) })
arglist = [ arglist = [
'--icmp-type', str(self._security_group_rule.port_range_min), '--icmp-type', str(self._security_group_rule.port_range_min),
@ -720,6 +724,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'ethertype': self._security_group_rule.ether_type, 'ethertype': self._security_group_rule.ether_type,
'port_range_min': self._security_group_rule.port_range_min, 'port_range_min': self._security_group_rule.port_range_min,
'protocol': self._security_group_rule.protocol, 'protocol': self._security_group_rule.protocol,
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id, 'security_group_id': self._security_group.id,
}) })
self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_columns, columns)
@ -868,15 +873,16 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
expected_columns_with_group_and_long = ( expected_columns_with_group_and_long = (
'ID', 'ID',
'IP Protocol', 'IP Protocol',
'Ethertype',
'IP Range', 'IP Range',
'Port Range', 'Port Range',
'Direction', 'Direction',
'Ethertype',
'Remote Security Group', 'Remote Security Group',
) )
expected_columns_no_group = ( expected_columns_no_group = (
'ID', 'ID',
'IP Protocol', 'IP Protocol',
'Ethertype',
'IP Range', 'IP Range',
'Port Range', 'Port Range',
'Remote Security Group', 'Remote Security Group',
@ -889,16 +895,17 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
expected_data_with_group_and_long.append(( expected_data_with_group_and_long.append((
_security_group_rule.id, _security_group_rule.id,
_security_group_rule.protocol, _security_group_rule.protocol,
_security_group_rule.ether_type,
_security_group_rule.remote_ip_prefix, _security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range( security_group_rule._format_network_port_range(
_security_group_rule), _security_group_rule),
_security_group_rule.direction, _security_group_rule.direction,
_security_group_rule.ether_type,
_security_group_rule.remote_group_id, _security_group_rule.remote_group_id,
)) ))
expected_data_no_group.append(( expected_data_no_group.append((
_security_group_rule.id, _security_group_rule.id,
_security_group_rule.protocol, _security_group_rule.protocol,
_security_group_rule.ether_type,
_security_group_rule.remote_ip_prefix, _security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range( security_group_rule._format_network_port_range(
_security_group_rule), _security_group_rule),

@ -0,0 +1,15 @@
---
features:
- |
Security group rules can now be filtered by Ethertype in
``security group rule list`` using ``--ethertype`` with either
``ipv4`` or ``ipv6`` as an argument.
upgrade:
- |
Security group rule listings now have the ``Ethertype`` field displayed
by default to more easily differentiate between IPv4 and IPv6 rules.
In addition, the ``IP Range`` field of a security group will be
changed to ``0.0.0.0/0`` for IPv4 and ``::/0`` for IPv6 if no
value is returned for the address, based on the Ethertype field of
the rule. For further information see
[Bug `1735575 <https://bugs.launchpad.net/bugs/1735575>`_]