Merge lp:~rackspace-titan/nova/osapi-images-metadata into lp:~hudson-openstack/nova/trunk
- osapi-images-metadata
- Merge into trunk
Proposed by
Brian Waldon
Status: | Merged |
---|---|
Approved by: | Devin Carlen |
Approved revision: | 1308 |
Merged at revision: | 1320 |
Proposed branch: | lp:~rackspace-titan/nova/osapi-images-metadata |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
604 lines (+254/-89) 6 files modified
nova/api/openstack/__init__.py (+8/-2) nova/api/openstack/create_instance_helper.py (+7/-38) nova/api/openstack/image_metadata.py (+50/-3) nova/api/openstack/wsgi.py (+38/-4) nova/tests/api/openstack/test_image_metadata.py (+144/-42) nova/tests/api/openstack/test_wsgi.py (+7/-0) |
To merge this branch: | bzr merge lp:~rackspace-titan/nova/osapi-images-metadata |
Related bugs: | |
Related blueprints: |
Openstack API 1.1 Finalization
(Essential)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Devin Carlen (community) | Approve | ||
Vish Ishaya (community) | Approve | ||
Review via email: mp+68909@code.launchpad.net |
Commit message
Description of the change
- Updates /images/<id>/meta and /images/
- Renames ../meta to ../metadata
- Adds PUT on ../metadata to set entire container (controller action is called update_all)
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'nova/api/openstack/__init__.py' |
2 | --- nova/api/openstack/__init__.py 2011-07-13 15:10:40 +0000 |
3 | +++ nova/api/openstack/__init__.py 2011-07-25 19:58:26 +0000 |
4 | @@ -164,11 +164,17 @@ |
5 | |
6 | def _setup_routes(self, mapper): |
7 | super(APIRouterV11, self)._setup_routes(mapper, '1.1') |
8 | - mapper.resource("image_meta", "meta", |
9 | - controller=image_metadata.create_resource(), |
10 | + image_metadata_controller = image_metadata.create_resource() |
11 | + mapper.resource("image_meta", "metadata", |
12 | + controller=image_metadata_controller, |
13 | parent_resource=dict(member_name='image', |
14 | collection_name='images')) |
15 | |
16 | + mapper.connect("metadata", "/images/{image_id}/metadata", |
17 | + controller=image_metadata_controller, |
18 | + action='update_all', |
19 | + conditions={"method": ['PUT']}) |
20 | + |
21 | mapper.resource("server_meta", "meta", |
22 | controller=server_metadata.create_resource(), |
23 | parent_resource=dict(member_name='server', |
24 | |
25 | === modified file 'nova/api/openstack/create_instance_helper.py' |
26 | --- nova/api/openstack/create_instance_helper.py 2011-07-21 19:14:16 +0000 |
27 | +++ nova/api/openstack/create_instance_helper.py 2011-07-25 19:58:26 +0000 |
28 | @@ -282,7 +282,7 @@ |
29 | return password |
30 | |
31 | |
32 | -class ServerXMLDeserializer(wsgi.XMLDeserializer): |
33 | +class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer): |
34 | """ |
35 | Deserializer to handle xml-formatted server create requests. |
36 | |
37 | @@ -299,11 +299,12 @@ |
38 | def _extract_server(self, node): |
39 | """Marshal the server attribute of a parsed request""" |
40 | server = {} |
41 | - server_node = self._find_first_child_named(node, 'server') |
42 | + server_node = self.find_first_child_named(node, 'server') |
43 | for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]: |
44 | if server_node.getAttribute(attr): |
45 | server[attr] = server_node.getAttribute(attr) |
46 | - metadata = self._extract_metadata(server_node) |
47 | + metadata_node = self.find_first_child_named(server_node, "metadata") |
48 | + metadata = self.extract_metadata(metadata_node) |
49 | if metadata is not None: |
50 | server["metadata"] = metadata |
51 | personality = self._extract_personality(server_node) |
52 | @@ -311,49 +312,17 @@ |
53 | server["personality"] = personality |
54 | return server |
55 | |
56 | - def _extract_metadata(self, server_node): |
57 | - """Marshal the metadata attribute of a parsed request""" |
58 | - metadata_node = self._find_first_child_named(server_node, "metadata") |
59 | - if metadata_node is None: |
60 | - return None |
61 | - metadata = {} |
62 | - for meta_node in self._find_children_named(metadata_node, "meta"): |
63 | - key = meta_node.getAttribute("key") |
64 | - metadata[key] = self._extract_text(meta_node) |
65 | - return metadata |
66 | - |
67 | def _extract_personality(self, server_node): |
68 | """Marshal the personality attribute of a parsed request""" |
69 | personality_node = \ |
70 | - self._find_first_child_named(server_node, "personality") |
71 | + self.find_first_child_named(server_node, "personality") |
72 | if personality_node is None: |
73 | return None |
74 | personality = [] |
75 | - for file_node in self._find_children_named(personality_node, "file"): |
76 | + for file_node in self.find_children_named(personality_node, "file"): |
77 | item = {} |
78 | if file_node.hasAttribute("path"): |
79 | item["path"] = file_node.getAttribute("path") |
80 | - item["contents"] = self._extract_text(file_node) |
81 | + item["contents"] = self.extract_text(file_node) |
82 | personality.append(item) |
83 | return personality |
84 | - |
85 | - def _find_first_child_named(self, parent, name): |
86 | - """Search a nodes children for the first child with a given name""" |
87 | - for node in parent.childNodes: |
88 | - if node.nodeName == name: |
89 | - return node |
90 | - return None |
91 | - |
92 | - def _find_children_named(self, parent, name): |
93 | - """Return all of a nodes children who have the given name""" |
94 | - for node in parent.childNodes: |
95 | - if node.nodeName == name: |
96 | - yield node |
97 | - |
98 | - def _extract_text(self, node): |
99 | - """Get the text field contained by the given node""" |
100 | - if len(node.childNodes) == 1: |
101 | - child = node.childNodes[0] |
102 | - if child.nodeType == child.TEXT_NODE: |
103 | - return child.nodeValue |
104 | - return "" |
105 | |
106 | === modified file 'nova/api/openstack/image_metadata.py' |
107 | --- nova/api/openstack/image_metadata.py 2011-07-21 14:02:16 +0000 |
108 | +++ nova/api/openstack/image_metadata.py 2011-07-25 19:58:26 +0000 |
109 | @@ -96,8 +96,16 @@ |
110 | self._check_quota_limit(context, metadata) |
111 | img['properties'] = metadata |
112 | self.image_service.update(context, image_id, img, None) |
113 | + return dict(meta=meta) |
114 | |
115 | - return req.body |
116 | + def update_all(self, req, image_id, body): |
117 | + context = req.environ['nova.context'] |
118 | + img = self.image_service.show(context, image_id) |
119 | + metadata = body.get('metadata', {}) |
120 | + self._check_quota_limit(context, metadata) |
121 | + img['properties'] = metadata |
122 | + self.image_service.update(context, image_id, img, None) |
123 | + return dict(metadata=metadata) |
124 | |
125 | def delete(self, req, image_id, id): |
126 | context = req.environ['nova.context'] |
127 | @@ -110,6 +118,32 @@ |
128 | self.image_service.update(context, image_id, img, None) |
129 | |
130 | |
131 | +class ImageMetadataXMLDeserializer(wsgi.MetadataXMLDeserializer): |
132 | + |
133 | + def _extract_metadata_container(self, datastring): |
134 | + dom = minidom.parseString(datastring) |
135 | + metadata_node = self.find_first_child_named(dom, "metadata") |
136 | + metadata = self.extract_metadata(metadata_node) |
137 | + return {'body': {'metadata': metadata}} |
138 | + |
139 | + def create(self, datastring): |
140 | + return self._extract_metadata_container(datastring) |
141 | + |
142 | + def update_all(self, datastring): |
143 | + return self._extract_metadata_container(datastring) |
144 | + |
145 | + def update(self, datastring): |
146 | + dom = minidom.parseString(datastring) |
147 | + metadata_item = self.extract_metadata(dom) |
148 | + return {'body': {'meta': metadata_item}} |
149 | + |
150 | + |
151 | +class HeadersSerializer(wsgi.ResponseHeadersSerializer): |
152 | + |
153 | + def delete(self, response, data): |
154 | + response.status_int = 204 |
155 | + |
156 | + |
157 | class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): |
158 | def __init__(self, xmlns=wsgi.XMLNS_V11): |
159 | super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns) |
160 | @@ -143,6 +177,9 @@ |
161 | def create(self, metadata_dict): |
162 | return self._meta_list_to_xml_string(metadata_dict) |
163 | |
164 | + def update_all(self, metadata_dict): |
165 | + return self._meta_list_to_xml_string(metadata_dict) |
166 | + |
167 | def _meta_item_to_xml_string(self, meta_item_dict): |
168 | xml_doc = minidom.Document() |
169 | item_key, item_value = meta_item_dict.items()[0] |
170 | @@ -157,11 +194,21 @@ |
171 | def update(self, meta_item_dict): |
172 | return self._meta_item_to_xml_string(meta_item_dict['meta']) |
173 | |
174 | + def default(self, *args, **kwargs): |
175 | + return '' |
176 | + |
177 | |
178 | def create_resource(): |
179 | + headers_serializer = HeadersSerializer() |
180 | + |
181 | + body_deserializers = { |
182 | + 'application/xml': ImageMetadataXMLDeserializer(), |
183 | + } |
184 | + |
185 | body_serializers = { |
186 | 'application/xml': ImageMetadataXMLSerializer(), |
187 | } |
188 | - serializer = wsgi.ResponseSerializer(body_serializers) |
189 | + serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer) |
190 | + deserializer = wsgi.RequestDeserializer(body_deserializers) |
191 | |
192 | - return wsgi.Resource(Controller(), serializer=serializer) |
193 | + return wsgi.Resource(Controller(), deserializer, serializer) |
194 | |
195 | === modified file 'nova/api/openstack/wsgi.py' |
196 | --- nova/api/openstack/wsgi.py 2011-07-22 03:44:45 +0000 |
197 | +++ nova/api/openstack/wsgi.py 2011-07-25 19:58:26 +0000 |
198 | @@ -136,10 +136,44 @@ |
199 | listnames) |
200 | return result |
201 | |
202 | + def find_first_child_named(self, parent, name): |
203 | + """Search a nodes children for the first child with a given name""" |
204 | + for node in parent.childNodes: |
205 | + if node.nodeName == name: |
206 | + return node |
207 | + return None |
208 | + |
209 | + def find_children_named(self, parent, name): |
210 | + """Return all of a nodes children who have the given name""" |
211 | + for node in parent.childNodes: |
212 | + if node.nodeName == name: |
213 | + yield node |
214 | + |
215 | + def extract_text(self, node): |
216 | + """Get the text field contained by the given node""" |
217 | + if len(node.childNodes) == 1: |
218 | + child = node.childNodes[0] |
219 | + if child.nodeType == child.TEXT_NODE: |
220 | + return child.nodeValue |
221 | + return "" |
222 | + |
223 | def default(self, datastring): |
224 | return {'body': self._from_xml(datastring)} |
225 | |
226 | |
227 | +class MetadataXMLDeserializer(XMLDeserializer): |
228 | + |
229 | + def extract_metadata(self, metadata_node): |
230 | + """Marshal the metadata attribute of a parsed request""" |
231 | + if metadata_node is None: |
232 | + return None |
233 | + metadata = {} |
234 | + for meta_node in self.find_children_named(metadata_node, "meta"): |
235 | + key = meta_node.getAttribute("key") |
236 | + metadata[key] = self.extract_text(meta_node) |
237 | + return metadata |
238 | + |
239 | + |
240 | class RequestHeadersDeserializer(ActionDispatcher): |
241 | """Default request headers deserializer""" |
242 | |
243 | @@ -397,8 +431,9 @@ |
244 | |
245 | def serialize_body(self, response, data, content_type, action): |
246 | response.headers['Content-Type'] = content_type |
247 | - serializer = self.get_body_serializer(content_type) |
248 | - response.body = serializer.serialize(data, action) |
249 | + if data is not None: |
250 | + serializer = self.get_body_serializer(content_type) |
251 | + response.body = serializer.serialize(data, action) |
252 | |
253 | def get_body_serializer(self, content_type): |
254 | try: |
255 | @@ -444,7 +479,7 @@ |
256 | action, args, accept = self.deserializer.deserialize(request) |
257 | except exception.InvalidContentType: |
258 | msg = _("Unsupported Content-Type") |
259 | - return webob.exc.HTTPBadRequest(explanation=msg) |
260 | + return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg)) |
261 | except exception.MalformedRequestBody: |
262 | msg = _("Malformed request body") |
263 | return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg)) |
264 | @@ -455,7 +490,6 @@ |
265 | LOG.info(_("HTTP exception thrown: %s"), unicode(ex)) |
266 | action_result = faults.Fault(ex) |
267 | |
268 | - #TODO(bcwaldon): find a more elegant way to pass through non-dict types |
269 | if type(action_result) is dict or action_result is None: |
270 | response = self.serializer.serialize(action_result, |
271 | accept, |
272 | |
273 | === modified file 'nova/tests/api/openstack/test_image_metadata.py' |
274 | --- nova/tests/api/openstack/test_image_metadata.py 2011-06-28 15:59:46 +0000 |
275 | +++ nova/tests/api/openstack/test_image_metadata.py 2011-07-25 19:58:26 +0000 |
276 | @@ -103,8 +103,7 @@ |
277 | super(ImageMetaDataTest, self).tearDown() |
278 | |
279 | def test_index(self): |
280 | - req = webob.Request.blank('/v1.1/images/1/meta') |
281 | - req.environ['api.version'] = '1.1' |
282 | + req = webob.Request.blank('/v1.1/images/1/metadata') |
283 | res = req.get_response(fakes.wsgi_app()) |
284 | res_dict = json.loads(res.body) |
285 | self.assertEqual(200, res.status_int) |
286 | @@ -114,8 +113,7 @@ |
287 | self.assertEqual(value, res_dict['metadata'][key]) |
288 | |
289 | def test_show(self): |
290 | - req = webob.Request.blank('/v1.1/images/1/meta/key1') |
291 | - req.environ['api.version'] = '1.1' |
292 | + req = webob.Request.blank('/v1.1/images/1/metadata/key1') |
293 | res = req.get_response(fakes.wsgi_app()) |
294 | res_dict = json.loads(res.body) |
295 | self.assertEqual(200, res.status_int) |
296 | @@ -124,42 +122,66 @@ |
297 | self.assertEqual('value1', res_dict['meta']['key1']) |
298 | |
299 | def test_show_not_found(self): |
300 | - req = webob.Request.blank('/v1.1/images/1/meta/key9') |
301 | - req.environ['api.version'] = '1.1' |
302 | + req = webob.Request.blank('/v1.1/images/1/metadata/key9') |
303 | res = req.get_response(fakes.wsgi_app()) |
304 | self.assertEqual(404, res.status_int) |
305 | |
306 | def test_create(self): |
307 | - req = webob.Request.blank('/v1.1/images/2/meta') |
308 | - req.environ['api.version'] = '1.1' |
309 | + req = webob.Request.blank('/v1.1/images/2/metadata') |
310 | req.method = 'POST' |
311 | req.body = '{"metadata": {"key9": "value9"}}' |
312 | req.headers["content-type"] = "application/json" |
313 | res = req.get_response(fakes.wsgi_app()) |
314 | - res_dict = json.loads(res.body) |
315 | - self.assertEqual(200, res.status_int) |
316 | - self.assertEqual('value9', res_dict['metadata']['key9']) |
317 | - # other items should not be modified |
318 | - self.assertEqual('value1', res_dict['metadata']['key1']) |
319 | - self.assertEqual('value2', res_dict['metadata']['key2']) |
320 | - self.assertEqual(1, len(res_dict)) |
321 | + |
322 | + self.assertEqual(200, res.status_int) |
323 | + actual_output = json.loads(res.body) |
324 | + |
325 | + expected_output = { |
326 | + 'metadata': { |
327 | + 'key1': 'value1', |
328 | + 'key2': 'value2', |
329 | + 'key9': 'value9', |
330 | + }, |
331 | + } |
332 | + |
333 | + self.assertEqual(expected_output, actual_output) |
334 | + |
335 | + def test_update_all(self): |
336 | + req = webob.Request.blank('/v1.1/images/2/metadata') |
337 | + req.method = 'PUT' |
338 | + req.body = '{"metadata": {"key9": "value9"}}' |
339 | + req.headers["content-type"] = "application/json" |
340 | + res = req.get_response(fakes.wsgi_app()) |
341 | + |
342 | + self.assertEqual(200, res.status_int) |
343 | + actual_output = json.loads(res.body) |
344 | + |
345 | + expected_output = { |
346 | + 'metadata': { |
347 | + 'key9': 'value9', |
348 | + }, |
349 | + } |
350 | + |
351 | + self.assertEqual(expected_output, actual_output) |
352 | |
353 | def test_update_item(self): |
354 | - req = webob.Request.blank('/v1.1/images/1/meta/key1') |
355 | - req.environ['api.version'] = '1.1' |
356 | + req = webob.Request.blank('/v1.1/images/1/metadata/key1') |
357 | req.method = 'PUT' |
358 | req.body = '{"meta": {"key1": "zz"}}' |
359 | req.headers["content-type"] = "application/json" |
360 | res = req.get_response(fakes.wsgi_app()) |
361 | + |
362 | self.assertEqual(200, res.status_int) |
363 | - res_dict = json.loads(res.body) |
364 | - self.assertTrue('meta' in res_dict) |
365 | - self.assertEqual(len(res_dict['meta']), 1) |
366 | - self.assertEqual('zz', res_dict['meta']['key1']) |
367 | + actual_output = json.loads(res.body) |
368 | + expected_output = { |
369 | + 'meta': { |
370 | + 'key1': 'zz', |
371 | + }, |
372 | + } |
373 | + self.assertEqual(actual_output, expected_output) |
374 | |
375 | def test_update_item_bad_body(self): |
376 | - req = webob.Request.blank('/v1.1/images/1/meta/key1') |
377 | - req.environ['api.version'] = '1.1' |
378 | + req = webob.Request.blank('/v1.1/images/1/metadata/key1') |
379 | req.method = 'PUT' |
380 | req.body = '{"key1": "zz"}' |
381 | req.headers["content-type"] = "application/json" |
382 | @@ -167,8 +189,7 @@ |
383 | self.assertEqual(400, res.status_int) |
384 | |
385 | def test_update_item_too_many_keys(self): |
386 | - req = webob.Request.blank('/v1.1/images/1/meta/key1') |
387 | - req.environ['api.version'] = '1.1' |
388 | + req = webob.Request.blank('/v1.1/images/1/metadata/key1') |
389 | req.method = 'PUT' |
390 | req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' |
391 | req.headers["content-type"] = "application/json" |
392 | @@ -176,24 +197,38 @@ |
393 | self.assertEqual(400, res.status_int) |
394 | |
395 | def test_update_item_body_uri_mismatch(self): |
396 | - req = webob.Request.blank('/v1.1/images/1/meta/bad') |
397 | - req.environ['api.version'] = '1.1' |
398 | + req = webob.Request.blank('/v1.1/images/1/metadata/bad') |
399 | req.method = 'PUT' |
400 | req.body = '{"meta": {"key1": "value1"}}' |
401 | req.headers["content-type"] = "application/json" |
402 | res = req.get_response(fakes.wsgi_app()) |
403 | self.assertEqual(400, res.status_int) |
404 | |
405 | + def test_update_item_xml(self): |
406 | + req = webob.Request.blank('/v1.1/images/1/metadata/key1') |
407 | + req.method = 'PUT' |
408 | + req.body = '<meta key="key1">five</meta>' |
409 | + req.headers["content-type"] = "application/xml" |
410 | + res = req.get_response(fakes.wsgi_app()) |
411 | + |
412 | + self.assertEqual(200, res.status_int) |
413 | + actual_output = json.loads(res.body) |
414 | + expected_output = { |
415 | + 'meta': { |
416 | + 'key1': 'five', |
417 | + }, |
418 | + } |
419 | + self.assertEqual(actual_output, expected_output) |
420 | + |
421 | def test_delete(self): |
422 | - req = webob.Request.blank('/v1.1/images/2/meta/key1') |
423 | - req.environ['api.version'] = '1.1' |
424 | + req = webob.Request.blank('/v1.1/images/2/metadata/key1') |
425 | req.method = 'DELETE' |
426 | res = req.get_response(fakes.wsgi_app()) |
427 | - self.assertEqual(200, res.status_int) |
428 | + self.assertEqual(204, res.status_int) |
429 | + self.assertEqual('', res.body) |
430 | |
431 | def test_delete_not_found(self): |
432 | - req = webob.Request.blank('/v1.1/images/2/meta/blah') |
433 | - req.environ['api.version'] = '1.1' |
434 | + req = webob.Request.blank('/v1.1/images/2/metadata/blah') |
435 | req.method = 'DELETE' |
436 | res = req.get_response(fakes.wsgi_app()) |
437 | self.assertEqual(404, res.status_int) |
438 | @@ -203,8 +238,7 @@ |
439 | for num in range(FLAGS.quota_metadata_items + 1): |
440 | data['metadata']['key%i' % num] = "blah" |
441 | json_string = str(data).replace("\'", "\"") |
442 | - req = webob.Request.blank('/v1.1/images/2/meta') |
443 | - req.environ['api.version'] = '1.1' |
444 | + req = webob.Request.blank('/v1.1/images/2/metadata') |
445 | req.method = 'POST' |
446 | req.body = json_string |
447 | req.headers["content-type"] = "application/json" |
448 | @@ -212,8 +246,7 @@ |
449 | self.assertEqual(400, res.status_int) |
450 | |
451 | def test_too_many_metadata_items_on_put(self): |
452 | - req = webob.Request.blank('/v1.1/images/3/meta/blah') |
453 | - req.environ['api.version'] = '1.1' |
454 | + req = webob.Request.blank('/v1.1/images/3/metadata/blah') |
455 | req.method = 'PUT' |
456 | req.body = '{"meta": {"blah": "blah"}}' |
457 | req.headers["content-type"] = "application/json" |
458 | @@ -221,9 +254,49 @@ |
459 | self.assertEqual(400, res.status_int) |
460 | |
461 | |
462 | +class ImageMetadataXMLDeserializationTest(test.TestCase): |
463 | + |
464 | + deserializer = openstack.image_metadata.ImageMetadataXMLDeserializer() |
465 | + |
466 | + def test_create(self): |
467 | + request_body = """ |
468 | + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
469 | + <meta key='123'>asdf</meta> |
470 | + <meta key='567'>jkl;</meta> |
471 | + </metadata>""" |
472 | + output = self.deserializer.deserialize(request_body, 'create') |
473 | + expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} |
474 | + self.assertEquals(output, expected) |
475 | + |
476 | + def test_create_empty(self): |
477 | + request_body = """ |
478 | + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>""" |
479 | + output = self.deserializer.deserialize(request_body, 'create') |
480 | + expected = {"body": {"metadata": {}}} |
481 | + self.assertEquals(output, expected) |
482 | + |
483 | + def test_update_all(self): |
484 | + request_body = """ |
485 | + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
486 | + <meta key='123'>asdf</meta> |
487 | + <meta key='567'>jkl;</meta> |
488 | + </metadata>""" |
489 | + output = self.deserializer.deserialize(request_body, 'update_all') |
490 | + expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}} |
491 | + self.assertEquals(output, expected) |
492 | + |
493 | + def test_update(self): |
494 | + request_body = """ |
495 | + <meta xmlns="http://docs.openstack.org/compute/api/v1.1" |
496 | + key='123'>asdf</meta>""" |
497 | + output = self.deserializer.deserialize(request_body, 'update') |
498 | + expected = {"body": {"meta": {"123": "asdf"}}} |
499 | + self.assertEquals(output, expected) |
500 | + |
501 | + |
502 | class ImageMetadataXMLSerializationTest(test.TestCase): |
503 | |
504 | - def test_index_xml(self): |
505 | + def test_index(self): |
506 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
507 | fixture = { |
508 | 'metadata': { |
509 | @@ -247,7 +320,7 @@ |
510 | |
511 | self.assertEqual(expected.toxml(), actual.toxml()) |
512 | |
513 | - def test_index_xml_null(self): |
514 | + def test_index_null(self): |
515 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
516 | fixture = { |
517 | 'metadata': { |
518 | @@ -267,7 +340,7 @@ |
519 | |
520 | self.assertEqual(expected.toxml(), actual.toxml()) |
521 | |
522 | - def test_index_xml_unicode(self): |
523 | + def test_index_unicode(self): |
524 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
525 | fixture = { |
526 | 'metadata': { |
527 | @@ -287,7 +360,7 @@ |
528 | |
529 | self.assertEqual(expected.toxml(), actual.toxml()) |
530 | |
531 | - def test_show_xml(self): |
532 | + def test_show(self): |
533 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
534 | fixture = { |
535 | 'meta': { |
536 | @@ -305,7 +378,31 @@ |
537 | |
538 | self.assertEqual(expected.toxml(), actual.toxml()) |
539 | |
540 | - def test_update_item_xml(self): |
541 | + def test_update_all(self): |
542 | + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
543 | + fixture = { |
544 | + 'metadata': { |
545 | + 'key6': 'value6', |
546 | + 'key4': 'value4', |
547 | + }, |
548 | + } |
549 | + output = serializer.serialize(fixture, 'update_all') |
550 | + actual = minidom.parseString(output.replace(" ", "")) |
551 | + |
552 | + expected = minidom.parseString(""" |
553 | + <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
554 | + <meta key="key6"> |
555 | + value6 |
556 | + </meta> |
557 | + <meta key="key4"> |
558 | + value4 |
559 | + </meta> |
560 | + </metadata> |
561 | + """.replace(" ", "")) |
562 | + |
563 | + self.assertEqual(expected.toxml(), actual.toxml()) |
564 | + |
565 | + def test_update_item(self): |
566 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
567 | fixture = { |
568 | 'meta': { |
569 | @@ -323,7 +420,7 @@ |
570 | |
571 | self.assertEqual(expected.toxml(), actual.toxml()) |
572 | |
573 | - def test_create_xml(self): |
574 | + def test_create(self): |
575 | serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
576 | fixture = { |
577 | 'metadata': { |
578 | @@ -350,3 +447,8 @@ |
579 | """.replace(" ", "")) |
580 | |
581 | self.assertEqual(expected.toxml(), actual.toxml()) |
582 | + |
583 | + def test_delete(self): |
584 | + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() |
585 | + output = serializer.serialize(None, 'delete') |
586 | + self.assertEqual(output, '') |
587 | |
588 | === modified file 'nova/tests/api/openstack/test_wsgi.py' |
589 | --- nova/tests/api/openstack/test_wsgi.py 2011-07-11 16:33:53 +0000 |
590 | +++ nova/tests/api/openstack/test_wsgi.py 2011-07-25 19:58:26 +0000 |
591 | @@ -256,6 +256,13 @@ |
592 | self.assertEqual(response.body, 'pew_json') |
593 | self.assertEqual(response.status_int, 404) |
594 | |
595 | + def test_serialize_response_None(self): |
596 | + response = self.serializer.serialize(None, 'application/json') |
597 | + print response |
598 | + self.assertEqual(response.headers['Content-Type'], 'application/json') |
599 | + self.assertEqual(response.body, '') |
600 | + self.assertEqual(response.status_int, 404) |
601 | + |
602 | def test_serialize_response_dict_to_unknown_content_type(self): |
603 | self.assertRaises(exception.InvalidContentType, |
604 | self.serializer.serialize, |
rock on.