# If ../quantum/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
++import gettext
import optparse
import os
import re
if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')):
sys.path.insert(0, possible_topdir)
++gettext.install('quantum', unicode=1)
from quantum.common import wsgi
from quantum.common import config
(options, args) = config.parse_options(oparser)
try:
-- conf, app = config.load_paste_app('quantum', options, args)
-
- server = wsgi.Server()
- server.start(app, int(conf['bind_port']), conf['bind_host'])
- server.wait()
++ conf, app = config.load_paste_app('quantumversionapp', options, args)
+ server = wsgi.Server()
+ server.start(app, int(conf['bind_port']), conf['bind_host'])
+ server.wait()
except RuntimeError, e:
- sys.exit("ERROR: %s" % e)
+ sys.exit("ERROR: %s" % e)
--- /dev/null
--- /dev/null
++[DEFAULT]
++# Show more verbose log output (sets INFO log level output)
++verbose = True
++
++# Show debugging output in logs (sets DEBUG log level output)
++debug = True
++
++# Address to bind the API server
++bind_host = 0.0.0.0
++
++# Port the bind the API server to
++bind_port = 9696
++
++#[app:quantum]
++#paste.app_factory = quantum.service:app_factory
++
++[app:quantumversionapp]
++paste.app_factory = quantum.api.versions:Versions.factory
++
--- /dev/null
--- /dev/null
++# 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.
++# @author: Somik Behera, Nicira Networks, Inc.
--- /dev/null
--- /dev/null
++# 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 logging
++import webob.dec
++
++from quantum.common import wsgi
++from quantum.api.views import versions as versions_view
++
++LOG = logging.getLogger('quantum.api.versions')
++
++class Versions(wsgi.Application):
++
++ @webob.dec.wsgify(RequestClass=wsgi.Request)
++ def __call__(self, req):
++ """Respond to a request for all Quantum API versions."""
++ version_objs = [
++ {
++ "id": "v0.1",
++ "status": "CURRENT",
++ },
++ {
++ "id": "v1.0",
++ "status": "FUTURE",
++ },
++ ]
++
++ builder = versions_view.get_view_builder(req)
++ versions = [builder.build(version) for version in version_objs]
++ response = dict(versions=versions)
++ LOG.debug("response:%s",response)
++ metadata = {
++ "application/xml": {
++ "attributes": {
++ "version": ["status", "id"],
++ "link": ["rel", "href"],
++ }
++ }
++ }
++
++ content_type = req.best_match_content_type()
++ body = wsgi.Serializer(metadata=metadata).serialize(response, content_type)
++
++ response = webob.Response()
++ response.content_type = content_type
++ response.body = body
++
++ return response
--- /dev/null
--- /dev/null
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++# Copyright 2011 Citrix Systems, Inc.
++# 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.
++# @author: Somik Behera, Nicira Networks, Inc.
--- /dev/null
--- /dev/null
++# vim: tabstop=4 shiftwidth=4 softtabstop=4
++
++# Copyright 2010-2011 OpenStack LLC.
++# 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 os
++
++
++def get_view_builder(req):
++ base_url = req.application_url
++ return ViewBuilder(base_url)
++
++
++class ViewBuilder(object):
++
++ def __init__(self, base_url):
++ """
++ :param base_url: url of the root wsgi application
++ """
++ self.base_url = base_url
++
++ def build(self, version_data):
++ """Generic method used to generate a version entity."""
++ version = {
++ "id": version_data["id"],
++ "status": version_data["status"],
++ "links": self._build_links(version_data),
++ }
++
++ return version
++
++ def _build_links(self, version_data):
++ """Generate a container of links that refer to the provided version."""
++ href = self.generate_href(version_data["id"])
++
++ links = [
++ {
++ "rel": "self",
++ "href": href,
++ },
++ ]
++
++ return links
++
++ def generate_href(self, version_number):
++ """Create an url that refers to a specific version_number."""
++ return os.path.join(self.base_url, version_number)
from paste import deploy
- import quantum.common.exception as exception
-import quantum.common.exceptions as exception
++from quantum.common import flags
++from quantum.common import exceptions as exception
DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s"
DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
++FLAGS = flags.FLAGS
++LOG = logging.getLogger('quantum.common.wsgi')
++
def parse_options(parser, cli_args=None):
"""
* .
* ~.quantum/
* ~
-- * /etc/quantum
-- * /etc
++ * $FLAGS.state_path/etc/quantum
++ * $FLAGS.state_path/etc
:retval Full path to config file, or None if no config file found
"""
config_file_dirs = [fix_path(os.getcwd()),
fix_path(os.path.join('~', '.quantum')),
fix_path('~'),
++ os.path.join(FLAGS.state_path, 'etc'),
++ os.path.join(FLAGS.state_path, 'etc','quantum'),
'/etc/quantum/',
'/etc']
--
for cfg_dir in config_file_dirs:
cfg_file = os.path.join(cfg_dir, 'quantum.conf')
if os.path.exists(cfg_file):
try:
# Setup logging early, supplying both the CLI options and the
# 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
conf['verbose'] = verbose
# Log the options used when starting if we're in debug mode...
-- if debug:
-- logger = logging.getLogger(app_name)
-- logger.debug("*" * 80)
-- logger.debug("Configuration options gathered from config file:")
-- logger.debug(conf_file)
-- logger.debug("================================================")
-- items = dict([(k, v) for k, v in conf.items()
-- if k not in ('__file__', 'here')])
-- for key, value in sorted(items.items()):
-- logger.debug("%(key)-30s %(value)s" % locals())
-- logger.debug("*" * 80)
++ 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)
except (LookupError, ImportError), e:
raise RuntimeError("Unable to load %(app_name)s from "
pass
++class InvalidContentType(Invalid):
++ message = _("Invalid content type %(content_type)s.")
++
++
class BadInputError(Exception):
"""Error resulting from a client sending bad input to a server"""
pass
"""
import getopt
++import os
import string
import sys
# __GLOBAL FLAGS ONLY__
# Define any app-specific flags in their own files, docs at:
# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#a9
++
++DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../../'),
++ "Top-level directory for maintaining quantum's state")
++
import webob.dec
import webob.exc
++from quantum.common import exceptions as exception
++
++LOG = logging.getLogger('quantum.common.wsgi')
class WritableLogger(object):
"""A thin wrapper that responds to `write` and logs."""
return self.process_response(response)
++class Request(webob.Request):
++
++ def best_match_content_type(self):
++ """Determine the most acceptable content-type.
++
++ Based on the query extension then the Accept header.
++
++ """
++ parts = self.path.rsplit('.', 1)
++
++ if len(parts) > 1:
++ format = parts[1]
++ if format in ['json', 'xml']:
++ return 'application/{0}'.format(parts[1])
++
++ ctypes = ['application/json', 'application/xml']
++ bm = self.accept.best_match(ctypes)
++
++ return bm or 'application/json'
++
++ def get_content_type(self):
++ allowed_types = ("application/xml", "application/json")
++ if not "Content-Type" in self.headers:
++ msg = _("Missing Content-Type")
++ LOG.debug(msg)
++ raise webob.exc.HTTPBadRequest(msg)
++ type = self.content_type
++ if type in allowed_types:
++ return type
++ LOG.debug(_("Wrong Content-Type: %s") % type)
++ raise webob.exc.HTTPBadRequest("Invalid content type")
++
++
++class Application(object):
++ """Base WSGI application wrapper. Subclasses need to implement __call__."""
++
++ @classmethod
++ def factory(cls, global_config, **local_config):
++ """Used for paste app factories in paste.deploy config files.
++
++ Any local configuration (that is, values under the [app:APPNAME]
++ section of the paste config) will be passed into the `__init__` method
++ as kwargs.
++
++ A hypothetical configuration would look like:
++
++ [app:wadl]
++ latest_version = 1.3
++ paste.app_factory = nova.api.fancy_api:Wadl.factory
++
++ which would result in a call to the `Wadl` class as
++
++ import quantum.api.fancy_api
++ fancy_api.Wadl(latest_version='1.3')
++
++ You could of course re-implement the `factory` method in subclasses,
++ but using the kwarg passing it shouldn't be necessary.
++
++ """
++ return cls(**local_config)
++
++ def __call__(self, environ, start_response):
++ r"""Subclasses will probably want to implement __call__ like this:
++
++ @webob.dec.wsgify(RequestClass=Request)
++ def __call__(self, req):
++ # Any of the following objects work as responses:
++
++ # Option 1: simple string
++ res = 'message\n'
++
++ # Option 2: a nicely formatted HTTP exception page
++ res = exc.HTTPForbidden(detail='Nice try')
++
++ # Option 3: a webob Response object (in case you need to play with
++ # headers, or you want to be treated like an iterable, or or or)
++ res = Response();
++ res.app_iter = open('somefile')
++
++ # Option 4: any wsgi app to be run next
++ res = self.application
++
++ # Option 5: you can get a Response object for a wsgi app, too, to
++ # play with headers etc
++ res = req.get_response(self.application)
++
++ # You can then just return your response...
++ return res
++ # ... or set req.response and return None.
++ req.response = res
++
++ See the end of http://pythonpaste.org/webob/modules/dec.html
++ for more info.
++
++ """
++ raise NotImplementedError(_('You must implement __call__'))
++
++
class Debug(Middleware):
"""
Helper class that can be inserted into any WSGI application chain
return serializer.to_content_type(data)
++
++
class Serializer(object):
"""
Serializes a dictionary to a Content Type specified by a WSGI environment.
"""
-- def __init__(self, environ, metadata=None):
++ def __init__(self,metadata=None):
"""
Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information
needed to serialize a dictionary to that type.
"""
-- self.environ = environ
self.metadata = metadata or {}
self._methods = {
'application/json': self._to_json,
'application/xml': self._to_xml}
-- def to_content_type(self, data):
-- """
-- Serialize a dictionary into a string. The format of the string
-- will be decided based on the Content Type requested in self.environ:
-- by Accept: header, or by URL suffix.
-- """
-- # FIXME(sirp): for now, supporting json only
-- #mimetype = 'application/xml'
-- mimetype = 'application/json'
-- # TODO(gundlach): determine mimetype from request
-- return self._methods.get(mimetype, repr)(data)
++ def _get_serialize_handler(self, content_type):
++ handlers = {
++ 'application/json': self._to_json,
++ 'application/xml': self._to_xml,
++ }
++ try:
++ return handlers[content_type]
++ except Exception:
++ raise exception.InvalidContentType(content_type=content_type)
++
++ def serialize(self, data, content_type):
++ """Serialize a dictionary into the specified content type."""
++ return self._get_serialize_handler(content_type)(data)
++
++ def get_deserialize_handler(self, content_type):
++ handlers = {
++ 'application/json': self._from_json,
++ 'application/xml': self._from_xml,
++ }
++
++ try:
++ return handlers[content_type]
++ except Exception:
++ raise exception.InvalidContentType(content_type=content_type)
++
++ 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 _to_json(self, data):
def sanitizer(obj):
if isinstance(obj, datetime.datetime):
elif type(data) is dict:
attrs = metadata.get('attributes', {}).get(nodename, {})
for k, v in data.items():
++ LOG.debug("K:%s - V:%s",k,v)
if k in attrs:
result.setAttribute(k, str(v))
else: