Coverage for manila/tests/api/v2/test_share_transfer.py: 100%

304 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 

16import http.client as http_client 

17from unittest import mock 

18 

19import ddt 

20from oslo_serialization import jsonutils 

21import webob 

22 

23from manila.api.v2 import share_transfer 

24from manila import context 

25from manila import db 

26from manila import exception 

27from manila import quota 

28from manila.share import api as share_api 

29from manila.share import rpcapi as share_rpcapi 

30from manila.share import share_types 

31from manila import test 

32from manila.tests.api import fakes 

33from manila.tests import db_utils 

34from manila.transfer import api as transfer_api 

35 

36SHARE_TRANSFER_VERSION = "2.77" 

37 

38 

39@ddt.ddt 

40class ShareTransferAPITestCase(test.TestCase): 

41 """Test Case for transfers V3 API.""" 

42 

43 microversion = SHARE_TRANSFER_VERSION 

44 

45 def setUp(self): 

46 super(ShareTransferAPITestCase, self).setUp() 

47 self.share_transfer_api = transfer_api.API() 

48 self.v2_controller = share_transfer.ShareTransferController() 

49 self.ctxt = context.RequestContext( 

50 'fake_user_id', 'fake_project_id', auth_token=True, is_admin=True) 

51 

52 def _create_transfer(self, share_id='fake_share_id', 

53 display_name='test_transfer'): 

54 transfer = self.share_transfer_api.create(context.get_admin_context(), 

55 share_id, display_name) 

56 return transfer 

57 

58 def _create_share(self, display_name='test_share', 

59 display_description='this is a test share', 

60 status='available', 

61 size=1, 

62 project_id='fake_project_id', 

63 user_id='fake_user_id', 

64 share_network_id=None, 

65 mount_point_name=None): 

66 """Create a share object.""" 

67 share_type = db_utils.create_share_type() 

68 instance_list = [] 

69 if mount_point_name: 

70 instance_list.append( 

71 db_utils.create_share_instance( 

72 status=status, 

73 share_id='fake_id', 

74 mount_point_name=mount_point_name 

75 ) 

76 ) 

77 

78 share = db_utils.create_share( 

79 display_name=display_name, 

80 display_description=display_description, 

81 status=status, 

82 size=size, 

83 project_id=project_id, 

84 user_id=user_id, 

85 share_type_id=share_type['id'], 

86 share_network_id=share_network_id, 

87 instances=instance_list, 

88 mount_point_name=mount_point_name 

89 ) 

90 

91 share_id = share['id'] 

92 

93 return share_id 

94 

95 def test_show_transfer(self): 

96 share_id = self._create_share(size=5) 

97 transfer = self._create_transfer(share_id) 

98 path = '/v2/fake_project_id/share-transfers/%s' % transfer['id'] 

99 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

100 req.environ['manila.context'] = self.ctxt 

101 req.method = 'GET' 

102 req.headers['Content-Type'] = 'application/json' 

103 res_dict = self.v2_controller.show(req, transfer['id']) 

104 

105 self.assertEqual('test_transfer', res_dict['transfer']['name']) 

106 self.assertEqual(transfer['id'], res_dict['transfer']['id']) 

107 self.assertEqual(share_id, res_dict['transfer']['resource_id']) 

108 

109 def test_list_transfers(self): 

110 share_id_1 = self._create_share(size=5) 

111 share_id_2 = self._create_share(size=5) 

112 transfer1 = self._create_transfer(share_id_1) 

113 transfer2 = self._create_transfer(share_id_2) 

114 

115 path = '/v2/fake_project_id/share-transfers' 

116 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

117 req.environ['manila.context'] = self.ctxt 

118 req.method = 'GET' 

119 req.headers['Content-Type'] = 'application/json' 

120 res_dict = self.v2_controller.index(req) 

121 

122 self.assertEqual(transfer1['id'], res_dict['transfers'][1]['id']) 

123 self.assertEqual('test_transfer', res_dict['transfers'][1]['name']) 

124 self.assertEqual(transfer2['id'], res_dict['transfers'][0]['id']) 

125 self.assertEqual('test_transfer', res_dict['transfers'][0]['name']) 

126 

127 def test_list_transfers_with_all_tenants(self): 

