Merge lp:~rackspace-titan/nova/images-metadata-container-lp795685 into lp:~hudson-openstack/nova/trunk

Proposed by Brian Waldon
Status: Merged
Approved by: Dan Prince
Approved revision: 1217
Merged at revision: 1231
Proposed branch: lp:~rackspace-titan/nova/images-metadata-container-lp795685
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 1025 lines (+629/-181)
5 files modified
nova/api/openstack/image_metadata.py (+10/-8)
nova/api/openstack/images.py (+71/-13)
nova/api/openstack/views/images.py (+3/-0)
nova/tests/api/openstack/test_image_metadata.py (+133/-89)
nova/tests/api/openstack/test_images.py (+412/-71)
To merge this branch: bzr merge lp:~rackspace-titan/nova/images-metadata-container-lp795685
Reviewer Review Type Date Requested Status
Dan Prince (community) Approve
Ed Leafe (community) Approve
Alex Meade (community) Approve
Brian Waldon Pending
Review via email: mp+65719@code.launchpad.net

Description of the change

- add metadata container to /images/detail and /images/<id> responses
- update xml serialization to encode image entities properly

To post a comment you must log in.
Revision history for this message
Alex Meade (alex-meade) wrote :

Hey it all looks great. I do think there should be some negative test cases though.

review: Needs Fixing
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Syntax looks good, but as I am not as versed in the OSAPI i'm requesting a review from one who is :)

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

> Syntax looks good, but as I am not as versed in the OSAPI i'm requesting a
> review from one who is :)

I'm flattered that you would ask me to review this, but I probably shouldn't as I am the one who proposed it.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Haha, oops

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

Alex: I added a test for serializing zero images, an image with zero metadata items, and a malformed image (no metadata dict). I accidentally merged trunk and added the tests in the same commit, so it may be hard to read individual revisions.

Revision history for this message
Alex Meade (alex-meade) wrote :

Looks good Waldon

review: Approve
Revision history for this message
Ed Leafe (ed-leafe) wrote :

Line 17: are you certain that 'value' will never contain non-ASCII characters? If not, using str() will cause an error without an explicit encoding.

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

I think it is pretty safe to assume we will always have ascii characters, as the values will have been added through the api. I did add some more testing, though.

Revision history for this message
Ed Leafe (ed-leafe) wrote :

lgtm with the encoding changes.

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

Brian,

Looks good. I'm getting the following conflicts with nova trunk:

Text conflict in nova/api/openstack/images.py
Text conflict in nova/tests/api/openstack/test_images.py

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

> Brian,
>
> Looks good. I'm getting the following conflicts with nova trunk:
>
> Text conflict in nova/api/openstack/images.py
> Text conflict in nova/tests/api/openstack/test_images.py

Oops. Disregard. Wrong branch.

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

Looks like test_images actually does have a conflict:

Text conflict in nova/tests/api/openstack/test_images.py

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

Thanks for catching that, Dan. It's good now.

