Work in progress on network API
This commit is contained in:
parent
702e64fc52
commit
3a421e759f
17
bin/quantum
17
bin/quantum
@ -35,6 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')):
|
|||||||
|
|
||||||
gettext.install('quantum', unicode=1)
|
gettext.install('quantum', unicode=1)
|
||||||
|
|
||||||
|
from quantum import service
|
||||||
from quantum.common import wsgi
|
from quantum.common import wsgi
|
||||||
from quantum.common import config
|
from quantum.common import config
|
||||||
|
|
||||||
@ -54,10 +55,18 @@ if __name__ == '__main__':
|
|||||||
(options, args) = config.parse_options(oparser)
|
(options, args) = config.parse_options(oparser)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conf, app = config.load_paste_app('quantumversionapp', options, args)
|
print "HERE-1"
|
||||||
server = wsgi.Server()
|
service = service.serve_wsgi(service.QuantumApiService,
|
||||||
server.start(app, int(conf['bind_port']), conf['bind_host'])
|
options=options,
|
||||||
server.wait()
|
args=args)
|
||||||
|
#version_conf, version_app = config.load_paste_app('quantumversion', options, args)
|
||||||
|
print "HERE-2"
|
||||||
|
service.wait()
|
||||||
|
#api_conf, api_app = config.load_paste_app('quantum', options, args)
|
||||||
|
#server = wsgi.Server()
|
||||||
|
#server.start(version_app, int(version_conf['bind_port']), version_conf['bind_host'])
|
||||||
|
#server.start(api_app, int(api_conf['bind_port']), api_conf['bind_host'])
|
||||||
|
#server.wait()
|
||||||
except RuntimeError, e:
|
except RuntimeError, e:
|
||||||
sys.exit("ERROR: %s" % e)
|
sys.exit("ERROR: %s" % e)
|
||||||
|
|
||||||
|
@ -11,9 +11,15 @@ bind_host = 0.0.0.0
|
|||||||
# Port the bind the API server to
|
# Port the bind the API server to
|
||||||
bind_port = 9696
|
bind_port = 9696
|
||||||
|
|
||||||
#[app:quantum]
|
[composite:quantum]
|
||||||
#paste.app_factory = quantum.service:app_factory
|
use = egg:Paste#urlmap
|
||||||
|
/: quantumversions
|
||||||
|
/v0.1: quantumapi
|
||||||
|
|
||||||
[app:quantumversionapp]
|
[app:quantumversions]
|
||||||
paste.app_factory = quantum.api.versions:Versions.factory
|
paste.app_factory = quantum.api.versions:Versions.factory
|
||||||
|
|
||||||
|
[app:quantumapi]
|
||||||
|
paste.app_factory = quantum.api:APIRouterV01.factory
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,4 +13,69 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
# @author: Somik Behera, Nicira Networks, Inc.
|
# @author: Salvatore Orlando, Citrix Systems
|
||||||
|
|
||||||
|
"""
|
||||||
|
Quantum API controllers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import routes
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from quantum.api import faults
|
||||||
|
from quantum.api import networks
|
||||||
|
from quantum.common import flags
|
||||||
|
from quantum.common import wsgi
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger('quantum.api')
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
class FaultWrapper(wsgi.Middleware):
|
||||||
|
"""Calls down the middleware stack, making exceptions into faults."""
|
||||||
|
|
||||||
|
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||||
|
def __call__(self, req):
|
||||||
|
try:
|
||||||
|
return req.get_response(self.application)
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.exception(_("Caught error: %s"), unicode(ex))
|
||||||
|
exc = webob.exc.HTTPInternalServerError(explanation=unicode(ex))
|
||||||
|
return faults.Fault(exc)
|
||||||
|
|
||||||
|
|
||||||
|
class APIRouterV01(wsgi.Router):
|
||||||
|
"""
|
||||||
|
Routes requests on the Quantum API to the appropriate controller
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ext_mgr=None):
|
||||||
|
mapper = routes.Mapper()
|
||||||
|
self._setup_routes(mapper)
|
||||||
|
super(APIRouterV01, self).__init__(mapper)
|
||||||
|
|
||||||
|
def _setup_routes(self, mapper):
|
||||||
|
#server_members = self.server_members
|
||||||
|
#server_members['action'] = 'POST'
|
||||||
|
|
||||||
|
#server_members['pause'] = 'POST'
|
||||||
|
#server_members['unpause'] = 'POST'
|
||||||
|
#server_members['diagnostics'] = 'GET'
|
||||||
|
#server_members['actions'] = 'GET'
|
||||||
|
#server_members['suspend'] = 'POST'
|
||||||
|
#server_members['resume'] = 'POST'
|
||||||
|
#server_members['rescue'] = 'POST'
|
||||||
|
#server_members['unrescue'] = 'POST'
|
||||||
|
#server_members['reset_network'] = 'POST'
|
||||||
|
#server_members['inject_network_info'] = 'POST'
|
||||||
|
|
||||||
|
mapper.resource("network", "networks", controller=networks.Controller(),
|
||||||
|
collection={'detail': 'GET'})
|
||||||
|
print mapper
|
||||||
|
#mapper.resource("port", "ports", controller=ports.Controller(),
|
||||||
|
# collection=dict(public='GET', private='GET'),
|
||||||
|
# parent_resource=dict(member_name='network',
|
||||||
|
# collection_name='networks'))
|
||||||
|
|
||||||
|
21
quantum/api/api_common.py
Normal file
21
quantum/api/api_common.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Citrix System.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
XML_NS_V01 = 'http://netstack.org/quantum/api/v0.1'
|
||||||
|
XML_NS_V10 = 'http://netstack.org/quantum/api/v1.0'
|
||||||
|
|
62
quantum/api/faults.py
Normal file
62
quantum/api/faults.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Citrix Systems.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import webob.dec
|
||||||
|
import webob.exc
|
||||||
|
|
||||||
|
from quantum.api import api_common as common
|
||||||
|
from quantum.common import wsgi
|
||||||
|
|
||||||
|
class Fault(webob.exc.HTTPException):
|
||||||
|
"""Error codes for API faults"""
|
||||||
|
|
||||||
|
_fault_names = {
|
||||||
|
400: "malformedRequest",
|
||||||
|
401: "unauthorized",
|
||||||
|
402: "networkNotFound",
|
||||||
|
403: "requestedStateInvalid",
|
||||||
|
460: "networkInUse",
|
||||||
|
461: "alreadyAttached",
|
||||||
|
462: "portInUse",
|
||||||
|
470: "serviceUnavailable",
|
||||||
|
471: "pluginFault"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, exception):
|
||||||
|
"""Create a Fault for the given webob.exc.exception."""
|
||||||
|
self.wrapped_exc = exception
|
||||||
|
|
||||||
|
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||||
|
def __call__(self, req):
|
||||||
|
"""Generate a WSGI response based on the exception passed to ctor."""
|
||||||
|
# Replace the body with fault details.
|
||||||
|
code = self.wrapped_exc.status_int
|
||||||
|
fault_name = self._fault_names.get(code, "quantumServiceFault")
|
||||||
|
fault_data = {
|
||||||
|
fault_name: {
|
||||||
|
'code': code,
|
||||||
|
'message': self.wrapped_exc.explanation}}
|
||||||
|
#TODO (salvatore-orlando): place over-limit stuff here
|
||||||
|
# 'code' is an attribute on the fault tag itself
|
||||||
|
metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
|
||||||
|
default_xmlns = common.XML_NS_V10
|
||||||
|
serializer = wsgi.Serializer(metadata, default_xmlns)
|
||||||
|
content_type = req.best_match_content_type()
|
||||||
|
self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
|
||||||
|
self.wrapped_exc.content_type = content_type
|
||||||
|
return self.wrapped_exc
|
200
quantum/api/networks.py
Normal file
200
quantum/api/networks.py
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
# Copyright 2011 Citrix Systems.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
from quantum import manager
|
||||||
|
from quantum import quantum_plugin_base
|
||||||
|
from quantum.common import exceptions as exception
|
||||||
|
from quantum.common import flags
|
||||||
|
from quantum.common import wsgi
|
||||||
|
from quantum import utils
|
||||||
|
from quantum.api import api_common as common
|
||||||
|
from quantum.api import faults
|
||||||
|
import quantum.api
|
||||||
|
|
||||||
|
LOG = logging.getLogger('quantum.api.networks')
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(wsgi.Controller):
|
||||||
|
""" Network API controller for Quantum API """
|
||||||
|
|
||||||
|
#TODO (salvatore-orlando): adjust metadata for quantum
|
||||||
|
_serialization_metadata = {
|
||||||
|
"application/xml": {
|
||||||
|
"attributes": {
|
||||||
|
"server": ["id", "imageId", "name", "flavorId", "hostId",
|
||||||
|
"status", "progress", "adminPass", "flavorRef",
|
||||||
|
"imageRef"],
|
||||||
|
"link": ["rel", "type", "href"],
|
||||||
|
},
|
||||||
|
"dict_collections": {
|
||||||
|
"metadata": {"item_name": "meta", "item_key": "key"},
|
||||||
|
},
|
||||||
|
"list_collections": {
|
||||||
|
"public": {"item_name": "ip", "item_key": "addr"},
|
||||||
|
"private": {"item_name": "ip", "item_key": "addr"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def index(self, request):
|
||||||
|
""" Returns a list of network names and ids """
|
||||||
|
#TODO: this should be for a given tenant!!!
|
||||||
|
print "PIPPO"
|
||||||
|
LOG.debug("HERE - index")
|
||||||
|
return self._items(request, is_detail=False)
|
||||||
|
|
||||||
|
def _items(self, req, is_detail):
|
||||||
|
""" Returns a list of networks. """
|
||||||
|
#TODO: we should return networks for a given tenant only
|
||||||
|
#TODO: network controller should be retrieved here!!!
|
||||||
|
test = { 'ciao':'bello','porco':'mondo' }
|
||||||
|
#builder = self._get_view_builder(req)
|
||||||
|
#servers = [builder.build(inst, is_detail)['server']
|
||||||
|
# for inst in limited_list]
|
||||||
|
#return dict(servers=servers)
|
||||||
|
return test
|
||||||
|
|
||||||
|
def show(self, req, id):
|
||||||
|
""" Returns network details by network id """
|
||||||
|
try:
|
||||||
|
return "TEST NETWORK DETAILS"
|
||||||
|
except exception.NotFound:
|
||||||
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
|
def delete(self, req, id):
|
||||||
|
""" Destroys the network with the given id """
|
||||||
|
try:
|
||||||
|
return "TEST NETWORK DELETE"
|
||||||
|
except exception.NotFound:
|
||||||
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
return exc.HTTPAccepted()
|
||||||
|
|
||||||
|
def create(self, req):
|
||||||
|
""" Creates a new network for a given tenant """
|
||||||
|
#env = self._deserialize_create(req)
|
||||||
|
#if not env:
|
||||||
|
# return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||||
|
return "TEST NETWORK CREATE"
|
||||||
|
|
||||||
|
def _deserialize_create(self, request):
|
||||||
|
"""
|
||||||
|
Deserialize a create request
|
||||||
|
Overrides normal behavior in the case of xml content
|
||||||
|
"""
|
||||||
|
#if request.content_type == "application/xml":
|
||||||
|
# deserializer = ServerCreateRequestXMLDeserializer()
|
||||||
|
# return deserializer.deserialize(request.body)
|
||||||
|
#else:
|
||||||
|
# return self._deserialize(request.body, request.get_content_type())
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, req, id):
|
||||||
|
""" Updates the name for the network wit the given id """
|
||||||
|
if len(req.body) == 0:
|
||||||
|
raise exc.HTTPUnprocessableEntity()
|
||||||
|
|
||||||
|
inst_dict = self._deserialize(req.body, req.get_content_type())
|
||||||
|
if not inst_dict:
|
||||||
|
return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||||
|
|
||||||
|
try:
|
||||||
|
return "TEST NETWORK UPDATE"
|
||||||
|
except exception.NotFound:
|
||||||
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
return exc.HTTPNoContent()
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkCreateRequestXMLDeserializer(object):
|
||||||
|
"""
|
||||||
|
Deserializer to handle xml-formatted server create requests.
|
||||||
|
|
||||||
|
Handles standard server attributes as well as optional metadata
|
||||||
|
and personality attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
def deserialize(self, string):
|
||||||
|
"""Deserialize an xml-formatted server create request"""
|
||||||
|
dom = minidom.parseString(string)
|
||||||
|
server = self._extract_server(dom)
|
||||||
|
return {'server': server}
|
||||||
|
|
||||||
|
def _extract_server(self, node):
|
||||||
|
"""Marshal the server attribute of a parsed request"""
|
||||||
|
server = {}
|
||||||
|
server_node = self._find_first_child_named(node, 'server')
|
||||||
|
for attr in ["name", "imageId", "flavorId"]:
|
||||||
|
server[attr] = server_node.getAttribute(attr)
|
||||||
|
metadata = self._extract_metadata(server_node)
|
||||||
|
if metadata is not None:
|
||||||
|
server["metadata"] = metadata
|
||||||
|
personality = self._extract_personality(server_node)
|
||||||
|
if personality is not None:
|
||||||
|
server["personality"] = personality
|
||||||
|
return server
|
||||||
|
|
||||||
|
def _extract_metadata(self, server_node):
|
||||||
|
"""Marshal the metadata attribute of a parsed request"""
|
||||||
|
metadata_node = self._find_first_child_named(server_node, "metadata")
|
||||||
|
if metadata_node is None:
|
||||||
|
return None
|
||||||
|
metadata = {}
|
||||||
|
for meta_node in self._find_children_named(metadata_node, "meta"):
|
||||||
|
key = meta_node.getAttribute("key")
|
||||||
|
metadata[key] = self._extract_text(meta_node)
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
def _extract_personality(self, server_node):
|
||||||
|
"""Marshal the personality attribute of a parsed request"""
|
||||||
|
personality_node = \
|
||||||
|
self._find_first_child_named(server_node, "personality")
|
||||||
|
if personality_node is None:
|
||||||
|
return None
|
||||||
|
personality = []
|
||||||
|
for file_node in self._find_children_named(personality_node, "file"):
|
||||||
|
item = {}
|
||||||
|
if file_node.hasAttribute("path"):
|
||||||
|
item["path"] = file_node.getAttribute("path")
|
||||||
|
item["contents"] = self._extract_text(file_node)
|
||||||
|
personality.append(item)
|
||||||
|
return personality
|
||||||
|
|
||||||
|
def _find_first_child_named(self, parent, name):
|
||||||
|
"""Search a nodes children for the first child with a given name"""
|
||||||
|
for node in parent.childNodes:
|
||||||
|
if node.nodeName == name:
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _find_children_named(self, parent, name):
|
||||||
|
"""Return all of a nodes children who have the given name"""
|
||||||
|
for node in parent.childNodes:
|
||||||
|
if node.nodeName == name:
|
||||||
|
yield node
|
||||||
|
|
||||||
|
def _extract_text(self, node):
|
||||||
|
"""Get the text field contained by the given node"""
|
||||||
|
if len(node.childNodes) == 1:
|
||||||
|
child = node.childNodes[0]
|
||||||
|
if child.nodeType == child.TEXT_NODE:
|
||||||
|
return child.nodeValue
|
||||||
|
return ""
|
@ -244,10 +244,12 @@ def load_paste_config(app_name, options, args):
|
|||||||
problem loading the configuration file.
|
problem loading the configuration file.
|
||||||
"""
|
"""
|
||||||
conf_file = find_config_file(options, args)
|
conf_file = find_config_file(options, args)
|
||||||
|
print "Conf_file:%s" %conf_file
|
||||||
if not conf_file:
|
if not conf_file:
|
||||||
raise RuntimeError("Unable to locate any configuration file. "
|
raise RuntimeError("Unable to locate any configuration file. "
|
||||||
"Cannot load application %s" % app_name)
|
"Cannot load application %s" % app_name)
|
||||||
try:
|
try:
|
||||||
|
print "App_name:%s" %app_name
|
||||||
conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
|
conf = deploy.appconfig("config:%s" % conf_file, name=app_name)
|
||||||
return conf_file, conf
|
return conf_file, conf
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
@ -255,7 +257,7 @@ def load_paste_config(app_name, options, args):
|
|||||||
% (conf_file, e))
|
% (conf_file, e))
|
||||||
|
|
||||||
|
|
||||||
def load_paste_app(app_name, options, args):
|
def load_paste_app(conf_file, app_name):
|
||||||
"""
|
"""
|
||||||
Builds and returns a WSGI app from a paste config file.
|
Builds and returns a WSGI app from a paste config file.
|
||||||
|
|
||||||
@ -276,40 +278,16 @@ def load_paste_app(app_name, options, args):
|
|||||||
:raises RuntimeError when config file cannot be located or application
|
:raises RuntimeError when config file cannot be located or application
|
||||||
cannot be loaded from config file
|
cannot be loaded from config file
|
||||||
"""
|
"""
|
||||||
conf_file, conf = load_paste_config(app_name, options, args)
|
#conf_file, conf = load_paste_config(app_name, options, args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Setup logging early, supplying both the CLI options and the
|
conf_file = os.path.abspath(conf_file)
|
||||||
# configuration mapping from the config file
|
|
||||||
print "OPTIONS:%s" %options
|
|
||||||
print "CONF:%s" %conf
|
|
||||||
setup_logging(options, conf)
|
|
||||||
|
|
||||||
# We only update the conf dict for the verbose and debug
|
|
||||||
# flags. Everything else must be set up in the conf file...
|
|
||||||
debug = options.get('debug') or \
|
|
||||||
get_option(conf, 'debug', type='bool', default=False)
|
|
||||||
verbose = options.get('verbose') or \
|
|
||||||
get_option(conf, 'verbose', type='bool', default=False)
|
|
||||||
conf['debug'] = debug
|
|
||||||
conf['verbose'] = verbose
|
|
||||||
|
|
||||||
# Log the options used when starting if we're in debug mode...
|
|
||||||
LOG.debug("*" * 80)
|
|
||||||
LOG.debug("Configuration options gathered from config file:")
|
|
||||||
LOG.debug(conf_file)
|
|
||||||
LOG.debug("================================================")
|
|
||||||
items = dict([(k, v) for k, v in conf.items()
|
|
||||||
if k not in ('__file__', 'here')])
|
|
||||||
for key, value in sorted(items.items()):
|
|
||||||
LOG.debug("%(key)-30s %(value)s" % locals())
|
|
||||||
LOG.debug("*" * 80)
|
|
||||||
app = deploy.loadapp("config:%s" % conf_file, name=app_name)
|
app = deploy.loadapp("config:%s" % conf_file, name=app_name)
|
||||||
except (LookupError, ImportError), e:
|
except (LookupError, ImportError), e:
|
||||||
raise RuntimeError("Unable to load %(app_name)s from "
|
raise RuntimeError("Unable to load %(app_name)s from "
|
||||||
"configuration file %(conf_file)s."
|
"configuration file %(conf_file)s."
|
||||||
"\nGot: %(e)r" % locals())
|
"\nGot: %(e)r" % locals())
|
||||||
return conf, app
|
return app
|
||||||
|
|
||||||
|
|
||||||
def get_option(options, option, **kwargs):
|
def get_option(options, option, **kwargs):
|
||||||
|
@ -29,7 +29,7 @@ import socket
|
|||||||
import sys
|
import sys
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
|
|
||||||
from common import exceptions
|
from quantum.common import exceptions
|
||||||
from exceptions import ProcessExecutionError
|
from exceptions import ProcessExecutionError
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
#
|
#
|
||||||
# Copyright 2011, Nicira Networks, Inc.
|
# Copyright 2011, Nicira Networks, Inc.
|
||||||
@ -253,6 +254,13 @@ class Router(object):
|
|||||||
WSGI middleware that maps incoming requests to WSGI apps.
|
WSGI middleware that maps incoming requests to WSGI apps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def factory(cls, global_config, **local_config):
|
||||||
|
"""
|
||||||
|
Returns an instance of the WSGI Router class
|
||||||
|
"""
|
||||||
|
return cls()
|
||||||
|
|
||||||
def __init__(self, mapper):
|
def __init__(self, mapper):
|
||||||
"""
|
"""
|
||||||
Create a router for the given routes.Mapper.
|
Create a router for the given routes.Mapper.
|
||||||
@ -337,7 +345,7 @@ class Controller(object):
|
|||||||
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(request.environ, _metadata)
|
serializer = Serializer(_metadata)
|
||||||
return serializer.to_content_type(data)
|
return serializer.to_content_type(data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,30 +15,104 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import logging
|
||||||
import json
|
import json
|
||||||
import routes
|
import routes
|
||||||
from common import wsgi
|
from quantum.common import config
|
||||||
|
from quantum.common import wsgi
|
||||||
|
from quantum.common import exceptions as exception
|
||||||
from webob import Response
|
from webob import Response
|
||||||
|
|
||||||
|
LOG = logging.getLogger('quantum.service')
|
||||||
|
|
||||||
class NetworkController(wsgi.Controller):
|
class WsgiService(object):
|
||||||
|
"""Base class for WSGI based services.
|
||||||
|
|
||||||
def version(self, request):
|
For each api you define, you must also define these flags:
|
||||||
return "Quantum version 0.1"
|
:<api>_listen: The address on which to listen
|
||||||
|
:<api>_listen_port: The port on which to listen
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, app_name, conf_file, conf):
|
||||||
|
self.app_name = app_name
|
||||||
|
self.conf_file = conf_file
|
||||||
|
self.conf = conf
|
||||||
|
self.wsgi_app = None
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.wsgi_app = _run_wsgi(self.app_name, self.conf, self.conf_file)
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
self.wsgi_app.wait()
|
||||||
|
|
||||||
|
|
||||||
class API(wsgi.Router):
|
class QuantumApiService(WsgiService):
|
||||||
def __init__(self, options):
|
"""Class for quantum-api service."""
|
||||||
self.options = options
|
|
||||||
mapper = routes.Mapper()
|
@classmethod
|
||||||
network_controller = NetworkController()
|
def create(cls, conf=None, options=None, args=None):
|
||||||
mapper.resource("net_controller", "/network",
|
app_name = "quantum"
|
||||||
controller=network_controller)
|
if not conf:
|
||||||
mapper.connect("/", controller=network_controller, action="version")
|
conf_file, conf = config.load_paste_config(
|
||||||
super(API, self).__init__(mapper)
|
app_name, options, args)
|
||||||
|
if not conf:
|
||||||
|
message = (_('No paste configuration found for: %s'),
|
||||||
|
app_name)
|
||||||
|
raise exception.Error(message)
|
||||||
|
print "OPTIONS:%s" %options
|
||||||
|
print "CONF:%s" %conf
|
||||||
|
|
||||||
|
# Setup logging early, supplying both the CLI options and the
|
||||||
|
# configuration mapping from the config file
|
||||||
|
# We only update the conf dict for the verbose and debug
|
||||||
|
# flags. Everything else must be set up in the conf file...
|
||||||
|
# Log the options used when starting if we're in debug mode...
|
||||||
|
|
||||||
|
config.setup_logging(options, conf)
|
||||||
|
debug = options.get('debug') or \
|
||||||
|
config.get_option(conf, 'debug',
|
||||||
|
type='bool', default=False)
|
||||||
|
verbose = options.get('verbose') or \
|
||||||
|
config.get_option(conf, 'verbose',
|
||||||
|
type='bool', default=False)
|
||||||
|
conf['debug'] = debug
|
||||||
|
conf['verbose'] = verbose
|
||||||
|
LOG.debug("*" * 80)
|
||||||
|
LOG.debug("Configuration options gathered from config file:")
|
||||||
|
LOG.debug(conf_file)
|
||||||
|
LOG.debug("================================================")
|
||||||
|
items = dict([(k, v) for k, v in conf.items()
|
||||||
|
if k not in ('__file__', 'here')])
|
||||||
|
for key, value in sorted(items.items()):
|
||||||
|
LOG.debug("%(key)-30s %(value)s" % locals())
|
||||||
|
LOG.debug("*" * 80)
|
||||||
|
service = cls(app_name, conf_file, conf)
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
def app_factory(global_conf, **local_conf):
|
def serve_wsgi(cls, conf=None, options = None, args = None):
|
||||||
conf = global_conf.copy()
|
try:
|
||||||
conf.update(local_conf)
|
service = cls.create(conf, options, args)
|
||||||
return API(conf)
|
except Exception:
|
||||||
|
logging.exception('in WsgiService.create()')
|
||||||
|
raise
|
||||||
|
|
||||||
|
service.start()
|
||||||
|
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
def _run_wsgi(app_name, paste_conf, paste_config_file):
|
||||||
|
print "CICCIO"
|
||||||
|
LOG.info(_('Using paste.deploy config at: %s'), paste_config_file)
|
||||||
|
app = config.load_paste_app(paste_config_file, app_name)
|
||||||
|
if not app:
|
||||||
|
LOG.error(_('No known API applications configured in %s.'),
|
||||||
|
paste_config_file)
|
||||||
|
return
|
||||||
|
server = wsgi.Server()
|
||||||
|
server.start(app,
|
||||||
|
int(paste_conf['bind_port']),paste_conf['bind_host'])
|
||||||
|
return server
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user