128 share_id_1 = self._create_share(size=5) 

129 share_id_2 = self._create_share(size=5, project_id='fake_project_id2', 

130 user_id='fake_user_id2') 

131 self._create_transfer(share_id_1) 

132 self._create_transfer(share_id_2) 

133 

134 path = '/v2/fake_project_id/share-transfers?all_tenants=true' 

135 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

136 req.environ['manila.context'] = context.get_admin_context() 

137 req.method = 'GET' 

138 req.headers['Content-Type'] = 'application/json' 

139 res_dict = self.v2_controller.index(req) 

140 

141 self.assertEqual(2, len(res_dict['transfers'])) 

142 

143 def test_list_transfers_with_limit(self): 

144 share_id_1 = self._create_share(size=5) 

145 share_id_2 = self._create_share(size=5) 

146 self._create_transfer(share_id_1) 

147 self._create_transfer(share_id_2) 

148 path = '/v2/fake_project_id/share-transfers?limit=1' 

149 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

150 req.environ['manila.context'] = self.ctxt 

151 req.method = 'GET' 

152 req.headers['Content-Type'] = 'application/json' 

153 res_dict = self.v2_controller.index(req) 

154 

155 self.assertEqual(1, len(res_dict['transfers'])) 

156 

157 @ddt.data("desc", "asc") 

158 def test_list_transfers_with_sort(self, sort_dir): 

159 share_id_1 = self._create_share(size=5) 

160 share_id_2 = self._create_share(size=5) 

161 transfer1 = self._create_transfer(share_id_1) 

162 transfer2 = self._create_transfer(share_id_2) 

163 path = \ 

164 '/v2/fake_project_id/share-transfers?sort_key=id&sort_dir=%s' % ( 

165 sort_dir) 

166 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

167 req.environ['manila.context'] = self.ctxt 

168 req.method = 'GET' 

169 req.headers['Content-Type'] = 'application/json' 

170 res_dict = self.v2_controller.index(req) 

171 

172 self.assertEqual(2, len(res_dict['transfers'])) 

173 order_ids = sorted([transfer1['id'], 

174 transfer2['id']]) 

175 expect_result = order_ids[1] if sort_dir == "desc" else order_ids[0] 

176 self.assertEqual(expect_result, 

177 res_dict['transfers'][0]['id']) 

178 

179 def test_list_transfers_detail(self): 

180 share_id_1 = self._create_share(size=5) 

181 share_id_2 = self._create_share(size=5) 

182 transfer1 = self._create_transfer(share_id_1) 

183 transfer2 = self._create_transfer(share_id_2) 

184 

185 path = '/v2/fake_project_id/share-transfers/detail' 

186 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

187 req.environ['manila.context'] = self.ctxt 

188 req.method = 'GET' 

189 req.headers['Content-Type'] = 'application/json' 

190 req.headers['Accept'] = 'application/json' 

191 res_dict = self.v2_controller.detail(req) 

192 

193 self.assertEqual('test_transfer', 

194 res_dict['transfers'][1]['name']) 

195 self.assertEqual(transfer1['id'], res_dict['transfers'][1]['id']) 

196 self.assertEqual(share_id_1, res_dict['transfers'][1]['resource_id']) 

197 

198 self.assertEqual('test_transfer', 

199 res_dict['transfers'][0]['name']) 

200 self.assertEqual(transfer2['id'], res_dict['transfers'][0]['id']) 

201 self.assertEqual(share_id_2, res_dict['transfers'][0]['resource_id']) 

202 

203 def test_create_transfer(self): 

204 share_id = self._create_share(status='available', size=5) 

205 body = {"transfer": {"name": "transfer1", 

206 "share_id": share_id}} 

207 

208 path = '/v2/fake_project_id/share-transfers' 

209 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

210 req.environ['manila.context'] = self.ctxt 

211 req.method = 'POST' 

212 req.headers['Content-Type'] = 'application/json' 

213 req.body = jsonutils.dumps(body).encode("utf-8") 

214 res_dict = self.v2_controller.create(req, body) 

215 

216 self.assertIn('id', res_dict['transfer']) 

217 self.assertIn('auth_key', res_dict['transfer']) 

218 self.assertIn('created_at', res_dict['transfer']) 

219 self.assertIn('name', res_dict['transfer']) 

