Coverage for manila/tests/share/drivers/dell_emc/plugins/powerscale/test_powerscale.py: 100%

809 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_log import log 

20from oslo_utils import units 

21 

22from manila.common import constants as const 

23from manila import exception 

24from manila.i18n import _ 

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

26from manila import test 

27 

28LOG = log.getLogger(__name__) 

29 

30 

31@ddt.ddt 

32class PowerScaleTest(test.TestCase): 

33 """Integration test for the PowerScale Manila driver.""" 

34 

35 POWERSCALE_ADDR = '10.0.0.1' 

36 API_URL = 'https://%s:8080' % POWERSCALE_ADDR 

37 AUTH = ('admin', 'admin') 

38 

39 ROOT_DIR = '/ifs/manila-test' 

40 SHARE_NAME = 'share-foo' 

41 SHARE_DIR = ROOT_DIR + '/' + SHARE_NAME 

42 ADMIN_HOME_DIR = '/ifs/home/admin' 

43 CLONE_DIR = ROOT_DIR + '/clone-dir' 

44 

45 class MockConfig(object): 

46 

47 def safe_get(self, value): 

48 if value == 'emc_nas_server': 

49 return '10.0.0.1' 

50 elif value == 'emc_nas_server_port': 

51 return '8080' 

52 elif value == 'emc_nas_login': 

53 return 'admin' 

54 elif value == 'emc_nas_password': 

55 return 'a' 

56 elif value == 'emc_nas_root_dir': 

57 return '/ifs/manila-test' 

58 elif value == 'powerscale_dir_permission': 

59 return '0777' 

60 else: 

61 return None 

62 

63 class MockInvalidConfig(object): 

64 

65 def safe_get(self, value): 

66 if value == 'emc_nas_server': 

67 return '10.0.0.1' 

68 elif value == 'emc_nas_server_port': 

69 return '8080' 

70 elif value == 'emc_nas_login': 

71 return 'admin' 

72 elif value == 'emc_nas_root_dir': 

73 return '/ifs/manila-test' 

74 else: 

75 return None 

76 

77 @mock.patch( 

78 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.' 

79 'powerscale_api.PowerScaleApi', autospec=True) 

80 def setUp(self, mock_isi_api): 

81 super(PowerScaleTest, self).setUp() 

82 

83 self._mock_powerscale_api = mock_isi_api.return_value 

84 self.storage_connection = powerscale.PowerScaleStorageConnection(LOG) 

85 

86 self.mock_context = mock.Mock('Context') 

87 self.mock_emc_driver = mock.Mock('EmcDriver') 

88 

89 self.mock_emc_driver.attach_mock(self.MockConfig(), 'configuration') 

90 self.storage_connection.connect( 

91 self.mock_emc_driver, self.mock_context) 

92 

93 def test_allow_access(self): 

94 self.assertRaises( 

95 NotImplementedError, 

96 self.storage_connection.allow_access, 

97 self.mock_context, 

98 share=None, 

99 access=None, 

100 share_server=None, 

101 ) 

102 

103 def test_deny_access(self): 

104 self.assertRaises( 

105 NotImplementedError, 

106 self.storage_connection.deny_access, 

107 self.mock_context, 

108 share=None, 

109 access=None, 

110 share_server=None, 

111 ) 

112 

113 def test_create_share_nfs(self): 

114 share_path = self.SHARE_DIR 

115 self.assertFalse(self._mock_powerscale_api.create_directory.called) 

116 self.assertFalse(self._mock_powerscale_api.create_nfs_export.called) 

117 

118 # create the share 

119 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', "size": 8} 

120 location = self.storage_connection.create_share(self.mock_context, 

121 share, None) 

122 

123 # verify location and API call made 

124 path = '%s:%s' % (self.POWERSCALE_ADDR, self.SHARE_DIR) 

125 expected_location = [{'is_admin_only': False, 

126 'metadata': {"preferred": True}, 

127 'path': path}] 

128 

129 self.assertEqual(expected_location, location) 

130 self._mock_powerscale_api.create_directory.assert_called_with( 

131 share_path, False) 

132 self._mock_powerscale_api.create_nfs_export.assert_called_with( 

133 share_path) 

134 

135 # verify directory quota call made 

136 self._mock_powerscale_api.quota_create.assert_called_with( 

137 share_path, 'directory', 8 * units.Gi) 

138 

139 def test_create_share_cifs(self): 

140 self.assertFalse(self._mock_powerscale_api.create_directory.called) 

141 self.assertFalse(self._mock_powerscale_api.create_smb_share.called) 

142 

143 # create the share 

144 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS', "size": 8} 

145 location = self.storage_connection.create_share(self.mock_context, 

146 share, None) 

147 path = '\\\\{0}\\{1}'.format(self.POWERSCALE_ADDR, self.SHARE_NAME) 

148 expected_location = [{'is_admin_only': False, 

149 'metadata': {"preferred": True}, 

150 'path': path}] 

151 

152 self.assertEqual(expected_location, location) 

153 self._mock_powerscale_api.create_directory.assert_called_once_with( 

154 self.SHARE_DIR, False) 

155 self._mock_powerscale_api.create_smb_share.assert_called_once_with( 

156 self.SHARE_NAME, self.SHARE_DIR) 

157 

158 # verify directory quota call made 

159 self._mock_powerscale_api.quota_create.assert_called_with( 

160 self.SHARE_DIR, 'directory', 8 * units.Gi) 

161 

162 def test_create_share_invalid_share_protocol(self): 

163 share = {"name": self.SHARE_NAME, "share_proto": 'FOO_PROTOCOL'} 

164 

165 self.assertRaises( 

166 exception.InvalidShare, self.storage_connection.create_share, 

167 self.mock_context, share, share_server=None) 

168 

169 def test_create_share_nfs_backend_failure(self): 

170 share = {"name": self.SHARE_NAME, "share_proto": 'NFS'} 

171 self._mock_powerscale_api.create_nfs_export.return_value = False 

172 

173 self.assertRaises( 

174 exception.ShareBackendException, 

175 self.storage_connection.create_share, 

176 self.mock_context, share, share_server=None) 

177 

178 def test_create_share_cifs_backend_failure(self): 

179 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} 

180 self._mock_powerscale_api.create_smb_share.return_value = False 

181 

182 self.assertRaises( 

183 exception.ShareBackendException, 

184 self.storage_connection.create_share, 

185 self.mock_context, share, share_server=None) 

186 

187 def test_create_directory_backend_failure(self): 

188 share = {"name": self.SHARE_NAME, "share_proto": 'NFS'} 

189 self._mock_powerscale_api.create_directory.return_value = False 

190 

191 self.assertRaises( 

192 exception.ShareBackendException, 

193 self.storage_connection.create_share, 

194 self.mock_context, share, share_server=None) 

195 

196 def test_create_snapshot(self): 

197 

198 # create snapshot 

199 snapshot_name = "snapshot01" 

200 snapshot_path = '/ifs/home/admin' 

201 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

202 "mount_snapshot_support": False} 

203 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

204 'share': share} 

205 self.storage_connection.create_snapshot(self.mock_context, snapshot, 

206 None) 

207 

208 # verify the create snapshot API call is executed 

209 self._mock_powerscale_api.create_snapshot.assert_called_with( 

210 snapshot_name, snapshot_path) 

211 

212 def test_create_snapshot_backend_failure(self): 

213 snapshot_name = "snapshot01" 

214 snapshot_path = '/ifs/home/admin' 

215 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

216 "mount_snapshot_support": False} 

217 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

218 'share': share} 

219 self._mock_powerscale_api.create_snapshot.return_value = None 

220 

221 self._mock_powerscale_api.create_snapshot.return_value = None 

222 self.assertRaises( 

223 exception.ShareBackendException, 

224 self.storage_connection.create_snapshot, 

225 self.mock_context, snapshot, None) 

226 

227 def test_create_share_from_snapshot_nfs(self): 

228 # assertions 

229 self.assertFalse(self._mock_powerscale_api.create_nfs_export.called) 

230 self.assertFalse(self._mock_powerscale_api.clone_snapshot.called) 

231 

232 snapshot_name = "snapshot01" 

233 snapshot_path = '/ifs/home/admin' 

234 

235 # execute method under test 

236 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

237 'provider_location': None, } 

238 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 'size': 5, 

239 'share_type_id': 'fake_id', } 

240 location = self.storage_connection.create_share_from_snapshot( 

241 self.mock_context, share, snapshot, None) 

242 

243 # verify NFS export created at expected location 

244 self._mock_powerscale_api.create_nfs_export.assert_called_with( 

245 self.SHARE_DIR) 

246 

247 # verify clone_directory(container_path) method called 

248 self._mock_powerscale_api.clone_snapshot.assert_called_once_with( 

249 snapshot_name, self.SHARE_DIR, None) 

250 path = '{0}:{1}'.format( 

251 self.POWERSCALE_ADDR, self.SHARE_DIR) 

252 expected_location = {'is_admin_only': False, 

253 'metadata': {"preferred": True}, 

254 'path': path} 

255 

256 self.assertEqual(expected_location, location[0]) 

257 

258 # verify directory quota call made 

259 self._mock_powerscale_api.quota_create.assert_called_with( 

260 self.SHARE_DIR, 'directory', 5 * units.Gi) 

261 

262 def test_create_share_from_snapshot_cifs(self): 

263 # assertions 

264 self.assertFalse(self._mock_powerscale_api.create_smb_share.called) 

265 self.assertFalse(self._mock_powerscale_api.clone_snapshot.called) 

266 # setup 

267 snapshot_name = "snapshot01" 

268 snapshot_path = '/ifs/home/admin' 

269 new_share_name = 'clone-dir' 

270 

271 # execute method under test 

272 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

273 'provider_location': None, } 

