Coverage for manila/tests/share/drivers/dell_emc/plugins/powerscale/test_powerscale_api.py: 99%

737 statements  

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

1# Copyright (c) 2015 EMC Corporation. 

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 

16from unittest import mock 

17 

18import ddt 

19from oslo_serialization import jsonutils as json 

20import requests 

21import requests_mock 

22from urllib.parse import quote 

23 

24from manila import exception 

25from manila.share.drivers.dell_emc.plugins.powerscale import powerscale_api 

26from manila import test 

27 

28 

29@ddt.ddt 

30class PowerScaleApiTest(test.TestCase): 

31 

32 @mock.patch('manila.share.drivers.dell_emc.plugins.powerscale.' 

33 'powerscale_api.PowerScaleApi.create_session') 

34 def setUp(self, mockup_create_session): 

35 super(PowerScaleApiTest, self).setUp() 

36 

37 mockup_create_session.return_value = True 

38 self._mock_url = 'https://localhost:8080' 

39 self.username = 'admin' 

40 self.password = 'pwd' 

41 self.dir_permission = '0777' 

42 self.powerscale_api = powerscale_api.PowerScaleApi( 

43 self._mock_url, self.username, self.password, 

44 dir_permission=self.dir_permission 

45 ) 

46 self.powerscale_api_threshold = powerscale_api.PowerScaleApi( 

47 self._mock_url, self.username, self.password, 

48 dir_permission=self.dir_permission, 

49 threshold_limit=80 

50 ) 

51 

52 @mock.patch('manila.share.drivers.dell_emc.plugins.powerscale.' 

53 'powerscale_api.PowerScaleApi.create_session') 

54 def test__init__login_failure(self, mockup_create_session): 

55 mockup_create_session.return_value = False 

56 self.assertRaises( 

57 exception.BadConfigurationException, 

58 self.powerscale_api.__init__, 

59 self._mock_url, 

60 self.username, 

61 self.password, 

62 False, 

63 None, 

64 self.dir_permission 

65 ) 

66 

67 def test__verify_cert(self): 

68 verify_cert = self.powerscale_api.verify_ssl_cert 

69 certificate_path = self.powerscale_api.certificate_path 

70 self.powerscale_api.verify_ssl_cert = True 

71 self.powerscale_api.certificate_path = "fake_certificate_path" 

72 self.assertEqual(self.powerscale_api._verify_cert, 

73 self.powerscale_api.certificate_path) 

74 self.powerscale_api.verify_ssl_cert = verify_cert 

75 self.powerscale_api.certificate_path = certificate_path 

76 

77 @mock.patch('requests.Session.request') 

78 def test_create_session_success(self, mock_request): 

79 mock_response = mock.Mock() 

80 mock_response.status_code = 201 

81 mock_response.cookies = {'isisessid': 'test_session_token', 

82 'isicsrf': 'test_csrf_token'} 

83 mock_request.return_value = mock_response 

84 result = self.powerscale_api.create_session( 

85 self.username, self.password) 

86 mock_request.assert_called_once_with( 

87 'POST', self._mock_url + '/session/1/session', 

88 headers={"Content-type": "application/json"}, 

89 data=json.dumps({"username": self.username, 

90 "password": self.password, 

91 "services": ["platform", "namespace"]}), 

92 verify=False 

93 ) 

94 self.assertTrue(result) 

95 self.assertEqual(self.powerscale_api.session_token, 

96 'test_session_token') 

97 self.assertEqual(self.powerscale_api.csrf_token, 'test_csrf_token') 

98 

99 @mock.patch('requests.Session.request') 

100 def test_create_session_failure(self, mock_request): 

101 mock_response = mock.Mock() 

102 mock_response.status_code = 401 

103 mock_response.json.return_value = { 

104 'message': 'Username or password is incorrect.'} 

105 mock_request.return_value = mock_response 

106 result = self.powerscale_api.create_session( 

107 self.username, self.password) 

108 self.assertFalse(result) 

109 self.assertIsNone(self.powerscale_api.session_token) 

110 self.assertIsNone(self.powerscale_api.csrf_token) 

111 

112 @ddt.data(False, True) 

113 def test_create_directory(self, is_recursive): 

114 with requests_mock.Mocker() as m: 

115 path = '/ifs/test' 

116 self.assertEqual(0, len(m.request_history)) 

117 self._add_create_directory_response(m, path, is_recursive) 

118 

119 r = self.powerscale_api.create_directory(path, 

120 recursive=is_recursive) 

121 

122 self.assertTrue(r) 

123 self.assertEqual(1, len(m.request_history)) 

124 request = m.request_history[0] 

125 self._verify_dir_creation_request(request, path, is_recursive) 

126 

127 def test_create_directory_no_permission(self): 

128 with requests_mock.Mocker() as m: 

129 path = '/ifs/test' 

130 self.powerscale_api.dir_permission = None 

131 self.assertEqual(0, len(m.request_history)) 

132 self._add_create_directory_response(m, path, True) 

133 

134 r = self.powerscale_api.create_directory(path, 

135 recursive=True) 

136 

137 self.powerscale_api.dir_permission = '0777' 

138 self.assertTrue(r) 

139 self.assertEqual(1, len(m.request_history)) 

140 request = m.request_history[0] 

141 self.assertNotIn("x-isi-ifs-access-control", request.headers) 

142 

143 @requests_mock.mock() 

144 def test_clone_snapshot(self, m): 

145 snapshot_name = 'snapshot01' 

146 fq_target_dir = '/ifs/admin/target' 

147 

148 self.assertEqual(0, len(m.request_history)) 

149 self._add_create_directory_response(m, fq_target_dir, False) 

150 snapshots_json = ( 

151 '{"snapshots": ' 

152 '[{"name": "snapshot01", "path": "/ifs/admin/source"}]' 

153 '}' 

154 ) 

155 self._add_get_snapshot_response(m, snapshot_name, snapshots_json) 

156 

157 # In order to test cloning a snapshot, we build out a mock 

158 # source directory tree. After the method under test is called we 

159 # will verify the necessary calls are made to clone a snapshot. 

160 source_dir_listing_json = ( 

161 '{"children": [' 

162 '{"name": "dir1", "type": "container"},' 

163 '{"name": "dir2", "type": "container"},' 

164 '{"name": "file1", "type": "object"},' 

165 '{"name": "file2", "type": "object"}' 

166 ']}' 

167 ) 

168 self._add_get_directory_listing_response( 

169 m, '/ifs/.snapshot/{0}/admin/source'.format(snapshot_name), 

170 source_dir_listing_json) 

171 

172 # Add request responses for creating directories and cloning files 

173 # to the destination tree 

174 self._add_file_clone_response(m, '/ifs/admin/target/file1', 

175 snapshot_name) 

176 self._add_file_clone_response(m, '/ifs/admin/target/file2', 

177 snapshot_name) 

178 self._add_create_directory_response(m, fq_target_dir + '/dir1', False) 

