Modernize the nova client in cinder

The nova client (used by the InstanceLocalityFilter for example)
seems to have obscure config options (compared to other projects),
and seems it is buggy too. Fix this by introducing a [nova] section,
where the usual auth parameters can be put (auth_type, auth_url,
username, password, etc...), and deprecate the old options.
Also doesn't play with the Service Catalog, the authentication plugins
can handle it, use a Token authentication plugin when using
the user context, and removed the separate usage of the admin endpoint.

Change-Id: I55613793c8f525a36ac74636f47d7ab76f5c7e39
Closes-bug: #1686616
DocImpact: to use a Nova connection (e.g. for InstanceLocalityFilter),
one has to configure the [nova] section.
This commit is contained in:
Gyorgy Szombathelyi 2017-04-27 17:25:49 +02:00
parent 0193bd6c30
commit da93e11794
6 changed files with 192 additions and 215 deletions

View File

@ -186,17 +186,33 @@ global_opts = [
cfg.StrOpt('os_privileged_user_name', cfg.StrOpt('os_privileged_user_name',
help='OpenStack privileged account username. Used for requests ' help='OpenStack privileged account username. Used for requests '
'to other services (such as Nova) that require an account ' 'to other services (such as Nova) that require an account '
'with special rights.'), 'with special rights.',
deprecated_for_removal=True,
deprecated_since="11.0.0",
deprecated_reason='Use the [nova] section for configuring '
'Keystone authentication for a privileged user.'),
cfg.StrOpt('os_privileged_user_password', cfg.StrOpt('os_privileged_user_password',
help='Password associated with the OpenStack privileged ' help='Password associated with the OpenStack privileged '
'account.', 'account.',
deprecated_for_removal=True,
deprecated_since="11.0.0",
deprecated_reason='Use the [nova] section to configure '
'Keystone authentication for a privileged user.',
secret=True), secret=True),
cfg.StrOpt('os_privileged_user_tenant', cfg.StrOpt('os_privileged_user_tenant',
help='Tenant name associated with the OpenStack privileged ' help='Tenant name associated with the OpenStack privileged '
'account.'), 'account.',
deprecated_for_removal=True,
deprecated_since="11.0.0",
deprecated_reason='Use the [nova] section to configure '
'Keystone authentication for a privileged user.'),
cfg.URIOpt('os_privileged_user_auth_url', cfg.URIOpt('os_privileged_user_auth_url',
help='Auth URL associated with the OpenStack privileged ' help='Auth URL associated with the OpenStack privileged '
'account.'), 'account.',
deprecated_for_removal=True,
deprecated_since="11.0.0",
deprecated_reason='Use the [nova] section to configure '
'Keystone authentication for a privileged user.')
] ]
CONF.register_opts(core_opts) CONF.register_opts(core_opts)

View File

