Merge "Pass thru credentials to allow re-authentication" into stable/havana

This commit is contained in:
Jenkins 2014-03-28 04:39:13 +00:00 committed by Gerrit Code Review
commit c082ec7595
3 changed files with 182 additions and 46 deletions

View File

@ -15,57 +15,60 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutronclient import client
from neutronclient.common import exceptions from neutronclient.common import exceptions
from neutronclient.v2_0 import client as clientv20 from neutronclient.v2_0 import client as clientv20
from oslo.config import cfg from oslo.config import cfg
from nova.openstack.common import excutils from nova.openstack.common import local
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging from nova.openstack.common import log as logging
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def _get_auth_token():
try:
httpclient = client.HTTPClient(
username=CONF.neutron_admin_username,
tenant_name=CONF.neutron_admin_tenant_name,
region_name=CONF.neutron_region_name,
password=CONF.neutron_admin_password,
auth_url=CONF.neutron_admin_auth_url,
timeout=CONF.neutron_url_timeout,
auth_strategy=CONF.neutron_auth_strategy,
ca_cert=CONF.neutron_ca_certificates_file,
insecure=CONF.neutron_api_insecure)
httpclient.authenticate()
return httpclient.auth_token
except exceptions.NeutronClientException as e:
with excutils.save_and_reraise_exception():
LOG.error(_('Neutron client authentication failed: %s'), e)
def _get_client(token=None): def _get_client(token=None):
if not token and CONF.neutron_auth_strategy:
token = _get_auth_token()
params = { params = {
'endpoint_url': CONF.neutron_url, 'endpoint_url': CONF.neutron_url,
'timeout': CONF.neutron_url_timeout, 'timeout': CONF.neutron_url_timeout,
'insecure': CONF.neutron_api_insecure, 'insecure': CONF.neutron_api_insecure,
'ca_cert': CONF.neutron_ca_certificates_file, 'ca_cert': CONF.neutron_ca_certificates_file,
} }
if token: if token:
params['token'] = token params['token'] = token
else:
params['auth_strategy'] = None params['auth_strategy'] = None
else:
params['username'] = CONF.neutron_admin_username
params['tenant_name'] = CONF.neutron_admin_tenant_name
params['password'] = CONF.neutron_admin_password
params['auth_url'] = CONF.neutron_admin_auth_url
params['auth_strategy'] = CONF.neutron_auth_strategy
return clientv20.Client(**params) return clientv20.Client(**params)
def get_client(context, admin=False): def get_client(context, admin=False):
if admin: # NOTE(dprince): In the case where no auth_token is present
token = None # we allow use of neutron admin tenant credentials if
else: # it is an admin context.
# This is to support some services (metadata API) where
# an admin context is used without an auth token.
if admin or (context.is_admin and not context.auth_token):
# 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 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 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()

View File

@ -372,7 +372,8 @@ class API(base.Base):
if (not self.last_neutron_extension_sync or if (not self.last_neutron_extension_sync or
((time.time() - self.last_neutron_extension_sync) ((time.time() - self.last_neutron_extension_sync)
>= CONF.neutron_extension_sync_interval)): >= CONF.neutron_extension_sync_interval)):
neutron = neutronv2.get_client(context.get_admin_context()) neutron = neutronv2.get_client(context.get_admin_context(),
admin=True)
extensions_list = neutron.list_extensions()['extensions'] extensions_list = neutron.list_extensions()['extensions']
self.last_neutron_extension_sync = time.time() self.last_neutron_extension_sync = time.time()
self.extensions.clear() self.extensions.clear()

View File