179 self._add_get_directory_listing_response( 

180 m, '/ifs/.snapshot/{0}/admin/source/dir1'.format(snapshot_name), 

181 '{"children": [' 

182 '{"name": "file11", "type": "object"}, ' 

183 '{"name": "file12", "type": "object"}' 

184 ']}') 

185 self._add_file_clone_response(m, '/ifs/admin/target/dir1/file11', 

186 snapshot_name) 

187 self._add_file_clone_response(m, '/ifs/admin/target/dir1/file12', 

188 snapshot_name) 

189 self._add_create_directory_response(m, fq_target_dir + '/dir2', False) 

190 self._add_get_directory_listing_response( 

191 m, '/ifs/.snapshot/{0}/admin/source/dir2'.format(snapshot_name), 

192 '{"children": [' 

193 '{"name": "file21", "type": "object"}, ' 

194 '{"name": "file22", "type": "object"}' 

195 ']}') 

196 self._add_file_clone_response(m, '/ifs/admin/target/dir2/file21', 

197 snapshot_name) 

198 self._add_file_clone_response(m, '/ifs/admin/target/dir2/file22', 

199 snapshot_name) 

200 

201 # Call method under test 

202 self.powerscale_api.clone_snapshot(snapshot_name, fq_target_dir, None) 

203 

204 # Verify calls needed to clone the source snapshot to the target dir 

205 expected_calls = [] 

206 clone_path_list = [ 

207 'file1', 'file2', 'dir1/file11', 'dir1/file12', 

208 'dir2/file21', 'dir2/file22'] 

209 for path in clone_path_list: 

210 expected_call = PowerScaleApiTest.ExpectedCall( 

211 PowerScaleApiTest.ExpectedCall.FILE_CLONE, 

212 self._mock_url + '/namespace/ifs/admin/target/' + path, 

213 ['/ifs/admin/target/' + path, '/ifs/admin/source/' + path, 

214 snapshot_name]) 

215 expected_calls.append(expected_call) 

216 dir_path_list = [ 

217 ('/dir1?recursive', '/dir1'), 

218 ('/dir2?recursive', '/dir2'), 

219 ('?recursive=', '')] 

220 for url, path in dir_path_list: 

221 expected_call = PowerScaleApiTest.ExpectedCall( 

222 PowerScaleApiTest.ExpectedCall.DIR_CREATION, 

223 self._mock_url + '/namespace/ifs/admin/target' + url, 

224 ['/ifs/admin/target' + path, False]) 

225 expected_calls.append(expected_call) 

226 

227 self._verify_clone_snapshot_calls(expected_calls, m.request_history) 

228 

229 class ExpectedCall(object): 

230 DIR_CREATION = 'dir_creation' 

231 FILE_CLONE = 'file_clone' 

232 

233 def __init__(self, request_type, match_url, verify_args): 

234 self.request_type = request_type 

235 self.match_url = match_url 

236 self.verify_args = verify_args 

237 

238 def _verify_clone_snapshot_calls(self, expected_calls, response_calls): 

239 actual_calls = [] 

240 for call in response_calls: 

241 actual_calls.append(call) 

242 for expected_call in expected_calls: 

243 # Match the expected call to the actual call, then verify 

244 match_found = False 

245 for call in actual_calls: 

246 if call.url.startswith(expected_call.match_url): 

247 match_found = True 

248 if expected_call.request_type == 'dir_creation': 

249 self._verify_dir_creation_request( 

250 call, *expected_call.verify_args) 

251 elif expected_call.request_type == 'file_clone': 251 ↛ 254line 251 didn't jump to line 254 because the condition on line 251 was always true

252 pass 

253 else: 

254 self.fail('Invalid request type') 

255 actual_calls.remove(call) 

256 self.assertTrue(match_found) 

257 

258 @requests_mock.mock() 

259 def test_get_directory_listing(self, m): 

260 self.assertEqual(0, len(m.request_history)) 

261 fq_dir_path = 'ifs/admin/test' 

262 json_str = '{"my_json": "test123"}' 

263 self._add_get_directory_listing_response(m, fq_dir_path, json_str) 

264 

265 actual_json = self.powerscale_api.get_directory_listing(fq_dir_path) 

266 

267 self.assertEqual(1, len(m.request_history)) 

268 self.assertEqual(json.loads(json_str), actual_json) 

269 

270 @ddt.data((200, True), (404, False)) 

271 def test_is_path_existent(self, data): 

272 status_code, expected_return_value = data 

273 with requests_mock.mock() as m: 

274 self.assertEqual(0, len(m.request_history)) 

275 path = '/ifs/home/admin' 

276 m.head('{0}/namespace{1}'.format(self._mock_url, path), 

277 status_code=status_code) 

278 

279 r = self.powerscale_api.is_path_existent(path) 

280 

281 self.assertEqual(expected_return_value, r) 

282 self.assertEqual(1, len(m.request_history)) 

283 

284 @requests_mock.mock() 

285 def test_is_path_existent_unexpected_error(self, m): 

286 path = '/ifs/home/admin' 

287 m.head('{0}/namespace{1}'.format(self._mock_url, path), 

288 status_code=400) 

289 

290 self.assertRaises( 

291 requests.exceptions.HTTPError, 

292 self.powerscale_api.is_path_existent, 

293 '/ifs/home/admin') 

294 

295 @ddt.data( 

296 (200, '{"snapshots": [{"path": "/ifs/home/test"}]}', 

297 {'path': '/ifs/home/test'}), 

298 (404, '{"errors": []}', None) 

299 ) 

300 def test_get_snapshot(self, data): 

301 status_code, json_body, expected_return_value = data 

302 with requests_mock.mock() as m: 

303 self.assertEqual(0, len(m.request_history)) 

304 snapshot_name = 'foo1' 

305 self._add_get_snapshot_response(m, snapshot_name, json_body, 

306 status=status_code) 

307 

308 r = self.powerscale_api.get_snapshot(snapshot_name) 

309 

310 self.assertEqual(1, len(m.request_history)) 

311 self.assertEqual(expected_return_value, r) 

312 

313 def test_clone_snapshot_with_provider_location(self): 

314 """clone_snapshot should use provider_location to get snapshot""" 

315 self.powerscale_api.create_directory = mock.Mock() 

316 self.powerscale_api.get_snapshot_id = mock.Mock() 

317 self.powerscale_api.get_snapshot = mock.Mock() 

318 self.powerscale_api._clone_directory_contents = mock.Mock() 

319 fq_target_dir = '/ifs/data/clone1' 

320 provider_location = '1234' 

321 snapshot = { 

322 'name': 'snap-123', 

323 'path': '/ifs/data/share1' 

324 } 

325 self.powerscale_api.get_snapshot_id.return_value = snapshot 

326 self.powerscale_api.clone_snapshot( 

327 snapshot_name='ignored-snapshot-name', 

328 fq_target_dir=fq_target_dir, 

329 provider_location=provider_location 

330 ) 

331 (self.powerscale_api. 

332 create_directory.assert_called_once_with(fq_target_dir)) 

