1
From aa036a66f32ba137895852d9dc59086634483b92 Mon Sep 17 00:00:00 2001
2
From: Anton Arefiev <aarefiev@mirantis.com>
3
Date: Tue, 31 Mar 2015 19:44:57 +0300
4
Subject: [PATCH 2/2] Fix properties extracting from image with glance api v2
6
When a volume is created using the glance v1 API, the non based
7
properties places in 'properties' dict, and nova is expected
8
to find them here. Otherwise api's v2 return custom image properties
9
as base properties, so nova can't find them in volume 'image
12
This change add new glance call to get image schema, it allows
13
extract custom properties from image.
17
Change-Id: Ib54bb6759b27334294fb2c6d2c0bfe4eae3d0920
18
(cherry picked from commit f20643ca44e139e3384b9dea638b54c2235b1169)
20
cinder/image/glance.py | 59 +++++++++++++++++++++++++++++----------
21
cinder/tests/glance/stubs.py | 23 +++++++++++----
22
cinder/tests/image/test_glance.py | 21 ++------------
23
3 files changed, 65 insertions(+), 38 deletions(-)
25
diff --git a/cinder/image/glance.py b/cinder/image/glance.py
26
index 6d79b28..e7a48c1 100644
27
--- a/cinder/image/glance.py
28
+++ b/cinder/image/glance.py
29
@@ -142,9 +142,7 @@ class GlanceClientWrapper(object):
30
If we get a connection error,
31
retry the request according to CONF.glance_num_retries.
33
- version = self.version
34
- if version in kwargs:
35
- version = kwargs['version']
36
+ version = kwargs.pop('version', self.version)
38
retry_excs = (glanceclient.exc.ServiceUnavailable,
39
glanceclient.exc.InvalidEndpoint,
40
@@ -155,7 +153,9 @@ class GlanceClientWrapper(object):
41
client = self.client or self._create_onetime_client(context,
44
- return getattr(client.images, method)(*args, **kwargs)
45
+ controller = getattr(client,
46
+ kwargs.pop('controller', 'images'))
47
+ return getattr(controller, method)(*args, **kwargs)
48
except retry_excs as e:
51
@@ -184,6 +184,7 @@ class GlanceImageService(object):
53
def __init__(self, client=None):
54
self._client = client or GlanceClientWrapper()
55
+ self._image_schema = None
57
def detail(self, context, **kwargs):
58
"""Calls out to Glance for a list of detailed image information."""
59
@@ -196,7 +197,7 @@ class GlanceImageService(object):
62
if self._is_image_available(context, image):
63
- _images.append(self._translate_from_glance(image))
64
+ _images.append(self._translate_from_glance(context, image))
68
@@ -225,7 +226,7 @@ class GlanceImageService(object):
69
if not self._is_image_available(context, image):
70
raise exception.ImageNotFound(image_id=image_id)
72
- base_image_meta = self._translate_from_glance(image)
73
+ base_image_meta = self._translate_from_glance(context, image)
74
return base_image_meta
76
def get_location(self, context, image_id):
77
@@ -285,7 +286,7 @@ class GlanceImageService(object):
78
recv_service_image_meta = self._client.call(context, 'create',
79
**sent_service_image_meta)
81
- return self._translate_from_glance(recv_service_image_meta)
82
+ return self._translate_from_glance(context, recv_service_image_meta)
84
def update(self, context, image_id,
85
image_meta, data=None, purge_props=True):
86
@@ -310,7 +311,7 @@ class GlanceImageService(object):
88
_reraise_translated_image_exception(image_id)
90
- return self._translate_from_glance(image_meta)
91
+ return self._translate_from_glance(context, image_meta)
93
def delete(self, context, image_id):
94
"""Delete the given image.
95
@@ -325,6 +326,41 @@ class GlanceImageService(object):
96
raise exception.ImageNotFound(image_id=image_id)
99
+ def _translate_from_glance(self, context, image):
100
+ """Get image metadata from glance image.
102
+ Extract metadata from image and convert it's properties
103
+ to type cinder expected.
105
+ :param image: glance image object
106
+ :return: image metadata dictionary
108
+ if CONF.glance_api_version == 2:
109
+ if self._image_schema is None:
110
+ self._image_schema = self._client.call(context, 'get',
111
+ controller='schemas',
112
+ schema_name='image',
114
+ # NOTE(aarefiev): get base image property, store image 'schema'
115
+ # is redundant, so ignore it.
116
+ image_meta = {key: getattr(image, key)
117
+ for key in image.keys()
118
+ if self._image_schema.is_base_property(key) is True
119
+ and key != 'schema'}
121
+ # NOTE(aarefiev): nova is expected that all image properties
122
+ # (custom or defined in schema-image.json) stores in
123
+ # 'properties' key.
124
+ image_meta['properties'] = {
125
+ key: getattr(image, key) for key in image.keys()
126
+ if self._image_schema.is_base_property(key) is False}
128
+ image_meta = _extract_attributes(image)
130
+ image_meta = _convert_timestamps_to_datetimes(image_meta)
131
+ image_meta = _convert_from_string(image_meta)
135
def _translate_to_glance(image_meta):
136
image_meta = _convert_to_string(image_meta)
137
@@ -332,13 +368,6 @@ class GlanceImageService(object):
141
- def _translate_from_glance(image):
142
- image_meta = _extract_attributes(image)
143
- image_meta = _convert_timestamps_to_datetimes(image_meta)
144
- image_meta = _convert_from_string(image_meta)
148
def _is_image_available(context, image):
149
"""Check image availability.
151
diff --git a/cinder/tests/glance/stubs.py b/cinder/tests/glance/stubs.py
152
index 9a1bbdd..7ad45d0 100644
153
--- a/cinder/tests/glance/stubs.py
154
+++ b/cinder/tests/glance/stubs.py
155
@@ -18,6 +18,13 @@ import glanceclient.exc
156
NOW_GLANCE_FORMAT = "2010-10-11T10:30:22"
159
+IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner',
160
+ 'container_format', 'checksum', 'id',
161
+ 'name', 'created_at', 'updated_at',
162
+ 'deleted', 'status',
163
+ 'min_disk', 'min_ram', 'is_public']
166
class StubGlanceClient(object):
168
def __init__(self, images=None):
169
@@ -88,11 +95,6 @@ class StubGlanceClient(object):
171
class FakeImage(object):
172
def __init__(self, metadata):
173
- IMAGE_ATTRIBUTES = ['size', 'disk_format', 'owner',
174
- 'container_format', 'checksum', 'id',
175
- 'name', 'created_at', 'updated_at',
176
- 'deleted', 'status',
177
- 'min_disk', 'min_ram', 'is_public']
178
raw = dict.fromkeys(IMAGE_ATTRIBUTES)
180
self.__dict__['raw'] = raw
181
@@ -108,3 +110,14 @@ class FakeImage(object):
182
self.__dict__['raw'][key] = value
184
raise AttributeError(key)
187
+ return self.__dict__['raw'].keys()
190
+class FakeSchema(object):
191
+ def is_base_property(self, key):
192
+ if key in IMAGE_ATTRIBUTES:
196
diff --git a/cinder/tests/image/test_glance.py b/cinder/tests/image/test_glance.py
197
index 1333f77..7817b90 100644
198
--- a/cinder/tests/image/test_glance.py
199
+++ b/cinder/tests/image/test_glance.py
200
@@ -590,10 +590,6 @@ class TestGlanceImageService(test.TestCase):
202
config.glance_api_version = 2
204
- attributes = ['size', 'disk_format', 'owner', 'container_format',
205
- 'checksum', 'id', 'name', 'created_at', 'updated_at',
206
- 'deleted', 'status', 'min_disk', 'min_ram', 'is_public']
211
@@ -603,23 +599,13 @@ class TestGlanceImageService(test.TestCase):
215
- class FakeSchema(object):
217
- def __init__(self, base):
220
- def is_base_property(self, key):
221
- if key in self.base:
226
image = glance_stubs.FakeImage(metadata)
227
client = glance_stubs.StubGlanceClient()
229
service = self._create_image_service(client)
230
- service._image_schema = FakeSchema(attributes)
231
- actual = service._translate_from_glance(image)
232
+ service._image_schema = glance_stubs.FakeSchema()
234
+ actual = service._translate_from_glance('fake_context', image)
238
@@ -631,7 +617,6 @@ class TestGlanceImageService(test.TestCase):
239
'container_format': None,
242
- 'deleted_at': None,
244
'properties': {'kernel_id': 'foo',
245
'ramdisk_id': 'bar'},