From 715d6b822838009530b7792cd7749164ae3fc663 Mon Sep 17 00:00:00 2001 From: Assaf Muller Date: Sun, 29 Dec 2013 16:44:46 +0200 Subject: [PATCH] Get instance networking information from Neutron project/instances, admin/instances and the instance details page all get networking information from Nova. However, with Neutron enabled, floating IP associations are done direcly with Neutron, meaning that Nova's DB will fall out of sync and thus the GUI won't reflect successful floating IP associations until Nova polls Neutron again and updates its DB. The polling can take up to several minutes to complete for consecutive floating IP operations. The solution is to update instances' networking information from Neutron immediately after the call to list Nova instances. Closes-Bug: #1265032 Change-Id: I0382fa9a4a9fff21e7b4d05cd3b76783f826735f --- openstack_dashboard/api/network.py | 12 ++ openstack_dashboard/api/neutron.py | 89 ++++++++- .../dashboards/admin/instances/tests.py | 26 ++- .../dashboards/admin/instances/views.py | 8 + .../dashboards/project/instances/tests.py | 189 ++++++++++++------ .../dashboards/project/instances/views.py | 18 +- 6 files changed, 273 insertions(+), 69 deletions(-) diff --git a/openstack_dashboard/api/network.py b/openstack_dashboard/api/network.py index 7ab233c5cd..ea76d41728 100644 --- a/openstack_dashboard/api/network.py +++ b/openstack_dashboard/api/network.py @@ -132,3 +132,15 @@ def server_update_security_groups(request, instance_id, def security_group_backend(request): return NetworkClient(request).secgroups.backend + + +def servers_update_addresses(request, servers): + """Retrieve servers networking information from Neutron if enabled. + + Should be used when up to date networking information is required, + and Nova's networking info caching mechanism is not fast enough. + + """ + neutron_enabled = base.is_service_enabled(request, 'network') + if neutron_enabled: + neutron.servers_update_addresses(request, servers) diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index 39f0877990..4ec535cb06 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -21,12 +21,15 @@ from __future__ import absolute_import +import collections import logging +import netaddr from django.conf import settings # noqa from django.utils.datastructures import SortedDict # noqa from django.utils.translation import ugettext_lazy as _ # noqa +from horizon import messages from horizon.utils.memoized import memoized # noqa from openstack_dashboard.api import base @@ -321,12 +324,12 @@ class FloatingIpManager(network_base.FloatingIpManager): return [FloatingIpPool(pool) for pool in self.client.list_networks(**search_opts).get('networks')] - def list(self): + def list(self, **search_opts): tenant_id = self.request.user.tenant_id # In Neutron, list_floatingips returns Floating IPs from all tenants # when the API is called with admin role, so we need to filter them # with tenant_id. - fips = self.client.list_floatingips(tenant_id=tenant_id) + fips = self.client.list_floatingips(tenant_id=tenant_id, **search_opts) fips = fips.get('floatingips') # Get port list to add instance_id to floating IP list # instance_id is stored in device_id attribute @@ -732,6 +735,88 @@ def provider_list(request): return providers['service_providers'] +def servers_update_addresses(request, servers): + """Retrieve servers networking information from Neutron if enabled. + + Should be used when up to date networking information is required, + and Nova's networking info caching mechanism is not fast enough. + """ + + # Get all (filtered for relevant servers) information from Neutron + try: + ports = port_list(request, + device_id=[instance.id for instance in servers]) + floating_ips = FloatingIpManager(request).list( + port_id=[port.id for port in ports]) + networks = network_list(request, + id=[port.network_id for port in ports]) + except Exception: + error_message = _('Unable to connect to Neutron.') + LOG.error(error_message) + messages.error(request, error_message) + return + + # Map instance to its ports + instances_ports = collections.defaultdict(list) + for port in ports: + instances_ports[port.device_id].append(port) + + # Map port to its floating ips + ports_floating_ips = collections.defaultdict(list) + for fip in floating_ips: + ports_floating_ips[fip.port_id].append(fip) + + # Map network id to its name + network_names = dict(((network.id, network.name) for network in networks)) + + for server in servers: + try: + addresses = _server_get_addresses( + request, + server, + instances_ports, + ports_floating_ips, + network_names) + except Exception as e: + LOG.error(e) + else: + server.addresses = addresses + + +def _server_get_addresses(request, server, ports, floating_ips, network_names): + def _format_address(mac, ip, type): + try: + version = netaddr.IPAddress(ip).version + except Exception as e: + error_message = _('Unable to parse IP address %s.') % ip + LOG.error(error_message) + messages.error(request, error_message) + raise e + return {u'OS-EXT-IPS-MAC:mac_addr': mac, + u'version': version, + u'addr': ip, + u'OS-EXT-IPS:type': type} + + addresses = collections.defaultdict(list) + instance_ports = ports.get(server.id, []) + for port in instance_ports: + network_name = network_names.get(port.network_id) + if network_name is not None: + for fixed_ip in port.fixed_ips: + addresses[network_name].append( + _format_address(port.mac_address, + fixed_ip['ip_address'], + u'fixed')) + port_fips = floating_ips.get(port.id, []) + for fip in port_fips: + addresses[network_name].append( + _format_address(port.mac_address, + fip.floating_ip_address, + u'floating')) + + return dict(addresses) + + @memoized def list_extensions(request): extensions_list = neutronclient(request).list_extensions() diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py index 2e20c4cb40..b41f3c28db 100644 --- a/openstack_dashboard/dashboards/admin/instances/tests.py +++ b/openstack_dashboard/dashboards/admin/instances/tests.py @@ -20,6 +20,7 @@ from django.core.urlresolvers import reverse # noqa from django import http from django.utils.datastructures import SortedDict # noqa +from mox import IgnoreArg # noqa from mox import IsA # noqa from openstack_dashboard import api @@ -32,7 +33,8 @@ INDEX_URL = reverse('horizon:admin:instances:index') class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'server_list', 'extension_supported',), - api.keystone: ('tenant_list',)}) + api.keystone: ('tenant_list',), + api.network: ('servers_update_addresses',)}) def test_index(self): servers = self.servers.list() flavors = self.flavors.list() @@ -45,6 +47,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors) self.mox.ReplayAll() @@ -55,7 +58,8 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'flavor_get', 'server_list', 'extension_supported',), - api.keystone: ('tenant_list',)}) + api.keystone: ('tenant_list',), + api.network: ('servers_update_addresses',)}) def test_index_flavor_list_exception(self): servers = self.servers.list() tenants = self.tenants.list() @@ -66,6 +70,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ .MultipleTimes().AndReturn(True) api.nova.flavor_list(IsA(http.HttpRequest)). \ @@ -85,7 +90,8 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'flavor_get', 'server_list', 'extension_supported', ), - api.keystone: ('tenant_list',)}) + api.keystone: ('tenant_list',), + api.network: ('servers_update_addresses',)}) def test_index_flavor_get_exception(self): servers = self.servers.list() flavors = self.flavors.list() @@ -99,6 +105,7 @@ class InstanceViewTest(test.BaseAdminViewTests): api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ .MultipleTimes().AndReturn(True) api.nova.flavor_list(IsA(http.HttpRequest)). \ @@ -165,14 +172,17 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'server_list', 'extension_supported', ), - api.keystone: ('tenant_list',)}) + api.keystone: ('tenant_list',), + api.network: ('servers_update_addresses',)}) def test_index_options_before_migrate(self): + servers = self.servers.list() api.keystone.tenant_list(IsA(http.HttpRequest)).\ AndReturn([self.tenants.list(), False]) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ .MultipleTimes().AndReturn(True) api.nova.flavor_list(IsA(http.HttpRequest)).\ @@ -186,7 +196,8 @@ class InstanceViewTest(test.BaseAdminViewTests): @test.create_stubs({api.nova: ('flavor_list', 'server_list', 'extension_supported', ), - api.keystone: ('tenant_list',)}) + api.keystone: ('tenant_list',), + api.network: ('servers_update_addresses',)}) def test_index_options_after_migrate(self): servers = self.servers.list() server1 = servers[0] @@ -200,7 +211,8 @@ class InstanceViewTest(test.BaseAdminViewTests): .MultipleTimes().AndReturn(True) api.nova.server_list(IsA(http.HttpRequest), all_tenants=True, search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IsA(http.HttpRequest)).\ AndReturn(self.flavors.list()) self.mox.ReplayAll() diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py index 3b3712de2b..52f8c30c22 100644 --- a/openstack_dashboard/dashboards/admin/instances/views.py +++ b/openstack_dashboard/dashboards/admin/instances/views.py @@ -80,6 +80,14 @@ class AdminIndexView(tables.DataTableView): exceptions.handle(self.request, _('Unable to retrieve instance list.')) if instances: + try: + api.network.servers_update_addresses(self.request, instances) + except Exception: + exceptions.handle( + self.request, + message=_('Unable to retrieve IP addresses from Neutron.'), + ignore=True) + # Gather our flavors to correlate against IDs try: flavors = api.nova.flavor_list(self.request) diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 78e60ef166..bb9136d5d7 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -54,9 +54,11 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_index(self): + servers = self.servers.list() api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ .MultipleTimes().AndReturn(True) @@ -66,7 +68,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) api.network.floating_ip_simple_associate_supported( @@ -106,7 +109,8 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_index_flavor_list_exception(self): servers = self.servers.list() @@ -118,6 +122,7 @@ class InstanceTests(test.TestCase): .MultipleTimes().AndReturn(True) api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndRaise(self.exceptions.nova) api.glance.image_list_detailed(IgnoreArg()) \ @@ -146,7 +151,8 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_index_flavor_get_exception(self): servers = self.servers.list() @@ -162,6 +168,7 @@ class InstanceTests(test.TestCase): search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) @@ -189,7 +196,8 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_index_with_instance_booted_from_volume(self): volume_server = self.servers.first() @@ -208,6 +216,7 @@ class InstanceTests(test.TestCase): search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) api.network.floating_ip_simple_associate_supported( @@ -225,18 +234,20 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_list', 'flavor_list', 'server_delete',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_terminate_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) api.nova.server_delete(IsA(http.HttpRequest), server.id) - self.mox.ReplayAll() formData = {'action': 'instances__terminate__%s' % server.id} @@ -247,13 +258,16 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_list', 'flavor_list', 'server_delete',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_terminate_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) @@ -271,9 +285,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_pause_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -284,7 +300,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_pause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -298,9 +315,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_pause_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -311,7 +330,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_pause(IsA(http.HttpRequest), server.id) \ .AndRaise(self.exceptions.nova) @@ -326,9 +346,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_unpause_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] server.status = "PAUSED" api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -339,7 +361,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_unpause(IsA(http.HttpRequest), server.id) self.mox.ReplayAll() @@ -353,9 +376,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_unpause_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] server.status = "PAUSED" api.nova.extension_supported('AdminActions', @@ -367,7 +392,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_unpause(IsA(http.HttpRequest), server.id) \ .AndRaise(self.exceptions.nova) @@ -381,16 +407,19 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_reboot', 'server_list', 'flavor_list',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_reboot_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_reboot(IsA(http.HttpRequest), server.id, soft_reboot=False) @@ -404,9 +433,11 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_reboot', 'server_list', 'flavor_list',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_reboot_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) @@ -414,7 +445,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_reboot(IsA(http.HttpRequest), server.id, soft_reboot=False) \ .AndRaise(self.exceptions.nova) @@ -429,9 +461,11 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ('server_reboot', 'server_list', 'flavor_list',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_soft_reboot_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.flavor_list(IsA(http.HttpRequest)) \ .AndReturn(self.flavors.list()) @@ -439,7 +473,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_reboot(IsA(http.HttpRequest), server.id, soft_reboot=True) @@ -454,9 +489,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_suspend_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -467,7 +504,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -481,9 +519,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_suspend_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -494,7 +534,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id)) \ .AndRaise(self.exceptions.nova) @@ -509,9 +550,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_resume_instance(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] server.status = "SUSPENDED" api.nova.extension_supported('AdminActions', @@ -523,7 +566,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_resume(IsA(http.HttpRequest), unicode(server.id)) self.mox.ReplayAll() @@ -537,9 +581,11 @@ class InstanceTests(test.TestCase): 'server_list', 'flavor_list', 'extension_supported',), - api.glance: ('image_list_detailed',)}) + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_resume_instance_exception(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] server.status = "SUSPENDED" api.nova.extension_supported('AdminActions', @@ -551,7 +597,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.server_resume(IsA(http.HttpRequest), unicode(server.id)) \ .AndRaise(self.exceptions.nova) @@ -566,12 +613,15 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ("server_get", "instance_volumes_list", "flavor_get"), - api.network: ("server_security_groups",)}) + api.network: ("server_security_groups", + "servers_update_addresses")}) def test_instance_details_volumes(self): server = self.servers.first() volumes = [self.volumes.list()[1]] api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.network.servers_update_addresses(IsA(http.HttpRequest), + IgnoreArg()) api.nova.instance_volumes_list(IsA(http.HttpRequest), server.id).AndReturn(volumes) api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ @@ -590,12 +640,15 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ("server_get", "instance_volumes_list", "flavor_get"), - api.network: ("server_security_groups",)}) + api.network: ("server_security_groups", + "servers_update_addresses")}) def test_instance_details_volume_sorting(self): server = self.servers.first() volumes = self.volumes.list()[1:3] api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.network.servers_update_addresses(IsA(http.HttpRequest), + IgnoreArg()) api.nova.instance_volumes_list(IsA(http.HttpRequest), server.id).AndReturn(volumes) api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ @@ -618,11 +671,14 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ("server_get", "instance_volumes_list", "flavor_get"), - api.network: ("server_security_groups",)}) + api.network: ("server_security_groups", + "servers_update_addresses")}) def test_instance_details_metadata(self): server = self.servers.first() api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.network.servers_update_addresses(IsA(http.HttpRequest), + IgnoreArg()) api.nova.instance_volumes_list(IsA(http.HttpRequest), server.id).AndReturn([]) api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ @@ -650,7 +706,8 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.nova: ("server_get", "instance_volumes_list", "flavor_get"), - api.network: ("server_security_groups",)}) + api.network: ("server_security_groups", + "servers_update_addresses")}) def test_instance_details_fault(self): server = self.servers.first() @@ -666,6 +723,8 @@ class InstanceTests(test.TestCase): "created": "2013-10-07T00:08:32Z"} api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.network.servers_update_addresses(IsA(http.HttpRequest), + IgnoreArg()) api.nova.instance_volumes_list(IsA(http.HttpRequest), server.id).AndReturn([]) api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ @@ -1781,9 +1840,11 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_launch_button_disabled_when_quota_exceeded(self): + servers = self.servers.list() limits = self.limits['absolute'] limits['totalInstancesUsed'] = limits['maxTotalInstances'] @@ -1796,7 +1857,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(limits) api.network.floating_ip_simple_associate_supported( @@ -1822,9 +1884,11 @@ class InstanceTests(test.TestCase): 'extension_supported',), api.glance: ('image_list_detailed',), api.network: - ('floating_ip_simple_associate_supported',), + ('floating_ip_simple_associate_supported', + 'servers_update_addresses',), }) def test_index_options_after_migrate(self): + servers = self.servers.list() server = self.servers.first() server.status = "VERIFY_RESIZE" api.nova.extension_supported('AdminActions', @@ -1836,7 +1900,8 @@ class InstanceTests(test.TestCase): .AndReturn((self.images.list(), False)) search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \ .MultipleTimes().AndReturn(self.limits['absolute']) api.network.floating_ip_simple_associate_supported( @@ -1915,17 +1980,20 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.network: ('floating_ip_target_get_by_instance', 'tenant_floating_ip_allocate', - 'floating_ip_associate'), + 'floating_ip_associate', + 'servers_update_addresses',), api.glance: ('image_list_detailed',), api.nova: ('server_list', 'flavor_list')}) def test_associate_floating_ip(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] fip = self.q_floating_ips.first() search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) @@ -1946,18 +2014,21 @@ class InstanceTests(test.TestCase): @test.create_stubs({api.network: ('floating_ip_target_get_by_instance', 'tenant_floating_ip_list', - 'floating_ip_disassociate',), + 'floating_ip_disassociate', + 'servers_update_addresses',), api.glance: ('image_list_detailed',), api.nova: ('server_list', 'flavor_list')}) def test_disassociate_floating_ip(self): - server = self.servers.first() + servers = self.servers.list() + server = servers[0] fip = self.q_floating_ips.first() fip.port_id = server.id search_opts = {'marker': None, 'paginate': True} api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \ - .AndReturn([self.servers.list(), False]) + .AndReturn([servers, False]) + api.network.servers_update_addresses(IsA(http.HttpRequest), servers) api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list()) api.glance.image_list_detailed(IgnoreArg()) \ .AndReturn((self.images.list(), False)) diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py index 55050f9e16..f114a2e816 100644 --- a/openstack_dashboard/dashboards/project/instances/views.py +++ b/openstack_dashboard/dashboards/project/instances/views.py @@ -67,8 +67,17 @@ class IndexView(tables.DataTableView): instances = [] exceptions.handle(self.request, _('Unable to retrieve instances.')) - # Gather our flavors and images and correlate our instances to them + if instances: + try: + api.network.servers_update_addresses(self.request, instances) + except Exception: + exceptions.handle( + self.request, + message=_('Unable to retrieve IP addresses from Neutron.'), + ignore=True) + + # Gather our flavors and images and correlate our instances to them try: flavors = api.nova.flavor_list(self.request) except Exception: @@ -233,6 +242,13 @@ class DetailView(tabs.TabView): _('Unable to retrieve details for ' 'instance "%s".') % instance_id, redirect=redirect) + try: + api.network.servers_update_addresses(self.request, [instance]) + except Exception: + exceptions.handle( + self.request, + _('Unable to retrieve IP addresses from Neutron for instance ' + '"%s".') % instance_id, ignore=True) return instance def get_tabs(self, request, *args, **kwargs):