333 (self.powerscale_api. 

334 get_snapshot_id.assert_called_once_with(provider_location)) 

335 self.powerscale_api.get_snapshot.assert_not_called() 

336 

337 @requests_mock.mock() 

338 def test_get_snapshot_unexpected_error(self, m): 

339 snapshot_name = 'foo1' 

340 json_body = '{"snapshots": [{"path": "/ifs/home/test"}]}' 

341 self._add_get_snapshot_response( 

342 m, snapshot_name, json_body, status=400) 

343 

344 self.assertRaises( 

345 requests.exceptions.HTTPError, self.powerscale_api.get_snapshot, 

346 snapshot_name) 

347 

348 @requests_mock.mock() 

349 def test_get_snapshots(self, m): 

350 self.assertEqual(0, len(m.request_history)) 

351 snapshot_json = '{"snapshots": [{"path": "/ifs/home/test"}]}' 

352 m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url), 

353 status_code=200, json=json.loads(snapshot_json)) 

354 

355 r = self.powerscale_api.get_snapshots() 

356 

357 self.assertEqual(1, len(m.request_history)) 

358 self.assertEqual(json.loads(snapshot_json), r) 

359 

360 @requests_mock.mock() 

361 def test_get_snapshots_error_occurred(self, m): 

362 self.assertEqual(0, len(m.request_history)) 

363 m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url), 

364 status_code=404) 

365 

366 self.assertRaises(requests.exceptions.HTTPError, 

367 self.powerscale_api.get_snapshots) 

368 

369 self.assertEqual(1, len(m.request_history)) 

370 

371 @ddt.data( 

372 ('/ifs/home/admin', 

373 '{"exports": [{"id": 42, "paths": ["/ifs/home/admin"]}], "total": 1}', 

374 42), 

375 ('/ifs/home/test', 

376 '{"exports": [], "total": 0}', None) 

377 ) 

378 def test_lookup_nfs_export(self, data): 

379 share_path, response_json, expected_return = data 

380 with requests_mock.mock() as m: 

381 self.assertEqual(0, len(m.request_history)) 

382 m.get('{0}/platform/12/protocols/nfs/exports?path={1}' 

383 .format(self._mock_url, 

384 share_path.replace('/', '%2F')), 

385 json=json.loads(response_json)) 

386 

387 r = self.powerscale_api.lookup_nfs_export(share_path) 

388 

389 self.assertEqual(1, len(m.request_history)) 

390 self.assertEqual(expected_return, r) 

391 

392 @requests_mock.mock() 

393 def test_get_nfs_export(self, m): 

394 self.assertEqual(0, len(m.request_history)) 

395 export_id = 42 

396 response_json = '{"exports": [{"id": 1}]}' 

397 status_code = 200 

398 m.get('{0}/platform/1/protocols/nfs/exports/{1}' 

399 .format(self._mock_url, export_id), 

400 json=json.loads(response_json), status_code=status_code) 

401 

402 r = self.powerscale_api.get_nfs_export(export_id) 

403 

404 self.assertEqual(1, len(m.request_history)) 

405 self.assertEqual(json.loads('{"id": 1}'), r) 

406 

407 @requests_mock.mock() 

408 def test_get_nfs_export_error(self, m): 

409 self.assertEqual(0, len(m.request_history)) 

410 export_id = 3 

411 response_json = '{}' 

412 status_code = 404 

413 m.get('{0}/platform/1/protocols/nfs/exports/{1}' 

414 .format(self._mock_url, export_id), 

415 json=json.loads(response_json), status_code=status_code) 

416 

417 r = self.powerscale_api.get_nfs_export(export_id) 

418 

419 self.assertEqual(1, len(m.request_history)) 

420 self.assertIsNone(r) 

421 

422 @requests_mock.mock() 

423 def test_lookup_smb_share(self, m): 

424 self.assertEqual(0, len(m.request_history)) 

425 share_name = 'my_smb_share' 

426 share_json = '{"id": "my_smb_share"}' 

427 response_json = '{{"shares": [{0}]}}'.format(share_json) 

428 m.get('{0}/platform/1/protocols/smb/shares/{1}' 

429 .format(self._mock_url, share_name), status_code=200, 

430 json=json.loads(response_json)) 

431 

432 r = self.powerscale_api.lookup_smb_share(share_name) 

433 

434 self.assertEqual(1, len(m.request_history)) 

435 self.assertEqual(json.loads(share_json), r) 

436 

437 @requests_mock.mock() 

438 def test_lookup_smb_share_error(self, m): 

439 self.assertEqual(0, len(m.request_history)) 

440 share_name = 'my_smb_share' 

441 m.get('{0}/platform/1/protocols/smb/shares/{1}'.format( 

442 self._mock_url, share_name), status_code=404) 

443 

444 r = self.powerscale_api.lookup_smb_share(share_name) 

445 

446 self.assertEqual(1, len(m.request_history)) 

447 self.assertIsNone(r) 

448 

449 @ddt.data((201, True), (404, False)) 

450 def test_create_nfs_export(self, data): 

451 status_code, expected_return_value = data 

452 with requests_mock.mock() as m: 

453 self.assertEqual(0, len(m.request_history)) 

454 export_path = '/ifs/home/test' 

455 m.post(self._mock_url + '/platform/1/protocols/nfs/exports', 

456 status_code=status_code) 

457 

458 r = self.powerscale_api.create_nfs_export(export_path) 

459 

460 self.assertEqual(1, len(m.request_history)) 

461 call = m.request_history[0] 

462 expected_request_body = '{"paths": ["/ifs/home/test"]}' 

463 self.assertEqual(json.loads(expected_request_body), 

464 json.loads(call.body)) 

465 self.assertEqual(expected_return_value, r) 

466 

467 @ddt.data((201, True), (404, False)) 

468 def test_create_smb_share(self, data): 

469 status_code, expected_return_value = data 

470 with requests_mock.mock() as m: 

471 self.assertEqual(0, len(m.request_history)) 

472 share_name = 'my_smb_share' 

473 share_path = '/ifs/home/admin/smb_share' 

474 m.post(self._mock_url + '/platform/1/protocols/smb/shares', 

475 status_code=status_code) 

476 

477 r = self.powerscale_api.create_smb_share(share_name, share_path) 

478 

479 self.assertEqual(expected_return_value, r) 

480 self.assertEqual(1, len(m.request_history)) 

481 expected_request_data = { 

482 'name': share_name, 

483 'path': share_path, 

484 'permissions': [] 

485 } 

486 self.assertEqual(expected_request_data, 

487 json.loads(m.request_history[0].body)) 

488 

489 @requests_mock.mock() 

490 def test_create_snapshot(self, m): 

491 self.assertEqual(0, len(m.request_history)) 

492 snapshot_name = 'my_snapshot_01' 

493 snapshot_path = '/ifs/home/admin' 

494 m.post( 

495 self._mock_url + '/platform/1/snapshot/snapshots', 

496 status_code=201, 

497 json={"id": 123, "name": snapshot_name, "path": snapshot_path} 

498 ) 

