adpating wsgi files

This commit is contained in:
salvatore 2011-05-25 13:11:12 +01:00
parent 3a421e759f
commit 64ed5da205
4 changed files with 165 additions and 41 deletions

View File

@ -56,12 +56,12 @@ class Controller(wsgi.Controller):
}, },
} }
def index(self, request): def index(self, req):
""" Returns a list of network names and ids """ """ Returns a list of network names and ids """
#TODO: this should be for a given tenant!!! #TODO: this should be for a given tenant!!!
print "PIPPO" print "PIPPO"
LOG.debug("HERE - index") LOG.debug("HERE - index")
return self._items(request, is_detail=False) return self._items(req, is_detail=False)
def _items(self, req, is_detail): def _items(self, req, is_detail):
""" Returns a list of networks. """ """ Returns a list of networks. """

View File

@ -53,6 +53,10 @@ class NotFound(Error):
pass pass
class ClassNotFound(NotFound):
message = _("Class %(class_name)s could not be found")
class Duplicate(Error): class Duplicate(Error):
pass pass

View File

@ -25,6 +25,8 @@ import logging
import sys import sys
import datetime import datetime
from xml.dom import minidom
import eventlet import eventlet
import eventlet.wsgi import eventlet.wsgi
eventlet.patcher.monkey_patch(all=False, socket=True) eventlet.patcher.monkey_patch(all=False, socket=True)
@ -33,6 +35,7 @@ import routes.middleware
import webob.dec import webob.dec
import webob.exc import webob.exc
from quantum import utils
from quantum.common import exceptions as exception from quantum.common import exceptions as exception
LOG = logging.getLogger('quantum.common.wsgi') LOG = logging.getLogger('quantum.common.wsgi')
@ -313,66 +316,95 @@ class Router(object):
class Controller(object): class Controller(object):
""" """WSGI app that dispatched to methods.
WSGI app that reads routing information supplied by RoutesMiddleware WSGI app that reads routing information supplied by RoutesMiddleware
and calls the requested action method upon itself. All action methods and calls the requested action method upon itself. All action methods
must, in addition to their normal parameters, accept a 'req' argument must, in addition to their normal parameters, accept a 'req' argument
which is the incoming webob.Request. They raise a webob.exc exception, which is the incoming wsgi.Request. They raise a webob.exc exception,
or return a dict which will be serialized by requested content type. or return a dict which will be serialized by requested content type.
""" """
@webob.dec.wsgify @webob.dec.wsgify(RequestClass=Request)
def __call__(self, req): def __call__(self, req):
""" """Call the method specified in req.environ by RoutesMiddleware."""
Call the method specified in req.environ by RoutesMiddleware.
"""
arg_dict = req.environ['wsgiorg.routing_args'][1] arg_dict = req.environ['wsgiorg.routing_args'][1]
action = arg_dict['action'] action = arg_dict['action']
method = getattr(self, action) method = getattr(self, action)
LOG.debug("%s %s" % (req.method, req.url))
del arg_dict['controller'] del arg_dict['controller']
del arg_dict['action'] del arg_dict['action']
arg_dict['request'] = req if 'format' in arg_dict:
del arg_dict['format']
arg_dict['req'] = req
result = method(**arg_dict) result = method(**arg_dict)
if type(result) is dict: if type(result) is dict:
return self._serialize(result, req) content_type = req.best_match_content_type()
default_xmlns = self.get_default_xmlns(req)
body = self._serialize(result, content_type, default_xmlns)
response = webob.Response()
response.headers['Content-Type'] = content_type
response.body = body
msg_dict = dict(url=req.url, status=response.status_int)
msg = _("%(url)s returned with HTTP %(status)d") % msg_dict
LOG.debug(msg)
return response
else: else:
return result return result
def _serialize(self, data, request): def _serialize(self, data, content_type, default_xmlns):
""" """Serialize the given dict to the provided content_type.
Serialize the given dict to the response type requested in request.
Uses self._serialization_metadata if it exists, which is a dict mapping Uses self._serialization_metadata if it exists, which is a dict mapping
MIME types to information needed to serialize to that type. MIME types to information needed to serialize to that type.
""" """
_metadata = getattr(type(self), "_serialization_metadata", {}) _metadata = getattr(type(self), '_serialization_metadata', {})
serializer = Serializer(_metadata, default_xmlns)
try:
return serializer.serialize(data, content_type)
except exception.InvalidContentType:
raise webob.exc.HTTPNotAcceptable()
def _deserialize(self, data, content_type):
"""Deserialize the request body to the specefied content type.
Uses self._serialization_metadata if it exists, which is a dict mapping
MIME types to information needed to serialize to that type.
"""
_metadata = getattr(type(self), '_serialization_metadata', {})
serializer = Serializer(_metadata) serializer = Serializer(_metadata)
return serializer.to_content_type(data) return serializer.deserialize(data, content_type)
def get_default_xmlns(self, req):
"""Provide the XML namespace to use if none is otherwise specified."""
return None
class Serializer(object): class Serializer(object):
""" """Serializes and deserializes dictionaries to certain MIME types."""
Serializes a dictionary to a Content Type specified by a WSGI environment.
""" def __init__(self, metadata=None, default_xmlns=None):
"""Create a serializer based on the given WSGI environment.
def __init__(self,metadata=None):
"""
Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information 'metadata' is an optional dict mapping MIME types to information
needed to serialize a dictionary to that type. needed to serialize a dictionary to that type.
""" """
self.metadata = metadata or {} self.metadata = metadata or {}
self._methods = { self.default_xmlns = default_xmlns
'application/json': self._to_json,
'application/xml': self._to_xml}
def _get_serialize_handler(self, content_type): def _get_serialize_handler(self, content_type):
handlers = { handlers = {
'application/json': self._to_json, 'application/json': self._to_json,
'application/xml': self._to_xml, 'application/xml': self._to_xml,
} }
try: try:
return handlers[content_type] return handlers[content_type]
except Exception: except Exception:
@ -382,6 +414,14 @@ class Serializer(object):
"""Serialize a dictionary into the specified content type.""" """Serialize a dictionary into the specified content type."""
return self._get_serialize_handler(content_type)(data) return self._get_serialize_handler(content_type)(data)
def deserialize(self, datastring, content_type):
"""Deserialize a string to a dictionary.
The string must be in the format of a supported MIME type.
"""
return self.get_deserialize_handler(content_type)(datastring)
def get_deserialize_handler(self, content_type): def get_deserialize_handler(self, content_type):
handlers = { handlers = {
'application/json': self._from_json, 'application/json': self._from_json,
@ -393,35 +433,71 @@ class Serializer(object):
except Exception: except Exception:
raise exception.InvalidContentType(content_type=content_type) raise exception.InvalidContentType(content_type=content_type)
def deserialize(self, datastring, content_type): def _from_json(self, datastring):
"""Deserialize a string to a dictionary. return utils.loads(datastring)
The string must be in the format of a supported MIME type. def _from_xml(self, datastring):
xmldata = self.metadata.get('application/xml', {})
plurals = set(xmldata.get('plurals', {}))
node = minidom.parseString(datastring).childNodes[0]
return {node.nodeName: self._from_xml_node(node, plurals)}
def _from_xml_node(self, node, listnames):
"""Convert a minidom node to a simple Python type.
listnames is a collection of names of XML nodes whose subnodes should
be considered list items.
""" """
return self.get_deserialize_handler(content_type)(datastring) if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3:
return node.childNodes[0].nodeValue
elif node.nodeName in listnames:
return [self._from_xml_node(n, listnames) for n in node.childNodes]
else:
result = dict()
for attr in node.attributes.keys():
result[attr] = node.attributes[attr].nodeValue
for child in node.childNodes:
if child.nodeType != node.TEXT_NODE:
result[child.nodeName] = self._from_xml_node(child,
listnames)
return result
def _to_json(self, data): def _to_json(self, data):
def sanitizer(obj): return utils.dumps(data)
if isinstance(obj, datetime.datetime):
return obj.isoformat()
return obj
return json.dumps(data, default=sanitizer)
def _to_xml(self, data): def _to_xml(self, data):
metadata = self.metadata.get('application/xml', {}) metadata = self.metadata.get('application/xml', {})
# We expect data to contain a single key which is the XML root. # We expect data to contain a single key which is the XML root.
root_key = data.keys()[0] root_key = data.keys()[0]
from xml.dom import minidom
doc = minidom.Document() doc = minidom.Document()
node = self._to_xml_node(doc, metadata, root_key, data[root_key]) node = self._to_xml_node(doc, metadata, root_key, data[root_key])
xmlns = node.getAttribute('xmlns')
if not xmlns and self.default_xmlns:
node.setAttribute('xmlns', self.default_xmlns)
return node.toprettyxml(indent=' ') return node.toprettyxml(indent=' ')
def _to_xml_node(self, doc, metadata, nodename, data): def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes.""" """Recursive method to convert data members to XML nodes."""
result = doc.createElement(nodename) result = doc.createElement(nodename)
# Set the xml namespace if one is specified
# TODO(justinsb): We could also use prefixes on the keys
xmlns = metadata.get('xmlns', None)
if xmlns:
result.setAttribute('xmlns', xmlns)
if type(data) is list: if type(data) is list:
collections = metadata.get('list_collections', {})
if nodename in collections:
metadata = collections[nodename]
for item in data:
node = doc.createElement(metadata['item_name'])
node.setAttribute(metadata['item_key'], str(item))
result.appendChild(node)
return result
singular = metadata.get('plurals', {}).get(nodename, None) singular = metadata.get('plurals', {}).get(nodename, None)
if singular is None: if singular is None:
if nodename.endswith('s'): if nodename.endswith('s'):
@ -432,15 +508,26 @@ class Serializer(object):
node = self._to_xml_node(doc, metadata, singular, item) node = self._to_xml_node(doc, metadata, singular, item)
result.appendChild(node) result.appendChild(node)
elif type(data) is dict: elif type(data) is dict:
collections = metadata.get('dict_collections', {})
if nodename in collections:
metadata = collections[nodename]
for k, v in data.items():
node = doc.createElement(metadata['item_name'])
node.setAttribute(metadata['item_key'], str(k))
text = doc.createTextNode(str(v))
node.appendChild(text)
result.appendChild(node)
return result
attrs = metadata.get('attributes', {}).get(nodename, {}) attrs = metadata.get('attributes', {}).get(nodename, {})
for k, v in data.items(): for k, v in data.items():
LOG.debug("K:%s - V:%s",k,v)
if k in attrs: if k in attrs:
result.setAttribute(k, str(v)) result.setAttribute(k, str(v))
else: else:
node = self._to_xml_node(doc, metadata, k, v) node = self._to_xml_node(doc, metadata, k, v)
result.appendChild(node) result.appendChild(node)
else: # atom else:
# Type is atom
node = doc.createTextNode(str(data)) node = doc.createTextNode(str(data))
result.appendChild(node) result.appendChild(node)
return result return result

View File

@ -35,6 +35,8 @@ import sys
import time import time
import types import types
from quantum.common import exceptions as exception
def import_class(import_str): def import_class(import_str):
"""Returns a class from a string including module and class.""" """Returns a class from a string including module and class."""
@ -55,3 +57,34 @@ def import_object(import_str):
except ImportError: except ImportError:
cls = import_class(import_str) cls = import_class(import_str)
return cls() return cls()
def to_primitive(value):
if type(value) is type([]) or type(value) is type((None,)):
o = []
for v in value:
o.append(to_primitive(v))
return o
elif type(value) is type({}):
o = {}
for k, v in value.iteritems():
o[k] = to_primitive(v)
return o
elif isinstance(value, datetime.datetime):
return str(value)
elif hasattr(value, 'iteritems'):
return to_primitive(dict(value.iteritems()))
elif hasattr(value, '__iter__'):
return to_primitive(list(value))
else:
return value
def dumps(value):
try:
return json.dumps(value)
except TypeError:
pass
return json.dumps(to_primitive(value))
def loads(s):
return json.loads(s)