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

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. 

12 

13from webob import exc 

14 

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 

21 

22 

23class MetadataController(object): 

24 """An abstract metadata controller resource.""" 

25 

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 } 

33 

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 } 

40 

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 } 

47 

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 } 

54 

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 } 

61 

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 } 

68 

69 resource_policy_get = { 

70 'share': 'get', 

71 'share_snapshot': 'get_snapshot', 

72 'share_network_subnet': 'show', 

73 } 

74 

75 def __init__(self): 

76 super(MetadataController, self).__init__() 

77 self.resource_name = None 

78 

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) 

97 

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 

121 

122 def _get_metadata(self, context, resource_id, parent_id=None): 

123 

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]) 

127 

128 result = get_metadata_method(context, resource_id) 

129 

130 return result 

131 

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) 

138 

139 return {'metadata': metadata} 

140 

141 @wsgi.response(200) 

142 def _create_metadata(self, req, resource_id, body, parent_id=None): 

143 """Returns the new metadata item created.""" 

144 

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) 

156 

157 self._get_resource(context, resource_id, 

158 for_modification=True, parent_id=parent_id) 

159 

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') 

164 

165 return {'metadata': result} 

166 

167 def _update_metadata_item(self, req, resource_id, body, key, 

168 parent_id=None): 

169 """Updates the specified metadata item.""" 

170 

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) 

182 

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) 

191 

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) 

195 

196 return {'metadata': result} 

197 

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.""" 

201 

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) 

213 

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) 

218 

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) 

223 

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} 

229 

230 @wsgi.response(200) 

231 def _show_metadata(self, req, resource_id, key, parent_id=None): 

232 """Return metadata item.""" 

233 

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) 

240 

241 return {'meta': {key: item[key]}} 

242 

243 @wsgi.response(200) 

244 def _delete_metadata(self, req, resource_id, key, parent_id=None): 

245 """Deletes existing metadata item.""" 

246 

247 context = req.environ['manila.context'] 

248 self._get_resource(context, resource_id, 

249 for_modification=True, parent_id=parent_id) 

250 

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) 

254 

255 delete_metadata_method = getattr( 

256 db, self.resource_metadata_delete[self.resource_name]) 

257 delete_metadata_method(context, resource_id, key)