1# Copyright 2013 OpenStack Foundation. 2# All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16from six.moves import http_client 17import webob 18 19from cinder.api import common 20from cinder.api.openstack import wsgi 21from cinder.api.schemas import volume_metadata as metadata 22from cinder.api import validation 23from cinder import exception 24from cinder.i18n import _ 25from cinder import volume 26 27 28class Controller(wsgi.Controller): 29 """The volume metadata API controller for the OpenStack API.""" 30 31 def __init__(self): 32 self.volume_api = volume.API() 33 super(Controller, self).__init__() 34 35 def _get_metadata(self, context, volume_id): 36 # The metadata is at the second position of the tuple returned 37 # from _get_volume_and_metadata 38 return self._get_volume_and_metadata(context, volume_id)[1] 39 40 def _get_volume_and_metadata(self, context, volume_id): 41 # Not found exception will be handled at the wsgi level 42 volume = self.volume_api.get(context, volume_id) 43 meta = self.volume_api.get_volume_metadata(context, volume) 44 return (volume, meta) 45 46 def index(self, req, volume_id): 47 """Returns the list of metadata for a given volume.""" 48 context = req.environ['cinder.context'] 49 return {'metadata': self._get_metadata(context, volume_id)} 50 51 @validation.schema(metadata.create) 52 def create(self, req, volume_id, body): 53 context = req.environ['cinder.context'] 54 metadata = body['metadata'] 55 56 new_metadata = self._update_volume_metadata(context, volume_id, 57 metadata, delete=False, 58 use_create=True) 59 return {'metadata': new_metadata} 60 61 @validation.schema(metadata.update) 62 def update(self, req, volume_id, id, body): 63 meta_item = body['meta'] 64 65 if id not in meta_item: 66 expl = _('Request body and URI mismatch') 67 raise webob.exc.HTTPBadRequest(explanation=expl) 68 69 if len(meta_item) > 1: 70 expl = _('Request body contains too many items') 71 raise webob.exc.HTTPBadRequest(explanation=expl) 72 73 context = req.environ['cinder.context'] 74 self._update_volume_metadata(context, 75 volume_id, 76 meta_item, 77 delete=False) 78 79 return {'meta': meta_item} 80 81 @validation.schema(metadata.create) 82 def update_all(self, req, volume_id, body): 83 metadata = body['metadata'] 84 context = req.environ['cinder.context'] 85 86 new_metadata = self._update_volume_metadata(context, 87 volume_id, 88 metadata, 89 delete=True) 90 91 return {'metadata': new_metadata} 92 93 def _update_volume_metadata(self, context, volume_id, metadata, 94 delete=False, use_create=False): 95 try: 96 volume = self.volume_api.get(context, volume_id) 97 if use_create: 98 return self.volume_api.create_volume_metadata(context, volume, 99 metadata) 100 else: 101 return self.volume_api.update_volume_metadata( 102 context, volume, metadata, delete, 103 meta_type=common.METADATA_TYPES.user) 104 # Not found exception will be handled at the wsgi level 105 except (ValueError, AttributeError): 106 msg = _("Malformed request body") 107 raise webob.exc.HTTPBadRequest(explanation=msg) 108 109 except exception.InvalidVolumeMetadata as error: 110 raise webob.exc.HTTPBadRequest(explanation=error.msg) 111 112 except exception.InvalidVolumeMetadataSize as error: 113 raise webob.exc.HTTPRequestEntityTooLarge(explanation=error.msg) 114 115 def show(self, req, volume_id, id): 116 """Return a single metadata item.""" 117 context = req.environ['cinder.context'] 118 data = self._get_metadata(context, volume_id) 119 120 try: 121 return {'meta': {id: data[id]}} 122 except KeyError: 123 raise exception.VolumeMetadataNotFound(volume_id=volume_id, 124 metadata_key=id) 125 126 def delete(self, req, volume_id, id): 127 """Deletes an existing metadata.""" 128 context = req.environ['cinder.context'] 129 130 volume, metadata = self._get_volume_and_metadata(context, volume_id) 131 132 if id not in metadata: 133 raise exception.VolumeMetadataNotFound(volume_id=volume_id, 134 metadata_key=id) 135 136 # Not found exception will be handled at the wsgi level 137 self.volume_api.delete_volume_metadata( 138 context, 139 volume, 140 id, 141 meta_type=common.METADATA_TYPES.user) 142 return webob.Response(status_int=http_client.OK) 143 144 145def create_resource(): 146 return wsgi.Resource(Controller()) 147