diff --git a/openstack_dashboard/api/network.py b/openstack_dashboard/api/network.py index ea76d417289..086fd646e88 100644 --- a/openstack_dashboard/api/network.py +++ b/openstack_dashboard/api/network.py @@ -83,6 +83,11 @@ def floating_ip_target_get_by_instance(request, instance_id): instance_id) +def floating_ip_target_list_by_instance(request, instance_id): + floating_ips = NetworkClient(request).floating_ips + return floating_ips.list_target_id_by_instance(instance_id) + + def floating_ip_simple_associate_supported(request): return NetworkClient(request).floating_ips.is_simple_associate_supported() diff --git a/openstack_dashboard/api/network_base.py b/openstack_dashboard/api/network_base.py index 74722bc31b6..8ee01499cf4 100644 --- a/openstack_dashboard/api/network_base.py +++ b/openstack_dashboard/api/network_base.py @@ -118,6 +118,13 @@ def get_target_id_by_instance(self, instance_id): """ pass + @abc.abstractmethod + def list_target_id_by_instance(self, instance_id): + """Returns a list of instance's target IDs of floating IP association + based on the backend implementation + """ + pass + @abc.abstractmethod def is_simple_associate_supported(self): """Returns True if the default floating IP pool is enabled.""" diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index c09a6743b30..72b42c2e148 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -392,16 +392,25 @@ def list_targets(self): targets.append(FloatingIpTarget(target)) return targets - def get_target_id_by_instance(self, instance_id): - # In Neutron one port can have multiple ip addresses, so this method - # picks up the first one and generate target id. + def _target_ports_by_instance(self, instance_id): if not instance_id: return None search_opts = {'device_id': instance_id} - ports = port_list(self.request, **search_opts) + return port_list(self.request, **search_opts) + + def get_target_id_by_instance(self, instance_id): + # In Neutron one port can have multiple ip addresses, so this method + # picks up the first one and generate target id. + ports = self._target_ports_by_instance(instance_id) if not ports: return None - return '%s_%s' % (ports[0].id, ports[0].fixed_ips[0]['ip_address']) + return '{0}_{1}'.format(ports[0].id, + ports[0].fixed_ips[0]['ip_address']) + + def list_target_id_by_instance(self, instance_id): + ports = self._target_ports_by_instance(instance_id) + return ['{0}_{1}'.format(p.id, p.fixed_ips[0]['ip_address']) + for p in ports] def is_simple_associate_supported(self): # NOTE: There are two reason that simple association support diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index 1d6e76306dd..5b6a25bdcd1 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -350,6 +350,9 @@ def list_targets(self): def get_target_id_by_instance(self, instance_id): return instance_id + def list_target_id_by_instance(self, instance_id): + return [instance_id, ] + def is_simple_associate_supported(self): return conf.HORIZON_CONFIG["simple_ip_management"] diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py index f96bc36b9bd..011676c6f3b 100644 --- a/openstack_dashboard/dashboards/project/instances/tables.py +++ b/openstack_dashboard/dashboards/project/instances/tables.py @@ -408,17 +408,19 @@ def single(self, table, request, instance_id): try: # target_id is port_id for Neutron and instance_id for Nova Network # (Neutron API wrapper returns a 'portid_fixedip' string) - target_id = api.network.floating_ip_target_get_by_instance( - request, instance_id).split('_')[0] + targets = api.network.floating_ip_target_list_by_instance( + request, instance_id) + + target_ids = [t.split('_')[0] for t in targets] fips = [fip for fip in api.network.tenant_floating_ip_list(request) - if fip.port_id == target_id] + if fip.port_id in target_ids] # Removing multiple floating IPs at once doesn't work, so this pops # off the first one. if fips: fip = fips.pop() api.network.floating_ip_disassociate(request, - fip.id, target_id) + fip.id, fip.port_id) messages.success(request, _("Successfully disassociated " "floating IP: %s") % fip.ip) diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index b2b066ff443..52e5948fdb4 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -1896,7 +1896,7 @@ def test_associate_floating_ip(self): self.assertRedirectsNoFollow(res, INDEX_URL) - @test.create_stubs({api.network: ('floating_ip_target_get_by_instance', + @test.create_stubs({api.network: ('floating_ip_target_list_by_instance', 'tenant_floating_ip_list', 'floating_ip_disassociate', 'servers_update_addresses',), @@ -1916,9 +1916,9 @@ def test_disassociate_floating_ip(self): api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) - api.network.floating_ip_target_get_by_instance( + api.network.floating_ip_target_list_by_instance( IsA(http.HttpRequest), - server.id).AndReturn(server.id) + server.id).AndReturn([server.id, ]) api.network.tenant_floating_ip_list( IsA(http.HttpRequest)).AndReturn([fip]) api.network.floating_ip_disassociate( diff --git a/openstack_dashboard/test/api_tests/network_tests.py b/openstack_dashboard/test/api_tests/network_tests.py index bf0f706e54f..3b65e852e45 100644 --- a/openstack_dashboard/test/api_tests/network_tests.py +++ b/openstack_dashboard/test/api_tests/network_tests.py @@ -542,3 +542,15 @@ def test_floating_ip_target_get_by_instance(self): ret = api.network.floating_ip_target_get_by_instance(self.request, '1') self.assertEqual(ret, self._get_target_id(candidates[0])) + + def test_target_floating_ip_port_by_instance(self): + ports = self.api_ports.list() + candidates = [p for p in ports if p['device_id'] == '1'] + search_opts = {'device_id': '1'} + self.qclient.list_ports(**search_opts).AndReturn({'ports': candidates}) + self.mox.ReplayAll() + + ret = api.network.floating_ip_target_list_by_instance(self.request, + '1') + self.assertEqual(ret[0], self._get_target_id(candidates[0])) + self.assertEqual(len(ret), len(candidates))