summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorNaveed Massjouni <naveedm9@gmail.com>2011-09-13 18:17:13 +0000
committerTarmac <>2011-09-13 18:17:13 +0000
commitc3195f02cad33acc7c43030b0a7151cd8a439ef3 (patch)
treea45d2ae2dab40bb885337dd8ef456a810a2b0b44 /nova/api
parent61e5825a43fff1ad60dcd26454dc4881bdc13ef6 (diff)
parent59cd446cc61fdbf933c504d498852835a187ea6f (diff)
downloadnova-c3195f02cad33acc7c43030b0a7151cd8a439ef3.tar.gz
nova-c3195f02cad33acc7c43030b0a7151cd8a439ef3.tar.xz
nova-c3195f02cad33acc7c43030b0a7151cd8a439ef3.zip
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.
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/common.py66
-rw-r--r--nova/api/openstack/flavors.py69
-rw-r--r--nova/api/openstack/images.py133
-rw-r--r--nova/api/openstack/ips.py59
-rw-r--r--nova/api/openstack/limits.py78
-rw-r--r--nova/api/openstack/schemas/v1.1/addresses.rng14
-rw-r--r--nova/api/openstack/schemas/v1.1/flavor.rng10
-rw-r--r--nova/api/openstack/schemas/v1.1/flavors.rng6
-rw-r--r--nova/api/openstack/schemas/v1.1/flavors_index.rng12
-rw-r--r--nova/api/openstack/schemas/v1.1/image.rng30
-rw-r--r--nova/api/openstack/schemas/v1.1/images.rng6
-rw-r--r--nova/api/openstack/schemas/v1.1/images_index.rng12
-rw-r--r--nova/api/openstack/schemas/v1.1/limits.rng28
-rw-r--r--nova/api/openstack/schemas/v1.1/metadata.rng9
-rw-r--r--nova/api/openstack/schemas/v1.1/server.rng6
-rw-r--r--nova/api/openstack/servers.py226
-rw-r--r--nova/api/openstack/versions.py242
-rw-r--r--nova/api/openstack/views/versions.py2
-rw-r--r--nova/api/openstack/wsgi.py5
19 files changed, 500 insertions, 513 deletions
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index d743a66ef..a836a584c 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -16,6 +16,7 @@
# under the License.
import functools
+from lxml import etree
import re
import urlparse
from xml.dom import minidom
@@ -27,6 +28,7 @@ from nova import flags
from nova import log as logging
from nova import quota
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.compute import vm_states
from nova.compute import task_states
@@ -308,54 +310,48 @@ class MetadataHeadersSerializer(wsgi.ResponseHeadersSerializer):
class MetadataXMLSerializer(wsgi.XMLDictSerializer):
+
+ NSMAP = {None: xmlutil.XMLNS_V11}
+
def __init__(self, xmlns=wsgi.XMLNS_V11):
super(MetadataXMLSerializer, self).__init__(xmlns=xmlns)
- def _meta_item_to_xml(self, doc, key, value):
- node = doc.createElement('meta')
- doc.appendChild(node)
- node.setAttribute('key', '%s' % key)
- text = doc.createTextNode('%s' % value)
- node.appendChild(text)
- return node
-
- def meta_list_to_xml(self, xml_doc, meta_items):
- container_node = xml_doc.createElement('metadata')
- for (key, value) in meta_items:
- item_node = self._meta_item_to_xml(xml_doc, key, value)
- container_node.appendChild(item_node)
- return container_node
-
- def _meta_list_to_xml_string(self, metadata_dict):
- xml_doc = minidom.Document()
- items = metadata_dict['metadata'].items()
- container_node = self.meta_list_to_xml(xml_doc, items)
- xml_doc.appendChild(container_node)
- self._add_xmlns(container_node)
- return xml_doc.toxml('UTF-8')
+ def populate_metadata(self, metadata_elem, meta_dict):
+ for (key, value) in meta_dict.items():
+ elem = etree.SubElement(metadata_elem, 'meta')
+ elem.set('key', str(key))
+ elem.text = value
+
+ def _populate_meta_item(self, meta_elem, meta_item_dict):
+ """Populate a meta xml element from a dict."""
+ (key, value) = meta_item_dict.items()[0]
+ meta_elem.set('key', str(key))
+ meta_elem.text = value
def index(self, metadata_dict):
- return self._meta_list_to_xml_string(metadata_dict)
+ metadata = etree.Element('metadata', nsmap=self.NSMAP)
+ self.populate_metadata(metadata, metadata_dict.get('metadata', {}))
+ return self._to_xml(metadata)
def create(self, metadata_dict):
- return self._meta_list_to_xml_string(metadata_dict)
+ metadata = etree.Element('metadata', nsmap=self.NSMAP)
+ self.populate_metadata(metadata, metadata_dict.get('metadata', {}))
+ return self._to_xml(metadata)
def update_all(self, metadata_dict):
- return self._meta_list_to_xml_string(metadata_dict)
-
- def _meta_item_to_xml_string(self, meta_item_dict):
- xml_doc = minidom.Document()
- item_key, item_value = meta_item_dict.items()[0]
- item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
- xml_doc.appendChild(item_node)
- self._add_xmlns(item_node)
- return xml_doc.toxml('UTF-8')
+ metadata = etree.Element('metadata', nsmap=self.NSMAP)
+ self.populate_metadata(metadata, metadata_dict.get('metadata', {}))
+ return self._to_xml(metadata)
def show(self, meta_item_dict):
- return self._meta_item_to_xml_string(meta_item_dict['meta'])
+ meta = etree.Element('meta', nsmap=self.NSMAP)
+ self._populate_meta_item(meta, meta_item_dict['meta'])
+ return self._to_xml(meta)
def update(self, meta_item_dict):
- return self._meta_item_to_xml_string(meta_item_dict['meta'])
+ meta = etree.Element('meta', nsmap=self.NSMAP)
+ self._populate_meta_item(meta, meta_item_dict['meta'])
+ return self._to_xml(meta)
def default(self, *args, **kwargs):
return ''
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index fd36060da..805aad772 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -16,12 +16,13 @@
# under the License.
import webob
-import xml.dom.minidom as minidom
+from lxml import etree
from nova import db
from nova import exception
from nova.api.openstack import views
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
class Controller(object):
@@ -78,48 +79,44 @@ class ControllerV11(Controller):
class FlavorXMLSerializer(wsgi.XMLDictSerializer):
+ NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
+
def __init__(self):
super(FlavorXMLSerializer, self).__init__(xmlns=wsgi.XMLNS_V11)
- def _flavor_to_xml(self, xml_doc, flavor, detailed):
- flavor_node = xml_doc.createElement('flavor')
- flavor_node.setAttribute('id', str(flavor['id']))
- flavor_node.setAttribute('name', flavor['name'])
+ def _populate_flavor(self, flavor_elem, flavor_dict, detailed=False):
+ """Populate a flavor xml element from a dict."""
+ flavor_elem.set('name', flavor_dict['name'])
+ flavor_elem.set('id', str(flavor_dict['id']))
if detailed:
- flavor_node.setAttribute('ram', str(flavor['ram']))
- flavor_node.setAttribute('disk', str(flavor['disk']))
-
- link_nodes = self._create_link_nodes(xml_doc, flavor['links'])
- for link_node in link_nodes:
- flavor_node.appendChild(link_node)
- return flavor_node
-
- def _flavors_list_to_xml(self, xml_doc, flavors, detailed):
- container_node = xml_doc.createElement('flavors')
-
- for flavor in flavors:
- item_node = self._flavor_to_xml(xml_doc, flavor, detailed)
- container_node.appendChild(item_node)
- return container_node
+ flavor_elem.set('ram', str(flavor_dict['ram']))
+ flavor_elem.set('disk', str(flavor_dict['disk']))
+ for link in flavor_dict.get('links', []):
+ elem = etree.SubElement(flavor_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return flavor_elem
def show(self, flavor_container):
- xml_doc = minidom.Document()
- flavor = flavor_container['flavor']
- node = self._flavor_to_xml(xml_doc, flavor, True)
- return self.to_xml_string(node, True)
-
- def detail(self, flavors_container):
- xml_doc = minidom.Document()
- flavors = flavors_container['flavors']
- node = self._flavors_list_to_xml(xml_doc, flavors, True)
- return self.to_xml_string(node, True)
-
- def index(self, flavors_container):
- xml_doc = minidom.Document()
- flavors = flavors_container['flavors']
- node = self._flavors_list_to_xml(xml_doc, flavors, False)
- return self.to_xml_string(node, True)
+ flavor = etree.Element('flavor', nsmap=self.NSMAP)
+ self._populate_flavor(flavor, flavor_container['flavor'], True)
+ return self._to_xml(flavor)
+
+ def detail(self, flavors_dict):
+ flavors = etree.Element('flavors', nsmap=self.NSMAP)
+ for flavor_dict in flavors_dict['flavors']:
+ flavor = etree.SubElement(flavors, 'flavor')
+ self._populate_flavor(flavor, flavor_dict, True)
+ return self._to_xml(flavors)
+
+ def index(self, flavors_dict):
+ flavors = etree.Element('flavors', nsmap=self.NSMAP)
+ for flavor_dict in flavors_dict['flavors']:
+ flavor = etree.SubElement(flavors, 'flavor')
+ self._populate_flavor(flavor, flavor_dict, False)
+ return self._to_xml(flavors)
def create_resource(version='1.0'):
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index fcaa94651..4340cbe3e 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -16,8 +16,8 @@
import urlparse
import os.path
+from lxml import etree
import webob.exc
-from xml.dom import minidom
from nova import compute
from nova import exception
@@ -29,6 +29,7 @@ from nova.api.openstack import image_metadata
from nova.api.openstack import servers
from nova.api.openstack.views import images as images_view
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
LOG = log.getLogger('nova.api.openstack.images')
@@ -206,93 +207,71 @@ class ControllerV11(Controller):
class ImageXMLSerializer(wsgi.XMLDictSerializer):
- xmlns = wsgi.XMLNS_V11
+ NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
def __init__(self):
self.metadata_serializer = common.MetadataXMLSerializer()
- def _image_to_xml(self, xml_doc, image):
- image_node = xml_doc.createElement('image')
- image_node.setAttribute('id', str(image['id']))
- image_node.setAttribute('name', image['name'])
- link_nodes = self._create_link_nodes(xml_doc,
- image['links'])
- for link_node in link_nodes:
- image_node.appendChild(link_node)
- return image_node
-
- def _image_to_xml_detailed(self, xml_doc, image):
- image_node = xml_doc.createElement('image')
- self._add_image_attributes(image_node, image)
-
- if 'server' in image:
- server_node = self._create_server_node(xml_doc, image['server'])
- image_node.appendChild(server_node)
-
- metadata = image.get('metadata', {}).items()
- if len(metadata) > 0:
- metadata_node = self._create_metadata_node(xml_doc, metadata)
- image_node.appendChild(metadata_node)
-
- link_nodes = self._create_link_nodes(xml_doc,
- image['links'])
- for link_node in link_nodes:
- image_node.appendChild(link_node)
-
- return image_node
-
- def _add_image_attributes(self, node, image):
- node.setAttribute('id', str(image['id']))
- node.setAttribute('name', image['name'])
- node.setAttribute('created', image['created'])
- node.setAttribute('updated', image['updated'])
- node.setAttribute('status', image['status'])
- if 'progress' in image:
- node.setAttribute('progress', str(image['progress']))
-
- def _create_metadata_node(self, xml_doc, metadata):
- return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata)
-
- def _create_server_node(self, xml_doc, server):
- server_node = xml_doc.createElement('server')
- server_node.setAttribute('id', str(server['id']))
- link_nodes = self._create_link_nodes(xml_doc,
- server['links'])
- for link_node in link_nodes:
- server_node.appendChild(link_node)
- return server_node
-
- def _image_list_to_xml(self, xml_doc, images, detailed):
- container_node = xml_doc.createElement('images')
+ def _create_metadata_node(self, metadata_dict):
+ metadata_elem = etree.Element('metadata', nsmap=self.NSMAP)
+ self.metadata_serializer.populate_metadata(metadata_elem,
+ metadata_dict)
+ return metadata_elem
+
+ def _create_server_node(self, server_dict):
+ server_elem = etree.Element('server', nsmap=self.NSMAP)
+ server_elem.set('id', str(server_dict['id']))
+ for link in server_dict.get('links', []):
+ elem = etree.SubElement(server_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return server_elem
+
+ def _populate_image(self, image_elem, image_dict, detailed=False):
+ """Populate an image xml element from a dict."""
+
+ image_elem.set('name', image_dict['name'])
+ image_elem.set('id', str(image_dict['id']))
if detailed:
- image_to_xml = self._image_to_xml_detailed
- else:
- image_to_xml = self._image_to_xml
-
- for image in images:
- item_node = image_to_xml(xml_doc, image)
- container_node.appendChild(item_node)
- return container_node
+ image_elem.set('updated', str(image_dict['updated']))
+ image_elem.set('created', str(image_dict['created']))
+ image_elem.set('status', str(image_dict['status']))
+ if 'progress' in image_dict:
+ image_elem.set('progress', str(image_dict['progress']))
+ if 'server' in image_dict:
+ server_elem = self._create_server_node(image_dict['server'])
+ image_elem.append(server_elem)
+
+ meta_elem = self._create_metadata_node(
+ image_dict.get('metadata', {}))
+ image_elem.append(meta_elem)
+
+ for link in image_dict.get('links', []):
+ elem = etree.SubElement(image_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return image_elem
def index(self, images_dict):
- xml_doc = minidom.Document()
- node = self._image_list_to_xml(xml_doc,
- images_dict['images'],
- detailed=False)
- return self.to_xml_string(node, True)
+ images = etree.Element('images', nsmap=self.NSMAP)
+ for image_dict in images_dict['images']:
+ image = etree.SubElement(images, 'image')
+ self._populate_image(image, image_dict, False)
+ return self._to_xml(images)
def detail(self, images_dict):
- xml_doc = minidom.Document()
- node = self._image_list_to_xml(xml_doc,
- images_dict['images'],
- detailed=True)
- return self.to_xml_string(node, True)
+ images = etree.Element('images', nsmap=self.NSMAP)
+ for image_dict in images_dict['images']:
+ image = etree.SubElement(images, 'image')
+ self._populate_image(image, image_dict, True)
+ return self._to_xml(images)
def show(self, image_dict):
- xml_doc = minidom.Document()
- node = self._image_to_xml_detailed(xml_doc,
- image_dict['image'])
- return self.to_xml_string(node, True)
+ image = etree.Element('image', nsmap=self.NSMAP)
+ self._populate_image(image, image_dict['image'], True)
+ return self._to_xml(image)
def create_resource(version='1.0'):
diff --git a/nova/api/openstack/ips.py b/nova/api/openstack/ips.py
index a74fae487..7e644ba04 100644
--- a/nova/api/openstack/ips.py
+++ b/nova/api/openstack/ips.py
@@ -15,14 +15,15 @@
# License for the specific language governing permissions and limitations
# under the License.
+from lxml import etree
import time
-from xml.dom import minidom
from webob import exc
import nova
import nova.api.openstack.views.addresses
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova import db
@@ -102,42 +103,36 @@ class ControllerV11(Controller):
class IPXMLSerializer(wsgi.XMLDictSerializer):
+
+ NSMAP = {None: xmlutil.XMLNS_V11}
+
def __init__(self, xmlns=wsgi.XMLNS_V11):
super(IPXMLSerializer, self).__init__(xmlns=xmlns)
- def _ip_to_xml(self, xml_doc, ip_dict):
- ip_node = xml_doc.createElement('ip')
- ip_node.setAttribute('addr', ip_dict['addr'])
- ip_node.setAttribute('version', str(ip_dict['version']))
- return ip_node
-
- def _network_to_xml(self, xml_doc, network_id, ip_dicts):
- network_node = xml_doc.createElement('network')
- network_node.setAttribute('id', network_id)
+ def populate_addresses_node(self, addresses_elem, addresses_dict):
+ for (network_id, ip_dicts) in addresses_dict.items():
+ network_elem = self._create_network_node(network_id, ip_dicts)
+ addresses_elem.append(network_elem)
+ def _create_network_node(self, network_id, ip_dicts):
+ network_elem = etree.Element('network', nsmap=self.NSMAP)
+ network_elem.set('id', str(network_id))
for ip_dict in ip_dicts:
- ip_node = self._ip_to_xml(xml_doc, ip_dict)
- network_node.appendChild(ip_node)
-
- return network_node
-
- def networks_to_xml(self, xml_doc, networks_container):
- addresses_node = xml_doc.createElement('addresses')
- for (network_id, ip_dicts) in networks_container.items():
- network_node = self._network_to_xml(xml_doc, network_id, ip_dicts)
- addresses_node.appendChild(network_node)
- return addresses_node
-
- def show(self, network_container):
- (network_id, ip_dicts) = network_container.items()[0]
- xml_doc = minidom.Document()
- node = self._network_to_xml(xml_doc, network_id, ip_dicts)
- return self.to_xml_string(node, False)
-
- def index(self, addresses_container):
- xml_doc = minidom.Document()
- node = self.networks_to_xml(xml_doc, addresses_container['addresses'])
- return self.to_xml_string(node, False)
+ ip_elem = etree.SubElement(network_elem, 'ip')
+ ip_elem.set('version', str(ip_dict['version']))
+ ip_elem.set('addr', ip_dict['addr'])
+ return network_elem
+
+ def show(self, network_dict):
+ (network_id, ip_dicts) = network_dict.items()[0]
+ network = self._create_network_node(network_id, ip_dicts)
+ return self._to_xml(network)
+
+ def index(self, addresses_dict):
+ addresses = etree.Element('addresses', nsmap=self.NSMAP)
+ self.populate_addresses_node(addresses,
+ addresses_dict.get('addresses', {}))
+ return self._to_xml(addresses)
def create_resource(version):
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 86afa3b62..f6df94eea 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -20,12 +20,12 @@ Module dedicated functions/classes dealing with rate limiting requests.
import copy
import httplib
import json
+from lxml import etree
import math
import re
import time
import urllib
import webob.exc
-from xml.dom import minidom
from collections import defaultdict
@@ -38,6 +38,7 @@ from nova.api.openstack import common
from nova.api.openstack import faults
from nova.api.openstack.views import limits as limits_views
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
# Convenience constants for the limits dictionary passed to Limiter().
@@ -81,52 +82,49 @@ class LimitsXMLSerializer(wsgi.XMLDictSerializer):
xmlns = wsgi.XMLNS_V11
+ NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
+
def __init__(self):
pass
- def _create_rates_node(self, xml_doc, rates):
- rates_node = xml_doc.createElement('rates')
+ def _create_rates_node(self, rates):
+ rates_elem = etree.Element('rates', nsmap=self.NSMAP)
for rate in rates:
- rate_node = xml_doc.createElement('rate')
- rate_node.setAttribute('uri', rate['uri'])
- rate_node.setAttribute('regex', rate['regex'])
-
+ rate_node = etree.SubElement(rates_elem, 'rate')
+ rate_node.set('uri', rate['uri'])
+ rate_node.set('regex', rate['regex'])
for limit in rate['limit']:
- limit_node = xml_doc.createElement('limit')
- limit_node.setAttribute('value', str(limit['value']))
- limit_node.setAttribute('verb', limit['verb'])
- limit_node.setAttribute('remaining', str(limit['remaining']))
- limit_node.setAttribute('unit', limit['unit'])
- limit_node.setAttribute('next-available',
- str(limit['next-available']))
- rate_node.appendChild(limit_node)
-
- rates_node.appendChild(rate_node)
- return rates_node
-
- def _create_absolute_node(self, xml_doc, absolutes):
- absolute_node = xml_doc.createElement('absolute')
- for key, value in absolutes.iteritems():
- limit_node = xml_doc.createElement('limit')
- limit_node.setAttribute('name', key)
- limit_node.setAttribute('value', str(value))
- absolute_node.appendChild(limit_node)
- return absolute_node
-
- def _limits_to_xml(self, xml_doc, limits):
- limits_node = xml_doc.createElement('limits')
- rates_node = self._create_rates_node(xml_doc, limits['rate'])
- limits_node.appendChild(rates_node)
-
- absolute_node = self._create_absolute_node(xml_doc, limits['absolute'])
- limits_node.appendChild(absolute_node)
-
- return limits_node
+ limit_elem = etree.SubElement(rate_node, 'limit')
+ limit_elem.set('value', str(limit['value']))
+ limit_elem.set('verb', str(limit['verb']))
+ limit_elem.set('remaining', str(limit['remaining']))
+ limit_elem.set('unit', str(limit['unit']))
+ limit_elem.set('next-available', str(limit['next-available']))
+ return rates_elem
+
+ def _create_absolute_node(self, absolute_dict):
+ absolute_elem = etree.Element('absolute', nsmap=self.NSMAP)
+ for key, value in absolute_dict.items():
+ limit_elem = etree.SubElement(absolute_elem, 'limit')
+ limit_elem.set('name', str(key))
+ limit_elem.set('value', str(value))
+ return absolute_elem
+
+ def _populate_limits(self, limits_elem, limits_dict):
+ """Populate a limits xml element from a dict."""
+
+ rates_elem = self._create_rates_node(
+ limits_dict.get('rate', []))
+ limits_elem.append(rates_elem)
+
+ absolutes_elem = self._create_absolute_node(
+ limits_dict.get('absolute', {}))
+ limits_elem.append(absolutes_elem)
def index(self, limits_dict):
- xml_doc = minidom.Document()
- node = self._limits_to_xml(xml_doc, limits_dict['limits'])
- return self.to_xml_string(node, False)
+ limits = etree.Element('limits', nsmap=self.NSMAP)
+ self._populate_limits(limits, limits_dict['limits'])
+ return self._to_xml(limits)
def create_resource(version='1.0'):
diff --git a/nova/api/openstack/schemas/v1.1/addresses.rng b/nova/api/openstack/schemas/v1.1/addresses.rng
new file mode 100644
index 000000000..b498e8a63
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/addresses.rng
@@ -0,0 +1,14 @@
+<element name="addresses" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <zeroOrMore>
+ <element name="network">
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="version"> <text/> </attribute>
+ <attribute name="addr"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/flavor.rng b/nova/api/openstack/schemas/v1.1/flavor.rng
new file mode 100644
index 000000000..a00e4e9ee
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/flavor.rng
@@ -0,0 +1,10 @@
+<element name="flavor" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="ram"> <text/> </attribute>
+ <attribute name="disk"> <text/> </attribute>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/flavors.rng b/nova/api/openstack/schemas/v1.1/flavors.rng
new file mode 100644
index 000000000..b7a3acc01
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/flavors.rng
@@ -0,0 +1,6 @@
+<element name="flavors" xmlns="http://relaxng.org/ns/structure/1.0"
+ ns="http://docs.openstack.org/compute/api/v1.1">
+ <zeroOrMore>
+ <externalRef href="flavor.rng"/>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/flavors_index.rng b/nova/api/openstack/schemas/v1.1/flavors_index.rng
new file mode 100644
index 000000000..d1a4fedb1
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/flavors_index.rng
@@ -0,0 +1,12 @@
+<element name="flavors" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <zeroOrMore>
+ <element name="flavor">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/image.rng b/nova/api/openstack/schemas/v1.1/image.rng
new file mode 100644
index 000000000..887f76751
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/image.rng
@@ -0,0 +1,30 @@
+<element name="image" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="updated"> <text/> </attribute>
+ <attribute name="created"> <text/> </attribute>
+ <attribute name="status"> <text/> </attribute>
+ <optional>
+ <attribute name="progress"> <text/> </attribute>
+ </optional>
+ <optional>
+ <element name="server">
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+ <element name="metadata">
+ <zeroOrMore>
+ <element name="meta">
+ <attribute name="key"> <text/> </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/images.rng b/nova/api/openstack/schemas/v1.1/images.rng
new file mode 100644
index 000000000..064d4d9cc
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/images.rng
@@ -0,0 +1,6 @@
+<element name="images" xmlns="http://relaxng.org/ns/structure/1.0"
+ ns="http://docs.openstack.org/compute/api/v1.1">
+ <zeroOrMore>
+ <externalRef href="image.rng"/>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/images_index.rng b/nova/api/openstack/schemas/v1.1/images_index.rng
new file mode 100644
index 000000000..81af19cb5
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/images_index.rng
@@ -0,0 +1,12 @@
+<element name="images" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <zeroOrMore>
+ <element name="image">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/limits.rng b/nova/api/openstack/schemas/v1.1/limits.rng
new file mode 100644
index 000000000..1af8108ec
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/limits.rng
@@ -0,0 +1,28 @@
+<element name="limits" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <element name="rates">
+ <zeroOrMore>
+ <element name="rate">
+ <attribute name="uri"> <text/> </attribute>
+ <attribute name="regex"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="limit">
+ <attribute name="value"> <text/> </attribute>
+ <attribute name="verb"> <text/> </attribute>
+ <attribute name="remaining"> <text/> </attribute>
+ <attribute name="unit"> <text/> </attribute>
+ <attribute name="next-available"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+ </element>
+ <element name="absolute">
+ <zeroOrMore>
+ <element name="limit">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="value"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/metadata.rng b/nova/api/openstack/schemas/v1.1/metadata.rng
new file mode 100644
index 000000000..b2f5d702a
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/metadata.rng
@@ -0,0 +1,9 @@
+ <element name="metadata" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <zeroOrMore>
+ <element name="meta">
+ <attribute name="key"> <text/> </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
diff --git a/nova/api/openstack/schemas/v1.1/server.rng b/nova/api/openstack/schemas/v1.1/server.rng
index ef835e408..4eb1a0b85 100644
--- a/nova/api/openstack/schemas/v1.1/server.rng
+++ b/nova/api/openstack/schemas/v1.1/server.rng
@@ -17,9 +17,6 @@
<optional>
<attribute name="adminPass"> <text/> </attribute>
</optional>
- <zeroOrMore>
- <externalRef href="../atom-link.rng"/>
- </zeroOrMore>
<element name="image">
<attribute name="id"> <text/> </attribute>
<externalRef href="../atom-link.rng"/>
@@ -49,4 +46,7 @@
</element>
</zeroOrMore>
</element>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
</element>
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index f5447edc5..5affd1f33 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -17,8 +17,8 @@ import base64
import os
import traceback
+from lxml import etree
from webob import exc
-from xml.dom import minidom
import webob
from nova import compute
@@ -38,6 +38,7 @@ import nova.api.openstack.views.addresses
import nova.api.openstack.views.flavors
import nova.api.openstack.views.images
import nova.api.openstack.views.servers
+from nova.api.openstack import xmlutil
LOG = logging.getLogger('nova.api.openstack.servers')
@@ -850,130 +851,113 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer):
class ServerXMLSerializer(wsgi.XMLDictSerializer):
- xmlns = wsgi.XMLNS_V11
+ NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
def __init__(self):
self.metadata_serializer = common.MetadataXMLSerializer()
self.addresses_serializer = ips.IPXMLSerializer()
- def _create_basic_entity_node(self, xml_doc, id, links, name):
- basic_node = xml_doc.createElement(name)
- basic_node.setAttribute('id', str(id))
- link_nodes = self._create_link_nodes(xml_doc, links)
- for link_node in link_nodes:
- basic_node.appendChild(link_node)
- return basic_node
-
- def _create_metadata_node(self, xml_doc, metadata):
- return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata)
-
- def _create_addresses_node(self, xml_doc, addresses):
- return self.addresses_serializer.networks_to_xml(xml_doc, addresses)
-
- def _add_server_attributes(self, node, server):
- node.setAttribute('id', str(server['id']))
- node.setAttribute('userId', str(server['user_id']))
- node.setAttribute('tenantId', str(server['tenant_id']))
- node.setAttribute('uuid', str(server['uuid']))
- node.setAttribute('hostId', str(server['hostId']))
- node.setAttribute('name', server['name'])
- node.setAttribute('created', str(server['created']))
- node.setAttribute('updated', str(server['updated']))
- node.setAttribute('status', server['status'])
- if 'accessIPv4' in server:
- node.setAttribute('accessIPv4', str(server['accessIPv4']))
- if 'accessIPv6' in server:
- node.setAttribute('accessIPv6', str(server['accessIPv6']))
- if 'progress' in server:
- node.setAttribute('progress', str(server['progress']))
-
- def _server_to_xml(self, xml_doc, server):
- server_node = xml_doc.createElement('server')
- server_node.setAttribute('id', str(server['id']))
- server_node.setAttribute('name', server['name'])
- link_nodes = self._create_link_nodes(xml_doc,
- server['links'])
- for link_node in link_nodes:
- server_node.appendChild(link_node)
- return server_node
-
- def _server_to_xml_detailed(self, xml_doc, server):
- server_node = xml_doc.createElement('server')
- self._add_server_attributes(server_node, server)
-
- link_nodes = self._create_link_nodes(xml_doc,
- server['links'])
- for link_node in link_nodes:
- server_node.appendChild(link_node)
-
- if 'image' in server:
- image_node = self._create_basic_entity_node(xml_doc,
- server['image']['id'],
- server['image']['links'],
- 'image')
- server_node.appendChild(image_node)
-
- if 'flavor' in server:
- flavor_node = self._create_basic_entity_node(xml_doc,
- server['flavor']['id'],
- server['flavor']['links'],
- 'flavor')
- server_node.appendChild(flavor_node)
-
- metadata = server.get('metadata', {}).items()
- if len(metadata) > 0:
- metadata_node = self._create_metadata_node(xml_doc, metadata)
- server_node.appendChild(metadata_node)
-
- addresses_node = self._create_addresses_node(xml_doc,
- server['addresses'])
- server_node.appendChild(addresses_node)
-
- if 'security_groups' in server:
- security_groups_node = self._create_security_groups_node(xml_doc,
- server['security_groups'])
- server_node.appendChild(security_groups_node)
-
- return server_node
-
- def _server_list_to_xml(self, xml_doc, servers, detailed):
- container_node = xml_doc.createElement('servers')
+ def _create_metadata_node(self, metadata_dict):
+ metadata_elem = etree.Element('metadata', nsmap=self.NSMAP)
+ self.metadata_serializer.populate_metadata(metadata_elem,
+ metadata_dict)
+ return metadata_elem
+
+ def _create_image_node(self, image_dict):
+ image_elem = etree.Element('image', nsmap=self.NSMAP)
+ image_elem.set('id', str(image_dict['id']))
+ for link in image_dict.get('links', []):
+ elem = etree.SubElement(image_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return image_elem
+
+ def _create_flavor_node(self, flavor_dict):
+ flavor_elem = etree.Element('flavor', nsmap=self.NSMAP)
+ flavor_elem.set('id', str(flavor_dict['id']))
+ for link in flavor_dict.get('links', []):
+ elem = etree.SubElement(flavor_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return flavor_elem
+
+ def _create_addresses_node(self, addresses_dict):
+ addresses_elem = etree.Element('addresses', nsmap=self.NSMAP)
+ self.addresses_serializer.populate_addresses_node(addresses_elem,
+ addresses_dict)
+ return addresses_elem
+
+ def _populate_server(self, server_elem, server_dict, detailed=False):
+ """Populate a server xml element from a dict."""
+
+ server_elem.set('name', server_dict['name'])
+ server_elem.set('id', str(server_dict['id']))
if detailed:
- server_to_xml = self._server_to_xml_detailed
- else:
- server_to_xml = self._server_to_xml
-
- for server in servers:
- item_node = server_to_xml(xml_doc, server)
- container_node.appendChild(item_node)
- return container_node
+ server_elem.set('uuid', str(server_dict['uuid']))
+ server_elem.set('userId', str(server_dict['user_id']))
+ server_elem.set('tenantId', str(server_dict['tenant_id']))
+ server_elem.set('updated', str(server_dict['updated']))
+ server_elem.set('created', str(server_dict['created']))
+ server_elem.set('hostId', str(server_dict['hostId']))
+ server_elem.set('accessIPv4', str(server_dict['accessIPv4']))
+ server_elem.set('accessIPv6', str(server_dict['accessIPv6']))
+ server_elem.set('status', str(server_dict['status']))
+ if 'progress' in server_dict:
+ server_elem.set('progress', str(server_dict['progress']))
+ image_elem = self._create_image_node(server_dict['image'])
+ server_elem.append(image_elem)
+
+ flavor_elem = self._create_flavor_node(server_dict['flavor'])
+ server_elem.append(flavor_elem)
+
+ meta_elem = self._create_metadata_node(
+ server_dict.get('metadata', {}))
+ server_elem.append(meta_elem)
+
+ addresses_elem = self._create_addresses_node(
+ server_dict.get('addresses', {}))
+ server_elem.append(addresses_elem)
+ groups = server_dict.get('security_groups')
+ if groups:
+ groups_elem = etree.SubElement(server_elem, 'security_groups')
+ for group in groups:
+ group_elem = etree.SubElement(groups_elem,
+ 'security_group')
+ group_elem.set('name', group['name'])
+
+ for link in server_dict.get('links', []):
+ elem = etree.SubElement(server_elem,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ return server_elem
def index(self, servers_dict):
- xml_doc = minidom.Document()
- node = self._server_list_to_xml(xml_doc,
- servers_dict['servers'],
- detailed=False)
- return self.to_xml_string(node, True)
+ servers = etree.Element('servers', nsmap=self.NSMAP)
+ for server_dict in servers_dict['servers']:
+ server = etree.SubElement(servers, 'server')
+ self._populate_server(server, server_dict, False)
+ return self._to_xml(servers)
def detail(self, servers_dict):
- xml_doc = minidom.Document()
- node = self._server_list_to_xml(xml_doc,
- servers_dict['servers'],
- detailed=True)
- return self.to_xml_string(node, True)
+ servers = etree.Element('servers', nsmap=self.NSMAP)
+ for server_dict in servers_dict['servers']:
+ server = etree.SubElement(servers, 'server')
+ self._populate_server(server, server_dict, True)
+ return self._to_xml(servers)
def show(self, server_dict):
- xml_doc = minidom.Document()
- node = self._server_to_xml_detailed(xml_doc,
- server_dict['server'])
- return self.to_xml_string(node, True)
+ server = etree.Element('server', nsmap=self.NSMAP)
+ self._populate_server(server, server_dict['server'], True)
+ return self._to_xml(server)
def create(self, server_dict):
- xml_doc = minidom.Document()
- node = self._server_to_xml_detailed(xml_doc,
- server_dict['server'])
- node.setAttribute('adminPass', server_dict['server']['adminPass'])
- return self.to_xml_string(node, True)
+ server = etree.Element('server', nsmap=self.NSMAP)
+ self._populate_server(server, server_dict['server'], True)
+ server.set('adminPass', server_dict['server']['adminPass'])
+ return self._to_xml(server)
def action(self, server_dict):
#NOTE(bcwaldon): We need a way to serialize actions individually. This
@@ -981,23 +965,9 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer):
return self.create(server_dict)
def update(self, server_dict):
- xml_doc = minidom.Document()
- node = self._server_to_xml_detailed(xml_doc,
- server_dict['server'])
- return self.to_xml_string(node, True)
-
- def _security_group_to_xml(self, doc, security_group):
- node = doc.createElement('security_group')
- node.setAttribute('name', str(security_group.get('name')))
- return node
-
- def _create_security_groups_node(self, xml_doc, security_groups):
- security_groups_node = xml_doc.createElement('security_groups')
- if security_groups:
- for security_group in security_groups:
- node = self._security_group_to_xml(xml_doc, security_group)
- security_groups_node.appendChild(node)
- return security_groups_node
+ server = etree.Element('server', nsmap=self.NSMAP)
+ self._populate_server(server, server_dict['server'], True)
+ return self._to_xml(server)
def create_resource(version='1.0'):
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index e2f892fb6..31dd9dc11 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -16,12 +16,13 @@
# under the License.
from datetime import datetime
+from lxml import etree
import webob
import webob.dec
-from xml.dom import minidom
import nova.api.openstack.views.versions
from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
VERSIONS = {
@@ -159,83 +160,51 @@ class VersionsRequestDeserializer(wsgi.RequestDeserializer):
class VersionsXMLSerializer(wsgi.XMLDictSerializer):
- #TODO(wwolf): this is temporary until we get rid of toprettyxml
- # in the base class (XMLDictSerializer), which I plan to do in
- # another branch
- def to_xml_string(self, node, has_atom=False):
- self._add_xmlns(node, has_atom)
- return node.toxml(encoding='UTF-8')
-
- def _versions_to_xml(self, versions, name="versions", xmlns=None):
- root = self._xml_doc.createElement(name)
- root.setAttribute("xmlns", wsgi.XMLNS_V11)
- root.setAttribute("xmlns:atom", wsgi.XMLNS_ATOM)
- for version in versions:
- root.appendChild(self._create_version_node(version))
-
- return root
-
- def _create_media_types(self, media_types):
- base = self._xml_doc.createElement('media-types')
- for type in media_types:
- node = self._xml_doc.createElement('media-type')
- node.setAttribute('base', type['base'])
- node.setAttribute('type', type['type'])
- base.appendChild(node)
-
- return base
-
- def _create_version_node(self, version, create_ns=False):
- version_node = self._xml_doc.createElement('version')
- if create_ns:
- xmlns = wsgi.XMLNS_V11
- xmlns_atom = wsgi.XMLNS_ATOM
- version_node.setAttribute('xmlns', xmlns)
- version_node.setAttribute('xmlns:atom', xmlns_atom)
-
- version_node.setAttribute('id', version['id'])
- version_node.setAttribute('status', version['status'])
+ def _populate_version(self, version_node, version):
+ version_node.set('id', version['id'])
+ version_node.set('status', version['status'])
if 'updated' in version:
- version_node.setAttribute('updated', version['updated'])
-
+ version_node.set('updated', version['updated'])
if 'media-types' in version:
- media_types = self._create_media_types(version['media-types'])
- version_node.appendChild(media_types)
-
- link_nodes = self._create_link_nodes(self._xml_doc, version['links'])
- for link in link_nodes:
- version_node.appendChild(link)
-
- return version_node
+ media_types = etree.SubElement(version_node, 'media-types')
+ for mtype in version['media-types']:
+ elem = etree.SubElement(media_types, 'media-type')
+ elem.set('base', mtype['base'])
+ elem.set('type', mtype['type'])
+ for link in version.get('links', []):
+ elem = etree.SubElement(version_node,
+ '{%s}link' % xmlutil.XMLNS_ATOM)
+ elem.set('rel', link['rel'])
+ elem.set('href', link['href'])
+ if 'type' in link:
+ elem.set('type', link['type'])
+
+ NSMAP = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
def index(self, data):
- self._xml_doc = minidom.Document()
- node = self._versions_to_xml(data['versions'])
-
- return self.to_xml_string(node)
+ root = etree.Element('versions', nsmap=self.NSMAP)
+ for version in data['versions']:
+ version_elem = etree.SubElement(root, 'version')
+ self._populate_version(version_elem, version)
+ return self._to_xml(root)
def show(self, data):
- self._xml_doc = minidom.Document()
- node = self._create_version_node(data['version'], True)
-
- return self.to_xml_string(node)
+ root = etree.Element('version', nsmap=self.NSMAP)
+ self._populate_version(root, data['version'])
+ return self._to_xml(root)
def multi(self, data):
- self._xml_doc = minidom.Document()
- node = self._versions_to_xml(data['choices'], 'choices',
- xmlns=wsgi.XMLNS_V11)
-
- return self.to_xml_string(node)
+ root = etree.Element('choices', nsmap=self.NSMAP)
+ for version in data['choices']:
+ version_elem = etree.SubElement(root, 'version')
+ self._populate_version(version_elem, version)
+ return self._to_xml(root)
class VersionsAtomSerializer(wsgi.XMLDictSerializer):
- #TODO(wwolf): this is temporary until we get rid of toprettyxml
- # in the base class (XMLDictSerializer), which I plan to do in
- # another branch
- def to_xml_string(self, node, has_atom=False):
- self._add_xmlns(node, has_atom)
- return node.toxml(encoding='UTF-8')
+
+ NSMAP = {None: xmlutil.XMLNS_ATOM}
def __init__(self, metadata=None, xmlns=None):
self.metadata = metadata or {}
@@ -244,14 +213,6 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
else:
self.xmlns = xmlns
- def _create_text_elem(self, name, text, type=None):
- elem = self._xml_doc.createElement(name)
- if type:
- elem.setAttribute('type', type)
- elem_text = self._xml_doc.createTextNode(text)
- elem.appendChild(elem_text)
- return elem
-
def _get_most_recent_update(self, versions):
recent = None
for version in versions:
@@ -269,105 +230,64 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
link_href = link_href.rstrip('/')
return link_href.rsplit('/', 1)[0] + '/'
- def _create_detail_meta(self, root, version):
- title = self._create_text_elem('title', "About This Version",
- type='text')
-
- updated = self._create_text_elem('updated', version['updated'])
-
- uri = version['links'][0]['href']
- id = self._create_text_elem('id', uri)
-
- link = self._xml_doc.createElement('link')
- link.setAttribute('rel', 'self')
- link.setAttribute('href', uri)
+ def _create_feed(self, versions, feed_title, feed_id):
+ feed = etree.Element('feed', nsmap=self.NSMAP)
+ title = etree.SubElement(feed, 'title')
+ title.set('type', 'text')
+ title.text = feed_title
- author = self._xml_doc.createElement('author')
- author_name = self._create_text_elem('name', 'Rackspace')
- author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/')
- author.appendChild(author_name)
- author.appendChild(author_uri)
-
- root.appendChild(title)
- root.appendChild(updated)
- root.appendChild(id)
- root.appendChild(author)
- root.appendChild(link)
-
- def _create_list_meta(self, root, versions):
- title = self._create_text_elem('title', "Available API Versions",
- type='text')
# Set this updated to the most recently updated version
recent = self._get_most_recent_update(versions)
- updated = self._create_text_elem('updated', recent)
-
- base_url = self._get_base_url(versions[0]['links'][0]['href'])
- id = self._create_text_elem('id', base_url)
+ etree.SubElement(feed, 'updated').text = recent
- link = self._xml_doc.createElement('link')
- link.setAttribute('rel', 'self')
- link.setAttribute('href', base_url)
+ etree.SubElement(feed, 'id').text = feed_id
- author = self._xml_doc.createElement('author')
- author_name = self._create_text_elem('name', 'Rackspace')
- author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/')
- author.appendChild(author_name)
- author.appendChild(author_uri)
+ link = etree.SubElement(feed, 'link')
+ link.set('rel', 'self')
+ link.set('href', feed_id)
- root.appendChild(title)
- root.appendChild(updated)
- root.appendChild(id)
- root.appendChild(author)
- root.appendChild(link)
+ author = etree.SubElement(feed, 'author')
+ etree.SubElement(author, 'name').text = 'Rackspace'
+ etree.SubElement(author, 'uri').text = 'http://www.rackspace.com/'
- def _create_version_entries(self, root, versions):
for version in versions:
- entry = self._xml_doc.createElement('entry')
-
- id = self._create_text_elem('id', version['links'][0]['href'])
- title = self._create_text_elem('title',
- 'Version %s' % version['id'],
- type='text')
- updated = self._create_text_elem('updated', version['updated'])
-
- entry.appendChild(id)
- entry.appendChild(title)
- entry.appendChild(updated)
-
- for link in version['links']:
- link_node = self._xml_doc.createElement('link')
- link_node.setAttribute('rel', link['rel'])
- link_node.setAttribute('href', link['href'])
- if 'type' in link:
- link_node.setAttribute('type', link['type'])
-
- entry.appendChild(link_node)
-
- content = self._create_text_elem('content',
- 'Version %s %s (%s)' %
- (version['id'],
- version['status'],
- version['updated']),
- type='text')
-
- entry.appendChild(content)
- root.appendChild(entry)
+ feed.append(self._create_version_entry(version))
+
+ return feed
+
+ def _create_version_entry(self, version):
+ entry = etree.Element('entry')
+ etree.SubElement(entry, 'id').text = version['links'][0]['href']
+ title = etree.SubElement(entry, 'title')
+ title.set('type', 'text')
+ title.text = 'Version %s' % version['id']
+ etree.SubElement(entry, 'updated').text = version['updated']
+
+ for link in version['links']:
+ link_elem = etree.SubElement(entry, 'link')
+ link_elem.set('rel', link['rel'])
+ link_elem.set('href', link['href'])
+ if 'type' in link:
+ link_elem.set('type', link['type'])
+
+ content = etree.SubElement(entry, 'content')
+ content.set('type', 'text')
+ content.text = 'Version %s %s (%s)' % (version['id'],
+ version['status'],
+ version['updated'])
+ return entry
def index(self, data):
- self._xml_doc = minidom.Document()
- node = self._xml_doc.createElementNS(self.xmlns, 'feed')
- self._create_list_meta(node, data['versions'])
- self._create_version_entries(node, data['versions'])
-
- return self.to_xml_string(node)
+ versions = data['versions']
+ feed_id = self._get_base_url(versions[0]['links'][0]['href'])
+ feed = self._create_feed(versions, 'Available API Versions', feed_id)
+ return self._to_xml(feed)
def show(self, data):
- self._xml_doc = minidom.Document()
- node = self._xml_doc.createElementNS(self.xmlns, 'feed')
- self._create_detail_meta(node, data['version'])
- self._create_version_entries(node, [data['version']])
-
- return self.to_xml_string(node)
+ version = data['version']
+ feed_id = version['links'][0]['href']
+ feed = self._create_feed([version], 'About This Version', feed_id)
+ return self._to_xml(feed)
class VersionsHeadersSerializer(wsgi.ResponseHeadersSerializer):
diff --git a/nova/api/openstack/views/versions.py b/nova/api/openstack/views/versions.py
index 03da80818..1ac398706 100644
--- a/nova/api/openstack/views/versions.py
+++ b/nova/api/openstack/views/versions.py
@@ -52,7 +52,7 @@ class ViewBuilder(object):
def build_versions(self, versions):
version_objs = []
- for version in versions:
+ for version in sorted(versions.keys()):
version = versions[version]
version_objs.append({
"id": version['id'],
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 8641e960a..bdcadcb99 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -1,5 +1,6 @@
import json
+from lxml import etree
import webob
from xml.dom import minidom
from xml.parsers import expat
@@ -392,6 +393,10 @@ class XMLDictSerializer(DictSerializer):
link_nodes.append(link_node)
return link_nodes
+ def _to_xml(self, root):
+ """Convert the xml object to an xml string."""
+ return etree.tostring(root, encoding='UTF-8', xml_declaration=True)
+
class ResponseHeadersSerializer(ActionDispatcher):
"""Default response headers serialization"""