diff --git a/nova/image/glance.py b/nova/image/glance.py index 38508a12c644..9282a1545e2a 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -56,7 +56,7 @@ def _parse_image_ref(image_href): return (image_id, host, port, use_ssl) -def _create_glance_client(context, host, port, use_ssl): +def _create_glance_client(context, host, port, use_ssl, version=1): """Instantiate a new glanceclient.Client object""" if use_ssl: scheme = 'https' @@ -67,7 +67,7 @@ def _create_glance_client(context, host, port, use_ssl): if FLAGS.auth_strategy == 'keystone': params['token'] = context.auth_token endpoint = '%s://%s:%s' % (scheme, host, port) - return glanceclient.Client('1', endpoint, **params) + return glanceclient.Client(str(version), endpoint, **params) def get_api_servers(): @@ -92,31 +92,36 @@ def get_api_servers(): class GlanceClientWrapper(object): """Glance client wrapper class that implements retries.""" - def __init__(self, context=None, host=None, port=None, use_ssl=False): + def __init__(self, context=None, host=None, port=None, use_ssl=False, + version=1): if host is not None: self.client = self._create_static_client(context, - host, port, use_ssl) + host, port, + use_ssl, version) else: self.client = None self.api_servers = None - def _create_static_client(self, context, host, port, use_ssl): + def _create_static_client(self, context, host, port, use_ssl, version): """Create a client that we'll use for every call.""" self.host = host self.port = port self.use_ssl = use_ssl + self.version = version return _create_glance_client(context, - self.host, self.port, self.use_ssl) + self.host, self.port, + self.use_ssl, self.version) - def _create_onetime_client(self, context): + 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.host, self.port, self.use_ssl = self.api_servers.next() return _create_glance_client(context, - self.host, self.port, self.use_ssl) + self.host, self.port, + self.use_ssl, version) - def call(self, context, method, *args, **kwargs): + def call(self, context, version, method, *args, **kwargs): """ Call a glance client method. If we get a connection error, retry the request according to FLAGS.glance_num_retries. @@ -127,7 +132,8 @@ class GlanceClientWrapper(object): num_attempts = 1 + FLAGS.glance_num_retries for attempt in xrange(1, num_attempts + 1): - client = self.client or self._create_onetime_client(context) + client = self.client or self._create_onetime_client(context, + version) try: return getattr(client.images, method)(*args, **kwargs) except retry_excs as e: @@ -155,7 +161,7 @@ class GlanceImageService(object): """Calls out to Glance for a list of detailed image information.""" params = self._extract_query_params(kwargs) try: - images = self._client.call(context, 'list', **params) + images = self._client.call(context, 1, 'list', **params) except Exception: _reraise_translated_exception() @@ -184,7 +190,7 @@ class GlanceImageService(object): def show(self, context, image_id): """Returns a dict with image data for the given opaque image id.""" try: - image = self._client.call(context, 'get', image_id) + image = self._client.call(context, 1, 'get', image_id) except Exception: _reraise_translated_image_exception(image_id) @@ -194,10 +200,24 @@ class GlanceImageService(object): base_image_meta = self._translate_from_glance(image) return base_image_meta + def get_location(self, context, image_id): + """Returns the direct url representing the backend storage location, + or None if this attribute is not shown by Glance.""" + try: + client = GlanceClientWrapper() + image_meta = client.call(context, 2, 'get', image_id) + except Exception: + _reraise_translated_image_exception(image_id) + + if not self._is_image_available(context, image_meta): + raise exception.ImageNotFound(image_id=image_id) + + return getattr(image_meta, 'direct_url', None) + def download(self, context, image_id, data): """Calls out to Glance for metadata and data and writes data.""" try: - image_chunks = self._client.call(context, 'data', image_id) + image_chunks = self._client.call(context, 1, 'data', image_id) except Exception: _reraise_translated_image_exception(image_id) @@ -211,7 +231,7 @@ class GlanceImageService(object): if data: sent_service_image_meta['data'] = data - recv_service_image_meta = self._client.call(context, 'create', + recv_service_image_meta = self._client.call(context, 1, 'create', **sent_service_image_meta) return self._translate_from_glance(recv_service_image_meta) @@ -227,7 +247,7 @@ class GlanceImageService(object): if data: image_meta['data'] = data try: - image_meta = self._client.call(context, 'update', + image_meta = self._client.call(context, 1, 'update', image_id, **image_meta) except Exception: _reraise_translated_image_exception(image_id) @@ -242,7 +262,7 @@ class GlanceImageService(object): """ try: - self._client.call(context, 'delete', image_id) + self._client.call(context, 1, 'delete', image_id) except glanceclient.exc.NotFound: raise exception.ImageNotFound(image_id=image_id) return True diff --git a/nova/tests/image/fake.py b/nova/tests/image/fake.py index 47b10ce76f4b..fa5e770f8669 100644 --- a/nova/tests/image/fake.py +++ b/nova/tests/image/fake.py @@ -217,6 +217,10 @@ class _FakeImageService(object): if not removed: raise exception.ImageNotFound(image_id=image_id) + def get_location(self, context, image_id): + if image_id in self.images: + return 'fake_location' + return None _fakeImageService = _FakeImageService() diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py index 35b3648bb5a6..13e090cef1e4 100644 --- a/nova/tests/image/test_glance.py +++ b/nova/tests/image/test_glance.py @@ -105,7 +105,7 @@ class TestGlanceImageService(test.TestCase): self.context = context.RequestContext('fake', 'fake', auth_token=True) def _create_image_service(self, client): - def _fake_create_glance_client(context, host, port, use_ssl): + def _fake_create_glance_client(context, host, port, use_ssl, version): return client self.stubs.Set(glance, '_create_glance_client', @@ -569,7 +569,7 @@ class TestGlanceClientWrapper(test.TestCase): info = {'num_calls': 0} - def _fake_create_glance_client(context, host, port, use_ssl): + def _fake_create_glance_client(context, host, port, use_ssl, version): self.assertEqual(host, fake_host) self.assertEqual(port, fake_port) self.assertEqual(use_ssl, fake_use_ssl) @@ -581,7 +581,7 @@ class TestGlanceClientWrapper(test.TestCase): client = glance.GlanceClientWrapper(context=ctxt, host=fake_host, port=fake_port, use_ssl=fake_use_ssl) self.assertRaises(exception.GlanceConnectionFailed, - client.call, ctxt, 'get', 'meow') + client.call, ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 1) def test_default_client_without_retries(self): @@ -598,7 +598,7 @@ class TestGlanceClientWrapper(test.TestCase): def _fake_shuffle(servers): pass - def _fake_create_glance_client(context, host, port, use_ssl): + def _fake_create_glance_client(context, host, port, use_ssl, version): self.assertEqual(host, info['host']) self.assertEqual(port, info['port']) self.assertEqual(use_ssl, info['use_ssl']) @@ -611,7 +611,7 @@ class TestGlanceClientWrapper(test.TestCase): client = glance.GlanceClientWrapper() client2 = glance.GlanceClientWrapper() self.assertRaises(exception.GlanceConnectionFailed, - client.call, ctxt, 'get', 'meow') + client.call, ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 1) info = {'num_calls': 0, @@ -626,7 +626,7 @@ class TestGlanceClientWrapper(test.TestCase): self.stubs.Set(random, 'shuffle', _fake_shuffle2) self.assertRaises(exception.GlanceConnectionFailed, - client2.call, ctxt, 'get', 'meow') + client2.call, ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 1) def test_static_client_with_retries(self): @@ -639,7 +639,7 @@ class TestGlanceClientWrapper(test.TestCase): info = {'num_calls': 0} - def _fake_create_glance_client(context, host, port, use_ssl): + def _fake_create_glance_client(context, host, port, use_ssl, version): self.assertEqual(host, fake_host) self.assertEqual(port, fake_port) self.assertEqual(use_ssl, fake_use_ssl) @@ -650,7 +650,7 @@ class TestGlanceClientWrapper(test.TestCase): client = glance.GlanceClientWrapper(context=ctxt, host=fake_host, port=fake_port, use_ssl=fake_use_ssl) - client.call(ctxt, 'get', 'meow') + client.call(ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 2) def test_default_client_with_retries(self): @@ -670,7 +670,7 @@ class TestGlanceClientWrapper(test.TestCase): def _fake_shuffle(servers): pass - def _fake_create_glance_client(context, host, port, use_ssl): + def _fake_create_glance_client(context, host, port, use_ssl, version): attempt = info['num_calls'] self.assertEqual(host, info['host%s' % attempt]) self.assertEqual(port, info['port%s' % attempt]) @@ -683,7 +683,7 @@ class TestGlanceClientWrapper(test.TestCase): client = glance.GlanceClientWrapper() client2 = glance.GlanceClientWrapper() - client.call(ctxt, 'get', 'meow') + client.call(ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 2) def _fake_shuffle2(servers): @@ -700,5 +700,5 @@ class TestGlanceClientWrapper(test.TestCase): 'port1': 9294, 'use_ssl1': False} - client2.call(ctxt, 'get', 'meow') + client2.call(ctxt, 1, 'get', 'meow') self.assertEqual(info['num_calls'], 2)