@ -33,6 +33,7 @@ from nova.network import neutronv2
from nova.network.neutronv2 import api as neutronapi from nova.network.neutronv2 import api as neutronapi
from nova.network.neutronv2 import constants from nova.network.neutronv2 import constants
from nova.openstack.common import jsonutils from nova.openstack.common import jsonutils
from nova.openstack.common import local
from nova import test from nova import test
from nova import utils from nova import utils
@ -101,6 +102,7 @@ class TestNeutronClient(test.TestCase):
auth_token='token') auth_token='token')
self.mox.StubOutWithMock(client.Client, "__init__") self.mox.StubOutWithMock(client.Client, "__init__")
client.Client.__init__( client.Client.__init__(
auth_strategy=None,
endpoint_url=CONF.neutron_url, endpoint_url=CONF.neutron_url,
token=my_context.auth_token, token=my_context.auth_token,
timeout=CONF.neutron_url_timeout, timeout=CONF.neutron_url_timeout,
@ -109,6 +111,33 @@ class TestNeutronClient(test.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
neutronv2.get_client(my_context) neutronv2.get_client(my_context)
def test_withouttoken(self):
my_context = context.RequestContext('userid', 'my_tenantid')
self.assertRaises(exceptions.Unauthorized,
neutronv2.get_client,
my_context)
def test_withtoken_context_is_admin(self):
self.flags(neutron_url='http://anyhost/')
self.flags(neutron_url_timeout=30)
my_context = context.RequestContext('userid',
'my_tenantid',
auth_token='token',
is_admin=True)
self.mox.StubOutWithMock(client.Client, "__init__")
client.Client.__init__(
auth_strategy=None,
endpoint_url=CONF.neutron_url,
token=my_context.auth_token,
timeout=CONF.neutron_url_timeout,
insecure=False,
ca_cert=None).AndReturn(None)
self.mox.ReplayAll()
# Note that although we have admin set in the context we
# are not asking for an admin client, and so we auth with
# our own token
neutronv2.get_client(my_context)
def test_withouttoken_keystone_connection_error(self): def test_withouttoken_keystone_connection_error(self):
self.flags(neutron_auth_strategy='keystone') self.flags(neutron_auth_strategy='keystone')
self.flags(neutron_url='http://anyhost/') self.flags(neutron_url='http://anyhost/')
@ -117,21 +146,6 @@ class TestNeutronClient(test.TestCase):
neutronv2.get_client, neutronv2.get_client,
my_context) my_context)
def test_withouttoken_keystone_not_auth(self):
self.flags(neutron_auth_strategy=None)
self.flags(neutron_url='http://anyhost/')
self.flags(neutron_url_timeout=30)
my_context = context.RequestContext('userid', 'my_tenantid')
self.mox.StubOutWithMock(client.Client, "__init__")
client.Client.__init__(
endpoint_url=CONF.neutron_url,
auth_strategy=None,
timeout=CONF.neutron_url_timeout,
insecure=False,
ca_cert=None).AndReturn(None)
self.mox.ReplayAll()
neutronv2.get_client(my_context)
class TestNeutronv2Base(test.TestCase): class TestNeutronv2Base(test.TestCase):
@ -596,6 +610,12 @@ class TestNeutronv2(TestNeutronv2Base):
def test_refresh_neutron_extensions_cache(self): def test_refresh_neutron_extensions_cache(self):
api = neutronapi.API() api = neutronapi.API()
# Note: Don't want the default get_client from setUp()
self.mox.ResetAll()
neutronv2.get_client(mox.IgnoreArg(),
admin=True).AndReturn(
self.moxed_client)
self.moxed_client.list_extensions().AndReturn( self.moxed_client.list_extensions().AndReturn(
{'extensions': [{'name': 'nvp-qos'}]}) {'extensions': [{'name': 'nvp-qos'}]})
self.mox.ReplayAll() self.mox.ReplayAll()
@ -604,6 +624,12 @@ class TestNeutronv2(TestNeutronv2Base):
def test_populate_neutron_extension_values_rxtx_factor(self): def test_populate_neutron_extension_values_rxtx_factor(self):
api = neutronapi.API() api = neutronapi.API()
# Note: Don't want the default get_client from setUp()
self.mox.ResetAll()
neutronv2.get_client(mox.IgnoreArg(),
admin=True).AndReturn(
self.moxed_client)
self.moxed_client.list_extensions().AndReturn( self.moxed_client.list_extensions().AndReturn(
{'extensions': [{'name': 'nvp-qos'}]}) {'extensions': [{'name': 'nvp-qos'}]})
self.mox.ReplayAll() self.mox.ReplayAll()
@ -797,6 +823,9 @@ class TestNeutronv2(TestNeutronv2Base):
{'networks': self.nets2}) {'networks': self.nets2})
self.moxed_client.list_networks(shared=True).AndReturn( self.moxed_client.list_networks(shared=True).AndReturn(
{'networks': []}) {'networks': []})
neutronv2.get_client(mox.IgnoreArg(),
admin=True).AndReturn(
self.moxed_client)
port_req_body = { port_req_body = {
'port': { 'port': {
'network_id': self.nets2[0]['id'], 'network_id': self.nets2[0]['id'],
@ -1709,7 +1738,7 @@ class TestNeutronv2Portbinding(TestNeutronv2Base):
def test_populate_neutron_extension_values_binding(self): def test_populate_neutron_extension_values_binding(self):
api = neutronapi.API() api = neutronapi.API()
neutronv2.get_client(mox.IgnoreArg()).AndReturn( neutronv2.get_client(mox.IgnoreArg(), admin=True).AndReturn(
self.moxed_client) self.moxed_client)
self.moxed_client.list_extensions().AndReturn( self.moxed_client.list_extensions().AndReturn(
{'extensions': [{'name': constants.PORTBINDING_EXT}]}) {'extensions': [{'name': constants.PORTBINDING_EXT}]})
@ -1795,3 +1824,106 @@ class TestNeutronv2ExtraDhcpOpts(TestNeutronv2Base):
self._allocate_for_instance(1, dhcp_options=dhcp_opts) self._allocate_for_instance(1, dhcp_options=dhcp_opts)
CONF.set_override('dhcp_options_enabled', False) 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)
def test_get_client_for_admin(self):
self.flags(neutron_auth_strategy=None)
self.flags(neutron_url='http://anyhost/')
self.flags(neutron_url_timeout=30)
my_context = context.RequestContext('userid', 'my_tenantid',
auth_token='token')
self.mox.StubOutWithMock(client.Client, "__init__")
client.Client.__init__(
auth_url=CONF.neutron_admin_auth_url,
password=CONF.neutron_admin_password,
tenant_name=CONF.neutron_admin_tenant_name,
username=CONF.neutron_admin_username,
endpoint_url=CONF.neutron_url,
auth_strategy=None,
timeout=CONF.neutron_url_timeout,
insecure=False,
ca_cert=None).AndReturn(None)
self.mox.ReplayAll()
# clear the cache
if hasattr(local.strong_store, 'neutron_client'):
delattr(local.strong_store, 'neutron_client')
# Note that the context is not elevated, but the True is passed in
# which will force an elevation to admin credentials even though
# the context has an auth_token.
neutronv2.get_client(my_context, True)
def test_get_client_for_admin_context(self):
self.flags(neutron_auth_strategy=None)
self.flags(neutron_url='http://anyhost/')
self.flags(neutron_url_timeout=30)
my_context = context.get_admin_context()
self.mox.StubOutWithMock(client.Client, "__init__")
client.Client.__init__(
auth_url=CONF.neutron_admin_auth_url,
password=CONF.neutron_admin_password,
tenant_name=CONF.neutron_admin_tenant_name,
username=CONF.neutron_admin_username,
endpoint_url=CONF.neutron_url,
auth_strategy=None,
timeout=CONF.neutron_url_timeout,
insecure=False,
ca_cert=None).AndReturn(None)
self.mox.ReplayAll()
# clear the cache
if hasattr(local.strong_store, 'neutron_client'):
delattr(local.strong_store, 'neutron_client')
# Note that the context does not contain a token but is
# an admin context which will force an elevation to admin
# credentials.
neutronv2.get_client(my_context)