From 9d5be5251f31ce78d76f0715402f6e1510ff41a5 Mon Sep 17 00:00:00 2001 From: Trygve Vea Date: Mon, 16 Oct 2017 21:58:23 +0200 Subject: [PATCH] Hide IPv6-addresses when assigning floating IP addresses A floating IP can only be associated with an IPv4 address, so we should not show IPv6 addresses in the list of ports when assigning floating IP addresses. Change-Id: I7b39a263445204a9efbc8fe113cc79146d9bee8c Closes-Bug: #1703360 Closes-Bug: #1724393 --- openstack_dashboard/api/neutron.py | 3 ++ .../project/networks/subnets/tests.py | 4 +- .../dashboards/project/networks/tests.py | 6 ++- .../test/api_tests/network_tests.py | 39 +++++++++------- .../test/api_tests/neutron_tests.py | 41 +++++++++++------ .../test/test_data/neutron_data.py | 46 +++++++++++++++++-- 6 files changed, 101 insertions(+), 38 deletions(-) diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index 49e9478e27..75ace31715 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -669,6 +669,9 @@ class FloatingIpManager(object): for ip in p.fixed_ips: if ip['subnet_id'] not in reachable_subnets: continue + # Floating IPs can only target IPv4 addresses. + if netaddr.IPAddress(ip['ip_address']).version != 4: + continue targets.append(FloatingIpTarget(p, ip['ip_address'], server_name)) return targets diff --git a/openstack_dashboard/dashboards/project/networks/subnets/tests.py b/openstack_dashboard/dashboards/project/networks/subnets/tests.py index 8eacab4d87..41e72607e0 100644 --- a/openstack_dashboard/dashboards/project/networks/subnets/tests.py +++ b/openstack_dashboard/dashboards/project/networks/subnets/tests.py @@ -175,7 +175,7 @@ class NetworkSubnetTests(test.TestCase): 'subnetpool_list')}) def test_subnet_create_post_with_additional_attributes(self): network = self.networks.list()[1] - subnet = self.subnets.list()[1] + subnet = self.subnets.list()[2] api.neutron.network_get(IsA(http.HttpRequest), network.id)\ .AndReturn(self.networks.first()) @@ -951,7 +951,7 @@ class NetworkSubnetTests(test.TestCase): 'is_extension_supported', 'subnetpool_list')}) def test_subnet_update_post_with_additional_attributes(self): - subnet = self.subnets.list()[1] + subnet = self.subnets.list()[2] api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)\ .AndReturn(subnet) api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)\ diff --git a/openstack_dashboard/dashboards/project/networks/tests.py b/openstack_dashboard/dashboards/project/networks/tests.py index 77164aa68b..da05879dca 100644 --- a/openstack_dashboard/dashboards/project/networks/tests.py +++ b/openstack_dashboard/dashboards/project/networks/tests.py @@ -685,7 +685,7 @@ class NetworkTests(test.TestCase, NetworkStubMixin): test_with_subnetpool=False ): network = self.networks.first() - subnet_v6 = self.subnets.list()[3] + subnet_v6 = self.subnets.list()[4] api.neutron.is_extension_supported(IsA(http.HttpRequest), 'subnet_allocation').\ @@ -936,12 +936,14 @@ class NetworkTests(test.TestCase, NetworkStubMixin): network = self.networks.first() network.subnets = [subnet.id for subnet in network.subnets] subnet_id = network.subnets[0] + subnetv6_id = network.subnets[1] api.neutron.network_get(IsA(http.HttpRequest), network.id, expand_subnet=False)\ .AndReturn(network) self._stub_net_list() api.neutron.subnet_delete(IsA(http.HttpRequest), subnet_id) + api.neutron.subnet_delete(IsA(http.HttpRequest), subnetv6_id) api.neutron.network_delete(IsA(http.HttpRequest), network.id) self.mox.ReplayAll() @@ -959,12 +961,14 @@ class NetworkTests(test.TestCase, NetworkStubMixin): network = self.networks.first() network.subnets = [subnet.id for subnet in network.subnets] subnet_id = network.subnets[0] + subnetv6_id = network.subnets[1] api.neutron.network_get(IsA(http.HttpRequest), network.id, expand_subnet=False)\ .AndReturn(network) self._stub_net_list() api.neutron.subnet_delete(IsA(http.HttpRequest), subnet_id) + api.neutron.subnet_delete(IsA(http.HttpRequest), subnetv6_id) api.neutron.network_delete(IsA(http.HttpRequest), network.id)\ .AndRaise(self.exceptions.neutron) diff --git a/openstack_dashboard/test/api_tests/network_tests.py b/openstack_dashboard/test/api_tests/network_tests.py index 79c9831c3f..f0c0ed14ad 100644 --- a/openstack_dashboard/test/api_tests/network_tests.py +++ b/openstack_dashboard/test/api_tests/network_tests.py @@ -14,6 +14,8 @@ import collections +import netaddr + from django.test.utils import override_settings from openstack_dashboard import api @@ -34,23 +36,24 @@ class NetworkApiNeutronTests(NetworkApiNeutronTestBase): for p in server_ports: net_name = self.networks.get(id=p['network_id']).name for ip in p.fixed_ips: + version = netaddr.IPAddress(ip['ip_address']).version addresses[net_name].append( - {'version': 4, + {'version': version, 'addr': ip['ip_address'], 'OS-EXT-IPS-MAC:mac_addr': p.mac_address, 'OS-EXT-IPS:type': 'fixed'}) - if no_fip_expected: - continue - fips = self.floating_ips.filter(port_id=p['id']) - if not fips: - continue - # Only one FIP should match. - fip = fips[0] - addresses[net_name].append( - {'version': 4, - 'addr': fip.floating_ip_address, - 'OS-EXT-IPS-MAC:mac_addr': p.mac_address, - 'OS-EXT-IPS:type': 'floating'}) + if no_fip_expected: + continue + fips = self.floating_ips.filter(port_id=p['id']) + if not fips: + continue + # Only one FIP should match. + fip = fips[0] + addresses[net_name].append( + {'version': 4, + 'addr': fip.floating_ip_address, + 'OS-EXT-IPS-MAC:mac_addr': p.mac_address, + 'OS-EXT-IPS:type': 'floating'}) return addresses def _check_server_address(self, res_server_data, no_fip_expected=False): @@ -105,12 +108,14 @@ class NetworkApiNeutronTests(NetworkApiNeutronTestBase): # The expected is also calculated, we examine the result manually once. addrs = servers[0].addresses['net1'] if router_enabled: + self.assertEqual(3, len(addrs)) + self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type']) + self.assertEqual('fixed', addrs[1]['OS-EXT-IPS:type']) + self.assertEqual('floating', addrs[2]['OS-EXT-IPS:type']) + else: self.assertEqual(2, len(addrs)) self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type']) - self.assertEqual('floating', addrs[1]['OS-EXT-IPS:type']) - else: - self.assertEqual(1, len(addrs)) - self.assertEqual('fixed', addrs[0]['OS-EXT-IPS:type']) + self.assertEqual('fixed', addrs[1]['OS-EXT-IPS:type']) # server[1] has one fixed IP. self._check_server_address(servers[1], no_fip_expected) diff --git a/openstack_dashboard/test/api_tests/neutron_tests.py b/openstack_dashboard/test/api_tests/neutron_tests.py index 3082c2b927..7c6a176fc3 100644 --- a/openstack_dashboard/test/api_tests/neutron_tests.py +++ b/openstack_dashboard/test/api_tests/neutron_tests.py @@ -14,6 +14,7 @@ import copy from mox3.mox import IsA +import netaddr from neutronclient.common import exceptions as neutron_exc from oslo_utils import uuidutils import six @@ -165,17 +166,20 @@ class NeutronApiTests(test.APITestCase): def test_network_get(self): network = {'network': self.api_networks.first()} subnet = {'subnet': self.api_subnets.first()} + subnetv6 = {'subnet': self.api_subnets.list()[1]} network_id = self.api_networks.first()['id'] subnet_id = self.api_networks.first()['subnets'][0] + subnetv6_id = self.api_networks.first()['subnets'][1] neutronclient = self.stub_neutronclient() neutronclient.show_network(network_id).AndReturn(network) neutronclient.show_subnet(subnet_id).AndReturn(subnet) + neutronclient.show_subnet(subnetv6_id).AndReturn(subnetv6) self.mox.ReplayAll() ret_val = api.neutron.network_get(self.request, network_id) self.assertIsInstance(ret_val, api.neutron.Network) - self.assertEqual(1, len(ret_val['subnets'])) + self.assertEqual(2, len(ret_val['subnets'])) self.assertIsInstance(ret_val['subnets'][0], api.neutron.Subnet) def test_network_get_with_subnet_get_notfound(self): @@ -190,7 +194,7 @@ class NeutronApiTests(test.APITestCase): ret_val = api.neutron.network_get(self.request, network_id) self.assertIsInstance(ret_val, api.neutron.Network) - self.assertEqual(1, len(ret_val['subnets'])) + self.assertEqual(2, len(ret_val['subnets'])) self.assertNotIsInstance(ret_val['subnets'][0], api.neutron.Subnet) self.assertIsInstance(ret_val['subnets'][0], str) @@ -1160,19 +1164,16 @@ class NeutronApiFloatingIpTests(NeutronApiTestBase): api.neutron.floating_ip_disassociate(self.request, fip['id']) - def _get_target_id(self, port): + def _get_target_id(self, port, ip=None): param = {'id': port['id'], - 'addr': port['fixed_ips'][0]['ip_address']} + 'addr': ip or port['fixed_ips'][0]['ip_address']} return '%(id)s_%(addr)s' % param - def _get_target_name(self, port): + def _get_target_name(self, port, ip=None): param = {'svrid': port['device_id'], - 'addr': port['fixed_ips'][0]['ip_address']} + 'addr': ip or port['fixed_ips'][0]['ip_address']} return 'server_%(svrid)s: %(addr)s' % param - def _subs_from_port(self, port): - return [ip['subnet_id'] for ip in port['fixed_ips']] - @override_settings( OPENSTACK_NEUTRON_NETWORK={ 'enable_fip_topology_check': True, @@ -1185,12 +1186,20 @@ class NeutronApiFloatingIpTests(NeutronApiTestBase): subnet_id = self.subnets.first().id shared_nets = [n for n in self.api_networks.list() if n['shared']] shared_subnet_ids = [s for n in shared_nets for s in n['subnets']] - target_ports = [ - (self._get_target_id(p), self._get_target_name(p)) for p in ports - if (not p['device_owner'].startswith('network:') and - (subnet_id in self._subs_from_port(p) or - (set(shared_subnet_ids) & set(self._subs_from_port(p))))) - ] + target_ports = [] + for p in ports: + if p['device_owner'].startswith('network:'): + continue + port_subnets = [ip['subnet_id'] for ip in p['fixed_ips']] + if not (subnet_id in port_subnets or + (set(shared_subnet_ids) & set(port_subnets))): + continue + for ip in p['fixed_ips']: + if netaddr.IPAddress(ip['ip_address']).version != 4: + continue + target_ports.append(( + self._get_target_id(p, ip['ip_address']), + self._get_target_name(p, ip['ip_address']))) filters = {'tenant_id': self.request.user.tenant_id} self.qclient.list_ports(**filters).AndReturn({'ports': ports}) servers = self.servers.list() @@ -1219,6 +1228,8 @@ class NeutronApiFloatingIpTests(NeutronApiTestBase): rets = api.neutron.floating_ip_target_list(self.request) self.assertEqual(len(target_ports), len(rets)) for ret, exp in zip(rets, target_ports): + pid, ip_address = ret.id.split('_', 1) + self.assertEqual(4, netaddr.IPAddress(ip['ip_address']).version) self.assertEqual(exp[0], ret.id) self.assertEqual(exp[1], ret.name) diff --git a/openstack_dashboard/test/test_data/neutron_data.py b/openstack_dashboard/test/test_data/neutron_data.py index 62297e576c..b3fbb72f68 100644 --- a/openstack_dashboard/test/test_data/neutron_data.py +++ b/openstack_dashboard/test/test_data/neutron_data.py @@ -74,7 +74,8 @@ def data(TEST): 'id': '82288d84-e0a5-42ac-95be-e6af08727e42', 'name': 'net1', 'status': 'ACTIVE', - 'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9'], + 'subnets': ['e8abc972-eb0c-41f1-9edd-4bc6e3bcd8c9', + '41e53a49-442b-4307-9e9a-88967a6b6657'], 'tenant_id': '1', 'router:external': False, 'shared': False} @@ -90,15 +91,34 @@ def data(TEST): 'name': 'mysubnet1', 'network_id': network_dict['id'], 'tenant_id': network_dict['tenant_id']} + subnetv6_dict = { + 'allocation_pools': [{'start': 'fdb6:b88a:488e::2', + 'end': 'fdb6:b88a:488e:0:ffff:ffff:ffff:ffff'}], + 'dns_nameservers': [], + 'host_routes': [], + 'cidr': 'fdb6:b88a:488e::/64', + 'enable_dhcp': True, + 'gateway_ip': 'fdb6:b88a:488e::1', + 'id': network_dict['subnets'][1], + 'ip_version': 6, + 'name': 'myv6subnet', + 'network_id': network_dict['id'], + 'tenant_id': network_dict['tenant_id'], + 'ipv6_ra_mode': 'slaac', + 'ipv6_address_mode': 'slaac' + } TEST.api_networks.add(network_dict) TEST.api_subnets.add(subnet_dict) + TEST.api_subnets.add(subnetv6_dict) network = copy.deepcopy(network_dict) subnet = neutron.Subnet(subnet_dict) - network['subnets'] = [subnet] + subnetv6 = neutron.Subnet(subnetv6_dict) + network['subnets'] = [subnet, subnetv6] TEST.networks.add(neutron.Network(network)) TEST.subnets.add(subnet) + TEST.subnets.add(subnetv6) # Ports on 1st network. port_dict = { @@ -130,7 +150,9 @@ def data(TEST): 'device_id': '1', 'device_owner': 'compute:nova', 'fixed_ips': [{'ip_address': '10.0.0.4', - 'subnet_id': subnet_dict['id']}], + 'subnet_id': subnet_dict['id']}, + {'ip_address': 'fdb6:b88a:488e:0:f816:3eff:fe9d:e62f', + 'subnet_id': subnetv6_dict['id']}], 'id': '7e6ce62c-7ea2-44f8-b6b4-769af90a8406', 'mac_address': 'fa:16:3e:9d:e6:2f', 'name': '', @@ -168,6 +190,24 @@ def data(TEST): } TEST.api_ports.add(port_dict) TEST.ports.add(neutron.Port(port_dict)) + port_dict = { + 'admin_state_up': True, + 'device_id': '279989f7-54bb-41d9-ba42-0d61f12fda61', + 'device_owner': 'network:router_interface', + 'fixed_ips': [{'ip_address': 'fdb6:b88a:488e::1', + 'subnet_id': subnetv6_dict['id']}], + 'id': '8047e0d5-5ef5-4b6e-a1a7-d3a52ad980f7', + 'mac_address': 'fa:16:3e:69:6e:e9', + 'name': '', + 'network_id': network_dict['id'], + 'status': 'ACTIVE', + 'tenant_id': network_dict['tenant_id'], + 'binding:vnic_type': 'normal', + 'binding:host_id': 'host', + 'security_groups': [], + } + TEST.api_ports.add(port_dict) + TEST.ports.add(neutron.Port(port_dict)) # 2nd network. network_dict = {'admin_state_up': True,