Merge lp:~rackspace-titan/nova/minidom-to-etree into lp:~hudson-openstack/nova/trunk

Proposed by Alex Meade
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
Reviewer Review Type Date Requested Status
Dan Prince (community) Approve
Rick Harris (community) Approve
Review via email: mp+73056@code.launchpad.net

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.

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

Naveed: I agree with your additions to the branch, nice work!

Revision history for this message
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):

review: Needs Information
Revision history for this message
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 :)

Revision history for this message
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.

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

Hey guys,

I'm getting a merge conflict with lp:nova...

Text conflict in nova/api/openstack/servers.py
1 conflicts encountered.

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

This looks good.

review: Approve
Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (128.0 KiB)

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_instance_not_found OK 0.00
    test_snapshot_not_found OK 0.00
    test_volume_not_found OK 0.00
LockoutTestCase
    test_lockout OK 0.01
    test_multiple_keys OK 0.01
    test_timeout OK 0.01
    test_window_timeout OK 0.01
CreateserverextTest
    test_create_instance_with_duplicate_networks OK 0.31
    test_create_instance_with_duplicate_networks_xml OK 0.42
    test_create_instance_with_network_empty_fixed_ip OK 0.30
    test_create_instance_with_network_empty_fixed_ip_xml OK 0.30
    test_create_instance_with_network_invalid_id OK 0.47
    test_create_instance_with_network_invalid_id_xml OK 0.29
    test_create_instance_with_network_no_fixed_ip OK 0.30
    test_create_instance_with_network_no_fixed_ip_xml OK 0.46
    test_create_instance_with_network_no_id OK 0.28
    test_create_instance_with_network_no_id_xml OK 0.44
    test_create_instance_with_network_non_string_fixed_ip OK 0.29
    test_create_instance_with_no_networks OK 0.30
    test_create_instance_with_no_networks_xml OK 0.48
    test_create_instance_with_one_network OK 0.31
    test_create_instance_with_one_network_xml OK 0.30
    test_create_instance_with_security_group_json OK 0.55
    test_create_instance_with_two_networks OK 0.30
    test_create_instance_with_two_networks_xml OK 0.29
    test_create_instance_with_userdata OK 0.46
    test_create_instance_with_userdata_none OK 0.29
    test_create_instance_with_userdata_with_non_b64_content OK 0.44
    test_get_server_by_id_verify_security_groups_json OK 0.29
    test_get_server_by_id_verify_security_groups_xml OK 0.28
FloatingIpTest
    test_add_associated_floating_ip_to_instance OK 1.51
    test_add_floating_ip_to_instance OK 0.31
    test_associate_floating_ip_to_instance_no_project_id OK 0.34
    test_associate_floating_ip_to_instance_wrong_project_id OK 0.53
    test_bad_address_param_in_add_floating_ip OK 0.32
    test_bad_address_param_in_remove_floating_ip OK 0.31
    test_floating_ip_allocate OK 0.48
    test_floating_ip_allocate_no_free_ips OK 0.30
    test_floating_ip_release_associated OK 0.46
    test_floating_ip_release_disassociated OK 0.30
    test_floating_ip_show OK 0.30
    test_fl...

Revision history for this message
Naveed Massjouni (ironcamel) wrote :

Missing dep was added by Monty.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (128.1 KiB)

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_instance_not_found OK 0.00
    test_snapshot_not_found OK 0.00
    test_volume_not_found OK 0.00
LockoutTestCase
    test_lockout OK 0.03
    test_multiple_keys OK 0.01
    test_timeout OK 0.01
    test_window_timeout OK 0.01
CreateserverextTest
    test_create_instance_with_duplicate_networks OK 0.31
    test_create_instance_with_duplicate_networks_xml OK 0.43
    test_create_instance_with_network_empty_fixed_ip OK 0.30
    test_create_instance_with_network_empty_fixed_ip_xml OK 0.30
    test_create_instance_with_network_invalid_id OK 0.45
    test_create_instance_with_network_invalid_id_xml OK 0.28
    test_create_instance_with_network_no_fixed_ip OK 0.30
    test_create_instance_with_network_no_fixed_ip_xml OK 0.45
    test_create_instance_with_network_no_id OK 0.28
    test_create_instance_with_network_no_id_xml OK 0.44
    test_create_instance_with_network_non_string_fixed_ip OK 0.30
    test_create_instance_with_no_networks OK 0.29
    test_create_instance_with_no_networks_xml OK 0.47
    test_create_instance_with_one_network OK 0.29
    test_create_instance_with_one_network_xml OK 0.32
    test_create_instance_with_security_group_json OK 0.61
    test_create_instance_with_two_networks OK 0.30
    test_create_instance_with_two_networks_xml OK 0.29
    test_create_instance_with_userdata OK 0.46
    test_create_instance_with_userdata_none OK 0.30
    test_create_instance_with_userdata_with_non_b64_content OK 1.23
    test_get_server_by_id_verify_security_groups_json OK 0.29
    test_get_server_by_id_verify_security_groups_xml OK 0.29