@ -17,7 +17,7 @@ Handles all requests to Nova.
""" """
from keystoneauth1 import identity from keystoneauth1 import identity
from keystoneauth1 import session as ka_session from keystoneauth1 import loading as ks_loading
from novaclient import api_versions from novaclient import api_versions
from novaclient import client as nova_client from novaclient import client as nova_client
from novaclient import exceptions as nova_exceptions from novaclient import exceptions as nova_exceptions
@ -25,37 +25,61 @@ from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from requests import exceptions as request_exceptions from requests import exceptions as request_exceptions
from cinder import context as ctx
from cinder.db import base from cinder.db import base
from cinder import exception from cinder import exception
nova_opts = [ old_opts = [
cfg.StrOpt('nova_catalog_info', cfg.StrOpt('nova_catalog_info',
default='compute:Compute Service:publicURL', default='compute:Compute Service:publicURL',
help='Match this value when searching for nova in the ' help='Match this value when searching for nova in the '
'service catalog. Format is: separated values of ' 'service catalog. Format is: separated values of '
'the form: ' 'the form: '
'<service_type>:<service_name>:<endpoint_type>'), '<service_type>:<service_name>:<endpoint_type>',
deprecated_for_removal=True),
cfg.StrOpt('nova_catalog_admin_info', cfg.StrOpt('nova_catalog_admin_info',
default='compute:Compute Service:publicURL', default='compute:Compute Service:publicURL',
help='Same as nova_catalog_info, but for admin endpoint.'), help='Same as nova_catalog_info, but for admin endpoint.',
deprecated_for_removal=True),
cfg.StrOpt('nova_endpoint_template', cfg.StrOpt('nova_endpoint_template',
help='Override service catalog lookup with template for nova ' help='Override service catalog lookup with template for nova '
'endpoint e.g. http://localhost:8774/v2/%(project_id)s'), 'endpoint e.g. http://localhost:8774/v2/%(project_id)s',
deprecated_for_removal=True),
cfg.StrOpt('nova_endpoint_admin_template', cfg.StrOpt('nova_endpoint_admin_template',
help='Same as nova_endpoint_template, but for admin endpoint.'), help='Same as nova_endpoint_template, but for admin endpoint.',
cfg.StrOpt('os_region_name', deprecated_for_removal=True),
help='Region name of this node'),
cfg.StrOpt('nova_ca_certificates_file',
help='Location of ca certificates file to use for nova client '
'requests.'),
cfg.BoolOpt('nova_api_insecure',
default=False,
help='Allow to perform insecure SSL requests to nova'),
] ]
nova_opts = [
cfg.StrOpt('region_name',
help='Name of nova region to use. Useful if keystone manages '
'more than one region.',
deprecated_name="os_region_name",
deprecated_group="DEFAULT"),
cfg.StrOpt('interface',
default='public',
choices=['public', 'admin', 'internal'],
help='Type of the nova endpoint to use. This endpoint will '
'be looked up in the keystone catalog and should be '
'one of public, internal or admin.'),
cfg.StrOpt('token_auth_url',
help='The authentication URL for the nova connection when '
'using the current user''s token'),
]
NOVA_GROUP = 'nova'
CONF = cfg.CONF CONF = cfg.CONF
CONF.register_opts(nova_opts)
deprecations = {'cafile': [cfg.DeprecatedOpt('nova_ca_certificates_file')],
'insecure': [cfg.DeprecatedOpt('nova_api_insecure')]}
nova_session_opts = ks_loading.get_session_conf_options(
deprecated_opts=deprecations)
nova_auth_opts = ks_loading.get_auth_common_conf_options()
CONF.register_opts(old_opts)
CONF.register_opts(nova_opts, group=NOVA_GROUP)
CONF.register_opts(nova_session_opts, group=NOVA_GROUP)
CONF.register_opts(nova_auth_opts, group=NOVA_GROUP)
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -67,167 +91,54 @@ nova_extensions = [ext for ext in
"list_extensions")] "list_extensions")]
# TODO(dmllr): This is a copy of the ServiceCatalog class in python-novaclient def novaclient(context, privileged_user=False, timeout=None):
# that got removed in 7.0.0 release. This needs to be cleaned up once we depend
# on newer novaclient.
class _NovaClientServiceCatalog(object):
"""Helper methods for dealing with a Keystone Service Catalog."""
def __init__(self, resource_dict):
self.catalog = resource_dict
def url_for(self, attr=None, filter_value=None,
service_type=None, endpoint_type='publicURL',
service_name=None, volume_service_name=None):
"""Fetch public URL for a particular endpoint.
If none given, return the first.
See tests for sample service catalog.
"""
matching_endpoints = []
if 'endpoints' in self.catalog:
# We have a bastardized service catalog. Treat it special. :/
for endpoint in self.catalog['endpoints']:
if not filter_value or endpoint[attr] == filter_value:
# Ignore 1.0 compute endpoints
if endpoint.get("type") == 'compute' and \
endpoint.get('versionId') in (None, '1.1', '2'):
matching_endpoints.append(endpoint)
if not matching_endpoints:
raise nova_exceptions.EndpointNotFound()
# We don't always get a service catalog back ...
if 'serviceCatalog' not in self.catalog['access']:
return None
# Full catalog ...
catalog = self.catalog['access']['serviceCatalog']
for service in catalog:
if service.get("type") != service_type:
continue
if (service_name and service_type == 'compute' and
service.get('name') != service_name):
continue
if (volume_service_name and service_type == 'volume' and
service.get('name') != volume_service_name):
continue
endpoints = service['endpoints']
for endpoint in endpoints:
# Ignore 1.0 compute endpoints
if (service.get("type") == 'compute' and
endpoint.get('versionId', '2') not in ('1.1', '2')):
continue
if (not filter_value or
endpoint.get(attr).lower() == filter_value.lower()):
endpoint["serviceName"] = service.get("name")
matching_endpoints.append(endpoint)
if not matching_endpoints:
raise nova_exceptions.EndpointNotFound()
elif len(matching_endpoints) > 1:
raise nova_exceptions.AmbiguousEndpoints(
endpoints=matching_endpoints)
else:
return matching_endpoints[0][endpoint_type]
def novaclient(context, admin_endpoint=False, privileged_user=False,
timeout=None):
"""Returns a Nova client """Returns a Nova client
@param admin_endpoint: If True, use the admin endpoint template from
configuration ('nova_endpoint_admin_template' and 'nova_catalog_info')
@param privileged_user: If True, use the account from configuration @param privileged_user: If True, use the account from configuration
(requires 'os_privileged_user_name', 'os_privileged_user_password' and (requires 'auth_type' and the other usual Keystone authentication
'os_privileged_user_tenant' to be set) options to be set in the [nova] section)
@param timeout: Number of seconds to wait for an answer before raising a @param timeout: Number of seconds to wait for an answer before raising a
Timeout exception (None to disable) Timeout exception (None to disable)
""" """
# FIXME: the novaclient ServiceCatalog object is mis-named.
# It actually contains the entire access blob.
# Only needed parts of the service catalog are passed in, see
# nova/context.py.
compat_catalog = {
'access': {'serviceCatalog': context.service_catalog or []}
}
sc = _NovaClientServiceCatalog(compat_catalog)
nova_endpoint_template = CONF.nova_endpoint_template if privileged_user and CONF[NOVA_GROUP].auth_type:
nova_catalog_info = CONF.nova_catalog_info n_auth = ks_loading.load_auth_from_conf_options(CONF, NOVA_GROUP)
if admin_endpoint:
nova_endpoint_template = CONF.nova_endpoint_admin_template
nova_catalog_info = CONF.nova_catalog_admin_info
service_type, service_name, endpoint_type = nova_catalog_info.split(':')
# Extract the region if set in configuration
if CONF.os_region_name:
region_filter = {'attr': 'region', 'filter_value': CONF.os_region_name}
else: else:
region_filter = {} if CONF[NOVA_GROUP].token_auth_url:
url = CONF[NOVA_GROUP].token_auth_url
if privileged_user and CONF.os_privileged_user_name:
context = ctx.RequestContext(
CONF.os_privileged_user_name, None,
auth_token=CONF.os_privileged_user_password,
project_name=CONF.os_privileged_user_tenant,
service_catalog=context.service_catalog)
# When privileged_user is used, it needs to authenticate to Keystone
# before querying Nova, so we set auth_url to the identity service
# endpoint.
if CONF.os_privileged_user_auth_url:
url = CONF.os_privileged_user_auth_url
else: else:
# We then pass region_name, endpoint_type, etc. to the # Search for the identity endpoint in the service catalog
# Client() constructor so that the final endpoint is # if nova.token_auth_url is not configured
# chosen correctly. matching_endpoints = []
url = sc.url_for(service_type='identity', for service in context.service_catalog:
endpoint_type=endpoint_type, if service.get('type') != 'identity':
**region_filter) continue
for endpoint in service['endpoints']:
LOG.debug('Creating a Nova client using "%s" user', if (not CONF[NOVA_GROUP].region_name or
CONF.os_privileged_user_name) endpoint.get('region') ==
else: CONF[NOVA_GROUP].region_name):
if nova_endpoint_template: matching_endpoints.append(endpoint)
url = nova_endpoint_template % context.to_dict() if not matching_endpoints:
else: raise nova_exceptions.EndpointNotFound()
url = sc.url_for(service_type=service_type, url = matching_endpoints[0].get(CONF[NOVA_GROUP].interface + 'URL')
service_name=service_name, n_auth = identity.Token(auth_url=url,
endpoint_type=endpoint_type, token=context.auth_token,
**region_filter) project_name=context.project_name,
project_domain_id=context.project_domain)
LOG.debug('Nova client connection created using URL: %s', url) keystone_session = ks_loading.load_session_from_conf_options(
CONF,
# Now that we have the correct auth_url, username, password, project_name NOVA_GROUP,
# and domain information, i.e. project_domain_id and user_domain_id (if auth=n_auth)
# using Identity v3 API) let's build a Keystone session.
auth = identity.Password(auth_url=url,
username=context.user_id,
password=context.auth_token,
project_name=context.project_name,
project_domain_id=context.project_domain,
user_domain_id=context.user_domain)
keystone_session = ka_session.Session(auth=auth)
c = nova_client.Client(api_versions.APIVersion(NOVA_API_VERSION), c = nova_client.Client(api_versions.APIVersion(NOVA_API_VERSION),
session=keystone_session, session=keystone_session,
insecure=CONF.nova_api_insecure, insecure=CONF[NOVA_GROUP].insecure,
timeout=timeout, timeout=timeout,
region_name=CONF.os_region_name, region_name=CONF[NOVA_GROUP].region_name,
endpoint_type=endpoint_type, endpoint_type=CONF[NOVA_GROUP].interface,
cacert=CONF.nova_ca_certificates_file, cacert=CONF[NOVA_GROUP].cafile,
extensions=nova_extensions) extensions=nova_extensions)
if not privileged_user:
# noauth extracts user_id:project_id from auth_token
c.client.auth_token = (context.auth_token or '%s:%s'
% (context.user_id, context.project_id))
c.client.management_url = url
return c return c
@ -243,13 +154,13 @@ class API(base.Base):
def update_server_volume(self, context, server_id, attachment_id, def update_server_volume(self, context, server_id, attachment_id,
new_volume_id): new_volume_id):
nova = novaclient(context, admin_endpoint=True, privileged_user=True) nova = novaclient(context, privileged_user=True)
nova.volumes.update_server_volume(server_id, nova.volumes.update_server_volume(server_id,
attachment_id, attachment_id,
new_volume_id) new_volume_id)
def create_volume_snapshot(self, context, volume_id, create_info): def create_volume_snapshot(self, context, volume_id, create_info):
nova = novaclient(context, admin_endpoint=True, privileged_user=True) nova = novaclient(context, privileged_user=True)
# pylint: disable=E1101 # pylint: disable=E1101
nova.assisted_volume_snapshots.create( nova.assisted_volume_snapshots.create(
@ -257,7 +168,7 @@ class API(base.Base):
create_info=create_info) create_info=create_info)
def delete_volume_snapshot(self, context, snapshot_id, delete_info): def delete_volume_snapshot(self, context, snapshot_id, delete_info):
nova = novaclient(context, admin_endpoint=True, privileged_user=True) nova = novaclient(context, privileged_user=True)
# pylint: disable=E1101 # pylint: disable=E1101
nova.assisted_volume_snapshots.delete( nova.assisted_volume_snapshots.delete(

View File

@ -243,7 +243,7 @@ def list_opts():
cinder_common_config.core_opts, cinder_common_config.core_opts,
cinder_common_config.global_opts, cinder_common_config.global_opts,
cinder.compute.compute_opts, cinder.compute.compute_opts,
cinder_compute_nova.nova_opts, cinder_compute_nova.old_opts,
cinder_context.context_opts, cinder_context.context_opts,
cinder_db_api.db_opts, cinder_db_api.db_opts,
[cinder_db_base.db_driver_opt], [cinder_db_base.db_driver_opt],
@ -388,4 +388,10 @@ def list_opts():
itertools.chain( itertools.chain(
cinder_keymgr_confkeymgr.key_mgr_opts, cinder_keymgr_confkeymgr.key_mgr_opts,
)), )),
('NOVA_GROUP',
itertools.chain(
cinder_compute_nova.nova_opts,
cinder_compute_nova.nova_session_opts,
cinder_compute_nova.nova_auth_opts,
)),
] ]

