diff --git a/nova/network/neutronv2/__init__.py b/nova/network/neutronv2/__init__.py index 41acb84f4c3..0308f451825 100644 --- a/nova/network/neutronv2/__init__.py +++ b/nova/network/neutronv2/__init__.py @@ -19,6 +19,7 @@ from neutronclient.v2_0 import client as clientv20 from oslo.config import cfg +from nova.openstack.common import local from nova.openstack.common import log as logging CONF = cfg.CONF @@ -46,11 +47,23 @@ def _get_client(token=None): def get_client(context, admin=False): + # NOTE(dims): We need to use admin token, let us cache a + # thread local copy for re-using this client + # multiple times and to avoid excessive calls + # to neutron to fetch tokens. Some of the hackiness in this code + # will go away once BP auth-plugins is implemented. + # That blue print will ensure that tokens can be shared + # across clients as well if admin or context.is_admin: - token = None - elif not context.auth_token: - raise exceptions.Unauthorized() - else: + if not hasattr(local.strong_store, 'neutron_client'): + local.strong_store.neutron_client = _get_client(token=None) + return local.strong_store.neutron_client + + # We got a user token that we can use that as-is + if context.auth_token: token = context.auth_token + return _get_client(token=token) - return _get_client(token=token) + # We did not get a user token and we should not be using + # an admin token so log an error + raise exceptions.Unauthorized() diff --git a/nova/tests/network/test_neutronv2.py b/nova/tests/network/test_neutronv2.py index f345d514079..4f64ac21c3f 100644 --- a/nova/tests/network/test_neutronv2.py +++ b/nova/tests/network/test_neutronv2.py @@ -31,6 +31,7 @@ from nova.network.neutronv2 import api as neutronapi from nova.network.neutronv2 import constants from nova.openstack.common import jsonutils +from nova.openstack.common import local from nova import test from nova import utils @@ -1814,3 +1815,49 @@ def test_allocate_for_instance_extradhcpopts(self): self._allocate_for_instance(1, dhcp_options=dhcp_opts) CONF.set_override('dhcp_options_enabled', False) + + +class TestNeutronClientForAdminScenarios(test.TestCase): + def test_get_cached_neutron_client_for_admin(self): + self.flags(neutron_url='http://anyhost/') + self.flags(neutron_url_timeout=30) + my_context = context.RequestContext('userid', + 'my_tenantid', + auth_token='token') + + # Make multiple calls and ensure we get the same + # client back again and again + client = neutronv2.get_client(my_context, True) + client2 = neutronv2.get_client(my_context, True) + client3 = neutronv2.get_client(my_context, True) + self.assertEqual(client, client2) + self.assertEqual(client, client3) + + # clear the cache + local.strong_store.neutron_client = None + + # A new client should be created now + client4 = neutronv2.get_client(my_context, True) + self.assertNotEqual(client, client4) + + def test_get_neutron_client_for_non_admin(self): + self.flags(neutron_url='http://anyhost/') + self.flags(neutron_url_timeout=30) + my_context = context.RequestContext('userid', + 'my_tenantid', + auth_token='token') + + # Multiple calls should return different clients + client = neutronv2.get_client(my_context) + client2 = neutronv2.get_client(my_context) + self.assertNotEqual(client, client2) + + def test_get_neutron_client_for_non_admin_and_no_token(self): + self.flags(neutron_url='http://anyhost/') + self.flags(neutron_url_timeout=30) + my_context = context.RequestContext('userid', + 'my_tenantid') + + self.assertRaises(exceptions.Unauthorized, + neutronv2.get_client, + my_context)