274 share = {"name": new_share_name, "share_proto": 'CIFS', "size": 2, 

275 'share_type_id': 'fake_id', } 

276 location = self.storage_connection.create_share_from_snapshot( 

277 self.mock_context, share, snapshot, None) 

278 

279 # verify call made to create new CIFS share 

280 self._mock_powerscale_api.create_smb_share.assert_called_once_with( 

281 new_share_name, self.CLONE_DIR) 

282 self._mock_powerscale_api.clone_snapshot.assert_called_once_with( 

283 snapshot_name, self.CLONE_DIR, None) 

284 path = '\\\\{0}\\{1}'.format(self.POWERSCALE_ADDR, new_share_name) 

285 expected_location = {'is_admin_only': False, 

286 'metadata': {"preferred": True}, 

287 'path': path} 

288 self.assertEqual(expected_location, location[0]) 

289 

290 # verify directory quota call made 

291 expected_share_path = '{0}/{1}'.format(self.ROOT_DIR, new_share_name) 

292 self._mock_powerscale_api.quota_create.assert_called_with( 

293 expected_share_path, 'directory', 2 * units.Gi) 

294 

295 def test_delete_share_nfs(self): 

296 share = {"name": self.SHARE_NAME, "share_proto": 'NFS'} 

297 fake_share_num = 42 

298 self._mock_powerscale_api.lookup_nfs_export.return_value = ( 

299 fake_share_num) 

300 self.assertFalse(self._mock_powerscale_api.delete_nfs_share.called) 

301 

302 # delete the share 

303 self.storage_connection.delete_share(self.mock_context, share, None) 

304 

305 # verify share delete 

306 self._mock_powerscale_api.delete_nfs_share.assert_called_with( 

307 fake_share_num) 

308 

309 def test_delete_share_cifs(self): 

310 self.assertFalse(self._mock_powerscale_api.delete_smb_share.called) 

311 

312 # delete the share 

313 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} 

314 self._mock_powerscale_api.lookup_smb_share.return_value = { 

315 'id': self.SHARE_NAME, 

316 'path': '/ifs/manila-test/share-foo', 

317 } 

318 container_path = '/ifs/manila-test/share-foo' 

319 self.storage_connection._get_container_path = mock.MagicMock( 

320 return_value=container_path) 

321 self.storage_connection.delete_share(self.mock_context, share, None) 

322 

323 # verify share deleted 

324 self._mock_powerscale_api.delete_smb_share.assert_called_with( 

325 self.SHARE_NAME) 

326 

327 @mock.patch( 

328 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG') 

329 def test_delete_share_invalid_share_proto(self, mock_log): 

330 share = {"name": self.SHARE_NAME, "share_proto": 'FOO_PROTOCOL'} 

331 

332 self.storage_connection.delete_share(self.mock_context, share, None) 

333 mock_log.warning.assert_called_once_with( 

334 'Unsupported share type: FOO_PROTOCOL.') 

335 

336 def test_delete_nfs_share_backend_failure(self): 

337 share = {"name": self.SHARE_NAME, "share_proto": 'NFS'} 

338 

339 self._mock_powerscale_api.delete_nfs_share.return_value = False 

340 self.assertRaises( 

341 exception.ShareBackendException, 

342 self.storage_connection.delete_share, 

343 self.mock_context, share, None 

344 ) 

345 

346 def test_delete_nfs_share_share_does_not_exist(self): 

347 self._mock_powerscale_api.lookup_nfs_export.return_value = None 

348 share = {"name": self.SHARE_NAME, "share_proto": 'NFS'} 

349 

350 # verify the calling delete on a non-existent share returns and does 

351 # not throw exception 

352 self.storage_connection.delete_share(self.mock_context, share, None) 

353 

354 def test_delete_cifs_share_backend_failure(self): 

355 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} 

356 self._mock_powerscale_api.lookup_smb_share.return_value = { 

357 'id': self.SHARE_NAME, 

358 'path': '/ifs/manila-test/share-foo', 

359 } 

360 container_path = '/ifs/manila-test/share-foo' 

361 self.storage_connection._get_container_path = mock.MagicMock( 

362 return_value=container_path) 

363 self._mock_powerscale_api.delete_smb_share.return_value = False 

364 self.assertRaises( 

365 exception.ShareBackendException, 

366 self.storage_connection.delete_share, 

367 self.mock_context, share, None 

368 ) 

369 

370 def test_delete_cifs_share_share_does_not_exist(self): 

371 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} 

372 self._mock_powerscale_api.lookup_smb_share.return_value = None 

373 

374 # verify the calling delete on a non-existent share returns and does 

375 # not throw exception 

376 self.storage_connection.delete_share(self.mock_context, share, None) 

377 

378 @mock.patch( 

379 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

380 ) 

381 def test_delete_quota_success(self, mock_log): 

382 path = '/path/to/quota' 

383 quota_id = '123' 

384 quota_data = {'id': quota_id} 

385 self._mock_powerscale_api.quota_get.return_value = quota_data 

386 self._mock_powerscale_api.delete_quota.return_value = True 

387 self.storage_connection._delete_quota(path) 

388 self._mock_powerscale_api.quota_get.assert_called_once_with( 

389 path, 'directory') 

390 self._mock_powerscale_api.delete_quota.assert_called_once_with( 

391 quota_id) 

392 mock_log.debug.assert_called_once_with(f'Removing quota {quota_id}') 

393 mock_log.warning.assert_not_called() 

394 

395 @mock.patch( 

396 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

397 ) 

398 def test_delete_quota_failure(self, mock_log): 

399 path = '/path/to/quota' 

400 quota_id = '123' 

401 quota_data = {'id': quota_id} 

402 self._mock_powerscale_api.quota_get.return_value = quota_data 

403 self._mock_powerscale_api.delete_quota.return_value = False 

404 self.storage_connection._delete_quota(path) 

405 self._mock_powerscale_api.quota_get.assert_called_once_with( 

406 path, 'directory') 

407 self._mock_powerscale_api.delete_quota.assert_called_once_with( 

408 quota_id) 

409 mock_log.debug.assert_called_once_with(f'Removing quota {quota_id}') 

410 mock_log.error.assert_called_once_with( 

411 _('Failed to delete quota "%(quota_id)s" for ' 

412 'directory "%(dir)s".') % 

413 {'quota_id': quota_id, 'dir': path}) 

414 mock_log.warning.assert_not_called() 

415 

416 @mock.patch( 

417 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

418 ) 

419 def test_delete_quota_not_found(self, mock_log): 

420 path = '/path/to/quota' 

421 self._mock_powerscale_api.quota_get.return_value = None 

422 self.storage_connection._delete_quota(path) 

423 self._mock_powerscale_api.quota_get.assert_called_once_with( 

424 path, 'directory') 

425 self._mock_powerscale_api.delete_quota.assert_not_called() 

426 mock_log.debug.assert_not_called() 

427 mock_log.warning.assert_called_once_with(f'Quota not found for {path}') 

428 

429 @mock.patch( 

430 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

431 ) 

432 def test_delete_directory_success(self, mock_log): 

433 path = '/path/to/directory' 

434 self._mock_powerscale_api.is_path_existent.return_value = True 

435 self._mock_powerscale_api.delete_path.return_value = True 

436 self.storage_connection._delete_directory(path) 

437 self._mock_powerscale_api.delete_path.assert_called_once_with( 

438 path, recursive=True) 

439 mock_log.debug.assert_called_once_with(f'Removing directory {path}') 

440 mock_log.warning.assert_not_called() 

441 

442 @mock.patch( 

443 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

444 ) 

445 def test_delete_directory_failure(self, mock_log): 

446 path = '/path/to/directory' 

447 self._mock_powerscale_api.is_path_existent.return_value = True 

448 self._mock_powerscale_api.delete_path.return_value = False 

449 self.storage_connection._delete_directory(path) 

450 self._mock_powerscale_api.delete_path.assert_called_once_with( 

451 path, recursive=True) 

452 mock_log.debug.assert_called_once_with(f'Removing directory {path}') 

453 mock_log.error.assert_called_once_with( 

454 _('Failed to delete directory "%(dir)s".') % 

455 {'dir': path}) 

456 mock_log.warning.assert_not_called() 

457 

458 @mock.patch( 

459 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.LOG' 

460 ) 

461 def test_delete_directory_not_found(self, mock_log): 

462 path = '/path/to/directory' 

463 self._mock_powerscale_api.is_path_existent.return_value = False 

464 self.storage_connection._delete_directory(path) 

465 self._mock_powerscale_api.delete_path.assert_not_called() 

466 mock_log.warning.assert_called_once_with( 

467 _('Directory not found for %s') % path) 

468 

469 def test_delete_snapshot(self): 

470 # create a snapshot 

471 snapshot_name = "snapshot01" 

472 snapshot_path = '/ifs/home/admin' 

473 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

474 "mount_snapshot_support": False} 

475 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

476 'share': share} 

477 self.assertFalse(self._mock_powerscale_api.delete_snapshot.called) 

478 

479 # delete the created snapshot 

480 self.storage_connection.delete_snapshot(self.mock_context, snapshot, 

481 None) 

482 

483 # verify the API call was made to delete the snapshot 

484 self._mock_powerscale_api.delete_snapshot.assert_called_once_with( 

485 snapshot_name) 

486 

487 def test_delete_snapshot_failure(self): 

488 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

489 "mount_snapshot_support": False} 

490 snapshot = {'name': 'test_snapshot', 'share': share} 

491 self._mock_powerscale_api.delete_snapshot.return_value = False 

492 self.assertRaises( 

493 exception.ShareBackendException, 

494 self.storage_connection.delete_snapshot, 

495 self.mock_context, snapshot, None) 

496 self._mock_powerscale_api.delete_snapshot.assert_called_once_with( 

497 snapshot['name']) 