220 self.assertIn('resource_id', res_dict['transfer']) 

221 

222 @ddt.data({}, 

223 {"transfer": {"name": "transfer1"}}, 

224 {"transfer": {"name": "transfer1", 

225 "share_id": "invalid_share_id"}}) 

226 def test_create_transfer_with_invalid_body(self, body): 

227 path = '/v2/fake_project_id/share-transfers' 

228 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

229 req.environ['manila.context'] = self.ctxt 

230 req.method = 'POST' 

231 req.headers['Content-Type'] = 'application/json' 

232 req.body = jsonutils.dumps(body).encode("utf-8") 

233 self.assertRaises(webob.exc.HTTPBadRequest, 

234 self.v2_controller.create, req, body) 

235 

236 def test_create_transfer_with_invalid_share_status(self): 

237 share_id = self._create_share() 

238 body = {"transfer": {"name": "transfer1", 

239 "share_id": share_id}} 

240 db.share_update(context.get_admin_context(), 

241 share_id, {'status': 'error'}) 

242 

243 path = '/v2/fake_project_id/share-transfers' 

244 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

245 req.environ['manila.context'] = self.ctxt 

246 req.method = 'POST' 

247 req.headers['Content-Type'] = 'application/json' 

248 req.body = jsonutils.dumps(body).encode("utf-8") 

249 self.assertRaises(webob.exc.HTTPBadRequest, 

250 self.v2_controller.create, req, body) 

251 

252 def test_create_transfer_with_invalid_mount_point_name(self): 

253 share_id = self._create_share( 

254 project_id='fake_pid', 

255 mount_point_name='fake_pid_mount_point_name') 

256 body = {"transfer": {"name": "transfer1", 

257 "share_id": share_id}} 

258 db.share_update(context.get_admin_context(), 

259 share_id, {'status': 'error'}) 

260 

261 path = '/v2/fake_project_id/share-transfers' 

262 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

263 req.environ['manila.context'] = self.ctxt 

264 req.method = 'POST' 

265 req.headers['Content-Type'] = 'application/json' 

266 req.body = jsonutils.dumps(body).encode("utf-8") 

267 self.assertRaises(webob.exc.HTTPBadRequest, 

268 self.v2_controller.create, req, body) 

269 

270 def test_create_transfer_with_project_id_prefix_mount_point_name(self): 

271 share_id = self._create_share(project_id='fake', 

272 mount_point_name='fake_mp') 

273 

274 self.assertRaises(exception.Invalid, 

275 self.share_transfer_api.create, 

276 context.get_admin_context(), share_id, 

277 'test_missing_share_type') 

278 

279 def test_create_transfer_share_with_network_id(self): 

280 share_id = self._create_share(share_network_id='fake_id') 

281 body = {"transfer": {"name": "transfer1", 

282 "share_id": share_id}} 

283 

284 path = '/v2/fake_project_id/share-transfers' 

285 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

286 req.environ['manila.context'] = self.ctxt 

287 req.method = 'POST' 

288 req.headers['Content-Type'] = 'application/json' 

289 req.body = jsonutils.dumps(body).encode("utf-8") 

290 self.assertRaises(webob.exc.HTTPBadRequest, 

291 self.v2_controller.create, req, body) 

292 

293 def test_create_transfer_share_with_invalid_snapshot(self): 

294 share_id = self._create_share(share_network_id='fake_id') 

295 db_utils.create_snapshot(share_id=share_id) 

296 body = {"transfer": {"name": "transfer1", 

297 "share_id": share_id}} 

298 

299 path = '/v2/fake_project_id/share-transfers' 

300 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

301 req.environ['manila.context'] = self.ctxt 

302 req.method = 'POST' 

303 req.headers['Content-Type'] = 'application/json' 

304 req.body = jsonutils.dumps(body).encode("utf-8") 

305 self.assertRaises(webob.exc.HTTPBadRequest, 

306 self.v2_controller.create, req, body) 

307 

308 def test_delete_transfer_awaiting_transfer(self): 

309 share_id = self._create_share() 

310 transfer = self.share_transfer_api.create(context.get_admin_context(), 

311 share_id, 'test_transfer') 

312 path = '/v2/fake_project_id/share-transfers/%s' % transfer['id'] 

