From 85332012dede96fa6729026c2a90594ea0502ac5 Mon Sep 17 00:00:00 2001 From: Davanum Srinivas Date: Tue, 12 Nov 2013 13:25:42 -0500 Subject: [PATCH] Cache Neutron Client for Admin Scenarios Store a thread local copy of Neutron client for use with admin tokens, this will prevent excessive calls to keystone to mint new tokens and will enable reuse of the already set up client for subsequent calls in this thread. Change-Id: I6346281ca3516c31b195ce71942786d724e5e9df Closes-Bug: #1250580 --- nova/network/neutronv2/__init__.py | 23 +++++++++++--- nova/tests/network/test_neutronv2.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) 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 8df685b1212..ffc7c0226f0 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 @@ -1760,3 +1761,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)