diff options
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/openstack/v2/contrib/flavorextraspecs.py | 20 | ||||
| -rw-r--r-- | nova/api/openstack/v2/contrib/floating_ips.py | 51 | ||||
| -rw-r--r-- | nova/api/openstack/v2/contrib/hosts.py | 76 | ||||
| -rw-r--r-- | nova/api/openstack/v2/contrib/keypairs.py | 33 | ||||
| -rw-r--r-- | nova/api/openstack/v2/contrib/quotas.py | 55 | ||||
| -rw-r--r-- | nova/api/openstack/v2/extensions.py | 20 | ||||
| -rw-r--r-- | nova/api/openstack/xmlutil.py | 58 |
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 |
