Implements blueprint separate-nova-volumeapi

* Moves openstack/v2 directory to compute and fixes tests accordingly
 * Moves some code from api/openstack/compute to shared location, for use by volume api
 * Implements basic volume functionality for types, volumes, and snapshots
 * Changes service name from osapi to osapi_compute (and adds osapi_volume)
 * Renames nova-api-os to nova-api-os-compute, adds nove-api-os-volume
 * Separate extension mechanism for compute and volume
 ** Removes flag osapi_extension and replaces with osapi_compute_extension and osapi_volume_extension
 * Updates the paste config
 * Fixes setup.py to include nova-os-api-compute and nova-os-api-volume
 * Fix bug in volume version code that occurred as result of trunk merge
 * Update integrated/test_volumes.py to use new endpoint

Change-Id: I4c2e57c3cafd4e1a9e2ff3ce201c8cf28326afcd
This commit is contained in:
Anthony Young 2012-01-09 14:02:02 -08:00
parent 26de3426d4
commit 60ff2e3b72
147 changed files with 2156 additions and 565 deletions

47
bin/nova-api-os-compute Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
"""Starter script for Nova OS API."""
import eventlet
eventlet.monkey_patch()
import os
import sys
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(
sys.argv[0]), os.pardir, os.pardir))
if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")):
sys.path.insert(0, possible_topdir)
from nova import flags
from nova import log as logging
from nova import service
from nova import utils
if __name__ == '__main__':
utils.default_flagfile()
flags.FLAGS(sys.argv)
logging.setup()
utils.monkey_patch()
server = service.WSGIService('osapi_compute')
service.serve(server)
service.wait()

View File

@ -42,6 +42,6 @@ if __name__ == '__main__':
flags.FLAGS(sys.argv)
logging.setup()
utils.monkey_patch()
server = service.WSGIService('osapi')
server = service.WSGIService('osapi_volume')
service.serve(server)
service.wait()

View File

@ -78,40 +78,60 @@ paste.app_factory = nova.api.ec2:Executor.factory
# Openstack #
#############
[composite:osapi]
use = call:nova.api.openstack.v2.urlmap:urlmap_factory
/: osversions
/v1.1: openstack_api_v2
/v2: openstack_api_v2
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v2
/v2: openstack_compute_api_v2
[pipeline:openstack_api_v2]
pipeline = faultwrap noauth ratelimit serialize extensions osapi_app_v2
[composite:osapi_volume]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: osvolumeversions
/v1: openstack_volume_api_v1
[pipeline:openstack_compute_api_v2]
pipeline = faultwrap noauth ratelimit serialize compute_extensions osapi_compute_app_v2
# NOTE(vish): use the following pipeline for deprecated auth
# pipeline = faultwrap auth ratelimit serialize extensions osapi_app_v2
# pipeline = faultwrap auth ratelimit serialize extensions osapi_compute_app_v2
[pipeline:openstack_volume_api_v1]
pipeline = faultwrap noauth ratelimit serialize volume_extensions osapi_volume_app_v1
[filter:faultwrap]
paste.filter_factory = nova.api.openstack.v2:FaultWrapper.factory
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
[filter:auth]
paste.filter_factory = nova.api.openstack.v2.auth:AuthMiddleware.factory
paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
[filter:noauth]
paste.filter_factory = nova.api.openstack.v2.auth:NoAuthMiddleware.factory
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
[filter:ratelimit]
paste.filter_factory = nova.api.openstack.v2.limits:RateLimitingMiddleware.factory
paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory
[filter:serialize]
paste.filter_factory = nova.api.openstack.wsgi:LazySerializationMiddleware.factory
[filter:extensions]
paste.filter_factory = nova.api.openstack.v2.extensions:ExtensionMiddleware.factory
[filter:compute_extensions]
paste.filter_factory = nova.api.openstack.compute.extensions:ExtensionMiddleware.factory
[app:osapi_app_v2]
paste.app_factory = nova.api.openstack.v2:APIRouter.factory
[filter:volume_extensions]
paste.filter_factory = nova.api.openstack.volume.extensions:ExtensionMiddleware.factory
[pipeline:osversions]
pipeline = faultwrap osversionapp
[app:osapi_compute_app_v2]
paste.app_factory = nova.api.openstack.compute:APIRouter.factory
[app:osversionapp]
paste.app_factory = nova.api.openstack.v2.versions:Versions.factory
[pipeline:oscomputeversions]
pipeline = faultwrap oscomputeversionapp
[app:osapi_volume_app_v1]
paste.app_factory = nova.api.openstack.volume:APIRouter.factory
[app:oscomputeversionapp]
paste.app_factory = nova.api.openstack.compute.versions:Versions.factory
[pipeline:osvolumeversions]
pipeline = faultwrap osvolumeversionapp
[app:osvolumeversionapp]
paste.app_factory = nova.api.openstack.volume.versions:Versions.factory

76
nova/api/mapper.py Normal file
View File

@ -0,0 +1,76 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
"""
WSGI middleware for OpenStack API controllers.
"""
import routes
import webob.dec
import webob.exc
from nova.api.openstack import wsgi
from nova import flags
from nova import log as logging
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.compute')
FLAGS = flags.FLAGS
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
flags.DEFINE_bool('allow_instance_snapshots',
True,
'When True, this API service will permit instance snapshot operations.')
class FaultWrapper(base_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()
return wsgi.Fault(exc)
class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None):
if url is "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)
class ProjectMapper(APIMapper):
def resource(self, member_name, collection_name, **kwargs):
if not ('parent_resource' in kwargs):
kwargs['path_prefix'] = '{project_id}/'
else:
parent_resource = kwargs['parent_resource']
p_collection = parent_resource['collection_name']
p_member = parent_resource['member_name']
kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection,
p_member)
routes.Mapper.resource(self, member_name,
collection_name,
**kwargs)

View File

@ -0,0 +1,69 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
"""
WSGI middleware for OpenStack API controllers.
"""
import routes
import webob.dec
import webob.exc
from nova.api.openstack import wsgi
from nova import flags
from nova import log as logging
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack')
class FaultWrapper(base_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()
return wsgi.Fault(exc)
class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None):
if url is "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)
class ProjectMapper(APIMapper):
def resource(self, member_name, collection_name, **kwargs):
if not ('parent_resource' in kwargs):
kwargs['path_prefix'] = '{project_id}/'
else:
parent_resource = kwargs['parent_resource']
p_collection = parent_resource['collection_name']
p_member = parent_resource['member_name']
kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection,
p_member)
routes.Mapper.resource(self, member_name,
collection_name,
**kwargs)

