# HG changeset patch # User Drew Fisher # Date 1408566139 25200 # Node ID 2d5dd38085ab642d595087129b9f06a94cddf2b5 # Parent c29135b8edbf6e43c22dc7f9cd88e8abbcd1c445 19277967 long delay before floating ip assignment displayed by "nova show" or horizon 19468547 problem in SERVICE/HORIZON diff -r c29135b8edbf -r 2d5dd38085ab components/openstack/horizon/patches/12-launchpad-1265032.patch --- /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 +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 + diff -r c29135b8edbf -r 2d5dd38085ab components/openstack/horizon/patches/13-CVE-2014-3594.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/openstack/horizon/patches/13-CVE-2014-3594.patch Wed Aug 20 13:22:19 2014 -0700 @@ -0,0 +1,49 @@ +Upstream patch to fix CVE-2014-3594. This will be fixed in future +2013.2.4 and 2014.1.3 releases. + +From 96c6cdaa084857b82b9681378d5d3a6a4732015e Mon Sep 17 00:00:00 2001 +From: Julie Pichon +Date: Thu, 7 Aug 2014 12:01:56 +0100 +Subject: [PATCH] Fix XSS issue with the unordered_list filter + +When using the unordered_list filter in a Horizon table (as opposed to +a template directly), autoescaping is not set by default and the input +wasn't sanitised. + +Closes-Bug: #1349491 +Change-Id: Id82eefe48ccb17a158751ec65d24f3ac779380ec +--- + .../dashboards/admin/info/tables.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/openstack_dashboard/dashboards/admin/info/tables.py b/openstack_dashboard/dashboards/admin/info/tables.py +index d4f3fe7..a123fab 100644 +--- a/openstack_dashboard/dashboards/admin/info/tables.py ++++ b/openstack_dashboard/dashboards/admin/info/tables.py +@@ -127,6 +127,10 @@ def get_metadata(aggregate): + in aggregate.metadata.iteritems()] + + ++def safe_unordered_list(value): ++ return filters.unordered_list(value, autoescape=True) ++ ++ + class AggregatesTable(tables.DataTable): + name = tables.Column("name", + verbose_name=_("Name")) +@@ -135,11 +139,11 @@ class AggregatesTable(tables.DataTable): + hosts = tables.Column(get_hosts, + verbose_name=_("Hosts"), + wrap_list=True, +- filters=(filters.unordered_list,)) ++ filters=(safe_unordered_list,)) + metadata = tables.Column(get_metadata, + verbose_name=_("Metadata"), + wrap_list=True, +- filters=(filters.unordered_list,)) ++ filters=(safe_unordered_list,)) + + class Meta: + name = "aggregates" +-- +1.7.9.5