499 

500 r = self.powerscale_api.create_snapshot(snapshot_name, snapshot_path) 

501 

502 self.assertEqual(1, len(m.request_history)) 

503 self.assertTrue(r) 

504 expected_request_body = json.loads( 

505 '{{"name": "{0}", "path": "{1}"}}' 

506 .format(snapshot_name, snapshot_path) 

507 ) 

508 self.assertEqual(expected_request_body, 

509 json.loads(m.request_history[0].body)) 

510 

511 @requests_mock.mock() 

512 def test_create_snapshot_error_case(self, m): 

513 self.assertEqual(0, len(m.request_history)) 

514 snapshot_name = 'my_snapshot_01' 

515 snapshot_path = '/ifs/home/admin' 

516 m.post(self._mock_url + '/platform/1/snapshot/snapshots', 

517 status_code=404) 

518 

519 self.assertIsNone(self.powerscale_api.create_snapshot(snapshot_name, 

520 snapshot_path)) 

521 

522 @ddt.data(True, False) 

523 def test_delete_path(self, is_recursive_delete): 

524 with requests_mock.mock() as m: 

525 self.assertEqual(0, len(m.request_history)) 

526 fq_path = '/ifs/home/admin/test' 

527 m.delete(self._mock_url + '/namespace' + fq_path + '?recursive=' 

528 + str(is_recursive_delete), status_code=204) 

529 

530 self.powerscale_api.delete_path( 

531 fq_path, recursive=is_recursive_delete) 

532 

533 self.assertEqual(1, len(m.request_history)) 

534 

535 @requests_mock.mock() 

536 def test_delete_path_error_case(self, m): 

537 fq_path = '/ifs/home/admin/test' 

538 m.delete(self._mock_url + '/namespace' + fq_path + '?recursive=False', 

539 status_code=403) 

540 

541 self.assertEqual( 

542 self.powerscale_api.delete_path( 

543 fq_path, recursive=False), False) 

544 

545 @ddt.data((204, True), (404, False)) 

546 def test_delete_nfs_share(self, data): 

547 status_code, expected_return_value = data 

548 with requests_mock.mock() as m: 

549 self.assertEqual(0, len(m.request_history)) 

550 share_number = 42 

551 m.delete('{0}/platform/1/protocols/nfs/exports/{1}' 

552 .format(self._mock_url, share_number), 

553 status_code=status_code) 

554 

555 r = self.powerscale_api.delete_nfs_share(share_number) 

556 

557 self.assertEqual(1, len(m.request_history)) 

558 self.assertEqual(expected_return_value, r) 

559 

560 @ddt.data((204, True), (404, False)) 

561 def test_delete_smb_shares(self, data): 

562 status_code, expected_return_value = data 

563 with requests_mock.mock() as m: 

564 self.assertEqual(0, len(m.request_history)) 

565 

566 share_name = 'smb_share_42' 

567 m.delete('{0}/platform/1/protocols/smb/shares/{1}' 

568 .format(self._mock_url, share_name), 

569 status_code=status_code) 

570 

571 r = self.powerscale_api.delete_smb_share(share_name) 

572 

573 self.assertEqual(1, len(m.request_history)) 

574 self.assertEqual(expected_return_value, r) 

575 

576 @requests_mock.mock() 

577 def test_delete_snapshot(self, m): 

578 self.assertEqual(0, len(m.request_history)) 

579 m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot', 

580 status_code=204) 

581 

582 self.powerscale_api.delete_snapshot("my_snapshot") 

583 

584 self.assertEqual(1, len(m.request_history)) 

585 

586 @requests_mock.mock() 

587 def test_delete_snapshot_error_case(self, m): 

588 m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot', 

589 status_code=403) 

590 

591 self.assertEqual( 

592 self.powerscale_api.delete_snapshot("my_snapshot"), False) 

593 

594 @requests_mock.mock() 

595 def test_quota_create(self, m): 

596 quota_path = '/ifs/manila/test' 

597 quota_size = 256 

598 self.assertEqual(0, len(m.request_history)) 

599 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=201) 

600 

601 self.powerscale_api.quota_create(quota_path, 'directory', quota_size) 

602 

603 self.assertEqual(1, len(m.request_history)) 

604 expected_request_json = { 

605 'path': quota_path, 

606 'type': 'directory', 

607 'include_snapshots': False, 

608 'thresholds_include_overhead': False, 

609 'enforced': True, 

610 'thresholds': {'hard': quota_size}, 

611 } 

612 call_body = m.request_history[0].body 

613 self.assertEqual(expected_request_json, json.loads(call_body)) 

614 

615 @requests_mock.mock() 

616 def test_quota_create_with_threshold(self, m): 

617 quota_path = '/ifs/manila/test' 

618 quota_size = 100 

619 self.assertEqual(0, len(m.request_history)) 

620 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=201) 

621 self.powerscale_api_threshold.quota_create( 

622 quota_path, 

623 'directory', 

624 quota_size 

625 ) 

626 

627 advisory_size = round( 

628 (quota_size * self.powerscale_api_threshold.threshold_limit) / 100) 

629 self.assertEqual(1, len(m.request_history)) 

630 expected_request_json = { 

631 'path': quota_path, 

632 'type': 'directory', 

633 'include_snapshots': False, 

634 'thresholds_include_overhead': False, 

635 'enforced': True, 

636 'thresholds': {'hard': quota_size, 

637 'advisory': advisory_size}, 

638 } 

639 call_body = m.request_history[0].body 

640 self.assertEqual(expected_request_json, json.loads(call_body)) 

641 

642 @requests_mock.mock() 

643 def test_quota_create__path_does_not_exist(self, m): 

644 quota_path = '/ifs/test2' 

645 self.assertEqual(0, len(m.request_history)) 

646 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=400) 

647 

648 self.assertRaises( 

649 requests.exceptions.HTTPError, 

650 self.powerscale_api.quota_create, 

651 quota_path, 'directory', 2 

652 ) 

653 

654 @requests_mock.mock() 

655 def test_quota_get(self, m): 

656 self.assertEqual(0, len(m.request_history)) 

657 response_json = {'quotas': [{}]} 

658 m.get(self._mock_url + '/platform/1/quota/quotas', json=response_json, 

659 status_code=200) 

660 quota_path = "/ifs/manila/test" 

661 quota_type = "directory" 

662 

663 self.powerscale_api.quota_get(quota_path, quota_type) 

664 

665 self.assertEqual(1, len(m.request_history)) 

666 request_query_string = m.request_history[0].qs 

667 expected_query_string = {'path': [quota_path]} 

668 self.assertEqual(expected_query_string, request_query_string) 

669 

670 @requests_mock.mock() 

671 def test_quota_get__path_does_not_exist(self, m): 

672 self.assertEqual(0, len(m.request_history)) 

673 m.get(self._mock_url + '/platform/1/quota/quotas', status_code=404) 

674 

