summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2012-10-23 19:30:48 -0700
committerVishvananda Ishaya <vishvananda@gmail.com>2012-10-28 11:34:05 -0700
commit7e2b93acc59dea81d52684f7f659fcff32507e14 (patch)
tree11bf7e96a3835d0002f133742dd9e1702c7cc6c6 /nova/api
parent4e449c0843150b785bc61e87599d05ff242a8f4a (diff)
removes the nova-volume code from nova
This removes the majority of the nova-volume code from the codebase. It updates relevent config options to default to cinder. It updates a number of existing tests that were depending on code that was removed. A few things still need to be removed: * volume/driver.py & volume/iscsi.py These files are used by the libvirt volume driver tests. These tests should be updated to mock the relevant calls. * scheduler/simple.py & scheduler/multi.py These files should no longer be necessary so they can be removed in a subsequent patch * exception.py cleanup Once the above files are removed there are a number of unused exceptions which can be removed * database calls and database tables The database calls have not been removed and the tables have not been dropped. This can be done in a separate migration * additional config options and nova.conf.sample There may be a few extra config options that can be removed and the conf sample can be regenerated Implements bp delete-nova-volume Change-Id: I0b540e54dbabd26901a7530035a38583bb521fda
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/compute/contrib/volumes.py7
-rw-r--r--nova/api/openstack/compute/contrib/volumetypes.py225
-rw-r--r--nova/api/openstack/volume/__init__.py68
-rw-r--r--nova/api/openstack/volume/contrib/__init__.py39
-rw-r--r--nova/api/openstack/volume/contrib/admin_actions.py129
-rw-r--r--nova/api/openstack/volume/contrib/image_create.py31
-rw-r--r--nova/api/openstack/volume/contrib/types_extra_specs.py149
-rw-r--r--nova/api/openstack/volume/contrib/types_manage.py91
-rw-r--r--nova/api/openstack/volume/contrib/volume_actions.py131
-rw-r--r--nova/api/openstack/volume/extensions.py34
-rw-r--r--nova/api/openstack/volume/snapshots.py185
-rw-r--r--nova/api/openstack/volume/types.py80
-rw-r--r--nova/api/openstack/volume/versions.py83
-rw-r--r--nova/api/openstack/volume/views/__init__.py16
-rw-r--r--nova/api/openstack/volume/views/types.py34
-rw-r--r--nova/api/openstack/volume/views/versions.py36
-rw-r--r--nova/api/openstack/volume/volumes.py364
17 files changed, 0 insertions, 1702 deletions
diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py
index 9940e3050..6eaa51079 100644
--- a/nova/api/openstack/compute/contrib/volumes.py
+++ b/nova/api/openstack/compute/contrib/volumes.py
@@ -29,7 +29,6 @@ from nova import flags
from nova.openstack.common import log as logging
from nova import utils
from nova import volume
-from nova.volume import volume_types
LOG = logging.getLogger(__name__)
@@ -227,12 +226,6 @@ class VolumeController(wsgi.Controller):
vol = body['volume']
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)
diff --git a/nova/api/openstack/compute/contrib/volumetypes.py b/nova/api/openstack/compute/contrib/volumetypes.py
deleted file mode 100644
index 036e3ff42..000000000
--- a/nova/api/openstack/compute/contrib/volumetypes.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# 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
-
-
-authorize = extensions.extension_authorizer('compute', 'volumetypes')
-
-
-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')
- elem = xmlutil.SubTemplateElement(root, 'volume_type',
- selector='volume_types')
- make_voltype(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypesController(wsgi.Controller):
- """ The volume types API controller for the OpenStack API """
-
- @wsgi.serializers(xml=VolumeTypesTemplate)
- def index(self, req):
- """ Returns the list of volume types """
- context = req.environ['nova.context']
- authorize(context)
- return {'volume_types': volume_types.get_all_types(context).values()}
-
- @wsgi.serializers(xml=VolumeTypeTemplate)
- def create(self, req, body):
- """Creates a new volume type."""
- context = req.environ['nova.context']
- authorize(context)
-
- if not self.is_valid_body(body, 'volume_type'):
- raise exc.HTTPUnprocessableEntity()
-
- vol_type = body['volume_type']
- name = vol_type.get('name', None)
- specs = vol_type.get('extra_specs', {})
-
- if name is None or name == "":
- raise exc.HTTPUnprocessableEntity()
-
- try:
- volume_types.create(context, name, specs)
- vol_type = volume_types.get_volume_type_by_name(context, name)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- return {'volume_type': vol_type}
-
- @wsgi.serializers(xml=VolumeTypeTemplate)
- def show(self, req, id):
- """ Return a single volume type item """
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vol_type = volume_types.get_volume_type(context, id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- return {'volume_type': vol_type}
-
- def delete(self, req, id):
- """ Deletes an existing volume type """
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vol_type = volume_types.get_volume_type(context, id)
- volume_types.destroy(context, vol_type['name'])
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
-
-class VolumeTypeExtraSpecsTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.make_flat_dict('extra_specs', selector='extra_specs')
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypeExtraSpecTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- tagname = xmlutil.Selector('key')
-
- def extraspec_sel(obj, do_raise=False):
- # Have to extract the key and value for later use...
- key, value = obj.items()[0]
- return dict(key=key, value=value)
-
- root = xmlutil.TemplateElement(tagname, selector=extraspec_sel)
- root.text = 'value'
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypeExtraSpecsController(object):
- """ The volume type extra specs API controller for the OpenStack API """
-
- def _get_extra_specs(self, context, vol_type_id):
- extra_specs = db.volume_type_extra_specs_get(context, vol_type_id)
- specs_dict = {}
- for key, value in extra_specs.iteritems():
- specs_dict[key] = value
- return dict(extra_specs=specs_dict)
-
- def _check_body(self, body):
- if body is None or body == "":
- expl = _('No Request Body')
- raise exc.HTTPBadRequest(explanation=expl)
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate)
- def index(self, req, vol_type_id):
- """ Returns the list of extra specs for a given volume type """
- context = req.environ['nova.context']
- authorize(context)
- return self._get_extra_specs(context, vol_type_id)
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate)
- def create(self, req, vol_type_id, body):
- context = req.environ['nova.context']
- authorize(context)
- self._check_body(body)
- specs = body.get('extra_specs')
- db.volume_type_extra_specs_update_or_create(context,
- vol_type_id,
- specs)
- return body
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecTemplate)
- def update(self, req, vol_type_id, id, body):
- context = req.environ['nova.context']
- authorize(context)
- self._check_body(body)
- if not id in body:
- expl = _('Request body and URI mismatch')
- raise exc.HTTPBadRequest(explanation=expl)
- if len(body) > 1:
- expl = _('Request body contains too many items')
- raise exc.HTTPBadRequest(explanation=expl)
- db.volume_type_extra_specs_update_or_create(context,
- vol_type_id,
- body)
- return body
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecTemplate)
- def show(self, req, vol_type_id, id):
- """ Return a single extra spec item """
- context = req.environ['nova.context']
- authorize(context)
- specs = self._get_extra_specs(context, vol_type_id)
- if id in specs['extra_specs']:
- return {id: specs['extra_specs'][id]}
- else:
- raise exc.HTTPNotFound()
-
- def delete(self, req, vol_type_id, id):
- """ Deletes an existing extra spec """
- context = req.environ['nova.context']
- authorize(context)
- db.volume_type_extra_specs_delete(context, vol_type_id, id)
-
-
-class Volumetypes(extensions.ExtensionDescriptor):
- """Volume types support"""
-
- name = "VolumeTypes"
- alias = "os-volume-types"
- namespace = "http://docs.openstack.org/compute/ext/volume_types/api/v1.1"
- updated = "2011-08-24T00:00:00+00:00"
-
- def get_resources(self):
- resources = []
-
- res = extensions.ResourceExtension(
- 'os-volume-types',
- VolumeTypesController())
- resources.append(res)
-
- res = extensions.ResourceExtension('extra_specs',
- VolumeTypeExtraSpecsController(),
- parent=dict(
- member_name='vol_type',
- collection_name='os-volume-types'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/volume/__init__.py b/nova/api/openstack/volume/__init__.py
deleted file mode 100644
index 3aca5bebf..000000000
--- a/nova/api/openstack/volume/__init__.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# 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 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 versions
-from nova.api.openstack.volume import volumes
-from nova.openstack.common import log as logging
-
-
-LOG = logging.getLogger(__name__)
-
-
-class APIRouter(nova.api.openstack.APIRouter):
- """
- Routes requests on the OpenStack API to the appropriate controller
- and method.
- """
- ExtensionManager = extensions.ExtensionManager
-
- def _setup_routes(self, mapper, ext_mgr, init_only):
- if init_only is None or 'versions' in init_only:
- self.resources['versions'] = versions.create_resource()
- mapper.connect("versions", "/",
- controller=self.resources['versions'],
- action='show')
-
- mapper.redirect("", "/")
-
- if init_only is None or 'volumes' in init_only:
- self.resources['volumes'] = volumes.create_resource(ext_mgr)
- mapper.resource("volume", "volumes",
- controller=self.resources['volumes'],
- collection={'detail': 'GET'},
- member={'action': 'POST'})
-
- if init_only is None or 'types' in init_only:
- self.resources['types'] = types.create_resource()
- mapper.resource("type", "types",
- controller=self.resources['types'])
-
- if init_only is None or 'snapshots' in init_only:
- self.resources['snapshots'] = snapshots.create_resource(ext_mgr)
- mapper.resource("snapshot", "snapshots",
- controller=self.resources['snapshots'],
- collection={'detail': 'GET'},
- member={'action': 'POST'})
diff --git a/nova/api/openstack/volume/contrib/__init__.py b/nova/api/openstack/volume/contrib/__init__.py
deleted file mode 100644
index 8e01d88d0..000000000
--- a/nova/api/openstack/volume/contrib/__init__.py
+++ /dev/null
@@ -1,39 +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.
-
-"""
-
-from nova.api.openstack import extensions
-from nova import flags
-from nova.openstack.common import log as logging
-
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-
-
-def standard_extensions(ext_mgr):
- extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)
-
-
-def select_extensions(ext_mgr):
- extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__,
- FLAGS.osapi_volume_ext_list)
diff --git a/nova/api/openstack/volume/contrib/admin_actions.py b/nova/api/openstack/volume/contrib/admin_actions.py
deleted file mode 100644
index 7e93283f7..000000000
--- a/nova/api/openstack/volume/contrib/admin_actions.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# Copyright 2012 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.
-
-import webob
-from webob import exc
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova import db
-from nova import exception
-from nova.openstack.common import log as logging
-from nova import volume
-
-
-LOG = logging.getLogger(__name__)
-
-
-class AdminController(wsgi.Controller):
- """Abstract base class for AdminControllers."""
-
- collection = None # api collection to extend
-
- # FIXME(clayg): this will be hard to keep up-to-date
- # Concrete classes can expand or over-ride
- valid_status = set([
- 'creating',
- 'available',
- 'deleting',
- 'error',
- 'error_deleting',
- ])
-
- def __init__(self, *args, **kwargs):
- super(AdminController, self).__init__(*args, **kwargs)
- # singular name of the resource
- self.resource_name = self.collection.rstrip('s')
- self.volume_api = volume.API()
-
- def _update(self, *args, **kwargs):
- raise NotImplementedError()
-
- def _validate_status(self, status):
- if status not in self.valid_status:
- raise exc.HTTPBadRequest("Must specify a valid status")
-
- def authorize(self, context, action_name):
- # e.g. "snapshot_admin_actions:reset_status"
- action = '%s_admin_actions:%s' % (self.resource_name, action_name)
- extensions.extension_authorizer('volume', action)(context)
-
- @wsgi.action('os-reset_status')
- def _reset_status(self, req, id, body):
- """Reset status on the resource."""
- context = req.environ['nova.context']
- self.authorize(context, 'reset_status')
- try:
- new_status = body['os-reset_status']['status']
- except (TypeError, KeyError):
- raise exc.HTTPBadRequest("Must specify 'status'")
- self._validate_status(new_status)
- msg = _("Updating status of %(resource)s '%(id)s' to '%(status)s'")
- LOG.debug(msg, {'resource': self.resource_name, 'id': id,
- 'status': new_status})
- try:
- self._update(context, id, {'status': new_status})
- except exception.NotFound, e:
- raise exc.HTTPNotFound(e)
- return webob.Response(status_int=202)
-
-
-class VolumeAdminController(AdminController):
- """AdminController for Volumes."""
-
- collection = 'volumes'
- valid_status = AdminController.valid_status.union(
- set(['attaching', 'in-use', 'detaching']))
-
- def _update(self, *args, **kwargs):
- db.volume_update(*args, **kwargs)
-
- @wsgi.action('os-force_delete')
- def _force_delete(self, req, id, body):
- """Delete a resource, bypassing the check that it must be available."""
- context = req.environ['nova.context']
- self.authorize(context, 'force_delete')
- try:
- volume = self.volume_api.get(context, id)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- self.volume_api.delete(context, volume, force=True)
- return webob.Response(status_int=202)
-
-
-class SnapshotAdminController(AdminController):
- """AdminController for Snapshots."""
-
- collection = 'snapshots'
-
- def _update(self, *args, **kwargs):
- db.snapshot_update(*args, **kwargs)
-
-
-class Admin_actions(extensions.ExtensionDescriptor):
- """Enable admin actions."""
-
- name = "AdminActions"
- alias = "os-admin-actions"
- namespace = "http://docs.openstack.org/volume/ext/admin-actions/api/v1.1"
- updated = "2012-08-25T00:00:00+00:00"
-
- def get_controller_extensions(self):
- exts = []
- for class_ in (VolumeAdminController, SnapshotAdminController):
- controller = class_()
- extension = extensions.ControllerExtension(
- self, class_.collection, controller)
- exts.append(extension)
- return exts
diff --git a/nova/api/openstack/volume/contrib/image_create.py b/nova/api/openstack/volume/contrib/image_create.py
deleted file mode 100644
index 840689799..000000000
--- a/nova/api/openstack/volume/contrib/image_create.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright (c) 2012 NTT.
-# Copyright (c) 2012 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.
-
-"""The Create Volume from Image extension."""
-
-
-from nova.api.openstack import extensions
-
-
-class Image_create(extensions.ExtensionDescriptor):
- """Allow creating a volume from an image in the Create Volume v1 API"""
-
- name = "CreateVolumeExtension"
- alias = "os-image-create"
- namespace = "http://docs.openstack.org/volume/ext/image-create/api/v1"
- updated = "2012-08-13T00:00:00+00:00"
diff --git a/nova/api/openstack/volume/contrib/types_extra_specs.py b/nova/api/openstack/volume/contrib/types_extra_specs.py
deleted file mode 100644
index 2e993ad8a..000000000
--- a/nova/api/openstack/volume/contrib/types_extra_specs.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# 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 types extra specs extension"""
-
-import webob
-
-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
-
-
-authorize = extensions.extension_authorizer('volume', 'types_extra_specs')
-
-
-class VolumeTypeExtraSpecsTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.make_flat_dict('extra_specs', selector='extra_specs')
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypeExtraSpecTemplate(xmlutil.TemplateBuilder):
- def construct(self):
- tagname = xmlutil.Selector('key')
-
- def extraspec_sel(obj, do_raise=False):
- # Have to extract the key and value for later use...
- key, value = obj.items()[0]
- return dict(key=key, value=value)
-
- root = xmlutil.TemplateElement(tagname, selector=extraspec_sel)
- root.text = 'value'
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypeExtraSpecsController(wsgi.Controller):
- """ The volume type extra specs API controller for the OpenStack API """
-
- def _get_extra_specs(self, context, type_id):
- extra_specs = db.volume_type_extra_specs_get(context, type_id)
- specs_dict = {}
- for key, value in extra_specs.iteritems():
- specs_dict[key] = value
- return dict(extra_specs=specs_dict)
-
- def _check_type(self, context, type_id):
- try:
- volume_types.get_volume_type(context, type_id)
- except exception.NotFound as ex:
- raise webob.exc.HTTPNotFound(explanation=unicode(ex))
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate)
- def index(self, req, type_id):
- """ Returns the list of extra specs for a given volume type """
- context = req.environ['nova.context']
- authorize(context)
- self._check_type(context, type_id)
- return self._get_extra_specs(context, type_id)
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate)
- def create(self, req, type_id, body=None):
- context = req.environ['nova.context']
- authorize(context)
-
- if not self.is_valid_body(body, 'extra_specs'):
- raise webob.exc.HTTPUnprocessableEntity()
-
- self._check_type(context, type_id)
-
- specs = body['extra_specs']
- db.volume_type_extra_specs_update_or_create(context,
- type_id,
- specs)
- return body
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecTemplate)
- def update(self, req, type_id, id, body=None):
- context = req.environ['nova.context']
- authorize(context)
- if not body:
- raise webob.exc.HTTPUnprocessableEntity()
- self._check_type(context, type_id)
- if not id in body:
- expl = _('Request body and URI mismatch')
- raise webob.exc.HTTPBadRequest(explanation=expl)
- if len(body) > 1:
- expl = _('Request body contains too many items')
- raise webob.exc.HTTPBadRequest(explanation=expl)
- db.volume_type_extra_specs_update_or_create(context,
- type_id,
- body)
- return body
-
- @wsgi.serializers(xml=VolumeTypeExtraSpecTemplate)
- def show(self, req, type_id, id):
- """Return a single extra spec item."""
- context = req.environ['nova.context']
- authorize(context)
- self._check_type(context, type_id)
- specs = self._get_extra_specs(context, type_id)
- if id in specs['extra_specs']:
- return {id: specs['extra_specs'][id]}
- else:
- raise webob.exc.HTTPNotFound()
-
- def delete(self, req, type_id, id):
- """ Deletes an existing extra spec """
- context = req.environ['nova.context']
- self._check_type(context, type_id)
- authorize(context)
- db.volume_type_extra_specs_delete(context, type_id, id)
- return webob.Response(status_int=202)
-
-
-class Types_extra_specs(extensions.ExtensionDescriptor):
- """Types extra specs support"""
-
- name = "TypesExtraSpecs"
- alias = "os-types-extra-specs"
- namespace = "http://docs.openstack.org/volume/ext/types-extra-specs/api/v1"
- updated = "2011-08-24T00:00:00+00:00"
-
- def get_resources(self):
- resources = []
- res = extensions.ResourceExtension('extra_specs',
- VolumeTypeExtraSpecsController(),
- parent=dict(
- member_name='type',
- collection_name='types'))
- resources.append(res)
-
- return resources
diff --git a/nova/api/openstack/volume/contrib/types_manage.py b/nova/api/openstack/volume/contrib/types_manage.py
deleted file mode 100644
index e68093ce8..000000000
--- a/nova/api/openstack/volume/contrib/types_manage.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# 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 types manage extension."""
-
-import webob
-
-from nova.api.openstack import extensions
-from nova.api.openstack.volume import types
-from nova.api.openstack.volume.views import types as views_types
-from nova.api.openstack import wsgi
-from nova import exception
-from nova.volume import volume_types
-
-
-authorize = extensions.extension_authorizer('volume', 'types_manage')
-
-
-class VolumeTypesManageController(wsgi.Controller):
- """ The volume types API controller for the OpenStack API """
-
- _view_builder_class = views_types.ViewBuilder
-
- @wsgi.action("create")
- @wsgi.serializers(xml=types.VolumeTypeTemplate)
- def _create(self, req, body):
- """Creates a new volume type."""
- context = req.environ['nova.context']
- authorize(context)
-
- if not self.is_valid_body(body, 'volume_type'):
- raise webob.exc.HTTPUnprocessableEntity()
-
- vol_type = body['volume_type']
- name = vol_type.get('name', None)
- specs = vol_type.get('extra_specs', {})
-
- if name is None or name == "":
- raise webob.exc.HTTPUnprocessableEntity()
-
- try:
- volume_types.create(context, name, specs)
- vol_type = volume_types.get_volume_type_by_name(context, name)
- except exception.VolumeTypeExists as err:
- raise webob.exc.HTTPConflict(explanation=str(err))
- except exception.NotFound:
- raise webob.exc.HTTPNotFound()
-
- return self._view_builder.show(req, vol_type)
-
- @wsgi.action("delete")
- def _delete(self, req, id):
- """ Deletes an existing volume type """
- context = req.environ['nova.context']
- authorize(context)
-
- try:
- vol_type = volume_types.get_volume_type(context, id)
- volume_types.destroy(context, vol_type['name'])
- except exception.NotFound:
- raise webob.exc.HTTPNotFound()
-
- return webob.Response(status_int=202)
-
-
-class Types_manage(extensions.ExtensionDescriptor):
- """Types manage support"""
-
- name = "TypesManage"
- alias = "os-types-manage"
- namespace = "http://docs.openstack.org/volume/ext/types-manage/api/v1"
- updated = "2011-08-24T00:00:00+00:00"
-
- def get_controller_extensions(self):
- controller = VolumeTypesManageController()
- extension = extensions.ControllerExtension(self, 'types', controller)
- return [extension]
diff --git a/nova/api/openstack/volume/contrib/volume_actions.py b/nova/api/openstack/volume/contrib/volume_actions.py
deleted file mode 100644
index 8a453bfb1..000000000
--- a/nova/api/openstack/volume/contrib/volume_actions.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright 2012 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.
-
-import webob
-from xml.dom import minidom
-
-from nova.api.openstack import extensions
-from nova.api.openstack import wsgi
-from nova.api.openstack import xmlutil
-from nova import exception
-from nova import flags
-from nova.openstack.common import log as logging
-from nova.openstack.common.rpc import common as rpc_common
-from nova import volume
-
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger(__name__)
-
-
-def authorize(context, action_name):
- action = 'volume_actions:%s' % action_name
- extensions.extension_authorizer('volume', action)(context)
-
-
-class VolumeToImageSerializer(xmlutil.TemplateBuilder):
- def construct(self):
- root = xmlutil.TemplateElement('os-volume_upload_image',
- selector='os-volume_upload_image')
- root.set('id')
- root.set('updated_at')
- root.set('status')
- root.set('display_description')
- root.set('size')
- root.set('volume_type')
- root.set('image_id')
- root.set('container_format')
- root.set('disk_format')
- root.set('image_name')
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeToImageDeserializer(wsgi.XMLDeserializer):
- """Deserializer to handle xml-formatted requests"""
- def default(self, string):
- dom = minidom.parseString(string)
- action_node = dom.childNodes[0]
- action_name = action_node.tagName
-
- action_data = {}
- attributes = ["force", "image_name", "container_format", "disk_format"]
- for attr in attributes:
- if action_node.hasAttribute(attr):
- action_data[attr] = action_node.getAttribute(attr)
- if 'force' in action_data and action_data['force'] == 'True':
- action_data['force'] = True
- return {'body': {action_name: action_data}}
-
-
-class VolumeActionsController(wsgi.Controller):
- def __init__(self, *args, **kwargs):
- super(VolumeActionsController, self).__init__(*args, **kwargs)
- self.volume_api = volume.API()
-
- @wsgi.response(202)
- @wsgi.action('os-volume_upload_image')
- @wsgi.serializers(xml=VolumeToImageSerializer)
- @wsgi.deserializers(xml=VolumeToImageDeserializer)
- def _volume_upload_image(self, req, id, body):
- """Uploads the specified volume to image service."""
- context = req.environ['nova.context']
- try:
- params = body['os-volume_upload_image']
- except (TypeError, KeyError):
- msg = _("Invalid request body")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- if not params.get("image_name"):
- msg = _("No image_name was specified in request.")
- raise webob.exc.HTTPBadRequest(explanation=msg)
-
- force = params.get('force', False)
- try:
- volume = self.volume_api.get(context, id)
- except exception.VolumeNotFound, error:
- raise webob.exc.HTTPNotFound(explanation=unicode(error))
- authorize(context, "upload_image")
- image_metadata = {"container_format": params.get("container_format",
- "bare"),
- "disk_format": params.get("disk_format", "raw"),
- "name": params["image_name"]}
- try:
- response = self.volume_api.copy_volume_to_image(context,
- volume,
- image_metadata,
- force)
- except exception.InvalidVolume, error:
- raise webob.exc.HTTPBadRequest(explanation=unicode(error))
- except ValueError, error:
- raise webob.exc.HTTPBadRequest(explanation=unicode(error))
- except rpc_common.RemoteError as error:
- msg = "%(err_type)s: %(err_msg)s" % {'err_type': error.exc_type,
- 'err_msg': error.value}
- raise webob.exc.HTTPBadRequest(explanation=msg)
- return {'os-volume_upload_image': response}
-
-
-class Volume_actions(extensions.ExtensionDescriptor):
- """Enable volume actions
- """
-
- name = "VolumeActions"
- alias = "os-volume-actions"
- namespace = "http://docs.openstack.org/volume/ext/volume-actions/api/v1.1"
- updated = "2012-05-31T00:00:00+00:00"
-
- def get_controller_extensions(self):
- controller = VolumeActionsController()
- extension = extensions.ControllerExtension(self, 'volumes', controller)
- return [extension]
diff --git a/nova/api/openstack/volume/extensions.py b/nova/api/openstack/volume/extensions.py
deleted file mode 100644
index b21b9d145..000000000
--- a/nova/api/openstack/volume/extensions.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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.openstack.common import log as logging
-
-
-LOG = logging.getLogger(__name__)
-FLAGS = flags.FLAGS
-
-
-class ExtensionManager(base_extensions.ExtensionManager):
- def __init__(self):
- LOG.audit(_('Initializing extension manager.'))
- self.cls_list = FLAGS.osapi_volume_extension
- self.extensions = {}
- self.plugins = []
- self.sorted_ext_list = []
- self._load_extensions()
diff --git a/nova/api/openstack/volume/snapshots.py b/nova/api/openstack/volume/snapshots.py
deleted file mode 100644
index 74c5f75e6..000000000
--- a/nova/api/openstack/volume/snapshots.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# 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."""
-
-import webob
-from webob import exc
-
-from nova.api.openstack import common
-from nova.api.openstack import wsgi
-from nova.api.openstack import xmlutil
-from nova import exception
-from nova import flags
-from nova.openstack.common import log as logging
-from nova import utils
-from nova import volume
-
-
-LOG = logging.getLogger(__name__)
-
-
-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 = {}
-
- # TODO(bcwaldon): remove str cast once we use uuids
- d['id'] = str(vol['id'])
- d['volume_id'] = str(vol['volume_id'])
- d['status'] = vol['status']
- # NOTE(gagupta): We map volume_size as the snapshot size
- d['size'] = vol['volume_size']
- d['created_at'] = vol['created_at']
- d['display_name'] = vol['display_name']
- d['display_description'] = vol['display_description']
- return d
-
-
-def make_snapshot(elem):
- elem.set('id')
- elem.set('status')
- elem.set('size')
- elem.set('created_at')
- elem.set('display_name')
- elem.set('display_description')
- elem.set('volume_id')
-
-
-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 SnapshotsController(wsgi.Controller):
- """The Volumes API controller for the OpenStack API."""
-
- def __init__(self, ext_mgr=None):
- self.volume_api = volume.API()
- self.ext_mgr = ext_mgr
- super(SnapshotsController, self).__init__()
-
- @wsgi.serializers(xml=SnapshotTemplate)
- 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:
- raise 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:
- snapshot = self.volume_api.get_snapshot(context, id)
- self.volume_api.delete_snapshot(context, snapshot)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- return webob.Response(status_int=202)
-
- @wsgi.serializers(xml=SnapshotsTemplate)
- def index(self, req):
- """Returns a summary list of snapshots."""
- return self._items(req, entity_maker=_translate_snapshot_summary_view)
-
- @wsgi.serializers(xml=SnapshotsTemplate)
- 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']
-
- search_opts = {}
- search_opts.update(req.GET)
-
- snapshots = self.volume_api.get_all_snapshots(context,
- search_opts=search_opts)
- limited_list = common.limited(snapshots, req)
- res = [entity_maker(context, snapshot) for snapshot in limited_list]
- return {'snapshots': res}
-
- @wsgi.serializers(xml=SnapshotTemplate)
- def create(self, req, body):
- """Creates a new snapshot."""
- context = req.environ['nova.context']
-
- if not self.is_valid_body(body, 'snapshot'):
- raise exc.HTTPUnprocessableEntity()
-
- snapshot = body['snapshot']
- volume_id = snapshot['volume_id']
-
- try:
- volume = self.volume_api.get(context, volume_id)
- except exception.VolumeNotFound as err:
- raise exc.HTTPNotFound(explanation=unicode(err))
-
- force = snapshot.get('force', False)
- msg = _("Create snapshot from volume %s")
- LOG.audit(msg, volume_id, context=context)
-
- if not utils.is_valid_boolstr(force):
- msg = _("Invalid value '%s' for force. ") % force
- raise exception.InvalidParameterValue(err=msg)
-
- if utils.bool_from_str(force):
- new_snapshot = self.volume_api.create_snapshot_force(context,
- volume,
- snapshot.get('display_name'),
- snapshot.get('display_description'))
- else:
- new_snapshot = self.volume_api.create_snapshot(context,
- volume,
- snapshot.get('display_name'),
- snapshot.get('display_description'))
-
- retval = _translate_snapshot_detail_view(context, new_snapshot)
-
- return {'snapshot': retval}
-
-
-def create_resource(ext_mgr):
- return wsgi.Resource(SnapshotsController(ext_mgr))
diff --git a/nova/api/openstack/volume/types.py b/nova/api/openstack/volume/types.py
deleted file mode 100644
index a39d2a1f6..000000000
--- a/nova/api/openstack/volume/types.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# 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.volume.views import types as views_types
-from nova.api.openstack import wsgi
-from nova.api.openstack import xmlutil
-from nova import exception
-from nova.volume import volume_types
-
-
-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')
- elem = xmlutil.SubTemplateElement(root, 'volume_type',
- selector='volume_types')
- make_voltype(elem)
- return xmlutil.MasterTemplate(root, 1)
-
-
-class VolumeTypesController(wsgi.Controller):
- """ The volume types API controller for the OpenStack API """
-
- _view_builder_class = views_types.ViewBuilder
-
- @wsgi.serializers(xml=VolumeTypesTemplate)
- def index(self, req):
- """ Returns the list of volume types """
- context = req.environ['nova.context']
- vol_types = volume_types.get_all_types(context).values()
- return self._view_builder.index(req, vol_types)
-
- @wsgi.serializers(xml=VolumeTypeTemplate)
- 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:
- raise exc.HTTPNotFound()
-
- # TODO(bcwaldon): remove str cast once we use uuids
- vol_type['id'] = str(vol_type['id'])
- return self._view_builder.show(req, vol_type)
-
-
-def create_resource():
- return wsgi.Resource(VolumeTypesController())
diff --git a/nova/api/openstack/volume/versions.py b/nova/api/openstack/volume/versions.py
deleted file mode 100644
index 68d34b1f9..000000000
--- a/nova/api/openstack/volume/versions.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# 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 nova.api.openstack.compute import versions
-from nova.api.openstack.volume.views import versions as views_versions
-from nova.api.openstack import wsgi
-
-
-VERSIONS = {
- "v1.0": {
- "id": "v1.0",
- "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):
- @wsgi.serializers(xml=versions.VersionsTemplate,
- atom=versions.VersionsAtomSerializer)
- def index(self, req):
- """Return all versions."""
- builder = views_versions.get_view_builder(req)
- return builder.build_versions(VERSIONS)
-
- @wsgi.serializers(xml=versions.ChoicesTemplate)
- @wsgi.response(300)
- def multi(self, req):
- """Return multiple choices."""
- builder = views_versions.get_view_builder(req)
- return builder.build_choices(VERSIONS, req)
-
-
-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['v1.0'])
-
-
-def create_resource():
- return wsgi.Resource(VolumeVersionV1())
diff --git a/nova/api/openstack/volume/views/__init__.py b/nova/api/openstack/volume/views/__init__.py
deleted file mode 100644
index d65c689a8..000000000
--- a/nova/api/openstack/volume/views/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
diff --git a/nova/api/openstack/volume/views/types.py b/nova/api/openstack/volume/views/types.py
deleted file mode 100644
index 8274a0c6f..000000000
--- a/nova/api/openstack/volume/views/types.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 Red Hat, Inc.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from nova.api.openstack import common
-
-
-class ViewBuilder(common.ViewBuilder):
-
- def show(self, request, volume_type, brief=False):
- """Trim away extraneous volume type attributes."""
- trimmed = dict(id=volume_type.get('id'),
- name=volume_type.get('name'),
- extra_specs=volume_type.get('extra_specs'))
- return trimmed if brief else dict(volume_type=trimmed)
-
- def index(self, request, volume_types):
- """Index over trimmed volume types"""
- volume_types_list = [self.show(request, volume_type, True)
- for volume_type in volume_types]
- return dict(volume_types=volume_types_list)
diff --git a/nova/api/openstack/volume/views/versions.py b/nova/api/openstack/volume/views/versions.py
deleted file mode 100644
index 2e659af6a..000000000
--- a/nova/api/openstack/volume/views/versions.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2010-2011 OpenStack LLC.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import os
-
-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) + '/'
diff --git a/nova/api/openstack/volume/volumes.py b/nova/api/openstack/volume/volumes.py
deleted file mode 100644
index e13f04036..000000000
--- a/nova/api/openstack/volume/volumes.py
+++ /dev/null
@@ -1,364 +0,0 @@
-# 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."""
-
-import webob
-from webob import exc
-from xml.dom import minidom
-
-from nova.api.openstack import common
-from nova.api.openstack import wsgi
-from nova.api.openstack import xmlutil
-from nova import exception
-from nova import flags
-from nova.openstack.common import log as logging
-from nova import utils
-from nova import volume
-from nova.volume import volume_types
-
-
-LOG = logging.getLogger(__name__)
-
-
-FLAGS = flags.FLAGS
-
-
-def _translate_attachment_detail_view(_context, vol):
- """Maps keys for attachment details view."""
-
- d = _translate_attachment_summary_view(_context, vol)
-
- # No additional data / lookups at the moment
-
- return d
-
-
-def _translate_attachment_summary_view(_context, vol):
- """Maps keys for attachment summary view."""
- d = {}
-
- volume_id = vol['id']
-
- # NOTE(justinsb): We use the volume id as the id of the attachment object
- d['id'] = volume_id
-
- d['volume_id'] = volume_id
- d['server_id'] = vol['instance_uuid']
- if vol.get('mountpoint'):
- d['device'] = vol['mountpoint']
-
- return d
-
-
-def _translate_volume_detail_view(context, vol, image_id=None):
- """Maps keys for volumes details view."""
-
- d = _translate_volume_summary_view(context, vol, image_id)
-
- # No additional data / lookups at the moment
-
- return d
-
-
-def _translate_volume_summary_view(context, vol, image_id=None):
- """Maps keys for volumes summary view."""
- d = {}
-
- d['id'] = vol['id']
- d['status'] = vol['status']
- d['size'] = vol['size']
- d['availability_zone'] = vol['availability_zone']
- d['created_at'] = vol['created_at']
-
- d['attachments'] = []
- if vol['attach_status'] == 'attached':
- attachment = _translate_attachment_detail_view(context, vol)
- d['attachments'].append(attachment)
-
- d['display_name'] = vol['display_name']
- d['display_description'] = vol['display_description']
-
- if vol['volume_type_id'] and vol.get('volume_type'):
- d['volume_type'] = vol['volume_type']['name']
- else:
- # TODO(bcwaldon): remove str cast once we use uuids
- d['volume_type'] = str(vol['volume_type_id'])
-
- d['snapshot_id'] = vol['snapshot_id']
-
- if image_id:
- d['image_id'] = image_id
-
- LOG.audit(_("vol=%s"), vol, context=context)
-
- if vol.get('volume_metadata'):
- metadata = vol.get('volume_metadata')
- d['metadata'] = dict((item['key'], item['value']) for item in metadata)
- else:
- d['metadata'] = {}
-
- return d
-
-
-def make_attachment(elem):
- elem.set('id')
- elem.set('server_id')
- elem.set('volume_id')
- elem.set('device')
-
-
-def make_volume(elem):
- elem.set('id')
- elem.set('status')
- elem.set('size')
- elem.set('availability_zone')
- elem.set('created_at')
- elem.set('display_name')
- elem.set('display_description')
- elem.set('volume_type')
- elem.set('snapshot_id')
-
- attachments = xmlutil.SubTemplateElement(elem, 'attachments')
- attachment = xmlutil.SubTemplateElement(attachments, 'attachment',
- selector='attachments')
- make_attachment(attachment)
-
- # Attach metadata node
- elem.append(common.MetadataTemplate())
-
-
-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 CommonDeserializer(wsgi.MetadataXMLDeserializer):
- """Common deserializer to handle xml-formatted volume requests.
-
- Handles standard volume attributes as well as the optional metadata
- attribute
- """
-
- metadata_deserializer = common.MetadataXMLDeserializer()
-
- def _extract_volume(self, node):
- """Marshal the volume attribute of a parsed request."""
- volume = {}
- volume_node = self.find_first_child_named(node, 'volume')
-
- attributes = ['display_name', 'display_description', 'size',
- 'volume_type', 'availability_zone']
- for attr in attributes:
- if volume_node.getAttribute(attr):
- volume[attr] = volume_node.getAttribute(attr)
-
- metadata_node = self.find_first_child_named(volume_node, 'metadata')
- if metadata_node is not None:
- volume['metadata'] = self.extract_metadata(metadata_node)
-
- return volume
-
-
-class CreateDeserializer(CommonDeserializer):
- """Deserializer to handle xml-formatted create volume requests.
-
- Handles standard volume attributes as well as the optional metadata
- attribute
- """
-
- def default(self, string):
- """Deserialize an xml-formatted volume create request."""
- dom = minidom.parseString(string)
- volume = self._extract_volume(dom)
- return {'body': {'volume': volume}}
-
-
-class VolumeController(wsgi.Controller):
- """The Volumes API controller for the OpenStack API."""
-
- def __init__(self, ext_mgr):
- self.volume_api = volume.API()
- self.ext_mgr = ext_mgr
- super(VolumeController, self).__init__()
-
- @wsgi.serializers(xml=VolumeTemplate)
- 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:
- volume = self.volume_api.get(context, id)
- self.volume_api.delete(context, volume)
- except exception.NotFound:
- raise exc.HTTPNotFound()
- return webob.Response(status_int=202)
-
- @wsgi.serializers(xml=VolumesTemplate)
- def index(self, req):
- """Returns a summary list of volumes."""
- return self._items(req, entity_maker=_translate_volume_summary_view)
-
- @wsgi.serializers(xml=VolumesTemplate)
- 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."""
-
- search_opts = {}
- search_opts.update(req.GET)
-
- context = req.environ['nova.context']
- remove_invalid_options(context,
- search_opts, self._get_volume_search_options())
-
- volumes = self.volume_api.get_all(context, search_opts=search_opts)
- limited_list = common.limited(volumes, req)
- res = [entity_maker(context, vol) for vol in limited_list]
- return {'volumes': res}
-
- def _image_uuid_from_href(self, image_href):
- # If the image href was generated by nova api, strip image_href
- # down to an id.
- try:
- image_uuid = image_href.split('/').pop()
- except (TypeError, AttributeError):
- msg = _("Invalid imageRef provided.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- if not utils.is_uuid_like(image_uuid):
- msg = _("Invalid imageRef provided.")
- raise exc.HTTPBadRequest(explanation=msg)
-
- return image_uuid
-
- @wsgi.serializers(xml=VolumeTemplate)
- @wsgi.deserializers(xml=CreateDeserializer)
- def create(self, req, body):
- """Creates a new volume."""
- if not self.is_valid_body(body, 'volume'):
- msg = _("Invalid request body. 'volume' not found")
- raise exc.HTTPUnprocessableEntity(explanation=msg)
-
- context = req.environ['nova.context']
- volume = body['volume']
-
- kwargs = {}
-
- req_volume_type = volume.get('volume_type', None)
- if req_volume_type:
- try:
- kwargs['volume_type'] = volume_types.get_volume_type_by_name(
- context, req_volume_type)
- except exception.NotFound:
- raise exc.HTTPNotFound()
-
- kwargs['metadata'] = volume.get('metadata', None)
-
- snapshot_id = volume.get('snapshot_id')
- if snapshot_id is not None:
- kwargs['snapshot'] = self.volume_api.get_snapshot(context,
- snapshot_id)
- else:
- kwargs['snapshot'] = None
-
- size = volume.get('size', None)
- if size is None and kwargs['snapshot'] is not None:
- size = kwargs['snapshot']['volume_size']
-
- if size is None:
- msg = _("Invalid request body. 'size' not found")
- raise exc.HTTPUnprocessableEntity(explanation=msg)
-
- LOG.audit(_("Create volume of %s GB"), size, context=context)
-
- image_href = None
- image_uuid = None
- if self.ext_mgr.is_loaded('os-image-create'):
- image_href = volume.get('imageRef')
- if snapshot_id and image_href:
- msg = _("Snapshot and image cannot be specified together.")
- raise exc.HTTPBadRequest(explanation=msg)
- if image_href:
- image_uuid = self._image_uuid_from_href(image_href)
- kwargs['image_id'] = image_uuid
-
- kwargs['availability_zone'] = volume.get('availability_zone', None)
-
- new_volume = self.volume_api.create(context,
- size,
- volume.get('display_name'),
- volume.get('display_description'),
- **kwargs)
-
- # TODO(vish): Instance should be None at db layer instead of
- # trying to lazy load, but for now we turn it into
- # a dict to avoid an error.
- retval = _translate_volume_detail_view(context, dict(new_volume),
- image_uuid)
-
- result = {'volume': retval}
-
- location = '%s/%s' % (req.url, new_volume['id'])
-
- return wsgi.ResponseObject(result, headers=dict(location=location))
-
- def _get_volume_search_options(self):
- """Return volume search options allowed by non-admin."""
- return ('name', 'status')
-
-
-def create_resource(ext_mgr):
- return wsgi.Resource(VolumeController(ext_mgr))
-
-
-def remove_invalid_options(context, search_options, allowed_search_options):
- """Remove search options that are not valid for non-admin API/context."""
- if context.is_admin:
- # Allow all options
- return
- # Otherwise, strip out all unknown options
- unknown_options = [opt for opt in search_options
- if opt not in allowed_search_options]
- bad_options = ", ".join(unknown_options)
- log_msg = _("Removing options '%(bad_options)s' from query") % locals()
- LOG.debug(log_msg)
- for opt in unknown_options:
- search_options.pop(opt, None)