components/openstack/horizon/patches/12-launchpad-1265032.patch
changeset 3998 5bd484384122
parent 3997 0ca3f3d6c919
child 4002 95b8f35fcdd5
equal deleted inserted replaced
3997:0ca3f3d6c919 3998:5bd484384122
     1 Upstream patch to fix launchpad bug 1265032.  This fix is available in
       
     2 Icehouse 2014.1 but not yet available in Havana.
       
     3 
       
     4 From f3557786e2a9f026a16d2fd3b8fa9243bc4c70a9 Mon Sep 17 00:00:00 2001
       
     5 From: Assaf Muller <[email protected]>
       
     6 Date: Sun, 29 Dec 2013 16:44:46 +0200
       
     7 Subject: Get instance networking information from Neutron
       
     8 
       
     9 project/instances, admin/instances and the instance details page
       
    10 all get networking information from Nova. However, with Neutron
       
    11 enabled, floating IP associations are done direcly with Neutron,
       
    12 meaning that Nova's DB will fall out of sync and thus the GUI
       
    13 won't reflect successful floating IP associations until Nova
       
    14 polls Neutron again and updates its DB. The polling can take
       
    15 up to several minutes to complete for consecutive floating IP
       
    16 operations.
       
    17 
       
    18 The solution is to update instances' networking information from
       
    19 Neutron immediately after the call to list Nova instances.
       
    20 
       
    21 Conflicts:
       
    22 	openstack_dashboard/dashboards/project/instances/tests.py
       
    23 	openstack_dashboard/dashboards/project/instances/views.py
       
    24 
       
    25 Closes-Bug: #1265032
       
    26 Change-Id: I0382fa9a4a9fff21e7b4d05cd3b76783f826735f
       
    27 (cherry picked from commit 715d6b822838009530b7792cd7749164ae3fc663)
       
    28 
       
    29 diff --git a/openstack_dashboard/api/network.py b/openstack_dashboard/api/network.py
       
    30 index 7ab233c..ea76d41 100644
       
    31 --- a/openstack_dashboard/api/network.py
       
    32 +++ b/openstack_dashboard/api/network.py
       
    33 @@ -132,3 +132,15 @@ def server_update_security_groups(request, instance_id,
       
    34  
       
    35  def security_group_backend(request):
       
    36      return NetworkClient(request).secgroups.backend
       
    37 +
       
    38 +
       
    39 +def servers_update_addresses(request, servers):
       
    40 +    """Retrieve servers networking information from Neutron if enabled.
       
    41 +
       
    42 +       Should be used when up to date networking information is required,
       
    43 +       and Nova's networking info caching mechanism is not fast enough.
       
    44 +
       
    45 +    """
       
    46 +    neutron_enabled = base.is_service_enabled(request, 'network')
       
    47 +    if neutron_enabled:
       
    48 +        neutron.servers_update_addresses(request, servers)
       
    49 diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py
       
    50 index 4443c9b..c09a674 100644
       
    51 --- a/openstack_dashboard/api/neutron.py
       
    52 +++ b/openstack_dashboard/api/neutron.py
       
    53 @@ -21,12 +21,15 @@
       
    54  
       
    55  from __future__ import absolute_import
       
    56  
       
    57 +import collections
       
    58  import logging
       
    59 +import netaddr
       
    60  
       
    61  from django.conf import settings  # noqa
       
    62  from django.utils.datastructures import SortedDict  # noqa
       
    63  from django.utils.translation import ugettext_lazy as _  # noqa
       
    64  
       
    65 +from horizon import messages
       
    66  from horizon.utils.memoized import memoized  # noqa
       
    67  
       
    68  from openstack_dashboard.api import base
       
    69 @@ -321,12 +324,12 @@ class FloatingIpManager(network_base.FloatingIpManager):
       
    70          return [FloatingIpPool(pool) for pool
       
    71                  in self.client.list_networks(**search_opts).get('networks')]
       
    72  
       
    73 -    def list(self):
       
    74 +    def list(self, **search_opts):
       
    75          tenant_id = self.request.user.tenant_id
       
    76          # In Neutron, list_floatingips returns Floating IPs from all tenants
       
    77          # when the API is called with admin role, so we need to filter them
       
    78          # with tenant_id.
       
    79 -        fips = self.client.list_floatingips(tenant_id=tenant_id)
       
    80 +        fips = self.client.list_floatingips(tenant_id=tenant_id, **search_opts)
       
    81          fips = fips.get('floatingips')
       
    82          # Get port list to add instance_id to floating IP list
       
    83          # instance_id is stored in device_id attribute
       
    84 @@ -727,6 +730,88 @@ def provider_list(request):
       
    85      return providers['service_providers']
       
    86  
       
    87  
       
    88 +def servers_update_addresses(request, servers):
       
    89 +    """Retrieve servers networking information from Neutron if enabled.
       
    90 +
       
    91 +       Should be used when up to date networking information is required,
       
    92 +       and Nova's networking info caching mechanism is not fast enough.
       
    93 +    """
       
    94 +
       
    95 +    # Get all (filtered for relevant servers) information from Neutron
       
    96 +    try:
       
    97 +        ports = port_list(request,
       
    98 +                          device_id=[instance.id for instance in servers])
       
    99 +        floating_ips = FloatingIpManager(request).list(
       
   100 +            port_id=[port.id for port in ports])
       
   101 +        networks = network_list(request,
       
   102 +                                id=[port.network_id for port in ports])
       
   103 +    except Exception:
       
   104 +        error_message = _('Unable to connect to Neutron.')
       
   105 +        LOG.error(error_message)
       
   106 +        messages.error(request, error_message)
       
   107 +        return
       
   108 +
       
   109 +    # Map instance to its ports
       
   110 +    instances_ports = collections.defaultdict(list)
       
   111 +    for port in ports:
       
   112 +        instances_ports[port.device_id].append(port)
       
   113 +
       
   114 +    # Map port to its floating ips
       
   115 +    ports_floating_ips = collections.defaultdict(list)
       
   116 +    for fip in floating_ips:
       
   117 +        ports_floating_ips[fip.port_id].append(fip)
       
   118 +
       
   119 +    # Map network id to its name
       
   120 +    network_names = dict(((network.id, network.name) for network in networks))
       
   121 +
       
   122 +    for server in servers:
       
   123 +        try:
       
   124 +            addresses = _server_get_addresses(
       
   125 +                request,
       
   126 +                server,
       
   127 +                instances_ports,
       
   128 +                ports_floating_ips,
       
   129 +                network_names)
       
   130 +        except Exception as e:
       
   131 +            LOG.error(e)
       
   132 +        else:
       
   133 +            server.addresses = addresses
       
   134 +
       
   135 +
       
   136 +def _server_get_addresses(request, server, ports, floating_ips, network_names):
       
   137 +    def _format_address(mac, ip, type):
       
   138 +        try:
       
   139 +            version = netaddr.IPAddress(ip).version
       
   140 +        except Exception as e:
       
   141 +            error_message = _('Unable to parse IP address %s.') % ip
       
   142 +            LOG.error(error_message)
       
   143 +            messages.error(request, error_message)
       
   144 +            raise e
       
   145 +        return {u'OS-EXT-IPS-MAC:mac_addr': mac,
       
   146 +                u'version': version,
       
   147 +                u'addr': ip,
       
   148 +                u'OS-EXT-IPS:type': type}
       
   149 +
       
   150 +    addresses = collections.defaultdict(list)
       
   151 +    instance_ports = ports.get(server.id, [])
       
   152 +    for port in instance_ports:
       
   153 +        network_name = network_names.get(port.network_id)
       
   154 +        if network_name is not None:
       
   155 +            for fixed_ip in port.fixed_ips:
       
   156 +                addresses[network_name].append(
       
   157 +                    _format_address(port.mac_address,
       
   158 +                                    fixed_ip['ip_address'],
       
   159 +                                    u'fixed'))
       
   160 +            port_fips = floating_ips.get(port.id, [])
       
   161 +            for fip in port_fips:
       
   162 +                addresses[network_name].append(
       
   163 +                    _format_address(port.mac_address,
       
   164 +                                    fip.floating_ip_address,
       
   165 +                                    u'floating'))
       
   166 +
       
   167 +    return dict(addresses)
       
   168 +
       
   169 +
       
   170  @memoized
       
   171  def list_extensions(request):
       
   172      extensions_list = neutronclient(request).list_extensions()
       
   173 diff --git a/openstack_dashboard/dashboards/admin/instances/tests.py b/openstack_dashboard/dashboards/admin/instances/tests.py
       
   174 index 6beb8fa..9236b4e 100644
       
   175 --- a/openstack_dashboard/dashboards/admin/instances/tests.py
       
   176 +++ b/openstack_dashboard/dashboards/admin/instances/tests.py
       
   177 @@ -20,6 +20,7 @@ from django.core.urlresolvers import reverse  # noqa
       
   178  from django import http
       
   179  from django.utils.datastructures import SortedDict  # noqa
       
   180  
       
   181 +from mox import IgnoreArg  # noqa
       
   182  from mox import IsA  # noqa
       
   183  
       
   184  from openstack_dashboard import api
       
   185 @@ -29,7 +30,8 @@ from openstack_dashboard.test import helpers as test
       
   186  class InstanceViewTest(test.BaseAdminViewTests):
       
   187      @test.create_stubs({api.nova: ('flavor_list', 'server_list',
       
   188                                     'extension_supported',),
       
   189 -                        api.keystone: ('tenant_list',)})
       
   190 +                        api.keystone: ('tenant_list',),
       
   191 +                        api.network: ('servers_update_addresses',)})
       
   192      def test_index(self):
       
   193          servers = self.servers.list()
       
   194          flavors = self.flavors.list()
       
   195 @@ -42,6 +44,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   196          api.nova.server_list(IsA(http.HttpRequest),
       
   197                               all_tenants=True, search_opts=search_opts) \
       
   198                                  .AndReturn([servers, False])
       
   199 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   200          api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors)
       
   201          self.mox.ReplayAll()
       
   202  
       
   203 @@ -52,7 +55,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   204  
       
   205      @test.create_stubs({api.nova: ('flavor_list', 'flavor_get',
       
   206                                      'server_list', 'extension_supported',),
       
   207 -                        api.keystone: ('tenant_list',)})
       
   208 +                        api.keystone: ('tenant_list',),
       
   209 +                        api.network: ('servers_update_addresses',)})
       
   210      def test_index_flavor_list_exception(self):
       
   211          servers = self.servers.list()
       
   212          tenants = self.tenants.list()
       
   213 @@ -63,6 +67,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   214          api.nova.server_list(IsA(http.HttpRequest),
       
   215                               all_tenants=True, search_opts=search_opts) \
       
   216                                  .AndReturn([servers, False])
       
   217 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   218          api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
       
   219              .MultipleTimes().AndReturn(True)
       
   220          api.nova.flavor_list(IsA(http.HttpRequest)). \
       
   221 @@ -82,7 +87,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   222  
       
   223      @test.create_stubs({api.nova: ('flavor_list', 'flavor_get',
       
   224                                      'server_list', 'extension_supported', ),
       
   225 -                        api.keystone: ('tenant_list',)})
       
   226 +                        api.keystone: ('tenant_list',),
       
   227 +                        api.network: ('servers_update_addresses',)})
       
   228      def test_index_flavor_get_exception(self):
       
   229          servers = self.servers.list()
       
   230          flavors = self.flavors.list()
       
   231 @@ -96,6 +102,7 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   232          api.nova.server_list(IsA(http.HttpRequest),
       
   233                               all_tenants=True, search_opts=search_opts) \
       
   234                                  .AndReturn([servers, False])
       
   235 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   236          api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
       
   237              .MultipleTimes().AndReturn(True)
       
   238          api.nova.flavor_list(IsA(http.HttpRequest)). \
       
   239 @@ -162,14 +169,17 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   240  
       
   241      @test.create_stubs({api.nova: ('flavor_list', 'server_list',
       
   242                                     'extension_supported', ),
       
   243 -                        api.keystone: ('tenant_list',)})
       
   244 +                        api.keystone: ('tenant_list',),
       
   245 +                        api.network: ('servers_update_addresses',)})
       
   246      def test_index_options_before_migrate(self):
       
   247 +        servers = self.servers.list()
       
   248          api.keystone.tenant_list(IsA(http.HttpRequest)).\
       
   249              AndReturn([self.tenants.list(), False])
       
   250          search_opts = {'marker': None, 'paginate': True}
       
   251          api.nova.server_list(IsA(http.HttpRequest),
       
   252                               all_tenants=True, search_opts=search_opts) \
       
   253 -                                .AndReturn([self.servers.list(), False])
       
   254 +                                .AndReturn([servers, False])
       
   255 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   256          api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \
       
   257              .MultipleTimes().AndReturn(True)
       
   258          api.nova.flavor_list(IsA(http.HttpRequest)).\
       
   259 @@ -183,7 +193,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   260  
       
   261      @test.create_stubs({api.nova: ('flavor_list', 'server_list',
       
   262                                     'extension_supported', ),
       
   263 -                        api.keystone: ('tenant_list',)})
       
   264 +                        api.keystone: ('tenant_list',),
       
   265 +                        api.network: ('servers_update_addresses',)})
       
   266      def test_index_options_after_migrate(self):
       
   267          servers = self.servers.list()
       
   268          server1 = servers[0]
       
   269 @@ -197,7 +208,8 @@ class InstanceViewTest(test.BaseAdminViewTests):
       
   270              .MultipleTimes().AndReturn(True)
       
   271          api.nova.server_list(IsA(http.HttpRequest),
       
   272                               all_tenants=True, search_opts=search_opts) \
       
   273 -                                .AndReturn([self.servers.list(), False])
       
   274 +                                .AndReturn([servers, False])
       
   275 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   276          api.nova.flavor_list(IsA(http.HttpRequest)).\
       
   277                               AndReturn(self.flavors.list())
       
   278          self.mox.ReplayAll()
       
   279 diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py
       
   280 index 3397f25..4bc6bee 100644
       
   281 --- a/openstack_dashboard/dashboards/admin/instances/views.py
       
   282 +++ b/openstack_dashboard/dashboards/admin/instances/views.py
       
   283 @@ -74,6 +74,14 @@ class AdminIndexView(tables.DataTableView):
       
   284              exceptions.handle(self.request,
       
   285                                _('Unable to retrieve instance list.'))
       
   286          if instances:
       
   287 +            try:
       
   288 +                api.network.servers_update_addresses(self.request, instances)
       
   289 +            except Exception:
       
   290 +                exceptions.handle(
       
   291 +                    self.request,
       
   292 +                    message=_('Unable to retrieve IP addresses from Neutron.'),
       
   293 +                    ignore=True)
       
   294 +
       
   295              # Gather our flavors to correlate against IDs
       
   296              try:
       
   297                  flavors = api.nova.flavor_list(self.request)
       
   298 diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py
       
   299 index 8d56455..b2b066f 100644
       
   300 --- a/openstack_dashboard/dashboards/project/instances/tests.py
       
   301 +++ b/openstack_dashboard/dashboards/project/instances/tests.py
       
   302 @@ -52,9 +52,11 @@ class InstanceTests(test.TestCase):
       
   303                                     'extension_supported',),
       
   304                          api.glance: ('image_list_detailed',),
       
   305                          api.network:
       
   306 -                            ('floating_ip_simple_associate_supported',),
       
   307 +                            ('floating_ip_simple_associate_supported',
       
   308 +                             'servers_update_addresses',),
       
   309                          })
       
   310      def test_index(self):
       
   311 +        servers = self.servers.list()
       
   312          api.nova.extension_supported('AdminActions',
       
   313                                       IsA(http.HttpRequest)) \
       
   314              .MultipleTimes().AndReturn(True)
       
   315 @@ -64,7 +66,8 @@ class InstanceTests(test.TestCase):
       
   316              .AndReturn((self.images.list(), False))
       
   317          search_opts = {'marker': None, 'paginate': True}
       
   318          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   319 -            .AndReturn([self.servers.list(), False])
       
   320 +            .AndReturn([servers, False])
       
   321 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   322          api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
       
   323             .MultipleTimes().AndReturn(self.limits['absolute'])
       
   324          api.network.floating_ip_simple_associate_supported(
       
   325 @@ -104,7 +107,8 @@ class InstanceTests(test.TestCase):
       
   326                                     'extension_supported',),
       
   327                          api.glance: ('image_list_detailed',),
       
   328                          api.network:
       
   329 -                            ('floating_ip_simple_associate_supported',),
       
   330 +                            ('floating_ip_simple_associate_supported',
       
   331 +                             'servers_update_addresses',),
       
   332                          })
       
   333      def test_index_flavor_list_exception(self):
       
   334          servers = self.servers.list()
       
   335 @@ -116,6 +120,7 @@ class InstanceTests(test.TestCase):
       
   336              .MultipleTimes().AndReturn(True)
       
   337          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   338              .AndReturn([servers, False])
       
   339 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   340          api.nova.flavor_list(IsA(http.HttpRequest)) \
       
   341              .AndRaise(self.exceptions.nova)
       
   342          api.glance.image_list_detailed(IgnoreArg()) \
       
   343 @@ -144,7 +149,8 @@ class InstanceTests(test.TestCase):
       
   344                                     'extension_supported',),
       
   345                          api.glance: ('image_list_detailed',),
       
   346                          api.network:
       
   347 -                            ('floating_ip_simple_associate_supported',),
       
   348 +                            ('floating_ip_simple_associate_supported',
       
   349 +                             'servers_update_addresses',),
       
   350                          })
       
   351      def test_index_flavor_get_exception(self):
       
   352          servers = self.servers.list()
       
   353 @@ -160,6 +166,7 @@ class InstanceTests(test.TestCase):
       
   354          search_opts = {'marker': None, 'paginate': True}
       
   355          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   356              .AndReturn([servers, False])
       
   357 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   358          api.nova.flavor_list(IsA(http.HttpRequest)).AndReturn(flavors)
       
   359          api.glance.image_list_detailed(IgnoreArg()) \
       
   360              .AndReturn((self.images.list(), False))
       
   361 @@ -187,7 +194,8 @@ class InstanceTests(test.TestCase):
       
   362                                     'extension_supported',),
       
   363                          api.glance: ('image_list_detailed',),
       
   364                          api.network:
       
   365 -                            ('floating_ip_simple_associate_supported',),
       
   366 +                            ('floating_ip_simple_associate_supported',
       
   367 +                             'servers_update_addresses',),
       
   368                          })
       
   369      def test_index_with_instance_booted_from_volume(self):
       
   370          volume_server = self.servers.first()
       
   371 @@ -206,6 +214,7 @@ class InstanceTests(test.TestCase):
       
   372          search_opts = {'marker': None, 'paginate': True}
       
   373          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   374              .AndReturn([servers, False])
       
   375 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   376          api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
       
   377             .MultipleTimes().AndReturn(self.limits['absolute'])
       
   378          api.network.floating_ip_simple_associate_supported(
       
   379 @@ -223,18 +232,20 @@ class InstanceTests(test.TestCase):
       
   380      @test.create_stubs({api.nova: ('server_list',
       
   381                                     'flavor_list',
       
   382                                     'server_delete',),
       
   383 -                        api.glance: ('image_list_detailed',)})
       
   384 +                        api.glance: ('image_list_detailed',),
       
   385 +                        api.network: ('servers_update_addresses',)})
       
   386      def test_terminate_instance(self):
       
   387 -        server = self.servers.first()
       
   388 +        servers = self.servers.list()
       
   389 +        server = servers[0]
       
   390  
       
   391          search_opts = {'marker': None, 'paginate': True}
       
   392          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   393 -            .AndReturn([self.servers.list(), False])
       
   394 +            .AndReturn([servers, False])
       
   395 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   396          api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
       
   397          api.glance.image_list_detailed(IgnoreArg()) \
       
   398              .AndReturn((self.images.list(), False))
       
   399          api.nova.server_delete(IsA(http.HttpRequest), server.id)
       
   400 -
       
   401          self.mox.ReplayAll()
       
   402  
       
   403          formData = {'action': 'instances__terminate__%s' % server.id}
       
   404 @@ -245,13 +256,16 @@ class InstanceTests(test.TestCase):
       
   405      @test.create_stubs({api.nova: ('server_list',
       
   406                                     'flavor_list',
       
   407                                     'server_delete',),
       
   408 -                        api.glance: ('image_list_detailed',)})
       
   409 +                        api.glance: ('image_list_detailed',),
       
   410 +                        api.network: ('servers_update_addresses',)})
       
   411      def test_terminate_instance_exception(self):
       
   412 -        server = self.servers.first()
       
   413 +        servers = self.servers.list()
       
   414 +        server = servers[0]
       
   415  
       
   416          search_opts = {'marker': None, 'paginate': True}
       
   417          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   418 -            .AndReturn([self.servers.list(), False])
       
   419 +            .AndReturn([servers, False])
       
   420 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   421          api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
       
   422          api.glance.image_list_detailed(IgnoreArg()) \
       
   423              .AndReturn((self.images.list(), False))
       
   424 @@ -269,9 +283,11 @@ class InstanceTests(test.TestCase):
       
   425                                     'server_list',
       
   426                                     'flavor_list',
       
   427                                     'extension_supported',),
       
   428 -                        api.glance: ('image_list_detailed',)})
       
   429 +                        api.glance: ('image_list_detailed',),
       
   430 +                        api.network: ('servers_update_addresses',)})
       
   431      def test_pause_instance(self):
       
   432 -        server = self.servers.first()
       
   433 +        servers = self.servers.list()
       
   434 +        server = servers[0]
       
   435  
       
   436          api.nova.extension_supported('AdminActions',
       
   437                                       IsA(http.HttpRequest)) \
       
   438 @@ -282,7 +298,8 @@ class InstanceTests(test.TestCase):
       
   439              .AndReturn((self.images.list(), False))
       
   440          search_opts = {'marker': None, 'paginate': True}
       
   441          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   442 -            .AndReturn([self.servers.list(), False])
       
   443 +            .AndReturn([servers, False])
       
   444 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   445          api.nova.server_pause(IsA(http.HttpRequest), server.id)
       
   446  
       
   447          self.mox.ReplayAll()
       
   448 @@ -296,9 +313,11 @@ class InstanceTests(test.TestCase):
       
   449                                     'server_list',
       
   450                                     'flavor_list',
       
   451                                     'extension_supported',),
       
   452 -                        api.glance: ('image_list_detailed',)})
       
   453 +                        api.glance: ('image_list_detailed',),
       
   454 +                        api.network: ('servers_update_addresses',)})
       
   455      def test_pause_instance_exception(self):
       
   456 -        server = self.servers.first()
       
   457 +        servers = self.servers.list()
       
   458 +        server = servers[0]
       
   459  
       
   460          api.nova.extension_supported('AdminActions',
       
   461                                       IsA(http.HttpRequest)) \
       
   462 @@ -309,7 +328,8 @@ class InstanceTests(test.TestCase):
       
   463              .AndReturn((self.images.list(), False))
       
   464          search_opts = {'marker': None, 'paginate': True}
       
   465          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   466 -            .AndReturn([self.servers.list(), False])
       
   467 +            .AndReturn([servers, False])
       
   468 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   469          api.nova.server_pause(IsA(http.HttpRequest), server.id) \
       
   470                          .AndRaise(self.exceptions.nova)
       
   471  
       
   472 @@ -324,9 +344,11 @@ class InstanceTests(test.TestCase):
       
   473                                     'server_list',
       
   474                                     'flavor_list',
       
   475                                     'extension_supported',),
       
   476 -                        api.glance: ('image_list_detailed',)})
       
   477 +                        api.glance: ('image_list_detailed',),
       
   478 +                        api.network: ('servers_update_addresses',)})
       
   479      def test_unpause_instance(self):
       
   480 -        server = self.servers.first()
       
   481 +        servers = self.servers.list()
       
   482 +        server = servers[0]
       
   483          server.status = "PAUSED"
       
   484          api.nova.extension_supported('AdminActions',
       
   485                                       IsA(http.HttpRequest)) \
       
   486 @@ -337,7 +359,8 @@ class InstanceTests(test.TestCase):
       
   487              .AndReturn((self.images.list(), False))
       
   488          search_opts = {'marker': None, 'paginate': True}
       
   489          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   490 -            .AndReturn([self.servers.list(), False])
       
   491 +            .AndReturn([servers, False])
       
   492 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   493          api.nova.server_unpause(IsA(http.HttpRequest), server.id)
       
   494  
       
   495          self.mox.ReplayAll()
       
   496 @@ -351,9 +374,11 @@ class InstanceTests(test.TestCase):
       
   497                                     'server_list',
       
   498                                     'flavor_list',
       
   499                                     'extension_supported',),
       
   500 -                        api.glance: ('image_list_detailed',)})
       
   501 +                        api.glance: ('image_list_detailed',),
       
   502 +                        api.network: ('servers_update_addresses',)})
       
   503      def test_unpause_instance_exception(self):
       
   504 -        server = self.servers.first()
       
   505 +        servers = self.servers.list()
       
   506 +        server = servers[0]
       
   507          server.status = "PAUSED"
       
   508  
       
   509          api.nova.extension_supported('AdminActions',
       
   510 @@ -365,7 +390,8 @@ class InstanceTests(test.TestCase):
       
   511              .AndReturn((self.images.list(), False))
       
   512          search_opts = {'marker': None, 'paginate': True}
       
   513          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   514 -            .AndReturn([self.servers.list(), False])
       
   515 +            .AndReturn([servers, False])
       
   516 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   517          api.nova.server_unpause(IsA(http.HttpRequest), server.id) \
       
   518                            .AndRaise(self.exceptions.nova)
       
   519  
       
   520 @@ -379,16 +405,19 @@ class InstanceTests(test.TestCase):
       
   521      @test.create_stubs({api.nova: ('server_reboot',
       
   522                                     'server_list',
       
   523                                     'flavor_list',),
       
   524 -                        api.glance: ('image_list_detailed',)})
       
   525 +                        api.glance: ('image_list_detailed',),
       
   526 +                        api.network: ('servers_update_addresses',)})
       
   527      def test_reboot_instance(self):
       
   528 -        server = self.servers.first()
       
   529 +        servers = self.servers.list()
       
   530 +        server = servers[0]
       
   531          api.nova.flavor_list(IsA(http.HttpRequest)) \
       
   532              .AndReturn(self.flavors.list())
       
   533          api.glance.image_list_detailed(IgnoreArg()) \
       
   534              .AndReturn((self.images.list(), False))
       
   535          search_opts = {'marker': None, 'paginate': True}
       
   536          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   537 -            .AndReturn([self.servers.list(), False])
       
   538 +            .AndReturn([servers, False])
       
   539 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   540          api.nova.server_reboot(IsA(http.HttpRequest), server.id,
       
   541                                 soft_reboot=False)
       
   542  
       
   543 @@ -402,9 +431,11 @@ class InstanceTests(test.TestCase):
       
   544      @test.create_stubs({api.nova: ('server_reboot',
       
   545                                     'server_list',
       
   546                                     'flavor_list',),
       
   547 -                        api.glance: ('image_list_detailed',)})
       
   548 +                        api.glance: ('image_list_detailed',),
       
   549 +                        api.network: ('servers_update_addresses',)})
       
   550      def test_reboot_instance_exception(self):
       
   551 -        server = self.servers.first()
       
   552 +        servers = self.servers.list()
       
   553 +        server = servers[0]
       
   554  
       
   555          api.nova.flavor_list(IsA(http.HttpRequest)) \
       
   556              .AndReturn(self.flavors.list())
       
   557 @@ -412,7 +443,8 @@ class InstanceTests(test.TestCase):
       
   558              .AndReturn((self.images.list(), False))
       
   559          search_opts = {'marker': None, 'paginate': True}
       
   560          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   561 -            .AndReturn([self.servers.list(), False])
       
   562 +            .AndReturn([servers, False])
       
   563 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   564          api.nova.server_reboot(IsA(http.HttpRequest), server.id,
       
   565                                 soft_reboot=False) \
       
   566              .AndRaise(self.exceptions.nova)
       
   567 @@ -427,9 +459,11 @@ class InstanceTests(test.TestCase):
       
   568      @test.create_stubs({api.nova: ('server_reboot',
       
   569                                     'server_list',
       
   570                                     'flavor_list',),
       
   571 -                        api.glance: ('image_list_detailed',)})
       
   572 +                        api.glance: ('image_list_detailed',),
       
   573 +                        api.network: ('servers_update_addresses',)})
       
   574      def test_soft_reboot_instance(self):
       
   575 -        server = self.servers.first()
       
   576 +        servers = self.servers.list()
       
   577 +        server = servers[0]
       
   578  
       
   579          api.nova.flavor_list(IsA(http.HttpRequest)) \
       
   580              .AndReturn(self.flavors.list())
       
   581 @@ -437,7 +471,8 @@ class InstanceTests(test.TestCase):
       
   582              .AndReturn((self.images.list(), False))
       
   583          search_opts = {'marker': None, 'paginate': True}
       
   584          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   585 -            .AndReturn([self.servers.list(), False])
       
   586 +            .AndReturn([servers, False])
       
   587 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   588          api.nova.server_reboot(IsA(http.HttpRequest), server.id,
       
   589                                 soft_reboot=True)
       
   590  
       
   591 @@ -452,9 +487,11 @@ class InstanceTests(test.TestCase):
       
   592                                     'server_list',
       
   593                                     'flavor_list',
       
   594                                     'extension_supported',),
       
   595 -                        api.glance: ('image_list_detailed',)})
       
   596 +                        api.glance: ('image_list_detailed',),
       
   597 +                        api.network: ('servers_update_addresses',)})
       
   598      def test_suspend_instance(self):
       
   599 -        server = self.servers.first()
       
   600 +        servers = self.servers.list()
       
   601 +        server = servers[0]
       
   602  
       
   603          api.nova.extension_supported('AdminActions',
       
   604                                       IsA(http.HttpRequest)) \
       
   605 @@ -465,7 +502,8 @@ class InstanceTests(test.TestCase):
       
   606              .AndReturn((self.images.list(), False))
       
   607          search_opts = {'marker': None, 'paginate': True}
       
   608          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   609 -            .AndReturn([self.servers.list(), False])
       
   610 +            .AndReturn([servers, False])
       
   611 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   612          api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id))
       
   613  
       
   614          self.mox.ReplayAll()
       
   615 @@ -479,9 +517,11 @@ class InstanceTests(test.TestCase):
       
   616                                     'server_list',
       
   617                                     'flavor_list',
       
   618                                     'extension_supported',),
       
   619 -                        api.glance: ('image_list_detailed',)})
       
   620 +                        api.glance: ('image_list_detailed',),
       
   621 +                        api.network: ('servers_update_addresses',)})
       
   622      def test_suspend_instance_exception(self):
       
   623 -        server = self.servers.first()
       
   624 +        servers = self.servers.list()
       
   625 +        server = servers[0]
       
   626  
       
   627          api.nova.extension_supported('AdminActions',
       
   628                                       IsA(http.HttpRequest)) \
       
   629 @@ -492,7 +532,8 @@ class InstanceTests(test.TestCase):
       
   630              .AndReturn((self.images.list(), False))
       
   631          search_opts = {'marker': None, 'paginate': True}
       
   632          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   633 -            .AndReturn([self.servers.list(), False])
       
   634 +            .AndReturn([servers, False])
       
   635 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   636          api.nova.server_suspend(IsA(http.HttpRequest), unicode(server.id)) \
       
   637              .AndRaise(self.exceptions.nova)
       
   638  
       
   639 @@ -507,9 +548,11 @@ class InstanceTests(test.TestCase):
       
   640                                     'server_list',
       
   641                                     'flavor_list',
       
   642                                     'extension_supported',),
       
   643 -                        api.glance: ('image_list_detailed',)})
       
   644 +                        api.glance: ('image_list_detailed',),
       
   645 +                        api.network: ('servers_update_addresses',)})
       
   646      def test_resume_instance(self):
       
   647 -        server = self.servers.first()
       
   648 +        servers = self.servers.list()
       
   649 +        server = servers[0]
       
   650          server.status = "SUSPENDED"
       
   651  
       
   652          api.nova.extension_supported('AdminActions',
       
   653 @@ -521,7 +564,8 @@ class InstanceTests(test.TestCase):
       
   654              .AndReturn((self.images.list(), False))
       
   655          search_opts = {'marker': None, 'paginate': True}
       
   656          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   657 -            .AndReturn([self.servers.list(), False])
       
   658 +            .AndReturn([servers, False])
       
   659 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   660          api.nova.server_resume(IsA(http.HttpRequest), unicode(server.id))
       
   661  
       
   662          self.mox.ReplayAll()
       
   663 @@ -535,9 +579,11 @@ class InstanceTests(test.TestCase):
       
   664                                     'server_list',
       
   665                                     'flavor_list',
       
   666                                     'extension_supported',),
       
   667 -                        api.glance: ('image_list_detailed',)})
       
   668 +                        api.glance: ('image_list_detailed',),
       
   669 +                        api.network: ('servers_update_addresses',)})
       
   670      def test_resume_instance_exception(self):
       
   671 -        server = self.servers.first()
       
   672 +        servers = self.servers.list()
       
   673 +        server = servers[0]
       
   674          server.status = "SUSPENDED"
       
   675  
       
   676          api.nova.extension_supported('AdminActions',
       
   677 @@ -549,7 +595,8 @@ class InstanceTests(test.TestCase):
       
   678              .AndReturn((self.images.list(), False))
       
   679          search_opts = {'marker': None, 'paginate': True}
       
   680          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   681 -            .AndReturn([self.servers.list(), False])
       
   682 +            .AndReturn([servers, False])
       
   683 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   684          api.nova.server_resume(IsA(http.HttpRequest),
       
   685                                 unicode(server.id)) \
       
   686              .AndRaise(self.exceptions.nova)
       
   687 @@ -564,12 +611,15 @@ class InstanceTests(test.TestCase):
       
   688      @test.create_stubs({api.nova: ("server_get",
       
   689                                     "instance_volumes_list",
       
   690                                     "flavor_get"),
       
   691 -                        api.network: ("server_security_groups",)})
       
   692 +                        api.network: ("server_security_groups",
       
   693 +                                      "servers_update_addresses")})
       
   694      def test_instance_details_volumes(self):
       
   695          server = self.servers.first()
       
   696          volumes = [self.volumes.list()[1]]
       
   697  
       
   698          api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
       
   699 +        api.network.servers_update_addresses(IsA(http.HttpRequest),
       
   700 +                                             IgnoreArg())
       
   701          api.nova.instance_volumes_list(IsA(http.HttpRequest),
       
   702                                         server.id).AndReturn(volumes)
       
   703          api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \
       
   704 @@ -588,12 +638,15 @@ class InstanceTests(test.TestCase):
       
   705      @test.create_stubs({api.nova: ("server_get",
       
   706                                     "instance_volumes_list",
       
   707                                     "flavor_get"),
       
   708 -                        api.network: ("server_security_groups",)})
       
   709 +                        api.network: ("server_security_groups",
       
   710 +                                      "servers_update_addresses")})
       
   711      def test_instance_details_volume_sorting(self):
       
   712          server = self.servers.first()
       
   713          volumes = self.volumes.list()[1:3]
       
   714  
       
   715          api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
       
   716 +        api.network.servers_update_addresses(IsA(http.HttpRequest),
       
   717 +                                             IgnoreArg())
       
   718          api.nova.instance_volumes_list(IsA(http.HttpRequest),
       
   719                                         server.id).AndReturn(volumes)
       
   720          api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \
       
   721 @@ -616,11 +669,14 @@ class InstanceTests(test.TestCase):
       
   722      @test.create_stubs({api.nova: ("server_get",
       
   723                                     "instance_volumes_list",
       
   724                                     "flavor_get"),
       
   725 -                        api.network: ("server_security_groups",)})
       
   726 +                        api.network: ("server_security_groups",
       
   727 +                                      "servers_update_addresses")})
       
   728      def test_instance_details_metadata(self):
       
   729          server = self.servers.first()
       
   730  
       
   731          api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server)
       
   732 +        api.network.servers_update_addresses(IsA(http.HttpRequest),
       
   733 +                                             IgnoreArg())
       
   734          api.nova.instance_volumes_list(IsA(http.HttpRequest),
       
   735                                         server.id).AndReturn([])
       
   736          api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \
       
   737 @@ -1668,9 +1724,11 @@ class InstanceTests(test.TestCase):
       
   738                                     'extension_supported',),
       
   739                          api.glance: ('image_list_detailed',),
       
   740                          api.network:
       
   741 -                            ('floating_ip_simple_associate_supported',),
       
   742 +                            ('floating_ip_simple_associate_supported',
       
   743 +                             'servers_update_addresses',),
       
   744                          })
       
   745      def test_launch_button_disabled_when_quota_exceeded(self):
       
   746 +        servers = self.servers.list()
       
   747          limits = self.limits['absolute']
       
   748          limits['totalInstancesUsed'] = limits['maxTotalInstances']
       
   749  
       
   750 @@ -1683,7 +1741,8 @@ class InstanceTests(test.TestCase):
       
   751              .AndReturn((self.images.list(), False))
       
   752          search_opts = {'marker': None, 'paginate': True}
       
   753          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   754 -            .AndReturn([self.servers.list(), False])
       
   755 +            .AndReturn([servers, False])
       
   756 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   757          api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
       
   758              .MultipleTimes().AndReturn(limits)
       
   759          api.network.floating_ip_simple_associate_supported(
       
   760 @@ -1709,9 +1768,11 @@ class InstanceTests(test.TestCase):
       
   761                                     'extension_supported',),
       
   762                          api.glance: ('image_list_detailed',),
       
   763                          api.network:
       
   764 -                            ('floating_ip_simple_associate_supported',),
       
   765 +                            ('floating_ip_simple_associate_supported',
       
   766 +                             'servers_update_addresses',),
       
   767                          })
       
   768      def test_index_options_after_migrate(self):
       
   769 +        servers = self.servers.list()
       
   770          server = self.servers.first()
       
   771          server.status = "VERIFY_RESIZE"
       
   772          api.nova.extension_supported('AdminActions',
       
   773 @@ -1723,7 +1784,8 @@ class InstanceTests(test.TestCase):
       
   774              .AndReturn((self.images.list(), False))
       
   775          search_opts = {'marker': None, 'paginate': True}
       
   776          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   777 -            .AndReturn([self.servers.list(), False])
       
   778 +            .AndReturn([servers, False])
       
   779 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   780          api.nova.tenant_absolute_limits(IsA(http.HttpRequest), reserved=True) \
       
   781             .MultipleTimes().AndReturn(self.limits['absolute'])
       
   782          api.network.floating_ip_simple_associate_supported(
       
   783 @@ -1802,17 +1864,20 @@ class InstanceTests(test.TestCase):
       
   784  
       
   785      @test.create_stubs({api.network: ('floating_ip_target_get_by_instance',
       
   786                                        'tenant_floating_ip_allocate',
       
   787 -                                      'floating_ip_associate'),
       
   788 +                                      'floating_ip_associate',
       
   789 +                                      'servers_update_addresses',),
       
   790                          api.glance: ('image_list_detailed',),
       
   791                          api.nova: ('server_list',
       
   792                                     'flavor_list')})
       
   793      def test_associate_floating_ip(self):
       
   794 -        server = self.servers.first()
       
   795 +        servers = self.servers.list()
       
   796 +        server = servers[0]
       
   797          fip = self.q_floating_ips.first()
       
   798  
       
   799          search_opts = {'marker': None, 'paginate': True}
       
   800          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   801 -            .AndReturn([self.servers.list(), False])
       
   802 +            .AndReturn([servers, False])
       
   803 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   804          api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
       
   805          api.glance.image_list_detailed(IgnoreArg()) \
       
   806              .AndReturn((self.images.list(), False))
       
   807 @@ -1839,13 +1905,15 @@ class InstanceTests(test.TestCase):
       
   808  
       
   809                          api.nova: ('server_list',
       
   810                                     'flavor_list')})
       
   811      def test_disassociate_floating_ip(self):
       
   812 -        server = self.servers.first()
       
   813 +        servers = self.servers.list()
       
   814 +        server = servers[0]
       
   815          fip = self.q_floating_ips.first()
       
   816          fip.port_id = server.id
       
   817  
       
   818          search_opts = {'marker': None, 'paginate': True}
       
   819          api.nova.server_list(IsA(http.HttpRequest), search_opts=search_opts) \
       
   820 -            .AndReturn([self.servers.list(), False])
       
   821 +            .AndReturn([servers, False])
       
   822 +        api.network.servers_update_addresses(IsA(http.HttpRequest), servers)
       
   823          api.nova.flavor_list(IgnoreArg()).AndReturn(self.flavors.list())
       
   824          api.glance.image_list_detailed(IgnoreArg()) \
       
   825              .AndReturn((self.images.list(), False))
       
   826 diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py
       
   827 index 4826d8a..cebc5c0 100644
       
   828 --- a/openstack_dashboard/dashboards/project/instances/views.py
       
   829 +++ b/openstack_dashboard/dashboards/project/instances/views.py
       
   830 @@ -66,9 +66,18 @@ class IndexView(tables.DataTableView):
       
   831              instances = []
       
   832              exceptions.handle(self.request,
       
   833                                _('Unable to retrieve instances.'))
       
   834 -        # Gather our flavors and images and correlate our instances to them
       
   835 +
       
   836          if instances:
       
   837              try:
       
   838 +                api.network.servers_update_addresses(self.request, instances)
       
   839 +            except Exception:
       
   840 +                exceptions.handle(
       
   841 +                    self.request,
       
   842 +                    message=_('Unable to retrieve IP addresses from Neutron.'),
       
   843 +                    ignore=True)
       
   844 +
       
   845 +            # Gather our flavors and images and correlate our instances to them
       
   846 +            try:
       
   847                  flavors = api.nova.flavor_list(self.request)
       
   848              except Exception:
       
   849                  flavors = []
       
   850 @@ -233,6 +242,15 @@ class DetailView(tabs.TabView):
       
   851                                      'instance "%s".') % instance_id,
       
   852                                      redirect=redirect)
       
   853              self._instance = instance
       
   854 +
       
   855 +            try:
       
   856 +                api.network.servers_update_addresses(self.request, [instance])
       
   857 +            except Exception:
       
   858 +                exceptions.handle(
       
   859 +                    self.request,
       
   860 +                    _('Unable to retrieve IP addresses from Neutron for '
       
   861 +                      'instance "%s".') % instance_id, ignore=True)
       
   862 +
       
   863          return self._instance
       
   864  
       
   865      def get_tabs(self, request, *args, **kwargs):
       
   866 -- 
       
   867 cgit v0.10.1
       
   868