View File

@ -32,7 +32,7 @@ from nova import log as logging
from nova import utils
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.v2.auth')
LOG = logging.getLogger('nova.api.openstack.compute.auth')
FLAGS = flags.FLAGS
flags.DECLARE('use_forwarded_for', 'nova.api.auth')

View File

@ -24,23 +24,24 @@ import routes
import webob.dec
import webob.exc
from nova.api.openstack.v2 import consoles
from nova.api.openstack.v2 import extensions
from nova.api.openstack.v2 import flavors
from nova.api.openstack.v2 import images
from nova.api.openstack.v2 import image_metadata
from nova.api.openstack.v2 import ips
from nova.api.openstack.v2 import limits
from nova.api.openstack.v2 import servers
from nova.api.openstack.v2 import server_metadata
from nova.api.openstack.v2 import versions
import nova.api.openstack
from nova.api.openstack.compute import consoles
from nova.api.openstack.compute import extensions
from nova.api.openstack.compute import flavors
from nova.api.openstack.compute import images
from nova.api.openstack.compute import image_metadata
from nova.api.openstack.compute import ips
from nova.api.openstack.compute import limits
from nova.api.openstack.compute import servers
from nova.api.openstack.compute import server_metadata
from nova.api.openstack.compute import versions
from nova.api.openstack import wsgi
from nova import flags
from nova import log as logging
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.v2')
LOG = logging.getLogger('nova.api.openstack.compute')
FLAGS = flags.FLAGS
flags.DEFINE_bool('allow_admin_api',
False,
@ -50,43 +51,6 @@ flags.DEFINE_bool('allow_instance_snapshots',
'When True, this API service will permit instance snapshot operations.')
class FaultWrapper(base_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()
return wsgi.Fault(exc)
class APIMapper(routes.Mapper):
def routematch(self, url=None, environ=None):
if url is "":
result = self._match("", environ)
return result[0], result[1]
return routes.Mapper.routematch(self, url, environ)
class ProjectMapper(APIMapper):
def resource(self, member_name, collection_name, **kwargs):
if not ('parent_resource' in kwargs):
kwargs['path_prefix'] = '{project_id}/'
else:
parent_resource = kwargs['parent_resource']
p_collection = parent_resource['collection_name']
p_member = parent_resource['member_name']
kwargs['path_prefix'] = '{project_id}/%s/:%s_id' % (p_collection,
p_member)
routes.Mapper.resource(self, member_name,
collection_name,
**kwargs)
class APIRouter(base_wsgi.Router):
"""
Routes requests on the OpenStack API to the appropriate controller
@ -102,7 +66,7 @@ class APIRouter(base_wsgi.Router):
if ext_mgr is None:
ext_mgr = extensions.ExtensionManager()
mapper = ProjectMapper()
mapper = nova.api.openstack.ProjectMapper()
self._setup_routes(mapper)
self._setup_ext_routes(mapper, ext_mgr)
super(APIRouter, self).__init__(mapper)

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Justin Santa Barbara
# 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.
"""Contrib contains extensions that are shipped with nova.
It can't be called 'extensions' because that causes namespacing problems.
"""
from nova import log as logging
from nova.api.openstack import extensions
LOG = logging.getLogger('nova.api.openstack.compute.contrib')
def standard_extensions(ext_mgr):
extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)

View File

@ -15,7 +15,7 @@
import webob.exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.auth import manager
@ -25,7 +25,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.v2.contrib.accounts')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.accounts')
class AccountTemplate(xmlutil.TemplateBuilder):

View File

@ -19,7 +19,7 @@ import webob
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import compute
from nova import exception
from nova import flags
@ -28,7 +28,7 @@ from nova.scheduler import api as scheduler_api
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.v2.contrib.admin_actions")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.admin_actions")
class Admin_actions(extensions.ExtensionDescriptor):

View File

@ -18,7 +18,7 @@ import os
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.auth import manager
from nova.cloudpipe import pipelib
from nova import compute
@ -31,7 +31,7 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.v2.contrib.cloudpipe")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.cloudpipe")
class CloudpipeTemplate(xmlutil.TemplateBuilder):

View File

@ -21,10 +21,10 @@ import webob
from nova import compute
from nova import exception
from nova import log as logging
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
LOG = logging.getLogger('nova.api.openstack.v2.contrib.console_output')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.console_output')
class Console_output(extensions.ExtensionDescriptor):

View File

@ -14,9 +14,9 @@
# License for the specific language governing permissions and limitations
# under the License
from nova.api.openstack.v2 import extensions
from nova.api.openstack.v2 import servers
from nova.api.openstack.v2 import views
from nova.api.openstack import extensions
from nova.api.openstack.compute import servers
from nova.api.openstack.compute import views
from nova.api.openstack import wsgi

View File

@ -18,14 +18,14 @@
import webob
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack.v2 import servers
from nova.api.openstack import extensions
from nova.api.openstack.compute import servers
from nova import compute
from nova import exception
from nova import log as logging
LOG = logging.getLogger("nova.api.openstack.v2.contrib.deferred-delete")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.deferred-delete")
class Deferred_delete(extensions.ExtensionDescriptor):

View File

@ -20,7 +20,7 @@ from xml.dom import minidom
from webob import exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import xmlutil
from nova import compute
from nova import db

View File

@ -16,7 +16,7 @@
from webob import exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import xmlutil
from nova import compute
from nova import exception
@ -25,7 +25,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.v2.contrib.extendedstatus")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.extendedstatus")
class Extended_status(extensions.ExtensionDescriptor):

View File

@ -24,7 +24,7 @@ attributes. This extension adds to that list:
swap
"""
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
class Flavorextradata(extensions.ExtensionDescriptor):

View File

@ -21,7 +21,7 @@ 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.api.openstack import extensions
from nova import db
from nova import exception

View File

@ -20,13 +20,13 @@ import webob
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import exception
from nova import log as logging
from nova import network
LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ip_dns')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_dns')
def make_dns_entry(elem):

View File

@ -16,12 +16,12 @@
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import log as logging
from nova import network
LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ip_poolss')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_pools')
def _translate_floating_ip_view(pool):

View File

@ -21,7 +21,7 @@ import webob
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import compute
from nova import exception
from nova import log as logging
@ -29,7 +29,7 @@ from nova import network
from nova import rpc
LOG = logging.getLogger('nova.api.openstack.v2.contrib.floating_ips')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ips')
def make_float_ip(elem):

