Coverage for manila/api/v2/share_transfer.py: 98%

115 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2022 China Telecom Digital Intelligence. 

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 

16"""The share transfer api.""" 

17 

18from http import client as http_client 

19 

20from oslo_log import log as logging 

21from oslo_utils import strutils 

22from oslo_utils import uuidutils 

23import webob 

24from webob import exc 

25 

26from manila.api import common 

27from manila.api.openstack import wsgi 

28from manila.api.views import transfers as transfer_view 

29from manila import exception 

30from manila.i18n import _ 

31from manila.transfer import api as transfer_api 

32 

33LOG = logging.getLogger(__name__) 

34SHARE_TRANSFER_VERSION = "2.77" 

35 

36 

37class ShareTransferController(wsgi.Controller): 

38 """The Share Transfer API controller for the OpenStack API.""" 

39 

40 resource_name = 'share_transfer' 

41 _view_builder_class = transfer_view.ViewBuilder 

42 

43 def __init__(self): 

44 self.transfer_api = transfer_api.API() 

45 super(ShareTransferController, self).__init__() 

46 

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

48 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

49 def show(self, req, id): 

50 """Return data about active transfers.""" 

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

52 

53 # Not found exception will be handled at the wsgi level 

54 transfer = self.transfer_api.get(context, transfer_id=id) 

55 

56 return self._view_builder.detail(req, transfer) 

57 

58 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

59 def index(self, req): 

60 """Returns a summary list of transfers.""" 

61 return self._get_transfers(req, is_detail=False) 

62 

63 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

64 def detail(self, req): 

65 """Returns a detailed list of transfers.""" 

66 return self._get_transfers(req, is_detail=True) 

67 

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

69 def _get_transfers(self, req, is_detail): 

70 """Returns a list of transfers, transformed through view builder.""" 

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

72 params = req.params.copy() 

73 pagination_params = common.get_pagination_params(req) 

74 limit, offset = [pagination_params.pop('limit', None), 

75 pagination_params.pop('offset', None)] 

76 sort_key, sort_dir = common.get_sort_params(params) 

77 

78 filters = params 

79 key_map = {'name': 'display_name', 'name~': 'display_name~'} 

80 for k in key_map: 

81 if k in filters: 81 ↛ 82line 81 didn't jump to line 82 because the condition on line 81 was never true

82 filters[key_map[k]] = filters.pop(k) 

83 LOG.debug('Listing share transfers.') 

84 

85 transfers = self.transfer_api.get_all(context, 

86 limit=limit, 

87 sort_key=sort_key, 

88 sort_dir=sort_dir, 

89 filters=filters, 

90 offset=offset) 

91 

92 if is_detail: 

93 transfers = self._view_builder.detail_list(req, transfers) 

94 else: 

95 transfers = self._view_builder.summary_list(req, transfers) 

96 

97 return transfers 

98 

99 @wsgi.response(http_client.ACCEPTED) 

100 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

101 @wsgi.Controller.authorize('create') 

102 def create(self, req, body): 

103 """Create a new share transfer.""" 

104 LOG.debug('Creating new share transfer %s', body) 

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

106 

107 if not self.is_valid_body(body, 'transfer'): 

108 msg = _("'transfer' is missing from the request body.") 

109 raise exc.HTTPBadRequest(explanation=msg) 

110 

111 transfer = body.get('transfer', {}) 

112 

113 share_id = transfer.get('share_id') 

114 if not share_id: 

115 msg = _("Must supply 'share_id' attribute.") 

116 raise exc.HTTPBadRequest(explanation=msg) 

117 if not uuidutils.is_uuid_like(share_id): 

118 msg = _("The 'share_id' attribute must be a uuid.") 

119 raise exc.HTTPBadRequest(explanation=msg) 

120 

121 transfer_name = transfer.get('name') 

122 if transfer_name is not None: 122 ↛ 125line 122 didn't jump to line 125 because the condition on line 122 was always true

123 transfer_name = transfer_name.strip() 

124 

125 LOG.debug("Creating transfer of share %s", share_id) 

126 

127 try: 

128 new_transfer = self.transfer_api.create(context, share_id, 

129 transfer_name) 

130 except exception.Invalid as error: 

131 raise exc.HTTPBadRequest(explanation=error.msg) 

132 

133 transfer = self._view_builder.create(req, 

134 dict(new_transfer)) 

135 return transfer 

136 

137 @wsgi.response(http_client.ACCEPTED) 

138 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

139 @wsgi.Controller.authorize('accept') 

140 def accept(self, req, id, body): 

141 """Accept a new share transfer.""" 

142 transfer_id = id 

143 LOG.debug('Accepting share transfer %s', transfer_id) 

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

145 

146 if not self.is_valid_body(body, 'accept'): 

147 msg = _("'accept' is missing from the request body.") 

148 raise exc.HTTPBadRequest(explanation=msg) 

149 

150 accept = body.get('accept', {}) 

151 auth_key = accept.get('auth_key') 

152 if not auth_key: 

153 msg = _("Must supply 'auth_key' while accepting a " 

154 "share transfer.") 

155 raise exc.HTTPBadRequest(explanation=msg) 

156 

157 clear_rules = accept.get('clear_access_rules', False) 

158 if clear_rules: 

159 try: 

160 clear_rules = strutils.bool_from_string(clear_rules, 

161 strict=True) 

162 except (ValueError, TypeError): 

163 msg = (_('Invalid boolean clear_access_rules : %(value)s') % 

164 {'value': accept['clear_access_rules']}) 

165 raise exc.HTTPBadRequest(explanation=msg) 

166 

167 LOG.debug("Accepting transfer %s", transfer_id) 

168 

169 try: 

170 self.transfer_api.accept( 

171 context, transfer_id, auth_key, clear_rules=clear_rules) 

172 except (exception.ShareSizeExceedsLimit, 

173 exception.ShareLimitExceeded, 

174 exception.ShareSizeExceedsAvailableQuota, 

175 exception.ShareReplicasLimitExceeded, 

176 exception.ShareReplicaSizeExceedsAvailableQuota, 

177 exception.SnapshotSizeExceedsAvailableQuota, 

178 exception.SnapshotLimitExceeded) as e: 

179 raise exc.HTTPRequestEntityTooLarge(explanation=e.msg, 

180 headers={'Retry-After': '0'}) 

181 except (exception.InvalidShare, 

182 exception.InvalidSnapshot, 

183 exception.InvalidAuthKey, 

184 exception.TransferNotFound) as error: 

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

186 

187 @wsgi.Controller.api_version(SHARE_TRANSFER_VERSION) 

188 @wsgi.Controller.authorize('delete') 

189 def delete(self, req, id): 

190 """Delete a transfer.""" 

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

192 

193 LOG.debug("Delete transfer with id: %s", id) 

194 

195 # Not found exception will be handled at the wsgi level 

196 self.transfer_api.delete(context, transfer_id=id) 

197 return webob.Response(status_int=http_client.OK) 

198 

199 

200def create_resource(): 

201 return wsgi.Resource(ShareTransferController())