498 

499 def test_ensure_share(self): 

500 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS'} 

501 self.assertRaises(NotImplementedError, 

502 self.storage_connection.ensure_share, 

503 self.mock_context, share, None) 

504 

505 @mock.patch( 

506 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.' 

507 'powerscale_api.PowerScaleApi', autospec=True) 

508 def test_connect(self, mock_isi_api): 

509 storage_connection = powerscale.PowerScaleStorageConnection(LOG) 

510 

511 # execute method under test 

512 storage_connection.connect( 

513 self.mock_emc_driver, self.mock_context) 

514 

515 # verify connect sets driver params appropriately 

516 mock_config = self.MockConfig() 

517 server_addr = mock_config.safe_get('emc_nas_server') 

518 self.assertEqual(server_addr, storage_connection._server) 

519 expected_port = mock_config.safe_get('emc_nas_server_port') 

520 self.assertEqual(expected_port, storage_connection._port) 

521 self.assertEqual('https://{0}:{1}'.format(server_addr, expected_port), 

522 storage_connection._server_url) 

523 expected_username = mock_config.safe_get('emc_nas_login') 

524 self.assertEqual(expected_username, storage_connection._username) 

525 expected_password = mock_config.safe_get('emc_nas_password') 

526 self.assertEqual(expected_password, storage_connection._password) 

527 self.assertFalse(storage_connection._verify_ssl_cert) 

528 expected_dir_permission = mock_config.safe_get( 

529 'powerscale_dir_permission') 

530 self.assertEqual(expected_dir_permission, 

531 storage_connection._dir_permission) 

532 

533 @mock.patch( 

534 'manila.share.drivers.dell_emc.plugins.powerscale.powerscale.' 

535 'powerscale_api.PowerScaleApi', autospec=True) 

536 def test_connect_root_dir_does_not_exist(self, mock_isi_api): 

537 mock_powerscale_api = mock_isi_api.return_value 

538 mock_powerscale_api.is_path_existent.return_value = False 

539 storage_connection = powerscale.PowerScaleStorageConnection(LOG) 

540 

541 # call method under test 

542 storage_connection.connect(self.mock_emc_driver, self.mock_context) 

543 

544 mock_powerscale_api.create_directory.assert_called_once_with( 

545 self.ROOT_DIR, recursive=True) 

546 

547 def test_connect_invalid_config(self): 

548 mock_emc_driver = mock.Mock('EmcDriver') 

549 mock_emc_driver.attach_mock(self.MockInvalidConfig(), 'configuration') 

550 

551 self.assertRaises(exception.BadConfigurationException, 

552 self.storage_connection.connect, 

553 mock_emc_driver, 

554 self.mock_context 

555 ) 

556 

557 def test_update_share_stats(self): 

558 self._mock_powerscale_api.get_space_stats.return_value = { 

559 'total': 1000 * units.Gi, 

560 'free': 100 * units.Gi, 

561 } 

562 self._mock_powerscale_api.get_allocated_space.return_value = 2110.0 

563 stats_dict = {'share_backend_name': 'PowerScale_backend'} 

564 self.storage_connection.update_share_stats(stats_dict) 

565 

566 expected_pool_stats = { 

567 'pool_name': 'PowerScale_backend', 

568 'reserved_percentage': 0, 

569 'reserved_snapshot_percentage': 0, 

570 'reserved_share_extend_percentage': 0, 

571 'max_over_subscription_ratio': None, 

572 'thin_provisioning': True, 

573 'total_capacity_gb': 1000, 

574 'free_capacity_gb': 100, 

575 'allocated_capacity_gb': 2110.0, 

576 'qos': False, 

577 'mount_snapshot_support': True, 

578 'mount_point_name_support': True, 

579 } 

580 expected_stats = { 

581 'share_backend_name': 'PowerScale_backend', 

582 'driver_version': powerscale.VERSION, 

583 'storage_protocol': 'NFS_CIFS', 

584 'pools': [expected_pool_stats] 

585 } 

586 self.assertEqual(expected_stats, stats_dict) 

587 

588 def test_get_network_allocations_number(self): 

589 # call method under test 

590 num = self.storage_connection.get_network_allocations_number() 

591 

592 self.assertEqual(0, num) 

593 

594 def test_extend_share(self): 

595 quota_id = 'abcdef' 

596 new_share_size = 8 

597 share = { 

598 "name": self.SHARE_NAME, 

599 "share_proto": 'NFS', 

600 "size": new_share_size 

601 } 

602 self._mock_powerscale_api.quota_get.return_value = {'id': quota_id} 

603 self.assertFalse(self._mock_powerscale_api.quota_set.called) 

604 

605 self.storage_connection.extend_share(share, new_share_size) 

606 

607 share_path = '{0}/{1}'.format(self.ROOT_DIR, self.SHARE_NAME) 

608 expected_quota_size = new_share_size * units.Gi 

609 self._mock_powerscale_api.quota_set.assert_called_once_with( 

610 share_path, 'directory', expected_quota_size) 

611 

612 def test_update_access_add_nfs(self): 

613 share = { 

614 "name": self.SHARE_NAME, 

615 "share_proto": 'NFS', 

616 } 

617 fake_export_id = 4 

618 self._mock_powerscale_api.lookup_nfs_export.return_value = ( 

619 fake_export_id) 

620 self._mock_powerscale_api.get_nfs_export.return_value = { 

621 'clients': [], 

622 'read_only_clients': [] 

623 } 

624 nfs_access = { 

625 'access_type': 'ip', 

626 'access_to': '10.1.1.10', 

627 'access_level': const.ACCESS_LEVEL_RW, 

628 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

629 } 

630 access_rules = [nfs_access] 

631 self._mock_powerscale_api.modify_nfs_export_access.return_value = True 

632 rule_map = self.storage_connection.update_access( 

633 self.mock_context, share, access_rules, [], 

634 [], share_server=None) 

635 expected_rule_map = { 

636 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

637 'state': 'active' 

638 } 

639 } 

640 self._mock_powerscale_api.modify_nfs_export_access. \ 

641 assert_called_once_with(fake_export_id, [], ['10.1.1.10']) 

642 self.assertEqual(expected_rule_map, rule_map) 

643 

644 def test_update_access_add_cifs(self): 

645 share = { 

646 "name": self.SHARE_NAME, 

647 "share_proto": 'CIFS', 

648 } 

649 access = { 

650 'access_type': 'user', 

651 'access_to': 'foo', 

652 'access_level': const.ACCESS_LEVEL_RW, 

653 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

654 } 

655 access_rules = [access] 

656 

657 self._mock_powerscale_api.get_user_sid.return_value = { 

658 'id': 'SID:S-1-5-22', 

659 'name': 'foo', 

660 'type': 'user', 

661 } 

662 rule_map = self.storage_connection.update_access( 

663 self.mock_context, share, access_rules, [], []) 

664 

665 expected_permissions = [ 

666 { 

667 "permission": "change", 

668 "permission_type": "allow", 

669 "trustee": { 

670 "id": "SID:S-1-5-22", 

671 "name": "foo", 

672 "type": "user" 

673 } 

674 } 

675 ] 

676 self._mock_powerscale_api.modify_smb_share_access.\ 

677 assert_called_once_with( 

678 self.SHARE_NAME, host_acl=[], permissions=expected_permissions) 

679 expected_rule_map = { 

680 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

681 'state': 'active' 

682 } 

683 } 

684 self.assertEqual(expected_rule_map, rule_map) 

685 

686 def test_update_access_delete_nfs(self): 

687 share = { 

688 "name": self.SHARE_NAME, 

689 "share_proto": 'NFS', 

690 } 

691 fake_export_id = 4 

692 self._mock_powerscale_api.lookup_nfs_export.return_value = ( 

693 fake_export_id) 

694 # simulate an IP added to the whitelist 

695 ip_addr = '10.0.0.4' 

696 ip_addr_ro = '10.0.0.50' 

697 self._mock_powerscale_api.get_nfs_export.return_value = { 

698 'clients': [ip_addr], 'read_only_clients': [ip_addr_ro]} 

699 access_rules = [] 

700 self._mock_powerscale_api.modify_nfs_export_access.return_value = True 

701 

702 rule_map = self.storage_connection.update_access( 

703 self.mock_context, share, access_rules, [], []) 

704 

705 self._mock_powerscale_api.modify_nfs_export_access. \ 

706 assert_called_once_with(fake_export_id, [], []) 

707 self.assertEqual({}, rule_map) 

708 

709 def test_update_access_delete_cifs(self): 

710 share = { 

711 "name": self.SHARE_NAME, 

712 "share_proto": 'CIFS', 

713 } 

714 access_rules = [] 

715 self._mock_powerscale_api.lookup_smb_share.return_value = { 

716 'permissions': [ 

717 { 

718 'permission': 'change', 

719 'permission_type': 'allow', 

720 'trustee': { 

721 'id': 'SID:S-1-5-21', 

722 'name': 'newuser', 

723 'type': 'user', 

724 } 

725 

726 } 

727 ] 

728 } 

729 

730 self._mock_powerscale_api.modify_smb_share_access.return_value = None 

731 rule_map = self.storage_connection.update_access( 

732 self.mock_context, share, access_rules, [], []) 

733 

734 self._mock_powerscale_api.modify_smb_share_access.\ 

735 assert_called_once_with( 

736 self.SHARE_NAME, host_acl=[], permissions=[]) 

737 self.assertEqual({}, rule_map) 

738 

739 def test_update_access_nfs_share_not_found(self): 

740 share = { 

741 "name": self.SHARE_NAME, 

742 "share_proto": 'NFS', 

743 } 

744 access = { 

745 'access_type': 'user', 

746 'access_to': 'foouser', 

747 'access_level': const.ACCESS_LEVEL_RW, 

748 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

749 } 