675 response = self.powerscale_api.quota_get( 

676 '/ifs/does_not_exist', 'directory') 

677 

678 self.assertIsNone(response) 

679 

680 @requests_mock.mock() 

681 def test_quota_modify(self, m): 

682 self.assertEqual(0, len(m.request_history)) 

683 quota_id = "ADEF1G" 

684 new_size = 1024 

685 m.put('{0}/platform/1/quota/quotas/{1}'.format( 

686 self._mock_url, quota_id), status_code=204) 

687 

688 self.powerscale_api.quota_modify_size(quota_id, new_size) 

689 

690 self.assertEqual(1, len(m.request_history)) 

691 expected_request_body = {'thresholds': {'hard': new_size}} 

692 request_body = m.request_history[0].body 

693 self.assertEqual(expected_request_body, json.loads(request_body)) 

694 

695 @requests_mock.mock() 

696 def test_quota_modify_with_threshold(self, m): 

697 self.assertEqual(0, len(m.request_history)) 

698 quota_id = "ADEF1G" 

699 new_size = 1024 

700 advisory_size = round( 

701 (new_size * self.powerscale_api_threshold.threshold_limit) / 100) 

702 m.put('{0}/platform/1/quota/quotas/{1}'.format( 

703 self._mock_url, quota_id), status_code=204) 

704 self.powerscale_api_threshold.quota_modify_size(quota_id, new_size) 

705 self.assertEqual(1, len(m.request_history)) 

706 expected_request_body = {'thresholds': {'hard': new_size, 

707 'advisory': advisory_size}} 

708 request_body = m.request_history[0].body 

709 self.assertEqual(expected_request_body, json.loads(request_body)) 

710 

711 @requests_mock.mock() 

712 def test_quota_modify__given_id_does_not_exist(self, m): 

713 quota_id = 'ADE2F' 

714 m.put('{0}/platform/1/quota/quotas/{1}'.format( 

715 self._mock_url, quota_id), status_code=404) 

716 

717 self.assertRaises( 

718 requests.exceptions.HTTPError, 

719 self.powerscale_api.quota_modify_size, 

720 quota_id, 1024 

721 ) 

722 

723 @requests_mock.mock() 

724 def test_quota_set__quota_already_exists(self, m): 

725 self.assertEqual(0, len(m.request_history)) 

726 quota_path = '/ifs/manila/test' 

727 quota_type = 'directory' 

728 quota_size = 256 

729 quota_id = 'AFE2C' 

730 m.get('{0}/platform/1/quota/quotas'.format( 

731 self._mock_url), json={'quotas': [{'id': quota_id}]}, 

732 status_code=200) 

733 m.put( 

734 '{0}/platform/1/quota/quotas/{1}'.format(self._mock_url, quota_id), 

735 status_code=204 

736 ) 

737 

738 self.powerscale_api.quota_set(quota_path, quota_type, quota_size) 

739 

740 expected_quota_modify_json = {'thresholds': {'hard': quota_size}} 

741 quota_put_json = json.loads(m.request_history[1].body) 

742 self.assertEqual(expected_quota_modify_json, quota_put_json) 

743 

744 @requests_mock.mock() 

745 def test_quota_set__quota_does_not_already_exist(self, m): 

746 self.assertEqual(0, len(m.request_history)) 

747 m.get('{0}/platform/1/quota/quotas'.format( 

748 self._mock_url), status_code=404) 

749 m.post('{0}/platform/1/quota/quotas'.format(self._mock_url), 

750 status_code=201) 

751 quota_path = '/ifs/manila/test' 

752 quota_type = 'directory' 

753 quota_size = 256 

754 

755 self.powerscale_api.quota_set(quota_path, quota_type, quota_size) 

756 

757 # verify a call is made to create a quota 

758 expected_create_json = { 

759 str('path'): quota_path, 

760 str('type'): 'directory', 

761 str('include_snapshots'): False, 

762 str('thresholds_include_overhead'): False, 

763 str('enforced'): True, 

764 str('thresholds'): {str('hard'): quota_size}, 

765 } 

766 create_request_json = json.loads(m.request_history[1].body) 

767 self.assertEqual(expected_create_json, create_request_json) 

768 

769 @requests_mock.mock() 

770 def test_quota_set__path_does_not_already_exist(self, m): 

771 m.get(self._mock_url + '/platform/1/quota/quotas', status_code=400) 

772 

773 e = self.assertRaises( 

774 requests.exceptions.HTTPError, 

775 self.powerscale_api.quota_set, 

776 '/ifs/does_not_exist', 'directory', 2048 

777 ) 

778 self.assertEqual(400, e.response.status_code) 

779 

780 def test_get_user_sid_success(self): 

781 sid = {"id": "SID:S-1-22-1-0", 

782 "name": "foo", 

783 "type": "user"} 

784 self.powerscale_api.auth_lookup_user = mock.MagicMock( 

785 return_value={ 

786 "mapping": [{"user": {"sid": sid}}] 

787 } 

788 ) 

789 expected_sid = self.powerscale_api.get_user_sid('foo') 

790 self.assertEqual(expected_sid, sid) 

791 

792 def test_get_user_sid_wrong_mappings(self): 

793 self.powerscale_api.auth_lookup_user = mock.MagicMock( 

794 return_value={ 

795 "mapping": [{"user": {"sid": 'fake_sid1'}}, 

796 {"user": {"sid": 'fake_sid2'}}] 

797 } 

798 ) 

799 expected_sid = self.powerscale_api.get_user_sid('foo') 

800 self.assertIsNone(expected_sid) 

801 

802 def test_get_user_sid_user_not_found(self): 

803 self.powerscale_api.auth_lookup_user = mock.MagicMock( 

804 return_value=None 

805 ) 

806 expected_sid = self.powerscale_api.get_user_sid('foo') 

807 self.assertIsNone(expected_sid) 

808 

809 @requests_mock.mock() 

810 def test_auth_lookup_user(self, m): 

811 user = 'foo' 

812 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format( 

813 self._mock_url, user) 

814 example_sid = 'SID:S-1-5-21' 

815 sid_json = { 

816 'id': example_sid, 

817 'name': user, 

818 'type': 'user' 

819 } 

820 auth_json = { 

821 'mapping': [ 

822 {'user': {'sid': sid_json}} 

823 ] 

824 } 

825 m.get(auth_url, status_code=200, json=auth_json) 

826 

827 returned_auth_json = self.powerscale_api.auth_lookup_user(user) 

828 self.assertEqual(auth_json, returned_auth_json) 

829 

830 @requests_mock.mock() 

831 def test_auth_lookup_user_with_nonexistent_user(self, m): 

832 user = 'nonexistent' 

833 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format( 

834 self._mock_url, user) 

835 m.get(auth_url, status_code=404) 

836 self.assertIsNone(self.powerscale_api.auth_lookup_user(user)) 

837 

838 @requests_mock.mock() 

839 def test_auth_lookup_user_with_backend_error(self, m): 

840 user = 'foo' 