View File

@ -21,7 +21,7 @@ from xml.parsers import expat
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import compute
from nova import exception
from nova import flags
@ -29,7 +29,7 @@ from nova import log as logging
from nova.scheduler import api as scheduler_api
LOG = logging.getLogger("nova.api.openstack.v2.contrib.hosts")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.hosts")
FLAGS = flags.FLAGS

View File

@ -26,7 +26,7 @@ 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.api.openstack import extensions
from nova import crypto
from nova import db
from nova import exception

View File

@ -18,13 +18,13 @@
import webob
from webob import exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import compute
from nova import exception
from nova import log as logging
LOG = logging.getLogger("nova.api.openstack.v2.contrib.multinic")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.multinic")
# Note: The class name is as it has to be for this to be loaded as an

View File

@ -19,7 +19,7 @@
from webob import exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import exception
from nova import flags
from nova import log as logging
@ -27,7 +27,7 @@ import nova.network.api
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.v2.contrib.networks')
LOG = logging.getLogger('nova.api.openstack.compute.contrib.networks')
def network_dict(network):

View File

@ -19,7 +19,7 @@ import webob
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova import db
from nova import exception
from nova import quota

View File

@ -17,7 +17,7 @@
import webob
from webob import exc
from nova.api.openstack.v2 import extensions as exts
from nova.api.openstack import extensions as exts
from nova import compute
from nova import exception
from nova import flags
@ -26,7 +26,7 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.v2.contrib.rescue")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.rescue")
class Rescue(exts.ExtensionDescriptor):

View File

@ -22,7 +22,7 @@ from webob import exc
import webob
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
@ -33,7 +33,7 @@ from nova import log as logging
from nova import utils
LOG = logging.getLogger("nova.api.openstack.v2.contrib.security_groups")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.security_groups")
FLAGS = flags.FLAGS

View File

@ -15,7 +15,7 @@
import webob.exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute

View File

@ -15,7 +15,7 @@
import webob.exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute

View File

@ -20,7 +20,7 @@ import urlparse
import webob
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.compute import api

View File

@ -16,7 +16,7 @@
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.auth import manager

View File

@ -16,14 +16,15 @@
"""The virtual interfaces extension."""
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import log as logging
from nova import network
LOG = logging.getLogger("nova.api.openstack.v2.contrib.virtual_interfaces")
LOG = logging.getLogger("nova.api.openstack.compute."
"contrib.virtual_interfaces")
vif_nsmap = {None: wsgi.XMLNS_V11}

View File

@ -22,9 +22,9 @@ import webob
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.v2.contrib import volumes
from nova.api.openstack.v2 import extensions
from nova.api.openstack.v2 import servers
from nova.api.openstack.compute.contrib import volumes
from nova.api.openstack import extensions
from nova.api.openstack.compute import servers
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
@ -40,7 +40,7 @@ from nova import volume
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.v2.contrib.vsa")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.vsa")
def _vsa_view(context, vsa, details=False, instances=None):

View File

@ -19,8 +19,8 @@ from webob import exc
import webob
from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova.api.openstack.v2 import servers
from nova.api.openstack import extensions
from nova.api.openstack.compute import servers
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
@ -31,7 +31,7 @@ from nova import volume
from nova.volume import volume_types
LOG = logging.getLogger("nova.api.openstack.v2.contrib.volumes")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.volumes")
FLAGS = flags.FLAGS

View File

@ -19,7 +19,7 @@
from webob import exc
from nova.api.openstack.v2 import extensions
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import db

View File

@ -20,8 +20,8 @@
import json
from nova.api.openstack import common
from nova.api.openstack.v2 import servers
from nova.api.openstack.v2 import extensions
from nova.api.openstack.compute import servers
from nova.api.openstack import extensions
from nova.api.openstack import xmlutil
from nova.api.openstack import wsgi
from nova.compute import api as compute
@ -32,7 +32,7 @@ from nova import log as logging
import nova.scheduler.api
LOG = logging.getLogger("nova.api.openstack.v2.contrib.zones")
LOG = logging.getLogger("nova.api.openstack.compute.contrib.zones")
FLAGS = flags.FLAGS

View File

@ -0,0 +1,45 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 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.
from nova.api.openstack import extensions as base_extensions
from nova import flags
from nova import log as logging
LOG = logging.getLogger('nova.api.openstack.compute.extensions')
FLAGS = flags.FLAGS
class ExtensionManager(base_extensions.ExtensionManager):
def __new__(cls):
if cls._ext_mgr is None:
LOG.audit(_('Initializing extension manager.'))
cls._ext_mgr = super(ExtensionManager, cls).__new__(cls)
cls.cls_list = FLAGS.osapi_compute_extension
cls._ext_mgr.extensions = {}
cls._ext_mgr._load_extensions()
return cls._ext_mgr
class ExtensionMiddleware(base_extensions.ExtensionMiddleware):
def __init__(self, application, ext_mgr=None):
if not ext_mgr:
ext_mgr = ExtensionManager()
super(ExtensionMiddleware, self).__init__(application, ext_mgr)

View File

@ -17,7 +17,7 @@
import webob
from nova.api.openstack.v2.views import flavors as flavors_view
from nova.api.openstack.compute.views import flavors as flavors_view
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.compute import instance_types

View File

