--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/horizon/patches/12-launchpad-1265032.patch Wed Aug 20 13:22:19 2014 -0700
@@ -0,0 +1,868 @@
+Upstream patch to fix launchpad bug 1265032. This fix is available in
+Icehouse 2014.1 but not yet available in Havana.
+
+From f3557786e2a9f026a16d2fd3b8fa9243bc4c70a9 Mon Sep 17 00:00:00 2001
+From: Assaf Muller <[email protected]>
+Date: Sun, 29 Dec 2013 16:44:46 +0200
+Subject: 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.
+
+Conflicts:
+ openstack_dashboard/dashboards/project/instances/tests.py
+ openstack_dashboard/dashboards/project/instances/views.py
+
+Closes-Bug: #1265032
+Change-Id: I0382fa9a4a9fff21e7b4d05cd3b76783f826735f
+(cherry picked from commit 715d6b822838009530b7792cd7749164ae3fc663)
+
+diff --git a/openstack_dashboard/api/network.py b/openstack_dashboard/api/network.py
+index 7ab233c..ea76d41 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 4443c9b..c09a674 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
+@@ -727,6 +730,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 6beb8fa..9236b4e 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
+@@ -29,7 +30,8 @@ from openstack_dashboard.test import helpers as test
+ 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()
+@@ -42,6 +44,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()
+
+@@ -52,7 +55,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()
+@@ -63,6 +67,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)). \
+@@ -82,7 +87,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()
+@@ -96,6 +102,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)). \
+@@ -162,14 +169,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)).\
+@@ -183,7 +193,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]
+@@ -197,7 +208,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 3397f25..4bc6bee 100644
+--- a/openstack_dashboard/dashboards/admin/instances/views.py
++++ b/openstack_dashboard/dashboards/admin/instances/views.py
+@@ -74,6 +74,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 8d56455..b2b066f 100644
+--- a/openstack_dashboard/dashboards/project/instances/tests.py
++++ b/openstack_dashboard/dashboards/project/instances/tests.py
+@@ -52,9 +52,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)
+@@ -64,7 +66,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(
+@@ -104,7 +107,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()
+@@ -116,6 +120,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()) \
+@@ -144,7 +149,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()
+@@ -160,6 +166,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))
+@@ -187,7 +194,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()
+@@ -206,6 +214,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(
+@@ -223,18 +232,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}
+@@ -245,13 +256,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))
+@@ -269,9 +283,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)) \
+@@ -282,7 +298,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()
+@@ -296,9 +313,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)) \
+@@ -309,7 +328,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)
+
+@@ -324,9 +344,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)) \
+@@ -337,7 +359,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()
+@@ -351,9 +374,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',
+@@ -365,7 +390,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)
+
+@@ -379,16 +405,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)
+
+@@ -402,9 +431,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())
+@@ -412,7 +443,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)
+@@ -427,9 +459,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())
+@@ -437,7 +471,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)
+
+@@ -452,9 +487,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)) \
+@@ -465,7 +502,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()
+@@ -479,9 +517,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)) \
+@@ -492,7 +532,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)
+
+@@ -507,9 +548,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',
+@@ -521,7 +564,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()
+@@ -535,9 +579,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',
+@@ -549,7 +595,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)
+@@ -564,12 +611,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']) \
+@@ -588,12 +638,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']) \
+@@ -616,11 +669,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']) \
+@@ -1668,9 +1724,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']
+
+@@ -1683,7 +1741,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(
+@@ -1709,9 +1768,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',
+@@ -1723,7 +1784,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(
+@@ -1802,17 +1864,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))
+@@ -1839,13 +1905,15 @@ class InstanceTests(test.TestCase):
+
+ 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 4826d8a..cebc5c0 100644
+--- a/openstack_dashboard/dashboards/project/instances/views.py
++++ b/openstack_dashboard/dashboards/project/instances/views.py
+@@ -66,9 +66,18 @@ 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:
+ flavors = []
+@@ -233,6 +242,15 @@ class DetailView(tabs.TabView):
+ 'instance "%s".') % instance_id,
+ redirect=redirect)
+ self._instance = instance
++
++ 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 self._instance
+
+ def get_tabs(self, request, *args, **kwargs):
+--
+cgit v0.10.1
+