FloatingIpTest
    test_add_associated_floating_ip_to_instance OK 0.52
    test_add_floating_ip_to_instance OK 0.31
    test_associate_floating_ip_to_instance_no_project_id OK 0.34
    test_associate_floating_ip_to_instance_wrong_project_id OK 0.50
    test_bad_address_param_in_add_floating_ip OK 0.30
    test_bad_address_param_in_remove_floating_ip OK 0.31
    test_floating_ip_allocate OK 0.47
    test_floating_ip_allocate_no_free_ips OK 0.31
    test_floating_ip_release_associated OK 0.47
    test_floating_ip_release_disassociated OK 0.31
    test_floating_ip_show OK 0.30
    test_fl...

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (199.9 KiB)

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_instance_not_found OK 0.01
    test_snapshot_not_found OK 0.00
    test_volume_not_found OK 0.00
LockoutTestCase
    test_lockout OK 0.01
    test_multiple_keys OK 0.01
    test_timeout OK 0.01
    test_window_timeout OK 0.01
CreateserverextTest
    test_create_instance_with_duplicate_networks OK 0.32
    test_create_instance_with_duplicate_networks_xml OK 0.42
    test_create_instance_with_network_empty_fixed_ip OK 0.30
    test_create_instance_with_network_empty_fixed_ip_xml OK 0.30
    test_create_instance_with_network_invalid_id OK 0.45
    test_create_instance_with_network_invalid_id_xml OK 0.29
    test_create_instance_with_network_no_fixed_ip OK 0.46
    test_create_instance_with_network_no_fixed_ip_xml OK 0.30
    test_create_instance_with_network_no_id OK 0.29
    test_create_instance_with_network_no_id_xml OK 0.47
    test_create_instance_with_network_non_string_fixed_ip OK 0.29
    test_create_instance_with_no_networks OK 0.30
    test_create_instance_with_no_networks_xml OK 0.46
    test_create_instance_with_one_network OK 0.30
    test_create_instance_with_one_network_xml OK 0.29
    test_create_instance_with_security_group_json OK 0.57
    test_create_instance_with_two_networks OK 0.31
    test_create_instance_with_two_networks_xml OK 0.46
    test_create_instance_with_userdata OK 0.31
    test_create_instance_with_userdata_none OK 0.30
    test_create_instance_with_userdata_with_non_b64_content OK 0.46
    test_get_server_by_id_verify_security_groups_json OK 0.29
    test_get_server_by_id_verify_security_groups_xml OK 0.29
FloatingIpTest
    test_add_associated_floating_ip_to_instance OK 1.61
    test_add_floating_ip_to_instance OK 0.32
    test_associate_floating_ip_to_instance_no_project_id OK 0.34
    test_associate_floating_ip_to_instance_wrong_project_id OK 0.51
    test_bad_address_param_in_add_floating_ip OK 0.31
    test_bad_address_param_in_remove_floating_ip OK 0.48
    test_floating_ip_allocate OK 0.32
    test_floating_ip_allocate_no_free_ips OK 0.31
    test_floating_ip_release_associated OK 0.48
    test_floating_ip_release_disassociated OK 0.31
    test_floating_ip_show OK 0.30
    test_fl...

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (199.5 KiB)

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_instance_not_found OK 0.01
    test_snapshot_not_found OK 0.00
    test_volume_not_found OK 0.00
LockoutTestCase
    test_lockout OK 0.01
    test_multiple_keys OK 0.01
    test_timeout OK 0.01
    test_window_timeout OK 0.01
