diff --git a/nova/conf/__init__.py b/nova/conf/__init__.py index 022927db8046..f0bd45d81576 100644 --- a/nova/conf/__init__.py +++ b/nova/conf/__init__.py @@ -60,7 +60,6 @@ from nova.conf import service from nova.conf import service_token from nova.conf import servicegroup from nova.conf import spice -from nova.conf import ssl from nova.conf import upgrade_levels from nova.conf import vendordata from nova.conf import vmware @@ -112,7 +111,6 @@ service.register_opts(CONF) service_token.register_opts(CONF) servicegroup.register_opts(CONF) spice.register_opts(CONF) -ssl.register_opts(CONF) upgrade_levels.register_opts(CONF) vendordata.register_opts(CONF) vmware.register_opts(CONF) diff --git a/nova/conf/glance.py b/nova/conf/glance.py index 8b41cd1296f1..61693b5a5b7e 100644 --- a/nova/conf/glance.py +++ b/nova/conf/glance.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from keystoneauth1 import loading as ks_loading from oslo_config import cfg glance_group = cfg.OptGroup( @@ -34,14 +35,6 @@ Possible values: * A list of any fully qualified url of the form "scheme://hostname:port[/path]" (i.e. "http://10.0.1.0:9292" or "https://my.glance.server/image"). -"""), - cfg.BoolOpt('api_insecure', - default=False, - help=""" -Enable insecure SSL (https) requests to glance. - -This setting can be used to turn off verification of the glance server -certificate against the certificate authorities. """), cfg.IntOpt('num_retries', default=0, @@ -142,6 +135,23 @@ def register_opts(conf): conf.register_group(glance_group) conf.register_opts(glance_opts, group=glance_group) + deprecated = { + 'insecure': [cfg.DeprecatedOpt('api_insecure', + group=glance_group.name)], + 'cafile': [cfg.DeprecatedOpt('ca_file', + group="ssl")], + 'certfile': [cfg.DeprecatedOpt('cert_file', + group="ssl")], + 'keyfile': [cfg.DeprecatedOpt('key_file', + group="ssl")], + } + ks_loading.register_session_conf_options(conf, glance_group.name, + deprecated) + def list_opts(): - return {glance_group: glance_opts} + return { + glance_group: ( + glance_opts + + ks_loading.get_session_conf_options()) + } diff --git a/nova/conf/ssl.py b/nova/conf/ssl.py deleted file mode 100644 index 8d59ae653220..000000000000 --- a/nova/conf/ssl.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2016 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_service import sslutils - - -def register_opts(conf): - sslutils.register_opts(conf) - - -def list_opts(): - # The oslo_cache library returns a list of tuples - return dict(sslutils.list_opts()) diff --git a/nova/image/glance.py b/nova/image/glance.py index 5620fc156612..0f598416abb3 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -31,9 +31,9 @@ from cursive import signature_utils import glanceclient import glanceclient.exc from glanceclient.v2 import schemas +from keystoneauth1 import loading as ks_loading from oslo_log import log as logging from oslo_serialization import jsonutils -from oslo_service import sslutils from oslo_utils import excutils from oslo_utils import timeutils import six @@ -50,6 +50,24 @@ from nova.objects import fields LOG = logging.getLogger(__name__) CONF = nova.conf.CONF +_SESSION = None + + +def _glanceclient_from_endpoint(context, endpoint, version): + global _SESSION + + if not _SESSION: + _SESSION = ks_loading.load_session_from_conf_options( + CONF, nova.conf.glance.glance_group.name) + + auth = context.get_auth_plugin() + + # TODO(johngarbutt) eventually we should default to getting the + # endpoint URL from the service catalog. + return glanceclient.Client(version, session=_SESSION, auth=auth, + endpoint_override=endpoint, + global_request_id=context.global_id) + def generate_glance_url(): """Return a random glance url from the api servers we know about.""" @@ -85,27 +103,6 @@ def generate_identity_headers(context, status='Confirmed'): } -def _glanceclient_from_endpoint(context, endpoint, version): - """Instantiate a new glanceclient.Client object.""" - params = {} - # NOTE(sdague): even if we aren't using keystone, it doesn't - # hurt to send these headers. - params['identity_headers'] = generate_identity_headers(context) - params['global_request_id'] = context.global_id - if endpoint.startswith('https://'): - # https specific params - params['insecure'] = CONF.glance.api_insecure - params['ssl_compression'] = False - sslutils.is_enabled(CONF) - if CONF.ssl.cert_file: - params['cert_file'] = CONF.ssl.cert_file - if CONF.ssl.key_file: - params['key_file'] = CONF.ssl.key_file - if CONF.ssl.ca_file: - params['cacert'] = CONF.ssl.ca_file - return glanceclient.Client(str(version), endpoint, **params) - - def get_api_servers(): """Shuffle a list of CONF.glance.api_servers and return an iterator that will cycle through the list, looping around to the beginning diff --git a/nova/tests/unit/image/test_glance.py b/nova/tests/unit/image/test_glance.py index e6b87a9220b0..84b85c8d0b70 100644 --- a/nova/tests/unit/image/test_glance.py +++ b/nova/tests/unit/image/test_glance.py @@ -22,6 +22,7 @@ from cursive import exception as cursive_exception import glanceclient.exc from glanceclient.v1 import images import glanceclient.v2.schemas as schemas +from keystoneauth1 import loading as ks_loading import mock import six from six.moves import StringIO @@ -340,52 +341,51 @@ class TestGetImageService(test.NoDBTestCase): class TestCreateGlanceClient(test.NoDBTestCase): + + @mock.patch.object(context.RequestContext, 'get_auth_plugin') + @mock.patch.object(ks_loading, 'load_session_from_conf_options') @mock.patch('glanceclient.Client') - def test_headers_passed_glanceclient(self, init_mock): - self.flags(auth_strategy='keystone', group='api') - auth_token = 'token' - ctx = context.RequestContext('fake', 'fake', auth_token=auth_token) + def test_glanceclient_with_ks_session(self, mock_client, mock_load, + mock_get_auth): + session = "fake_session" + mock_load.return_value = session + auth = "fake_auth" + mock_get_auth.return_value = auth + ctx = context.RequestContext('fake', 'fake', global_request_id='reqid') + endpoint = "fake_endpoint" + mock_client.side_effect = ["a", "b"] - expected_endpoint = 'http://host4:9295' - expected_params = { - 'identity_headers': { - 'X-Auth-Token': 'token', - 'X-User-Id': 'fake', - 'X-Roles': '', - 'X-Tenant-Id': 'fake', - 'X-Identity-Status': 'Confirmed', - }, - 'global_request_id': mock.ANY + # Reset the cache, so we know its empty before we start + glance._SESSION = None + + result1 = glance._glanceclient_from_endpoint(ctx, endpoint, 2) + result2 = glance._glanceclient_from_endpoint(ctx, endpoint, 2) + + # Ensure that session is only loaded once. + mock_load.assert_called_once_with(glance.CONF, "glance") + self.assertEqual(session, glance._SESSION) + # Ensure new client created every time + client_call = mock.call(2, auth="fake_auth", + endpoint_override=endpoint, session=session, + global_request_id='reqid') + mock_client.assert_has_calls([client_call, client_call]) + self.assertEqual("a", result1) + self.assertEqual("b", result2) + + def test_generate_identity_headers(self): + ctx = context.RequestContext('user', 'tenant', + auth_token='token', roles=["a", "b"]) + + result = glance.generate_identity_headers(ctx, 'test') + + expected = { + 'X-Auth-Token': 'token', + 'X-User-Id': 'user', + 'X-Tenant-Id': 'tenant', + 'X-Roles': 'a,b', + 'X-Identity-Status': 'test', } - glance._glanceclient_from_endpoint(ctx, expected_endpoint, 2) - init_mock.assert_called_once_with('2', expected_endpoint, - **expected_params) - - # Test the version is properly passed to glanceclient. - init_mock.reset_mock() - - expected_endpoint = 'http://host4:9295' - expected_params = { - 'identity_headers': { - 'X-Auth-Token': 'token', - 'X-User-Id': 'fake', - 'X-Roles': '', - 'X-Tenant-Id': 'fake', - 'X-Identity-Status': 'Confirmed', - }, - 'global_request_id': mock.ANY - } - glance._glanceclient_from_endpoint(ctx, expected_endpoint, 2) - init_mock.assert_called_once_with('2', expected_endpoint, - **expected_params) - - # Test that the IPv6 bracketization adapts the endpoint properly. - init_mock.reset_mock() - - expected_endpoint = 'http://[host4]:9295' - glance._glanceclient_from_endpoint(ctx, expected_endpoint, 2) - init_mock.assert_called_once_with('2', expected_endpoint, - **expected_params) + self.assertDictEqual(expected, result) class TestGlanceClientWrapperRetries(test.NoDBTestCase): @@ -505,23 +505,6 @@ class TestGlanceClientWrapperRetries(test.NoDBTestCase): create_client_mock.return_value = client_mock -class TestGlanceClientWrapper(test.NoDBTestCase): - - @mock.patch('oslo_service.sslutils.is_enabled') - @mock.patch('glanceclient.Client') - def test_create_glance_client_with_ssl(self, client_mock, - ssl_enable_mock): - self.flags(ca_file='foo.cert', cert_file='bar.cert', - key_file='wut.key', group='ssl') - ctxt = mock.MagicMock() - glance._glanceclient_from_endpoint(ctxt, 'https://host4:9295', 2) - client_mock.assert_called_once_with( - '2', 'https://host4:9295', global_request_id=mock.ANY, - insecure=False, ssl_compression=False, - cert_file='bar.cert', key_file='wut.key', cacert='foo.cert', - identity_headers=mock.ANY) - - class TestDownloadNoDirectUri(test.NoDBTestCase): """Tests the download method of the GlanceImageServiceV2 when the diff --git a/releasenotes/notes/move-ssl-opts-to-glance-6553de9e773bccbc.yaml b/releasenotes/notes/move-ssl-opts-to-glance-6553de9e773bccbc.yaml new file mode 100644 index 000000000000..9675f8896d9a --- /dev/null +++ b/releasenotes/notes/move-ssl-opts-to-glance-6553de9e773bccbc.yaml @@ -0,0 +1,11 @@ +--- +upgrade: + - | + The ``ssl`` options were only used by Nova code that interacts with + Glance client. These options are now defined and read by Keystoneauth. + ``api_insecure`` option from glance group is renamed to ``insecure``. The + following ''ssl'' options are moved to ``glance`` group + + - ``ca_file`` now called ``cafile`` + - ``cert_file`` now called ``certfile`` + - ``key_file`` now called ``keyfile``