841 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format( 

842 self._mock_url, user) 

843 m.get(auth_url, status_code=400) 

844 self.assertIsNone(self.powerscale_api.auth_lookup_user(user)) 

845 

846 def _add_create_directory_response(self, m, path, is_recursive): 

847 url = '{0}/namespace{1}?recursive={2}'.format( 

848 self._mock_url, path, str(is_recursive)) 

849 m.put(url, status_code=200) 

850 

851 def _add_file_clone_response(self, m, fq_dest_path, snapshot_name): 

852 url = '{0}/namespace{1}?clone=true&snapshot={2}'.format( 

853 self._mock_url, fq_dest_path, snapshot_name) 

854 m.put(url) 

855 

856 def _add_get_directory_listing_response(self, m, fq_dir_path, json_str): 

857 url = '{0}/namespace{1}?detail=default'.format( 

858 self._mock_url, fq_dir_path) 

859 m.get(url, json=json.loads(json_str), status_code=200) 

860 

861 def _add_get_snapshot_response( 

862 self, m, snapshot_name, json_str, status=200): 

863 url = '{0}/platform/1/snapshot/snapshots/{1}'.format( 

864 self._mock_url, snapshot_name 

865 ) 

866 m.get(url, status_code=status, json=json.loads(json_str)) 

867 

868 def _verify_dir_creation_request(self, request, path, is_recursive): 

869 self.assertEqual('PUT', request.method) 

870 expected_url = '{0}/namespace{1}?recursive={2}'.format( 

871 self._mock_url, path, str(is_recursive)) 

872 self.assertEqual(expected_url, request.url) 

873 self.assertIn("x-isi-ifs-target-type", request.headers) 

874 self.assertEqual("container", 

875 request.headers['x-isi-ifs-target-type']) 

876 self.assertIn("x-isi-ifs-access-control", request.headers) 

877 self.assertEqual(self.dir_permission, 

878 request.headers['x-isi-ifs-access-control']) 

879 

880 def _verify_clone_file_from_snapshot( 

881 self, request, fq_file_path, fq_dest_path, snapshot_name): 

882 self.assertEqual('PUT', request.method) 

883 expected_url = '{0}/namespace{1}?clone=true&snapshot={2}'.format( 

884 self._mock_url, fq_dest_path, snapshot_name 

885 ) 

886 self.assertEqual(expected_url, request.request.url) 

887 self.assertIn("x-isi-ifs-copy-source", request.headers) 

888 self.assertEqual('/namespace' + fq_file_path, 

889 request.headers['x-isi-ifs-copy-source']) 

890 

891 def test_modify_nfs_export_access_success(self): 

892 self.powerscale_api.send_put_request = mock.MagicMock() 

893 share_id = '123' 

894 ro_ips = ['10.0.0.1', '10.0.0.2'] 

895 rw_ips = ['10.0.0.3', '10.0.0.4'] 

896 self.powerscale_api.modify_nfs_export_access(share_id, ro_ips, rw_ips) 

897 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format( 

898 self.powerscale_api.host_url, share_id) 

899 expected_data = {'read_only_clients': ro_ips, 'clients': rw_ips} 

900 self.powerscale_api.send_put_request.assert_called_once_with( 

901 expected_url, data=expected_data) 

902 

903 def test_modify_nfs_export_access_no_ro_ips(self): 

904 self.powerscale_api.send_put_request = mock.MagicMock() 

905 share_id = '123' 

906 rw_ips = ['10.0.0.3', '10.0.0.4'] 

907 self.powerscale_api.modify_nfs_export_access(share_id, None, rw_ips) 

908 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format( 

909 self.powerscale_api.host_url, share_id) 

910 expected_data = {'clients': rw_ips} 

911 self.powerscale_api.send_put_request.assert_called_once_with( 

912 expected_url, data=expected_data) 

913 

914 def test_modify_nfs_export_access_no_rw_ips(self): 

915 self.powerscale_api.send_put_request = mock.MagicMock() 

916 share_id = '123' 

917 ro_ips = ['10.0.0.1', '10.0.0.2'] 

918 self.powerscale_api.modify_nfs_export_access(share_id, ro_ips, None) 

919 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format( 

920 self.powerscale_api.host_url, share_id) 

921 expected_data = {'read_only_clients': ro_ips} 

922 self.powerscale_api.send_put_request.assert_called_once_with( 

923 expected_url, data=expected_data) 

924 

925 @mock.patch('requests.Session.request') 

926 def test_request_with_401_response(self, mock_request): 

927 """Test sending a request with a 401 Unauthorized response.""" 

928 mock_request.return_value.status_code = 401 

929 self.powerscale_api.create_session = mock.MagicMock(return_value=True) 

930 self.powerscale_api.request('GET', 'http://example.com/api/data') 

931 self.assertEqual(mock_request.call_count, 2) 

932 

933 def test_delete_quota_sends_delete_request(self): 

934 self.powerscale_api.send_delete_request = mock.MagicMock() 

935 quota_id = '123' 

936 self.powerscale_api.delete_quota(quota_id) 

937 self.powerscale_api.send_delete_request.assert_called_once_with( 

938 '{0}/platform/1/quota/quotas/{1}'.format( 

939 self.powerscale_api.host_url, quota_id) 

940 ) 

941 

942 def test_delete_quota_raises_exception_on_error(self): 

943 quota_id = '123' 

944 self.powerscale_api.send_delete_request = mock.MagicMock( 

945 side_effect=requests.exceptions.HTTPError) 

946 self.assertRaises(requests.exceptions.HTTPError, 

947 self.powerscale_api.delete_quota, 

948 quota_id) 

949 

950 def test_get_space_stats_success(self): 

951 self.powerscale_api.send_get_request = mock.MagicMock() 

952 self.powerscale_api.send_get_request.return_value.status_code = 200 

953 self.powerscale_api.send_get_request.return_value.json.return_value = { 

954 'stats': [ 

955 {'key': 'ifs.bytes.free', 'value': 1000}, 

956 {'key': 'ifs.bytes.total', 'value': 2000}, 

957 {'key': 'ifs.bytes.used', 'value': 500} 

958 ] 

959 } 

960 result = self.powerscale_api.get_space_stats() 

961 self.assertEqual(result, {'total': 2000, 'free': 1000, 'used': 500}) 

962 

963 def test_get_space_stats_failure(self): 

964 self.powerscale_api.send_get_request = mock.MagicMock() 

965 self.powerscale_api.send_get_request.return_value.status_code = 400 

966 self.assertRaises(exception.ShareBackendException, 

967 self.powerscale_api.get_space_stats) 

968 

969 def test_get_allocated_space_success(self): 

970 self.powerscale_api.send_get_request = mock.MagicMock() 

971 self.powerscale_api.send_get_request.return_value.status_code = 200 