@ -16,7 +16,7 @@
import webob.exc
from nova.api.openstack import common
from nova.api.openstack.v2.views import images as views_images
from nova.api.openstack.compute.views import images as views_images
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
@ -26,7 +26,7 @@ import nova.image
from nova import log
LOG = log.getLogger('nova.api.openstack.v2.images')
LOG = log.getLogger('nova.api.openstack.compute.images')
FLAGS = flags.FLAGS
SUPPORTED_FILTERS = {

View File

@ -19,14 +19,14 @@ from webob import exc
import nova
from nova.api.openstack import common
from nova.api.openstack.v2.views import addresses as view_addresses
from nova.api.openstack.compute.views import addresses as view_addresses
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import log as logging
from nova import flags
LOG = logging.getLogger('nova.api.openstack.v2.ips')
LOG = logging.getLogger('nova.api.openstack.compute.ips')
FLAGS = flags.FLAGS

View File

@ -28,7 +28,7 @@ import time
from webob.dec import wsgify
import webob.exc
from nova.api.openstack.v2.views import limits as limits_views
from nova.api.openstack.compute.views import limits as limits_views
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import quota

View File

@ -22,8 +22,8 @@ from webob import exc
import webob
from nova.api.openstack import common
from nova.api.openstack.v2 import ips
from nova.api.openstack.v2.views import servers as views_servers
from nova.api.openstack.compute import ips
from nova.api.openstack.compute.views import servers as views_servers
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
@ -36,7 +36,7 @@ from nova.scheduler import api as scheduler_api
from nova import utils
LOG = logging.getLogger('nova.api.openstack.v2.servers')
LOG = logging.getLogger('nova.api.openstack.compute.servers')
FLAGS = flags.FLAGS

View File

@ -19,7 +19,7 @@ from datetime import datetime
from lxml import etree
from nova.api.openstack.v2.views import versions as views_versions
from nova.api.openstack.compute.views import versions as views_versions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil

View File

@ -23,7 +23,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.v2.views.addresses')
LOG = logging.getLogger('nova.api.openstack.compute.views.addresses')
class ViewBuilder(common.ViewBuilder):

View File

@ -19,14 +19,14 @@
import hashlib
from nova.api.openstack import common
from nova.api.openstack.v2.views import addresses as views_addresses
from nova.api.openstack.v2.views import flavors as views_flavors
from nova.api.openstack.v2.views import images as views_images
from nova.api.openstack.compute.views import addresses as views_addresses
from nova.api.openstack.compute.views import flavors as views_flavors
from nova.api.openstack.compute.views import images as views_images
from nova import log as logging
from nova import utils
LOG = logging.getLogger('nova.api.openstack.v2.views.servers')
LOG = logging.getLogger('nova.api.openstack.compute.views.servers')
class ViewBuilder(common.ViewBuilder):

View File

@ -16,11 +16,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
import routes
import webob.dec
import webob.exc
import nova.api.openstack.v2
import nova.api.openstack
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
@ -30,7 +31,7 @@ from nova import utils
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.v2.extensions')
LOG = logging.getLogger('nova.api.openstack.extensions')
FLAGS = flags.FLAGS
@ -326,7 +327,7 @@ class ExtensionMiddleware(base_wsgi.Middleware):
ext_mgr = ExtensionManager()
self.ext_mgr = ext_mgr
mapper = nova.api.openstack.v2.ProjectMapper()
mapper = nova.api.openstack.ProjectMapper()
# extended actions
action_resources = self._action_ext_resources(application, ext_mgr,
@ -388,17 +389,6 @@ class ExtensionManager(object):
def reset(cls):
cls._ext_mgr = None
def __new__(cls):
if cls._ext_mgr is None:
LOG.audit(_('Initializing extension manager.'))
cls._ext_mgr = super(ExtensionManager, cls).__new__(cls)
cls._ext_mgr.extensions = {}
cls._ext_mgr._load_extensions()
return cls._ext_mgr
def register(self, ext):
# Do nothing if the extension doesn't check out
if not self._check_extension(ext):
@ -483,7 +473,6 @@ class ExtensionManager(object):
LOG.debug(_("Loading extension %s"), ext_factory)
# Load the factory
factory = utils.import_class(ext_factory)
# Call it
@ -493,7 +482,7 @@ class ExtensionManager(object):
def _load_extensions(self):
"""Load extensions specified on the command line."""
extensions = list(FLAGS.osapi_extension)
extensions = list(self.cls_list)
for ext_factory in extensions:
try:
@ -573,3 +562,62 @@ def wrap_errors(fn):
except Exception, e:
raise webob.exc.HTTPInternalServerError()
return wrapped
def load_standard_extensions(ext_mgr, logger, path, package):
"""Registers all standard API extensions."""
# Walk through all the modules in our directory...
our_dir = path[0]
for dirpath, dirnames, filenames in os.walk(our_dir):
# Compute the relative package name from the dirpath
relpath = os.path.relpath(dirpath, our_dir)
if relpath == '.':
relpkg = ''
else:
relpkg = '.%s' % '.'.join(relpath.split(os.sep))
# Now, consider each file in turn, only considering .py files
for fname in filenames:
root, ext = os.path.splitext(fname)
# Skip __init__ and anything that's not .py
if ext != '.py' or root == '__init__':
continue
# Try loading it
classname = ("%s%s.%s.%s%s" %
(package, relpkg, root,
root[0].upper(), root[1:]))
try:
ext_mgr.load_extension(classname)
except Exception as exc:
logger.warn(_('Failed to load extension %(classname)s: '
'%(exc)s') % locals())
# Now, let's consider any subdirectories we may have...
subdirs = []
for dname in dirnames:
# Skip it if it does not have __init__.py
if not os.path.exists(os.path.join(dirpath, dname,
'__init__.py')):
continue
# If it has extension(), delegate...
ext_name = ("%s%s.%s.extension" %
(package, relpkg, dname))
try:
ext = utils.import_class(ext_name)
except exception.ClassNotFound:
# extension() doesn't exist on it, so we'll explore
# the directory for ourselves
subdirs.append(dname)
else:
try:
ext(ext_mgr)
except Exception as exc:
logger.warn(_('Failed to load extension %(ext_name)s: '
'%(exc)s') % locals())
# Update the list of directories we'll explore...
dirnames[:] = subdirs

View File

@ -28,7 +28,7 @@ _option_header_piece_re = re.compile(r';\s*([^\s;=]+|%s)\s*'
r'(?:=\s*([^;]+|%s))?\s*' %
(_quoted_string_re, _quoted_string_re))
LOG = logging.getLogger('nova.api.openstack.v2.map')
LOG = logging.getLogger('nova.api.openstack.compute.map')
def unquote_header_value(value):

View File

@ -1,90 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Justin Santa Barbara
# 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.
"""Contrib contains extensions that are shipped with nova.
It can't be called 'extensions' because that causes namespacing problems.
"""
import os
from nova import exception
from nova import log as logging
from nova import utils
LOG = logging.getLogger('nova.api.openstack.v2.contrib')
def standard_extensions(ext_mgr):
"""Registers all standard API extensions."""
# Walk through all the modules in our directory...
our_dir = __path__[0]
for dirpath, dirnames, filenames in os.walk(our_dir):
# Compute the relative package name from the dirpath
relpath = os.path.relpath(dirpath, our_dir)
if relpath == '.':
relpkg = ''
else:
relpkg = '.%s' % '.'.join(relpath.split(os.sep))
# Now, consider each file in turn, only considering .py files
for fname in filenames:
root, ext = os.path.splitext(fname)
# Skip __init__ and anything that's not .py
if ext != '.py' or root == '__init__':
continue
# Try loading it
classname = ("%s%s.%s.%s%s" %
(__package__, relpkg, root,
root[0].upper(), root[1:]))
try:
ext_mgr.load_extension(classname)
except Exception as exc:
LOG.warn(_('Failed to load extension %(classname)s: '
'%(exc)s') % locals())
# Now, let's consider any subdirectories we may have...
subdirs = []
for dname in dirnames:
# Skip it if it does not have __init__.py
if not os.path.exists(os.path.join(dirpath, dname,
'__init__.py')):
continue
# If it has extension(), delegate...
ext_name = ("%s%s.%s.extension" %
(__package__, relpkg, dname))
try:
ext = utils.import_class(ext_name)
except exception.ClassNotFound:
# extension() doesn't exist on it, so we'll explore
# the directory for ourselves
subdirs.append(dname)
else:
try:
ext(ext_mgr)
except Exception as exc:
LOG.warn(_('Failed to load extension %(ext_name)s: '
'%(exc)s') % locals())
# Update the list of directories we'll explore...
dirnames[:] = subdirs

View File

@ -0,0 +1,99 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.
"""
WSGI middleware for OpenStack Volume API.
"""
import routes
import webob.dec
import webob.exc
import nova.api.openstack
from nova.api.openstack.volume import extensions
from nova.api.openstack.volume import snapshots
from nova.api.openstack.volume import types
from nova.api.openstack.volume import volumes
from nova.api.openstack.volume import versions
from nova.api.openstack import wsgi
from nova import flags
from nova import log as logging
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.volume')
FLAGS = flags.FLAGS
class APIRouter(base_wsgi.Router):
"""
Routes requests on the OpenStack API to the appropriate controller
and method.
"""
@classmethod
def factory(cls, global_config, **local_config):
"""Simple paste factory, :class:`nova.wsgi.Router` doesn't have one"""
return cls()
def __init__(self, ext_mgr=None):
if ext_mgr is None:
ext_mgr = extensions.ExtensionManager()
mapper = nova.api.openstack.ProjectMapper()
self._setup_routes(mapper)
self._setup_ext_routes(mapper, ext_mgr)
super(APIRouter, self).__init__(mapper)
def _setup_ext_routes(self, mapper, ext_mgr):
serializer = wsgi.ResponseSerializer(
{'application/xml': wsgi.XMLDictSerializer()})
for resource in ext_mgr.get_resources():
LOG.debug(_('Extended resource: %s'),
resource.collection)
if resource.serializer is None:
resource.serializer = serializer
kargs = dict(
controller=wsgi.Resource(
resource.controller, resource.deserializer,
resource.serializer),
collection=resource.collection_actions,
member=resource.member_actions)
if resource.parent:
kargs['parent_resource'] = resource.parent
mapper.resource(resource.collection, resource.collection, **kargs)
def _setup_routes(self, mapper):
mapper.connect("versions", "/",
controller=versions.create_resource(),
action='show')
mapper.redirect("", "/")
mapper.resource("volume", "volumes",
controller=volumes.create_resource(),
collection={'detail': 'GET'})
mapper.resource("type", "types",
controller=types.create_resource())
mapper.resource("snapshot", "snapshots",
controller=snapshots.create_resource())

View File

@ -0,0 +1,32 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 Justin Santa Barbara
# 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.
"""Contrib contains extensions that are shipped with nova.
It can't be called 'extensions' because that causes namespacing problems.
"""
from nova import log as logging
from nova.api.openstack import extensions
LOG = logging.getLogger('nova.api.openstack.volume.contrib')
def standard_extensions(ext_mgr):
extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)

View File

@ -0,0 +1,44 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 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.
from nova.api.openstack import extensions as base_extensions
from nova import flags
from nova import log as logging
LOG = logging.getLogger('nova.api.openstack.volume.extensions')
FLAGS = flags.FLAGS
class ExtensionManager(base_extensions.ExtensionManager):
def __new__(cls):
if cls._ext_mgr is None:
LOG.audit(_('Initializing extension manager.'))
cls._ext_mgr = super(ExtensionManager, cls).__new__(cls)
cls.cls_list = FLAGS.osapi_volume_extension
cls._ext_mgr.extensions = {}
cls._ext_mgr._load_extensions()
return cls._ext_mgr
class ExtensionMiddleware(base_extensions.ExtensionMiddleware):
def __init__(self, application, ext_mgr=None):
ext_mgr = ExtensionManager()
super(ExtensionMiddleware, self).__init__(application, ext_mgr)

View File

@ -0,0 +1,183 @@
# Copyright 2011 Justin Santa Barbara
# 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.
"""The volumes snapshots api."""
from webob import exc
import webob
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack.compute import servers
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
from nova import exception
from nova import flags
from nova import log as logging
from nova import volume
from nova.volume import volume_types
LOG = logging.getLogger("nova.api.openstack.volume.snapshots")
FLAGS = flags.FLAGS
def _translate_snapshot_detail_view(context, vol):
"""Maps keys for snapshots details view."""
d = _translate_snapshot_summary_view(context, vol)
# NOTE(gagupta): No additional data / lookups at the moment
return d
def _translate_snapshot_summary_view(context, vol):
"""Maps keys for snapshots summary view."""
d = {}
d['id'] = vol['id']
d['volumeId'] = vol['volume_id']
d['status'] = vol['status']
# NOTE(gagupta): We map volume_size as the snapshot size
d['size'] = vol['volume_size']
d['createdAt'] = vol['created_at']
d['displayName'] = vol['display_name']
d['displayDescription'] = vol['display_description']
return d
class SnapshotsController(object):
"""The Volumes API controller for the OpenStack API."""
def __init__(self):
self.volume_api = volume.API()
super(SnapshotsController, self).__init__()
def show(self, req, id):
"""Return data about the given snapshot."""
context = req.environ['nova.context']
try:
vol = self.volume_api.get_snapshot(context, id)
except exception.NotFound:
return exc.HTTPNotFound()
return {'snapshot': _translate_snapshot_detail_view(context, vol)}
def delete(self, req, id):
"""Delete a snapshot."""
context = req.environ['nova.context']
LOG.audit(_("Delete snapshot with id: %s"), id, context=context)
try:
self.volume_api.delete_snapshot(context, snapshot_id=id)
except exception.NotFound:
return exc.HTTPNotFound()
return webob.Response(status_int=202)
def index(self, req):
"""Returns a summary list of snapshots."""
return self._items(req, entity_maker=_translate_snapshot_summary_view)
def detail(self, req):
"""Returns a detailed list of snapshots."""
return self._items(req, entity_maker=_translate_snapshot_detail_view)
def _items(self, req, entity_maker):
"""Returns a list of snapshots, transformed through entity_maker."""
context = req.environ['nova.context']
snapshots = self.volume_api.get_all_snapshots(context)
limited_list = common.limited(snapshots, req)
res = [entity_maker(context, snapshot) for snapshot in limited_list]
return {'snapshots': res}
def create(self, req, body):
"""Creates a new snapshot."""
context = req.environ['nova.context']
if not body:
return exc.HTTPUnprocessableEntity()
snapshot = body['snapshot']
volume_id = snapshot['volume_id']
force = snapshot.get('force', False)
LOG.audit(_("Create snapshot from volume %s"), volume_id,
context=context)
if force:
new_snapshot = self.volume_api.create_snapshot_force(context,
volume_id,
snapshot.get('display_name'),
snapshot.get('display_description'))
else:
new_snapshot = self.volume_api.create_snapshot(context,
volume_id,
snapshot.get('display_name'),
snapshot.get('display_description'))
retval = _translate_snapshot_detail_view(context, new_snapshot)
return {'snapshot': retval}
def make_snapshot(elem):
elem.set('id')
elem.set('status')
elem.set('size')
elem.set('createdAt')
elem.set('displayName')
elem.set('displayDescription')
elem.set('volumeId')
class SnapshotTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('snapshot', selector='snapshot')
make_snapshot(root)
return xmlutil.MasterTemplate(root, 1)
class SnapshotsTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('snapshots')
elem = xmlutil.SubTemplateElement(root, 'snapshot',
selector='snapshots')
make_snapshot(elem)
return xmlutil.MasterTemplate(root, 1)
class SnapshotSerializer(xmlutil.XMLTemplateSerializer):
def default(self):
return SnapshotTemplate()
def index(self):
return SnapshotsTemplate()
def detail(self):
return SnapshotsTemplate()
def create_resource():
body_serializers = {
'application/xml': SnapshotSerializer(),
}
serializer = wsgi.ResponseSerializer(body_serializers)
return wsgi.Resource(SnapshotsController(), serializer=serializer)

View File

@ -0,0 +1,89 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2011 Zadara Storage Inc.
# Copyright (c) 2011 OpenStack LLC.
#
# 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.
""" The volume type & volume types extra specs extension"""
from webob import exc
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import db
from nova import exception
from nova.volume import volume_types
class VolumeTypesController(object):
""" The volume types API controller for the Openstack API """
def index(self, req):
""" Returns the list of volume types """
context = req.environ['nova.context']
return volume_types.get_all_types(context)
def show(self, req, id):
""" Return a single volume type item """
context = req.environ['nova.context']
try:
vol_type = volume_types.get_volume_type(context, id)
except exception.NotFound or exception.ApiError:
raise exc.HTTPNotFound()
return {'volume_type': vol_type}
def make_voltype(elem):
elem.set('id')
elem.set('name')
extra_specs = xmlutil.make_flat_dict('extra_specs', selector='extra_specs')
elem.append(extra_specs)
class VolumeTypeTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volume_type', selector='volume_type')
make_voltype(root)
return xmlutil.MasterTemplate(root, 1)
class VolumeTypesTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volume_types')
sel = lambda obj, do_raise=False: obj.values()
elem = xmlutil.SubTemplateElement(root, 'volume_type', selector=sel)
make_voltype(elem)
return xmlutil.MasterTemplate(root, 1)
class VolumeTypesSerializer(xmlutil.XMLTemplateSerializer):
def index(self):
return VolumeTypesTemplate()
def default(self):
return VolumeTypeTemplate()
def create_resource():
body_serializers = {
'application/xml': VolumeTypesSerializer(),
}
serializer = wsgi.ResponseSerializer(body_serializers)
deserializer = wsgi.RequestDeserializer()
return wsgi.Resource(VolumeTypesController(), serializer=serializer)

View File

@ -0,0 +1,83 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 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.
from datetime import datetime
from lxml import etree
from nova.api.openstack.compute import versions
from nova.api.openstack.volume.views import versions as views_versions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
VERSIONS = {
"v1": {
"id": "v1",
"status": "CURRENT",
"updated": "2012-01-04T11:33:21Z",
"links": [
{
"rel": "describedby",
"type": "application/pdf",
"href": "http://jorgew.github.com/block-storage-api/"
"content/os-block-storage-1.0.pdf",
},
{
"rel": "describedby",
"type": "application/vnd.sun.wadl+xml",
#(anthony) FIXME
"href": "http://docs.rackspacecloud.com/"
"servers/api/v1.1/application.wadl",
},
],
"media-types": [
{
"base": "application/xml",
"type": "application/vnd.openstack.volume+xml;version=1",
},
{
"base": "application/json",
"type": "application/vnd.openstack.volume+json;version=1",
}
],
}
}
class Versions(versions.Versions):
def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
builder = views_versions.get_view_builder(request)
if request.path == '/':
# List Versions
return builder.build_versions(VERSIONS)
else:
# Versions Multiple Choice
return builder.build_choices(VERSIONS, request)
class VolumeVersionV1(object):
@wsgi.serializers(xml=versions.VersionTemplate,
atom=versions.VersionAtomSerializer)
def show(self, req):
builder = views_versions.get_view_builder(req)
return builder.build_version(VERSIONS['v2.0'])
def create_resource():
return wsgi.Resource(VolumeVersionV1())

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 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.

View File

@ -0,0 +1,37 @@
# 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 copy
import os
from nova.api.openstack.compute.views import versions as compute_views
def get_view_builder(req):
base_url = req.application_url
return ViewBuilder(base_url)
class ViewBuilder(compute_views.ViewBuilder):
def generate_href(self, path=None):
"""Create an url that refers to a specific version_number."""
version_number = 'v1'
if path:
path = path.strip('/')
return os.path.join(self.base_url, version_number, path)
else:
return os.path.join(self.base_url, version_number) + '/'

View File

@ -0,0 +1,254 @@
# Copyright 2011 Justin Santa Barbara
# 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.
"""The volumes api."""
from webob import exc
import webob
from nova.api.openstack import common
from nova.api.openstack.compute import servers
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import exception
from nova import flags
from nova import log as logging
from nova import volume
from nova.volume import volume_types
LOG = logging.getLogger("nova.api.openstack.volume.volumes")
FLAGS = flags.FLAGS
def _translate_volume_detail_view(context, vol):
"""Maps keys for volumes details view."""
d = _translate_volume_summary_view(context, vol)
# No additional data / lookups at the moment
return d
def _translate_volume_summary_view(context, vol):
"""Maps keys for volumes summary view."""
d = {}
d['id'] = vol['id']
d['status'] = vol['status']
d['size'] = vol['size']
d['availabilityZone'] = vol['availability_zone']
d['createdAt'] = vol['created_at']
if vol['attach_status'] == 'attached':
d['attachments'] = [_translate_attachment_detail_view(context, vol)]
else:
d['attachments'] = [{}]
d['displayName'] = vol['display_name']
d['displayDescription'] = vol['display_description']
if vol['volume_type_id'] and vol.get('volume_type'):
d['volumeType'] = vol['volume_type']['name']
else:
d['volumeType'] = vol['volume_type_id']
d['snapshotId'] = vol['snapshot_id']
LOG.audit(_("vol=%s"), vol, context=context)
if vol.get('volume_metadata'):
meta_dict = {}
for i in vol['volume_metadata']:
meta_dict[i['key']] = i['value']
d['metadata'] = meta_dict
else:
d['metadata'] = {}
return d
class VolumeController(object):
"""The Volumes API controller for the OpenStack API."""
def __init__(self):
self.volume_api = volume.API()
super(VolumeController, self).__init__()
def show(self, req, id):
"""Return data about the given volume."""
context = req.environ['nova.context']
try:
vol = self.volume_api.get(context, id)
except exception.NotFound:
raise exc.HTTPNotFound()
return {'volume': _translate_volume_detail_view(context, vol)}
def delete(self, req, id):
"""Delete a volume."""
context = req.environ['nova.context']
LOG.audit(_("Delete volume with id: %s"), id, context=context)
try:
self.volume_api.delete(context, volume_id=id)
except exception.NotFound:
raise exc.HTTPNotFound()
return webob.Response(status_int=202)
def index(self, req):
"""Returns a summary list of volumes."""
return self._items(req, entity_maker=_translate_volume_summary_view)
def detail(self, req):
"""Returns a detailed list of volumes."""
return self._items(req, entity_maker=_translate_volume_detail_view)
def _items(self, req, entity_maker):
"""Returns a list of volumes, transformed through entity_maker."""
context = req.environ['nova.context']
volumes = self.volume_api.get_all(context)
limited_list = common.limited(volumes, req)
res = [entity_maker(context, vol) for vol in limited_list]
return {'volumes': res}
def create(self, req, body):
"""Creates a new volume."""
context = req.environ['nova.context']
if not body:
raise exc.HTTPUnprocessableEntity()
vol = body['volume']
size = vol['size']
LOG.audit(_("Create volume of %s GB"), size, context=context)
vol_type = vol.get('volume_type', None)
if vol_type:
try:
vol_type = volume_types.get_volume_type_by_name(context,
vol_type)
except exception.NotFound:
raise exc.HTTPNotFound()
metadata = vol.get('metadata', None)
new_volume = self.volume_api.create(context, size,
vol.get('snapshot_id'),
vol.get('display_name'),
vol.get('display_description'),
volume_type=vol_type,
metadata=metadata)
# Work around problem that instance is lazy-loaded...
new_volume = self.volume_api.get(context, new_volume['id'])
retval = _translate_volume_detail_view(context, new_volume)
return {'volume': retval}
class VolumeAttachmentTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volumeAttachment',
selector='volumeAttachment')
make_attachment(root)
return xmlutil.MasterTemplate(root, 1)
class VolumeAttachmentsTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volumeAttachments')
elem = xmlutil.SubTemplateElement(root, 'volumeAttachment',
selector='volumeAttachments')
make_attachment(elem)
return xmlutil.MasterTemplate(root, 1)
class VolumeAttachmentSerializer(xmlutil.XMLTemplateSerializer):
def default(self):
return VolumeAttachmentTemplate()
def index(self):
return VolumeAttachmentsTemplate()
def make_attachment(elem):
elem.set('id')
elem.set('serverId')
elem.set('volumeId')
elem.set('device')
def make_volume(elem):
elem.set('id')
elem.set('status')
elem.set('size')
elem.set('availabilityZone')
elem.set('createdAt')
elem.set('displayName')
elem.set('displayDescription')
elem.set('volumeType')
elem.set('snapshotId')
attachments = xmlutil.SubTemplateElement(elem, 'attachments')
attachment = xmlutil.SubTemplateElement(attachments, 'attachment',
selector='attachments')
make_attachment(attachment)
metadata = xmlutil.make_flat_dict('metadata')
elem.append(metadata)
class VolumeTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volume', selector='volume')
make_volume(root)
return xmlutil.MasterTemplate(root, 1)
class VolumesTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('volumes')
elem = xmlutil.SubTemplateElement(root, 'volume', selector='volumes')
make_volume(elem)
return xmlutil.MasterTemplate(root, 1)
class VolumeSerializer(xmlutil.XMLTemplateSerializer):
def default(self):
return VolumeTemplate()
def index(self):
return VolumesTemplate()
def detail(self):
return VolumesTemplate()
def create_resource():
body_serializers = {
'application/xml': VolumeSerializer(),
}
serializer = wsgi.ResponseSerializer(body_serializers)
deserializer = wsgi.RequestDeserializer()
return wsgi.Resource(VolumeController(), serializer=serializer)