313 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

314 req.environ['manila.context'] = self.ctxt 

315 req.method = 'DELETE' 

316 req.headers['Content-Type'] = 'application/json' 

317 self.v2_controller.delete(req, transfer['id']) 

318 

319 # verify transfer has been deleted 

320 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

321 req.environ['manila.context'] = self.ctxt 

322 req.method = 'GET' 

323 req.headers['Content-Type'] = 'application/json' 

324 res = req.get_response(fakes.app()) 

325 

326 self.assertEqual(http_client.NOT_FOUND, res.status_int) 

327 self.assertEqual(db.share_get(context.get_admin_context(), 

328 share_id)['status'], 'available') 

329 

330 def test_delete_transfer_not_awaiting_transfer(self): 

331 share_id = self._create_share() 

332 transfer = self.share_transfer_api.create(context.get_admin_context(), 

333 share_id, 'test_transfer') 

334 db.share_update(context.get_admin_context(), 

335 share_id, {'status': 'available'}) 

336 

337 path = '/v2/fake_project_id/share-transfers/%s' % transfer['id'] 

338 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

339 req.environ['manila.context'] = self.ctxt 

340 req.method = 'DELETE' 

341 req.headers['Content-Type'] = 'application/json' 

342 self.assertRaises(exception.InvalidShare, 

343 self.v2_controller.delete, req, 

344 transfer['id']) 

345 

346 def test_transfer_accept_share_id_specified(self): 

347 share_id = self._create_share() 

348 transfer = self.share_transfer_api.create(context.get_admin_context(), 

349 share_id, 'test_transfer') 

350 self.mock_object(quota.QUOTAS, 'reserve', mock.Mock()) 

351 self.mock_object(quota.QUOTAS, 'commit', mock.Mock()) 

352 self.mock_object(share_api.API, 

353 'check_is_share_size_within_per_share_quota_limit', 

354 mock.Mock()) 

355 self.mock_object(share_rpcapi.ShareAPI, 

356 'transfer_accept', 

357 mock.Mock()) 

358 fake_share_type = {'id': 'fake_id', 

359 'name': 'fake_name', 

360 'is_public': True} 

361 self.mock_object(share_types, 'get_share_type', 

362 mock.Mock(return_value=fake_share_type)) 

363 self.mock_object(db, 'share_snapshot_get_all_for_share', 

364 mock.Mock(return_value={})) 

365 

366 body = {"accept": {"auth_key": transfer['auth_key']}} 

367 path = '/v2/fake_project_id/share-transfers/%s/accept' % transfer['id'] 

368 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

369 req.environ['manila.context'] = self.ctxt 

370 req.method = 'POST' 

371 req.headers['Content-Type'] = 'application/json' 

372 req.body = jsonutils.dumps(body).encode("utf-8") 

373 self.v2_controller.accept(req, transfer['id'], body) 

374 

375 def test_transfer_accept_with_not_public_share_type(self): 

376 share_id = self._create_share() 

377 transfer = self.share_transfer_api.create(context.get_admin_context(), 

378 share_id, 'test_transfer') 

379 fake_share_type = {'id': 'fake_id', 

380 'name': 'fake_name', 

381 'is_public': False, 

382 'projects': ['project_id1', 'project_id2']} 

383 self.mock_object(share_types, 'get_share_type', 

384 mock.Mock(return_value=fake_share_type)) 

385 

386 body = {"accept": {"auth_key": transfer['auth_key']}} 

387 path = '/v2/fake_project_id/share-transfers/%s/accept' % transfer['id'] 

388 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

389 req.environ['manila.context'] = self.ctxt 

390 req.method = 'POST' 

391 req.headers['Content-Type'] = 'application/json' 

392 req.body = jsonutils.dumps(body).encode("utf-8") 

393 self.assertRaises(webob.exc.HTTPBadRequest, 

394 self.v2_controller.accept, req, 

395 transfer['id'], body) 

396 

397 @ddt.data({}, 

398 {"accept": {}}, 

399 {"accept": {"auth_key": "fake_auth_key", 

400 "clear_access_rules": "invalid_bool"}}) 

401 def test_transfer_accept_with_invalid_body(self, body): 

402 path = '/v2/fake_project_id/share-transfers/fake_transfer_id/accept' 

