summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/v2/contrib/flavorextraspecs.py20
-rw-r--r--nova/api/openstack/v2/contrib/floating_ips.py51
-rw-r--r--nova/api/openstack/v2/contrib/hosts.py76
-rw-r--r--nova/api/openstack/v2/contrib/keypairs.py33
-rw-r--r--nova/api/openstack/v2/contrib/quotas.py55
-rw-r--r--nova/api/openstack/v2/extensions.py20
-rw-r--r--nova/api/openstack/xmlutil.py58
7 files changed, 279 insertions, 34 deletions
diff --git a/nova/api/openstack/v2/contrib/flavorextraspecs.py b/nova/api/openstack/v2/contrib/flavorextraspecs.py
index bb38ea64e..611ef3685 100644
--- a/nova/api/openstack/v2/contrib/flavorextraspecs.py
+++ b/nova/api/openstack/v2/contrib/flavorextraspecs.py
@@ -19,6 +19,8 @@
from webob import exc
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import db
from nova import exception
@@ -95,6 +97,16 @@ class FlavorExtraSpecsController(object):
raise error
+class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1)
+
+
+class ExtraSpecsSerializer(xmlutil.XMLTemplateSerializer):
+ def default(self):
+ return ExtraSpecsTemplate()
+
+
class Flavorextraspecs(extensions.ExtensionDescriptor):
"""Instance type (flavor) extra specs"""
@@ -105,9 +117,17 @@ class Flavorextraspecs(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
+
+ body_serializers = {
+ 'application/xml': ExtraSpecsSerializer(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers)
+
res = extensions.ResourceExtension(
'os-extra_specs',
FlavorExtraSpecsController(),
+ serializer=serializer,
parent=dict(member_name='flavor', collection_name='flavors'))
resources.append(res)
diff --git a/nova/api/openstack/v2/contrib/floating_ips.py b/nova/api/openstack/v2/contrib/floating_ips.py
index 8bb575682..072ada1ba 100644
--- a/nova/api/openstack/v2/contrib/floating_ips.py
+++ b/nova/api/openstack/v2/contrib/floating_ips.py
@@ -18,6 +18,8 @@
import webob
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import compute
from nova import exception
@@ -51,16 +53,6 @@ def _translate_floating_ips_view(floating_ips):
class FloatingIPController(object):
"""The Floating IPs API controller for the OpenStack API."""
- _serialization_metadata = {
- 'application/xml': {
- "attributes": {
- "floating_ip": [
- "id",
- "ip",
- "instance_id",
- "fixed_ip",
- ]}}}
-
def __init__(self):
self.network_api = network.API()
super(FloatingIPController, self).__init__()
@@ -117,6 +109,38 @@ class FloatingIPController(object):
return self.network_api.get_floating_ip(context, value)['address']
+def make_float_ip(elem):
+ elem.set('id')
+ elem.set('ip')
+ elem.set('fixed_ip')
+ elem.set('instance_id')
+
+
+class FloatingIPTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('floating_ip',
+ selector='floating_ip')
+ make_float_ip(root)
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class FloatingIPsTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('floating_ips')
+ elem = xmlutil.SubTemplateElement(root, 'floating_ip',
+ selector='floating_ips')
+ make_float_ip(elem)
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class FloatingIPSerializer(xmlutil.XMLTemplateSerializer):
+ def index(self):
+ return FloatingIPsTemplate()
+
+ def default(self):
+ return FloatingIPTemplate()
+
+
class Floating_ips(extensions.ExtensionDescriptor):
"""Floating IPs support"""
@@ -179,8 +203,15 @@ class Floating_ips(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
+ body_serializers = {
+ 'application/xml': FloatingIPSerializer(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers)
+
res = extensions.ResourceExtension('os-floating-ips',
FloatingIPController(),
+ serializer=serializer,
member_actions={})
resources.append(res)
diff --git a/nova/api/openstack/v2/contrib/hosts.py b/nova/api/openstack/v2/contrib/hosts.py
index eb5074520..b422a80ab 100644
--- a/nova/api/openstack/v2/contrib/hosts.py
+++ b/nova/api/openstack/v2/contrib/hosts.py
@@ -16,8 +16,12 @@
"""The hosts admin extension."""
import webob.exc
+from xml.dom import minidom
+from xml.parsers import expat
from nova.api.openstack import common
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import compute
from nova import exception
@@ -116,6 +120,64 @@ class HostController(object):
return self._host_power_action(req, host=id, action="reboot")
+class HostIndexTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ def shimmer(obj, do_raise=False):
+ # A bare list is passed in; we need to wrap it in a dict
+ return dict(hosts=obj)
+
+ root = xmlutil.TemplateElement('hosts', selector=shimmer)
+ elem = xmlutil.SubTemplateElement(root, 'host', selector='hosts')
+ elem.set('host_name')
+ elem.set('service')
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class HostUpdateTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('host')
+ root.set('host')
+ root.set('status')
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class HostActionTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('host')
+ root.set('host')
+ root.set('power_action')
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class HostSerializer(xmlutil.XMLTemplateSerializer):
+ def index(self):
+ return HostIndexTemplate()
+
+ def update(self):
+ return HostUpdateTemplate()
+
+ def default(self):
+ return HostActionTemplate()
+
+
+class HostDeserializer(wsgi.XMLDeserializer):
+ def update(self, string):
+ try:
+ node = minidom.parseString(string)
+ except expat.ExpatError:
+ msg = _("cannot understand XML")
+ raise exception.MalformedRequestBody(reason=msg)
+
+ updates = {}
+ for child in node.childNodes[0].childNodes:
+ updates[child.tagName] = self.extract_text(child)
+
+ return dict(body=updates)
+
+
class Hosts(extensions.ExtensionDescriptor):
"""Host administration"""
@@ -125,8 +187,20 @@ class Hosts(extensions.ExtensionDescriptor):
updated = "2011-06-29T00:00:00+00:00"
def get_resources(self):
+ body_serializers = {
+ 'application/xml': HostSerializer(),
+ }
+ body_deserializers = {
+ 'application/xml': HostDeserializer(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers)
+ deserializer = wsgi.RequestDeserializer(body_deserializers)
+
resources = [extensions.ResourceExtension('os-hosts',
- HostController(), collection_actions={'update': 'PUT'},
+ HostController(),
+ serializer=serializer, deserializer=deserializer,
+ collection_actions={'update': 'PUT'},
member_actions={"startup": "GET", "shutdown": "GET",
"reboot": "GET"})]
return resources
diff --git a/nova/api/openstack/v2/contrib/keypairs.py b/nova/api/openstack/v2/contrib/keypairs.py
index e57d40001..954c425df 100644
--- a/nova/api/openstack/v2/contrib/keypairs.py
+++ b/nova/api/openstack/v2/contrib/keypairs.py
@@ -24,6 +24,8 @@ import tempfile
import webob
from webob import exc
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import crypto
from nova import db
@@ -117,6 +119,29 @@ class KeypairController(object):
return {'keypairs': rval}
+class KeypairTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ return xmlutil.MasterTemplate(xmlutil.make_flat_dict('keypair'), 1)
+
+
+class KeypairsTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('keypairs')
+ elem = xmlutil.make_flat_dict('keypair', selector='keypairs',
+ subselector='keypair')
+ root.append(elem)
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class KeypairsSerializer(xmlutil.XMLTemplateSerializer):
+ def index(self):
+ return KeypairsTemplate()
+
+ def default(self):
+ return KeypairTemplate()
+
+
class Keypairs(extensions.ExtensionDescriptor):
"""Keypair Support"""
@@ -128,9 +153,15 @@ class Keypairs(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
+ body_serializers = {
+ 'application/xml': KeypairsSerializer(),
+ }
+ serializer = wsgi.ResponseSerializer(body_serializers)
+
res = extensions.ResourceExtension(
'os-keypairs',
- KeypairController())
+ KeypairController(),
+ serializer=serializer)
resources.append(res)
return resources
diff --git a/nova/api/openstack/v2/contrib/quotas.py b/nova/api/openstack/v2/contrib/quotas.py
index 7ad426c44..d0d31964d 100644
--- a/nova/api/openstack/v2/contrib/quotas.py
+++ b/nova/api/openstack/v2/contrib/quotas.py
@@ -17,30 +17,30 @@
import webob
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova import db
from nova import exception
from nova import quota
+quota_resources = ['metadata_items', 'injected_file_content_bytes',
+ 'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
+ 'injected_files', 'cores']
+
+
class QuotaSetsController(object):
def _format_quota_set(self, project_id, quota_set):
"""Convert the quota object to a result dict"""
- return {'quota_set': {
- 'id': str(project_id),
- 'metadata_items': quota_set['metadata_items'],
- 'injected_file_content_bytes':
- quota_set['injected_file_content_bytes'],
- 'volumes': quota_set['volumes'],
- 'gigabytes': quota_set['gigabytes'],
- 'ram': quota_set['ram'],
- 'floating_ips': quota_set['floating_ips'],
- 'instances': quota_set['instances'],
- 'injected_files': quota_set['injected_files'],
- 'cores': quota_set['cores'],
- }}
+ result = dict(id=str(project_id))
+
+ for resource in quota_resources:
+ result[resource] = quota_set[resource]
+
+ return dict(quota_set=result)
def show(self, req, id):
context = req.environ['nova.context']
@@ -54,11 +54,8 @@ class QuotaSetsController(object):
def update(self, req, id, body):
context = req.environ['nova.context']
project_id = id
- resources = ['metadata_items', 'injected_file_content_bytes',
- 'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
- 'injected_files', 'cores']
for key in body['quota_set'].keys():
- if key in resources:
+ if key in quota_resources:
value = int(body['quota_set'][key])
try:
db.quota_update(context, project_id, key, value)
@@ -72,6 +69,23 @@ class QuotaSetsController(object):
return self._format_quota_set(id, quota._get_default_quotas())
+class QuotaTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('quota_set', selector='quota_set')
+ root.set('id')
+
+ for resource in quota_resources:
+ elem = xmlutil.SubTemplateElement(root, resource)
+ elem.text = resource
+
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class QuotaSerializer(xmlutil.XMLTemplateSerializer):
+ def default(self):
+ return QuotaTemplate()
+
+
class Quotas(extensions.ExtensionDescriptor):
"""Quotas management support"""
@@ -83,8 +97,15 @@ class Quotas(extensions.ExtensionDescriptor):
def get_resources(self):
resources = []
+ body_serializers = {
+ 'application/xml': QuotaSerializer(),
+ }
+
+ serializer = wsgi.ResponseSerializer(body_serializers)
+
res = extensions.ResourceExtension('os-quota-sets',
QuotaSetsController(),
+ serializer=serializer,
member_actions={'defaults': 'GET'})
resources.append(res)
diff --git a/nova/api/openstack/v2/extensions.py b/nova/api/openstack/v2/extensions.py
index bf7ae8b86..91db251f5 100644
--- a/nova/api/openstack/v2/extensions.py
+++ b/nova/api/openstack/v2/extensions.py
@@ -100,6 +100,24 @@ class ExtensionDescriptor(object):
request_exts = []
return request_exts
+ @classmethod
+ def nsmap(cls):
+ """Synthesize a namespace map from extension."""
+
+ # Start with a base nsmap
+ nsmap = ext_nsmap.copy()
+
+ # Add the namespace for the extension
+ nsmap[cls.alias] = cls.namespace
+
+ return nsmap
+
+ @classmethod
+ def xmlname(cls, name):
+ """Synthesize element and attribute names."""
+
+ return '{%s}%s' % (cls.namespace, name)
+
class ActionExtensionController(object):
def __init__(self, application):
@@ -545,7 +563,7 @@ def admin_only(fnc):
def wrap_errors(fn):
- """"Ensure errors are not passed along."""
+ """Ensure errors are not passed along."""
def wrapped(*args):
try:
return fn(*args)
diff --git a/nova/api/openstack/xmlutil.py b/nova/api/openstack/xmlutil.py
index db490e652..10bc08f05 100644
--- a/nova/api/openstack/xmlutil.py
+++ b/nova/api/openstack/xmlutil.py
@@ -134,7 +134,8 @@ class ConstantSelector(object):
class TemplateElement(object):
"""Represent an element in the template."""
- def __init__(self, tag, attrib=None, selector=None, **extra):
+ def __init__(self, tag, attrib=None, selector=None, subselector=None,
+ **extra):
"""Initialize an element.
Initializes an element in the template. Keyword arguments
@@ -146,6 +147,12 @@ class TemplateElement(object):
:param selector: An optional callable taking an object and
optional boolean do_raise indicator and
returning the object bound to the element.
+ :param subselector: An optional callable taking an object and
+ optional boolean do_raise indicator and
+ returning the object bound to the element.
+ This is used to further refine the datum
+ object returned by selector in the event
+ that it is a list of objects.
"""
# Convert selector into a Selector
@@ -154,8 +161,13 @@ class TemplateElement(object):
elif not callable(selector):
selector = Selector(selector)
+ # Convert subselector into a Selector
+ if subselector is not None and not callable(subselector):
+ subselector = Selector(subselector)
+
self.tag = tag
self.selector = selector
+ self.subselector = subselector
self.attrib = {}
self._text = None
self._children = []
@@ -404,6 +416,8 @@ class TemplateElement(object):
# Render all the elements
elems = []
for datum in data:
+ if self.subselector is not None:
+ datum = self.subselector(datum)
elems.append((self._render(parent, datum, patches, nsmap), datum))
# Return all the elements rendered, as well as the
@@ -465,7 +479,7 @@ class TemplateElement(object):
# If there are no children, return it as a closed tag
if len(self) == 0:
- return '<%s/>' % ' '.join(contents)
+ return '<%s/>' % ' '.join([str(i) for i in contents])
# OK, recurse to our children
children = [c.tree() for c in self]
@@ -475,7 +489,8 @@ class TemplateElement(object):
(' '.join(contents), ''.join(children), self.tag))
-def SubTemplateElement(parent, tag, attrib=None, selector=None, **extra):
+def SubTemplateElement(parent, tag, attrib=None, selector=None,
+ subselector=None, **extra):
"""Create a template element as a child of another.
Corresponds to the etree.SubElement interface. Parameters are as
@@ -487,7 +502,8 @@ def SubTemplateElement(parent, tag, attrib=None, selector=None, **extra):
attrib.update(extra)
# Get a TemplateElement
- elem = TemplateElement(tag, attrib=attrib, selector=selector)
+ elem = TemplateElement(tag, attrib=attrib, selector=selector,
+ subselector=subselector)
# Append the parent safely
if parent is not None:
@@ -893,3 +909,37 @@ def make_links(parent, selector=None):
# Just for completeness...
return elem
+
+
+def make_flat_dict(name, selector=None, subselector=None, ns=None):
+ """
+ Utility for simple XML templates that traditionally used
+ XMLDictSerializer with no metadata. Returns a template element
+ where the top-level element has the given tag name, and where
+ sub-elements have tag names derived from the object's keys and
+ text derived from the object's values. This only works for flat
+ dictionary objects, not dictionaries containing nested lists or
+ dictionaries.
+ """
+
+ # Set up the names we need...
+ if ns is None:
+ elemname = name
+ tagname = Selector(0)
+ else:
+ elemname = '{%s}%s' % (ns, name)
+ tagname = lambda obj, do_raise=False: '{%s}%s' % (ns, obj[0])
+
+ if selector is None:
+ selector = name
+
+ # Build the root element
+ root = TemplateElement(elemname, selector=selector,
+ subselector=subselector)
+
+ # Build an element to represent all the keys and values
+ elem = SubTemplateElement(root, tagname, selector=get_items)
+ elem.text = 1
+
+ # Return the template
+ return root