750 access_rules = [access] 

751 self._mock_powerscale_api.lookup_nfs_export.return_value = None 

752 

753 rule_map = self.storage_connection.update_access( 

754 self.mock_context, share, access_rules, [], []) 

755 

756 expected_rule_map = { 

757 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

758 'state': 'error' 

759 } 

760 } 

761 self.assertEqual(expected_rule_map, rule_map) 

762 

763 def test_update_access_nfs_http_error_on_clear_rules(self): 

764 share = { 

765 "name": self.SHARE_NAME, 

766 "share_proto": 'NFS', 

767 } 

768 access = { 

769 'access_type': 'user', 

770 'access_to': 'foouser', 

771 'access_level': const.ACCESS_LEVEL_RW, 

772 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

773 } 

774 access_rules = [access] 

775 self._mock_powerscale_api.modify_nfs_export_access.return_value = False 

776 

777 rule_map = self.storage_connection.update_access( 

778 self.mock_context, share, access_rules, [], []) 

779 

780 expected_rule_map = { 

781 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

782 'state': 'error' 

783 } 

784 } 

785 self.assertEqual(expected_rule_map, rule_map) 

786 

787 def test_update_access_cifs_http_error_on_clear_rules(self): 

788 share = { 

789 "name": self.SHARE_NAME, 

790 "share_proto": 'CIFS', 

791 } 

792 access = { 

793 'access_type': 'user', 

794 'access_to': 'foo', 

795 'access_level': const.ACCESS_LEVEL_RW, 

796 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

797 } 

798 access_rules = [access] 

799 self._mock_powerscale_api.modify_smb_share_access.return_value = False 

800 

801 rule_map = self.storage_connection.update_access( 

802 self.mock_context, share, access_rules, None, None) 

803 

804 expected_rule_map = { 

805 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

806 'state': 'error' 

807 } 

808 } 

809 self.assertEqual(expected_rule_map, rule_map) 

810 

811 def test_update_access_cifs_invalid_user_access_level(self): 

812 share = { 

813 "name": self.SHARE_NAME, 

814 "share_proto": 'CIFS', 

815 } 

816 access = { 

817 'access_type': 'user', 

818 'access_to': 'foo', 

819 'access_level': 'fake', 

820 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

821 } 

822 access_rules = [access] 

823 self._mock_powerscale_api.modify_smb_share_access.return_value = False 

824 

825 rule_map = self.storage_connection.update_access( 

826 self.mock_context, share, access_rules, [], []) 

827 

828 expected_rule_map = { 

829 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

830 'state': 'error' 

831 } 

832 } 

833 self.assertEqual(expected_rule_map, rule_map) 

834 

835 def test_update_access_cifs_user_not_found(self): 

836 share = { 

837 "name": self.SHARE_NAME, 

838 "share_proto": 'CIFS', 

839 } 

840 access = { 

841 'access_type': 'user', 

842 'access_to': 'foo', 

843 'access_level': const.ACCESS_LEVEL_RW, 

844 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

845 } 

846 access_rules = [access] 

847 self._mock_powerscale_api.get_user_sid.return_value = None 

848 self._mock_powerscale_api.modify_smb_share_access.return_value = True 

849 

850 rule_map = self.storage_connection.update_access( 

851 self.mock_context, share, access_rules, [], []) 

852 

853 expected_rule_map = { 

854 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

855 'state': 'error' 

856 } 

857 } 

858 self.assertEqual(expected_rule_map, rule_map) 

859 

860 def test_update_access_cifs_invalid_access_type(self): 

861 share = { 

862 "name": self.SHARE_NAME, 

863 "share_proto": 'CIFS', 

864 } 

865 access = { 

866 'access_type': 'foo', 

867 'access_to': 'foo', 

868 'access_level': const.ACCESS_LEVEL_RW, 

869 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

870 } 

871 access_rules = [access] 

872 

873 rule_map = self.storage_connection.update_access( 

874 self.mock_context, share, access_rules, [], []) 

875 

876 expected_rule_map = { 

877 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

878 'state': 'error' 

879 } 

880 } 

881 self.assertEqual(expected_rule_map, rule_map) 

882 

883 def test_update_access_recover_nfs(self): 

884 # verify that new ips are added and ips not in rules are removed 

885 share = { 

886 "name": self.SHARE_NAME, 

887 "share_proto": 'NFS', 

888 } 

889 fake_export_id = 4 

890 self._mock_powerscale_api.lookup_nfs_export.return_value = ( 

891 fake_export_id) 

892 self._mock_powerscale_api.get_nfs_export.return_value = { 

893 'clients': ['10.1.1.8'], 

894 'read_only_clients': ['10.2.0.2'] 

895 } 

896 nfs_access_1 = { 

897 'access_type': 'ip', 

898 'access_to': '10.1.1.10', 

899 'access_level': const.ACCESS_LEVEL_RW, 

900 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

901 } 

902 nfs_access_2 = { 

903 'access_type': 'ip', 

904 'access_to': '10.1.1.2', 

905 'access_level': const.ACCESS_LEVEL_RO, 

906 'access_id': '19960614-8574-4e03-89cf-7cf267b0bd08' 

907 } 

908 access_rules = [nfs_access_1, nfs_access_2] 

909 

910 self._mock_powerscale_api.modify_nfs_export_access.return_value = True 

911 

912 rule_map = self.storage_connection.update_access( 

913 self.mock_context, share, access_rules, [], []) 

914 

915 expected_rule_map = { 

916 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

917 'state': 'active' 

918 }, 

919 '19960614-8574-4e03-89cf-7cf267b0bd08': { 

920 'state': 'active' 

921 } 

922 } 

923 self._mock_powerscale_api.modify_nfs_export_access. \ 

924 assert_called_once_with(fake_export_id, 

925 ['10.1.1.2'], 

926 ['10.1.1.10']) 

927 self.assertEqual(expected_rule_map, rule_map) 

928 

929 def test_update_access_recover_cifs(self): 

930 share = { 

931 "name": self.SHARE_NAME, 

932 "share_proto": 'CIFS', 

933 } 

934 self._mock_powerscale_api.get_user_sid.return_value = { 

935 'id': 'SID:S-1-5-22', 

936 'name': 'testuser', 

937 'type': 'user', 

938 } 

939 self._mock_powerscale_api.modify_smb_share_access.return_value = True 

940 access_1 = { 

941 'access_type': 'ip', 

942 'access_to': '10.1.1.10', 

943 'access_level': const.ACCESS_LEVEL_RW, 

944 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

945 } 

946 access_2 = { 

947 'access_type': 'user', 

948 'access_to': 'testuser', 

949 'access_level': const.ACCESS_LEVEL_RO, 

950 'access_id': '19960614-8574-4e03-89cf-7cf267b0bd08' 

951 } 

952 access_rules = [access_1, access_2] 

953 

954 rule_map = self.storage_connection.update_access( 

955 self.mock_context, share, access_rules, [], []) 

956 

957 expected_data = { 

958 'host_acl': ['allow:10.1.1.10', 'deny:ALL'], 

959 'permissions': [ 

960 { 

961 'permission': 'read', 

962 'permission_type': 'allow', 

963 'trustee': { 

964 'id': 'SID:S-1-5-22', 

965 'name': 'testuser', 

966 'type': 'user', 

967 } 

968 } 

969 ] 

970 } 

971 expected_rule_map = { 

972 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

973 'state': 'active' 

974 }, 

975 '19960614-8574-4e03-89cf-7cf267b0bd08': { 

976 'state': 'active' 

977 } 

978 } 

979 self._mock_powerscale_api.lookup_smb_share.assert_not_called() 

980 self._mock_powerscale_api.get_user_sid.assert_called_once_with( 

981 'testuser') 

982 self._mock_powerscale_api.modify_smb_share_access.\ 

983 assert_called_once_with( 

984 self.SHARE_NAME, 

985 host_acl=expected_data['host_acl'], 

986 permissions=expected_data['permissions'] 

987 ) 

988 self.assertEqual(expected_rule_map, rule_map) 

989 

990 def test_update_access_with_cifs_ip_readonly(self): 

991 # Note: Driver does not currently support readonly access for "ip" type 

992 share = {'name': self.SHARE_NAME, 'share_proto': 'CIFS'} 

993 access = {'access_type': 'ip', 'access_to': '10.1.1.10', 

994 'access_level': const.ACCESS_LEVEL_RO, 

995 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08'} 

996 

997 rule_map = self.storage_connection.update_access( 

998 self.mock_context, share, [access], None, None) 

999 expected_rule_map = { 

1000 '09960614-8574-4e03-89cf-7cf267b0bd08': {'state': 'error'}} 

1001 self.assertEqual(expected_rule_map, rule_map) 

1002 

1003 def test_delete_quota_when_quota_exists(self): 

1004 path = '/path/to/quota' 

1005 quota_id = '123' 

1006 quota_data = {'id': quota_id} 

1007 self._mock_powerscale_api.quota_get.return_value = quota_data 

1008 self._mock_powerscale_api.delete_quota.return_value = True 

1009 

1010 self.storage_connection._delete_quota(path) 

1011 

1012 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1013 path, 'directory') 

1014 self._mock_powerscale_api.delete_quota.assert_called_once_with( 

1015 quota_id) 

1016 

1017 def test_delete_quota_when_quota_does_not_exist(self): 

1018 path = '/path/to/quota' 

1019 self._mock_powerscale_api.quota_get.return_value = None 

1020 

1021 self.storage_connection._delete_quota(path) 

1022 

1023 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1024 path, 'directory') 

1025 self._mock_powerscale_api.delete_quota.assert_not_called() 

1026 

1027 def test_delete_directory_when_path_exists(self): 

1028 path = '/path/to/directory' 

