From 29940dd27f3a40a4ad54bc2f7a4cea5ac2226b83 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Tue, 23 Aug 2011 20:22:27 -0700 Subject: added volume metadata APIs (OS & volume layers), search volume by metadata & other --- nova/api/openstack/contrib/volumes.py | 36 +++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py index 867fe301e..d62225e58 100644 --- a/nova/api/openstack/contrib/volumes.py +++ b/nova/api/openstack/contrib/volumes.py @@ -24,6 +24,7 @@ from nova import flags from nova import log as logging from nova import quota from nova import volume +from nova.volume import volume_types from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import faults @@ -63,6 +64,22 @@ def _translate_volume_summary_view(context, vol): 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'] + + 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 @@ -80,6 +97,8 @@ class VolumeController(object): "createdAt", "displayName", "displayDescription", + "volumeType", + "metadata", ]}}} def __init__(self): @@ -136,12 +155,25 @@ class VolumeController(object): 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: + return faults.Fault(exc.HTTPNotFound()) + + metadata = vol.get('metadata', None) + new_volume = self.volume_api.create(context, size, None, vol.get('display_name'), - vol.get('display_description')) + vol.get('display_description'), + volume_type=vol_type, + metadata=metadata) # Work around problem that instance is lazy-loaded... - new_volume['instance'] = None + new_volume = self.volume_api.get(context, new_volume['id']) retval = _translate_volume_detail_view(context, new_volume) -- cgit From 576dd4a0dce66c7949a1f66a6979d9e1d11916bf Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Wed, 24 Aug 2011 14:07:17 -0700 Subject: added Openstack APIs for volume types & extradata --- nova/api/openstack/contrib/volumetypes.py | 197 ++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 nova/api/openstack/contrib/volumetypes.py (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/volumetypes.py b/nova/api/openstack/contrib/volumetypes.py new file mode 100644 index 000000000..ed33a8819 --- /dev/null +++ b/nova/api/openstack/contrib/volumetypes.py @@ -0,0 +1,197 @@ +# 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 import db +from nova import exception +from nova import quota +from nova.volume import volume_types +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.api.openstack import wsgi + + +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 create(self, req, body): + """Creates a new volume type.""" + context = req.environ['nova.context'] + + if not body or body == "": + return faults.Fault(exc.HTTPUnprocessableEntity()) + + vol_type = body.get('volume_type', None) + if vol_type is None or vol_type == "": + return faults.Fault(exc.HTTPUnprocessableEntity()) + + name = vol_type.get('name', None) + specs = vol_type.get('extra_specs', {}) + + if name is None or name == "": + return faults.Fault(exc.HTTPUnprocessableEntity()) + + try: + volume_types.create(context, name, specs) + vol_type = volume_types.get_volume_type_by_name(context, name) + except quota.QuotaError as error: + self._handle_quota_error(error) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + return {'volume_type': vol_type} + + 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: + return faults.Fault(exc.HTTPNotFound()) + + return {'volume_type': vol_type} + + def delete(self, req, id): + """ Deletes an existing volume type """ + context = req.environ['nova.context'] + + try: + vol_type = volume_types.get_volume_type(context, id) + volume_types.destroy(context, vol_type['name']) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + def _handle_quota_error(self, error): + """Reraise quota errors as api-specific http exceptions.""" + if error.code == "MetadataLimitExceeded": + raise exc.HTTPBadRequest(explanation=error.message) + raise error + + +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.api.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 == None or body == "": + expl = _('No Request Body') + raise exc.HTTPBadRequest(explanation=expl) + + def index(self, req, vol_type_id): + """ Returns the list of extra specs for a given volume type """ + context = req.environ['nova.context'] + return self._get_extra_specs(context, vol_type_id) + + def create(self, req, vol_type_id, body): + self._check_body(body) + context = req.environ['nova.context'] + specs = body.get('extra_specs') + try: + db.api.volume_type_extra_specs_update_or_create(context, + vol_type_id, + specs) + except quota.QuotaError as error: + self._handle_quota_error(error) + return body + + def update(self, req, vol_type_id, id, body): + self._check_body(body) + context = req.environ['nova.context'] + 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) + try: + db.api.volume_type_extra_specs_update_or_create(context, + vol_type_id, + body) + except quota.QuotaError as error: + self._handle_quota_error(error) + + return body + + def show(self, req, vol_type_id, id): + """ Return a single extra spec item """ + context = req.environ['nova.context'] + specs = self._get_extra_specs(context, vol_type_id) + if id in specs['extra_specs']: + return {id: specs['extra_specs'][id]} + else: + return faults.Fault(exc.HTTPNotFound()) + + def delete(self, req, vol_type_id, id): + """ Deletes an existing extra spec """ + context = req.environ['nova.context'] + db.api.volume_type_extra_specs_delete(context, vol_type_id, id) + + def _handle_quota_error(self, error): + """Reraise quota errors as api-specific http exceptions.""" + if error.code == "MetadataLimitExceeded": + raise exc.HTTPBadRequest(explanation=error.message) + raise error + + +class Volumetypes(extensions.ExtensionDescriptor): + + def get_name(self): + return "VolumeTypes" + + def get_alias(self): + return "os-volume-types" + + def get_description(self): + return "Volume types support" + + def get_namespace(self): + return \ + "http://docs.openstack.org/ext/volume_types/api/v1.1" + + def get_updated(self): + return "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 -- cgit