Coverage for manila/api/v2/resource_locks.py: 91%

130 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 

13"""The resource_locks API controller module. 

14 

15This module handles the following requests: 

16GET /resource-locks 

17GET /resource-locks/{lock_id} 

18POST /resource-locks 

19PUT /resource-locks/{lock_id} 

20DELETE /resource-locks/{lock_id} 

21""" 

22 

23from http import client as http_client 

24 

25from oslo_utils import timeutils 

26from oslo_utils import uuidutils 

27import webob 

28from webob import exc 

29 

30from manila.api import common 

31from manila.api.openstack import wsgi 

32from manila.api.schemas import resource_locks as schema 

33from manila.api import validation 

34from manila.api.views import resource_locks as resource_locks_view 

35from manila.common import constants 

36from manila import exception 

37from manila.i18n import _ 

38from manila.lock import api as resource_locks 

39from manila import utils 

40 

41RESOURCE_LOCKS_MIN_API_VERSION = '2.81' 

42 

43 

44@validation.validated 

45class ResourceLocksController(wsgi.Controller): 

46 """The Resource Locks API controller for the OpenStack API.""" 

47 

48 _view_builder_class = resource_locks_view.ViewBuilder 

49 resource_name = 'resource_lock' 

50 

51 def _check_body(self, body, lock_to_update=None): 

52 if 'resource_lock' not in body: 

53 raise exc.HTTPBadRequest( 

54 explanation="Malformed request body.") 

55 lock_data = body['resource_lock'] 

56 resource_type = ( 

57 lock_to_update['resource_type'] 

58 if lock_to_update 

59 else lock_data.get('resource_type', constants.SHARE_RESOURCE_TYPE) 

60 ) 

61 resource_id = lock_data.get('resource_id') or '' 

62 resource_action = (lock_data.get('resource_action') or 

63 constants.RESOURCE_ACTION_DELETE) 

64 lock_reason = lock_data.get('lock_reason') or '' 

65 

66 if len(lock_reason) > 1023: 

67 msg = _("'lock_reason' can contain a maximum of 1023 characters.") 

68 raise exc.HTTPBadRequest(explanation=msg) 

69 if resource_type not in constants.RESOURCE_LOCK_RESOURCE_TYPES: 

70 msg = _("'resource_type' is required and must be one " 

71 "of %(resource_types)s") % { 

72 'resource_types': constants.RESOURCE_LOCK_RESOURCE_TYPES 

73 } 

74 raise exc.HTTPBadRequest(explanation=msg) 

75 resource_type_lock_actions = ( 

76 constants.RESOURCE_LOCK_ACTIONS_MAPPING[resource_type]) 

77 if resource_action not in resource_type_lock_actions: 

78 msg = _("'resource_action' can only be one of %(actions)s" % 

79 {'actions': resource_type_lock_actions}) 

80 raise exc.HTTPBadRequest(explanation=msg) 

81 

82 if lock_to_update: 

83 if set(lock_data.keys()) - {'resource_action', 'lock_reason'}: 

84 msg = _("Only 'resource_action' and 'lock_reason' " 

85 "can be updated.") 

86 raise exc.HTTPBadRequest(explanation=msg) 

87 else: 

88 if not uuidutils.is_uuid_like(resource_id): 

89 msg = _("Resource ID is required and must be in uuid format.") 

90 raise exc.HTTPBadRequest(explanation=msg) 

91 

92 def __init__(self): 

93 self.resource_locks_api = resource_locks.API() 

94 super(ResourceLocksController, self).__init__() 

95 

96 @wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION) 

97 @wsgi.Controller.authorize('get_all') 

98 @validation.request_query_schema(schema.index_request_query) 

99 @validation.response_body_schema(schema.index_response_body) 

100 def index(self, req): 

101 """Returns a list of locks, transformed through view builder.""" 

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

103 filters = req.params.copy() 

104 

105 params = common.get_pagination_params(req) 

106 limit, offset = [params.pop('limit', None), params.pop('offset', None)] 

107 sort_key, sort_dir = common.get_sort_params(filters) 

108 for key in ('limit', 'offset'): 

109 filters.pop(key, None) 

110 

111 show_count = utils.get_bool_from_api_params( 

112 'with_count', {'with_count': filters.pop('with_count', False)}) 

