Merge lp:~rackspace-titan/nova/minidom-to-etree into lp:~hudson-openstack/nova/trunk
- minidom-to-etree
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Dan Prince |
Approved revision: | 1529 |
Merged at revision: | 1562 |
Proposed branch: | lp:~rackspace-titan/nova/minidom-to-etree |
Merge into: | lp:~hudson-openstack/nova/trunk |
Diff against target: |
3701 lines (+1453/-1340) 29 files modified
nova/api/openstack/common.py (+31/-35) nova/api/openstack/flavors.py (+33/-36) nova/api/openstack/images.py (+56/-77) nova/api/openstack/ips.py (+28/-33) nova/api/openstack/limits.py (+38/-40) nova/api/openstack/schemas/v1.1/addresses.rng (+14/-0) nova/api/openstack/schemas/v1.1/flavor.rng (+10/-0) nova/api/openstack/schemas/v1.1/flavors.rng (+6/-0) nova/api/openstack/schemas/v1.1/flavors_index.rng (+12/-0) nova/api/openstack/schemas/v1.1/image.rng (+30/-0) nova/api/openstack/schemas/v1.1/images.rng (+6/-0) nova/api/openstack/schemas/v1.1/images_index.rng (+12/-0) nova/api/openstack/schemas/v1.1/limits.rng (+28/-0) nova/api/openstack/schemas/v1.1/metadata.rng (+9/-0) nova/api/openstack/schemas/v1.1/server.rng (+3/-3) nova/api/openstack/servers.py (+98/-128) nova/api/openstack/versions.py (+87/-167) nova/api/openstack/views/versions.py (+1/-1) nova/api/openstack/wsgi.py (+5/-0) nova/tests/api/openstack/common.py (+22/-0) nova/tests/api/openstack/contrib/test_createserverext.py (+5/-1) nova/tests/api/openstack/test_common.py (+82/-54) nova/tests/api/openstack/test_flavors.py (+106/-101) nova/tests/api/openstack/test_images.py (+205/-185) nova/tests/api/openstack/test_limits.py (+57/-37) nova/tests/api/openstack/test_servers.py (+204/-76) nova/tests/api/openstack/test_versions.py (+258/-360) nova/tests/integrated/test_xml.py (+6/-6) tools/pip-requires (+1/-0) |
To merge this branch: | bzr merge lp:~rackspace-titan/nova/minidom-to-etree |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dan Prince (community) | Approve | ||
Rick Harris (community) | Approve | ||
Review via email: mp+73056@code.launchpad.net |
Commit message
Description of the change
This branch changes XML Serializers and their tests to use lxml.etree instead of minidom.
The current use of minidom in tests unnecessarily forces xml ordering as well as pretty printing. Using etree allows for validating the XML and checking individual element values.
Changing all the serializers to use etree allows for fixing of bug 814196 and consistency among the serializers.
Schema validation has been added for addresses, flavors, images, limits, metadata and servers resources.
The atom feeds we generated are now tested using the feedparser module. This allows for stronger, cleaner and more correct validation.
Note: Not all minidom usage has been replaced with lxml. We realized this was too big a job to tackle in a single merge. Fixing those in subsequent merges will be more manageable.
Alex Meade (alex-meade) wrote : | # |
Rick Harris (rconradharris) wrote : | # |
Nice work.
Some thoughts:
> 2930 + for (i, v) in ((0, 'v1.0'), (1, 'v1.1')):
Could also be written:
for i, v in enumerate(['v1.0', 'v1.1']):
> 2627 + print output
> 2593 + print output
> 2571 + print output
> 2553 + print output
... bunch more ...
Were these left in by mistake?
> 460 + for i in range(len(rates)):
> 2461 + rate = rates[i]
Would enumerate work here?
for idx, rate in enumerate(rates):
Naveed Massjouni (ironcamel) wrote : | # |
> Nice work.
>
> Some thoughts:
>
> > 2930 + for (i, v) in ((0, 'v1.0'), (1, 'v1.1')):
>
> Could also be written:
>
> for i, v in enumerate(['v1.0', 'v1.1']):
Nice catch. Fixed.
>
> > 2627 + print output
> > 2593 + print output
> > 2571 + print output
> > 2553 + print output
> ... bunch more ...
>
> Were these left in by mistake?
These are strategically placed in the tests such that if a test happens to break in the future, they will help to track down the reason. When tests are passing, these statements are harmless - they are just ignored.
>
> > 460 + for i in range(len(rates)):
> > 2461 + rate = rates[i]
>
> Would enumerate work here?
>
> for idx, rate in enumerate(rates):
Yes! Fixed :)
Rick Harris (rconradharris) wrote : | # |
> > > 2627 + print output
> > > 2593 + print output
> > > 2571 + print output
> > > 2553 + print output
> > ... bunch more ...
> >
> > Were these left in by mistake?
>
> These are strategically placed in the tests such that if a test happens to
> break in the future, they will help to track down the reason. When tests are
> passing, these statements are harmless - they are just ignored.
Sounds good.
LGTM.
Dan Prince (dan-prince) wrote : | # |
Hey guys,
I'm getting a merge conflict with lp:nova...
Text conflict in nova/api/
1 conflicts encountered.
Dan Prince (dan-prince) wrote : | # |
This looks good.
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~rackspace-titan/nova/minidom-to-etree into lp:nova failed. Below is the output from the failed tests.
ExecutorTestCase
test_
test_
test_
LockoutTestCase
test_lockout OK 0.01
test_
test_timeout OK 0.01
test_
CreateserverextTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_fl...
Naveed Massjouni (ironcamel) wrote : | # |
Missing dep was added by Monty.
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~rackspace-titan/nova/minidom-to-etree into lp:nova failed. Below is the output from the failed tests.
ExecutorTestCase
test_
test_
test_
LockoutTestCase
test_lockout OK 0.03
test_
test_timeout OK 0.01
test_
CreateserverextTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_fl...
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~rackspace-titan/nova/minidom-to-etree into lp:nova failed. Below is the output from the failed tests.
ExecutorTestCase
test_
test_
test_
LockoutTestCase
test_lockout OK 0.01
test_
test_timeout OK 0.01
test_
CreateserverextTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_fl...
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~rackspace-titan/nova/minidom-to-etree into lp:nova failed. Below is the output from the failed tests.
ExecutorTestCase
test_
test_
test_
LockoutTestCase
test_lockout OK 0.01
test_
test_timeout OK 0.01
test_
CreateserverextTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
FloatingIpTest
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_fl...
Dan Prince (dan-prince) wrote : | # |
Naveed made the tests a bit more robust. Trying one more time.
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~rackspace-titan/nova/minidom-to-etree into lp:nova failed. Below is the output from the failed tests.
CloudTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
- 1528. By Naveed Massjouni
-
And again.
OpenStack Infra (hudson-openstack) wrote : | # |
Attempt to merge into lp:nova failed due to conflicts:
text conflict in nova/tests/
- 1529. By Naveed Massjouni
-
Merge from trunk.
Preview Diff
1 | === modified file 'nova/api/openstack/common.py' |
2 | --- nova/api/openstack/common.py 2011-08-25 21:27:10 +0000 |
3 | +++ nova/api/openstack/common.py 2011-09-13 18:01:31 +0000 |
4 | @@ -16,6 +16,7 @@ |
5 | # under the License. |
6 | |
7 | import functools |
8 | +from lxml import etree |
9 | import re |
10 | import urlparse |
11 | from xml.dom import minidom |
12 | @@ -27,6 +28,7 @@ |
13 | from nova import log as logging |
14 | from nova import quota |
15 | from nova.api.openstack import wsgi |
16 | +from nova.api.openstack import xmlutil |
17 | from nova.compute import vm_states |
18 | from nova.compute import task_states |
19 | |
20 | @@ -308,54 +310,48 @@ |
21 | |
22 | |
23 | class MetadataXMLSerializer(wsgi.XMLDictSerializer): |
24 | + |
25 | + NSMAP = {None: xmlutil.XMLNS_V11} |
26 | + |
27 | def __init__(self, xmlns=wsgi.XMLNS_V11): |
28 | super(MetadataXMLSerializer, self).__init__(xmlns=xmlns) |
29 | |
30 | - def _meta_item_to_xml(self, doc, key, value): |
31 | - node = doc.createElement('meta') |
32 | - doc.appendChild(node) |
33 | - node.setAttribute('key', '%s' % key) |
34 | - text = doc.createTextNode('%s' % value) |
35 | - node.appendChild(text) |
36 | - return node |
37 | - |
38 | - def meta_list_to_xml(self, xml_doc, meta_items): |
39 | - container_node = xml_doc.createElement('metadata') |
40 | - for (key, value) in meta_items: |
41 | - item_node = self._meta_item_to_xml(xml_doc, key, value) |
42 | - container_node.appendChild(item_node) |
43 | - return container_node |
44 | - |
45 | - def _meta_list_to_xml_string(self, metadata_dict): |
46 | - xml_doc = minidom.Document() |
47 | - items = metadata_dict['metadata'].items() |
48 | - container_node = self.meta_list_to_xml(xml_doc, items) |
49 | - xml_doc.appendChild(container_node) |
50 | - self._add_xmlns(container_node) |
51 | - return xml_doc.toxml('UTF-8') |
52 | + def populate_metadata(self, metadata_elem, meta_dict): |
53 | + for (key, value) in meta_dict.items(): |
54 | + elem = etree.SubElement(metadata_elem, 'meta') |
55 | + elem.set('key', str(key)) |
56 | + elem.text = value |
57 | + |
58 | + def _populate_meta_item(self, meta_elem, meta_item_dict): |
59 | + """Populate a meta xml element from a dict.""" |
60 | + (key, value) = meta_item_dict.items()[0] |
61 | + meta_elem.set('key', str(key)) |
62 | + meta_elem.text = value |
63 | |
64 | def index(self, metadata_dict): |
65 | - return self._meta_list_to_xml_string(metadata_dict) |
66 | + metadata = etree.Element('metadata', nsmap=self.NSMAP) |
67 | + self.populate_metadata(metadata, metadata_dict.get('metadata', {})) |
68 | + return self._to_xml(metadata) |
69 | |
70 | def create(self, metadata_dict): |
71 | - return self._meta_list_to_xml_string(metadata_dict) |
72 | + metadata = etree.Element('metadata', nsmap=self.NSMAP) |
73 | + self.populate_metadata(metadata, metadata_dict.get('metadata', {})) |
74 | + return self._to_xml(metadata) |
75 | |
76 | def update_all(self, metadata_dict): |
77 | - return self._meta_list_to_xml_string(metadata_dict) |
78 | - |
79 | - def _meta_item_to_xml_string(self, meta_item_dict): |
80 | - xml_doc = minidom.Document() |
81 | - item_key, item_value = meta_item_dict.items()[0] |
82 | - item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) |
83 | - xml_doc.appendChild(item_node) |
84 | - self._add_xmlns(item_node) |
85 | - return xml_doc.toxml('UTF-8') |
86 | + metadata = etree.Element('metadata', nsmap=self.NSMAP) |
87 | + self.populate_metadata(metadata, metadata_dict.get('metadata', {})) |
88 | + return self._to_xml(metadata) |
89 | |
90 | def show(self, meta_item_dict): |
91 | - return self._meta_item_to_xml_string(meta_item_dict['meta']) |
92 | + meta = etree.Element('meta', nsmap=self.NSMAP) |
93 | + self._populate_meta_item(meta, meta_item_dict['meta']) |
94 | + return self._to_xml(meta) |
95 | |
96 | def update(self, meta_item_dict): |
97 | - return self._meta_item_to_xml_string(meta_item_dict['meta']) |
98 | + meta = etree.Element('meta', nsmap=self.NSMAP) |
99 | + self._populate_meta_item(meta, meta_item_dict['meta']) |
100 | + return self._to_xml(meta) |
101 | |
102 | def default(self, *args, **kwargs): |
103 | return '' |
104 | |
105 | === modified file 'nova/api/openstack/flavors.py' |
106 | --- nova/api/openstack/flavors.py 2011-08-10 06:01:03 +0000 |
107 | +++ nova/api/openstack/flavors.py 2011-09-13 18:01:31 +0000 |
108 | @@ -16,12 +16,13 @@ |
109 | # under the License. |
110 | |
111 | import webob |
112 | -import xml.dom.minidom as minidom |
113 | +from lxml import etree |
114 | |
115 | from nova import db |
116 | from nova import exception |
117 | from nova.api.openstack import views |
118 | from nova.api.openstack import wsgi |
119 | +from nova.api.openstack import xmlutil |
120 | |
121 | |
122 | class Controller(object): |
123 | @@ -78,48 +79,44 @@ |
124 | |
125 | class FlavorXMLSerializer(wsgi.XMLDictSerializer): |
126 | |
127 | + NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM} |
128 | + |
129 | def __init__(self): |
130 | super(FlavorXMLSerializer, self).__init__(xmlns=wsgi.XMLNS_V11) |
131 | |
132 | - def _flavor_to_xml(self, xml_doc, flavor, detailed): |
133 | - flavor_node = xml_doc.createElement('flavor') |
134 | - flavor_node.setAttribute('id', str(flavor['id'])) |
135 | - flavor_node.setAttribute('name', flavor['name']) |
136 | + def _populate_flavor(self, flavor_elem, flavor_dict, detailed=False): |
137 | + """Populate a flavor xml element from a dict.""" |
138 | |
139 | + flavor_elem.set('name', flavor_dict['name']) |
140 | + flavor_elem.set('id', str(flavor_dict['id'])) |
141 | if detailed: |
142 | - flavor_node.setAttribute('ram', str(flavor['ram'])) |
143 | - flavor_node.setAttribute('disk', str(flavor['disk'])) |
144 | - |
145 | - link_nodes = self._create_link_nodes(xml_doc, flavor['links']) |
146 | - for link_node in link_nodes: |
147 | - flavor_node.appendChild(link_node) |
148 | - return flavor_node |
149 | - |
150 | - def _flavors_list_to_xml(self, xml_doc, flavors, detailed): |
151 | - container_node = xml_doc.createElement('flavors') |
152 | - |
153 | - for flavor in flavors: |
154 | - item_node = self._flavor_to_xml(xml_doc, flavor, detailed) |
155 | - container_node.appendChild(item_node) |
156 | - return container_node |
157 | + flavor_elem.set('ram', str(flavor_dict['ram'])) |
158 | + flavor_elem.set('disk', str(flavor_dict['disk'])) |
159 | + for link in flavor_dict.get('links', []): |
160 | + elem = etree.SubElement(flavor_elem, |
161 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
162 | + elem.set('rel', link['rel']) |
163 | + elem.set('href', link['href']) |
164 | + return flavor_elem |
165 | |
166 | def show(self, flavor_container): |
167 | - xml_doc = minidom.Document() |
168 | - flavor = flavor_container['flavor'] |
169 | - node = self._flavor_to_xml(xml_doc, flavor, True) |
170 | - return self.to_xml_string(node, True) |
171 | - |
172 | - def detail(self, flavors_container): |
173 | - xml_doc = minidom.Document() |
174 | - flavors = flavors_container['flavors'] |
175 | - node = self._flavors_list_to_xml(xml_doc, flavors, True) |
176 | - return self.to_xml_string(node, True) |
177 | - |
178 | - def index(self, flavors_container): |
179 | - xml_doc = minidom.Document() |
180 | - flavors = flavors_container['flavors'] |
181 | - node = self._flavors_list_to_xml(xml_doc, flavors, False) |
182 | - return self.to_xml_string(node, True) |
183 | + flavor = etree.Element('flavor', nsmap=self.NSMAP) |
184 | + self._populate_flavor(flavor, flavor_container['flavor'], True) |
185 | + return self._to_xml(flavor) |
186 | + |
187 | + def detail(self, flavors_dict): |
188 | + flavors = etree.Element('flavors', nsmap=self.NSMAP) |
189 | + for flavor_dict in flavors_dict['flavors']: |
190 | + flavor = etree.SubElement(flavors, 'flavor') |
191 | + self._populate_flavor(flavor, flavor_dict, True) |
192 | + return self._to_xml(flavors) |
193 | + |
194 | + def index(self, flavors_dict): |
195 | + flavors = etree.Element('flavors', nsmap=self.NSMAP) |
196 | + for flavor_dict in flavors_dict['flavors']: |
197 | + flavor = etree.SubElement(flavors, 'flavor') |
198 | + self._populate_flavor(flavor, flavor_dict, False) |
199 | + return self._to_xml(flavors) |
200 | |
201 | |
202 | def create_resource(version='1.0'): |
203 | |
204 | === modified file 'nova/api/openstack/images.py' |
205 | --- nova/api/openstack/images.py 2011-09-01 02:43:26 +0000 |
206 | +++ nova/api/openstack/images.py 2011-09-13 18:01:31 +0000 |
207 | @@ -16,8 +16,8 @@ |
208 | import urlparse |
209 | import os.path |
210 | |
211 | +from lxml import etree |
212 | import webob.exc |
213 | -from xml.dom import minidom |
214 | |
215 | from nova import compute |
216 | from nova import exception |
217 | @@ -29,6 +29,7 @@ |
218 | from nova.api.openstack import servers |
219 | from nova.api.openstack.views import images as images_view |
220 | from nova.api.openstack import wsgi |
221 | +from nova.api.openstack import xmlutil |
222 | |
223 | |
224 | LOG = log.getLogger('nova.api.openstack.images') |
225 | @@ -206,93 +207,71 @@ |
226 | |
227 | class ImageXMLSerializer(wsgi.XMLDictSerializer): |
228 | |
229 | - xmlns = wsgi.XMLNS_V11 |
230 | + NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM} |
231 | |
232 | def __init__(self): |
233 | self.metadata_serializer = common.MetadataXMLSerializer() |
234 | |
235 | - def _image_to_xml(self, xml_doc, image): |
236 | - image_node = xml_doc.createElement('image') |
237 | - image_node.setAttribute('id', str(image['id'])) |
238 | - image_node.setAttribute('name', image['name']) |
239 | - link_nodes = self._create_link_nodes(xml_doc, |
240 | - image['links']) |
241 | - for link_node in link_nodes: |
242 | - image_node.appendChild(link_node) |
243 | - return image_node |
244 | - |
245 | - def _image_to_xml_detailed(self, xml_doc, image): |
246 | - image_node = xml_doc.createElement('image') |
247 | - self._add_image_attributes(image_node, image) |
248 | - |
249 | - if 'server' in image: |
250 | - server_node = self._create_server_node(xml_doc, image['server']) |
251 | - image_node.appendChild(server_node) |
252 | - |
253 | - metadata = image.get('metadata', {}).items() |
254 | - if len(metadata) > 0: |
255 | - metadata_node = self._create_metadata_node(xml_doc, metadata) |
256 | - image_node.appendChild(metadata_node) |
257 | - |
258 | - link_nodes = self._create_link_nodes(xml_doc, |
259 | - image['links']) |
260 | - for link_node in link_nodes: |
261 | - image_node.appendChild(link_node) |
262 | - |
263 | - return image_node |
264 | - |
265 | - def _add_image_attributes(self, node, image): |
266 | - node.setAttribute('id', str(image['id'])) |
267 | - node.setAttribute('name', image['name']) |
268 | - node.setAttribute('created', image['created']) |
269 | - node.setAttribute('updated', image['updated']) |
270 | - node.setAttribute('status', image['status']) |
271 | - if 'progress' in image: |
272 | - node.setAttribute('progress', str(image['progress'])) |
273 | - |
274 | - def _create_metadata_node(self, xml_doc, metadata): |
275 | - return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata) |
276 | - |
277 | - def _create_server_node(self, xml_doc, server): |
278 | - server_node = xml_doc.createElement('server') |
279 | - server_node.setAttribute('id', str(server['id'])) |
280 | - link_nodes = self._create_link_nodes(xml_doc, |
281 | - server['links']) |
282 | - for link_node in link_nodes: |
283 | - server_node.appendChild(link_node) |
284 | - return server_node |
285 | - |
286 | - def _image_list_to_xml(self, xml_doc, images, detailed): |
287 | - container_node = xml_doc.createElement('images') |
288 | + def _create_metadata_node(self, metadata_dict): |
289 | + metadata_elem = etree.Element('metadata', nsmap=self.NSMAP) |
290 | + self.metadata_serializer.populate_metadata(metadata_elem, |
291 | + metadata_dict) |
292 | + return metadata_elem |
293 | + |
294 | + def _create_server_node(self, server_dict): |
295 | + server_elem = etree.Element('server', nsmap=self.NSMAP) |
296 | + server_elem.set('id', str(server_dict['id'])) |
297 | + for link in server_dict.get('links', []): |
298 | + elem = etree.SubElement(server_elem, |
299 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
300 | + elem.set('rel', link['rel']) |
301 | + elem.set('href', link['href']) |
302 | + return server_elem |
303 | + |
304 | + def _populate_image(self, image_elem, image_dict, detailed=False): |
305 | + """Populate an image xml element from a dict.""" |
306 | + |
307 | + image_elem.set('name', image_dict['name']) |
308 | + image_elem.set('id', str(image_dict['id'])) |
309 | if detailed: |
310 | - image_to_xml = self._image_to_xml_detailed |
311 | - else: |
312 | - image_to_xml = self._image_to_xml |
313 | - |
314 | - for image in images: |
315 | - item_node = image_to_xml(xml_doc, image) |
316 | - container_node.appendChild(item_node) |
317 | - return container_node |
318 | + image_elem.set('updated', str(image_dict['updated'])) |
319 | + image_elem.set('created', str(image_dict['created'])) |
320 | + image_elem.set('status', str(image_dict['status'])) |
321 | + if 'progress' in image_dict: |
322 | + image_elem.set('progress', str(image_dict['progress'])) |
323 | + if 'server' in image_dict: |
324 | + server_elem = self._create_server_node(image_dict['server']) |
325 | + image_elem.append(server_elem) |
326 | + |
327 | + meta_elem = self._create_metadata_node( |
328 | + image_dict.get('metadata', {})) |
329 | + image_elem.append(meta_elem) |
330 | + |
331 | + for link in image_dict.get('links', []): |
332 | + elem = etree.SubElement(image_elem, |
333 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
334 | + elem.set('rel', link['rel']) |
335 | + elem.set('href', link['href']) |
336 | + return image_elem |
337 | |
338 | def index(self, images_dict): |
339 | - xml_doc = minidom.Document() |
340 | - node = self._image_list_to_xml(xml_doc, |
341 | - images_dict['images'], |
342 | - detailed=False) |
343 | - return self.to_xml_string(node, True) |
344 | + images = etree.Element('images', nsmap=self.NSMAP) |
345 | + for image_dict in images_dict['images']: |
346 | + image = etree.SubElement(images, 'image') |
347 | + self._populate_image(image, image_dict, False) |
348 | + return self._to_xml(images) |
349 | |
350 | def detail(self, images_dict): |
351 | - xml_doc = minidom.Document() |
352 | - node = self._image_list_to_xml(xml_doc, |
353 | - images_dict['images'], |
354 | - detailed=True) |
355 | - return self.to_xml_string(node, True) |
356 | + images = etree.Element('images', nsmap=self.NSMAP) |
357 | + for image_dict in images_dict['images']: |
358 | + image = etree.SubElement(images, 'image') |
359 | + self._populate_image(image, image_dict, True) |
360 | + return self._to_xml(images) |
361 | |
362 | def show(self, image_dict): |
363 | - xml_doc = minidom.Document() |
364 | - node = self._image_to_xml_detailed(xml_doc, |
365 | - image_dict['image']) |
366 | - return self.to_xml_string(node, True) |
367 | + image = etree.Element('image', nsmap=self.NSMAP) |
368 | + self._populate_image(image, image_dict['image'], True) |
369 | + return self._to_xml(image) |
370 | |
371 | |
372 | def create_resource(version='1.0'): |
373 | |
374 | === modified file 'nova/api/openstack/ips.py' |
375 | --- nova/api/openstack/ips.py 2011-07-25 21:00:19 +0000 |
376 | +++ nova/api/openstack/ips.py 2011-09-13 18:01:31 +0000 |
377 | @@ -15,14 +15,15 @@ |
378 | # License for the specific language governing permissions and limitations |
379 | # under the License. |
380 | |
381 | +from lxml import etree |
382 | import time |
383 | -from xml.dom import minidom |
384 | |
385 | from webob import exc |
386 | |
387 | import nova |
388 | import nova.api.openstack.views.addresses |
389 | from nova.api.openstack import wsgi |
390 | +from nova.api.openstack import xmlutil |
391 | from nova import db |
392 | |
393 | |
394 | @@ -102,42 +103,36 @@ |
395 | |
396 | |
397 | class IPXMLSerializer(wsgi.XMLDictSerializer): |
398 | + |
399 | + NSMAP = {None: xmlutil.XMLNS_V11} |
400 | + |
401 | def __init__(self, xmlns=wsgi.XMLNS_V11): |
402 | super(IPXMLSerializer, self).__init__(xmlns=xmlns) |
403 | |
404 | - def _ip_to_xml(self, xml_doc, ip_dict): |
405 | - ip_node = xml_doc.createElement('ip') |
406 | - ip_node.setAttribute('addr', ip_dict['addr']) |
407 | - ip_node.setAttribute('version', str(ip_dict['version'])) |
408 | - return ip_node |
409 | - |
410 | - def _network_to_xml(self, xml_doc, network_id, ip_dicts): |
411 | - network_node = xml_doc.createElement('network') |
412 | - network_node.setAttribute('id', network_id) |
413 | - |
414 | + def populate_addresses_node(self, addresses_elem, addresses_dict): |
415 | + for (network_id, ip_dicts) in addresses_dict.items(): |
416 | + network_elem = self._create_network_node(network_id, ip_dicts) |
417 | + addresses_elem.append(network_elem) |
418 | + |
419 | + def _create_network_node(self, network_id, ip_dicts): |
420 | + network_elem = etree.Element('network', nsmap=self.NSMAP) |
421 | + network_elem.set('id', str(network_id)) |
422 | for ip_dict in ip_dicts: |
423 | - ip_node = self._ip_to_xml(xml_doc, ip_dict) |
424 | - network_node.appendChild(ip_node) |
425 | - |
426 | - return network_node |
427 | - |
428 | - def networks_to_xml(self, xml_doc, networks_container): |
429 | - addresses_node = xml_doc.createElement('addresses') |
430 | - for (network_id, ip_dicts) in networks_container.items(): |
431 | - network_node = self._network_to_xml(xml_doc, network_id, ip_dicts) |
432 | - addresses_node.appendChild(network_node) |
433 | - return addresses_node |
434 | - |
435 | - def show(self, network_container): |
436 | - (network_id, ip_dicts) = network_container.items()[0] |
437 | - xml_doc = minidom.Document() |
438 | - node = self._network_to_xml(xml_doc, network_id, ip_dicts) |
439 | - return self.to_xml_string(node, False) |
440 | - |
441 | - def index(self, addresses_container): |
442 | - xml_doc = minidom.Document() |
443 | - node = self.networks_to_xml(xml_doc, addresses_container['addresses']) |
444 | - return self.to_xml_string(node, False) |
445 | + ip_elem = etree.SubElement(network_elem, 'ip') |
446 | + ip_elem.set('version', str(ip_dict['version'])) |
447 | + ip_elem.set('addr', ip_dict['addr']) |
448 | + return network_elem |
449 | + |
450 | + def show(self, network_dict): |
451 | + (network_id, ip_dicts) = network_dict.items()[0] |
452 | + network = self._create_network_node(network_id, ip_dicts) |
453 | + return self._to_xml(network) |
454 | + |
455 | + def index(self, addresses_dict): |
456 | + addresses = etree.Element('addresses', nsmap=self.NSMAP) |
457 | + self.populate_addresses_node(addresses, |
458 | + addresses_dict.get('addresses', {})) |
459 | + return self._to_xml(addresses) |
460 | |
461 | |
462 | def create_resource(version): |
463 | |
464 | === modified file 'nova/api/openstack/limits.py' |
465 | --- nova/api/openstack/limits.py 2011-07-21 13:43:25 +0000 |
466 | +++ nova/api/openstack/limits.py 2011-09-13 18:01:31 +0000 |
467 | @@ -20,12 +20,12 @@ |
468 | import copy |
469 | import httplib |
470 | import json |
471 | +from lxml import etree |
472 | import math |
473 | import re |
474 | import time |
475 | import urllib |
476 | import webob.exc |
477 | -from xml.dom import minidom |
478 | |
479 | from collections import defaultdict |
480 | |
481 | @@ -38,6 +38,7 @@ |
482 | from nova.api.openstack import faults |
483 | from nova.api.openstack.views import limits as limits_views |
484 | from nova.api.openstack import wsgi |
485 | +from nova.api.openstack import xmlutil |
486 | |
487 | |
488 | # Convenience constants for the limits dictionary passed to Limiter(). |
489 | @@ -81,52 +82,49 @@ |
490 | |
491 | xmlns = wsgi.XMLNS_V11 |
492 | |
493 | + NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM} |
494 | + |
495 | def __init__(self): |
496 | pass |
497 | |
498 | - def _create_rates_node(self, xml_doc, rates): |
499 | - rates_node = xml_doc.createElement('rates') |
500 | + def _create_rates_node(self, rates): |
501 | + rates_elem = etree.Element('rates', nsmap=self.NSMAP) |
502 | for rate in rates: |
503 | - rate_node = xml_doc.createElement('rate') |
504 | - rate_node.setAttribute('uri', rate['uri']) |
505 | - rate_node.setAttribute('regex', rate['regex']) |
506 | - |
507 | + rate_node = etree.SubElement(rates_elem, 'rate') |
508 | + rate_node.set('uri', rate['uri']) |
509 | + rate_node.set('regex', rate['regex']) |
510 | for limit in rate['limit']: |
511 | - limit_node = xml_doc.createElement('limit') |
512 | - limit_node.setAttribute('value', str(limit['value'])) |
513 | - limit_node.setAttribute('verb', limit['verb']) |
514 | - limit_node.setAttribute('remaining', str(limit['remaining'])) |
515 | - limit_node.setAttribute('unit', limit['unit']) |
516 | - limit_node.setAttribute('next-available', |
517 | - str(limit['next-available'])) |
518 | - rate_node.appendChild(limit_node) |
519 | - |
520 | - rates_node.appendChild(rate_node) |
521 | - return rates_node |
522 | - |
523 | - def _create_absolute_node(self, xml_doc, absolutes): |
524 | - absolute_node = xml_doc.createElement('absolute') |
525 | - for key, value in absolutes.iteritems(): |
526 | - limit_node = xml_doc.createElement('limit') |
527 | - limit_node.setAttribute('name', key) |
528 | - limit_node.setAttribute('value', str(value)) |
529 | - absolute_node.appendChild(limit_node) |
530 | - return absolute_node |
531 | - |
532 | - def _limits_to_xml(self, xml_doc, limits): |
533 | - limits_node = xml_doc.createElement('limits') |
534 | - rates_node = self._create_rates_node(xml_doc, limits['rate']) |
535 | - limits_node.appendChild(rates_node) |
536 | - |
537 | - absolute_node = self._create_absolute_node(xml_doc, limits['absolute']) |
538 | - limits_node.appendChild(absolute_node) |
539 | - |
540 | - return limits_node |
541 | + limit_elem = etree.SubElement(rate_node, 'limit') |
542 | + limit_elem.set('value', str(limit['value'])) |
543 | + limit_elem.set('verb', str(limit['verb'])) |
544 | + limit_elem.set('remaining', str(limit['remaining'])) |
545 | + limit_elem.set('unit', str(limit['unit'])) |
546 | + limit_elem.set('next-available', str(limit['next-available'])) |
547 | + return rates_elem |
548 | + |
549 | + def _create_absolute_node(self, absolute_dict): |
550 | + absolute_elem = etree.Element('absolute', nsmap=self.NSMAP) |
551 | + for key, value in absolute_dict.items(): |
552 | + limit_elem = etree.SubElement(absolute_elem, 'limit') |
553 | + limit_elem.set('name', str(key)) |
554 | + limit_elem.set('value', str(value)) |
555 | + return absolute_elem |
556 | + |
557 | + def _populate_limits(self, limits_elem, limits_dict): |
558 | + """Populate a limits xml element from a dict.""" |
559 | + |
560 | + rates_elem = self._create_rates_node( |
561 | + limits_dict.get('rate', [])) |
562 | + limits_elem.append(rates_elem) |
563 | + |
564 | + absolutes_elem = self._create_absolute_node( |
565 | + limits_dict.get('absolute', {})) |
566 | + limits_elem.append(absolutes_elem) |
567 | |
568 | def index(self, limits_dict): |
569 | - xml_doc = minidom.Document() |
570 | - node = self._limits_to_xml(xml_doc, limits_dict['limits']) |
571 | - return self.to_xml_string(node, False) |
572 | + limits = etree.Element('limits', nsmap=self.NSMAP) |
573 | + self._populate_limits(limits, limits_dict['limits']) |
574 | + return self._to_xml(limits) |
575 | |
576 | |
577 | def create_resource(version='1.0'): |
578 | |
579 | === added file 'nova/api/openstack/schemas/v1.1/addresses.rng' |
580 | --- nova/api/openstack/schemas/v1.1/addresses.rng 1970-01-01 00:00:00 +0000 |
581 | +++ nova/api/openstack/schemas/v1.1/addresses.rng 2011-09-13 18:01:31 +0000 |
582 | @@ -0,0 +1,14 @@ |
583 | +<element name="addresses" ns="http://docs.openstack.org/compute/api/v1.1" |
584 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
585 | + <zeroOrMore> |
586 | + <element name="network"> |
587 | + <attribute name="id"> <text/> </attribute> |
588 | + <zeroOrMore> |
589 | + <element name="ip"> |
590 | + <attribute name="version"> <text/> </attribute> |
591 | + <attribute name="addr"> <text/> </attribute> |
592 | + </element> |
593 | + </zeroOrMore> |
594 | + </element> |
595 | + </zeroOrMore> |
596 | +</element> |
597 | |
598 | === added file 'nova/api/openstack/schemas/v1.1/flavor.rng' |
599 | --- nova/api/openstack/schemas/v1.1/flavor.rng 1970-01-01 00:00:00 +0000 |
600 | +++ nova/api/openstack/schemas/v1.1/flavor.rng 2011-09-13 18:01:31 +0000 |
601 | @@ -0,0 +1,10 @@ |
602 | +<element name="flavor" ns="http://docs.openstack.org/compute/api/v1.1" |
603 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
604 | + <attribute name="name"> <text/> </attribute> |
605 | + <attribute name="id"> <text/> </attribute> |
606 | + <attribute name="ram"> <text/> </attribute> |
607 | + <attribute name="disk"> <text/> </attribute> |
608 | + <zeroOrMore> |
609 | + <externalRef href="../atom-link.rng"/> |
610 | + </zeroOrMore> |
611 | +</element> |
612 | |
613 | === added file 'nova/api/openstack/schemas/v1.1/flavors.rng' |
614 | --- nova/api/openstack/schemas/v1.1/flavors.rng 1970-01-01 00:00:00 +0000 |
615 | +++ nova/api/openstack/schemas/v1.1/flavors.rng 2011-09-13 18:01:31 +0000 |
616 | @@ -0,0 +1,6 @@ |
617 | +<element name="flavors" xmlns="http://relaxng.org/ns/structure/1.0" |
618 | + ns="http://docs.openstack.org/compute/api/v1.1"> |
619 | + <zeroOrMore> |
620 | + <externalRef href="flavor.rng"/> |
621 | + </zeroOrMore> |
622 | +</element> |
623 | |
624 | === added file 'nova/api/openstack/schemas/v1.1/flavors_index.rng' |
625 | --- nova/api/openstack/schemas/v1.1/flavors_index.rng 1970-01-01 00:00:00 +0000 |
626 | +++ nova/api/openstack/schemas/v1.1/flavors_index.rng 2011-09-13 18:01:31 +0000 |
627 | @@ -0,0 +1,12 @@ |
628 | +<element name="flavors" ns="http://docs.openstack.org/compute/api/v1.1" |
629 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
630 | + <zeroOrMore> |
631 | + <element name="flavor"> |
632 | + <attribute name="name"> <text/> </attribute> |
633 | + <attribute name="id"> <text/> </attribute> |
634 | + <zeroOrMore> |
635 | + <externalRef href="../atom-link.rng"/> |
636 | + </zeroOrMore> |
637 | + </element> |
638 | + </zeroOrMore> |
639 | +</element> |
640 | |
641 | === added file 'nova/api/openstack/schemas/v1.1/image.rng' |
642 | --- nova/api/openstack/schemas/v1.1/image.rng 1970-01-01 00:00:00 +0000 |
643 | +++ nova/api/openstack/schemas/v1.1/image.rng 2011-09-13 18:01:31 +0000 |
644 | @@ -0,0 +1,30 @@ |
645 | +<element name="image" ns="http://docs.openstack.org/compute/api/v1.1" |
646 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
647 | + <attribute name="name"> <text/> </attribute> |
648 | + <attribute name="id"> <text/> </attribute> |
649 | + <attribute name="updated"> <text/> </attribute> |
650 | + <attribute name="created"> <text/> </attribute> |
651 | + <attribute name="status"> <text/> </attribute> |
652 | + <optional> |
653 | + <attribute name="progress"> <text/> </attribute> |
654 | + </optional> |
655 | + <optional> |
656 | + <element name="server"> |
657 | + <attribute name="id"> <text/> </attribute> |
658 | + <zeroOrMore> |
659 | + <externalRef href="../atom-link.rng"/> |
660 | + </zeroOrMore> |
661 | + </element> |
662 | + </optional> |
663 | + <element name="metadata"> |
664 | + <zeroOrMore> |
665 | + <element name="meta"> |
666 | + <attribute name="key"> <text/> </attribute> |
667 | + <text/> |
668 | + </element> |
669 | + </zeroOrMore> |
670 | + </element> |
671 | + <zeroOrMore> |
672 | + <externalRef href="../atom-link.rng"/> |
673 | + </zeroOrMore> |
674 | +</element> |
675 | |
676 | === added file 'nova/api/openstack/schemas/v1.1/images.rng' |
677 | --- nova/api/openstack/schemas/v1.1/images.rng 1970-01-01 00:00:00 +0000 |
678 | +++ nova/api/openstack/schemas/v1.1/images.rng 2011-09-13 18:01:31 +0000 |
679 | @@ -0,0 +1,6 @@ |
680 | +<element name="images" xmlns="http://relaxng.org/ns/structure/1.0" |
681 | + ns="http://docs.openstack.org/compute/api/v1.1"> |
682 | + <zeroOrMore> |
683 | + <externalRef href="image.rng"/> |
684 | + </zeroOrMore> |
685 | +</element> |
686 | |
687 | === added file 'nova/api/openstack/schemas/v1.1/images_index.rng' |
688 | --- nova/api/openstack/schemas/v1.1/images_index.rng 1970-01-01 00:00:00 +0000 |
689 | +++ nova/api/openstack/schemas/v1.1/images_index.rng 2011-09-13 18:01:31 +0000 |
690 | @@ -0,0 +1,12 @@ |
691 | +<element name="images" ns="http://docs.openstack.org/compute/api/v1.1" |
692 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
693 | + <zeroOrMore> |
694 | + <element name="image"> |
695 | + <attribute name="name"> <text/> </attribute> |
696 | + <attribute name="id"> <text/> </attribute> |
697 | + <zeroOrMore> |
698 | + <externalRef href="../atom-link.rng"/> |
699 | + </zeroOrMore> |
700 | + </element> |
701 | + </zeroOrMore> |
702 | +</element> |
703 | |
704 | === added file 'nova/api/openstack/schemas/v1.1/limits.rng' |
705 | --- nova/api/openstack/schemas/v1.1/limits.rng 1970-01-01 00:00:00 +0000 |
706 | +++ nova/api/openstack/schemas/v1.1/limits.rng 2011-09-13 18:01:31 +0000 |
707 | @@ -0,0 +1,28 @@ |
708 | +<element name="limits" ns="http://docs.openstack.org/compute/api/v1.1" |
709 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
710 | + <element name="rates"> |
711 | + <zeroOrMore> |
712 | + <element name="rate"> |
713 | + <attribute name="uri"> <text/> </attribute> |
714 | + <attribute name="regex"> <text/> </attribute> |
715 | + <zeroOrMore> |
716 | + <element name="limit"> |
717 | + <attribute name="value"> <text/> </attribute> |
718 | + <attribute name="verb"> <text/> </attribute> |
719 | + <attribute name="remaining"> <text/> </attribute> |
720 | + <attribute name="unit"> <text/> </attribute> |
721 | + <attribute name="next-available"> <text/> </attribute> |
722 | + </element> |
723 | + </zeroOrMore> |
724 | + </element> |
725 | + </zeroOrMore> |
726 | + </element> |
727 | + <element name="absolute"> |
728 | + <zeroOrMore> |
729 | + <element name="limit"> |
730 | + <attribute name="name"> <text/> </attribute> |
731 | + <attribute name="value"> <text/> </attribute> |
732 | + </element> |
733 | + </zeroOrMore> |
734 | + </element> |
735 | +</element> |
736 | |
737 | === added file 'nova/api/openstack/schemas/v1.1/metadata.rng' |
738 | --- nova/api/openstack/schemas/v1.1/metadata.rng 1970-01-01 00:00:00 +0000 |
739 | +++ nova/api/openstack/schemas/v1.1/metadata.rng 2011-09-13 18:01:31 +0000 |
740 | @@ -0,0 +1,9 @@ |
741 | + <element name="metadata" ns="http://docs.openstack.org/compute/api/v1.1" |
742 | + xmlns="http://relaxng.org/ns/structure/1.0"> |
743 | + <zeroOrMore> |
744 | + <element name="meta"> |
745 | + <attribute name="key"> <text/> </attribute> |
746 | + <text/> |
747 | + </element> |
748 | + </zeroOrMore> |
749 | + </element> |
750 | |
751 | === modified file 'nova/api/openstack/schemas/v1.1/server.rng' |
752 | --- nova/api/openstack/schemas/v1.1/server.rng 2011-09-02 19:52:02 +0000 |
753 | +++ nova/api/openstack/schemas/v1.1/server.rng 2011-09-13 18:01:31 +0000 |
754 | @@ -17,9 +17,6 @@ |
755 | <optional> |
756 | <attribute name="adminPass"> <text/> </attribute> |
757 | </optional> |
758 | - <zeroOrMore> |
759 | - <externalRef href="../atom-link.rng"/> |
760 | - </zeroOrMore> |
761 | <element name="image"> |
762 | <attribute name="id"> <text/> </attribute> |
763 | <externalRef href="../atom-link.rng"/> |
764 | @@ -49,4 +46,7 @@ |
765 | </element> |
766 | </zeroOrMore> |
767 | </element> |
768 | + <zeroOrMore> |
769 | + <externalRef href="../atom-link.rng"/> |
770 | + </zeroOrMore> |
771 | </element> |
772 | |
773 | === modified file 'nova/api/openstack/servers.py' |
774 | --- nova/api/openstack/servers.py 2011-09-08 21:10:03 +0000 |
775 | +++ nova/api/openstack/servers.py 2011-09-13 18:01:31 +0000 |
776 | @@ -17,8 +17,8 @@ |
777 | import os |
778 | import traceback |
779 | |
780 | +from lxml import etree |
781 | from webob import exc |
782 | -from xml.dom import minidom |
783 | import webob |
784 | |
785 | from nova import compute |
786 | @@ -38,6 +38,7 @@ |
787 | import nova.api.openstack.views.flavors |
788 | import nova.api.openstack.views.images |
789 | import nova.api.openstack.views.servers |
790 | +from nova.api.openstack import xmlutil |
791 | |
792 | |
793 | LOG = logging.getLogger('nova.api.openstack.servers') |
794 | @@ -850,130 +851,113 @@ |
795 | |
796 | class ServerXMLSerializer(wsgi.XMLDictSerializer): |
797 | |
798 | - xmlns = wsgi.XMLNS_V11 |
799 | + NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM} |
800 | |
801 | def __init__(self): |
802 | self.metadata_serializer = common.MetadataXMLSerializer() |
803 | self.addresses_serializer = ips.IPXMLSerializer() |
804 | |
805 | - def _create_basic_entity_node(self, xml_doc, id, links, name): |
806 | - basic_node = xml_doc.createElement(name) |
807 | - basic_node.setAttribute('id', str(id)) |
808 | - link_nodes = self._create_link_nodes(xml_doc, links) |
809 | - for link_node in link_nodes: |
810 | - basic_node.appendChild(link_node) |
811 | - return basic_node |
812 | - |
813 | - def _create_metadata_node(self, xml_doc, metadata): |
814 | - return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata) |
815 | - |
816 | - def _create_addresses_node(self, xml_doc, addresses): |
817 | - return self.addresses_serializer.networks_to_xml(xml_doc, addresses) |
818 | - |
819 | - def _add_server_attributes(self, node, server): |
820 | - node.setAttribute('id', str(server['id'])) |
821 | - node.setAttribute('userId', str(server['user_id'])) |
822 | - node.setAttribute('tenantId', str(server['tenant_id'])) |
823 | - node.setAttribute('uuid', str(server['uuid'])) |
824 | - node.setAttribute('hostId', str(server['hostId'])) |
825 | - node.setAttribute('name', server['name']) |
826 | - node.setAttribute('created', str(server['created'])) |
827 | - node.setAttribute('updated', str(server['updated'])) |
828 | - node.setAttribute('status', server['status']) |
829 | - if 'accessIPv4' in server: |
830 | - node.setAttribute('accessIPv4', str(server['accessIPv4'])) |
831 | - if 'accessIPv6' in server: |
832 | - node.setAttribute('accessIPv6', str(server['accessIPv6'])) |
833 | - if 'progress' in server: |
834 | - node.setAttribute('progress', str(server['progress'])) |
835 | - |
836 | - def _server_to_xml(self, xml_doc, server): |
837 | - server_node = xml_doc.createElement('server') |
838 | - server_node.setAttribute('id', str(server['id'])) |
839 | - server_node.setAttribute('name', server['name']) |
840 | - link_nodes = self._create_link_nodes(xml_doc, |
841 | - server['links']) |
842 | - for link_node in link_nodes: |
843 | - server_node.appendChild(link_node) |
844 | - return server_node |
845 | - |
846 | - def _server_to_xml_detailed(self, xml_doc, server): |
847 | - server_node = xml_doc.createElement('server') |
848 | - self._add_server_attributes(server_node, server) |
849 | - |
850 | - link_nodes = self._create_link_nodes(xml_doc, |
851 | - server['links']) |
852 | - for link_node in link_nodes: |
853 | - server_node.appendChild(link_node) |
854 | - |
855 | - if 'image' in server: |
856 | - image_node = self._create_basic_entity_node(xml_doc, |
857 | - server['image']['id'], |
858 | - server['image']['links'], |
859 | - 'image') |
860 | - server_node.appendChild(image_node) |
861 | - |
862 | - if 'flavor' in server: |
863 | - flavor_node = self._create_basic_entity_node(xml_doc, |
864 | - server['flavor']['id'], |
865 | - server['flavor']['links'], |
866 | - 'flavor') |
867 | - server_node.appendChild(flavor_node) |
868 | - |
869 | - metadata = server.get('metadata', {}).items() |
870 | - if len(metadata) > 0: |
871 | - metadata_node = self._create_metadata_node(xml_doc, metadata) |
872 | - server_node.appendChild(metadata_node) |
873 | - |
874 | - addresses_node = self._create_addresses_node(xml_doc, |
875 | - server['addresses']) |
876 | - server_node.appendChild(addresses_node) |
877 | - |
878 | - if 'security_groups' in server: |
879 | - security_groups_node = self._create_security_groups_node(xml_doc, |
880 | - server['security_groups']) |
881 | - server_node.appendChild(security_groups_node) |
882 | - |
883 | - return server_node |
884 | - |
885 | - def _server_list_to_xml(self, xml_doc, servers, detailed): |
886 | - container_node = xml_doc.createElement('servers') |
887 | + def _create_metadata_node(self, metadata_dict): |
888 | + metadata_elem = etree.Element('metadata', nsmap=self.NSMAP) |
889 | + self.metadata_serializer.populate_metadata(metadata_elem, |
890 | + metadata_dict) |
891 | + return metadata_elem |
892 | + |
893 | + def _create_image_node(self, image_dict): |
894 | + image_elem = etree.Element('image', nsmap=self.NSMAP) |
895 | + image_elem.set('id', str(image_dict['id'])) |
896 | + for link in image_dict.get('links', []): |
897 | + elem = etree.SubElement(image_elem, |
898 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
899 | + elem.set('rel', link['rel']) |
900 | + elem.set('href', link['href']) |
901 | + return image_elem |
902 | + |
903 | + def _create_flavor_node(self, flavor_dict): |
904 | + flavor_elem = etree.Element('flavor', nsmap=self.NSMAP) |
905 | + flavor_elem.set('id', str(flavor_dict['id'])) |
906 | + for link in flavor_dict.get('links', []): |
907 | + elem = etree.SubElement(flavor_elem, |
908 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
909 | + elem.set('rel', link['rel']) |
910 | + elem.set('href', link['href']) |
911 | + return flavor_elem |
912 | + |
913 | + def _create_addresses_node(self, addresses_dict): |
914 | + addresses_elem = etree.Element('addresses', nsmap=self.NSMAP) |
915 | + self.addresses_serializer.populate_addresses_node(addresses_elem, |
916 | + addresses_dict) |
917 | + return addresses_elem |
918 | + |
919 | + def _populate_server(self, server_elem, server_dict, detailed=False): |
920 | + """Populate a server xml element from a dict.""" |
921 | + |
922 | + server_elem.set('name', server_dict['name']) |
923 | + server_elem.set('id', str(server_dict['id'])) |
924 | if detailed: |
925 | - server_to_xml = self._server_to_xml_detailed |
926 | - else: |
927 | - server_to_xml = self._server_to_xml |
928 | - |
929 | - for server in servers: |
930 | - item_node = server_to_xml(xml_doc, server) |
931 | - container_node.appendChild(item_node) |
932 | - return container_node |
933 | + server_elem.set('uuid', str(server_dict['uuid'])) |
934 | + server_elem.set('userId', str(server_dict['user_id'])) |
935 | + server_elem.set('tenantId', str(server_dict['tenant_id'])) |
936 | + server_elem.set('updated', str(server_dict['updated'])) |
937 | + server_elem.set('created', str(server_dict['created'])) |
938 | + server_elem.set('hostId', str(server_dict['hostId'])) |
939 | + server_elem.set('accessIPv4', str(server_dict['accessIPv4'])) |
940 | + server_elem.set('accessIPv6', str(server_dict['accessIPv6'])) |
941 | + server_elem.set('status', str(server_dict['status'])) |
942 | + if 'progress' in server_dict: |
943 | + server_elem.set('progress', str(server_dict['progress'])) |
944 | + image_elem = self._create_image_node(server_dict['image']) |
945 | + server_elem.append(image_elem) |
946 | + |
947 | + flavor_elem = self._create_flavor_node(server_dict['flavor']) |
948 | + server_elem.append(flavor_elem) |
949 | + |
950 | + meta_elem = self._create_metadata_node( |
951 | + server_dict.get('metadata', {})) |
952 | + server_elem.append(meta_elem) |
953 | + |
954 | + addresses_elem = self._create_addresses_node( |
955 | + server_dict.get('addresses', {})) |
956 | + server_elem.append(addresses_elem) |
957 | + groups = server_dict.get('security_groups') |
958 | + if groups: |
959 | + groups_elem = etree.SubElement(server_elem, 'security_groups') |
960 | + for group in groups: |
961 | + group_elem = etree.SubElement(groups_elem, |
962 | + 'security_group') |
963 | + group_elem.set('name', group['name']) |
964 | + |
965 | + for link in server_dict.get('links', []): |
966 | + elem = etree.SubElement(server_elem, |
967 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
968 | + elem.set('rel', link['rel']) |
969 | + elem.set('href', link['href']) |
970 | + return server_elem |
971 | |
972 | def index(self, servers_dict): |
973 | - xml_doc = minidom.Document() |
974 | - node = self._server_list_to_xml(xml_doc, |
975 | - servers_dict['servers'], |
976 | - detailed=False) |
977 | - return self.to_xml_string(node, True) |
978 | + servers = etree.Element('servers', nsmap=self.NSMAP) |
979 | + for server_dict in servers_dict['servers']: |
980 | + server = etree.SubElement(servers, 'server') |
981 | + self._populate_server(server, server_dict, False) |
982 | + return self._to_xml(servers) |
983 | |
984 | def detail(self, servers_dict): |
985 | - xml_doc = minidom.Document() |
986 | - node = self._server_list_to_xml(xml_doc, |
987 | - servers_dict['servers'], |
988 | - detailed=True) |
989 | - return self.to_xml_string(node, True) |
990 | + servers = etree.Element('servers', nsmap=self.NSMAP) |
991 | + for server_dict in servers_dict['servers']: |
992 | + server = etree.SubElement(servers, 'server') |
993 | + self._populate_server(server, server_dict, True) |
994 | + return self._to_xml(servers) |
995 | |
996 | def show(self, server_dict): |
997 | - xml_doc = minidom.Document() |
998 | - node = self._server_to_xml_detailed(xml_doc, |
999 | - server_dict['server']) |
1000 | - return self.to_xml_string(node, True) |
1001 | + server = etree.Element('server', nsmap=self.NSMAP) |
1002 | + self._populate_server(server, server_dict['server'], True) |
1003 | + return self._to_xml(server) |
1004 | |
1005 | def create(self, server_dict): |
1006 | - xml_doc = minidom.Document() |
1007 | - node = self._server_to_xml_detailed(xml_doc, |
1008 | - server_dict['server']) |
1009 | - node.setAttribute('adminPass', server_dict['server']['adminPass']) |
1010 | - return self.to_xml_string(node, True) |
1011 | + server = etree.Element('server', nsmap=self.NSMAP) |
1012 | + self._populate_server(server, server_dict['server'], True) |
1013 | + server.set('adminPass', server_dict['server']['adminPass']) |
1014 | + return self._to_xml(server) |
1015 | |
1016 | def action(self, server_dict): |
1017 | #NOTE(bcwaldon): We need a way to serialize actions individually. This |
1018 | @@ -981,23 +965,9 @@ |
1019 | return self.create(server_dict) |
1020 | |
1021 | def update(self, server_dict): |
1022 | - xml_doc = minidom.Document() |
1023 | - node = self._server_to_xml_detailed(xml_doc, |
1024 | - server_dict['server']) |
1025 | - return self.to_xml_string(node, True) |
1026 | - |
1027 | - def _security_group_to_xml(self, doc, security_group): |
1028 | - node = doc.createElement('security_group') |
1029 | - node.setAttribute('name', str(security_group.get('name'))) |
1030 | - return node |
1031 | - |
1032 | - def _create_security_groups_node(self, xml_doc, security_groups): |
1033 | - security_groups_node = xml_doc.createElement('security_groups') |
1034 | - if security_groups: |
1035 | - for security_group in security_groups: |
1036 | - node = self._security_group_to_xml(xml_doc, security_group) |
1037 | - security_groups_node.appendChild(node) |
1038 | - return security_groups_node |
1039 | + server = etree.Element('server', nsmap=self.NSMAP) |
1040 | + self._populate_server(server, server_dict['server'], True) |
1041 | + return self._to_xml(server) |
1042 | |
1043 | |
1044 | def create_resource(version='1.0'): |
1045 | |
1046 | === modified file 'nova/api/openstack/versions.py' |
1047 | --- nova/api/openstack/versions.py 2011-08-03 13:54:00 +0000 |
1048 | +++ nova/api/openstack/versions.py 2011-09-13 18:01:31 +0000 |
1049 | @@ -16,12 +16,13 @@ |
1050 | # under the License. |
1051 | |
1052 | from datetime import datetime |
1053 | +from lxml import etree |
1054 | import webob |
1055 | import webob.dec |
1056 | -from xml.dom import minidom |
1057 | |
1058 | import nova.api.openstack.views.versions |
1059 | from nova.api.openstack import wsgi |
1060 | +from nova.api.openstack import xmlutil |
1061 | |
1062 | |
1063 | VERSIONS = { |
1064 | @@ -159,83 +160,51 @@ |
1065 | |
1066 | |
1067 | class VersionsXMLSerializer(wsgi.XMLDictSerializer): |
1068 | - #TODO(wwolf): this is temporary until we get rid of toprettyxml |
1069 | - # in the base class (XMLDictSerializer), which I plan to do in |
1070 | - # another branch |
1071 | - def to_xml_string(self, node, has_atom=False): |
1072 | - self._add_xmlns(node, has_atom) |
1073 | - return node.toxml(encoding='UTF-8') |
1074 | - |
1075 | - def _versions_to_xml(self, versions, name="versions", xmlns=None): |
1076 | - root = self._xml_doc.createElement(name) |
1077 | - root.setAttribute("xmlns", wsgi.XMLNS_V11) |
1078 | - root.setAttribute("xmlns:atom", wsgi.XMLNS_ATOM) |
1079 | - |
1080 | - for version in versions: |
1081 | - root.appendChild(self._create_version_node(version)) |
1082 | - |
1083 | - return root |
1084 | - |
1085 | - def _create_media_types(self, media_types): |
1086 | - base = self._xml_doc.createElement('media-types') |
1087 | - for type in media_types: |
1088 | - node = self._xml_doc.createElement('media-type') |
1089 | - node.setAttribute('base', type['base']) |
1090 | - node.setAttribute('type', type['type']) |
1091 | - base.appendChild(node) |
1092 | - |
1093 | - return base |
1094 | - |
1095 | - def _create_version_node(self, version, create_ns=False): |
1096 | - version_node = self._xml_doc.createElement('version') |
1097 | - if create_ns: |
1098 | - xmlns = wsgi.XMLNS_V11 |
1099 | - xmlns_atom = wsgi.XMLNS_ATOM |
1100 | - version_node.setAttribute('xmlns', xmlns) |
1101 | - version_node.setAttribute('xmlns:atom', xmlns_atom) |
1102 | - |
1103 | - version_node.setAttribute('id', version['id']) |
1104 | - version_node.setAttribute('status', version['status']) |
1105 | + |
1106 | + def _populate_version(self, version_node, version): |
1107 | + version_node.set('id', version['id']) |
1108 | + version_node.set('status', version['status']) |
1109 | if 'updated' in version: |
1110 | - version_node.setAttribute('updated', version['updated']) |
1111 | - |
1112 | + version_node.set('updated', version['updated']) |
1113 | if 'media-types' in version: |
1114 | - media_types = self._create_media_types(version['media-types']) |
1115 | - version_node.appendChild(media_types) |
1116 | - |
1117 | - link_nodes = self._create_link_nodes(self._xml_doc, version['links']) |
1118 | - for link in link_nodes: |
1119 | - version_node.appendChild(link) |
1120 | - |
1121 | - return version_node |
1122 | + media_types = etree.SubElement(version_node, 'media-types') |
1123 | + for mtype in version['media-types']: |
1124 | + elem = etree.SubElement(media_types, 'media-type') |
1125 | + elem.set('base', mtype['base']) |
1126 | + elem.set('type', mtype['type']) |
1127 | + for link in version.get('links', []): |
1128 | + elem = etree.SubElement(version_node, |
1129 | + '{%s}link' % xmlutil.XMLNS_ATOM) |
1130 | + elem.set('rel', link['rel']) |
1131 | + elem.set('href', link['href']) |
1132 | + if 'type' in link: |
1133 | + elem.set('type', link['type']) |
1134 | + |
1135 | + NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM} |
1136 | |
1137 | def index(self, data): |
1138 | - self._xml_doc = minidom.Document() |
1139 | - node = self._versions_to_xml(data['versions']) |
1140 | - |
1141 | - return self.to_xml_string(node) |
1142 | + root = etree.Element('versions', nsmap=self.NSMAP) |
1143 | + for version in data['versions']: |
1144 | + version_elem = etree.SubElement(root, 'version') |
1145 | + self._populate_version(version_elem, version) |
1146 | + return self._to_xml(root) |
1147 | |
1148 | def show(self, data): |
1149 | - self._xml_doc = minidom.Document() |
1150 | - node = self._create_version_node(data['version'], True) |
1151 | - |
1152 | - return self.to_xml_string(node) |
1153 | + root = etree.Element('version', nsmap=self.NSMAP) |
1154 | + self._populate_version(root, data['version']) |
1155 | + return self._to_xml(root) |
1156 | |
1157 | def multi(self, data): |
1158 | - self._xml_doc = minidom.Document() |
1159 | - node = self._versions_to_xml(data['choices'], 'choices', |
1160 | - xmlns=wsgi.XMLNS_V11) |
1161 | - |
1162 | - return self.to_xml_string(node) |
1163 | + root = etree.Element('choices', nsmap=self.NSMAP) |
1164 | + for version in data['choices']: |
1165 | + version_elem = etree.SubElement(root, 'version') |
1166 | + self._populate_version(version_elem, version) |
1167 | + return self._to_xml(root) |
1168 | |
1169 | |
1170 | class VersionsAtomSerializer(wsgi.XMLDictSerializer): |
1171 | - #TODO(wwolf): this is temporary until we get rid of toprettyxml |
1172 | - # in the base class (XMLDictSerializer), which I plan to do in |
1173 | - # another branch |
1174 | - def to_xml_string(self, node, has_atom=False): |
1175 | - self._add_xmlns(node, has_atom) |
1176 | - return node.toxml(encoding='UTF-8') |
1177 | + |
1178 | + NSMAP = {None: xmlutil.XMLNS_ATOM} |
1179 | |
1180 | def __init__(self, metadata=None, xmlns=None): |
1181 | self.metadata = metadata or {} |
1182 | @@ -244,14 +213,6 @@ |
1183 | else: |
1184 | self.xmlns = xmlns |
1185 | |
1186 | - def _create_text_elem(self, name, text, type=None): |
1187 | - elem = self._xml_doc.createElement(name) |
1188 | - if type: |
1189 | - elem.setAttribute('type', type) |
1190 | - elem_text = self._xml_doc.createTextNode(text) |
1191 | - elem.appendChild(elem_text) |
1192 | - return elem |
1193 | - |
1194 | def _get_most_recent_update(self, versions): |
1195 | recent = None |
1196 | for version in versions: |
1197 | @@ -269,105 +230,64 @@ |
1198 | link_href = link_href.rstrip('/') |
1199 | return link_href.rsplit('/', 1)[0] + '/' |
1200 | |
1201 | - def _create_detail_meta(self, root, version): |
1202 | - title = self._create_text_elem('title', "About This Version", |
1203 | - type='text') |
1204 | - |
1205 | - updated = self._create_text_elem('updated', version['updated']) |
1206 | - |
1207 | - uri = version['links'][0]['href'] |
1208 | - id = self._create_text_elem('id', uri) |
1209 | - |
1210 | - link = self._xml_doc.createElement('link') |
1211 | - link.setAttribute('rel', 'self') |
1212 | - link.setAttribute('href', uri) |
1213 | - |
1214 | - author = self._xml_doc.createElement('author') |
1215 | - author_name = self._create_text_elem('name', 'Rackspace') |
1216 | - author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/') |
1217 | - author.appendChild(author_name) |
1218 | - author.appendChild(author_uri) |
1219 | - |
1220 | - root.appendChild(title) |
1221 | - root.appendChild(updated) |
1222 | - root.appendChild(id) |
1223 | - root.appendChild(author) |
1224 | - root.appendChild(link) |
1225 | - |
1226 | - def _create_list_meta(self, root, versions): |
1227 | - title = self._create_text_elem('title', "Available API Versions", |
1228 | - type='text') |
1229 | + def _create_feed(self, versions, feed_title, feed_id): |
1230 | + feed = etree.Element('feed', nsmap=self.NSMAP) |
1231 | + title = etree.SubElement(feed, 'title') |
1232 | + title.set('type', 'text') |
1233 | + title.text = feed_title |
1234 | + |
1235 | # Set this updated to the most recently updated version |
1236 | recent = self._get_most_recent_update(versions) |
1237 | - updated = self._create_text_elem('updated', recent) |
1238 | - |
1239 | - base_url = self._get_base_url(versions[0]['links'][0]['href']) |
1240 | - id = self._create_text_elem('id', base_url) |
1241 | - |
1242 | - link = self._xml_doc.createElement('link') |
1243 | - link.setAttribute('rel', 'self') |
1244 | - link.setAttribute('href', base_url) |
1245 | - |
1246 | - author = self._xml_doc.createElement('author') |
1247 | - author_name = self._create_text_elem('name', 'Rackspace') |
1248 | - author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/') |
1249 | - author.appendChild(author_name) |
1250 | - author.appendChild(author_uri) |
1251 | - |
1252 | - root.appendChild(title) |
1253 | - root.appendChild(updated) |
1254 | - root.appendChild(id) |
1255 | - root.appendChild(author) |
1256 | - root.appendChild(link) |
1257 | - |
1258 | - def _create_version_entries(self, root, versions): |
1259 | + etree.SubElement(feed, 'updated').text = recent |
1260 | + |
1261 | + etree.SubElement(feed, 'id').text = feed_id |
1262 | + |
1263 | + link = etree.SubElement(feed, 'link') |
1264 | + link.set('rel', 'self') |
1265 | + link.set('href', feed_id) |
1266 | + |
1267 | + author = etree.SubElement(feed, 'author') |
1268 | + etree.SubElement(author, 'name').text = 'Rackspace' |
1269 | + etree.SubElement(author, 'uri').text = 'http://www.rackspace.com/' |
1270 | + |
1271 | for version in versions: |
1272 | - entry = self._xml_doc.createElement('entry') |
1273 | - |
1274 | - id = self._create_text_elem('id', version['links'][0]['href']) |
1275 | - title = self._create_text_elem('title', |
1276 | - 'Version %s' % version['id'], |
1277 | - type='text') |
1278 | - updated = self._create_text_elem('updated', version['updated']) |
1279 | - |
1280 | - entry.appendChild(id) |
1281 | - entry.appendChild(title) |
1282 | - entry.appendChild(updated) |
1283 | - |
1284 | - for link in version['links']: |
1285 | - link_node = self._xml_doc.createElement('link') |
1286 | - link_node.setAttribute('rel', link['rel']) |
1287 | - link_node.setAttribute('href', link['href']) |
1288 | - if 'type' in link: |
1289 | - link_node.setAttribute('type', link['type']) |
1290 | - |
1291 | - entry.appendChild(link_node) |
1292 | - |
1293 | - content = self._create_text_elem('content', |
1294 | - 'Version %s %s (%s)' % |
1295 | - (version['id'], |
1296 | - version['status'], |
1297 | - version['updated']), |
1298 | - type='text') |
1299 | - |
1300 | - entry.appendChild(content) |
1301 | - root.appendChild(entry) |
1302 | + feed.append(self._create_version_entry(version)) |
1303 | + |
1304 | + return feed |
1305 | + |
1306 | + def _create_version_entry(self, version): |
1307 | + entry = etree.Element('entry') |
1308 | + etree.SubElement(entry, 'id').text = version['links'][0]['href'] |
1309 | + title = etree.SubElement(entry, 'title') |
1310 | + title.set('type', 'text') |
1311 | + title.text = 'Version %s' % version['id'] |
1312 | + etree.SubElement(entry, 'updated').text = version['updated'] |
1313 | + |
1314 | + for link in version['links']: |
1315 | + link_elem = etree.SubElement(entry, 'link') |
1316 | + link_elem.set('rel', link['rel']) |
1317 | + link_elem.set('href', link['href']) |
1318 | + if 'type' in link: |
1319 | + link_elem.set('type', link['type']) |
1320 | + |
1321 | + content = etree.SubElement(entry, 'content') |
1322 | + content.set('type', 'text') |
1323 | + content.text = 'Version %s %s (%s)' % (version['id'], |
1324 | + version['status'], |
1325 | + version['updated']) |
1326 | + return entry |
1327 | |
1328 | def index(self, data): |
1329 | - self._xml_doc = minidom.Document() |
1330 | - node = self._xml_doc.createElementNS(self.xmlns, 'feed') |
1331 | - self._create_list_meta(node, data['versions']) |
1332 | - self._create_version_entries(node, data['versions']) |
1333 | - |
1334 | - return self.to_xml_string(node) |
1335 | + versions = data['versions'] |
1336 | + feed_id = self._get_base_url(versions[0]['links'][0]['href']) |
1337 | + feed = self._create_feed(versions, 'Available API Versions', feed_id) |
1338 | + return self._to_xml(feed) |
1339 | |
1340 | def show(self, data): |
1341 | - self._xml_doc = minidom.Document() |
1342 | - node = self._xml_doc.createElementNS(self.xmlns, 'feed') |
1343 | - self._create_detail_meta(node, data['version']) |
1344 | - self._create_version_entries(node, [data['version']]) |
1345 | - |
1346 | - return self.to_xml_string(node) |
1347 | + version = data['version'] |
1348 | + feed_id = version['links'][0]['href'] |
1349 | + feed = self._create_feed([version], 'About This Version', feed_id) |
1350 | + return self._to_xml(feed) |
1351 | |
1352 | |
1353 | class VersionsHeadersSerializer(wsgi.ResponseHeadersSerializer): |
1354 | |
1355 | === modified file 'nova/api/openstack/views/versions.py' |
1356 | --- nova/api/openstack/views/versions.py 2011-08-03 13:54:00 +0000 |
1357 | +++ nova/api/openstack/views/versions.py 2011-09-13 18:01:31 +0000 |
1358 | @@ -52,7 +52,7 @@ |
1359 | |
1360 | def build_versions(self, versions): |
1361 | version_objs = [] |
1362 | - for version in versions: |
1363 | + for version in sorted(versions.keys()): |
1364 | version = versions[version] |
1365 | version_objs.append({ |
1366 | "id": version['id'], |
1367 | |
1368 | === modified file 'nova/api/openstack/wsgi.py' |
1369 | --- nova/api/openstack/wsgi.py 2011-08-23 17:08:42 +0000 |
1370 | +++ nova/api/openstack/wsgi.py 2011-09-13 18:01:31 +0000 |
1371 | @@ -1,5 +1,6 @@ |
1372 | |
1373 | import json |
1374 | +from lxml import etree |
1375 | import webob |
1376 | from xml.dom import minidom |
1377 | from xml.parsers import expat |
1378 | @@ -392,6 +393,10 @@ |
1379 | link_nodes.append(link_node) |
1380 | return link_nodes |
1381 | |
1382 | + def _to_xml(self, root): |
1383 | + """Convert the xml object to an xml string.""" |
1384 | + return etree.tostring(root, encoding='UTF-8', xml_declaration=True) |
1385 | + |
1386 | |
1387 | class ResponseHeadersSerializer(ActionDispatcher): |
1388 | """Default response headers serialization""" |
1389 | |
1390 | === modified file 'nova/tests/api/openstack/common.py' |
1391 | --- nova/tests/api/openstack/common.py 2011-03-09 20:08:11 +0000 |
1392 | +++ nova/tests/api/openstack/common.py 2011-09-13 18:01:31 +0000 |
1393 | @@ -34,3 +34,25 @@ |
1394 | req.body = json.dumps(body) |
1395 | return req |
1396 | return web_request |
1397 | + |
1398 | + |
1399 | +def compare_links(actual, expected): |
1400 | + """Compare xml atom links.""" |
1401 | + |
1402 | + return compare_tree_to_dict(actual, expected, ('rel', 'href', 'type')) |
1403 | + |
1404 | + |
1405 | +def compare_media_types(actual, expected): |
1406 | + """Compare xml media types.""" |
1407 | + |
1408 | + return compare_tree_to_dict(actual, expected, ('base', 'type')) |
1409 | + |
1410 | + |
1411 | +def compare_tree_to_dict(actual, expected, keys): |
1412 | + """Compare parts of lxml.etree objects to dicts.""" |
1413 | + |
1414 | + for elem, data in zip(actual, expected): |
1415 | + for key in keys: |
1416 | + if elem.get(key) != data.get(key): |
1417 | + return False |
1418 | + return True |
1419 | |
1420 | === modified file 'nova/tests/api/openstack/contrib/test_createserverext.py' |
1421 | --- nova/tests/api/openstack/contrib/test_createserverext.py 2011-09-06 19:47:09 +0000 |
1422 | +++ nova/tests/api/openstack/contrib/test_createserverext.py 2011-09-13 18:01:31 +0000 |
1423 | @@ -49,9 +49,13 @@ |
1424 | "id": 1, |
1425 | "display_name": "test_server", |
1426 | "uuid": FAKE_UUID, |
1427 | + "user_id": 'fake_user_id', |
1428 | + "tenant_id": 'fake_tenant_id', |
1429 | "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), |
1430 | "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), |
1431 | - "security_groups": [{"id": 1, "name": "test"}] |
1432 | + "security_groups": [{"id": 1, "name": "test"}], |
1433 | + "image_ref": 'http://foo.com/123', |
1434 | + "instance_type": {"flavorid": '124'}, |
1435 | } |
1436 | |
1437 | |
1438 | |
1439 | === modified file 'nova/tests/api/openstack/test_common.py' |
1440 | --- nova/tests/api/openstack/test_common.py 2011-08-11 19:30:43 +0000 |
1441 | +++ nova/tests/api/openstack/test_common.py 2011-09-13 18:01:31 +0000 |
1442 | @@ -19,6 +19,7 @@ |
1443 | Test suites for 'common' code used throughout the OpenStack HTTP API. |
1444 | """ |
1445 | |
1446 | +from lxml import etree |
1447 | import webob.exc |
1448 | import xml.dom.minidom as minidom |
1449 | |
1450 | @@ -26,6 +27,11 @@ |
1451 | |
1452 | from nova import test |
1453 | from nova.api.openstack import common |
1454 | +from nova.api.openstack import xmlutil |
1455 | + |
1456 | + |
1457 | +NS = "{http://docs.openstack.org/compute/api/v1.1}" |
1458 | +ATOMNS = "{http://www.w3.org/2005/Atom}" |
1459 | |
1460 | |
1461 | class LimiterTest(test.TestCase): |
1462 | @@ -314,6 +320,20 @@ |
1463 | |
1464 | class MetadataXMLSerializationTest(test.TestCase): |
1465 | |
1466 | + def test_xml_declaration(self): |
1467 | + serializer = common.MetadataXMLSerializer() |
1468 | + fixture = { |
1469 | + 'metadata': { |
1470 | + 'one': 'two', |
1471 | + 'three': 'four', |
1472 | + }, |
1473 | + } |
1474 | + |
1475 | + output = serializer.serialize(fixture, 'index') |
1476 | + print output |
1477 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
1478 | + self.assertTrue(has_dec) |
1479 | + |
1480 | def test_index(self): |
1481 | serializer = common.MetadataXMLSerializer() |
1482 | fixture = { |
1483 | @@ -323,16 +343,16 @@ |
1484 | }, |
1485 | } |
1486 | output = serializer.serialize(fixture, 'index') |
1487 | - actual = minidom.parseString(output.replace(" ", "")) |
1488 | - |
1489 | - expected = minidom.parseString(""" |
1490 | - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
1491 | - <meta key="three">four</meta> |
1492 | - <meta key="one">two</meta> |
1493 | - </metadata> |
1494 | - """.replace(" ", "").replace("\n", "")) |
1495 | - |
1496 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1497 | + print output |
1498 | + root = etree.XML(output) |
1499 | + xmlutil.validate_schema(root, 'metadata') |
1500 | + metadata_dict = fixture['metadata'] |
1501 | + metadata_elems = root.findall('{0}meta'.format(NS)) |
1502 | + self.assertEqual(len(metadata_elems), 2) |
1503 | + for i, metadata_elem in enumerate(metadata_elems): |
1504 | + (meta_key, meta_value) = metadata_dict.items()[i] |
1505 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
1506 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
1507 | |
1508 | def test_index_null(self): |
1509 | serializer = common.MetadataXMLSerializer() |
1510 | @@ -342,15 +362,16 @@ |
1511 | }, |
1512 | } |
1513 | output = serializer.serialize(fixture, 'index') |
1514 | - actual = minidom.parseString(output.replace(" ", "")) |
1515 | - |
1516 | - expected = minidom.parseString(""" |
1517 | - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
1518 | - <meta key="None">None</meta> |
1519 | - </metadata> |
1520 | - """.replace(" ", "").replace("\n", "")) |
1521 | - |
1522 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1523 | + print output |
1524 | + root = etree.XML(output) |
1525 | + xmlutil.validate_schema(root, 'metadata') |
1526 | + metadata_dict = fixture['metadata'] |
1527 | + metadata_elems = root.findall('{0}meta'.format(NS)) |
1528 | + self.assertEqual(len(metadata_elems), 1) |
1529 | + for i, metadata_elem in enumerate(metadata_elems): |
1530 | + (meta_key, meta_value) = metadata_dict.items()[i] |
1531 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
1532 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
1533 | |
1534 | def test_index_unicode(self): |
1535 | serializer = common.MetadataXMLSerializer() |
1536 | @@ -360,15 +381,16 @@ |
1537 | }, |
1538 | } |
1539 | output = serializer.serialize(fixture, 'index') |
1540 | - actual = minidom.parseString(output.replace(" ", "")) |
1541 | - |
1542 | - expected = minidom.parseString(u""" |
1543 | - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
1544 | - <meta key="three">Jos\xe9</meta> |
1545 | - </metadata> |
1546 | - """.encode("UTF-8").replace(" ", "").replace("\n", "")) |
1547 | - |
1548 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1549 | + print output |
1550 | + root = etree.XML(output) |
1551 | + xmlutil.validate_schema(root, 'metadata') |
1552 | + metadata_dict = fixture['metadata'] |
1553 | + metadata_elems = root.findall('{0}meta'.format(NS)) |
1554 | + self.assertEqual(len(metadata_elems), 1) |
1555 | + for i, metadata_elem in enumerate(metadata_elems): |
1556 | + (meta_key, meta_value) = metadata_dict.items()[i] |
1557 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
1558 | + self.assertEqual(metadata_elem.text.strip(), meta_value) |
1559 | |
1560 | def test_show(self): |
1561 | serializer = common.MetadataXMLSerializer() |
1562 | @@ -378,14 +400,12 @@ |
1563 | }, |
1564 | } |
1565 | output = serializer.serialize(fixture, 'show') |
1566 | - actual = minidom.parseString(output.replace(" ", "")) |
1567 | - |
1568 | - expected = minidom.parseString(""" |
1569 | - <meta xmlns="http://docs.openstack.org/compute/api/v1.1" |
1570 | - key="one">two</meta> |
1571 | - """.replace(" ", "").replace("\n", "")) |
1572 | - |
1573 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1574 | + print output |
1575 | + root = etree.XML(output) |
1576 | + meta_dict = fixture['meta'] |
1577 | + (meta_key, meta_value) = meta_dict.items()[0] |
1578 | + self.assertEqual(str(root.get('key')), str(meta_key)) |
1579 | + self.assertEqual(root.text.strip(), meta_value) |
1580 | |
1581 | def test_update_all(self): |
1582 | serializer = common.MetadataXMLSerializer() |
1583 | @@ -396,16 +416,16 @@ |
1584 | }, |
1585 | } |
1586 | output = serializer.serialize(fixture, 'update_all') |
1587 | - actual = minidom.parseString(output.replace(" ", "")) |
1588 | - |
1589 | - expected = minidom.parseString(""" |
1590 | - <metadata xmlns="http://docs.openstack.org/compute/api/v1.1"> |
1591 | - <meta key="key6">value6</meta> |
1592 | - <meta key="key4">value4</meta> |
1593 | - </metadata> |
1594 | - """.replace(" ", "").replace("\n", "")) |
1595 | - |
1596 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1597 | + print output |
1598 | + root = etree.XML(output) |
1599 | + xmlutil.validate_schema(root, 'metadata') |
1600 | + metadata_dict = fixture['metadata'] |
1601 | + metadata_elems = root.findall('{0}meta'.format(NS)) |
1602 | + self.assertEqual(len(metadata_elems), 2) |
1603 | + for i, metadata_elem in enumerate(metadata_elems): |
1604 | + (meta_key, meta_value) = metadata_dict.items()[i] |
1605 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
1606 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
1607 | |
1608 | def test_update_item(self): |
1609 | serializer = common.MetadataXMLSerializer() |
1610 | @@ -415,14 +435,12 @@ |
1611 | }, |
1612 | } |
1613 | output = serializer.serialize(fixture, 'update') |
1614 | - actual = minidom.parseString(output.replace(" ", "")) |
1615 | - |
1616 | - expected = minidom.parseString(""" |
1617 | - <meta xmlns="http://docs.openstack.org/compute/api/v1.1" |
1618 | - key="one">two</meta> |
1619 | - """.replace(" ", "").replace("\n", "")) |
1620 | - |
1621 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1622 | + print output |
1623 | + root = etree.XML(output) |
1624 | + meta_dict = fixture['meta'] |
1625 | + (meta_key, meta_value) = meta_dict.items()[0] |
1626 | + self.assertEqual(str(root.get('key')), str(meta_key)) |
1627 | + self.assertEqual(root.text.strip(), meta_value) |
1628 | |
1629 | def test_create(self): |
1630 | serializer = common.MetadataXMLSerializer() |
1631 | @@ -434,6 +452,16 @@ |
1632 | }, |
1633 | } |
1634 | output = serializer.serialize(fixture, 'create') |
1635 | + print output |
1636 | + root = etree.XML(output) |
1637 | + xmlutil.validate_schema(root, 'metadata') |
1638 | + metadata_dict = fixture['metadata'] |
1639 | + metadata_elems = root.findall('{0}meta'.format(NS)) |
1640 | + self.assertEqual(len(metadata_elems), 3) |
1641 | + for i, metadata_elem in enumerate(metadata_elems): |
1642 | + (meta_key, meta_value) = metadata_dict.items()[i] |
1643 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
1644 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
1645 | actual = minidom.parseString(output.replace(" ", "")) |
1646 | |
1647 | expected = minidom.parseString(""" |
1648 | |
1649 | === modified file 'nova/tests/api/openstack/test_flavors.py' |
1650 | --- nova/tests/api/openstack/test_flavors.py 2011-08-11 18:40:05 +0000 |
1651 | +++ nova/tests/api/openstack/test_flavors.py 2011-09-13 18:01:31 +0000 |
1652 | @@ -17,16 +17,21 @@ |
1653 | |
1654 | import json |
1655 | import webob |
1656 | -import xml.dom.minidom as minidom |
1657 | +from lxml import etree |
1658 | |
1659 | from nova.api.openstack import flavors |
1660 | import nova.db.api |
1661 | from nova import exception |
1662 | from nova import test |
1663 | +from nova.api.openstack import xmlutil |
1664 | from nova.tests.api.openstack import fakes |
1665 | from nova import wsgi |
1666 | |
1667 | |
1668 | +NS = "{http://docs.openstack.org/compute/api/v1.1}" |
1669 | +ATOMNS = "{http://www.w3.org/2005/Atom}" |
1670 | + |
1671 | + |
1672 | def stub_flavor(flavorid, name, memory_mb="256", local_gb="10"): |
1673 | return { |
1674 | "flavorid": str(flavorid), |
1675 | @@ -262,10 +267,37 @@ |
1676 | |
1677 | class FlavorsXMLSerializationTest(test.TestCase): |
1678 | |
1679 | + def test_xml_declaration(self): |
1680 | + serializer = flavors.FlavorXMLSerializer() |
1681 | + |
1682 | + fixture = { |
1683 | + "flavor": { |
1684 | + "id": "12", |
1685 | + "name": "asdf", |
1686 | + "ram": "256", |
1687 | + "disk": "10", |
1688 | + "links": [ |
1689 | + { |
1690 | + "rel": "self", |
1691 | + "href": "http://localhost/v1.1/fake/flavors/12", |
1692 | + }, |
1693 | + { |
1694 | + "rel": "bookmark", |
1695 | + "href": "http://localhost/fake/flavors/12", |
1696 | + }, |
1697 | + ], |
1698 | + }, |
1699 | + } |
1700 | + |
1701 | + output = serializer.serialize(fixture, 'show') |
1702 | + print output |
1703 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
1704 | + self.assertTrue(has_dec) |
1705 | + |
1706 | def test_show(self): |
1707 | serializer = flavors.FlavorXMLSerializer() |
1708 | |
1709 | - input = { |
1710 | + fixture = { |
1711 | "flavor": { |
1712 | "id": "12", |
1713 | "name": "asdf", |
1714 | @@ -284,29 +316,25 @@ |
1715 | }, |
1716 | } |
1717 | |
1718 | - output = serializer.serialize(input, 'show') |
1719 | - actual = minidom.parseString(output.replace(" ", "")) |
1720 | - |
1721 | - expected = minidom.parseString(""" |
1722 | - <flavor xmlns="http://docs.openstack.org/compute/api/v1.1" |
1723 | - xmlns:atom="http://www.w3.org/2005/Atom" |
1724 | - id="12" |
1725 | - name="asdf" |
1726 | - ram="256" |
1727 | - disk="10"> |
1728 | - <atom:link href="http://localhost/v1.1/fake/flavors/12" |
1729 | - rel="self"/> |
1730 | - <atom:link href="http://localhost/fake/flavors/12" |
1731 | - rel="bookmark"/> |
1732 | - </flavor> |
1733 | - """.replace(" ", "")) |
1734 | - |
1735 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1736 | + output = serializer.serialize(fixture, 'show') |
1737 | + print output |
1738 | + root = etree.XML(output) |
1739 | + xmlutil.validate_schema(root, 'flavor') |
1740 | + flavor_dict = fixture['flavor'] |
1741 | + |
1742 | + for key in ['name', 'id', 'ram', 'disk']: |
1743 | + self.assertEqual(root.get(key), str(flavor_dict[key])) |
1744 | + |
1745 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
1746 | + self.assertEqual(len(link_nodes), 2) |
1747 | + for i, link in enumerate(flavor_dict['links']): |
1748 | + for key, value in link.items(): |
1749 | + self.assertEqual(link_nodes[i].get(key), value) |
1750 | |
1751 | def test_show_handles_integers(self): |
1752 | serializer = flavors.FlavorXMLSerializer() |
1753 | |
1754 | - input = { |
1755 | + fixture = { |
1756 | "flavor": { |
1757 | "id": 12, |
1758 | "name": "asdf", |
1759 | @@ -325,29 +353,25 @@ |
1760 | }, |
1761 | } |
1762 | |
1763 | - output = serializer.serialize(input, 'show') |
1764 | - actual = minidom.parseString(output.replace(" ", "")) |
1765 | - |
1766 | - expected = minidom.parseString(""" |
1767 | - <flavor xmlns="http://docs.openstack.org/compute/api/v1.1" |
1768 | - xmlns:atom="http://www.w3.org/2005/Atom" |
1769 | - id="12" |
1770 | - name="asdf" |
1771 | - ram="256" |
1772 | - disk="10"> |
1773 | - <atom:link href="http://localhost/v1.1/fake/flavors/12" |
1774 | - rel="self"/> |
1775 | - <atom:link href="http://localhost/fake/flavors/12" |
1776 | - rel="bookmark"/> |
1777 | - </flavor> |
1778 | - """.replace(" ", "")) |
1779 | - |
1780 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1781 | + output = serializer.serialize(fixture, 'show') |
1782 | + print output |
1783 | + root = etree.XML(output) |
1784 | + xmlutil.validate_schema(root, 'flavor') |
1785 | + flavor_dict = fixture['flavor'] |
1786 | + |
1787 | + for key in ['name', 'id', 'ram', 'disk']: |
1788 | + self.assertEqual(root.get(key), str(flavor_dict[key])) |
1789 | + |
1790 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
1791 | + self.assertEqual(len(link_nodes), 2) |
1792 | + for i, link in enumerate(flavor_dict['links']): |
1793 | + for key, value in link.items(): |
1794 | + self.assertEqual(link_nodes[i].get(key), value) |
1795 | |
1796 | def test_detail(self): |
1797 | serializer = flavors.FlavorXMLSerializer() |
1798 | |
1799 | - input = { |
1800 | + fixture = { |
1801 | "flavors": [ |
1802 | { |
1803 | "id": "23", |
1804 | @@ -383,39 +407,28 @@ |
1805 | ], |
1806 | } |
1807 | |
1808 | - output = serializer.serialize(input, 'detail') |
1809 | - actual = minidom.parseString(output.replace(" ", "")) |
1810 | - |
1811 | - expected = minidom.parseString(""" |
1812 | - <flavors xmlns="http://docs.openstack.org/compute/api/v1.1" |
1813 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
1814 | - <flavor id="23" |
1815 | - name="flavor 23" |
1816 | - ram="512" |
1817 | - disk="20"> |
1818 | - <atom:link href="http://localhost/v1.1/fake/flavors/23" |
1819 | - rel="self"/> |
1820 | - <atom:link href="http://localhost/fake/flavors/23" |
1821 | - rel="bookmark"/> |
1822 | - </flavor> |
1823 | - <flavor id="13" |
1824 | - name="flavor 13" |
1825 | - ram="256" |
1826 | - disk="10"> |
1827 | - <atom:link href="http://localhost/v1.1/fake/flavors/13" |
1828 | - rel="self"/> |
1829 | - <atom:link href="http://localhost/fake/flavors/13" |
1830 | - rel="bookmark"/> |
1831 | - </flavor> |
1832 | - </flavors> |
1833 | - """.replace(" ", "") % locals()) |
1834 | - |
1835 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1836 | + output = serializer.serialize(fixture, 'detail') |
1837 | + print output |
1838 | + root = etree.XML(output) |
1839 | + xmlutil.validate_schema(root, 'flavors') |
1840 | + flavor_elems = root.findall('{0}flavor'.format(NS)) |
1841 | + self.assertEqual(len(flavor_elems), 2) |
1842 | + for i, flavor_elem in enumerate(flavor_elems): |
1843 | + flavor_dict = fixture['flavors'][i] |
1844 | + |
1845 | + for key in ['name', 'id', 'ram', 'disk']: |
1846 | + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) |
1847 | + |
1848 | + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) |
1849 | + self.assertEqual(len(link_nodes), 2) |
1850 | + for i, link in enumerate(flavor_dict['links']): |
1851 | + for key, value in link.items(): |
1852 | + self.assertEqual(link_nodes[i].get(key), value) |
1853 | |
1854 | def test_index(self): |
1855 | serializer = flavors.FlavorXMLSerializer() |
1856 | |
1857 | - input = { |
1858 | + fixture = { |
1859 | "flavors": [ |
1860 | { |
1861 | "id": "23", |
1862 | @@ -451,42 +464,34 @@ |
1863 | ], |
1864 | } |
1865 | |
1866 | - output = serializer.serialize(input, 'index') |
1867 | - actual = minidom.parseString(output.replace(" ", "")) |
1868 | - |
1869 | - expected = minidom.parseString(""" |
1870 | - <flavors xmlns="http://docs.openstack.org/compute/api/v1.1" |
1871 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
1872 | - <flavor id="23" name="flavor 23"> |
1873 | - <atom:link href="http://localhost/v1.1/fake/flavors/23" |
1874 | - rel="self"/> |
1875 | - <atom:link href="http://localhost/fake/flavors/23" |
1876 | - rel="bookmark"/> |
1877 | - </flavor> |
1878 | - <flavor id="13" name="flavor 13"> |
1879 | - <atom:link href="http://localhost/v1.1/fake/flavors/13" |
1880 | - rel="self"/> |
1881 | - <atom:link href="http://localhost/fake/flavors/13" |
1882 | - rel="bookmark"/> |
1883 | - </flavor> |
1884 | - </flavors> |
1885 | - """.replace(" ", "") % locals()) |
1886 | - |
1887 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1888 | + output = serializer.serialize(fixture, 'index') |
1889 | + print output |
1890 | + root = etree.XML(output) |
1891 | + xmlutil.validate_schema(root, 'flavors_index') |
1892 | + flavor_elems = root.findall('{0}flavor'.format(NS)) |
1893 | + self.assertEqual(len(flavor_elems), 2) |
1894 | + for i, flavor_elem in enumerate(flavor_elems): |
1895 | + flavor_dict = fixture['flavors'][i] |
1896 | + |
1897 | + for key in ['name', 'id']: |
1898 | + self.assertEqual(flavor_elem.get(key), str(flavor_dict[key])) |
1899 | + |
1900 | + link_nodes = flavor_elem.findall('{0}link'.format(ATOMNS)) |
1901 | + self.assertEqual(len(link_nodes), 2) |
1902 | + for i, link in enumerate(flavor_dict['links']): |
1903 | + for key, value in link.items(): |
1904 | + self.assertEqual(link_nodes[i].get(key), value) |
1905 | |
1906 | def test_index_empty(self): |
1907 | serializer = flavors.FlavorXMLSerializer() |
1908 | |
1909 | - input = { |
1910 | + fixture = { |
1911 | "flavors": [], |
1912 | } |
1913 | |
1914 | - output = serializer.serialize(input, 'index') |
1915 | - actual = minidom.parseString(output.replace(" ", "")) |
1916 | - |
1917 | - expected = minidom.parseString(""" |
1918 | - <flavors xmlns="http://docs.openstack.org/compute/api/v1.1" |
1919 | - xmlns:atom="http://www.w3.org/2005/Atom" /> |
1920 | - """.replace(" ", "") % locals()) |
1921 | - |
1922 | - self.assertEqual(expected.toxml(), actual.toxml()) |
1923 | + output = serializer.serialize(fixture, 'index') |
1924 | + print output |
1925 | + root = etree.XML(output) |
1926 | + xmlutil.validate_schema(root, 'flavors_index') |
1927 | + flavor_elems = root.findall('{0}flavor'.format(NS)) |
1928 | + self.assertEqual(len(flavor_elems), 0) |
1929 | |
1930 | === modified file 'nova/tests/api/openstack/test_images.py' |
1931 | --- nova/tests/api/openstack/test_images.py 2011-09-12 21:52:29 +0000 |
1932 | +++ nova/tests/api/openstack/test_images.py 2011-09-13 18:01:31 +0000 |
1933 | @@ -24,6 +24,7 @@ |
1934 | import json |
1935 | import xml.dom.minidom as minidom |
1936 | |
1937 | +from lxml import etree |
1938 | import mox |
1939 | import stubout |
1940 | import webob |
1941 | @@ -31,10 +32,13 @@ |
1942 | from nova import context |
1943 | import nova.api.openstack |
1944 | from nova.api.openstack import images |
1945 | +from nova.api.openstack import xmlutil |
1946 | from nova import test |
1947 | from nova.tests.api.openstack import fakes |
1948 | |
1949 | |
1950 | +NS = "{http://docs.openstack.org/compute/api/v1.1}" |
1951 | +ATOMNS = "{http://www.w3.org/2005/Atom}" |
1952 | NOW_API_FORMAT = "2010-10-11T10:30:22Z" |
1953 | |
1954 | |
1955 | @@ -972,6 +976,51 @@ |
1956 | IMAGE_HREF = 'http://localhost/v1.1/fake/images/%s' |
1957 | IMAGE_BOOKMARK = 'http://localhost/fake/images/%s' |
1958 | |
1959 | + def test_xml_declaration(self): |
1960 | + serializer = images.ImageXMLSerializer() |
1961 | + |
1962 | + fixture = { |
1963 | + 'image': { |
1964 | + 'id': 1, |
1965 | + 'name': 'Image1', |
1966 | + 'created': self.TIMESTAMP, |
1967 | + 'updated': self.TIMESTAMP, |
1968 | + 'status': 'ACTIVE', |
1969 | + 'progress': 80, |
1970 | + 'server': { |
1971 | + 'id': '1', |
1972 | + 'links': [ |
1973 | + { |
1974 | + 'href': self.SERVER_HREF, |
1975 | + 'rel': 'self', |
1976 | + }, |
1977 | + { |
1978 | + 'href': self.SERVER_BOOKMARK, |
1979 | + 'rel': 'bookmark', |
1980 | + }, |
1981 | + ], |
1982 | + }, |
1983 | + 'metadata': { |
1984 | + 'key1': 'value1', |
1985 | + }, |
1986 | + 'links': [ |
1987 | + { |
1988 | + 'href': self.IMAGE_HREF % 1, |
1989 | + 'rel': 'self', |
1990 | + }, |
1991 | + { |
1992 | + 'href': self.IMAGE_BOOKMARK % 1, |
1993 | + 'rel': 'bookmark', |
1994 | + }, |
1995 | + ], |
1996 | + }, |
1997 | + } |
1998 | + |
1999 | + output = serializer.serialize(fixture, 'show') |
2000 | + print output |
2001 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
2002 | + self.assertTrue(has_dec) |
2003 | + |
2004 | def test_show(self): |
2005 | serializer = images.ImageXMLSerializer() |
2006 | |
2007 | @@ -984,7 +1033,7 @@ |
2008 | 'status': 'ACTIVE', |
2009 | 'progress': 80, |
2010 | 'server': { |
2011 | - 'id': 1, |
2012 | + 'id': '1', |
2013 | 'links': [ |
2014 | { |
2015 | 'href': self.SERVER_HREF, |
2016 | @@ -1013,37 +1062,35 @@ |
2017 | } |
2018 | |
2019 | output = serializer.serialize(fixture, 'show') |
2020 | - actual = minidom.parseString(output.replace(" ", "")) |
2021 | - |
2022 | - expected_server_href = self.SERVER_HREF |
2023 | - expected_server_bookmark = self.SERVER_BOOKMARK |
2024 | - expected_href = self.IMAGE_HREF % 1 |
2025 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2026 | - expected_now = self.TIMESTAMP |
2027 | - expected = minidom.parseString(""" |
2028 | - <image id="1" |
2029 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2030 | - xmlns:atom="http://www.w3.org/2005/Atom" |
2031 | - name="Image1" |
2032 | - updated="%(expected_now)s" |
2033 | - created="%(expected_now)s" |
2034 | - status="ACTIVE" |
2035 | - progress="80"> |
2036 | - <server id="1"> |
2037 | - <atom:link rel="self" href="%(expected_server_href)s"/> |
2038 | - <atom:link rel="bookmark" href="%(expected_server_bookmark)s"/> |
2039 | - </server> |
2040 | - <metadata> |
2041 | - <meta key="key1"> |
2042 | - value1 |
2043 | - </meta> |
2044 | - </metadata> |
2045 | - <atom:link href="%(expected_href)s" rel="self"/> |
2046 | - <atom:link href="%(expected_bookmark)s" rel="bookmark"/> |
2047 | - </image> |
2048 | - """.replace(" ", "") % (locals())) |
2049 | - |
2050 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2051 | + print output |
2052 | + root = etree.XML(output) |
2053 | + xmlutil.validate_schema(root, 'image') |
2054 | + image_dict = fixture['image'] |
2055 | + |
2056 | + for key in ['name', 'id', 'updated', 'created', 'status', 'progress']: |
2057 | + self.assertEqual(root.get(key), str(image_dict[key])) |
2058 | + |
2059 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
2060 | + self.assertEqual(len(link_nodes), 2) |
2061 | + for i, link in enumerate(image_dict['links']): |
2062 | + for key, value in link.items(): |
2063 | + self.assertEqual(link_nodes[i].get(key), value) |
2064 | + |
2065 | + metadata_root = root.find('{0}metadata'.format(NS)) |
2066 | + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) |
2067 | + self.assertEqual(len(metadata_elems), 1) |
2068 | + for i, metadata_elem in enumerate(metadata_elems): |
2069 | + (meta_key, meta_value) = image_dict['metadata'].items()[i] |
2070 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
2071 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
2072 | + |
2073 | + server_root = root.find('{0}server'.format(NS)) |
2074 | + self.assertEqual(server_root.get('id'), image_dict['server']['id']) |
2075 | + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) |
2076 | + self.assertEqual(len(link_nodes), 2) |
2077 | + for i, link in enumerate(image_dict['server']['links']): |
2078 | + for key, value in link.items(): |
2079 | + self.assertEqual(link_nodes[i].get(key), value) |
2080 | |
2081 | def test_show_zero_metadata(self): |
2082 | serializer = images.ImageXMLSerializer() |
2083 | @@ -1056,7 +1103,7 @@ |
2084 | 'updated': self.TIMESTAMP, |
2085 | 'status': 'ACTIVE', |
2086 | 'server': { |
2087 | - 'id': 1, |
2088 | + 'id': '1', |
2089 | 'links': [ |
2090 | { |
2091 | 'href': self.SERVER_HREF, |
2092 | @@ -1083,31 +1130,31 @@ |
2093 | } |
2094 | |
2095 | output = serializer.serialize(fixture, 'show') |
2096 | - actual = minidom.parseString(output.replace(" ", "")) |
2097 | - |
2098 | - expected_server_href = self.SERVER_HREF |
2099 | - expected_server_bookmark = self.SERVER_BOOKMARK |
2100 | - expected_href = self.IMAGE_HREF % 1 |
2101 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2102 | - expected_now = self.TIMESTAMP |
2103 | - expected = minidom.parseString(""" |
2104 | - <image id="1" |
2105 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2106 | - xmlns:atom="http://www.w3.org/2005/Atom" |
2107 | - name="Image1" |
2108 | - updated="%(expected_now)s" |
2109 | - created="%(expected_now)s" |
2110 | - status="ACTIVE"> |
2111 | - <server id="1"> |
2112 | - <atom:link rel="self" href="%(expected_server_href)s"/> |
2113 | - <atom:link rel="bookmark" href="%(expected_server_bookmark)s"/> |
2114 | - </server> |
2115 | - <atom:link href="%(expected_href)s" rel="self"/> |
2116 | - <atom:link href="%(expected_bookmark)s" rel="bookmark"/> |
2117 | - </image> |
2118 | - """.replace(" ", "") % (locals())) |
2119 | - |
2120 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2121 | + print output |
2122 | + root = etree.XML(output) |
2123 | + xmlutil.validate_schema(root, 'image') |
2124 | + image_dict = fixture['image'] |
2125 | + |
2126 | + for key in ['name', 'id', 'updated', 'created', 'status']: |
2127 | + self.assertEqual(root.get(key), str(image_dict[key])) |
2128 | + |
2129 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
2130 | + self.assertEqual(len(link_nodes), 2) |
2131 | + for i, link in enumerate(image_dict['links']): |
2132 | + for key, value in link.items(): |
2133 | + self.assertEqual(link_nodes[i].get(key), value) |
2134 | + |
2135 | + metadata_root = root.find('{0}metadata'.format(NS)) |
2136 | + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) |
2137 | + self.assertEqual(len(meta_nodes), 0) |
2138 | + |
2139 | + server_root = root.find('{0}server'.format(NS)) |
2140 | + self.assertEqual(server_root.get('id'), image_dict['server']['id']) |
2141 | + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) |
2142 | + self.assertEqual(len(link_nodes), 2) |
2143 | + for i, link in enumerate(image_dict['server']['links']): |
2144 | + for key, value in link.items(): |
2145 | + self.assertEqual(link_nodes[i].get(key), value) |
2146 | |
2147 | def test_show_image_no_metadata_key(self): |
2148 | serializer = images.ImageXMLSerializer() |
2149 | @@ -1120,7 +1167,7 @@ |
2150 | 'updated': self.TIMESTAMP, |
2151 | 'status': 'ACTIVE', |
2152 | 'server': { |
2153 | - 'id': 1, |
2154 | + 'id': '1', |
2155 | 'links': [ |
2156 | { |
2157 | 'href': self.SERVER_HREF, |
2158 | @@ -1146,31 +1193,31 @@ |
2159 | } |
2160 | |
2161 | output = serializer.serialize(fixture, 'show') |
2162 | - actual = minidom.parseString(output.replace(" ", "")) |
2163 | - |
2164 | - expected_server_href = self.SERVER_HREF |
2165 | - expected_server_bookmark = self.SERVER_BOOKMARK |
2166 | - expected_href = self.IMAGE_HREF % 1 |
2167 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2168 | - expected_now = self.TIMESTAMP |
2169 | - expected = minidom.parseString(""" |
2170 | - <image id="1" |
2171 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2172 | - xmlns:atom="http://www.w3.org/2005/Atom" |
2173 | - name="Image1" |
2174 | - updated="%(expected_now)s" |
2175 | - created="%(expected_now)s" |
2176 | - status="ACTIVE"> |
2177 | - <server id="1"> |
2178 | - <atom:link rel="self" href="%(expected_server_href)s"/> |
2179 | - <atom:link rel="bookmark" href="%(expected_server_bookmark)s"/> |
2180 | - </server> |
2181 | - <atom:link href="%(expected_href)s" rel="self"/> |
2182 | - <atom:link href="%(expected_bookmark)s" rel="bookmark"/> |
2183 | - </image> |
2184 | - """.replace(" ", "") % (locals())) |
2185 | - |
2186 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2187 | + print output |
2188 | + root = etree.XML(output) |
2189 | + xmlutil.validate_schema(root, 'image') |
2190 | + image_dict = fixture['image'] |
2191 | + |
2192 | + for key in ['name', 'id', 'updated', 'created', 'status']: |
2193 | + self.assertEqual(root.get(key), str(image_dict[key])) |
2194 | + |
2195 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
2196 | + self.assertEqual(len(link_nodes), 2) |
2197 | + for i, link in enumerate(image_dict['links']): |
2198 | + for key, value in link.items(): |
2199 | + self.assertEqual(link_nodes[i].get(key), value) |
2200 | + |
2201 | + metadata_root = root.find('{0}metadata'.format(NS)) |
2202 | + meta_nodes = root.findall('{0}meta'.format(ATOMNS)) |
2203 | + self.assertEqual(len(meta_nodes), 0) |
2204 | + |
2205 | + server_root = root.find('{0}server'.format(NS)) |
2206 | + self.assertEqual(server_root.get('id'), image_dict['server']['id']) |
2207 | + link_nodes = server_root.findall('{0}link'.format(ATOMNS)) |
2208 | + self.assertEqual(len(link_nodes), 2) |
2209 | + for i, link in enumerate(image_dict['server']['links']): |
2210 | + for key, value in link.items(): |
2211 | + self.assertEqual(link_nodes[i].get(key), value) |
2212 | |
2213 | def test_show_no_server(self): |
2214 | serializer = images.ImageXMLSerializer() |
2215 | @@ -1199,30 +1246,30 @@ |
2216 | } |
2217 | |
2218 | output = serializer.serialize(fixture, 'show') |
2219 | - actual = minidom.parseString(output.replace(" ", "")) |
2220 | - |
2221 | - expected_href = self.IMAGE_HREF % 1 |
2222 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2223 | - expected_now = self.TIMESTAMP |
2224 | - expected = minidom.parseString(""" |
2225 | - <image id="1" |
2226 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2227 | - xmlns:atom="http://www.w3.org/2005/Atom" |
2228 | - name="Image1" |
2229 | - updated="%(expected_now)s" |
2230 | - created="%(expected_now)s" |
2231 | - status="ACTIVE"> |
2232 | - <metadata> |
2233 | - <meta key="key1"> |
2234 | - value1 |
2235 | - </meta> |
2236 | - </metadata> |
2237 | - <atom:link href="%(expected_href)s" rel="self"/> |
2238 | - <atom:link href="%(expected_bookmark)s" rel="bookmark"/> |
2239 | - </image> |
2240 | - """.replace(" ", "") % (locals())) |
2241 | - |
2242 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2243 | + print output |
2244 | + root = etree.XML(output) |
2245 | + xmlutil.validate_schema(root, 'image') |
2246 | + image_dict = fixture['image'] |
2247 | + |
2248 | + for key in ['name', 'id', 'updated', 'created', 'status']: |
2249 | + self.assertEqual(root.get(key), str(image_dict[key])) |
2250 | + |
2251 | + link_nodes = root.findall('{0}link'.format(ATOMNS)) |
2252 | + self.assertEqual(len(link_nodes), 2) |
2253 | + for i, link in enumerate(image_dict['links']): |
2254 | + for key, value in link.items(): |
2255 | + self.assertEqual(link_nodes[i].get(key), value) |
2256 | + |
2257 | + metadata_root = root.find('{0}metadata'.format(NS)) |
2258 | + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) |
2259 | + self.assertEqual(len(metadata_elems), 1) |
2260 | + for i, metadata_elem in enumerate(metadata_elems): |
2261 | + (meta_key, meta_value) = image_dict['metadata'].items()[i] |
2262 | + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) |
2263 | + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) |
2264 | + |
2265 | + server_root = root.find('{0}server'.format(NS)) |
2266 | + self.assertEqual(server_root, None) |
2267 | |
2268 | def test_index(self): |
2269 | serializer = images.ImageXMLSerializer() |
2270 | @@ -1237,6 +1284,10 @@ |
2271 | 'href': self.IMAGE_HREF % 1, |
2272 | 'rel': 'self', |
2273 | }, |
2274 | + { |
2275 | + 'href': self.IMAGE_BOOKMARK % 1, |
2276 | + 'rel': 'bookmark', |
2277 | + }, |
2278 | ], |
2279 | }, |
2280 | { |
2281 | @@ -1247,35 +1298,32 @@ |
2282 | 'href': self.IMAGE_HREF % 2, |
2283 | 'rel': 'self', |
2284 | }, |
2285 | + { |
2286 | + 'href': self.IMAGE_BOOKMARK % 2, |
2287 | + 'rel': 'bookmark', |
2288 | + }, |
2289 | ], |
2290 | }, |
2291 | ] |
2292 | } |
2293 | |
2294 | output = serializer.serialize(fixture, 'index') |
2295 | - actual = minidom.parseString(output.replace(" ", "")) |
2296 | - |
2297 | - expected_server_href = self.SERVER_HREF |
2298 | - expected_server_bookmark = self.SERVER_BOOKMARK |
2299 | - expected_href = self.IMAGE_HREF % 1 |
2300 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2301 | - expected_href_two = self.IMAGE_HREF % 2 |
2302 | - expected_bookmark_two = self.IMAGE_BOOKMARK % 2 |
2303 | - expected_now = self.TIMESTAMP |
2304 | - expected = minidom.parseString(""" |
2305 | - <images |
2306 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2307 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
2308 | - <image id="1" name="Image1"> |
2309 | - <atom:link href="%(expected_href)s" rel="self"/> |
2310 | - </image> |
2311 | - <image id="2" name="Image2"> |
2312 | - <atom:link href="%(expected_href_two)s" rel="self"/> |
2313 | - </image> |
2314 | - </images> |
2315 | - """.replace(" ", "") % (locals())) |
2316 | - |
2317 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2318 | + print output |
2319 | + root = etree.XML(output) |
2320 | + xmlutil.validate_schema(root, 'images_index') |
2321 | + image_elems = root.findall('{0}image'.format(NS)) |
2322 | + self.assertEqual(len(image_elems), 2) |
2323 | + for i, image_elem in enumerate(image_elems): |
2324 | + image_dict = fixture['images'][i] |
2325 | + |
2326 | + for key in ['name', 'id']: |
2327 | + self.assertEqual(image_elem.get(key), str(image_dict[key])) |
2328 | + |
2329 | + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) |
2330 | + self.assertEqual(len(link_nodes), 2) |
2331 | + for i, link in enumerate(image_dict['links']): |
2332 | + for key, value in link.items(): |
2333 | + self.assertEqual(link_nodes[i].get(key), value) |
2334 | |
2335 | def test_index_zero_images(self): |
2336 | serializer = images.ImageXMLSerializer() |
2337 | @@ -1285,15 +1333,11 @@ |
2338 | } |
2339 | |
2340 | output = serializer.serialize(fixtures, 'index') |
2341 | - actual = minidom.parseString(output.replace(" ", "")) |
2342 | - |
2343 | - expected = minidom.parseString(""" |
2344 | - <images |
2345 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2346 | - xmlns:atom="http://www.w3.org/2005/Atom" /> |
2347 | - """.replace(" ", "") % (locals())) |
2348 | - |
2349 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2350 | + print output |
2351 | + root = etree.XML(output) |
2352 | + xmlutil.validate_schema(root, 'images_index') |
2353 | + image_elems = root.findall('{0}image'.format(NS)) |
2354 | + self.assertEqual(len(image_elems), 0) |
2355 | |
2356 | def test_detail(self): |
2357 | serializer = images.ImageXMLSerializer() |
2358 | @@ -1307,7 +1351,7 @@ |
2359 | 'updated': self.TIMESTAMP, |
2360 | 'status': 'ACTIVE', |
2361 | 'server': { |
2362 | - 'id': 1, |
2363 | + 'id': '1', |
2364 | 'links': [ |
2365 | { |
2366 | 'href': self.SERVER_HREF, |
2367 | @@ -1331,7 +1375,7 @@ |
2368 | ], |
2369 | }, |
2370 | { |
2371 | - 'id': 2, |
2372 | + 'id': '2', |
2373 | 'name': 'Image2', |
2374 | 'created': self.TIMESTAMP, |
2375 | 'updated': self.TIMESTAMP, |
2376 | @@ -1355,46 +1399,22 @@ |
2377 | } |
2378 | |
2379 | output = serializer.serialize(fixture, 'detail') |
2380 | - actual = minidom.parseString(output.replace(" ", "")) |
2381 | - |
2382 | - expected_server_href = self.SERVER_HREF |
2383 | - expected_server_bookmark = self.SERVER_BOOKMARK |
2384 | - expected_href = self.IMAGE_HREF % 1 |
2385 | - expected_bookmark = self.IMAGE_BOOKMARK % 1 |
2386 | - expected_href_two = self.IMAGE_HREF % 2 |
2387 | - expected_bookmark_two = self.IMAGE_BOOKMARK % 2 |
2388 | - expected_now = self.TIMESTAMP |
2389 | - expected = minidom.parseString(""" |
2390 | - <images |
2391 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2392 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
2393 | - <image id="1" |
2394 | - name="Image1" |
2395 | - updated="%(expected_now)s" |
2396 | - created="%(expected_now)s" |
2397 | - status="ACTIVE"> |
2398 | - <server id="1"> |
2399 | - <atom:link rel="self" href="%(expected_server_href)s"/> |
2400 | - <atom:link rel="bookmark" href="%(expected_server_bookmark)s"/> |
2401 | - </server> |
2402 | - <atom:link href="%(expected_href)s" rel="self"/> |
2403 | - <atom:link href="%(expected_bookmark)s" rel="bookmark"/> |
2404 | - </image> |
2405 | - <image id="2" |
2406 | - name="Image2" |
2407 | - updated="%(expected_now)s" |
2408 | - created="%(expected_now)s" |
2409 | - status="SAVING" |
2410 | - progress="80"> |
2411 | - <metadata> |
2412 | - <meta key="key1"> |
2413 | - value1 |
2414 | - </meta> |
2415 | - </metadata> |
2416 | - <atom:link href="%(expected_href_two)s" rel="self"/> |
2417 | - <atom:link href="%(expected_bookmark_two)s" rel="bookmark"/> |
2418 | - </image> |
2419 | - </images> |
2420 | - """.replace(" ", "") % (locals())) |
2421 | - |
2422 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2423 | + print output |
2424 | + root = etree.XML(output) |
2425 | + xmlutil.validate_schema(root, 'images') |
2426 | + image_elems = root.findall('{0}image'.format(NS)) |
2427 | + self.assertEqual(len(image_elems), 2) |
2428 | + for i, image_elem in enumerate(image_elems): |
2429 | + image_dict = fixture['images'][i] |
2430 | + |
2431 | + for key in ['name', 'id', 'updated', 'created', 'status']: |
2432 | + self.assertEqual(image_elem.get(key), str(image_dict[key])) |
2433 | + |
2434 | + link_nodes = image_elem.findall('{0}link'.format(ATOMNS)) |
2435 | + self.assertEqual(len(link_nodes), 2) |
2436 | + for i, link in enumerate(image_dict['links']): |
2437 | + for key, value in link.items(): |
2438 | + self.assertEqual(link_nodes[i].get(key), value) |
2439 | + |
2440 | + metadata_root = image_elem.find('{0}metadata'.format(NS)) |
2441 | + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) |
2442 | |
2443 | === modified file 'nova/tests/api/openstack/test_limits.py' |
2444 | --- nova/tests/api/openstack/test_limits.py 2011-08-12 19:00:48 +0000 |
2445 | +++ nova/tests/api/openstack/test_limits.py 2011-09-13 18:01:31 +0000 |
2446 | @@ -19,6 +19,7 @@ |
2447 | |
2448 | import httplib |
2449 | import json |
2450 | +from lxml import etree |
2451 | import StringIO |
2452 | import stubout |
2453 | import time |
2454 | @@ -29,6 +30,7 @@ |
2455 | import nova.context |
2456 | from nova.api.openstack import limits |
2457 | from nova.api.openstack import views |
2458 | +from nova.api.openstack import xmlutil |
2459 | from nova import test |
2460 | |
2461 | |
2462 | @@ -39,6 +41,10 @@ |
2463 | limits.Limit("PUT", "*", "", 10, limits.PER_MINUTE), |
2464 | limits.Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE), |
2465 | ] |
2466 | +NS = { |
2467 | + 'atom': 'http://www.w3.org/2005/Atom', |
2468 | + 'ns': 'http://docs.openstack.org/compute/api/v1.1' |
2469 | +} |
2470 | |
2471 | |
2472 | class BaseLimitTestSuite(unittest.TestCase): |
2473 | @@ -980,9 +986,22 @@ |
2474 | def tearDown(self): |
2475 | pass |
2476 | |
2477 | + def test_xml_declaration(self): |
2478 | + serializer = limits.LimitsXMLSerializer() |
2479 | + |
2480 | + fixture = {"limits": { |
2481 | + "rate": [], |
2482 | + "absolute": {}}} |
2483 | + |
2484 | + output = serializer.serialize(fixture, 'index') |
2485 | + print output |
2486 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
2487 | + self.assertTrue(has_dec) |
2488 | + |
2489 | def test_index(self): |
2490 | serializer = limits.LimitsXMLSerializer() |
2491 | - fixture = {"limits": { |
2492 | + fixture = { |
2493 | + "limits": { |
2494 | "rate": [{ |
2495 | "uri": "*", |
2496 | "regex": ".*", |
2497 | @@ -1006,32 +1025,32 @@ |
2498 | "maxPersonalitySize": 10240}}} |
2499 | |
2500 | output = serializer.serialize(fixture, 'index') |
2501 | - actual = minidom.parseString(output.replace(" ", "")) |
2502 | - |
2503 | - expected = minidom.parseString(""" |
2504 | - <limits xmlns="http://docs.openstack.org/compute/api/v1.1"> |
2505 | - <rates> |
2506 | - <rate uri="*" regex=".*"> |
2507 | - <limit value="10" verb="POST" remaining="2" |
2508 | - unit="MINUTE" |
2509 | - next-available="2011-12-15T22:42:45Z"/> |
2510 | - </rate> |
2511 | - <rate uri="*/servers" regex="^/servers"> |
2512 | - <limit value="50" verb="POST" remaining="10" |
2513 | - unit="DAY" |
2514 | - next-available="2011-12-15T22:42:45Z"/> |
2515 | - </rate> |
2516 | - </rates> |
2517 | - <absolute> |
2518 | - <limit name="maxServerMeta" value="1"/> |
2519 | - <limit name="maxPersonality" value="5"/> |
2520 | - <limit name="maxImageMeta" value="1"/> |
2521 | - <limit name="maxPersonalitySize" value="10240"/> |
2522 | - </absolute> |
2523 | - </limits> |
2524 | - """.replace(" ", "")) |
2525 | - |
2526 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2527 | + print output |
2528 | + root = etree.XML(output) |
2529 | + xmlutil.validate_schema(root, 'limits') |
2530 | + |
2531 | + #verify absolute limits |
2532 | + absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) |
2533 | + self.assertEqual(len(absolutes), 4) |
2534 | + for limit in absolutes: |
2535 | + name = limit.get('name') |
2536 | + value = limit.get('value') |
2537 | + self.assertEqual(value, str(fixture['limits']['absolute'][name])) |
2538 | + |
2539 | + #verify rate limits |
2540 | + rates = root.xpath('ns:rates/ns:rate', namespaces=NS) |
2541 | + self.assertEqual(len(rates), 2) |
2542 | + for i, rate in enumerate(rates): |
2543 | + for key in ['uri', 'regex']: |
2544 | + self.assertEqual(rate.get(key), |
2545 | + str(fixture['limits']['rate'][i][key])) |
2546 | + rate_limits = rate.xpath('ns:limit', namespaces=NS) |
2547 | + self.assertEqual(len(rate_limits), 1) |
2548 | + for j, limit in enumerate(rate_limits): |
2549 | + for key in ['verb', 'value', 'remaining', 'unit', |
2550 | + 'next-available']: |
2551 | + self.assertEqual(limit.get(key), |
2552 | + str(fixture['limits']['rate'][i]['limit'][j][key])) |
2553 | |
2554 | def test_index_no_limits(self): |
2555 | serializer = limits.LimitsXMLSerializer() |
2556 | @@ -1041,13 +1060,14 @@ |
2557 | "absolute": {}}} |
2558 | |
2559 | output = serializer.serialize(fixture, 'index') |
2560 | - actual = minidom.parseString(output.replace(" ", "")) |
2561 | - |
2562 | - expected = minidom.parseString(""" |
2563 | - <limits xmlns="http://docs.openstack.org/compute/api/v1.1"> |
2564 | - <rates /> |
2565 | - <absolute /> |
2566 | - </limits> |
2567 | - """.replace(" ", "")) |
2568 | - |
2569 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2570 | + print output |
2571 | + root = etree.XML(output) |
2572 | + xmlutil.validate_schema(root, 'limits') |
2573 | + |
2574 | + #verify absolute limits |
2575 | + absolutes = root.xpath('ns:absolute/ns:limit', namespaces=NS) |
2576 | + self.assertEqual(len(absolutes), 0) |
2577 | + |
2578 | + #verify rate limits |
2579 | + rates = root.xpath('ns:rates/ns:rate', namespaces=NS) |
2580 | + self.assertEqual(len(rates), 0) |
2581 | |
2582 | === modified file 'nova/tests/api/openstack/test_servers.py' |
2583 | --- nova/tests/api/openstack/test_servers.py 2011-09-12 14:13:34 +0000 |
2584 | +++ nova/tests/api/openstack/test_servers.py 2011-09-13 18:01:31 +0000 |
2585 | @@ -52,6 +52,10 @@ |
2586 | FAKE_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' |
2587 | NS = "{http://docs.openstack.org/compute/api/v1.1}" |
2588 | ATOMNS = "{http://www.w3.org/2005/Atom}" |
2589 | +XPATH_NS = { |
2590 | + 'atom': 'http://www.w3.org/2005/Atom', |
2591 | + 'ns': 'http://docs.openstack.org/compute/api/v1.1' |
2592 | +} |
2593 | |
2594 | |
2595 | def fake_gen_uuid(): |
2596 | @@ -412,12 +416,7 @@ |
2597 | |
2598 | def test_get_server_by_id_v1_1_xml(self): |
2599 | image_bookmark = "http://localhost/fake/images/10" |
2600 | - flavor_ref = "http://localhost/v1.1/fake/flavors/1" |
2601 | - flavor_id = "1" |
2602 | flavor_bookmark = "http://localhost/fake/flavors/1" |
2603 | - server_href = "http://localhost/v1.1/fake/servers/1" |
2604 | - server_bookmark = "http://localhost/fake/servers/1" |
2605 | - |
2606 | public_ip = '192.168.0.3' |
2607 | private_ip = '172.19.0.1' |
2608 | interfaces = [ |
2609 | @@ -441,50 +440,88 @@ |
2610 | req = webob.Request.blank('/v1.1/fake/servers/1') |
2611 | req.headers['Accept'] = 'application/xml' |
2612 | res = req.get_response(fakes.wsgi_app()) |
2613 | - actual = minidom.parseString(res.body.replace(' ', '')) |
2614 | - expected_uuid = FAKE_UUID |
2615 | - expected_updated = "2010-11-11T11:00:00Z" |
2616 | - expected_created = "2010-10-10T12:00:00Z" |
2617 | - expected = minidom.parseString(""" |
2618 | - <server id="1" |
2619 | - uuid="%(expected_uuid)s" |
2620 | - userId="fake" |
2621 | - tenantId="fake" |
2622 | - xmlns="http://docs.openstack.org/compute/api/v1.1" |
2623 | - xmlns:atom="http://www.w3.org/2005/Atom" |
2624 | - name="server1" |
2625 | - updated="%(expected_updated)s" |
2626 | - created="%(expected_created)s" |
2627 | - hostId="" |
2628 | - status="BUILD" |
2629 | - accessIPv4="" |
2630 | - accessIPv6="" |
2631 | - progress="0"> |
2632 | - <atom:link href="%(server_href)s" rel="self"/> |
2633 | - <atom:link href="%(server_bookmark)s" rel="bookmark"/> |
2634 | - <image id="10"> |
2635 | - <atom:link rel="bookmark" href="%(image_bookmark)s"/> |
2636 | - </image> |
2637 | - <flavor id="1"> |
2638 | - <atom:link rel="bookmark" href="%(flavor_bookmark)s"/> |
2639 | - </flavor> |
2640 | - <metadata> |
2641 | - <meta key="seq"> |
2642 | - 1 |
2643 | - </meta> |
2644 | - </metadata> |
2645 | - <addresses> |
2646 | - <network id="public"> |
2647 | - <ip version="4" addr="%(public_ip)s"/> |
2648 | - </network> |
2649 | - <network id="private"> |
2650 | - <ip version="4" addr="%(private_ip)s"/> |
2651 | - </network> |
2652 | - </addresses> |
2653 | - </server> |
2654 | - """.replace(" ", "") % (locals())) |
2655 | - |
2656 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2657 | + output = res.body |
2658 | + print output |
2659 | + root = etree.XML(output) |
2660 | + xmlutil.validate_schema(root, 'server') |
2661 | + |
2662 | + expected = { |
2663 | + 'id': 1, |
2664 | + 'uuid': FAKE_UUID, |
2665 | + 'user_id': 'fake', |
2666 | + 'tenant_id': 'fake', |
2667 | + 'updated': '2010-11-11T11:00:00Z', |
2668 | + 'created': '2010-10-10T12:00:00Z', |
2669 | + 'progress': 0, |
2670 | + 'name': 'server1', |
2671 | + 'status': 'BUILD', |
2672 | + 'accessIPv4': '', |
2673 | + 'accessIPv6': '', |
2674 | + 'hostId': '', |
2675 | + 'key_name': '', |
2676 | + 'image': { |
2677 | + 'id': '10', |
2678 | + 'links': [{'rel': 'bookmark', 'href': image_bookmark}], |
2679 | + }, |
2680 | + 'flavor': { |
2681 | + 'id': '1', |
2682 | + 'links': [{'rel': 'bookmark', 'href': flavor_bookmark}], |
2683 | + }, |
2684 | + 'addresses': { |
2685 | + 'public': [{'version': 4, 'addr': public_ip}], |
2686 | + 'private': [{'version': 4, 'addr': private_ip}], |
2687 | + }, |
2688 | + 'metadata': {'seq': '1'}, |
2689 | + 'config_drive': None, |
2690 | + 'links': [ |
2691 | + { |
2692 | + 'rel': 'self', |
2693 | + 'href': 'http://localhost/v1.1/fake/servers/1', |
2694 | + }, |
2695 | + { |
2696 | + 'rel': 'bookmark', |
2697 | + 'href': 'http://localhost/fake/servers/1', |
2698 | + }, |
2699 | + ], |
2700 | + } |
2701 | + |
2702 | + self.assertTrue(root.xpath('/ns:server', namespaces=XPATH_NS)) |
2703 | + for key in ['id', 'uuid', 'created', 'progress', 'name', 'status', |
2704 | + 'accessIPv4', 'accessIPv6', 'hostId']: |
2705 | + self.assertEqual(root.get(key), str(expected[key])) |
2706 | + self.assertEqual(root.get('userId'), str(expected['user_id'])) |
2707 | + self.assertEqual(root.get('tenantId'), str(expected['tenant_id'])) |
2708 | + |
2709 | + (image,) = root.xpath('ns:image', namespaces=XPATH_NS) |
2710 | + self.assertEqual(image.get('id'), str(expected['image']['id'])) |
2711 | + |
2712 | + links = root.xpath('ns:image/atom:link', namespaces=XPATH_NS) |
2713 | + self.assertTrue(common.compare_links(links, |
2714 | + expected['image']['links'])) |
2715 | + |
2716 | + (flavor,) = root.xpath('ns:flavor', namespaces=XPATH_NS) |
2717 | + self.assertEqual(flavor.get('id'), str(expected['flavor']['id'])) |
2718 | + |
2719 | + (meta,) = root.xpath('ns:metadata/ns:meta', namespaces=XPATH_NS) |
2720 | + self.assertEqual(meta.get('key'), 'seq') |
2721 | + self.assertEqual(meta.text, '1') |
2722 | + |
2723 | + (pub_network, priv_network) = root.xpath('ns:addresses/ns:network', |
2724 | + namespaces=XPATH_NS) |
2725 | + self.assertEqual(pub_network.get('id'), 'public') |
2726 | + (pub_ip,) = pub_network.xpath('ns:ip', namespaces=XPATH_NS) |
2727 | + (priv_ip,) = priv_network.xpath('ns:ip', namespaces=XPATH_NS) |
2728 | + self.assertEqual(pub_ip.get('version'), |
2729 | + str(expected['addresses']['public'][0]['version'])) |
2730 | + self.assertEqual(pub_ip.get('addr'), |
2731 | + str(expected['addresses']['public'][0]['addr'])) |
2732 | + self.assertEqual(priv_ip.get('version'), |
2733 | + str(expected['addresses']['private'][0]['version'])) |
2734 | + self.assertEqual(priv_ip.get('addr'), |
2735 | + str(expected['addresses']['private'][0]['addr'])) |
2736 | + |
2737 | + links = root.xpath('atom:link', namespaces=XPATH_NS) |
2738 | + self.assertTrue(common.compare_links(links, expected['links'])) |
2739 | |
2740 | def test_get_server_with_active_status_by_id_v1_1(self): |
2741 | image_bookmark = "http://localhost/fake/images/10" |
2742 | @@ -3285,6 +3322,18 @@ |
2743 | |
2744 | serializer = nova.api.openstack.ips.IPXMLSerializer() |
2745 | |
2746 | + def test_xml_declaration(self): |
2747 | + fixture = { |
2748 | + 'network_2': [ |
2749 | + {'addr': '192.168.0.1', 'version': 4}, |
2750 | + {'addr': 'fe80::beef', 'version': 6}, |
2751 | + ], |
2752 | + } |
2753 | + output = self.serializer.serialize(fixture, 'show') |
2754 | + print output |
2755 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
2756 | + self.assertTrue(has_dec) |
2757 | + |
2758 | def test_show(self): |
2759 | fixture = { |
2760 | 'network_2': [ |
2761 | @@ -3293,17 +3342,17 @@ |
2762 | ], |
2763 | } |
2764 | output = self.serializer.serialize(fixture, 'show') |
2765 | - actual = minidom.parseString(output.replace(" ", "")) |
2766 | - |
2767 | - expected = minidom.parseString(""" |
2768 | - <network xmlns="http://docs.openstack.org/compute/api/v1.1" |
2769 | - id="network_2"> |
2770 | - <ip version="4" addr="192.168.0.1"/> |
2771 | - <ip version="6" addr="fe80::beef"/> |
2772 | - </network> |
2773 | - """.replace(" ", "")) |
2774 | - |
2775 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2776 | + print output |
2777 | + root = etree.XML(output) |
2778 | + network = fixture['network_2'] |
2779 | + self.assertEqual(str(root.get('id')), 'network_2') |
2780 | + ip_elems = root.findall('{0}ip'.format(NS)) |
2781 | + for z, ip_elem in enumerate(ip_elems): |
2782 | + ip = network[z] |
2783 | + self.assertEqual(str(ip_elem.get('version')), |
2784 | + str(ip['version'])) |
2785 | + self.assertEqual(str(ip_elem.get('addr')), |
2786 | + str(ip['addr'])) |
2787 | |
2788 | def test_index(self): |
2789 | fixture = { |
2790 | @@ -3319,22 +3368,22 @@ |
2791 | }, |
2792 | } |
2793 | output = self.serializer.serialize(fixture, 'index') |
2794 | - actual = minidom.parseString(output.replace(" ", "")) |
2795 | - |
2796 | - expected = minidom.parseString(""" |
2797 | - <addresses xmlns="http://docs.openstack.org/compute/api/v1.1"> |
2798 | - <network id="network_2"> |
2799 | - <ip version="4" addr="192.168.0.1"/> |
2800 | - <ip version="6" addr="fe80::beef"/> |
2801 | - </network> |
2802 | - <network id="network_1"> |
2803 | - <ip version="4" addr="192.168.0.3"/> |
2804 | - <ip version="4" addr="192.168.0.5"/> |
2805 | - </network> |
2806 | - </addresses> |
2807 | - """.replace(" ", "")) |
2808 | - |
2809 | - self.assertEqual(expected.toxml(), actual.toxml()) |
2810 | + print output |
2811 | + root = etree.XML(output) |
2812 | + xmlutil.validate_schema(root, 'addresses') |
2813 | + addresses_dict = fixture['addresses'] |
2814 | + network_elems = root.findall('{0}network'.format(NS)) |
2815 | + self.assertEqual(len(network_elems), 2) |
2816 | + for i, network_elem in enumerate(network_elems): |
2817 | + network = addresses_dict.items()[i] |
2818 | + self.assertEqual(str(network_elem.get('id')), str(network[0])) |
2819 | + ip_elems = network_elem.findall('{0}ip'.format(NS)) |
2820 | + for z, ip_elem in enumerate(ip_elems): |
2821 | + ip = network[1][z] |
2822 | + self.assertEqual(str(ip_elem.get('version')), |
2823 | + str(ip['version'])) |
2824 | + self.assertEqual(str(ip_elem.get('addr')), |
2825 | + str(ip['addr'])) |
2826 | |
2827 | |
2828 | class TestServerInstanceCreation(test.TestCase): |
2829 | @@ -4059,6 +4108,85 @@ |
2830 | self.maxDiff = None |
2831 | test.TestCase.setUp(self) |
2832 | |
2833 | + def test_xml_declaration(self): |
2834 | + serializer = servers.ServerXMLSerializer() |
2835 | + |
2836 | + fixture = { |
2837 | + "server": { |
2838 | + 'id': 1, |
2839 | + 'uuid': FAKE_UUID, |
2840 | + 'user_id': 'fake_user_id', |
2841 | + 'tenant_id': 'fake_tenant_id', |
2842 | + 'created': self.TIMESTAMP, |
2843 | + 'updated': self.TIMESTAMP, |
2844 | + "progress": 0, |
2845 | + "name": "test_server", |
2846 | + "status": "BUILD", |
2847 | + "hostId": 'e4d909c290d0fb1ca068ffaddf22cbd0', |
2848 | + "accessIPv4": "1.2.3.4", |
2849 | + "accessIPv6": "fead::1234", |
2850 | + "image": { |
2851 | + "id": "5", |
2852 | + "links": [ |
2853 | + { |
2854 | + "rel": "bookmark", |
2855 | + "href": self.IMAGE_BOOKMARK, |
2856 | + }, |
2857 | + ], |
2858 | + }, |
2859 | + "flavor": { |
2860 | + "id": "1", |
2861 | + "links": [ |
2862 | + { |
2863 | + "rel": "bookmark", |
2864 | + "href": self.FLAVOR_BOOKMARK, |
2865 | + }, |
2866 | + ], |
2867 | + }, |
2868 | + "addresses": { |
2869 | + "network_one": [ |
2870 | + { |
2871 | + "version": 4, |
2872 | + "addr": "67.23.10.138", |
2873 | + }, |
2874 | + { |
2875 | + "version": 6, |
2876 | + "addr": "::babe:67.23.10.138", |
2877 | + }, |
2878 | + ], |
2879 | + "network_two": [ |
2880 | + { |
2881 | + "version": 4, |
2882 | + "addr": "67.23.10.139", |
2883 | + }, |
2884 | + { |
2885 | + "version": 6, |
2886 | + "addr": "::babe:67.23.10.139", |
2887 | + }, |
2888 | + ], |
2889 | + }, |
2890 | + "metadata": { |
2891 | + "Open": "Stack", |
2892 | + "Number": "1", |
2893 | + }, |
2894 | + 'links': [ |
2895 | + { |
2896 | + 'href': self.SERVER_HREF, |
2897 | + 'rel': 'self', |
2898 | + }, |
2899 | + { |
2900 | + 'href': self.SERVER_BOOKMARK, |
2901 | + 'rel': 'bookmark', |
2902 | + }, |
2903 | + ], |
2904 | + } |
2905 | + } |
2906 | + |
2907 | + output = serializer.serialize(fixture, 'show') |
2908 | + print output |
2909 | + has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>") |
2910 | + self.assertTrue(has_dec) |
2911 | + |
2912 | def test_show(self): |
2913 | serializer = servers.ServerXMLSerializer() |
2914 | |
2915 | |
2916 | === modified file 'nova/tests/api/openstack/test_versions.py' |
2917 | --- nova/tests/api/openstack/test_versions.py 2011-08-03 13:57:27 +0000 |
2918 | +++ nova/tests/api/openstack/test_versions.py 2011-09-13 18:01:31 +0000 |
2919 | @@ -15,19 +15,24 @@ |
2920 | # License for the specific language governing permissions and limitations |
2921 | # under the License. |
2922 | |
2923 | +import feedparser |
2924 | import json |
2925 | import stubout |
2926 | import webob |
2927 | -import xml.etree.ElementTree |
2928 | - |
2929 | +from lxml import etree |
2930 | |
2931 | from nova import context |
2932 | from nova import test |
2933 | -from nova.tests.api.openstack import fakes |
2934 | from nova.api.openstack import versions |
2935 | from nova.api.openstack import views |
2936 | from nova.api.openstack import wsgi |
2937 | +from nova.tests.api.openstack import common |
2938 | +from nova.tests.api.openstack import fakes |
2939 | |
2940 | +NS = { |
2941 | + 'atom': 'http://www.w3.org/2005/Atom', |
2942 | + 'ns': 'http://docs.openstack.org/compute/api/v1.1' |
2943 | +} |
2944 | VERSIONS = { |
2945 | "v1.0": { |
2946 | "id": "v1.0", |
2947 | @@ -113,6 +118,16 @@ |
2948 | versions = json.loads(res.body)["versions"] |
2949 | expected = [ |
2950 | { |
2951 | + "id": "v1.0", |
2952 | + "status": "DEPRECATED", |
2953 | + "updated": "2011-01-21T11:33:21Z", |
2954 | + "links": [ |
2955 | + { |
2956 | + "rel": "self", |
2957 | + "href": "http://localhost/v1.0/", |
2958 | + }], |
2959 | + }, |
2960 | + { |
2961 | "id": "v1.1", |
2962 | "status": "CURRENT", |
2963 | "updated": "2011-01-21T11:33:21Z", |
2964 | @@ -122,16 +137,6 @@ |
2965 | "href": "http://localhost/v1.1/", |
2966 | }], |
2967 | }, |
2968 | - { |
2969 | - "id": "v1.0", |
2970 | - "status": "DEPRECATED", |
2971 | - "updated": "2011-01-21T11:33:21Z", |
2972 | - "links": [ |
2973 | - { |
2974 | - "rel": "self", |
2975 | - "href": "http://localhost/v1.0/", |
2976 | - }], |
2977 | - }, |
2978 | ] |
2979 | self.assertEqual(versions, expected) |
2980 | |
2981 | @@ -233,48 +238,20 @@ |
2982 | res = req.get_response(fakes.wsgi_app()) |
2983 | self.assertEqual(res.status_int, 200) |
2984 | self.assertEqual(res.content_type, "application/xml") |
2985 | - root = xml.etree.ElementTree.XML(res.body) |
2986 | - self.assertEqual(root.tag.split('}')[1], "version") |
2987 | - self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11) |
2988 | - |
2989 | - children = list(root) |
2990 | - media_types = children[0] |
2991 | - media_type_nodes = list(media_types) |
2992 | - links = (children[1], children[2], children[3]) |
2993 | - |
2994 | - self.assertEqual(media_types.tag.split('}')[1], 'media-types') |
2995 | - for media_node in media_type_nodes: |
2996 | - self.assertEqual(media_node.tag.split('}')[1], 'media-type') |
2997 | - |
2998 | - expected = """ |
2999 | - <version id="v1.0" status="DEPRECATED" |
3000 | - updated="2011-01-21T11:33:21Z" |
3001 | - xmlns="%s" |
3002 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
3003 | - |
3004 | - <media-types> |
3005 | - <media-type base="application/xml" |
3006 | - type="application/vnd.openstack.compute-v1.0+xml"/> |
3007 | - <media-type base="application/json" |
3008 | - type="application/vnd.openstack.compute-v1.0+json"/> |
3009 | - </media-types> |
3010 | - |
3011 | - <atom:link href="http://localhost/v1.0/" |
3012 | - rel="self"/> |
3013 | - |
3014 | - <atom:link href="http://docs.rackspacecloud.com/servers/ |
3015 | - api/v1.0/cs-devguide-20110125.pdf" |
3016 | - rel="describedby" |
3017 | - type="application/pdf"/> |
3018 | - |
3019 | - <atom:link href="http://docs.rackspacecloud.com/servers/ |
3020 | - api/v1.0/application.wadl" |
3021 | - rel="describedby" |
3022 | - type="application/vnd.sun.wadl+xml"/> |
3023 | - </version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11 |
3024 | - |
3025 | - actual = res.body.replace(" ", "").replace("\n", "") |
3026 | - self.assertEqual(expected, actual) |
3027 | + |
3028 | + version = etree.XML(res.body) |
3029 | + expected = VERSIONS['v1.0'] |
3030 | + self.assertTrue(version.xpath('/ns:version', namespaces=NS)) |
3031 | + media_types = version.xpath('ns:media-types/ns:media-type', |
3032 | + namespaces=NS) |
3033 | + self.assertTrue(common.compare_media_types(media_types, |
3034 | + expected['media-types'])) |
3035 | + for key in ['id', 'status', 'updated']: |
3036 | + self.assertEqual(version.get(key), expected[key]) |
3037 | + links = version.xpath('atom:link', namespaces=NS) |
3038 | + self.assertTrue(common.compare_links(links, |
3039 | + [{'rel': 'self', 'href': 'http://localhost/v1.0/'}] |
3040 | + + expected['links'])) |
3041 | |
3042 | def test_get_version_1_1_detail_xml(self): |
3043 | req = webob.Request.blank('/v1.1/') |
3044 | @@ -282,35 +259,20 @@ |
3045 | res = req.get_response(fakes.wsgi_app()) |
3046 | self.assertEqual(res.status_int, 200) |
3047 | self.assertEqual(res.content_type, "application/xml") |
3048 | - expected = """ |
3049 | - <version id="v1.1" status="CURRENT" |
3050 | - updated="2011-01-21T11:33:21Z" |
3051 | - xmlns="%s" |
3052 | - xmlns:atom="http://www.w3.org/2005/Atom"> |
3053 | - |
3054 | - <media-types> |
3055 | - <media-type base="application/xml" |
3056 | - type="application/vnd.openstack.compute-v1.1+xml"/> |
3057 | - <media-type base="application/json" |
3058 | - type="application/vnd.openstack.compute-v1.1+json"/> |
3059 | - </media-types> |
3060 | - |
3061 | - <atom:link href="http://localhost/v1.1/" |
3062 | - rel="self"/> |
3063 | - |
3064 | - <atom:link href="http://docs.rackspacecloud.com/servers/ |
3065 | - api/v1.1/cs-devguide-20110125.pdf" |
3066 | - rel="describedby" |
3067 | - type="application/pdf"/> |
3068 | - |
3069 | - <atom:link href="http://docs.rackspacecloud.com/servers/ |
3070 | - api/v1.1/application.wadl" |
3071 | - rel="describedby" |
3072 | - type="application/vnd.sun.wadl+xml"/> |
3073 | - </version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11 |
3074 | - |
3075 | - actual = res.body.replace(" ", "").replace("\n", "") |
3076 | - self.assertEqual(expected, actual) |
3077 | + |
3078 | + version = etree.XML(res.body) |
3079 | + expected = VERSIONS['v1.1'] |
3080 | + self.assertTrue(version.xpath('/ns:version', namespaces=NS)) |
3081 | + media_types = version.xpath('ns:media-types/ns:media-type', |
3082 | + namespaces=NS) |
3083 | + self.assertTrue(common.compare_media_types(media_types, |
3084 | + expected['media-types'])) |
3085 | + for key in ['id', 'status', 'updated']: |
3086 | + self.assertEqual(version.get(key), expected[key]) |
3087 | + links = version.xpath('atom:link', namespaces=NS) |
3088 | + self.assertTrue(common.compare_links(links, |
3089 | + [{'rel': 'self', 'href': 'http://localhost/v1.1/'}] |
3090 | + + expected['links'])) |
3091 | |
3092 | def test_get_version_list_xml(self): |
3093 | req = webob.Request.blank('/') |
3094 | @@ -319,21 +281,19 @@ |
3095 | self.assertEqual(res.status_int, 200) |
3096 | self.assertEqual(res.content_type, "application/xml") |
3097 | |
3098 | - expected = """ |
3099 | - <versions xmlns="%s" xmlns:atom="%s"> |
3100 | - <version id="v1.1" status="CURRENT" updated="2011-01-21T11:33:21Z"> |
3101 | - <atom:link href="http://localhost/v1.1/" rel="self"/> |
3102 | - </version> |
3103 | - <version id="v1.0" status="DEPRECATED" |
3104 | - updated="2011-01-21T11:33:21Z"> |
3105 | - <atom:link href="http://localhost/v1.0/" rel="self"/> |
3106 | - </version> |
3107 | - </versions>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11, |
3108 | - wsgi.XMLNS_ATOM) |
3109 | - |
3110 | - actual = res.body.replace(" ", "").replace("\n", "") |
3111 | - |
3112 | - self.assertEqual(expected, actual) |
3113 | + root = etree.XML(res.body) |
3114 | + self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) |
3115 | + versions = root.xpath('ns:version', namespaces=NS) |
3116 | + self.assertEqual(len(versions), 2) |
3117 | + |
3118 | + for i, v in enumerate(['v1.0', 'v1.1']): |
3119 | + version = versions[i] |
3120 | + expected = VERSIONS[v] |
3121 | + for key in ['id', 'status', 'updated']: |
3122 | + self.assertEqual(version.get(key), expected[key]) |
3123 | + (link,) = version.xpath('atom:link', namespaces=NS) |
3124 | + self.assertTrue(common.compare_links(link, |
3125 | + [{'rel': 'self', 'href': 'http://localhost/%s/' % v}])) |
3126 | |
3127 | def test_get_version_1_0_detail_atom(self): |
3128 | req = webob.Request.blank('/v1.0/') |
3129 | @@ -341,36 +301,38 @@ |
3130 | res = req.get_response(fakes.wsgi_app()) |
3131 | self.assertEqual(res.status_int, 200) |
3132 | self.assertEqual("application/atom+xml", res.content_type) |
3133 | - expected = """ |
3134 | - <feed xmlns="http://www.w3.org/2005/Atom"> |
3135 | - <title type="text">About This Version</title> |
3136 | - <updated>2011-01-21T11:33:21Z</updated> |
3137 | - <id>http://localhost/v1.0/</id> |
3138 | - <author> |
3139 | - <name>Rackspace</name> |
3140 | - <uri>http://www.rackspace.com/</uri> |
3141 | - </author> |
3142 | - <link href="http://localhost/v1.0/" rel="self"/> |
3143 | - <entry> |
3144 | - <id>http://localhost/v1.0/</id> |
3145 | - <title type="text">Version v1.0</title> |
3146 | - <updated>2011-01-21T11:33:21Z</updated> |
3147 | - <link href="http://localhost/v1.0/" |
3148 | - rel="self"/> |
3149 | - <link href="http://docs.rackspacecloud.com/servers/ |
3150 | - api/v1.0/cs-devguide-20110125.pdf" |
3151 | - rel="describedby" type="application/pdf"/> |
3152 | - <link href="http://docs.rackspacecloud.com/servers/ |
3153 | - api/v1.0/application.wadl" |
3154 | - rel="describedby" type="application/vnd.sun.wadl+xml"/> |
3155 | - <content type="text"> |
3156 | - Version v1.0 DEPRECATED (2011-01-21T11:33:21Z) |
3157 | - </content> |
3158 | - </entry> |
3159 | - </feed>""".replace(" ", "").replace("\n", "") |
3160 | - |
3161 | - actual = res.body.replace(" ", "").replace("\n", "") |
3162 | - self.assertEqual(expected, actual) |
3163 | + |
3164 | + f = feedparser.parse(res.body) |
3165 | + self.assertEqual(f.feed.title, 'About This Version') |
3166 | + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') |
3167 | + self.assertEqual(f.feed.id, 'http://localhost/v1.0/') |
3168 | + self.assertEqual(f.feed.author, 'Rackspace') |
3169 | + self.assertEqual(f.feed.author_detail.href, |
3170 | + 'http://www.rackspace.com/') |
3171 | + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.0/') |
3172 | + self.assertEqual(f.feed.links[0]['rel'], 'self') |
3173 | + |
3174 | + self.assertEqual(len(f.entries), 1) |
3175 | + entry = f.entries[0] |
3176 | + self.assertEqual(entry.id, 'http://localhost/v1.0/') |
3177 | + self.assertEqual(entry.title, 'Version v1.0') |
3178 | + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') |
3179 | + self.assertEqual(len(entry.content), 1) |
3180 | + self.assertEqual(entry.content[0].value, |
3181 | + 'Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)') |
3182 | + self.assertEqual(len(entry.links), 3) |
3183 | + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.0/') |
3184 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3185 | + self.assertEqual(entry.links[1], { |
3186 | + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.0/'\ |
3187 | + 'cs-devguide-20110125.pdf', |
3188 | + 'type': 'application/pdf', |
3189 | + 'rel': 'describedby'}) |
3190 | + self.assertEqual(entry.links[2], { |
3191 | + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.0/'\ |
3192 | + 'application.wadl', |
3193 | + 'type': 'application/vnd.sun.wadl+xml', |
3194 | + 'rel': 'describedby'}) |
3195 | |
3196 | def test_get_version_1_1_detail_atom(self): |
3197 | req = webob.Request.blank('/v1.1/') |
3198 | @@ -378,36 +340,38 @@ |
3199 | res = req.get_response(fakes.wsgi_app()) |
3200 | self.assertEqual(res.status_int, 200) |
3201 | self.assertEqual("application/atom+xml", res.content_type) |
3202 | - expected = """ |
3203 | - <feed xmlns="http://www.w3.org/2005/Atom"> |
3204 | - <title type="text">About This Version</title> |
3205 | - <updated>2011-01-21T11:33:21Z</updated> |
3206 | - <id>http://localhost/v1.1/</id> |
3207 | - <author> |
3208 | - <name>Rackspace</name> |
3209 | - <uri>http://www.rackspace.com/</uri> |
3210 | - </author> |
3211 | - <link href="http://localhost/v1.1/" rel="self"/> |
3212 | - <entry> |
3213 | - <id>http://localhost/v1.1/</id> |
3214 | - <title type="text">Version v1.1</title> |
3215 | - <updated>2011-01-21T11:33:21Z</updated> |
3216 | - <link href="http://localhost/v1.1/" |
3217 | - rel="self"/> |
3218 | - <link href="http://docs.rackspacecloud.com/servers/ |
3219 | - api/v1.1/cs-devguide-20110125.pdf" |
3220 | - rel="describedby" type="application/pdf"/> |
3221 | - <link href="http://docs.rackspacecloud.com/servers/ |
3222 | - api/v1.1/application.wadl" |
3223 | - rel="describedby" type="application/vnd.sun.wadl+xml"/> |
3224 | - <content type="text"> |
3225 | - Version v1.1 CURRENT (2011-01-21T11:33:21Z) |
3226 | - </content> |
3227 | - </entry> |
3228 | - </feed>""".replace(" ", "").replace("\n", "") |
3229 | - |
3230 | - actual = res.body.replace(" ", "").replace("\n", "") |
3231 | - self.assertEqual(expected, actual) |
3232 | + |
3233 | + f = feedparser.parse(res.body) |
3234 | + self.assertEqual(f.feed.title, 'About This Version') |
3235 | + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') |
3236 | + self.assertEqual(f.feed.id, 'http://localhost/v1.1/') |
3237 | + self.assertEqual(f.feed.author, 'Rackspace') |
3238 | + self.assertEqual(f.feed.author_detail.href, |
3239 | + 'http://www.rackspace.com/') |
3240 | + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') |
3241 | + self.assertEqual(f.feed.links[0]['rel'], 'self') |
3242 | + |
3243 | + self.assertEqual(len(f.entries), 1) |
3244 | + entry = f.entries[0] |
3245 | + self.assertEqual(entry.id, 'http://localhost/v1.1/') |
3246 | + self.assertEqual(entry.title, 'Version v1.1') |
3247 | + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') |
3248 | + self.assertEqual(len(entry.content), 1) |
3249 | + self.assertEqual(entry.content[0].value, |
3250 | + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') |
3251 | + self.assertEqual(len(entry.links), 3) |
3252 | + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') |
3253 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3254 | + self.assertEqual(entry.links[1], { |
3255 | + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ |
3256 | + 'cs-devguide-20110125.pdf', |
3257 | + 'type': 'application/pdf', |
3258 | + 'rel': 'describedby'}) |
3259 | + self.assertEqual(entry.links[2], { |
3260 | + 'href': 'http://docs.rackspacecloud.com/servers/api/v1.1/'\ |
3261 | + 'application.wadl', |
3262 | + 'type': 'application/vnd.sun.wadl+xml', |
3263 | + 'rel': 'describedby'}) |
3264 | |
3265 | def test_get_version_list_atom(self): |
3266 | req = webob.Request.blank('/') |
3267 | @@ -416,40 +380,37 @@ |
3268 | self.assertEqual(res.status_int, 200) |
3269 | self.assertEqual(res.content_type, "application/atom+xml") |
3270 | |
3271 | - expected = """ |
3272 | - <feed xmlns="http://www.w3.org/2005/Atom"> |
3273 | - <title type="text">Available API Versions</title> |
3274 | - <updated>2011-01-21T11:33:21Z</updated> |
3275 | - <id>http://localhost/</id> |
3276 | - <author> |
3277 | - <name>Rackspace</name> |
3278 | - <uri>http://www.rackspace.com/</uri> |
3279 | - </author> |
3280 | - <link href="http://localhost/" rel="self"/> |
3281 | - <entry> |
3282 | - <id>http://localhost/v1.1/</id> |
3283 | - <title type="text">Version v1.1</title> |
3284 | - <updated>2011-01-21T11:33:21Z</updated> |
3285 | - <link href="http://localhost/v1.1/" rel="self"/> |
3286 | - <content type="text"> |
3287 | - Version v1.1 CURRENT (2011-01-21T11:33:21Z) |
3288 | - </content> |
3289 | - </entry> |
3290 | - <entry> |
3291 | - <id>http://localhost/v1.0/</id> |
3292 | - <title type="text">Version v1.0</title> |
3293 | - <updated>2011-01-21T11:33:21Z</updated> |
3294 | - <link href="http://localhost/v1.0/" rel="self"/> |
3295 | - <content type="text"> |
3296 | - Version v1.0 DEPRECATED (2011-01-21T11:33:21Z) |
3297 | - </content> |
3298 | - </entry> |
3299 | - </feed> |
3300 | - """.replace(" ", "").replace("\n", "") |
3301 | - |
3302 | - actual = res.body.replace(" ", "").replace("\n", "") |
3303 | - |
3304 | - self.assertEqual(expected, actual) |
3305 | + f = feedparser.parse(res.body) |
3306 | + self.assertEqual(f.feed.title, 'Available API Versions') |
3307 | + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') |
3308 | + self.assertEqual(f.feed.id, 'http://localhost/') |
3309 | + self.assertEqual(f.feed.author, 'Rackspace') |
3310 | + self.assertEqual(f.feed.author_detail.href, |
3311 | + 'http://www.rackspace.com/') |
3312 | + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/') |
3313 | + self.assertEqual(f.feed.links[0]['rel'], 'self') |
3314 | + |
3315 | + self.assertEqual(len(f.entries), 2) |
3316 | + entry = f.entries[0] |
3317 | + self.assertEqual(entry.id, 'http://localhost/v1.0/') |
3318 | + self.assertEqual(entry.title, 'Version v1.0') |
3319 | + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') |
3320 | + self.assertEqual(len(entry.content), 1) |
3321 | + self.assertEqual(entry.content[0].value, |
3322 | + 'Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)') |
3323 | + self.assertEqual(len(entry.links), 1) |
3324 | + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.0/') |
3325 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3326 | + entry = f.entries[1] |
3327 | + self.assertEqual(entry.id, 'http://localhost/v1.1/') |
3328 | + self.assertEqual(entry.title, 'Version v1.1') |
3329 | + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') |
3330 | + self.assertEqual(len(entry.content), 1) |
3331 | + self.assertEqual(entry.content[0].value, |
3332 | + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') |
3333 | + self.assertEqual(len(entry.links), 1) |
3334 | + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') |
3335 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3336 | |
3337 | def test_multi_choice_image(self): |
3338 | req = webob.Request.blank('/images/1') |
3339 | @@ -511,28 +472,32 @@ |
3340 | self.assertEqual(res.status_int, 300) |
3341 | self.assertEqual(res.content_type, "application/xml") |
3342 | |
3343 | - expected = """ |
3344 | - <choices xmlns="%s" xmlns:atom="%s"> |
3345 | - <version id="v1.1" status="CURRENT"> |
3346 | - <media-types> |
3347 | - <media-type base="application/xml" |
3348 | - type="application/vnd.openstack.compute-v1.1+xml"/> |
3349 | - <media-type base="application/json" |
3350 | - type="application/vnd.openstack.compute-v1.1+json"/> |
3351 | - </media-types> |
3352 | - <atom:link href="http://localhost/v1.1/images/1" rel="self"/> |
3353 | - </version> |
3354 | - <version id="v1.0" status="DEPRECATED"> |
3355 | - <media-types> |
3356 | - <media-type base="application/xml" |
3357 | - type="application/vnd.openstack.compute-v1.0+xml"/> |
3358 | - <media-type base="application/json" |
3359 | - type="application/vnd.openstack.compute-v1.0+json"/> |
3360 | - </media-types> |
3361 | - <atom:link href="http://localhost/v1.0/images/1" rel="self"/> |
3362 | - </version> |
3363 | - </choices>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11, |
3364 | - wsgi.XMLNS_ATOM) |
3365 | + root = etree.XML(res.body) |
3366 | + self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) |
3367 | + versions = root.xpath('ns:version', namespaces=NS) |
3368 | + self.assertEqual(len(versions), 2) |
3369 | + |
3370 | + version = versions[0] |
3371 | + self.assertEqual(version.get('id'), 'v1.1') |
3372 | + self.assertEqual(version.get('status'), 'CURRENT') |
3373 | + media_types = version.xpath('ns:media-types/ns:media-type', |
3374 | + namespaces=NS) |
3375 | + self.assertTrue(common.compare_media_types(media_types, |
3376 | + VERSIONS['v1.1']['media-types'])) |
3377 | + links = version.xpath('atom:link', namespaces=NS) |
3378 | + self.assertTrue(common.compare_links(links, |
3379 | + [{'rel': 'self', 'href': 'http://localhost/v1.1/images/1'}])) |
3380 | + |
3381 | + version = versions[1] |
3382 | + self.assertEqual(version.get('id'), 'v1.0') |
3383 | + self.assertEqual(version.get('status'), 'DEPRECATED') |
3384 | + media_types = version.xpath('ns:media-types/ns:media-type', |
3385 | + namespaces=NS) |
3386 | + self.assertTrue(common.compare_media_types(media_types, |
3387 | + VERSIONS['v1.0']['media-types'])) |
3388 | + links = version.xpath('atom:link', namespaces=NS) |
3389 | + self.assertTrue(common.compare_links(links, |
3390 | + [{'rel': 'self', 'href': 'http://localhost/v1.0/images/1'}])) |
3391 | |
3392 | def test_multi_choice_server_atom(self): |
3393 | """ |
3394 | @@ -665,22 +630,20 @@ |
3395 | serializer = versions.VersionsXMLSerializer() |
3396 | response = serializer.index(versions_data) |
3397 | |
3398 | - root = xml.etree.ElementTree.XML(response) |
3399 | - self.assertEqual(root.tag.split('}')[1], "versions") |
3400 | - self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11) |
3401 | - version = list(root)[0] |
3402 | - self.assertEqual(version.tag.split('}')[1], "version") |
3403 | - self.assertEqual(version.get('id'), |
3404 | - versions_data['versions'][0]['id']) |
3405 | + root = etree.XML(response) |
3406 | + self.assertTrue(root.xpath('/ns:versions', namespaces=NS)) |
3407 | + version_elems = root.xpath('ns:version', namespaces=NS) |
3408 | + self.assertEqual(len(version_elems), 1) |
3409 | + version = version_elems[0] |
3410 | + self.assertEqual(version.get('id'), versions_data['versions'][0]['id']) |
3411 | self.assertEqual(version.get('status'), |
3412 | versions_data['versions'][0]['status']) |
3413 | |
3414 | - link = list(version)[0] |
3415 | - |
3416 | - self.assertEqual(link.tag.split('}')[1], "link") |
3417 | - self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM) |
3418 | - for key, val in versions_data['versions'][0]['links'][0].items(): |
3419 | - self.assertEqual(link.get(key), val) |
3420 | + (link,) = version.xpath('atom:link', namespaces=NS) |
3421 | + self.assertTrue(common.compare_links(link, [{ |
3422 | + 'rel': 'self', |
3423 | + 'href': 'http://test/2.7.1', |
3424 | + 'type': 'application/atom+xml'}])) |
3425 | |
3426 | def test_versions_multi_xml_serializer(self): |
3427 | versions_data = { |
3428 | @@ -703,11 +666,9 @@ |
3429 | serializer = versions.VersionsXMLSerializer() |
3430 | response = serializer.multi(versions_data) |
3431 | |
3432 | - root = xml.etree.ElementTree.XML(response) |
3433 | - self.assertEqual(root.tag.split('}')[1], "choices") |
3434 | - self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11) |
3435 | - version = list(root)[0] |
3436 | - self.assertEqual(version.tag.split('}')[1], "version") |
3437 | + root = etree.XML(response) |
3438 | + self.assertTrue(root.xpath('/ns:choices', namespaces=NS)) |
3439 | + (version,) = root.xpath('ns:version', namespaces=NS) |
3440 | self.assertEqual(version.get('id'), versions_data['choices'][0]['id']) |
3441 | self.assertEqual(version.get('status'), |
3442 | versions_data['choices'][0]['status']) |
3443 | @@ -716,19 +677,14 @@ |
3444 | media_type_nodes = list(media_types) |
3445 | self.assertEqual(media_types.tag.split('}')[1], "media-types") |
3446 | |
3447 | - set_types = versions_data['choices'][0]['media-types'] |
3448 | - for i, type in enumerate(set_types): |
3449 | - node = media_type_nodes[i] |
3450 | - self.assertEqual(node.tag.split('}')[1], "media-type") |
3451 | - for key, val in set_types[i].items(): |
3452 | - self.assertEqual(node.get(key), val) |
3453 | - |
3454 | - link = list(version)[1] |
3455 | - |
3456 | - self.assertEqual(link.tag.split('}')[1], "link") |
3457 | - self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM) |
3458 | - for key, val in versions_data['choices'][0]['links'][0].items(): |
3459 | - self.assertEqual(link.get(key), val) |
3460 | + media_types = version.xpath('ns:media-types/ns:media-type', |
3461 | + namespaces=NS) |
3462 | + self.assertTrue(common.compare_media_types(media_types, |
3463 | + versions_data['choices'][0]['media-types'])) |
3464 | + |
3465 | + (link,) = version.xpath('atom:link', namespaces=NS) |
3466 | + self.assertTrue(common.compare_links(link, |
3467 | + versions_data['choices'][0]['links'])) |
3468 | |
3469 | def test_version_detail_xml_serializer(self): |
3470 | version_data = { |
3471 | @@ -770,7 +726,7 @@ |
3472 | serializer = versions.VersionsXMLSerializer() |
3473 | response = serializer.show(version_data) |
3474 | |
3475 | - root = xml.etree.ElementTree.XML(response) |
3476 | + root = etree.XML(response) |
3477 | self.assertEqual(root.tag.split('}')[1], "version") |
3478 | self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11) |
3479 | |
3480 | @@ -811,59 +767,28 @@ |
3481 | |
3482 | serializer = versions.VersionsAtomSerializer() |
3483 | response = serializer.index(versions_data) |
3484 | - |
3485 | - root = xml.etree.ElementTree.XML(response) |
3486 | - self.assertEqual(root.tag.split('}')[1], "feed") |
3487 | - self.assertEqual(root.tag.split('}')[0].strip('{'), |
3488 | - "http://www.w3.org/2005/Atom") |
3489 | - |
3490 | - children = list(root) |
3491 | - title = children[0] |
3492 | - updated = children[1] |
3493 | - id = children[2] |
3494 | - author = children[3] |
3495 | - link = children[4] |
3496 | - entry = children[5] |
3497 | - |
3498 | - self.assertEqual(title.tag.split('}')[1], 'title') |
3499 | - self.assertEqual(title.text, 'Available API Versions') |
3500 | - self.assertEqual(updated.tag.split('}')[1], 'updated') |
3501 | - self.assertEqual(updated.text, '2011-07-20T11:40:00Z') |
3502 | - self.assertEqual(id.tag.split('}')[1], 'id') |
3503 | - self.assertEqual(id.text, 'http://test/') |
3504 | - |
3505 | - self.assertEqual(author.tag.split('}')[1], 'author') |
3506 | - author_name = list(author)[0] |
3507 | - author_uri = list(author)[1] |
3508 | - self.assertEqual(author_name.tag.split('}')[1], 'name') |
3509 | - self.assertEqual(author_name.text, 'Rackspace') |
3510 | - self.assertEqual(author_uri.tag.split('}')[1], 'uri') |
3511 | - self.assertEqual(author_uri.text, 'http://www.rackspace.com/') |
3512 | - |
3513 | - self.assertEqual(link.get('href'), 'http://test/') |
3514 | - self.assertEqual(link.get('rel'), 'self') |
3515 | - |
3516 | - self.assertEqual(entry.tag.split('}')[1], 'entry') |
3517 | - entry_children = list(entry) |
3518 | - entry_id = entry_children[0] |
3519 | - entry_title = entry_children[1] |
3520 | - entry_updated = entry_children[2] |
3521 | - entry_link = entry_children[3] |
3522 | - entry_content = entry_children[4] |
3523 | - self.assertEqual(entry_id.tag.split('}')[1], "id") |
3524 | - self.assertEqual(entry_id.text, "http://test/2.9.8") |
3525 | - self.assertEqual(entry_title.tag.split('}')[1], "title") |
3526 | - self.assertEqual(entry_title.get('type'), "text") |
3527 | - self.assertEqual(entry_title.text, "Version 2.9.8") |
3528 | - self.assertEqual(entry_updated.tag.split('}')[1], "updated") |
3529 | - self.assertEqual(entry_updated.text, "2011-07-20T11:40:00Z") |
3530 | - self.assertEqual(entry_link.tag.split('}')[1], "link") |
3531 | - self.assertEqual(entry_link.get('href'), "http://test/2.9.8") |
3532 | - self.assertEqual(entry_link.get('rel'), "self") |
3533 | - self.assertEqual(entry_content.tag.split('}')[1], "content") |
3534 | - self.assertEqual(entry_content.get('type'), "text") |
3535 | - self.assertEqual(entry_content.text, |
3536 | - "Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)") |
3537 | + f = feedparser.parse(response) |
3538 | + |
3539 | + self.assertEqual(f.feed.title, 'Available API Versions') |
3540 | + self.assertEqual(f.feed.updated, '2011-07-20T11:40:00Z') |
3541 | + self.assertEqual(f.feed.id, 'http://test/') |
3542 | + self.assertEqual(f.feed.author, 'Rackspace') |
3543 | + self.assertEqual(f.feed.author_detail.href, |
3544 | + 'http://www.rackspace.com/') |
3545 | + self.assertEqual(f.feed.links[0]['href'], 'http://test/') |
3546 | + self.assertEqual(f.feed.links[0]['rel'], 'self') |
3547 | + |
3548 | + self.assertEqual(len(f.entries), 1) |
3549 | + entry = f.entries[0] |
3550 | + self.assertEqual(entry.id, 'http://test/2.9.8') |
3551 | + self.assertEqual(entry.title, 'Version 2.9.8') |
3552 | + self.assertEqual(entry.updated, '2011-07-20T11:40:00Z') |
3553 | + self.assertEqual(len(entry.content), 1) |
3554 | + self.assertEqual(entry.content[0].value, |
3555 | + 'Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)') |
3556 | + self.assertEqual(len(entry.links), 1) |
3557 | + self.assertEqual(entry.links[0]['href'], 'http://test/2.9.8') |
3558 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3559 | |
3560 | def test_version_detail_atom_serializer(self): |
3561 | versions_data = { |
3562 | @@ -904,63 +829,36 @@ |
3563 | |
3564 | serializer = versions.VersionsAtomSerializer() |
3565 | response = serializer.show(versions_data) |
3566 | - |
3567 | - root = xml.etree.ElementTree.XML(response) |
3568 | - self.assertEqual(root.tag.split('}')[1], "feed") |
3569 | - self.assertEqual(root.tag.split('}')[0].strip('{'), |
3570 | - "http://www.w3.org/2005/Atom") |
3571 | - |
3572 | - children = list(root) |
3573 | - title = children[0] |
3574 | - updated = children[1] |
3575 | - id = children[2] |
3576 | - author = children[3] |
3577 | - link = children[4] |
3578 | - entry = children[5] |
3579 | - |
3580 | - self.assertEqual(root.tag.split('}')[1], 'feed') |
3581 | - self.assertEqual(title.tag.split('}')[1], 'title') |
3582 | - self.assertEqual(title.text, 'About This Version') |
3583 | - self.assertEqual(updated.tag.split('}')[1], 'updated') |
3584 | - self.assertEqual(updated.text, '2011-01-21T11:33:21Z') |
3585 | - self.assertEqual(id.tag.split('}')[1], 'id') |
3586 | - self.assertEqual(id.text, 'http://localhost/v1.1/') |
3587 | - |
3588 | - self.assertEqual(author.tag.split('}')[1], 'author') |
3589 | - author_name = list(author)[0] |
3590 | - author_uri = list(author)[1] |
3591 | - self.assertEqual(author_name.tag.split('}')[1], 'name') |
3592 | - self.assertEqual(author_name.text, 'Rackspace') |
3593 | - self.assertEqual(author_uri.tag.split('}')[1], 'uri') |
3594 | - self.assertEqual(author_uri.text, 'http://www.rackspace.com/') |
3595 | - |
3596 | - self.assertEqual(link.get('href'), |
3597 | - 'http://localhost/v1.1/') |
3598 | - self.assertEqual(link.get('rel'), 'self') |
3599 | - |
3600 | - self.assertEqual(entry.tag.split('}')[1], 'entry') |
3601 | - entry_children = list(entry) |
3602 | - entry_id = entry_children[0] |
3603 | - entry_title = entry_children[1] |
3604 | - entry_updated = entry_children[2] |
3605 | - entry_links = (entry_children[3], entry_children[4], entry_children[5]) |
3606 | - entry_content = entry_children[6] |
3607 | - |
3608 | - self.assertEqual(entry_id.tag.split('}')[1], "id") |
3609 | - self.assertEqual(entry_id.text, |
3610 | - "http://localhost/v1.1/") |
3611 | - self.assertEqual(entry_title.tag.split('}')[1], "title") |
3612 | - self.assertEqual(entry_title.get('type'), "text") |
3613 | - self.assertEqual(entry_title.text, "Version v1.1") |
3614 | - self.assertEqual(entry_updated.tag.split('}')[1], "updated") |
3615 | - self.assertEqual(entry_updated.text, "2011-01-21T11:33:21Z") |
3616 | - |
3617 | - for i, link in enumerate(versions_data["version"]["links"]): |
3618 | - self.assertEqual(entry_links[i].tag.split('}')[1], "link") |
3619 | - for key, val in versions_data["version"]["links"][i].items(): |
3620 | - self.assertEqual(entry_links[i].get(key), val) |
3621 | - |
3622 | - self.assertEqual(entry_content.tag.split('}')[1], "content") |
3623 | - self.assertEqual(entry_content.get('type'), "text") |
3624 | - self.assertEqual(entry_content.text, |
3625 | - "Version v1.1 CURRENT (2011-01-21T11:33:21Z)") |
3626 | + f = feedparser.parse(response) |
3627 | + |
3628 | + self.assertEqual(f.feed.title, 'About This Version') |
3629 | + self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z') |
3630 | + self.assertEqual(f.feed.id, 'http://localhost/v1.1/') |
3631 | + self.assertEqual(f.feed.author, 'Rackspace') |
3632 | + self.assertEqual(f.feed.author_detail.href, |
3633 | + 'http://www.rackspace.com/') |
3634 | + self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v1.1/') |
3635 | + self.assertEqual(f.feed.links[0]['rel'], 'self') |
3636 | + |
3637 | + self.assertEqual(len(f.entries), 1) |
3638 | + entry = f.entries[0] |
3639 | + self.assertEqual(entry.id, 'http://localhost/v1.1/') |
3640 | + self.assertEqual(entry.title, 'Version v1.1') |
3641 | + self.assertEqual(entry.updated, '2011-01-21T11:33:21Z') |
3642 | + self.assertEqual(len(entry.content), 1) |
3643 | + self.assertEqual(entry.content[0].value, |
3644 | + 'Version v1.1 CURRENT (2011-01-21T11:33:21Z)') |
3645 | + self.assertEqual(len(entry.links), 3) |
3646 | + self.assertEqual(entry.links[0]['href'], 'http://localhost/v1.1/') |
3647 | + self.assertEqual(entry.links[0]['rel'], 'self') |
3648 | + self.assertEqual(entry.links[1], { |
3649 | + 'rel': 'describedby', |
3650 | + 'type': 'application/pdf', |
3651 | + 'href': 'http://docs.rackspacecloud.com/' |
3652 | + 'servers/api/v1.1/cs-devguide-20110125.pdf'}) |
3653 | + self.assertEqual(entry.links[2], { |
3654 | + 'rel': 'describedby', |
3655 | + 'type': 'application/vnd.sun.wadl+xml', |
3656 | + 'href': 'http://docs.rackspacecloud.com/' |
3657 | + 'servers/api/v1.1/application.wadl', |
3658 | + }) |
3659 | |
3660 | === modified file 'nova/tests/integrated/test_xml.py' |
3661 | --- nova/tests/integrated/test_xml.py 2011-08-03 21:06:56 +0000 |
3662 | +++ nova/tests/integrated/test_xml.py 2011-09-13 18:01:31 +0000 |
3663 | @@ -15,6 +15,8 @@ |
3664 | # License for the specific language governing permissions and limitations |
3665 | # under the License. |
3666 | |
3667 | +from lxml import etree |
3668 | + |
3669 | from nova.log import logging |
3670 | from nova.tests.integrated import integrated_helpers |
3671 | from nova.api.openstack import common |
3672 | @@ -34,9 +36,8 @@ |
3673 | response = self.api.api_request('/limits', headers=headers) |
3674 | data = response.read() |
3675 | LOG.debug("data: %s" % data) |
3676 | - |
3677 | - prefix = '<limits xmlns="%s"' % common.XML_NS_V11 |
3678 | - self.assertTrue(data.startswith(prefix)) |
3679 | + root = etree.XML(data) |
3680 | + self.assertEqual(root.nsmap.get(None), common.XML_NS_V11) |
3681 | |
3682 | def test_namespace_servers(self): |
3683 | """/servers should have v1.1 namespace (has changed in 1.1).""" |
3684 | @@ -46,6 +47,5 @@ |
3685 | response = self.api.api_request('/servers', headers=headers) |
3686 | data = response.read() |
3687 | LOG.debug("data: %s" % data) |
3688 | - |
3689 | - prefix = '<servers xmlns="%s"' % common.XML_NS_V11 |
3690 | - self.assertTrue(data.startswith(prefix)) |
3691 | + root = etree.XML(data) |
3692 | + self.assertEqual(root.nsmap.get(None), common.XML_NS_V11) |
3693 | |
3694 | === modified file 'tools/pip-requires' |
3695 | --- tools/pip-requires 2011-08-30 20:55:06 +0000 |
3696 | +++ tools/pip-requires 2011-09-13 18:01:31 +0000 |
3697 | @@ -35,3 +35,4 @@ |
3698 | nosexcover |
3699 | GitPython |
3700 | paramiko |
3701 | +feedparser |
Naveed: I agree with your additions to the branch, nice work!