Revision history for this message
Dan Prince (dan-prince) :
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/image_metadata.py'
2--- nova/api/openstack/image_metadata.py 2011-06-24 12:01:51 +0000
3+++ nova/api/openstack/image_metadata.py 2011-06-30 12:56:35 +0000
4@@ -112,18 +112,18 @@
5
6
7 class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
8- def __init__(self):
9- xmlns = wsgi.XMLNS_V11
10+ def __init__(self, xmlns=wsgi.XMLNS_V11):
11 super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns)
12
13 def _meta_item_to_xml(self, doc, key, value):
14 node = doc.createElement('meta')
15- node.setAttribute('key', key)
16- text = doc.createTextNode(value)
17+ doc.appendChild(node)
18+ node.setAttribute('key', '%s' % key)
19+ text = doc.createTextNode('%s' % value)
20 node.appendChild(text)
21 return node
22
23- def _meta_list_to_xml(self, xml_doc, meta_items):
24+ def meta_list_to_xml(self, xml_doc, meta_items):
25 container_node = xml_doc.createElement('metadata')
26 for (key, value) in meta_items:
27 item_node = self._meta_item_to_xml(xml_doc, key, value)
28@@ -133,9 +133,10 @@
29 def _meta_list_to_xml_string(self, metadata_dict):
30 xml_doc = minidom.Document()
31 items = metadata_dict['metadata'].items()
32- container_node = self._meta_list_to_xml(xml_doc, items)
33+ container_node = self.meta_list_to_xml(xml_doc, items)
34+ xml_doc.appendChild(container_node)
35 self._add_xmlns(container_node)
36- return container_node.toprettyxml(indent=' ')
37+ return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
38
39 def index(self, metadata_dict):
40 return self._meta_list_to_xml_string(metadata_dict)
41@@ -147,8 +148,9 @@
42 xml_doc = minidom.Document()
43 item_key, item_value = meta_item_dict.items()[0]
44 item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
45+ xml_doc.appendChild(item_node)
46 self._add_xmlns(item_node)
47- return item_node.toprettyxml(indent=' ')
48+ return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
49
50 def show(self, meta_item_dict):
51 return self._meta_item_to_xml_string(meta_item_dict['meta'])
52
53=== modified file 'nova/api/openstack/images.py'
54--- nova/api/openstack/images.py 2011-06-29 19:24:32 +0000
55+++ nova/api/openstack/images.py 2011-06-30 12:56:35 +0000
56@@ -16,6 +16,7 @@
57 import os.path
58
59 import webob.exc
60+from xml.dom import minidom
61
62 from nova import compute
63 from nova import exception
64@@ -25,6 +26,7 @@
65 from nova import utils
66 from nova.api.openstack import common
67 from nova.api.openstack import faults
68+from nova.api.openstack import image_metadata
69 from nova.api.openstack.views import images as images_view
70 from nova.api.openstack import wsgi
71
72@@ -260,28 +262,84 @@
73 return {'instance_ref': server_ref}
74
75
76+class ImageXMLSerializer(wsgi.XMLDictSerializer):
77+
78+ metadata = {
79+ "attributes": {
80+ "image": ["id", "name", "updated", "created", "status",
81+ "serverId", "progress", "serverRef"],
82+ "link": ["rel", "type", "href"],
83+ },
84+ }
85+
86+ xmlns = wsgi.XMLNS_V11
87+
88+ def __init__(self):
89+ self.metadata_serializer = image_metadata.ImageMetadataXMLSerializer()
90+
91+ def _image_to_xml(self, xml_doc, image):
92+ try:
93+ metadata = image.pop('metadata').items()
94+ except Exception:
95+ LOG.debug(_("Image object missing metadata attribute"))
96+ metadata = {}
97+
98+ node = self._to_xml_node(xml_doc, self.metadata, 'image', image)
99+ metadata_node = self.metadata_serializer.meta_list_to_xml(xml_doc,
100+ metadata)
101+ node.appendChild(metadata_node)
102+ return node
103+
104+ def _image_list_to_xml(self, xml_doc, images):
105+ container_node = xml_doc.createElement('images')
106+ for image in images:
107+ item_node = self._image_to_xml(xml_doc, image)
108+ container_node.appendChild(item_node)
109+ return container_node
110+
111+ def _image_to_xml_string(self, image):
112+ xml_doc = minidom.Document()
113+ item_node = self._image_to_xml(xml_doc, image)
114+ self._add_xmlns(item_node)
115+ return item_node.toprettyxml(indent=' ')
116+
117+ def _image_list_to_xml_string(self, images):
118+ xml_doc = minidom.Document()
119+ container_node = self._image_list_to_xml(xml_doc, images)
120+ self._add_xmlns(container_node)
121+ return container_node.toprettyxml(indent=' ')
122+
123+ def detail(self, images_dict):
124+ return self._image_list_to_xml_string(images_dict['images'])
125+
126+ def show(self, image_dict):
127+ return self._image_to_xml_string(image_dict['image'])
128+
129+ def create(self, image_dict):
130+ return self._image_to_xml_string(image_dict['image'])
131+
132+
133 def create_resource(version='1.0'):
134 controller = {
135 '1.0': ControllerV10,
136 '1.1': ControllerV11,
137 }[version]()
138
139- xmlns = {
140- '1.0': wsgi.XMLNS_V10,
141- '1.1': wsgi.XMLNS_V11,
142+ metadata = {
143+ "attributes": {
144+ "image": ["id", "name", "updated", "created", "status",
145+ "serverId", "progress", "serverRef"],
146+ "link": ["rel", "type", "href"],
147+ },
148+ }
149+
150+ xml_serializer = {
151+ '1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
152+ '1.1': ImageXMLSerializer(),
153 }[version]
154
155- metadata = {
156- "attributes": {
157- "image": ["id", "name", "updated", "created", "status",
158- "serverId", "progress", "serverRef"],
159- "link": ["rel", "type", "href"],
160- },
161- }
162-
163 serializers = {
164- 'application/xml': wsgi.XMLDictSerializer(xmlns=xmlns,
165- metadata=metadata),
166+ 'application/xml': xml_serializer,
167 }
168
169 return wsgi.Resource(controller, serializers=serializers)
170
171=== modified file 'nova/api/openstack/views/images.py'
172--- nova/api/openstack/views/images.py 2011-06-24 12:01:51 +0000
173+++ nova/api/openstack/views/images.py 2011-06-30 12:56:35 +0000
174@@ -105,6 +105,9 @@
175 image = ViewBuilder.build(self, image_obj, detail)
176 href = self.generate_href(image_obj["id"])
177
178+ if detail:
179+ image["metadata"] = image_obj.get("properties", {})
180+
181 image["links"] = [{
182 "rel": "self",
183 "href": href,
184
185=== modified file 'nova/tests/api/openstack/test_image_metadata.py'
186--- nova/tests/api/openstack/test_image_metadata.py 2011-06-25 19:38:07 +0000
187+++ nova/tests/api/openstack/test_image_metadata.py 2011-06-30 12:56:35 +0000
188@@ -24,6 +24,7 @@
189
190 from nova import flags
191 from nova.api import openstack
192+from nova import test
193 from nova.tests.api.openstack import fakes
194 import nova.wsgi
195
196@@ -31,7 +32,7 @@
197 FLAGS = flags.FLAGS
198
199
200-class ImageMetaDataTest(unittest.TestCase):
201+class ImageMetaDataTest(test.TestCase):
202
203 IMAGE_FIXTURES = [
204 {'status': 'active',
205@@ -112,30 +113,6 @@
206 for (key, value) in res_dict['metadata'].items():
207 self.assertEqual(value, res_dict['metadata'][key])
208
209- def test_index_xml(self):
210- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
211- fixture = {
212- 'metadata': {
213- 'one': 'two',
214- 'three': 'four',
215- },
216- }
217- output = serializer.index(fixture)
218- actual = minidom.parseString(output.replace(" ", ""))
219-
220- expected = minidom.parseString("""
221- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
222- <meta key="three">
223- four
224- </meta>
225- <meta key="one">
226- two
227- </meta>
228- </metadata>
229- """.replace(" ", ""))
230-
231- self.assertEqual(expected.toxml(), actual.toxml())
232-
233 def test_show(self):
234 req = webob.Request.blank('/v1.1/images/1/meta/key1')
235 req.environ['api.version'] = '1.1'
236@@ -146,24 +123,6 @@
237 self.assertEqual(len(res_dict['meta']), 1)
238 self.assertEqual('value1', res_dict['meta']['key1'])
239
240- def test_show_xml(self):
241- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
242- fixture = {
243- 'meta': {
244- 'one': 'two',
245- },
246- }
247- output = serializer.show(fixture)
248- actual = minidom.parseString(output.replace(" ", ""))
249-
250- expected = minidom.parseString("""
251- <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
252- two
253- </meta>
254- """.replace(" ", ""))
255-
256- self.assertEqual(expected.toxml(), actual.toxml())
257-
258 def test_show_not_found(self):
259 req = webob.Request.blank('/v1.1/images/1/meta/key9')
260 req.environ['api.version'] = '1.1'
261@@ -185,34 +144,6 @@
262 self.assertEqual('value2', res_dict['metadata']['key2'])
263 self.assertEqual(1, len(res_dict))
264
265- def test_create_xml(self):
266- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
267- fixture = {
268- 'metadata': {
269- 'key9': 'value9',
270- 'key2': 'value2',
271- 'key1': 'value1',
272- },
273- }
274- output = serializer.create(fixture)
275- actual = minidom.parseString(output.replace(" ", ""))
276-
277- expected = minidom.parseString("""
278- <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
279- <meta key="key2">
280- value2
281- </meta>
282- <meta key="key9">
283- value9
284- </meta>
285- <meta key="key1">
286- value1
287- </meta>
288- </metadata>
289- """.replace(" ", ""))
290-
291- self.assertEqual(expected.toxml(), actual.toxml())
292-
293 def test_update_item(self):
294 req = webob.Request.blank('/v1.1/images/1/meta/key1')
295 req.environ['api.version'] = '1.1'
296@@ -235,24 +166,6 @@
297 res = req.get_response(fakes.wsgi_app())
298 self.assertEqual(400, res.status_int)
299
300- def test_update_item_xml(self):
301- serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
302- fixture = {
303- 'meta': {
304- 'one': 'two',
305- },
306- }
307- output = serializer.update(fixture)
308- actual = minidom.parseString(output.replace(" ", ""))
309-
310- expected = minidom.parseString("""
311- <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
312- two
313- </meta>
314- """.replace(" ", ""))
315-
316- self.assertEqual(expected.toxml(), actual.toxml())
317-
318 def test_update_item_too_many_keys(self):
319 req = webob.Request.blank('/v1.1/images/1/meta/key1')
320 req.environ['api.version'] = '1.1'
321@@ -306,3 +219,134 @@
322 req.headers["content-type"] = "application/json"
323 res = req.get_response(fakes.wsgi_app())
324 self.assertEqual(400, res.status_int)
325+
326+
327+class ImageMetadataXMLSerializationTest(test.TestCase):
328+
329+ def test_index_xml(self):
330+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
331+ fixture = {
332+ 'metadata': {
333+ 'one': 'two',
334+ 'three': 'four',
335+ },
336+ }
337+ output = serializer.serialize(fixture, 'index')
338+ actual = minidom.parseString(output.replace(" ", ""))
339+
340+ expected = minidom.parseString("""
341+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
342+ <meta key="three">
343+ four
344+ </meta>
345+ <meta key="one">
346+ two
347+ </meta>
348+ </metadata>
349+ """.replace(" ", ""))
350+
351+ self.assertEqual(expected.toxml(), actual.toxml())
352+
353+ def test_index_xml_null(self):
354+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
355+ fixture = {
356+ 'metadata': {
357+ None: None,
358+ },
359+ }
360+ output = serializer.serialize(fixture, 'index')
361+ actual = minidom.parseString(output.replace(" ", ""))
362+
363+ expected = minidom.parseString("""
364+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
365+ <meta key="None">
366+ None
367+ </meta>
368+ </metadata>
369+ """.replace(" ", ""))
370+
371+ self.assertEqual(expected.toxml(), actual.toxml())
372+
373+ def test_index_xml_unicode(self):
374+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
375+ fixture = {
376+ 'metadata': {
377+ u'three': u'Jos\xe9',
378+ },
379+ }
380+ output = serializer.serialize(fixture, 'index')
381+ actual = minidom.parseString(output.replace(" ", ""))
382+
383+ expected = minidom.parseString(u"""
384+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
385+ <meta key="three">
386+ Jos\xe9
387+ </meta>
388+ </metadata>
389+ """.encode("UTF-8").replace(" ", ""))
390+
391+ self.assertEqual(expected.toxml(), actual.toxml())
392+
393+ def test_show_xml(self):
394+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
395+ fixture = {
396+ 'meta': {
397+ 'one': 'two',
398+ },
399+ }
400+ output = serializer.serialize(fixture, 'show')
401+ actual = minidom.parseString(output.replace(" ", ""))
402+
403+ expected = minidom.parseString("""
404+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
405+ two
406+ </meta>
407+ """.replace(" ", ""))
408+
409+ self.assertEqual(expected.toxml(), actual.toxml())
410+
411+ def test_update_item_xml(self):
412+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
413+ fixture = {
414+ 'meta': {
415+ 'one': 'two',
416+ },
417+ }
418+ output = serializer.serialize(fixture, 'update')
419+ actual = minidom.parseString(output.replace(" ", ""))
420+
421+ expected = minidom.parseString("""
422+ <meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
423+ two
424+ </meta>
425+ """.replace(" ", ""))
426+
427+ self.assertEqual(expected.toxml(), actual.toxml())
428+
429+ def test_create_xml(self):
430+ serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
431+ fixture = {
432+ 'metadata': {
433+ 'key9': 'value9',
434+ 'key2': 'value2',
435+ 'key1': 'value1',
436+ },
437+ }
438+ output = serializer.serialize(fixture, 'create')
439+ actual = minidom.parseString(output.replace(" ", ""))
440+
441+ expected = minidom.parseString("""
442+ <metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
443+ <meta key="key2">
444+ value2
445+ </meta>
446+ <meta key="key9">
447+ value9
448+ </meta>
449+ <meta key="key1">
450+ value1
451+ </meta>
452+ </metadata>
453+ """.replace(" ", ""))
454+
455+ self.assertEqual(expected.toxml(), actual.toxml())
456
457=== modified file 'nova/tests/api/openstack/test_images.py'
458--- nova/tests/api/openstack/test_images.py 2011-06-29 19:24:32 +0000
459+++ nova/tests/api/openstack/test_images.py 2011-06-30 12:56:35 +0000
460@@ -394,20 +394,25 @@
461 self.assertEqual(expected_image, actual_image)
462
463 def test_get_image_v1_1(self):
464- request = webob.Request.blank('/v1.1/images/123')
465+ request = webob.Request.blank('/v1.1/images/124')
466 response = request.get_response(fakes.wsgi_app())
467
468 actual_image = json.loads(response.body)
469
470- href = "http://localhost/v1.1/images/123"
471+ href = "http://localhost/v1.1/images/124"
472
473 expected_image = {
474 "image": {
475- "id": 123,
476- "name": "public image",
477+ "id": 124,
478+ "name": "queued snapshot",
479+ "serverRef": "http://localhost/v1.1/servers/42",
480 "updated": self.NOW_API_FORMAT,
481 "created": self.NOW_API_FORMAT,
482- "status": "ACTIVE",
483+ "status": "QUEUED",
484+ "metadata": {
485+ "instance_ref": "http://localhost/v1.1/servers/42",
486+ "user_id": "1",
487+ },
488 "links": [{
489 "rel": "self",
490 "href": href,
491@@ -465,34 +470,6 @@
492
493 self.assertEqual(expected_image.toxml(), actual_image.toxml())
494
495- def test_get_image_v1_1_xml(self):
496- request = webob.Request.blank('/v1.1/images/123')
497- request.accept = "application/xml"
498- response = request.get_response(fakes.wsgi_app())
499-
500- actual_image = minidom.parseString(response.body.replace(" ", ""))
501-
502- expected_href = "http://localhost/v1.1/images/123"
503- expected_now = self.NOW_API_FORMAT
504- expected_image = minidom.parseString("""
505- <image id="123"
506- name="public image"
507- updated="%(expected_now)s"
508- created="%(expected_now)s"
509- status="ACTIVE"
510- xmlns="http://docs.openstack.org/compute/api/v1.1">
511- <links>
512- <link href="%(expected_href)s" rel="self"/>
513- <link href="%(expected_href)s" rel="bookmark"
514- type="application/json" />
515- <link href="%(expected_href)s" rel="bookmark"
516- type="application/xml" />
517- </links>
518- </image>
519- """.replace(" ", "") % (locals()))
520-
521- self.assertEqual(expected_image.toxml(), actual_image.toxml())
522-
523 def test_get_image_404_json(self):
524 request = webob.Request.blank('/v1.0/images/NonExistantImage')
525 response = request.get_response(fakes.wsgi_app())
526@@ -665,6 +642,7 @@
527 expected = [{
528 'id': 123,
529 'name': 'public image',
530+ 'metadata': {},
531 'updated': self.NOW_API_FORMAT,
532 'created': self.NOW_API_FORMAT,
533 'status': 'ACTIVE',
534@@ -686,7 +664,11 @@
535 {
536 'id': 124,
537 'name': 'queued snapshot',
538- 'serverRef': "http://localhost:8774/v1.1/servers/42",
539+ 'metadata': {
540+ u'instance_ref': u'http://localhost/v1.1/servers/42',
541+ u'user_id': u'1',
542+ },
543+ 'serverRef': "http://localhost/v1.1/servers/42",
544 'updated': self.NOW_API_FORMAT,
545 'created': self.NOW_API_FORMAT,
546 'status': 'QUEUED',
547@@ -708,7 +690,11 @@
548 {
549 'id': 125,
550 'name': 'saving snapshot',
551- 'serverRef': "http://localhost:8774/v1.1/servers/42",
552+ 'metadata': {
553+ u'instance_ref': u'http://localhost/v1.1/servers/42',
554+ u'user_id': u'1',
555+ },
556+ 'serverRef': "http://localhost/v1.1/servers/42",
557 'updated': self.NOW_API_FORMAT,
558 'created': self.NOW_API_FORMAT,
559 'status': 'SAVING',
560@@ -731,7 +717,11 @@
561 {
562 'id': 126,
563 'name': 'active snapshot',
564- 'serverRef': "http://localhost:8774/v1.1/servers/42",
565+ 'metadata': {
566+ u'instance_ref': u'http://localhost/v1.1/servers/42',
567+ u'user_id': u'1',
568+ },
569+ 'serverRef': "http://localhost/v1.1/servers/42",
570 'updated': self.NOW_API_FORMAT,
571 'created': self.NOW_API_FORMAT,
572 'status': 'ACTIVE',
573@@ -753,7 +743,11 @@
574 {
575 'id': 127,
576 'name': 'killed snapshot',
577- 'serverRef': "http://localhost:8774/v1.1/servers/42",
578+ 'metadata': {
579+ u'instance_ref': u'http://localhost/v1.1/servers/42',
580+ u'user_id': u'1',
581+ },
582+ 'serverRef': "http://localhost/v1.1/servers/42",
583 'updated': self.NOW_API_FORMAT,
584 'created': self.NOW_API_FORMAT,
585 'status': 'FAILED',
586@@ -775,6 +769,7 @@
587 {
588 'id': 129,
589 'name': None,
590+ 'metadata': {},
591 'updated': self.NOW_API_FORMAT,
592 'created': self.NOW_API_FORMAT,
593 'status': 'ACTIVE',
594@@ -1108,39 +1103,6 @@
595 response = req.get_response(fakes.wsgi_app())
596 self.assertEqual(400, response.status_int)
597
598- def test_create_image_v1_1_xml_serialization(self):
599-
600- body = dict(image=dict(serverRef='123', name='Snapshot 1'))
601- req = webob.Request.blank('/v1.1/images')
602- req.method = 'POST'
603- req.body = json.dumps(body)
604- req.headers["content-type"] = "application/json"
605- req.headers["accept"] = "application/xml"
606- response = req.get_response(fakes.wsgi_app())
607- self.assertEqual(200, response.status_int)
608- resp_xml = minidom.parseString(response.body.replace(" ", ""))
609- expected_href = "http://localhost/v1.1/images/123"
610- expected_image = minidom.parseString("""
611- <image
612- created="None"
613- id="123"
614- name="Snapshot 1"
615- serverRef="http://localhost/v1.1/servers/123"
616- status="ACTIVE"
617- updated="None"
618- xmlns="http://docs.openstack.org/compute/api/v1.1">
619- <links>
620- <link href="%(expected_href)s" rel="self"/>
621- <link href="%(expected_href)s" rel="bookmark"
622- type="application/json" />
623- <link href="%(expected_href)s" rel="bookmark"
624- type="application/xml" />
625- </links>
626- </image>
627- """.replace(" ", "") % (locals()))
628-
629- self.assertEqual(expected_image.toxml(), resp_xml.toxml())
630-
631 def test_create_image_v1_1_no_server_ref(self):
632
633 body = dict(image=dict(name='Snapshot 1'))
634@@ -1171,7 +1133,7 @@
635 image_id += 1
636
637 # Snapshot for User 1
638- server_ref = 'http://localhost:8774/v1.1/servers/42'
639+ server_ref = 'http://localhost/v1.1/servers/42'
640 snapshot_properties = {'instance_ref': server_ref, 'user_id': '1'}
641 for status in ('queued', 'saving', 'active', 'killed'):
642 add_fixture(id=image_id, name='%s snapshot' % status,
643@@ -1193,3 +1155,382 @@
644 image_id += 1
645
646 return fixtures
647+
648+
649+class ImageXMLSerializationTest(test.TestCase):
650+
651+ TIMESTAMP = "2010-10-11T10:30:22Z"
652+ SERVER_HREF = 'http://localhost/v1.1/servers/123'
653+ IMAGE_HREF = 'http://localhost/v1.1/images/%s'
654+
655+ def test_show(self):
656+ serializer = images.ImageXMLSerializer()
657+
658+ fixture = {
659+ 'image': {
660+ 'id': 1,
661+ 'name': 'Image1',
662+ 'created': self.TIMESTAMP,
663+ 'updated': self.TIMESTAMP,
664+ 'serverRef': self.SERVER_HREF,
665+ 'status': 'ACTIVE',
666+ 'metadata': {
667+ 'key1': 'value1',
668+ },
669+ 'links': [
670+ {
671+ 'href': self.IMAGE_HREF % (1,),
672+ 'rel': 'bookmark',
673+ 'type': 'application/json',
674+ },
675+ ],
676+ },
677+ }
678+
679+ output = serializer.serialize(fixture, 'show')
680+ actual = minidom.parseString(output.replace(" ", ""))
681+
682+ expected_server_href = self.SERVER_HREF
683+ expected_href = self.IMAGE_HREF % (1, )
684+ expected_now = self.TIMESTAMP
685+ expected = minidom.parseString("""
686+ <image id="1"
687+ name="Image1"
688+ serverRef="%(expected_server_href)s"
689+ updated="%(expected_now)s"
690+ created="%(expected_now)s"
691+ status="ACTIVE"
692+ xmlns="http://docs.openstack.org/compute/api/v1.1">
693+ <links>
694+ <link href="%(expected_href)s" rel="bookmark"
695+ type="application/json" />
696+ </links>
697+ <metadata>
698+ <meta key="key1">
699+ value1
700+ </meta>
701+ </metadata>
702+ </image>
703+ """.replace(" ", "") % (locals()))
704+
705+ self.assertEqual(expected.toxml(), actual.toxml())
706+
707+ def test_show_zero_metadata(self):
708+ serializer = images.ImageXMLSerializer()
709+
710+ fixture = {
711+ 'image': {
712+ 'id': 1,
713+ 'name': 'Image1',
714+ 'created': self.TIMESTAMP,
715+ 'updated': self.TIMESTAMP,
716+ 'serverRef': self.SERVER_HREF,
717+ 'status': 'ACTIVE',
718+ 'metadata': {},
719+ 'links': [
720+ {
721+ 'href': self.IMAGE_HREF % (1,),
722+ 'rel': 'bookmark',
723+ 'type': 'application/json',
724+ },
725+ ],
726+ },
727+ }
728+
729+ output = serializer.serialize(fixture, 'show')
730+ actual = minidom.parseString(output.replace(" ", ""))
731+
732+ expected_server_href = self.SERVER_HREF
733+ expected_href = self.IMAGE_HREF % (1, )
734+ expected_now = self.TIMESTAMP
735+ expected = minidom.parseString("""
736+ <image id="1"
737+ name="Image1"
738+ serverRef="%(expected_server_href)s"
739+ updated="%(expected_now)s"
740+ created="%(expected_now)s"
741+ status="ACTIVE"
742+ xmlns="http://docs.openstack.org/compute/api/v1.1">
743+ <links>
744+ <link href="%(expected_href)s" rel="bookmark"
745+ type="application/json" />
746+ </links>
747+ <metadata />
748+ </image>
749+ """.replace(" ", "") % (locals()))
750+
751+ self.assertEqual(expected.toxml(), actual.toxml())
752+
753+ def test_show_image_no_metadata_key(self):
754+ serializer = images.ImageXMLSerializer()
755+
756+ fixture = {
757+ 'image': {
758+ 'id': 1,
759+ 'name': 'Image1',
760+ 'created': self.TIMESTAMP,
761+ 'updated': self.TIMESTAMP,
762+ 'serverRef': self.SERVER_HREF,
763+ 'status': 'ACTIVE',
764+ 'links': [
765+ {
766+ 'href': self.IMAGE_HREF % (1,),
767+ 'rel': 'bookmark',
768+ 'type': 'application/json',
769+ },
770+ ],
771+
772+ },
773+ }
774+
775+ output = serializer.serialize(fixture, 'show')
776+ actual = minidom.parseString(output.replace(" ", ""))
777+
778+ expected_server_href = self.SERVER_HREF
779+ expected_href = self.IMAGE_HREF % (1, )
780+ expected_now = self.TIMESTAMP
781+ expected = minidom.parseString("""
782+ <image id="1"
783+ name="Image1"
784+ serverRef="%(expected_server_href)s"
785+ updated="%(expected_now)s"
786+ created="%(expected_now)s"
787+ status="ACTIVE"
788+ xmlns="http://docs.openstack.org/compute/api/v1.1">
789+ <links>
790+ <link href="%(expected_href)s" rel="bookmark"
791+ type="application/json" />
792+ </links>
793+ <metadata />
794+ </image>
795+ """.replace(" ", "") % (locals()))
796+
797+ self.assertEqual(expected.toxml(), actual.toxml())
798+
799+ def test_index(self):
800+ serializer = images.ImageXMLSerializer()
801+
802+ fixtures = {
803+ 'images': [
804+ {
805+ 'id': 1,
806+ 'name': 'Image1',
807+ 'created': self.TIMESTAMP,
808+ 'updated': self.TIMESTAMP,
809+ 'serverRef': self.SERVER_HREF,
810+ 'status': 'ACTIVE',
811+ 'links': [
812+ {
813+ 'href': 'http://localhost/v1.1/images/1',
814+ 'rel': 'bookmark',
815+ 'type': 'application/json',
816+ },
817+ ],
818+ },
819+ {
820+ 'id': 2,
821+ 'name': 'queued image',
822+ 'created': self.TIMESTAMP,
823+ 'updated': self.TIMESTAMP,
824+ 'serverRef': self.SERVER_HREF,
825+ 'status': 'QUEUED',
826+ 'links': [
827+ {
828+ 'href': 'http://localhost/v1.1/images/2',
829+ 'rel': 'bookmark',
830+ 'type': 'application/json',
831+ },
832+ ],
833+ },
834+ ],
835+ }
836+
837+ output = serializer.serialize(fixtures, 'index')
838+ actual = minidom.parseString(output.replace(" ", ""))
839+
840+ expected_serverRef = self.SERVER_HREF
841+ expected_now = self.TIMESTAMP
842+ expected = minidom.parseString("""
843+ <images xmlns="http://docs.openstack.org/compute/api/v1.1">
844+ <image id="1"
845+ name="Image1"
846+ serverRef="%(expected_serverRef)s"
847+ updated="%(expected_now)s"
848+ created="%(expected_now)s"
849+ status="ACTIVE">
850+ <links>
851+ <link href="http://localhost/v1.1/images/1" rel="bookmark"
852+ type="application/json" />
853+ </links>
854+ </image>
855+ <image id="2"
856+ name="queued image"
857+ serverRef="%(expected_serverRef)s"
858+ updated="%(expected_now)s"
859+ created="%(expected_now)s"
860+ status="QUEUED">
861+ <links>
862+ <link href="http://localhost/v1.1/images/2" rel="bookmark"
863+ type="application/json" />
864+ </links>
865+ </image>
866+ </images>
867+ """.replace(" ", "") % (locals()))
868+
869+ self.assertEqual(expected.toxml(), actual.toxml())
870+
871+ def test_index_zero_images(self):
872+ serializer = images.ImageXMLSerializer()
873+
874+ fixtures = {
875+ 'images': [],
876+ }
877+
878+ output = serializer.serialize(fixtures, 'index')
879+ actual = minidom.parseString(output.replace(" ", ""))
880+
881+ expected_serverRef = self.SERVER_HREF
882+ expected_now = self.TIMESTAMP
883+ expected = minidom.parseString("""
884+ <images xmlns="http://docs.openstack.org/compute/api/v1.1" />
885+ """.replace(" ", "") % (locals()))
886+
887+ self.assertEqual(expected.toxml(), actual.toxml())
888+
889+ def test_detail(self):
890+ serializer = images.ImageXMLSerializer()
891+
892+ fixtures = {
893+ 'images': [
894+ {
895+ 'id': 1,
896+ 'name': 'Image1',
897+ 'created': self.TIMESTAMP,
898+ 'updated': self.TIMESTAMP,
899+ 'serverRef': self.SERVER_HREF,
900+ 'status': 'ACTIVE',
901+ 'metadata': {
902+ 'key1': 'value1',
903+ 'key2': 'value2',
904+ },
905+ 'links': [
906+ {
907+ 'href': 'http://localhost/v1.1/images/1',
908+ 'rel': 'bookmark',
909+ 'type': 'application/json',
910+ },
911+ ],
912+ },
913+ {
914+ 'id': 2,
915+ 'name': 'queued image',
916+ 'created': self.TIMESTAMP,
917+ 'updated': self.TIMESTAMP,
918+ 'serverRef': self.SERVER_HREF,
919+ 'metadata': {},
920+ 'status': 'QUEUED',
921+ 'links': [
922+ {
923+ 'href': 'http://localhost/v1.1/images/2',
924+ 'rel': 'bookmark',
925+ 'type': 'application/json',
926+ },
927+ ],
928+ },
929+ ],
930+ }
931+
932+ output = serializer.serialize(fixtures, 'detail')
933+ actual = minidom.parseString(output.replace(" ", ""))
934+
935+ expected_serverRef = self.SERVER_HREF
936+ expected_now = self.TIMESTAMP
937+ expected = minidom.parseString("""
938+ <images xmlns="http://docs.openstack.org/compute/api/v1.1">
939+ <image id="1"
940+ name="Image1"
941+ serverRef="%(expected_serverRef)s"
942+ updated="%(expected_now)s"
943+ created="%(expected_now)s"
944+ status="ACTIVE">
945+ <links>
946+ <link href="http://localhost/v1.1/images/1" rel="bookmark"
947+ type="application/json" />
948+ </links>
949+ <metadata>
950+ <meta key="key2">
951+ value2
952+ </meta>
953+ <meta key="key1">
954+ value1
955+ </meta>
956+ </metadata>
957+ </image>
958+ <image id="2"
959+ name="queued image"
960+ serverRef="%(expected_serverRef)s"
961+ updated="%(expected_now)s"
962+ created="%(expected_now)s"
963+ status="QUEUED">
964+ <links>
965+ <link href="http://localhost/v1.1/images/2" rel="bookmark"
966+ type="application/json" />
967+ </links>
968+ <metadata />
969+ </image>
970+ </images>
971+ """.replace(" ", "") % (locals()))
972+
973+ self.assertEqual(expected.toxml(), actual.toxml())
974+
975+ def test_create(self):
976+ serializer = images.ImageXMLSerializer()
977+
978+ fixture = {
979+ 'image': {
980+ 'id': 1,
981+ 'name': 'Image1',
982+ 'created': self.TIMESTAMP,
983+ 'updated': self.TIMESTAMP,
984+ 'serverRef': self.SERVER_HREF,
985+ 'status': 'ACTIVE',
986+ 'metadata': {
987+ 'key1': 'value1',
988+ },
989+ 'links': [
990+ {
991+ 'href': self.IMAGE_HREF % (1,),
992+ 'rel': 'bookmark',
993+ 'type': 'application/json',
994+ },
995+ ],
996+ },
997+ }
998+
999+ output = serializer.serialize(fixture, 'create')
1000+ actual = minidom.parseString(output.replace(" ", ""))
1001+
1002+ expected_server_href = self.SERVER_HREF
1003+ expected_href = self.IMAGE_HREF % (1, )
1004+ expected_now = self.TIMESTAMP
1005+ expected = minidom.parseString("""
1006+ <image id="1"
1007+ name="Image1"
1008+ serverRef="%(expected_server_href)s"
1009+ updated="%(expected_now)s"
1010+ created="%(expected_now)s"
1011+ status="ACTIVE"
1012+ xmlns="http://docs.openstack.org/compute/api/v1.1">
1013+ <links>
1014+ <link href="%(expected_href)s" rel="bookmark"
1015+ type="application/json" />
1016+ </links>
1017+ <metadata>
1018+ <meta key="key1">
1019+ value1
1020+ </meta>
1021+ </metadata>
1022+ </image>
1023+ """.replace(" ", "") % (locals()))
1024+
1025+ self.assertEqual(expected.toxml(), actual.toxml())