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:
parent
26de3426d4
commit
60ff2e3b72
47
bin/nova-api-os-compute
Executable file
47
bin/nova-api-os-compute
Executable 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()
|
@ -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()
|
@ -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
76
nova/api/mapper.py
Normal 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)
|
@ -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)
|
@ -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')
|
||||
|
@ -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)
|
32
nova/api/openstack/compute/contrib/__init__.py
Normal file
32
nova/api/openstack/compute/contrib/__init__.py
Normal 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__)
|
@ -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):
|
@ -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):
|
@ -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):
|
@ -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):
|
@ -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
|
||||
|
||||
|
@ -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):
|
@ -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
|
@ -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):
|
@ -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):
|
@ -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
|
||||
|
@ -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):
|
@ -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):
|
@ -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):
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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):
|
@ -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
|
@ -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):
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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}
|
@ -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):
|
@ -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
|
@ -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
|
@ -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
|
||||
|
||||
|
45
nova/api/openstack/compute/extensions.py
Normal file
45
nova/api/openstack/compute/extensions.py
Normal 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)
|
@ -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
|
@ -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 = {
|
@ -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
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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):
|
@ -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):
|
@ -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
|
@ -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):
|
@ -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
|
99
nova/api/openstack/volume/__init__.py
Normal file
99
nova/api/openstack/volume/__init__.py
Normal 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())
|
32
nova/api/openstack/volume/contrib/__init__.py
Normal file
32
nova/api/openstack/volume/contrib/__init__.py
Normal 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__)
|
44
nova/api/openstack/volume/extensions.py
Normal file
44
nova/api/openstack/volume/extensions.py
Normal 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)
|
183
nova/api/openstack/volume/snapshots.py
Normal file
183
nova/api/openstack/volume/snapshots.py
Normal 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)
|
89
nova/api/openstack/volume/types.py
Normal file
89
nova/api/openstack/volume/types.py
Normal 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)
|
83
nova/api/openstack/volume/versions.py
Normal file
83
nova/api/openstack/volume/versions.py
Normal 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())
|
16
nova/api/openstack/volume/views/__init__.py
Normal file
16
nova/api/openstack/volume/views/__init__.py
Normal 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.
|
37
nova/api/openstack/volume/views/versions.py
Normal file
37
nova/api/openstack/volume/views/versions.py
Normal 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) + '/'
|
254
nova/api/openstack/volume/volumes.py
Normal file
254
nova/api/openstack/volume/volumes.py
Normal 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)
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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')
|
@ -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
|
@ -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
Loading…
Reference in New Issue
Block a user