View File

@ -31,9 +31,9 @@ XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
def validate_schema(xml, schema_name):
if isinstance(xml, str):
xml = etree.fromstring(xml)
base_path = 'nova/api/openstack/v2/schemas/v1.1/'
base_path = 'nova/api/openstack/compute/schemas/v1.1/'
if schema_name in ('atom', 'atom-link'):
base_path = 'nova/api/openstack/v2/schemas/'
base_path = 'nova/api/openstack/compute/schemas/'
schema_path = os.path.join(utils.novadir(),
'%s%s.rng' % (base_path, schema_name))
schema_doc = etree.parse(schema_path)

View File

@ -827,7 +827,7 @@ class AuthManager(object):
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme,
ec2_host,
FLAGS.osapi_port,
FLAGS.osapi_compute_listen_port,
FLAGS.osapi_path),
'user': user.name,
'nova': FLAGS.ca_file,

View File

@ -32,14 +32,14 @@ Options can be strings, integers, floats, booleans, lists or 'multi strings':
enabled_apis_opt = \
cfg.ListOpt('enabled_apis',
default=['ec2', 'osapi'],
default=['ec2', 'osapi_compute'],
help='List of APIs to enable by default')
DEFAULT_EXTENSIONS = [
'nova.api.openstack.contrib.standard_extensions'
]
osapi_extension_opt = \
cfg.MultiStrOpt('osapi_extension',
osapi_compute_extension_opt = \
cfg.MultiStrOpt('osapi_compute_extension',
default=DEFAULT_EXTENSIONS)
Option schemas are registered with with the config manager at runtime, but
@ -55,7 +55,7 @@ before the option is referenced:
...
def _load_extensions(self):
for ext_factory in self.conf.osapi_extension:
for ext_factory in self.conf.osapi_compute_extension:
....
A common usage pattern is for each option schema to be defined in the module or

View File

@ -309,19 +309,21 @@ DEFINE_integer('rabbit_max_retries', 0,
'maximum rabbit connection attempts (0=try forever)')
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
DEFINE_boolean('rabbit_durable_queues', False, 'use durable queues')
DEFINE_list('enabled_apis', ['ec2', 'osapi', 'metadata'],
DEFINE_list('enabled_apis',
['ec2', 'osapi_compute', 'osapi_volume', 'metadata'],
'list of APIs to enable by default')
DEFINE_string('ec2_host', '$my_ip', 'ip of api server')
DEFINE_string('ec2_dmz_host', '$my_ip', 'internal ip of api server')
DEFINE_integer('ec2_port', 8773, 'cloud controller port')
DEFINE_string('ec2_scheme', 'http', 'prefix for ec2')
DEFINE_string('ec2_path', '/services/Cloud', 'suffix for ec2')
DEFINE_multistring('osapi_extension',
['nova.api.openstack.v2.contrib.standard_extensions'],
'osapi extension to load')
DEFINE_string('osapi_host', '$my_ip', 'ip of api server')
DEFINE_multistring('osapi_compute_extension',
['nova.api.openstack.compute.contrib.standard_extensions'],
'osapi compute extension to load')
DEFINE_multistring('osapi_volume_extension',
['nova.api.openstack.volume.contrib.standard_extensions'],
'osapi volume extension to load')
DEFINE_string('osapi_scheme', 'http', 'prefix for openstack')
DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
DEFINE_string('osapi_path', '/v1.1/', 'suffix for openstack')
DEFINE_integer('osapi_max_limit', 1000,
'max number of items returned in a collection response')

View File

@ -48,9 +48,10 @@ flags.DEFINE_integer('periodic_interval', 60,
flags.DEFINE_string('ec2_listen', "0.0.0.0",
'IP address for EC2 API to listen')
flags.DEFINE_integer('ec2_listen_port', 8773, 'port for ec2 api to listen')
flags.DEFINE_string('osapi_listen', "0.0.0.0",
flags.DEFINE_string('osapi_compute_listen', "0.0.0.0",
'IP address for OpenStack API to listen')
flags.DEFINE_integer('osapi_listen_port', 8774, 'port for os api to listen')
flags.DEFINE_integer('osapi_compute_listen_port', 8774,
'list port for osapi compute')
flags.DEFINE_string('metadata_manager', 'nova.api.manager.MetadataManager',
'OpenStack metadata service manager')
flags.DEFINE_string('metadata_listen', "0.0.0.0",
@ -59,6 +60,10 @@ flags.DEFINE_integer('metadata_listen_port', 8775,
'port for metadata api to listen')
flags.DEFINE_string('api_paste_config', "api-paste.ini",
'File name for the paste.deploy config for nova-api')
flags.DEFINE_string('osapi_volume_listen', "0.0.0.0",
'IP address for OpenStack Volume API to listen')
flags.DEFINE_integer('osapi_volume_listen_port', 8776,
'port for os volume api to listen')
class Launcher(object):

View File

@ -20,7 +20,7 @@ from lxml import etree
import webob
from nova import test
from nova.api.openstack.v2.contrib import accounts
from nova.api.openstack.compute.contrib import accounts
from nova.auth.manager import User
from nova.tests.api.openstack import fakes

View File

@ -17,8 +17,8 @@ import json
import webob
from nova.api.openstack import v2
from nova.api.openstack.v2 import extensions
from nova.api.openstack import compute as compute_api
from nova.api.openstack.compute import extensions
from nova.api.openstack import wsgi
from nova import compute
from nova import exception
@ -123,7 +123,7 @@ class CreateBackupTests(test.TestCase):
self.backup_stubs = fakes.stub_out_compute_api_backup(self.stubs)
self.flags(allow_admin_api=True)
router = v2.APIRouter()
router = compute_api.APIRouter()
ext_middleware = extensions.ExtensionMiddleware(router)
self.app = wsgi.LazySerializationMiddleware(ext_middleware)

View File

@ -20,9 +20,9 @@ import webob
from lxml import etree
from nova.api import auth
from nova.api.openstack import v2
from nova.api.openstack.v2 import wsgi
from nova.api.openstack.v2.contrib import cloudpipe
from nova.api.openstack import compute
from nova.api.openstack.compute import wsgi
from nova.api.openstack.compute.contrib import cloudpipe
from nova.auth import manager
from nova.cloudpipe import pipelib
from nova import context
@ -112,7 +112,7 @@ class CloudpipeTest(test.TestCase):
super(CloudpipeTest, self).setUp()
self.flags(allow_admin_api=True)
self.app = fakes.wsgi_app()
inner_app = v2.APIRouter()
inner_app = compute.APIRouter()
self.context = context.RequestContext('fake', 'fake', is_admin=True)
self.app = auth.InjectContext(self.context, inner_app)
route = inner_app.map.match('/1234/os-cloudpipe')

View File

@ -18,7 +18,7 @@
import mox
import webob
from nova.api.openstack.v2.contrib import deferred_delete
from nova.api.openstack.compute.contrib import deferred_delete
from nova import compute
from nova import exception
from nova import test

View File

@ -17,8 +17,8 @@
import datetime
from nova.api.openstack import v2
from nova.api.openstack.v2 import extensions
from nova.api.openstack import compute
from nova.api.openstack.compute import extensions
from nova.api.openstack import wsgi
import nova.db.api
from nova import flags
@ -117,7 +117,7 @@ class DiskConfigTestCase(test.TestCase):
self.stubs.Set(nova.db, 'instance_create', fake_instance_create)
app = v2.APIRouter()
app = compute.APIRouter()
app = extensions.ExtensionMiddleware(app)
app = wsgi.LazySerializationMiddleware(app)
self.app = app

Some files were not shown because too many files have changed in this diff Show More