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:
parent
0193bd6c30
commit
da93e11794
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
|
)),
|
||||||
]
|
]
|
||||||
|
@ -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).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
@ -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)
|
Loading…
Reference in New Issue
Block a user