diff --git a/cinder/common/config.py b/cinder/common/config.py index 24ee23d0bd7..2342f23237b 100644 --- a/cinder/common/config.py +++ b/cinder/common/config.py @@ -54,15 +54,8 @@ global_opts = [ cfg.StrOpt('my_ip', default=netutils.get_my_ipv4(), help='IP address of this host'), - cfg.StrOpt('glance_host', - default='$my_ip', - help='Default glance host name or IP'), - cfg.IntOpt('glance_port', - default=9292, - min=1, max=65535, - help='Default glance port'), cfg.ListOpt('glance_api_servers', - default=['$glance_host:$glance_port'], + default=None, help='A list of the URLs of glance API servers available to ' 'cinder ([http[s]://][hostname|ip]:port). If protocol ' 'is not specified it defaults to http.'), diff --git a/cinder/context.py b/cinder/context.py index e2f62797116..abd34cdfb71 100644 --- a/cinder/context.py +++ b/cinder/context.py @@ -92,7 +92,8 @@ class RequestContext(context.RequestContext): # Only include required parts of service_catalog self.service_catalog = [s for s in service_catalog if s.get('type') in - ('identity', 'compute', 'object-store')] + ('identity', 'compute', 'object-store', + 'image')] else: # if list is empty or none self.service_catalog = [] diff --git a/cinder/image/glance.py b/cinder/image/glance.py index bb5810fb559..5d483253be0 100644 --- a/cinder/image/glance.py +++ b/cinder/image/glance.py @@ -36,7 +36,7 @@ from six.moves import range from six.moves import urllib from cinder import exception -from cinder.i18n import _LE, _LW +from cinder.i18n import _, _LE, _LW glance_opts = [ @@ -45,6 +45,12 @@ glance_opts = [ help='A list of url schemes that can be downloaded directly ' 'via the direct_url. Currently supported schemes: ' '[file].'), + cfg.StrOpt('glance_catalog_info', + default='image:glance:publicURL', + help='Info to match when looking for glance in the service ' + 'catalog. Format is: separated values of the form: ' + ':: - ' + 'Only used if glance_api_servers are not provided.'), ] glance_core_properties_opts = [ cfg.ListOpt('glance_core_properties', @@ -97,23 +103,44 @@ def _create_glance_client(context, netloc, use_ssl, version=None): return glanceclient.Client(str(version), endpoint, **params) -def get_api_servers(): +def get_api_servers(context): """Return Iterable over shuffled api servers. - Shuffle a list of CONF.glance_api_servers and return an iterator + Shuffle a list of glance_api_servers and return an iterator that will cycle through the list, looping around to the beginning - if necessary. + if necessary. If CONF.glance_api_servers is None then they will + be retrieved from the catalog. """ api_servers = [] - for api_server in CONF.glance_api_servers: + api_servers_info = [] + + if CONF.glance_api_servers is None: + info = CONF.glance_catalog_info + try: + service_type, service_name, endpoint_type = info.split(':') + except ValueError: + raise exception.InvalidConfigurationValue(_( + "Failed to parse the configuration option " + "'glance_catalog_info', must be in the form " + "::")) + for entry in context.service_catalog: + if entry.get('type') == service_type: + api_servers.append( + entry.get('endpoints')[0].get(endpoint_type)) + else: + for api_server in CONF.glance_api_servers: + api_servers.append(api_server) + + for api_server in api_servers: if '//' not in api_server: api_server = 'http://' + api_server url = urllib.parse.urlparse(api_server) netloc = url.netloc use_ssl = (url.scheme == 'https') - api_servers.append((netloc, use_ssl)) - random.shuffle(api_servers) - return itertools.cycle(api_servers) + api_servers_info.append((netloc, use_ssl)) + + random.shuffle(api_servers_info) + return itertools.cycle(api_servers_info) class GlanceClientWrapper(object): @@ -149,7 +176,7 @@ class GlanceClientWrapper(object): def _create_onetime_client(self, context, version): """Create a client that will be used for one call.""" if self.api_servers is None: - self.api_servers = get_api_servers() + self.api_servers = get_api_servers(context) self.netloc, self.use_ssl = next(self.api_servers) return _create_glance_client(context, self.netloc, diff --git a/cinder/tests/unit/image/test_glance.py b/cinder/tests/unit/image/test_glance.py index f078e6143b7..0b83aba29f9 100644 --- a/cinder/tests/unit/image/test_glance.py +++ b/cinder/tests/unit/image/test_glance.py @@ -15,6 +15,7 @@ import datetime +import itertools import glanceclient.exc import mock @@ -94,8 +95,12 @@ class TestGlanceImageService(test.TestCase): super(TestGlanceImageService, self).setUp() client = glance_stubs.StubGlanceClient() + service_catalog = [{u'type': u'image', u'name': u'glance', + u'endpoints': [{ + u'publicURL': u'http://example.com:9292'}]}] self.service = self._create_image_service(client) self.context = context.RequestContext('fake', 'fake', auth_token=True) + self.context.service_catalog = service_catalog self.stubs.Set(glance.time, 'sleep', lambda s: None) def _create_image_service(self, client): @@ -123,6 +128,11 @@ class TestGlanceImageService(test.TestCase): updated_at=self.NOW_GLANCE_FORMAT, deleted_at=self.NOW_GLANCE_FORMAT) + def test_get_api_servers(self): + result = glance.get_api_servers(self.context) + expected = (u'example.com:9292', False) + self.assertEqual(expected, next(result)) + def test_create_with_instance_id(self): """Ensure instance_id is persisted as an image-property.""" fixture = {'name': 'test image', @@ -533,7 +543,10 @@ class TestGlanceImageService(test.TestCase): @mock.patch('six.moves.builtins.open') @mock.patch('shutil.copyfileobj') - def test_download_from_direct_file(self, mock_copyfileobj, mock_open): + @mock.patch('cinder.image.glance.get_api_servers', + return_value=itertools.cycle([(False, 'localhost:9292')])) + def test_download_from_direct_file(self, api_servers, + mock_copyfileobj, mock_open): fixture = self._make_fixture(name='test image', locations=[{'url': 'file:///tmp/test'}]) image_id = self.service.create(self.context, fixture)['id'] @@ -545,7 +558,9 @@ class TestGlanceImageService(test.TestCase): @mock.patch('six.moves.builtins.open') @mock.patch('shutil.copyfileobj') - def test_download_from_direct_file_non_file(self, + @mock.patch('cinder.image.glance.get_api_servers', + return_value=itertools.cycle([(False, 'localhost:9292')])) + def test_download_from_direct_file_non_file(self, api_servers, mock_copyfileobj, mock_open): fixture = self._make_fixture(name='test image', direct_url='swift+http://test/image') @@ -734,7 +749,9 @@ class TestGlanceClientVersion(test.TestCase): self.assertEqual('2', _mockglanceclient.call_args[0][0]) @mock.patch('cinder.image.glance.glanceclient.Client') - def test_call_glance_version_by_arg(self, _mockglanceclient): + @mock.patch('cinder.image.glance.get_api_servers', + return_value=itertools.cycle([(False, 'localhost:9292')])) + def test_call_glance_version_by_arg(self, api_servers, _mockglanceclient): """Test glance version set by arg to GlanceClientWrapper""" glance_wrapper = glance.GlanceClientWrapper() glance_wrapper.call('fake_context', 'method', version=2) diff --git a/cinder/tests/unit/test_context.py b/cinder/tests/unit/test_context.py index c39c03d83aa..66173b8996e 100644 --- a/cinder/tests/unit/test_context.py +++ b/cinder/tests/unit/test_context.py @@ -82,7 +82,7 @@ class ContextTestCase(test.TestCase): object_catalog = [{u'name': u'swift', u'type': u'object-store'}] ctxt = context.RequestContext('111', '222', service_catalog=service_catalog) - self.assertEqual(3, len(ctxt.service_catalog)) + self.assertEqual(4, len(ctxt.service_catalog)) return_compute = [v for v in ctxt.service_catalog if v['type'] == u'compute'] return_object = [v for v in ctxt.service_catalog if