View File

@ -43,9 +43,9 @@ class InstanceLocalityFilter(filters.BaseBackendFilter):
is by default), so that the 'OS-EXT-SRV-ATTR:host' property is returned is by default), so that the 'OS-EXT-SRV-ATTR:host' property is returned
when requesting instance info. when requesting instance info.
- Either an account with privileged rights for Nova must be configured in - Either an account with privileged rights for Nova must be configured in
Cinder configuration (see 'os_privileged_user_name'), or the user making Cinder configuration (configure a keystone authentication plugin in the
the call needs to have sufficient rights (see [nova] section), or the user making the call needs to have sufficient
'extended_server_attributes' in Nova policy). rights (see 'extended_server_attributes' in Nova policy).
""" """

View File

@ -17,64 +17,86 @@ import mock
from cinder.compute import nova from cinder.compute import nova
from cinder import context from cinder import context
from cinder import test from cinder import test
from keystoneauth1 import loading as ks_loading
from novaclient import exceptions as nova_exceptions from novaclient import exceptions as nova_exceptions
from oslo_config import cfg
CONF = cfg.CONF
class NovaClientTestCase(test.TestCase): class NovaClientTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(NovaClientTestCase, self).setUp() super(NovaClientTestCase, self).setUp()
# Register the Password auth plugin options,
# so we can use CONF.set_override
# reset() first, otherwise already registered CLI options will
# prevent unregister in tearDown()
# Use CONF.set_override(), because we'll unregister the opts,
# no need (and not possible) to cleanup.
CONF.reset()
self.password_opts = \
ks_loading.get_auth_plugin_conf_options('password')
CONF.register_opts(self.password_opts, group='nova')
CONF.set_override('auth_url',
'http://keystonehost:5000',
group='nova')
CONF.set_override('username', 'adminuser', group='nova')
CONF.set_override('password', 'strongpassword', group='nova')
self.ctx = context.RequestContext('regularuser', 'e3f0833dc08b4cea', self.ctx = context.RequestContext('regularuser', 'e3f0833dc08b4cea',
auth_token='token', is_admin=False) auth_token='token', is_admin=False)
self.ctx.service_catalog = \ self.ctx.service_catalog = \
[{'type': 'compute', 'name': 'nova', 'endpoints': [{'type': 'compute', 'name': 'nova', 'endpoints':
[{'publicURL': 'http://novahost:8774/v2/e3f0833dc08b4cea'}]}, [{'publicURL': 'http://novahost:8774/v2/e3f0833dc08b4cea'}]},
{'type': 'identity', 'name': 'keystone', 'endpoints': {'type': 'identity', 'name': 'keystone', 'endpoints':
[{'publicURL': 'http://keystonehost:5000/v2.0'}]}] [{'publicURL': 'http://keystonehostfromsc:5000/v3'}]}]
self.override_config('nova_endpoint_template', self.override_config('auth_type', 'password', group='nova')
'http://novahost:8774/v2/%(project_id)s') self.override_config('cafile', 'my.ca', group='nova')
self.override_config('nova_endpoint_admin_template',
'http://novaadmhost:4778/v2/%(project_id)s') def tearDown(self):
self.override_config('nova_catalog_admin_info', super(NovaClientTestCase, self).tearDown()
'compute:Compute Service:adminURL')
self.override_config('os_privileged_user_name', 'adminuser') CONF.unregister_opts(self.password_opts, group='nova')
self.override_config('os_privileged_user_password', 'strongpassword')
@mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.api_versions.APIVersion')
@mock.patch('novaclient.client.Client') @mock.patch('novaclient.client.Client')
@mock.patch('keystoneauth1.identity.Password') @mock.patch('keystoneauth1.identity.Token')
@mock.patch('keystoneauth1.session.Session') @mock.patch('keystoneauth1.session.Session')
def test_nova_client_regular(self, p_session, p_password_plugin, p_client, def test_nova_client_regular(self, p_session, p_token_plugin, p_client,
p_api_version): p_api_version):
self.override_config('token_auth_url',
'http://keystonehost:5000',
group='nova')
nova.novaclient(self.ctx) nova.novaclient(self.ctx)
p_password_plugin.assert_called_once_with( p_token_plugin.assert_called_once_with(
auth_url='http://novahost:8774/v2/e3f0833dc08b4cea', auth_url='http://keystonehost:5000',
password='token', project_name=None, username='regularuser', token='token', project_name=None, project_domain_id=None
project_domain_id=None, user_domain_id=None
) )
p_client.assert_called_once_with( p_client.assert_called_once_with(
p_api_version(nova.NOVA_API_VERSION), p_api_version(nova.NOVA_API_VERSION),
session=p_session.return_value, region_name=None, session=p_session.return_value, region_name=None,
insecure=False, endpoint_type='publicURL', cacert=None, insecure=False, endpoint_type='public', cacert='my.ca',
timeout=None, extensions=nova.nova_extensions) timeout=None, extensions=nova.nova_extensions)
@mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.api_versions.APIVersion')
@mock.patch('novaclient.client.Client') @mock.patch('novaclient.client.Client')
@mock.patch('keystoneauth1.identity.Password') @mock.patch('keystoneauth1.identity.Token')
@mock.patch('keystoneauth1.session.Session') @mock.patch('keystoneauth1.session.Session')
def test_nova_client_admin_endpoint(self, p_session, p_password_plugin, def test_nova_client_regular_service_catalog(self, p_session,
p_client, p_api_version): p_token_plugin, p_client,
nova.novaclient(self.ctx, admin_endpoint=True) p_api_version):
p_password_plugin.assert_called_once_with(
auth_url='http://novaadmhost:4778/v2/e3f0833dc08b4cea', nova.novaclient(self.ctx)
password='token', project_name=None, username='regularuser', p_token_plugin.assert_called_once_with(
project_domain_id=None, user_domain_id=None auth_url='http://keystonehostfromsc:5000/v3',
token='token', project_name=None, project_domain_id=None
) )
p_client.assert_called_once_with( p_client.assert_called_once_with(
p_api_version(nova.NOVA_API_VERSION), p_api_version(nova.NOVA_API_VERSION),
session=p_session.return_value, region_name=None, session=p_session.return_value, region_name=None,
insecure=False, endpoint_type='adminURL', cacert=None, insecure=False, endpoint_type='public', cacert='my.ca',
timeout=None, extensions=nova.nova_extensions) timeout=None, extensions=nova.nova_extensions)
@mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.api_versions.APIVersion')
@ -83,16 +105,20 @@ class NovaClientTestCase(test.TestCase):
@mock.patch('keystoneauth1.session.Session') @mock.patch('keystoneauth1.session.Session')
def test_nova_client_privileged_user(self, p_session, p_password_plugin, def test_nova_client_privileged_user(self, p_session, p_password_plugin,
p_client, p_api_version): p_client, p_api_version):
nova.novaclient(self.ctx, privileged_user=True) nova.novaclient(self.ctx, privileged_user=True)
p_password_plugin.assert_called_once_with( p_password_plugin.assert_called_once_with(
auth_url='http://keystonehost:5000/v2.0', auth_url='http://keystonehost:5000', default_domain_id=None,
password='strongpassword', project_name=None, username='adminuser', default_domain_name=None, domain_id=None, domain_name=None,
project_domain_id=None, user_domain_id=None password='strongpassword', project_domain_id=None,
project_domain_name=None, project_id=None, project_name=None,
trust_id=None, user_domain_id=None, user_domain_name=None,
user_id=None, username='adminuser'
) )
p_client.assert_called_once_with( p_client.assert_called_once_with(
p_api_version(nova.NOVA_API_VERSION), p_api_version(nova.NOVA_API_VERSION),
session=p_session.return_value, region_name=None, session=p_session.return_value, region_name=None,
insecure=False, endpoint_type='publicURL', cacert=None, insecure=False, endpoint_type='public', cacert='my.ca',
timeout=None, extensions=nova.nova_extensions) timeout=None, extensions=nova.nova_extensions)
@mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.api_versions.APIVersion')
@ -103,18 +129,23 @@ class NovaClientTestCase(test.TestCase):
p_password_plugin, p_password_plugin,
p_client, p_client,
p_api_version): p_api_version):
self.override_config('os_privileged_user_auth_url',
'http://privatekeystonehost:5000/v2.0') CONF.set_override('auth_url',
'http://privatekeystonehost:5000',
group='nova')
nova.novaclient(self.ctx, privileged_user=True) nova.novaclient(self.ctx, privileged_user=True)
p_password_plugin.assert_called_once_with( p_password_plugin.assert_called_once_with(
auth_url='http://privatekeystonehost:5000/v2.0', auth_url='http://privatekeystonehost:5000', default_domain_id=None,
password='strongpassword', project_name=None, username='adminuser', default_domain_name=None, domain_id=None, domain_name=None,
project_domain_id=None, user_domain_id=None password='strongpassword', project_domain_id=None,
project_domain_name=None, project_id=None, project_name=None,
trust_id=None, user_domain_id=None, user_domain_name=None,
user_id=None, username='adminuser'
) )
p_client.assert_called_once_with( p_client.assert_called_once_with(
p_api_version(nova.NOVA_API_VERSION), p_api_version(nova.NOVA_API_VERSION),
session=p_session.return_value, region_name=None, session=p_session.return_value, region_name=None,
insecure=False, endpoint_type='publicURL', cacert=None, insecure=False, endpoint_type='public', cacert='my.ca',
timeout=None, extensions=nova.nova_extensions) timeout=None, extensions=nova.nova_extensions)
@mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.api_versions.APIVersion')
@ -123,17 +154,21 @@ class NovaClientTestCase(test.TestCase):
@mock.patch('keystoneauth1.session.Session') @mock.patch('keystoneauth1.session.Session')
def test_nova_client_custom_region(self, p_session, p_password_plugin, def test_nova_client_custom_region(self, p_session, p_password_plugin,
p_client, p_api_version): p_client, p_api_version):
self.override_config('os_region_name', 'farfaraway')
nova.novaclient(self.ctx) CONF.set_override('region_name', 'farfaraway', group='nova')
nova.novaclient(self.ctx, privileged_user=True)
p_password_plugin.assert_called_once_with( p_password_plugin.assert_called_once_with(
auth_url='http://novahost:8774/v2/e3f0833dc08b4cea', auth_url='http://keystonehost:5000', default_domain_id=None,
password='token', project_name=None, username='regularuser', default_domain_name=None, domain_id=None, domain_name=None,
project_domain_id=None, user_domain_id=None password='strongpassword', project_domain_id=None,
project_domain_name=None, project_id=None, project_name=None,
trust_id=None, user_domain_id=None, user_domain_name=None,
user_id=None, username='adminuser'
) )
p_client.assert_called_once_with( p_client.assert_called_once_with(
p_api_version(nova.NOVA_API_VERSION), p_api_version(nova.NOVA_API_VERSION),
session=p_session.return_value, region_name='farfaraway', session=p_session.return_value, region_name='farfaraway',
insecure=False, endpoint_type='publicURL', cacert=None, insecure=False, endpoint_type='public', cacert='my.ca',
timeout=None, extensions=nova.nova_extensions) timeout=None, extensions=nova.nova_extensions)
def test_novaclient_exceptions(self): def test_novaclient_exceptions(self):
@ -141,7 +176,6 @@ class NovaClientTestCase(test.TestCase):
# removed from novaclient since the service catalog # removed from novaclient since the service catalog
# code does not have thorough tests. # code does not have thorough tests.
self.assertTrue(hasattr(nova_exceptions, 'EndpointNotFound')) self.assertTrue(hasattr(nova_exceptions, 'EndpointNotFound'))
self.assertTrue(hasattr(nova_exceptions, 'AmbiguousEndpoints'))
class FakeNovaClient(object): class FakeNovaClient(object):
@ -178,7 +212,6 @@ class NovaApiTestCase(test.TestCase):
'attach_id', 'new_volume_id') 'attach_id', 'new_volume_id')
mock_novaclient.assert_called_once_with(self.ctx, mock_novaclient.assert_called_once_with(self.ctx,
admin_endpoint=True,
privileged_user=True) privileged_user=True)
mock_update_server_volume.assert_called_once_with( mock_update_server_volume.assert_called_once_with(
'server_id', 'server_id',

View File

@ -0,0 +1,11 @@
---
features:
- a [nova] section is added to configure the connection
to the compute service, which is needed to the
InstanceLocalityFilter, for example.
deprecations:
- The os_privileged_xxx and nova_xxx in the [default]
section are deprecated in favor of the settings in
the [nova] section.
fixes:
- Fixed using of the user's token in the nova client (bug #1686616)