1029 self.storage_connection._delete_directory(path) 

1030 self._mock_powerscale_api.is_path_existent.assert_called_with(path) 

1031 self._mock_powerscale_api.delete_path.assert_called_with( 

1032 path, recursive=True) 

1033 

1034 def test_delete_directory_when_path_does_not_exist(self): 

1035 path = '/path/to/directory' 

1036 self._mock_powerscale_api.is_path_existent.return_value = False 

1037 self.storage_connection._delete_directory(path) 

1038 self._mock_powerscale_api.is_path_existent.assert_called_with(path) 

1039 self._mock_powerscale_api.delete_path.assert_not_called() 

1040 

1041 def test_get_backend_info(self): 

1042 self._mock_powerscale_api.get_cluster_version.return_value = '1.0' 

1043 result = self.storage_connection.get_backend_info(None) 

1044 expected_info = { 

1045 'driver_version': powerscale.VERSION, 

1046 'cluster_version': '1.0', 

1047 'rest_server': self.POWERSCALE_ADDR, 

1048 'rest_port': '8080', 

1049 } 

1050 self.assertEqual(expected_info, result) 

1051 

1052 def test_ensure_shares_nfs_share_exists(self): 

1053 share = { 

1054 'id': '123', 

1055 'share_proto': 'NFS', 

1056 'name': 'my_share', 

1057 } 

1058 container_path = '/ifs/my_share' 

1059 location = '10.0.0.1:/ifs/my_share' 

1060 self.storage_connection._get_container_path = mock.MagicMock( 

1061 return_value=container_path) 

1062 self._mock_powerscale_api.lookup_nfs_export.return_value = '123' 

1063 

1064 result = self.storage_connection.ensure_shares(None, [share]) 

1065 expected_result = { 

1066 '123': { 

1067 'export_locations': [location], 

1068 'status': 'available', 

1069 'reapply_access_rules': True, 

1070 } 

1071 } 

1072 self.assertEqual(result, expected_result) 

1073 

1074 def test_ensure_shares_cifs_share_exists(self): 

1075 share = { 

1076 'id': '123', 

1077 'share_proto': 'CIFS', 

1078 'name': 'my_share', 

1079 } 

1080 location = '\\\\10.0.0.1\\my_share' 

1081 self._mock_powerscale_api.lookup_smb_share.return_value = share 

1082 

1083 result = self.storage_connection.ensure_shares(None, [share]) 

1084 expected_result = { 

1085 '123': { 

1086 'export_locations': [location], 

1087 'status': 'available', 

1088 'reapply_access_rules': True, 

1089 } 

1090 } 

1091 self.assertEqual(result, expected_result) 

1092 

1093 def test_ensure_shares_nfs_share_does_not_exist(self): 

1094 share = { 

1095 'id': '123', 

1096 'share_proto': 'NFS', 

1097 'name': 'my_share', 

1098 } 

1099 self._mock_powerscale_api.lookup_nfs_export.return_value = None 

1100 result = self.storage_connection.ensure_shares(None, [share]) 

1101 expected_result = { 

1102 '123': { 

1103 'export_locations': [], 

1104 'status': 'error', 

1105 'reapply_access_rules': False, 

1106 } 

1107 } 

1108 self.assertEqual(result, expected_result) 

1109 

1110 def test_ensure_shares_cifs_share_does_not_exist(self): 

1111 share = { 

1112 'id': '123', 

1113 'share_proto': 'CIFS', 

1114 'name': 'my_share', 

1115 } 

1116 self._mock_powerscale_api.lookup_smb_share.return_value = None 

1117 result = self.storage_connection.ensure_shares(None, [share]) 

1118 expected_result = { 

1119 '123': { 

1120 'export_locations': [], 

1121 'status': 'error', 

1122 'reapply_access_rules': False, 

1123 } 

1124 } 

1125 self.assertEqual(result, expected_result) 

1126 

1127 def test_shrink_share_success(self): 

1128 share = {"name": self.SHARE_NAME, "share_proto": "CIFS", "size": 8} 

1129 path = f"{self.ROOT_DIR}/{self.SHARE_NAME}" 

1130 self._mock_powerscale_api.quota_get.return_value = { 

1131 'usage': {'logical': 5 * units.Gi} 

1132 } 

1133 self.storage_connection.shrink_share(share, new_size=6) 

1134 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1135 path, 'directory' 

1136 ) 

1137 self._mock_powerscale_api.quota_set.assert_called_once_with( 

1138 path, 'directory', 6 * units.Gi 

1139 ) 

1140 

1141 def test_shrink_share_raises_when_new_quota_less_than_used(self): 

1142 share = {"name": self.SHARE_NAME, "share_proto": "NFS", "size": 8} 

1143 path = f"{self.ROOT_DIR}/{self.SHARE_NAME}" 

1144 self._mock_powerscale_api.quota_get.return_value = { 

1145 'usage': {'logical': 7 * units.Gi} 

1146 } 

1147 self.assertRaises( 

1148 exception.ShareShrinkingPossibleDataLoss, 

1149 self.storage_connection.shrink_share, 

1150 share, 6 

1151 ) 

1152 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1153 path, 'directory' 

1154 ) 

1155 self._mock_powerscale_api.quota_set.assert_not_called() 

1156 

1157 def _get_base_snapshot(self): 

1158 return { 

1159 'id': 'snapshot-id-123', 

1160 'provider_location': '123', 

1161 'name': 'test-ss', 

1162 'share': { 

1163 'id': 'share-id-123', 

1164 'name': self.SHARE_NAME, 

1165 'mount_snapshot_support': False, 

1166 'size': 8, 

1167 "share_proto": "NFS", 

1168 'export_locations': [{ 

1169 # NFS-style export: <ip>:/ifs/manila-test/share-foo 

1170 'path': '%s:%s' % (self.POWERSCALE_ADDR, self.SHARE_DIR), 

1171 }], 

1172 }, 

1173 } 

1174 

1175 def test_manage_existing_snapshot_not_found_in_backend(self): 

1176 snapshot = self._get_base_snapshot() 

1177 driver_options = {} 

1178 

1179 # Backend returns no snapshot 

1180 self._mock_powerscale_api.get_snapshot_id.return_value = None 

1181 

1182 self.assertRaises( 

1183 exception.ManageInvalidShareSnapshot, 

1184 self.storage_connection.manage_existing_snapshot, 

1185 snapshot, 

1186 driver_options, 

1187 ) 

1188 self._mock_powerscale_api.get_snapshot_id.assert_called_once_with( 

1189 snapshot['provider_location']) 

1190 

1191 def test_manage_existing_snapshot_share_path_mismatch(self): 

1192 snapshot = self._get_base_snapshot() 

1193 driver_options = {} 

1194 

1195 # Snapshot exists but path does not match share export path 

1196 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1197 'id': 'backend-snap-id', 

1198 'path': '/ifs/some/other/path', 

1199 } 

1200 

1201 self.assertRaises( 

1202 exception.ManageInvalidShareSnapshot, 

1203 self.storage_connection.manage_existing_snapshot, 

1204 snapshot, 

1205 driver_options, 

1206 ) 

1207 self._mock_powerscale_api.get_snapshot_id.assert_called_once_with( 

1208 snapshot['provider_location']) 

1209 

1210 def test_manage_existing_snapshot_invalid_size_value(self): 

1211 snapshot = self._get_base_snapshot() 

1212 driver_options = { 

1213 # This will cause int("invalid") to raise ValueError 

1214 'size': 'invalid', 

1215 } 

1216 

1217 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1218 'id': 'backend-snap-id', 

1219 'path': self.SHARE_DIR, 

1220 } 

1221 

1222 self.assertRaises( 

1223 exception.ManageInvalidShareSnapshot, 

1224 self.storage_connection.manage_existing_snapshot, 

1225 snapshot, 

1226 driver_options, 

1227 ) 

1228 

1229 def test_manage_existing_snapshot_invalid_size_type(self): 

1230 snapshot = self._get_base_snapshot() 

1231 driver_options = { 

1232 # This will cause int(None) to raise TypeError 

1233 'size': None, 

1234 } 

1235 

1236 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1237 'id': 'backend-snap-id', 

1238 'path': self.SHARE_DIR, 

1239 } 

1240 

1241 self.assertRaises( 

1242 exception.ManageInvalidShareSnapshot, 

1243 self.storage_connection.manage_existing_snapshot, 

1244 snapshot, 

1245 driver_options, 

1246 ) 

1247 

1248 def test_manage_existing_snapshot_no_size_uses_share_size(self): 

1249 snapshot = self._get_base_snapshot() 

1250 driver_options = {} # size not provided 

1251 

1252 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1253 'id': 'backend-snap-id', 

1254 'path': self.SHARE_DIR, 

1255 } 

1256 

1257 result = self.storage_connection.manage_existing_snapshot( 

1258 snapshot, driver_options) 

1259 

1260 self._mock_powerscale_api.get_snapshot_id.assert_called_once_with( 

1261 snapshot['provider_location']) 

1262 # Should fall back to share['size'] 

1263 self.assertEqual( 

1264 {'size': snapshot['share']['size'], 

1265 'provider_location': snapshot['provider_location']}, 

1266 result) 

1267 

1268 def test_manage_existing_snapshot_explicit_size_used(self): 

1269 snapshot = self._get_base_snapshot() 

1270 driver_options = { 

1271 'size': '3', # valid integer string 

1272 } 

1273 

1274 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1275 'id': 'backend-snap-id', 

1276 'path': self.SHARE_DIR, 

1277 } 

1278 

1279 result = self.storage_connection.manage_existing_snapshot( 

1280 snapshot, driver_options) 

1281 

1282 self._mock_powerscale_api.get_snapshot_id.assert_called_once_with( 

1283 snapshot['provider_location']) 