972 self.powerscale_api.send_get_request.return_value.json.return_value = { 

973 'quotas': [ 

974 { 

975 'path': '/ifs/home', 

976 'thresholds': { 

977 'hard': None 

978 } 

979 }, 

980 { 

981 'path': '/ifs/manila/CI-1d52ed66-a1ee-4b19-8f56-3706b', 

982 'thresholds': { 

983 'hard': 2147483648000 

984 } 

985 }, 

986 { 

987 'path': '/ifs/manila/CI-0b622133-8b58-4a9f-ad1a-b8247', 

988 'thresholds': { 

989 'hard': 107374182400 

990 } 

991 }, 

992 { 

993 'path': '/ifs/nilesh', 

994 'thresholds': { 

995 'hard': 10737418240 

996 } 

997 } 

998 ] 

999 } 

1000 result = self.powerscale_api.get_allocated_space() 

1001 self.assertEqual(result, 2110.0) 

1002 

1003 def test_get_allocated_space_failure(self): 

1004 self.powerscale_api.send_get_request = mock.MagicMock() 

1005 self.powerscale_api.send_get_request.return_value.status_code = 400 

1006 self.assertRaises(exception.ShareBackendException, 

1007 self.powerscale_api.get_allocated_space) 

1008 

1009 def test_get_cluster_version_success(self): 

1010 self.powerscale_api.send_get_request = mock.MagicMock() 

1011 self.powerscale_api.send_get_request.return_value.status_code = 200 

1012 self.powerscale_api.send_get_request.return_value.json.return_value = { 

1013 'nodes': [{'release': '1.0'}]} 

1014 

1015 version = self.powerscale_api.get_cluster_version() 

1016 self.assertEqual(version, '1.0') 

1017 self.powerscale_api.send_get_request.assert_called_once_with( 

1018 '{0}/platform/12/cluster/version'.format( 

1019 self.powerscale_api.host_url) 

1020 ) 

1021 

1022 def test_get_cluster_version_failure(self): 

1023 self.powerscale_api.send_get_request = mock.MagicMock() 

1024 self.powerscale_api.send_get_request.return_value.status_code = 404 

1025 

1026 self.assertRaises(exception.ShareBackendException, 

1027 self.powerscale_api.get_cluster_version) 

1028 

1029 self.powerscale_api.send_get_request.assert_called_once_with( 

1030 '{0}/platform/12/cluster/version'.format( 

1031 self.powerscale_api.host_url) 

1032 ) 

1033 

1034 def test_modify_smb_share_access_with_host_acl_and_smb_permission(self): 

1035 self.powerscale_api.send_put_request = mock.MagicMock() 

1036 share_name = 'my_share' 

1037 host_acl = 'host1,host2' 

1038 smb_permission = 'read' 

1039 self.powerscale_api.modify_smb_share_access( 

1040 share_name, host_acl, smb_permission) 

1041 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format( 

1042 self.powerscale_api.host_url, share_name) 

1043 expected_data = {'host_acl': host_acl, 'permissions': smb_permission} 

1044 self.powerscale_api.send_put_request.assert_called_with( 

1045 expected_url, data=expected_data) 

1046 

1047 def test_modify_smb_share_access_with_host_acl_only(self): 

1048 self.powerscale_api.send_put_request = mock.MagicMock() 

1049 share_name = 'my_share' 

1050 host_acl = 'host1,host2' 

1051 self.powerscale_api.modify_smb_share_access(share_name, host_acl) 

1052 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format( 

1053 self.powerscale_api.host_url, share_name) 

1054 expected_data = {'host_acl': host_acl} 

1055 self.powerscale_api.send_put_request.assert_called_with( 

1056 expected_url, data=expected_data) 

1057 

1058 def test_modify_smb_share_access_with_smb_permission_only(self): 

1059 self.powerscale_api.send_put_request = mock.MagicMock() 

1060 share_name = 'my_share' 

1061 smb_permission = 'read' 

1062 self.powerscale_api.modify_smb_share_access( 

1063 share_name, permissions=smb_permission) 

1064 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format( 

1065 self.powerscale_api.host_url, share_name) 

1066 expected_data = {'permissions': smb_permission} 

1067 self.powerscale_api.send_put_request.assert_called_with( 

1068 expected_url, data=expected_data) 

1069 

1070 def test_modify_smb_share_access_with_no_arguments(self): 

1071 self.powerscale_api.send_put_request = mock.MagicMock() 

1072 share_name = 'my_share' 

1073 self.powerscale_api.modify_smb_share_access(share_name) 

1074 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format( 

1075 self.powerscale_api.host_url, share_name) 

1076 expected_data = {} 

1077 self.powerscale_api.send_put_request.assert_called_with( 

1078 expected_url, data=expected_data) 

1079 

1080 def test_modify_smb_share_access_with_http_error(self): 

1081 self.powerscale_api.send_put_request = mock.MagicMock( 

1082 side_effect=requests.exceptions.HTTPError 

1083 ) 

1084 share_name = 'my_share' 

1085 host_acl = 'host1,host2' 

1086 smb_permission = 'read' 

1087 

1088 self.assertRaises(requests.exceptions.HTTPError, 

1089 self.powerscale_api.modify_smb_share_access, 

1090 share_name, host_acl, smb_permission) 

1091 

1092 @requests_mock.mock() 

1093 def test_get_snapshot_id_status_200_returns_first_snapshot(self, m): 

1094 snap_id = "snap123" 

1095 fake_snapshot = {'id': 'snap123', 'path': '/ifs/test'} 

1096 

1097 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id 

1098 

1099 m.get(url, status_code=200, 

1100 json={'snapshots': [fake_snapshot]}) 

1101 

1102 result = self.powerscale_api.get_snapshot_id(snap_id) 

1103 

1104 self.assertEqual(fake_snapshot, result) 

1105 

1106 @requests_mock.mock() 

1107 def test_get_snapshot_id_status_404_returns_none(self, m): 

1108 snap_id = "snap404" 

1109 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id 

1110 

1111 # Provide a valid JSON body so r.json() doesn't fail 

1112 m.get(url, status_code=404, json={}) 

1113 

1114 result = self.powerscale_api.get_snapshot_id(snap_id) 

1115 

1116 self.assertIsNone(result) 

1117 

1118 @requests_mock.mock() 

1119 def test_get_snapshot_id_other_status_raises(self, m): 

1120 snap_id = "snapFail" 

1121 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id 

1122 

1123 # Again, give it valid JSON so r.json() succeeds first 

1124 m.get(url, status_code=500, json={"error": "Fake error"}) 

1125 

1126 self.assertRaises( 

1127 requests.exceptions.HTTPError, 

1128 self.powerscale_api.get_snapshot_id, 

1129 snap_id, 

1130 ) 

1131 

1132 @requests_mock.mock() 

1133 def test_delete_snapshot_by_id_success_204(self, m): 

1134 snapshot_id = "snap123" 

1135 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snapshot_id 

1136 

1137 m.delete(url, status_code=204) 

1138 

1139 result = self.powerscale_api.delete_snapshot_by_id(snapshot_id) 

1140 self.assertTrue(result) 

1141 

1142 @requests_mock.mock() 