113 

114 for time_comparison_filter in ['created_since', 'created_before']: 

115 if time_comparison_filter in filters: 115 ↛ 116line 115 didn't jump to line 116 because the condition on line 115 was never true

116 time_str = filters.get(time_comparison_filter) 

117 try: 

118 parsed_time = timeutils.parse_isotime(time_str) 

119 filters[time_comparison_filter] = parsed_time 

120 except ValueError: 

121 msg = _('Invalid value specified for the query ' 

122 'key: %s') % time_comparison_filter 

123 raise exc.HTTPBadRequest(explanation=msg) 

124 

125 locks, count = self.resource_locks_api.get_all(context, 

126 search_opts=filters, 

127 limit=limit, 

128 offset=offset, 

129 sort_key=sort_key, 

130 sort_dir=sort_dir, 

131 show_count=show_count) 

132 

133 return self._view_builder.index(req, 

134 locks, 

135 count=count) 

136 

137 @wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION) 

138 @wsgi.Controller.authorize('get') 

139 @validation.request_query_schema(schema.show_request_query) 

140 @validation.response_body_schema(schema.show_response_body) 

141 def show(self, req, id): 

142 """Return an existing resource lock by ID.""" 

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

144 try: 

145 resource_lock = self.resource_locks_api.get(context, id) 

146 except exception.ResourceLockNotFound as error: 

147 raise exc.HTTPNotFound(explanation=error.msg) 

148 return self._view_builder.detail(req, resource_lock) 

149 

150 @wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION) 

151 @wsgi.Controller.authorize 

152 @wsgi.action("delete") 

153 @validation.response_body_schema(schema.delete_response_body) 

154 def delete(self, req, id): 

155 """Delete an existing resource lock.""" 

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

157 try: 

158 self.resource_locks_api.delete(context, id) 

159 except exception.ResourceLockNotFound as error: 

160 raise exc.HTTPNotFound(explanation=error.msg) 

161 return webob.Response(status_int=http_client.NO_CONTENT) 

162 

163 @wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION) 

164 @wsgi.Controller.authorize 

165 @validation.request_body_schema(schema.create_request_body) 

166 @validation.response_body_schema(schema.create_response_body) 

167 def create(self, req, body): 

168 """Create a resource lock.""" 

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

170 self._check_body(body) 

171 lock_data = body['resource_lock'] 

172 try: 

173 resource_lock = self.resource_locks_api.create( 

174 context, 

175 resource_id=lock_data['resource_id'], 

176 resource_type=lock_data['resource_type'], 

177 resource_action=(lock_data.get('resource_action') or 

178 constants.RESOURCE_ACTION_DELETE), 

179 lock_reason=lock_data.get('lock_reason') 

180 ) 

181 except exception.NotFound: 

182 raise exc.HTTPBadRequest( 

183 explanation="No such resource found.") 

184 except exception.InvalidInput as error: 

185 raise exc.HTTPConflict(explanation=error.msg) 

186 except exception.ResourceVisibilityLockExists: 

187 raise exc.HTTPConflict( 

188 "Resource's visibility is already locked by other user.") 

189 return self._view_builder.detail(req, resource_lock) 

190 

191 @wsgi.Controller.api_version(RESOURCE_LOCKS_MIN_API_VERSION) 

192 @wsgi.Controller.authorize 

193 @validation.request_body_schema(schema.update_request_body) 

194 @validation.response_body_schema(schema.update_response_body) 

195 def update(self, req, id, body): 

196 """Update an existing resource lock.""" 

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

198 try: 

199 resource_lock = self.resource_locks_api.get(context, id) 

200 except exception.NotFound as e: 

201 raise exc.HTTPNotFound(explanation=e.msg) 

202 

203 self._check_body(body, lock_to_update=resource_lock) 

204 lock_data = body['resource_lock'] 

205 try: 

206 resource_lock = self.resource_locks_api.update( 

207 context, 

208 resource_lock, 

209 lock_data, 

210 ) 

211 except exception.InvalidInput as e: 

212 raise exc.HTTPBadRequest(explanation=e.msg) 

213 return self._view_builder.detail(req, resource_lock) 

214 

215 

216def create_resource(): 

217 return wsgi.Resource(ResourceLocksController())