1284 self.assertEqual( 

1285 {'size': 3, 'provider_location': snapshot['provider_location']}, 

1286 result) 

1287 

1288 def test_manage_existing_snapshot_with_mount_support(self): 

1289 snapshot = self._get_base_snapshot() 

1290 snapshot['share']['mount_snapshot_support'] = True 

1291 driver_options = { 

1292 'size': '3', # valid integer string 

1293 } 

1294 

1295 self._mock_powerscale_api.get_snapshot_id.return_value = { 

1296 'id': 'backend-snap-id', 

1297 'path': self.SHARE_DIR, 

1298 'name': 'fake-snap-name', 

1299 } 

1300 

1301 result = self.storage_connection.manage_existing_snapshot( 

1302 snapshot, driver_options) 

1303 

1304 self._mock_powerscale_api.get_snapshot_id.assert_called_once_with( 

1305 snapshot['provider_location']) 

1306 self.assertEqual( 

1307 {'size': 3, 'provider_location': snapshot['provider_location'], 

1308 'export_locations': [{'is_admin_only': False, 

1309 'metadata': {'preferred': True}, 

1310 'path': '10.0.0.1:/ifs/' 

1311 '.snapshot/fake-snap-name'}]}, 

1312 result) 

1313 

1314 def test_delete_snapshot_with_provider_location_success(self): 

1315 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

1316 "mount_snapshot_support": False} 

1317 snapshot = { 

1318 'name': 'snap-001', 

1319 'provider_location': 'backend-snap-loc-001', 

1320 'share': share 

1321 } 

1322 

1323 # Backend delete succeeds 

1324 (self._mock_powerscale_api. 

1325 delete_snapshot_by_id).return_value = True 

1326 

1327 # Should not raise 

1328 self.storage_connection.delete_snapshot( 

1329 self.mock_context, snapshot, share_server=None) 

1330 

1331 # Must be called with provider_location, not name 

1332 (self._mock_powerscale_api.delete_snapshot_by_id. 

1333 assert_called_once_with(snapshot['provider_location'])) 

1334 

1335 def test_delete_snapshot_with_provider_location_failure(self): 

1336 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

1337 "mount_snapshot_support": False} 

1338 snapshot = { 

1339 'name': 'snap-001', 

1340 'provider_location': 'backend-snap-loc-001', 

1341 'share': share 

1342 } 

1343 

1344 # Backend delete fails 

1345 (self._mock_powerscale_api. 

1346 delete_snapshot_by_id).return_value = False 

1347 

1348 # Expect backend exception 

1349 self.assertRaises( 

1350 exception.ShareBackendException, 

1351 self.storage_connection.delete_snapshot, 

1352 self.mock_context, 

1353 snapshot, 

1354 None, 

1355 ) 

1356 

1357 # Must be called with provider_location 

1358 (self._mock_powerscale_api.delete_snapshot_by_id. 

1359 assert_called_once_with(snapshot['provider_location'])) 

1360 

1361 def test_manage_existing_nfs_success(self): 

1362 share = { 

1363 'share_proto': 'NFS', 

1364 'export_location': '10.0.0.1:/ifs/manila-test/share-foo', 

1365 } 

1366 self._mock_powerscale_api.lookup_nfs_export.return_value = 42 

1367 self._mock_powerscale_api.quota_get.return_value = { 

1368 'thresholds': {'hard': 10 * units.Gi}, 

1369 } 

1370 result = self.storage_connection.manage_existing( 

1371 share, driver_options={} 

1372 ) 

1373 self.assertEqual( 

1374 ['10.0.0.1:/ifs/manila-test/share-foo'], 

1375 result['export_locations'], 

1376 ) 

1377 self.assertEqual(10, result['size']) 

1378 self._mock_powerscale_api.lookup_nfs_export.assert_called_once_with( 

1379 '/ifs/manila-test/share-foo' 

1380 ) 

1381 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1382 '/ifs/manila-test/share-foo', 'directory' 

1383 ) 

1384 

1385 def test_manage_existing_nfs_not_found(self): 

1386 share = { 

1387 'share_proto': 'NFS', 

1388 'export_location': '10.0.0.1:/ifs/missing', 

1389 } 

1390 self._mock_powerscale_api.lookup_nfs_export.return_value = None 

1391 self.assertRaises( 

1392 exception.ShareBackendException, 

1393 self.storage_connection.manage_existing, 

1394 share, 

1395 driver_options={}, 

1396 ) 

1397 

1398 def test_manage_existing_cifs_success(self): 

1399 share = { 

1400 'share_proto': 'CIFS', 

1401 'export_location': '\\\\10.0.0.1\\share-foo', 

1402 } 

1403 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1404 'name': 'share-foo', 

1405 'path': '/ifs/manila-test/share-foo', 

1406 } 

1407 self._mock_powerscale_api.quota_get.return_value = { 

1408 'thresholds': {'hard': 5 * units.Gi}, 

1409 } 

1410 result = self.storage_connection.manage_existing( 

1411 share, driver_options={} 

1412 ) 

1413 self.assertEqual( 

1414 ['\\\\10.0.0.1\\share-foo'], result['export_locations'], 

1415 ) 

1416 self.assertEqual(5, result['size']) 

1417 self._mock_powerscale_api.lookup_smb_share.assert_called_once_with( 

1418 'share-foo' 

1419 ) 

1420 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1421 '/ifs/manila-test/share-foo', 'directory' 

1422 ) 

1423 

1424 def test_manage_existing_cifs_share_not_found(self): 

1425 share = { 

1426 'share_proto': 'CIFS', 

1427 'export_location': '\\\\10.0.0.1\\missing', 

1428 } 

1429 self._mock_powerscale_api.lookup_smb_share.return_value = None 

1430 self.assertRaises( 

1431 exception.ShareBackendException, 

1432 self.storage_connection.manage_existing, 

1433 share, 

1434 driver_options={}, 

1435 ) 

1436 

1437 def test_manage_existing_cifs_path_not_resolved(self): 

1438 share = { 

1439 'share_proto': 'CIFS', 

1440 'export_location': '\\\\10.0.0.1\\share-foo', 

1441 } 

1442 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1443 'name': 'share-foo' 

1444 } 

1445 self.assertRaises( 

1446 exception.ShareBackendException, 

1447 self.storage_connection.manage_existing, 

1448 share, 

1449 driver_options={}, 

1450 ) 

1451 

1452 def test_manage_existing_nfs_export_locations_fallback(self): 

1453 """Fallback for export_location using export_locations list.""" 

1454 share = { 

1455 'share_proto': 'NFS', 

1456 'export_locations': ['10.0.0.2:/ifs/projects/teamX'], 

1457 } 

1458 self._mock_powerscale_api.lookup_nfs_export.return_value = 77 

1459 self._mock_powerscale_api.quota_get.return_value = { 

1460 'thresholds': {'hard': 9 * units.Gi}, 

1461 } 

1462 result = self.storage_connection.manage_existing( 

1463 share, driver_options={} 

1464 ) 

1465 self.assertEqual( 

1466 ['10.0.0.2:/ifs/projects/teamX'], result['export_locations'], 

1467 ) 

1468 self.assertEqual(9, result['size']) 

1469 self._mock_powerscale_api.lookup_nfs_export.assert_called_once_with( 

1470 '/ifs/projects/teamX' 

1471 ) 

1472 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1473 '/ifs/projects/teamX', 'directory' 

1474 ) 

1475 

1476 def test_manage_existing_cifs_export_locations_fallback(self): 

1477 share = { 

1478 'share_proto': 'CIFS', 

1479 'export_locations': ['\\\\10.0.0.1\\share-foo'], 

1480 } 

1481 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1482 'name': 'share-foo', 

1483 'path': f'{self.ROOT_DIR}/{self.SHARE_NAME}', 

1484 } 

1485 self._mock_powerscale_api.quota_get.return_value = { 

1486 'thresholds': {'hard': 12 * units.Gi}, 

1487 } 

1488 result = self.storage_connection.manage_existing( 

1489 share, driver_options={} 

1490 ) 

1491 self.assertEqual( 

1492 ['\\\\10.0.0.1\\share-foo'], 

1493 result['export_locations'], 

1494 ) 

1495 self.assertEqual(12, result['size']) 

1496 self._mock_powerscale_api.lookup_smb_share.assert_called_once_with( 

1497 'share-foo' 

1498 ) 

1499 self._mock_powerscale_api.quota_get.assert_called_once_with( 

1500 f'{self.ROOT_DIR}/{self.SHARE_NAME}', 'directory' 

1501 ) 

1502 

1503 def test_manage_existing_cifs_raises_when_quota_absent(self): 

1504 share = { 

1505 'share_proto': 'CIFS', 

1506 'export_location': '\\\\10.0.0.1\\share-foo', 

1507 } 

1508 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1509 'name': 'share-foo', 

1510 'path': f'{self.ROOT_DIR}/{self.SHARE_NAME}', 

1511 } 

1512 self._mock_powerscale_api.quota_get.return_value = None 

1513 

1514 self.assertRaises( 

1515 exception.ManageInvalidShare, 

1516 self.storage_connection.manage_existing, 

1517 share, 

1518 driver_options={}, 

1519 ) 

1520 

1521 def test_manage_existing_nfs_raises_when_quota_absent(self): 

1522 share = { 

1523 'share_proto': 'NFS', 

1524 'export_location': '10.0.0.1:/ifs/projects/teamX', 

1525 } 

1526 self._mock_powerscale_api.lookup_nfs_export.return_value = 42 

1527 self._mock_powerscale_api.quota_get.return_value = None 

1528 

1529 self.assertRaises( 

1530 exception.ManageInvalidShare, 

1531 self.storage_connection.manage_existing, 

1532 share, 

1533 driver_options={}, 

1534 ) 

1535 