CreateserverextTest
    test_create_instance_with_duplicate_networks OK 0.31
    test_create_instance_with_duplicate_networks_xml OK 0.42
    test_create_instance_with_network_empty_fixed_ip OK 0.30
    test_create_instance_with_network_empty_fixed_ip_xml OK 0.30
    test_create_instance_with_network_invalid_id OK 0.45
    test_create_instance_with_network_invalid_id_xml OK 0.29
    test_create_instance_with_network_no_fixed_ip OK 0.46
    test_create_instance_with_network_no_fixed_ip_xml OK 0.30
    test_create_instance_with_network_no_id OK 0.29
    test_create_instance_with_network_no_id_xml OK 0.47
    test_create_instance_with_network_non_string_fixed_ip OK 0.28
    test_create_instance_with_no_networks OK 0.29
    test_create_instance_with_no_networks_xml OK 0.47
    test_create_instance_with_one_network OK 0.30
    test_create_instance_with_one_network_xml OK 0.31
    test_create_instance_with_security_group_json OK 0.56
    test_create_instance_with_two_networks OK 0.30
    test_create_instance_with_two_networks_xml OK 0.46
    test_create_instance_with_userdata OK 0.30
    test_create_instance_with_userdata_none OK 0.30
    test_create_instance_with_userdata_with_non_b64_content OK 0.46
    test_get_server_by_id_verify_security_groups_json OK 0.29
    test_get_server_by_id_verify_security_groups_xml OK 0.29
FloatingIpTest
    test_add_associated_floating_ip_to_instance OK 0.52
    test_add_floating_ip_to_instance OK 0.32
    test_associate_floating_ip_to_instance_no_project_id OK 0.34
    test_associate_floating_ip_to_instance_wrong_project_id OK 1.66
    test_bad_address_param_in_add_floating_ip OK 0.32
    test_bad_address_param_in_remove_floating_ip OK 0.47
    test_floating_ip_allocate OK 0.30
    test_floating_ip_allocate_no_free_ips OK 0.30
    test_floating_ip_release_associated OK 0.47
    test_floating_ip_release_disassociated OK 0.31
    test_floating_ip_show OK 0.31
    test_fl...

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

Naveed made the tests a bit more robust. Trying one more time.

review: Approve
Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (151.3 KiB)

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_ajax_console OK 1.87
    test_allocate_address OK 0.28
    test_associate_disassociate_address OK 1.44
    test_authorize_revoke_security_group_ingress_by_id OK 0.35
    test_authorize_security_group_fail_missing_source_group OK 0.27
    test_authorize_security_group_ingress OK 0.28
    test_authorize_security_group_ingress_already_exists OK 0.48
    test_authorize_security_group_ingress_ip_permissions_groups OK 0.33
    test_authorize_security_group_ingress_ip_permissions_ip_rangesOK 0.28
    test_authorize_security_group_ingress_missing_group_name_or_idOK 0.21
    test_authorize_security_group_ingress_missing_protocol_paramsOK 0.26
    test_console_output OK 1.51
    test_create_delete_security_group OK 0.27
    test_create_image OK 4.02
    test_create_snapshot OK 1.53
    test_create_volume_from_snapshot OK 0.47
    test_delete_key_pair OK 0.40
    test_delete_security_group_by_id OK 0.25
    test_delete_security_group_no_params OK 0.20
    test_delete_security_group_with_bad_group_id OK 0.22
    test_delete_security_group_with_bad_name OK 0.22
    test_delete_snapshot OK 0.63
    test_deregister_image OK 0.21
    test_deregister_image_wrong_container_type OK 0.19
    test_describe_addresses OK 0.50
    test_describe_availability_zones OK 0.24
    test_describe_image_attribute OK 0.21
    test_describe_image_attribute_block_device_mapping OK 0.19
    test_describe_image_attribute_root_device_name OK 0.20
    test_describe_image_mapping OK 0.20
    test_describe_images OK 0.20
    test_describe_instance_attribute OK 0.20
    test_describe_instances OK 0.38
    test_describe_instances_bdm OK 1.26
    test_describe_instances_deleted OK 0.43
    test_describe_key_pairs OK 0.64
    test_describe_regions OK 0.21
    test_describe_security_groups OK 0.29
    test_describe_security_groups_by_id OK 0.32
    test_describe_snapshots OK 0.24
    test_describe_volumes OK 0.27
    test_format_instance_bdm ...

1528. By Naveed Massjouni

And again.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :

Attempt to merge into lp:nova failed due to conflicts:

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

1529. By Naveed Massjouni

Merge from trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/api/openstack/common.py'
2--- nova/api/openstack/common.py 2011-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