Merge lp:~rackspace-titan/nova/fix_list_v11_snapshot_images into lp:~hudson-openstack/nova/trunk

Proposed by Dan Prince
Status: Merged
Approved by: Brian Waldon
Approved revision: 1533
Merged at revision: 1570
Proposed branch: lp:~rackspace-titan/nova/fix_list_v11_snapshot_images
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 313 lines (+73/-57)
4 files modified
nova/api/openstack/common.py (+5/-19)
nova/api/openstack/views/images.py (+10/-0)
nova/tests/api/openstack/test_common.py (+33/-13)
nova/tests/api/openstack/test_images.py (+25/-25)
To merge this branch: bzr merge lp:~rackspace-titan/nova/fix_list_v11_snapshot_images
Reviewer Review Type Date Requested Status
Brian Waldon (community) Approve
Brian Lamar (community) Approve
Review via email: mp+74285@code.launchpad.net

Description of the change

Fixes an issue where 'invalid literal for int' would occur when listing images after making a v1.1 server snapshot (with a UUID).

v1.1 image id's are now treated as strings (not integer ID's). The v1.0 API still tries to treat image id's as integers but doesn't fail miserably if they are uuid's either.

This should pave the way for image ID's as uuids and more closely matches the v1.1 spec with regards to images and the server refs they contain.

To post a comment you must log in.
Revision history for this message
Brian Lamar (blamar) wrote :

36 + return urlparse.urlsplit("%s" % href).path.split('/')[-1]

Good stuff, I actually like this strategy much better than the one I implemented/advocated for which first attempts to cast to an integer and then tries to parse a URL.

Thanks!

review: Approve
Revision history for this message
Brian Waldon (bcwaldon) wrote :

60: Does this mean that if we switch to uuids for images we will not be able to communicate that through the v1.0 api?

review: Needs Information
Revision history for this message
Dan Prince (dan-prince) wrote :

The v1.0 SPEC doesn't "officially" allow for UUIDs. We have taken the middle ground on UUID's for zones support etc. however in nova. So v1.0 as far as this code goes v1.0 tries to give you an integer (because that is what the SPEC says). If you are using v1.0 and there is a UUID present it will give you a JSON string UUID.

Revision history for this message
Brian Waldon (bcwaldon) wrote :

Okay, I think I'm okay with this behavior. You do have one merge conflict.

Revision history for this message
Dan Prince (dan-prince) wrote :

Okay after 2 rounds of conflicts related to the image tests this should be ready again. Incidentally this branch also fixes an issue that crept into nova trunk recently where v1.0 image ID's are getting incorrectly getting returned as strings again. I've updated the v1.0 tests to use integer IDs again.

Revision history for this message
Brian Waldon (bcwaldon) wrote :