1536 def test_manage_existing_raises_when_hard_limit_missing_usage_only(self): 

1537 share = { 

1538 'share_proto': 'CIFS', 

1539 'export_location': '\\\\10.0.0.1\\share-foo', 

1540 } 

1541 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1542 'name': 'share-foo', 

1543 'path': f'{self.ROOT_DIR}/{self.SHARE_NAME}', 

1544 } 

1545 self._mock_powerscale_api.quota_get.return_value = { 

1546 'usage': {'logical': 7 * units.Gi}, 

1547 } 

1548 self.assertRaises( 

1549 exception.ManageInvalidShare, 

1550 self.storage_connection.manage_existing, 

1551 share, 

1552 driver_options={}, 

1553 ) 

1554 

1555 def _make_snapshot(self, proto="NFS"): 

1556 return { 

1557 "name": "snap-001", 

1558 "share": { 

1559 "share_proto": proto, 

1560 } 

1561 } 

1562 

1563 def test_create_snap_export_path_nfs(self): 

1564 snapshot = self._make_snapshot(proto="NFS") 

1565 

1566 snap_path = "/ifs/manila-test/.snapshots/snap-001" 

1567 export_path = "%s:%s" % (self.POWERSCALE_ADDR, snap_path) 

1568 expected_location = [{"path": export_path, 

1569 "is_admin_only": False, 

1570 "metadata": {"preferred": True}}] 

1571 self.storage_connection._format_nfs_path = mock.Mock( 

1572 return_value=export_path) 

1573 self.storage_connection._get_location = mock.Mock( 

1574 return_value=expected_location) 

1575 self.storage_connection._get_snapshot_path = mock.Mock( 

1576 return_value=snap_path) 

1577 (self._mock_powerscale_api. 

1578 lookup_nfs_export).return_value = None 

1579 (self. 

1580 _mock_powerscale_api. 

1581 create_snapshot_nfs_export).return_value = True 

1582 result = self.storage_connection._create_snap_export_path(snapshot) 

1583 self.storage_connection._get_snapshot_path.assert_called_once_with( 

1584 snapshot) 

1585 (self. 

1586 _mock_powerscale_api. 

1587 create_snapshot_nfs_export.assert_called_once_with(snap_path)) 

1588 self.storage_connection._format_nfs_path.assert_called_once_with( 

1589 snap_path) 

1590 self.storage_connection._get_location.assert_called_once_with( 

1591 {export_path: True}) 

1592 self.assertEqual({"export_locations": expected_location}, result) 

1593 

1594 def test_create_snap_export_path_cifs(self): 

1595 snap_path = "/ifs/manila-test/.snapshots/snap-001" 

1596 snapshot = self._make_snapshot(proto="CIFS") 

1597 smb_export_path = "\\\\%s\\snap-001" % self.POWERSCALE_ADDR 

1598 expected_location = [{"path": smb_export_path, 

1599 "is_admin_only": False, 

1600 "metadata": {"preferred": True}}] 

1601 self.storage_connection._format_smb_path = mock.Mock( 

1602 return_value=smb_export_path) 

1603 self.storage_connection._get_location = mock.Mock( 

1604 return_value=expected_location) 

1605 self.storage_connection._get_snapshot_path = mock.Mock( 

1606 return_value=snap_path) 

1607 (self. 

1608 _mock_powerscale_api. 

1609 create_snapshot_smb_export).return_value = True 

1610 (self._mock_powerscale_api. 

1611 lookup_smb_share).return_value = None 

1612 result = self.storage_connection._create_snap_export_path(snapshot) 

1613 self.storage_connection._get_snapshot_path.assert_called_once_with( 

1614 snapshot) 

1615 (self. 

1616 _mock_powerscale_api. 

1617 create_snapshot_smb_export.assert_called_once_with("snap-001", 

1618 snap_path)) 

1619 self.storage_connection._format_smb_path.assert_called_once_with( 

1620 "snap-001") 

1621 self.storage_connection._get_location.assert_called_once_with( 

1622 {smb_export_path: True}) 

1623 

1624 self.assertEqual({"export_locations": expected_location}, result) 

1625 

1626 def test_create_snap_export_path_failure_raises(self): 

1627 snapshot = self._make_snapshot(proto="NFS") 

1628 snap_path = "/ifs/manila-test/.snapshots/snap-001" 

1629 self.storage_connection._get_snapshot_path = mock.Mock( 

1630 return_value=snap_path) 

1631 (self._mock_powerscale_api. 

1632 lookup_nfs_export).return_value = None 

1633 (self. 

1634 _mock_powerscale_api. 

1635 create_snapshot_nfs_export).return_value = False 

1636 self.assertRaises( 

1637 exception.ShareBackendException, 

1638 self.storage_connection._create_snap_export_path, 

1639 snapshot) 

1640 

1641 def test_snapshot_update_access_nfs(self): 

1642 snapshot = self._make_snapshot(proto="NFS") 

1643 access_rules = [{"access_to": "10.10.10.10"}] 

1644 snap_path = "/ifs/manila-test/.snapshots/snap-001" 

1645 expected_state = {"10.10.10.10": "active"} 

1646 self.storage_connection._get_snapshot_path = mock.Mock( 

1647 return_value=snap_path) 

1648 self.storage_connection._update_access_nfs = mock.Mock( 

1649 return_value=expected_state) 

1650 result = self.storage_connection.snapshot_update_access( 

1651 self.mock_context, 

1652 snapshot, 

1653 access_rules, 

1654 add_rules=None, 

1655 delete_rules=None, 

1656 share_server=None) 

1657 self.storage_connection._get_snapshot_path.assert_called_once_with( 

1658 snapshot) 

1659 self.storage_connection._update_access_nfs.assert_called_once_with( 

1660 "snap-001", snap_path, access_rules) 

1661 self.assertEqual(expected_state, result) 

1662 

1663 def test_snapshot_update_access_cifs(self): 

1664 snapshot = self._make_snapshot(proto="CIFS") 

1665 access_rules = [{"access_to": "user1"}] 

1666 expected_state = {"user1": "active"} 

1667 self.storage_connection._update_access_cifs = mock.Mock( 

1668 return_value=expected_state) 

1669 result = self.storage_connection.snapshot_update_access( 

1670 self.mock_context, 

1671 snapshot, 

1672 access_rules, 

1673 add_rules=None, 

1674 delete_rules=None, 

1675 share_server=None) 

1676 self.storage_connection._update_access_cifs.assert_called_once_with( 

1677 "snap-001", access_rules, read_only=True) 

1678 self.assertEqual(expected_state, result) 

1679 

1680 def test_update_snapshot_ip_access_rule(self): 

1681 snapshot = self._make_snapshot(proto="CIFS") 

1682 access = { 

1683 'access_type': 'ip', 

1684 'access_to': '1.1.1.1', 

1685 'access_level': const.ACCESS_LEVEL_RO, 

1686 'access_id': '09960614-8574-4e03-89cf-7cf267b0bd08' 

1687 } 

1688 access_rules = [access] 

1689 self._mock_powerscale_api.modify_smb_share_access.return_value = True 

1690 rule_map = self.storage_connection.snapshot_update_access( 

1691 self.mock_context, 

1692 snapshot, 

1693 access_rules, 

1694 add_rules=None, 

1695 delete_rules=None, 

1696 share_server=None) 

1697 expected_rule_map = { 

1698 '09960614-8574-4e03-89cf-7cf267b0bd08': { 

1699 'state': 'active' 

1700 } 

1701 } 

1702 self.assertEqual(expected_rule_map, rule_map) 

1703 

1704 def test_create_snapshot_with_mount_support(self): 

1705 snapshot_name = "snapshot01" 

1706 snapshot_path = '/ifs/home/admin' 

1707 share = {"name": self.SHARE_NAME, "share_proto": 'NFS', 

1708 "mount_snapshot_support": True} 

1709 snapshot = {'name': snapshot_name, 'share_name': snapshot_path, 

1710 'share': share} 

1711 self.storage_connection.create_snapshot(self.mock_context, snapshot, 

1712 None) 

1713 self._mock_powerscale_api.create_snapshot.assert_called_with( 

1714 snapshot_name, snapshot_path) 

1715 

1716 def test_create_share_nfs_with_mount_point_name(self): 

1717 share_path = self.SHARE_DIR 

1718 mount_point_name = "/my_custom_share" 

1719 alias_path = mount_point_name 

1720 self._mock_powerscale_api.create_nfs_export.return_value = True 

1721 self._mock_powerscale_api.create_nfs_export_aliases.return_value = True 

1722 

1723 self.assertFalse(self._mock_powerscale_api.create_directory.called) 

1724 self.assertFalse(self._mock_powerscale_api.create_nfs_export.called) 

1725 self.assertFalse( 

1726 self._mock_powerscale_api.create_nfs_export_aliases.called) 

1727 share = { 

1728 "name": self.SHARE_NAME, 

1729 "share_proto": 'NFS', 

1730 "size": 8, 

1731 "mount_point_name": "my_custom_share", 

1732 'share_type_id': 'fake_id', 

1733 } 

1734 location = self.storage_connection.create_share( 

1735 self.mock_context, share, None) 

1736 original_path = '%s:%s' % (self.POWERSCALE_ADDR, share_path) 

1737 alias_export_path = '%s:%s' % (self.POWERSCALE_ADDR, alias_path) 

1738 expected_location = [ 

1739 { 

1740 'is_admin_only': False, 

1741 'metadata': {'preferred': False}, 

1742 'path': original_path, 

1743 }, 

1744 { 

1745 'is_admin_only': False, 

1746 'metadata': {'preferred': True}, 

1747 'path': alias_export_path, 

1748 } 

1749 ] 

1750 self.assertEqual(expected_location, location) 

1751 self._mock_powerscale_api.create_directory.assert_called_once_with( 

1752 share_path, False) 

