From da93e11794d2a7479b037f96fc5aef83f98be8dd Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Thu, 27 Apr 2017 17:25:49 +0200 Subject: [PATCH] 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. --- cinder/common/config.py | 22 +- cinder/compute/nova.py | 241 ++++++------------ cinder/opts.py | 8 +- .../filters/instance_locality_filter.py | 6 +- cinder/tests/unit/compute/test_nova.py | 119 +++++---- ...-nova-config-section-2a7a51a0572e7064.yaml | 11 + 6 files changed, 192 insertions(+), 215 deletions(-) create mode 100644 releasenotes/notes/new-nova-config-section-2a7a51a0572e7064.yaml diff --git a/cinder/common/config.py b/cinder/common/config.py index ff5074f0c6f..334fd93e00d 100644 --- a/cinder/common/config.py +++ b/cinder/common/config.py @@ -186,17 +186,33 @@ global_opts = [ cfg.StrOpt('os_privileged_user_name', help='OpenStack privileged account username. Used for requests ' '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', help='Password associated with the OpenStack privileged ' '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), cfg.StrOpt('os_privileged_user_tenant', 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', 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) diff --git a/cinder/compute/nova.py b/cinder/compute/nova.py index 5290d5ca2e3..7edecfd0dc4 100644 --- a/cinder/compute/nova.py +++ b/cinder/compute/nova.py @@ -17,7 +17,7 @@ Handles all requests to Nova. """ 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 client as nova_client from novaclient import exceptions as nova_exceptions @@ -25,37 +25,61 @@ from oslo_config import cfg from oslo_log import log as logging from requests import exceptions as request_exceptions -from cinder import context as ctx from cinder.db import base from cinder import exception -nova_opts = [ +old_opts = [ cfg.StrOpt('nova_catalog_info', default='compute:Compute Service:publicURL', help='Match this value when searching for nova in the ' 'service catalog. Format is: separated values of ' 'the form: ' - '::'), + '::', + deprecated_for_removal=True), cfg.StrOpt('nova_catalog_admin_info', 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', 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', - help='Same as nova_endpoint_template, but for admin endpoint.'), - cfg.StrOpt('os_region_name', - 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'), + help='Same as nova_endpoint_template, but for admin endpoint.', + deprecated_for_removal=True), ] +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.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__) @@ -67,167 +91,54 @@ nova_extensions = [ext for ext in "list_extensions")] -# TODO(dmllr): This is a copy of the ServiceCatalog class in python-novaclient -# 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): +def novaclient(context, privileged_user=False, timeout=None): """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 - (requires 'os_privileged_user_name', 'os_privileged_user_password' and - 'os_privileged_user_tenant' to be set) + (requires 'auth_type' and the other usual Keystone authentication + options to be set in the [nova] section) @param timeout: Number of seconds to wait for an answer before raising a 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 - nova_catalog_info = CONF.nova_catalog_info - - 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} + if privileged_user and CONF[NOVA_GROUP].auth_type: + n_auth = ks_loading.load_auth_from_conf_options(CONF, NOVA_GROUP) else: - region_filter = {} - - 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 + if CONF[NOVA_GROUP].token_auth_url: + url = CONF[NOVA_GROUP].token_auth_url else: - # We then pass region_name, endpoint_type, etc. to the - # Client() constructor so that the final endpoint is - # chosen correctly. - url = sc.url_for(service_type='identity', - endpoint_type=endpoint_type, - **region_filter) - - LOG.debug('Creating a Nova client using "%s" user', - CONF.os_privileged_user_name) - else: - if nova_endpoint_template: - url = nova_endpoint_template % context.to_dict() - else: - url = sc.url_for(service_type=service_type, - service_name=service_name, - endpoint_type=endpoint_type, - **region_filter) - - LOG.debug('Nova client connection created using URL: %s', url) - - # Now that we have the correct auth_url, username, password, project_name - # and domain information, i.e. project_domain_id and user_domain_id (if - # 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) + # Search for the identity endpoint in the service catalog + # if nova.token_auth_url is not configured + matching_endpoints = [] + for service in context.service_catalog: + if service.get('type') != 'identity': + continue + for endpoint in service['endpoints']: + if (not CONF[NOVA_GROUP].region_name or + endpoint.get('region') == + CONF[NOVA_GROUP].region_name): + matching_endpoints.append(endpoint) + if not matching_endpoints: + raise nova_exceptions.EndpointNotFound() + url = matching_endpoints[0].get(CONF[NOVA_GROUP].interface + 'URL') + n_auth = identity.Token(auth_url=url, + token=context.auth_token, + project_name=context.project_name, + project_domain_id=context.project_domain) + keystone_session = ks_loading.load_session_from_conf_options( + CONF, + NOVA_GROUP, + auth=n_auth) c = nova_client.Client(api_versions.APIVersion(NOVA_API_VERSION), session=keystone_session, - insecure=CONF.nova_api_insecure, + insecure=CONF[NOVA_GROUP].insecure, timeout=timeout, - region_name=CONF.os_region_name, - endpoint_type=endpoint_type, - cacert=CONF.nova_ca_certificates_file, + region_name=CONF[NOVA_GROUP].region_name, + endpoint_type=CONF[NOVA_GROUP].interface, + cacert=CONF[NOVA_GROUP].cafile, 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 @@ -243,13 +154,13 @@ class API(base.Base): def update_server_volume(self, context, server_id, attachment_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, attachment_id, new_volume_id) 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 nova.assisted_volume_snapshots.create( @@ -257,7 +168,7 @@ class API(base.Base): create_info=create_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 nova.assisted_volume_snapshots.delete( diff --git a/cinder/opts.py b/cinder/opts.py index 8e9dd58b9db..011bdbb9091 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -243,7 +243,7 @@ def list_opts(): cinder_common_config.core_opts, cinder_common_config.global_opts, cinder.compute.compute_opts, - cinder_compute_nova.nova_opts, + cinder_compute_nova.old_opts, cinder_context.context_opts, cinder_db_api.db_opts, [cinder_db_base.db_driver_opt], @@ -388,4 +388,10 @@ def list_opts(): itertools.chain( 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, + )), ] diff --git a/cinder/scheduler/filters/instance_locality_filter.py b/cinder/scheduler/filters/instance_locality_filter.py index 652bd763c85..83ecafcec82 100644 --- a/cinder/scheduler/filters/instance_locality_filter.py +++ b/cinder/scheduler/filters/instance_locality_filter.py @@ -43,9 +43,9 @@ class InstanceLocalityFilter(filters.BaseBackendFilter): is by default), so that the 'OS-EXT-SRV-ATTR:host' property is returned when requesting instance info. - Either an account with privileged rights for Nova must be configured in - Cinder configuration (see 'os_privileged_user_name'), or the user making - the call needs to have sufficient rights (see - 'extended_server_attributes' in Nova policy). + Cinder configuration (configure a keystone authentication plugin in the + [nova] section), or the user making the call needs to have sufficient + rights (see 'extended_server_attributes' in Nova policy). """ diff --git a/cinder/tests/unit/compute/test_nova.py b/cinder/tests/unit/compute/test_nova.py index f9206abbb62..5ed1f8c2bc7 100644 --- a/cinder/tests/unit/compute/test_nova.py +++ b/cinder/tests/unit/compute/test_nova.py @@ -17,64 +17,86 @@ import mock from cinder.compute import nova from cinder import context from cinder import test +from keystoneauth1 import loading as ks_loading from novaclient import exceptions as nova_exceptions +from oslo_config import cfg + +CONF = cfg.CONF class NovaClientTestCase(test.TestCase): def setUp(self): 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', auth_token='token', is_admin=False) self.ctx.service_catalog = \ [{'type': 'compute', 'name': 'nova', 'endpoints': [{'publicURL': 'http://novahost:8774/v2/e3f0833dc08b4cea'}]}, {'type': 'identity', 'name': 'keystone', 'endpoints': - [{'publicURL': 'http://keystonehost:5000/v2.0'}]}] + [{'publicURL': 'http://keystonehostfromsc:5000/v3'}]}] - self.override_config('nova_endpoint_template', - 'http://novahost:8774/v2/%(project_id)s') - self.override_config('nova_endpoint_admin_template', - 'http://novaadmhost:4778/v2/%(project_id)s') - self.override_config('nova_catalog_admin_info', - 'compute:Compute Service:adminURL') - self.override_config('os_privileged_user_name', 'adminuser') - self.override_config('os_privileged_user_password', 'strongpassword') + self.override_config('auth_type', 'password', group='nova') + self.override_config('cafile', 'my.ca', group='nova') + + def tearDown(self): + super(NovaClientTestCase, self).tearDown() + + CONF.unregister_opts(self.password_opts, group='nova') @mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.client.Client') - @mock.patch('keystoneauth1.identity.Password') + @mock.patch('keystoneauth1.identity.Token') @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): + + self.override_config('token_auth_url', + 'http://keystonehost:5000', + group='nova') nova.novaclient(self.ctx) - p_password_plugin.assert_called_once_with( - auth_url='http://novahost:8774/v2/e3f0833dc08b4cea', - password='token', project_name=None, username='regularuser', - project_domain_id=None, user_domain_id=None + p_token_plugin.assert_called_once_with( + auth_url='http://keystonehost:5000', + token='token', project_name=None, project_domain_id=None ) p_client.assert_called_once_with( p_api_version(nova.NOVA_API_VERSION), 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) @mock.patch('novaclient.api_versions.APIVersion') @mock.patch('novaclient.client.Client') - @mock.patch('keystoneauth1.identity.Password') + @mock.patch('keystoneauth1.identity.Token') @mock.patch('keystoneauth1.session.Session') - def test_nova_client_admin_endpoint(self, p_session, p_password_plugin, - p_client, p_api_version): - nova.novaclient(self.ctx, admin_endpoint=True) - p_password_plugin.assert_called_once_with( - auth_url='http://novaadmhost:4778/v2/e3f0833dc08b4cea', - password='token', project_name=None, username='regularuser', - project_domain_id=None, user_domain_id=None + def test_nova_client_regular_service_catalog(self, p_session, + p_token_plugin, p_client, + p_api_version): + + nova.novaclient(self.ctx) + p_token_plugin.assert_called_once_with( + auth_url='http://keystonehostfromsc:5000/v3', + token='token', project_name=None, project_domain_id=None ) p_client.assert_called_once_with( p_api_version(nova.NOVA_API_VERSION), 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) @mock.patch('novaclient.api_versions.APIVersion') @@ -83,16 +105,20 @@ class NovaClientTestCase(test.TestCase): @mock.patch('keystoneauth1.session.Session') def test_nova_client_privileged_user(self, p_session, p_password_plugin, p_client, p_api_version): + nova.novaclient(self.ctx, privileged_user=True) p_password_plugin.assert_called_once_with( - auth_url='http://keystonehost:5000/v2.0', - password='strongpassword', project_name=None, username='adminuser', - project_domain_id=None, user_domain_id=None + auth_url='http://keystonehost:5000', default_domain_id=None, + default_domain_name=None, domain_id=None, domain_name=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_api_version(nova.NOVA_API_VERSION), 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) @mock.patch('novaclient.api_versions.APIVersion') @@ -103,18 +129,23 @@ class NovaClientTestCase(test.TestCase): p_password_plugin, p_client, 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) p_password_plugin.assert_called_once_with( - auth_url='http://privatekeystonehost:5000/v2.0', - password='strongpassword', project_name=None, username='adminuser', - project_domain_id=None, user_domain_id=None + auth_url='http://privatekeystonehost:5000', default_domain_id=None, + default_domain_name=None, domain_id=None, domain_name=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_api_version(nova.NOVA_API_VERSION), 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) @mock.patch('novaclient.api_versions.APIVersion') @@ -123,17 +154,21 @@ class NovaClientTestCase(test.TestCase): @mock.patch('keystoneauth1.session.Session') def test_nova_client_custom_region(self, p_session, p_password_plugin, 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( - auth_url='http://novahost:8774/v2/e3f0833dc08b4cea', - password='token', project_name=None, username='regularuser', - project_domain_id=None, user_domain_id=None + auth_url='http://keystonehost:5000', default_domain_id=None, + default_domain_name=None, domain_id=None, domain_name=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_api_version(nova.NOVA_API_VERSION), 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) def test_novaclient_exceptions(self): @@ -141,7 +176,6 @@ class NovaClientTestCase(test.TestCase): # removed from novaclient since the service catalog # code does not have thorough tests. self.assertTrue(hasattr(nova_exceptions, 'EndpointNotFound')) - self.assertTrue(hasattr(nova_exceptions, 'AmbiguousEndpoints')) class FakeNovaClient(object): @@ -178,7 +212,6 @@ class NovaApiTestCase(test.TestCase): 'attach_id', 'new_volume_id') mock_novaclient.assert_called_once_with(self.ctx, - admin_endpoint=True, privileged_user=True) mock_update_server_volume.assert_called_once_with( 'server_id', diff --git a/releasenotes/notes/new-nova-config-section-2a7a51a0572e7064.yaml b/releasenotes/notes/new-nova-config-section-2a7a51a0572e7064.yaml new file mode 100644 index 00000000000..dc91f1ae498 --- /dev/null +++ b/releasenotes/notes/new-nova-config-section-2a7a51a0572e7064.yaml @@ -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)