Coverage for manila/api/v2/metadata.py: 80%
124 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
13from webob import exc
15from manila.api import common
16from manila.api.openstack import wsgi
17from manila import db
18from manila import exception
19from manila.i18n import _
20from manila import policy
23class MetadataController(object):
24 """An abstract metadata controller resource."""
26 # From db, ensure it exists
27 resource_get = {
28 "share": "share_get",
29 "share_snapshot": "share_snapshot_get",
30 "share_network_subnet": "share_network_subnet_get",
31 "share_export_location": "export_location_get_by_uuid",
32 }
34 resource_metadata_get = {
35 "share": "share_metadata_get",
36 "share_snapshot": "share_snapshot_metadata_get",
37 "share_network_subnet": "share_network_subnet_metadata_get",
38 "share_export_location": "export_location_metadata_get",
39 }
41 resource_metadata_get_item = {
42 "share": "share_metadata_get_item",
43 "share_snapshot": "share_snapshot_metadata_get_item",
44 "share_network_subnet": "share_network_subnet_metadata_get_item",
45 "share_export_location": "export_location_metadata_get_item",
46 }
48 resource_metadata_update = {
49 "share": "share_metadata_update",
50 "share_snapshot": "share_snapshot_metadata_update",
51 "share_network_subnet": "share_network_subnet_metadata_update",
52 "share_export_location": "export_location_metadata_update",
53 }
55 resource_metadata_update_item = {
56 "share": "share_metadata_update_item",
57 "share_snapshot": "share_snapshot_metadata_update_item",
58 "share_network_subnet": "share_network_subnet_metadata_update_item",
59 "share_export_location": "export_location_metadata_update_item",
60 }
62 resource_metadata_delete = {
63 "share": "share_metadata_delete",
64 "share_snapshot": "share_snapshot_metadata_delete",
65 "share_network_subnet": "share_network_subnet_metadata_delete",
66 "share_export_location": "export_location_metadata_delete",
67 }
69 resource_policy_get = {
70 'share': 'get',
71 'share_snapshot': 'get_snapshot',
72 'share_network_subnet': 'show',
73 }
75 def __init__(self):
76 super(MetadataController, self).__init__()
77 self.resource_name = None
79 def _get_resource(self, context, resource_id,
80 for_modification=False, parent_id=None):
81 if self.resource_name in ['share', 'share_network_subnet', 81 ↛ 90line 81 didn't jump to line 90 because the condition on line 81 was always true
82 'share_export_location']:
83 # some resources don't have a "project_id" field (like
84 # share_export_location or share_network_subnet),
85 # and sometimes we want to retrieve "public" resources
86 # (like shares), so avoid hard coding project_only=True in the
87 # lookup where necessary
88 kwargs = {}
89 else:
90 kwargs = {'project_only': True}
91 try:
92 get_res_method = getattr(
93 db, self.resource_get[self.resource_name])
94 if parent_id is not None: 94 ↛ 95line 94 didn't jump to line 95 because the condition on line 94 was never true
95 kwargs["parent_id"] = parent_id
96 res = get_res_method(context, resource_id, **kwargs)
98 if self.resource_name not in ["share_export_location"]: 98 ↛ 120line 98 didn't jump to line 120 because the condition on line 98 was always true
99 get_policy = self.resource_policy_get[self.resource_name]
100 # skip policy check for export locations
101 if res.get('is_public') is False:
102 authorized = policy.check_policy(context,
103 self.resource_name,
104 get_policy,
105 res,
106 do_raise=False)
107 if not authorized:
108 # Raising NotFound to prevent existence detection
109 raise exception.NotFound()
110 elif for_modification: 110 ↛ 120line 110 didn't jump to line 120 because the condition on line 110 was always true
111 # a public resource's metadata can be viewed, but not
112 # modified by non owners
113 policy.check_policy(context,
114 self.resource_name,
115 get_policy,
116 res)
117 except exception.NotFound:
118 msg = _('%s not found.' % self.resource_name.capitalize())
119 raise exc.HTTPNotFound(explanation=msg)
120 return res
122 def _get_metadata(self, context, resource_id, parent_id=None):
124 self._get_resource(context, resource_id, parent_id=parent_id)
125 get_metadata_method = getattr(
126 db, self.resource_metadata_get[self.resource_name])
128 result = get_metadata_method(context, resource_id)
130 return result
132 @wsgi.response(200)
133 def _index_metadata(self, req, resource_id, parent_id=None):
134 """Lists existing metadata."""
135 context = req.environ['manila.context']
136 metadata = self._get_metadata(context, resource_id,
137 parent_id=parent_id)
139 return {'metadata': metadata}
141 @wsgi.response(200)
142 def _create_metadata(self, req, resource_id, body, parent_id=None):
143 """Returns the new metadata item created."""
145 context = req.environ['manila.context']
146 try:
147 metadata = body['metadata']
148 common.check_metadata_properties(metadata)
149 except (KeyError, TypeError):
150 msg = _("Malformed request body")
151 raise exc.HTTPBadRequest(explanation=msg)
152 except exception.InvalidMetadata as error:
153 raise exc.HTTPBadRequest(explanation=error.msg)
154 except exception.InvalidMetadataSize as error:
155 raise exc.HTTPBadRequest(explanation=error.msg)
157 self._get_resource(context, resource_id,
158 for_modification=True, parent_id=parent_id)
160 create_metadata_method = getattr(
161 db, self.resource_metadata_update[self.resource_name])
162 result = create_metadata_method(context, resource_id, metadata,
163 delete='False')
165 return {'metadata': result}
167 def _update_metadata_item(self, req, resource_id, body, key,
168 parent_id=None):
169 """Updates the specified metadata item."""
171 context = req.environ['manila.context']
172 try:
173 meta_item = body['metadata']
174 common.check_metadata_properties(meta_item)
175 except (TypeError, KeyError):
176 expl = _('Malformed request body')
177 raise exc.HTTPBadRequest(explanation=expl)
178 except exception.InvalidMetadata as error:
179 raise exc.HTTPBadRequest(explanation=error.msg)
180 except exception.InvalidMetadataSize as error:
181 raise exc.HTTPBadRequest(explanation=error.msg)
183 if key not in meta_item: 183 ↛ 184line 183 didn't jump to line 184 because the condition on line 183 was never true
184 expl = _('Request body and URI mismatch')
185 raise exc.HTTPBadRequest(explanation=expl)
186 if len(meta_item) > 1: 186 ↛ 187line 186 didn't jump to line 187 because the condition on line 186 was never true
187 expl = _('Request body contains too many items')
188 raise exc.HTTPBadRequest(explanation=expl)
189 self._get_resource(context, resource_id,
190 for_modification=True, parent_id=parent_id)
192 update_metadata_item_method = getattr(
193 db, self.resource_metadata_update_item[self.resource_name])
194 result = update_metadata_item_method(context, resource_id, meta_item)
196 return {'metadata': result}
198 @wsgi.response(200)
199 def _update_all_metadata(self, req, resource_id, body, parent_id=None):
200 """Deletes existing metadata, and returns the updated metadata."""
202 context = req.environ['manila.context']
203 try:
204 metadata = body['metadata']
205 common.check_metadata_properties(metadata)
206 except (TypeError, KeyError):
207 expl = _('Malformed request body')
208 raise exc.HTTPBadRequest(explanation=expl)
209 except exception.InvalidMetadata as error:
210 raise exc.HTTPBadRequest(explanation=error.msg)
211 except exception.InvalidMetadataSize as error:
212 raise exc.HTTPBadRequest(explanation=error.msg)
214 self._get_resource(context, resource_id,
215 for_modification=True, parent_id=parent_id)
216 meta_ref = self._get_metadata(context, resource_id,
217 parent_id=parent_id)
219 for key in meta_ref: 219 ↛ 220line 219 didn't jump to line 220 because the loop on line 219 never started
220 delete_metadata_method = getattr(
221 db, self.resource_metadata_delete[self.resource_name])
222 delete_metadata_method(context, resource_id, key)
224 update_metadata_method = getattr(
225 db, self.resource_metadata_update[self.resource_name])
226 new_metadata = update_metadata_method(context, resource_id,
227 metadata, delete='False')
228 return {'metadata': new_metadata}
230 @wsgi.response(200)
231 def _show_metadata(self, req, resource_id, key, parent_id=None):
232 """Return metadata item."""
234 context = req.environ['manila.context']
235 self._get_resource(context, resource_id,
236 for_modification=False, parent_id=parent_id)
237 get_metadata_item_method = getattr(
238 db, self.resource_metadata_get_item[self.resource_name])
239 item = get_metadata_item_method(context, resource_id, key)
241 return {'meta': {key: item[key]}}
243 @wsgi.response(200)
244 def _delete_metadata(self, req, resource_id, key, parent_id=None):
245 """Deletes existing metadata item."""
247 context = req.environ['manila.context']
248 self._get_resource(context, resource_id,
249 for_modification=True, parent_id=parent_id)
251 get_metadata_item_method = getattr(
252 db, self.resource_metadata_get_item[self.resource_name])
253 get_metadata_item_method(context, resource_id, key)
255 delete_metadata_method = getattr(
256 db, self.resource_metadata_delete[self.resource_name])
257 delete_metadata_method(context, resource_id, key)