1753 self._mock_powerscale_api.create_nfs_export.assert_called_once_with( 

1754 share_path) 

1755 (self._mock_powerscale_api.create_nfs_export_aliases. 

1756 assert_called_once_with(mount_point_name, share_path)) 

1757 self._mock_powerscale_api.quota_create.assert_called_once_with( 

1758 share_path, 'directory', 8 * units.Gi) 

1759 

1760 def test_failed_to_create_share_nfs_with_mount_point_name(self): 

1761 self._mock_powerscale_api.create_nfs_export.return_value = True 

1762 (self._mock_powerscale_api. 

1763 create_nfs_export_aliases).return_value = False 

1764 self.assertFalse(self._mock_powerscale_api.create_directory.called) 

1765 self.assertFalse(self._mock_powerscale_api.create_nfs_export.called) 

1766 self.assertFalse( 

1767 self._mock_powerscale_api.create_nfs_export_aliases.called) 

1768 share = { 

1769 "name": self.SHARE_NAME, 

1770 "share_proto": 'NFS', 

1771 "size": 8, 

1772 "mount_point_name": "my_custom_share", 

1773 } 

1774 self.assertRaises(exception.ShareBackendException, 

1775 self.storage_connection.create_share, 

1776 self.mock_context, share, 

1777 share_server=None) 

1778 

1779 def test_create_share_cifs_with_mount_point_name(self): 

1780 self._mock_powerscale_api.create_smb_share.return_value = True 

1781 share = { 

1782 "name": self.SHARE_NAME, 

1783 "share_proto": 'CIFS', 

1784 "size": 8, 

1785 "mount_point_name": "custom_smb", 

1786 'share_type_id': 'fake_id', 

1787 } 

1788 location = self.storage_connection.create_share( 

1789 self.mock_context, share, None) 

1790 

1791 path = '\\\\{0}\\{1}'.format(self.POWERSCALE_ADDR, "custom_smb") 

1792 expected_location = [{ 

1793 'is_admin_only': False, 

1794 'metadata': {'preferred': True}, 

1795 'path': path, 

1796 }] 

1797 self.assertEqual(expected_location, location) 

1798 self._mock_powerscale_api.create_smb_share.assert_called_once_with( 

1799 "custom_smb", self.SHARE_DIR) 

1800 

1801 def test_delete_share_nfs_with_mount_point_alias_deleted(self): 

1802 share = { 

1803 "name": self.SHARE_NAME, 

1804 "share_proto": "NFS", 

1805 "mount_point_name": "my_custom_share", 

1806 } 

1807 fake_share_id = 42 

1808 (self._mock_powerscale_api. 

1809 lookup_nfs_export).return_value = fake_share_id 

1810 self._mock_powerscale_api.delete_nfs_share.return_value = True 

1811 self.storage_connection._check_valid_aliases = mock.MagicMock( 

1812 return_value=True) 

1813 self._mock_powerscale_api.delete_nfs_export_aliases.return_value = True 

1814 self.storage_connection.delete_share(self.mock_context, share, None) 

1815 self._mock_powerscale_api.lookup_nfs_export.assert_called_once_with( 

1816 self.storage_connection._get_container_path(share) 

1817 ) 

1818 (self._mock_powerscale_api. 

1819 delete_nfs_share.assert_called_once_with(fake_share_id)) 

1820 (self._mock_powerscale_api. 

1821 delete_nfs_export_aliases.assert_called_once_with("/my_custom_share")) 

1822 

1823 def test_delete_invalid_share_nfs_with_mount_point_alias_deleted(self): 

1824 share = { 

1825 "name": self.SHARE_NAME, 

1826 "share_proto": "NFS", 

1827 "mount_point_name": "my_custom_share", 

1828 } 

1829 fake_share_id = 42 

1830 (self._mock_powerscale_api. 

1831 lookup_nfs_export).return_value = fake_share_id 

1832 self._mock_powerscale_api.delete_nfs_share.return_value = True 

1833 self.storage_connection._check_valid_aliases = mock.MagicMock( 

1834 return_value=False) 

1835 self.storage_connection.delete_share(self.mock_context, share, None) 

1836 self._mock_powerscale_api.lookup_nfs_export.assert_called_once_with( 

1837 self.storage_connection._get_container_path(share) 

1838 ) 

1839 (self._mock_powerscale_api. 

1840 delete_nfs_share.assert_called_once_with(fake_share_id)) 

1841 (self._mock_powerscale_api. 

1842 delete_nfs_export_aliases.assert_not_called()) 

1843 

1844 def test_failed_to_delete_share_nfs_with_mount_point_alias_deleted(self): 

1845 share = { 

1846 "name": self.SHARE_NAME, 

1847 "share_proto": "NFS", 

1848 "mount_point_name": "my_custom_share", 

1849 } 

1850 fake_share_id = 42 

1851 (self._mock_powerscale_api. 

1852 lookup_nfs_export).return_value = fake_share_id 

1853 self._mock_powerscale_api.delete_nfs_share.return_value = True 

1854 self.storage_connection._check_valid_aliases = mock.MagicMock( 

1855 return_value=True) 

1856 (self._mock_powerscale_api. 

1857 delete_nfs_export_aliases).return_value = False 

1858 self.assertRaises(exception.ShareBackendException, 

1859 self.storage_connection.delete_share, 

1860 self.mock_context, share, 

1861 share_server=None) 

1862 

1863 def test_delete_share_cifs_with_mount_point_name(self): 

1864 self.assertFalse(self._mock_powerscale_api.delete_smb_share.called) 

1865 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS', 

1866 "mount_point_name": "my_custom_share", } 

1867 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1868 'id': self.SHARE_NAME, 

1869 'path': '/ifs/manila-test/share-foo', 

1870 } 

1871 container_path = '/ifs/manila-test/share-foo' 

1872 self.storage_connection._get_container_path = mock.MagicMock( 

1873 return_value=container_path) 

1874 self.storage_connection.delete_share(self.mock_context, share, None) 

1875 self._mock_powerscale_api.delete_smb_share.assert_called_with( 

1876 'my_custom_share') 

1877 

1878 def test_delete_invalid_share_cifs_with_mount_point_name(self): 

1879 self.assertFalse(self._mock_powerscale_api.delete_smb_share.called) 

1880 share = {"name": self.SHARE_NAME, "share_proto": 'CIFS', 

1881 "mount_point_name": "my_custom_share", } 

1882 self._mock_powerscale_api.lookup_smb_share.return_value = { 

1883 'id': self.SHARE_NAME, 

1884 'path': '/ifs/manila-test/share-foo-test', 

1885 } 

1886 container_path = '/ifs/manila-test/share-foo' 

1887 self.storage_connection._get_container_path = mock.MagicMock( 

1888 return_value=container_path) 

1889 self.storage_connection.delete_share(self.mock_context, share, None) 

1890 self._mock_powerscale_api.delete_smb_share.assert_not_called() 

1891 

1892 def test_ensure_shares_nfs_share_exists_with_mount_point_name(self): 

1893 share = { 

1894 'id': '123', 

1895 'share_proto': 'NFS', 

1896 'name': 'my_share', 

1897 "mount_point_name": "my_custom_share", 

1898 } 

1899 container_path = '/ifs/my_share' 

1900 location = ['10.0.0.1:/ifs/my_share', 

1901 '10.0.0.1:/my_custom_share'] 

1902 self.storage_connection._get_container_path = mock.MagicMock( 

1903 return_value=container_path) 

1904 self._mock_powerscale_api.lookup_nfs_export.return_value = '123' 

1905 result = self.storage_connection.ensure_shares(None, 

1906 [share]) 

1907 expected_result = { 

1908 '123': { 

1909 'export_locations': location, 

1910 'status': 'available', 

1911 'reapply_access_rules': True, 

1912 } 

1913 } 

1914 self.assertEqual(result, expected_result) 

1915 

1916 def test_ensure_shares_cifs_share_exists_with_mount_point_name(self): 

1917 share = { 

1918 'id': '123', 

1919 'share_proto': 'CIFS', 

1920 'name': 'my_share', 

1921 "mount_point_name": "my_custom_share", 

1922 } 

1923 location = '\\\\10.0.0.1\\my_custom_share' 

1924 self._mock_powerscale_api.lookup_smb_share.return_value = share 

1925 

1926 result = self.storage_connection.ensure_shares(None, 

1927 [share]) 

1928 expected_result = { 

1929 '123': { 

1930 'export_locations': [location], 

1931 'status': 'available', 

1932 'reapply_access_rules': True, 

1933 } 

1934 } 

1935 self.assertEqual(result, expected_result) 

1936 

1937 def test_check_valid_aliases_success(self): 

1938 mount_point_name = '/my_alias' 

1939 container_path = '/ifs/manila/share-123' 

1940 self._mock_powerscale_api.get_nfs_export_aliases.return_value = { 

1941 'path': container_path 

1942 } 

1943 result = self.storage_connection._check_valid_aliases( 

1944 mount_point_name, container_path) 

1945 self.assertTrue(result) 

1946 (self._mock_powerscale_api. 

1947 get_nfs_export_aliases.assert_called_once_with(mount_point_name)) 

1948 

1949 def test_check_valid_aliases_path_mismatch(self): 

1950 mount_point_name = '/my_alias' 

1951 container_path = '/ifs/manila/share-123' 

1952 self._mock_powerscale_api.get_nfs_export_aliases.return_value = { 

1953 'path': '/ifs/manila/other-share' 

1954 } 

1955 result = self.storage_connection._check_valid_aliases( 

1956 mount_point_name, container_path) 

1957 self.assertFalse(result) 

1958 (self._mock_powerscale_api. 

1959 get_nfs_export_aliases.assert_called_once_with(mount_point_name))