Looks good, Dan.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/openstack/common.py'
2--- nova/api/openstack/common.py 2011-09-02 05:39:31 +0000
3+++ nova/api/openstack/common.py 2011-09-14 15:37:50 +0000
4@@ -187,30 +187,16 @@
5
6
7 def get_id_from_href(href):
8- """Return the id portion of a url as an int.
9+ """Return the id or uuid portion of a url.
10
11 Given: 'http://www.foo.com/bar/123?q=4'
12- Returns: 123
13+ Returns: '123'
14
15- In order to support local hrefs, the href argument can be just an id:
16- Given: '123'
17- Returns: 123
18+ Given: 'http://www.foo.com/bar/abc123?q=4'
19+ Returns: 'abc123'
20
21 """
22- LOG.debug(_("Attempting to treat %(href)s as an integer ID.") % locals())
23-
24- try:
25- return int(href)
26- except ValueError:
27- pass
28-
29- LOG.debug(_("Attempting to treat %(href)s as a URL.") % locals())
30-
31- try:
32- return int(urlparse.urlsplit(href).path.split('/')[-1])
33- except ValueError as error:
34- LOG.debug(_("Failed to parse ID from %(href)s: %(error)s") % locals())
35- raise
36+ return urlparse.urlsplit("%s" % href).path.split('/')[-1]
37
38
39 def remove_version_from_href(href):
40
41=== modified file 'nova/api/openstack/views/images.py'
42--- nova/api/openstack/views/images.py 2011-09-09 21:25:45 +0000
43+++ nova/api/openstack/views/images.py 2011-09-14 15:37:50 +0000
44@@ -71,6 +71,7 @@
45 }
46
47 self._build_server(image, image_obj)
48+ self._build_image_id(image, image_obj)
49
50 if detail:
51 image.update({
52@@ -96,6 +97,12 @@
53 except (KeyError, ValueError):
54 pass
55
56+ def _build_image_id(self, image, image_obj):
57+ try:
58+ image['id'] = int(image_obj['id'])
59+ except ValueError:
60+ pass
61+
62
63 class ViewBuilderV11(ViewBuilder):
64 """OpenStack API v1.1 Image Builder"""
65@@ -119,6 +126,9 @@
66 except KeyError:
67 return
68
69+ def _build_image_id(self, image, image_obj):
70+ image['id'] = "%s" % image_obj['id']
71+
72 def generate_href(self, image_id):
73 """Return an href string pointing to this object."""
74 return os.path.join(self.base_url, self.project_id,
75
76=== modified file 'nova/tests/api/openstack/test_common.py'
77--- nova/tests/api/openstack/test_common.py 2011-08-25 20:35:04 +0000
78+++ nova/tests/api/openstack/test_common.py 2011-09-14 15:37:50 +0000
79@@ -243,21 +243,41 @@
80 common.remove_version_from_href,
81 fixture)
82
83- def test_get_id_from_href(self):
84+ def test_get_id_from_href_with_int_url(self):
85 fixture = 'http://www.testsite.com/dir/45'
86 actual = common.get_id_from_href(fixture)
87- expected = 45
88- self.assertEqual(actual, expected)
89-
90- def test_get_id_from_href_bad_request(self):
91- fixture = 'http://45'
92- self.assertRaises(ValueError,
93- common.get_id_from_href,
94- fixture)
95-
96- def test_get_id_from_href_int(self):
97- fixture = 1
98- self.assertEqual(fixture, common.get_id_from_href(fixture))
99+ expected = '45'
100+ self.assertEqual(actual, expected)
101+
102+ def test_get_id_from_href_with_int(self):
103+ fixture = '45'
104+ actual = common.get_id_from_href(fixture)
105+ expected = '45'
106+ self.assertEqual(actual, expected)
107+
108+ def test_get_id_from_href_with_int_url_query(self):
109+ fixture = 'http://www.testsite.com/dir/45?asdf=jkl'
110+ actual = common.get_id_from_href(fixture)
111+ expected = '45'
112+ self.assertEqual(actual, expected)
113+
114+ def test_get_id_from_href_with_uuid_url(self):
115+ fixture = 'http://www.testsite.com/dir/abc123'
116+ actual = common.get_id_from_href(fixture)
117+ expected = "abc123"
118+ self.assertEqual(actual, expected)
119+
120+ def test_get_id_from_href_with_uuid_url_query(self):
121+ fixture = 'http://www.testsite.com/dir/abc123?asdf=jkl'
122+ actual = common.get_id_from_href(fixture)
123+ expected = "abc123"
124+ self.assertEqual(actual, expected)
125+
126+ def test_get_id_from_href_with_uuid(self):
127+ fixture = 'abc123'
128+ actual = common.get_id_from_href(fixture)
129+ expected = 'abc123'
130+ self.assertEqual(actual, expected)
131
132 def test_get_version_from_href(self):
133 fixture = 'http://www.testsite.com/v1.1/images'
134
135=== modified file 'nova/tests/api/openstack/test_images.py'
136--- nova/tests/api/openstack/test_images.py 2011-09-13 18:00:20 +0000
137+++ nova/tests/api/openstack/test_images.py 2011-09-14 15:37:50 +0000
138@@ -77,14 +77,14 @@
139 response_dict = json.loads(response.body)
140 response_list = response_dict["images"]
141
142- expected = [{'id': '123', 'name': 'public image'},
143- {'id': '124', 'name': 'queued snapshot'},
144- {'id': '125', 'name': 'saving snapshot'},
145- {'id': '126', 'name': 'active snapshot'},
146- {'id': '127', 'name': 'killed snapshot'},
147- {'id': '128', 'name': 'deleted snapshot'},
148- {'id': '129', 'name': 'pending_delete snapshot'},
149- {'id': '130', 'name': None}]
150+ expected = [{'id': 123, 'name': 'public image'},
151+ {'id': 124, 'name': 'queued snapshot'},
152+ {'id': 125, 'name': 'saving snapshot'},
153+ {'id': 126, 'name': 'active snapshot'},
154+ {'id': 127, 'name': 'killed snapshot'},
155+ {'id': 128, 'name': 'deleted snapshot'},
156+ {'id': 129, 'name': 'pending_delete snapshot'},
157+ {'id': 130, 'name': None}]
158
159 self.assertDictListMatch(response_list, expected)
160
161@@ -99,7 +99,7 @@
162
163 expected_image = {
164 "image": {
165- "id": "123",
166+ "id": 123,
167 "name": "public image",
168 "updated": NOW_API_FORMAT,
169 "created": NOW_API_FORMAT,
170@@ -131,7 +131,7 @@
171 "status": "SAVING",
172 "progress": 0,
173 'server': {
174- 'id': 42,
175+ 'id': '42',
176 "links": [{
177 "rel": "self",
178 "href": server_href,
179@@ -406,7 +406,7 @@
180 response_list = response_dict["images"]
181
182 expected = [{
183- 'id': '123',
184+ 'id': 123,
185 'name': 'public image',
186 'updated': NOW_API_FORMAT,
187 'created': NOW_API_FORMAT,
188@@ -414,7 +414,7 @@
189 'progress': 100,
190 },
191 {
192- 'id': '124',
193+ 'id': 124,
194 'name': 'queued snapshot',
195 'updated': NOW_API_FORMAT,
196 'created': NOW_API_FORMAT,
197@@ -422,7 +422,7 @@
198 'progress': 0,
199 },
200 {
201- 'id': '125',
202+ 'id': 125,
203 'name': 'saving snapshot',
204 'updated': NOW_API_FORMAT,
205 'created': NOW_API_FORMAT,
206@@ -430,7 +430,7 @@
207 'progress': 0,
208 },
209 {
210- 'id': '126',
211+ 'id': 126,
212 'name': 'active snapshot',
213 'updated': NOW_API_FORMAT,
214 'created': NOW_API_FORMAT,
215@@ -438,7 +438,7 @@
216 'progress': 100,
217 },
218 {
219- 'id': '127',
220+ 'id': 127,
221 'name': 'killed snapshot',
222 'updated': NOW_API_FORMAT,
223 'created': NOW_API_FORMAT,
224@@ -446,7 +446,7 @@
225 'progress': 0,
226 },
227 {
228- 'id': '128',
229+ 'id': 128,
230 'name': 'deleted snapshot',
231 'updated': NOW_API_FORMAT,
232 'created': NOW_API_FORMAT,
233@@ -454,7 +454,7 @@
234 'progress': 0,
235 },
236 {
237- 'id': '129',
238+ 'id': 129,
239 'name': 'pending_delete snapshot',
240 'updated': NOW_API_FORMAT,
241 'created': NOW_API_FORMAT,
242@@ -462,7 +462,7 @@
243 'progress': 0,
244 },
245 {
246- 'id': '130',
247+ 'id': 130,
248 'name': None,
249 'updated': NOW_API_FORMAT,
250 'created': NOW_API_FORMAT,
251@@ -511,7 +511,7 @@
252 'status': 'SAVING',
253 'progress': 0,
254 'server': {
255- 'id': 42,
256+ 'id': '42',
257 "links": [{
258 "rel": "self",
259 "href": server_href,
260@@ -542,7 +542,7 @@
261 'status': 'SAVING',
262 'progress': 0,
263 'server': {
264- 'id': 42,
265+ 'id': '42',
266 "links": [{
267 "rel": "self",
268 "href": server_href,
269@@ -573,7 +573,7 @@
270 'status': 'ACTIVE',
271 'progress': 100,
272 'server': {
273- 'id': 42,
274+ 'id': '42',
275 "links": [{
276 "rel": "self",
277 "href": server_href,
278@@ -604,7 +604,7 @@
279 'status': 'ERROR',
280 'progress': 0,
281 'server': {
282- 'id': 42,
283+ 'id': '42',
284 "links": [{
285 "rel": "self",
286 "href": server_href,
287@@ -635,7 +635,7 @@
288 'status': 'DELETED',
289 'progress': 0,
290 'server': {
291- 'id': 42,
292+ 'id': '42',
293 "links": [{
294 "rel": "self",
295 "href": server_href,
296@@ -666,7 +666,7 @@
297 'status': 'DELETED',
298 'progress': 0,
299 'server': {
300- 'id': 42,
301+ 'id': '42',
302 "links": [{
303 "rel": "self",
304 "href": server_href,
305@@ -914,7 +914,7 @@
306 app = fakes.wsgi_app(fake_auth_context=self._get_fake_context())
307 res = req.get_response(app)
308 image_meta = json.loads(res.body)['image']
309- expected = {'id': '123', 'name': 'public image',
310+ expected = {'id': 123, 'name': 'public image',
311 'updated': NOW_API_FORMAT,
312 'created': NOW_API_FORMAT, 'status': 'ACTIVE',
313 'progress': 100}