403 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

404 req.environ['manila.context'] = self.ctxt 

405 req.method = 'POST' 

406 req.headers['Content-Type'] = 'application/json' 

407 req.body = jsonutils.dumps(body).encode("utf-8") 

408 self.assertRaises(webob.exc.HTTPBadRequest, 

409 self.v2_controller.accept, req, 

410 'fake_transfer_id', body) 

411 

412 def test_transfer_accept_with_invalid_auth_key(self): 

413 share_id = self._create_share(size=5) 

414 transfer = self._create_transfer(share_id) 

415 body = {"accept": {"auth_key": "invalid_auth_key"}} 

416 path = '/v2/fake_project_id/share-transfers/%s/accept' % transfer['id'] 

417 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

418 req.environ['manila.context'] = self.ctxt 

419 req.method = 'POST' 

420 req.headers['Content-Type'] = 'application/json' 

421 req.body = jsonutils.dumps(body).encode("utf-8") 

422 self.assertRaises(webob.exc.HTTPBadRequest, 

423 self.v2_controller.accept, req, 

424 transfer['id'], body) 

425 

426 def test_transfer_accept_with_invalid_share_status(self): 

427 share_id = self._create_share(size=5) 

428 transfer = self._create_transfer(share_id) 

429 db.share_update(context.get_admin_context(), 

430 share_id, {'status': 'error'}) 

431 body = {"accept": {"auth_key": transfer['auth_key']}} 

432 path = '/v2/fake_project_id/share-transfers/%s/accept' % transfer['id'] 

433 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

434 req.environ['manila.context'] = self.ctxt 

435 req.method = 'POST' 

436 req.headers['Content-Type'] = 'application/json' 

437 req.body = jsonutils.dumps(body).encode("utf-8") 

438 self.assertRaises(webob.exc.HTTPBadRequest, 

439 self.v2_controller.accept, req, 

440 transfer['id'], body) 

441 

442 @ddt.data({'overs': {'gigabytes': 'fake'}}, 

443 {'overs': {'shares': 'fake'}}, 

444 {'overs': {'snapshot_gigabytes': 'fake'}}, 

445 {'overs': {'snapshots': 'fake'}}) 

446 @ddt.unpack 

447 def test_accept_share_over_quota(self, overs): 

448 share_id = self._create_share() 

449 db_utils.create_snapshot(share_id=share_id, status='available') 

450 transfer = self.share_transfer_api.create(context.get_admin_context(), 

451 share_id, 'test_transfer') 

452 

453 usages = {'gigabytes': {'reserved': 5, 'in_use': 5}, 

454 'shares': {'reserved': 10, 'in_use': 10}, 

455 'snapshot_gigabytes': {'reserved': 5, 'in_use': 5}, 

456 'snapshots': {'reserved': 10, 'in_use': 10}} 

457 

458 quotas = {'gigabytes': 5, 'shares': 10, 

459 'snapshot_gigabytes': 5, 'snapshots': 10} 

460 exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas) 

461 self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc)) 

462 self.mock_object(quota.QUOTAS, 'commit', mock.Mock()) 

463 self.mock_object(share_api.API, 

464 'check_is_share_size_within_per_share_quota_limit', 

465 mock.Mock()) 

466 self.mock_object(share_rpcapi.ShareAPI, 

467 'transfer_accept', 

468 mock.Mock()) 

469 fake_share_type = {'id': 'fake_id', 

470 'name': 'fake_name', 

471 'is_public': True} 

472 self.mock_object(share_types, 'get_share_type', 

473 mock.Mock(return_value=fake_share_type)) 

474 

475 body = {"accept": {"auth_key": transfer['auth_key']}} 

476 path = '/v2/fake_project_id/share-transfers/%s/accept' % transfer['id'] 

477 req = fakes.HTTPRequest.blank(path, version=self.microversion) 

478 req.environ['manila.context'] = self.ctxt 

479 req.method = 'POST' 

480 req.headers['Content-Type'] = 'application/json' 

481 req.body = jsonutils.dumps(body).encode("utf-8") 

482 self.assertRaises(webob.exc.HTTPRequestEntityTooLarge, 

483 self.v2_controller.accept, req, 

484 transfer['id'], body)