1143 def test_delete_snapshot_by_id_non_204(self, m): 

1144 snapshot_id = "snap123" 

1145 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snapshot_id 

1146 

1147 m.delete(url, status_code=409) 

1148 

1149 result = self.powerscale_api.delete_snapshot_by_id(snapshot_id) 

1150 self.assertFalse(result) 

1151 

1152 @ddt.data((201, True), (400, False), (500, False)) 

1153 def test_create_snapshot_nfs_export(self, data): 

1154 status_code, expected_return_value = data 

1155 with requests_mock.mock() as m: 

1156 self.assertEqual(0, len(m.request_history)) 

1157 export_path = "/ifs/manila-test/.snapshots/snap-001" 

1158 url = self._mock_url + "/platform/22/protocols/nfs/exports" 

1159 m.post(url, status_code=status_code) 

1160 r = self.powerscale_api.create_snapshot_nfs_export(export_path) 

1161 self.assertEqual(expected_return_value, r) 

1162 self.assertEqual(1, len(m.request_history)) 

1163 call = m.request_history[0] 

1164 expected_request_body = { 

1165 "paths": [export_path], 

1166 "read_only": True, 

1167 "map_root": {"enabled": False}, 

1168 } 

1169 self.assertEqual(expected_request_body, json.loads(call.body)) 

1170 self.assertEqual(url, call.url) 

1171 

1172 @ddt.data((201, True), (404, False), (500, False)) 

1173 def test_create_snapshot_smb_export(self, data): 

1174 status_code, expected_return_value = data 

1175 with requests_mock.mock() as m: 

1176 self.assertEqual(0, len(m.request_history)) 

1177 snapshot_name = "snap-001" 

1178 share_path = "/ifs/manila-test/.snapshots/snap-001" 

1179 url = self._mock_url + "/platform/1/protocols/smb/shares" 

1180 m.post(url, status_code=status_code) 

1181 r = self.powerscale_api.create_snapshot_smb_export( 

1182 snapshot_name, share_path) 

1183 self.assertEqual(expected_return_value, r) 

1184 self.assertEqual(1, len(m.request_history)) 

1185 expected_request_data = { 

1186 "name": snapshot_name, 

1187 "path": share_path, 

1188 } 

1189 self.assertEqual(expected_request_data, 

1190 json.loads(m.request_history[0].body)) 

1191 self.assertEqual(url, m.request_history[0].url) 

1192 

1193 @requests_mock.mock() 

1194 def test_create_nfs_export_aliases_success(self, m): 

1195 self.assertEqual(0, len(m.request_history)) 

1196 alias_name = 'my_custom_name' 

1197 alias_path = '/ifs/my_share' 

1198 expected_url = (self. 

1199 _mock_url + 

1200 '/platform/12/protocols/nfs/aliases?zone=System') 

1201 m.post(expected_url, status_code=201) 

1202 result = (self. 

1203 powerscale_api.create_nfs_export_aliases(alias_name, 

1204 alias_path)) 

1205 self.assertTrue(result) 

1206 self.assertEqual(1, len(m.request_history)) 

1207 expected_body = {"name": alias_name, "path": alias_path} 

1208 self.assertEqual(expected_body, json.loads(m.request_history[0].body)) 

1209 

1210 @ddt.data(400, 404, 500) 

1211 def test_create_nfs_export_aliases_error_codes(self, status): 

1212 with requests_mock.mock() as m: 

1213 self.assertEqual(0, len(m.request_history)) 

1214 alias_name = 'my_custom_name' 

1215 alias_path = '/ifs/my_share' 

1216 url = (self. 

1217 _mock_url + 

1218 '/platform/12/protocols/nfs/aliases?zone=System') 

1219 m.post(url, status_code=status) 

1220 result = (self. 

1221 powerscale_api.create_nfs_export_aliases(alias_name, 

1222 alias_path)) 

1223 self.assertFalse(result) 

1224 self.assertEqual(1, len(m.request_history)) 

1225 

1226 @requests_mock.mock() 

1227 def test_delete_nfs_export_aliases_success(self, m): 

1228 self.assertEqual(0, len(m.request_history)) 

1229 alias_name = '/my_custom_name' 

1230 encoded_alias = quote(alias_name, safe='') 

1231 expected_url = (self._mock_url + 

1232 '/platform/12/protocols/nfs/aliases/' + 

1233 encoded_alias + '?zone=System') 

1234 m.delete(expected_url, status_code=204) 

1235 result = self.powerscale_api.delete_nfs_export_aliases(alias_name) 

1236 self.assertTrue(result) 

1237 self.assertEqual(1, len(m.request_history)) 

1238 

1239 @requests_mock.mock() 

1240 def test_delete_nfs_export_aliases_not_found(self, m): 

1241 self.assertEqual(0, len(m.request_history)) 

1242 alias_name = '/my_custom_name' 

1243 encoded_alias = quote(alias_name, safe='') 

1244 expected_url = (self._mock_url + 

1245 '/platform/12/protocols/nfs/aliases/' + 

1246 encoded_alias + '?zone=System') 

1247 m.delete(expected_url, status_code=404) 

1248 result = self.powerscale_api.delete_nfs_export_aliases(alias_name) 

1249 self.assertFalse(result) 

1250 self.assertEqual(1, len(m.request_history)) 

1251 

1252 @requests_mock.mock() 

1253 def test_get_nfs_export_aliases_success(self, m): 

1254 self.assertEqual(0, len(m.request_history)) 

1255 alias_name = '/my_custom_name' 

1256 encoded_alias = quote(alias_name, safe='') 

1257 expected_url = (self._mock_url + 

1258 '/platform/12/protocols/nfs/aliases/' + 

1259 encoded_alias + '?zone=System') 

1260 expected_response = { 

1261 "aliases": [ 

1262 { 

1263 "id": alias_name, 

1264 "name": alias_name, 

1265 "path": "/ifs/my_share", 

1266 "zone": "System" 

1267 } 

1268 ] 

1269 } 

1270 m.get(expected_url, status_code=200, json=expected_response) 

1271 result = self.powerscale_api.get_nfs_export_aliases(alias_name) 

1272 self.assertEqual(expected_response['aliases'][0], result) 

1273 self.assertEqual(1, len(m.request_history)) 

1274 

1275 @ddt.data(400, 404, 500) 

1276 def test_get_nfs_export_aliases_error_codes(self, status): 

1277 with requests_mock.mock() as m: 

1278 self.assertEqual(0, len(m.request_history)) 

1279 alias_name = '/my_custom_name' 

1280 encoded_alias = quote(alias_name, safe='') 

1281 expected_url = (self._mock_url + 

1282 '/platform/12/protocols/nfs/aliases/' + 

1283 encoded_alias + '?zone=System') 

1284 m.get(expected_url, status_code=status) 

1285 self.assertRaises( 

1286 exception.ShareBackendException, 

1287 self.powerscale_api.get_nfs_export_aliases, 

1288 alias_name 

1289 ) 

1290 self.assertEqual(1, len(m.request_history))