Coverage for manila/tests/share/drivers/hitachi/hnas/test_ssh.py: 100%

614 statements  

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

1# Copyright (c) 2015 Hitachi Data Systems, Inc. 

2# All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15 

16import time 

17from unittest import mock 

18 

19import ddt 

20from oslo_concurrency import processutils as putils 

21from oslo_config import cfg 

22import paramiko 

23 

24from manila import exception 

25from manila.share.drivers.hitachi.hnas import ssh 

26from manila import ssh_utils 

27from manila import test 

28 

29 

30CONF = cfg.CONF 

31 

32HNAS_RESULT_empty = "" 

33 

34HNAS_RESULT_limits = """ 

35Filesystem Ensure on span fake_fs: 

36 

37Current capacity 50GiB 

38 

39Thin provision: disabled 

40 

41Filesystem is confined to: 100GiB 

42 (Run 'filesystem-confine') 

43Free space on span allows expansion to: 143GiB 

44 (Run 'span-expand') 

45Chunk size allows growth to: 1069GiB 

46 (This is a conservative estimate) 

47Largest filesystem that can be checked: 262144GiB 

48 (This is a hard limit) 

49This server model allows growth to: 262144GiB 

50 (Upgrade the server) """ 

51 

52HNAS_RESULT_expdel = """Deleting the export '/dir1' on fs 'fake_fs'... 

53NFS Export Delete: Export successfully deleted""" 

54 

55HNAS_RESULT_vvoldel = """ 

56Warning: Clearing dangling space trackers from empty vivol""" 

57 

58HNAS_RESULT_selectfs = "Current selected file system: fake_fs, number(1)" 

59 

60HNAS_RESULT_expadd = "NFS Export Add: Export added successfully" 

61 

62HNAS_RESULT_vvol = """vvol_test 

63 email : 

64 root : /vvol_test 

65 tag : 39 

66 usage bytes : 0 B files: 1 

67 last modified: 2015-06-23 22:36:12.830698800+00:00""" 

68 

69HNAS_RESULT_vvol_error = "The virtual volume does not exist." 

70 

71HNAS_RESULT_mount = """ \ 

72Request to mount file system fake_fs submitted successfully. 

73File system fake_fs successfully mounted.""" 

74 

75HNAS_RESULT_quota = """Type : Explicit 

76Target : ViVol: vvol_test 

77Usage : 1 GB 

78 Limit : 5 GB (Hard) 

79 Warning : Unset 

80 Critical : Unset 

81 Reset : 5% (51.2 MB) 

82File Count : 1 

83 Limit : Unset 

84 Warning : Unset 

85 Critical : Unset 

86 Reset : 5% (0) 

87Generate Events : Disabled 

88Global id : 28a3c9f8-ae05-11d0-9025-836896aada5d 

89Last modified : 2015-06-23 22:37:17.363660800+00:00 """ 

90 

91HNAS_RESULT_quota_tb = """Type : Explicit 

92Target : ViVol: vvol_test 

93Usage : 1 TB 

94 Limit : 1 TB (Hard) 

95 Warning : Unset 

96 Critical : Unset 

97 Reset : 5% (51.2 MB) 

98File Count : 1 

99 Limit : Unset 

100 Warning : Unset 

101 Critical : Unset 

102 Reset : 5% (0) 

103Generate Events : Disabled 

104Global id : 28a3c9f8-ae05-11d0-9025-836896aada5d 

105Last modified : 2015-06-23 22:37:17.363660800+00:00 """ 

106 

107HNAS_RESULT_quota_mb = """Type : Explicit 

108Target : ViVol: vvol_test 

109Usage : 20 MB 

110 Limit : 500 MB (Hard) 

111 Warning : Unset 

112 Critical : Unset 

113 Reset : 5% (51.2 MB) 

114File Count : 1 

115 Limit : Unset 

116 Warning : Unset 

117 Critical : Unset 

118 Reset : 5% (0) 

119Generate Events : Disabled 

120Global id : 28a3c9f8-ae05-11d0-9025-836896aada5d 

121Last modified : 2015-06-23 22:37:17.363660800+00:00 """ 

122 

123HNAS_RESULT_quota_unset = """Type : Explicit 

124Target : ViVol: vvol_test 

125Usage : 0 B 

126 Limit : Unset 

127 Warning : Unset 

128 Critical : Unset 

129 Reset : 5% (51.2 MB) 

130File Count : 1 

131 Limit : Unset 

132 Warning : Unset 

133 Critical : Unset 

134 Reset : 5% (0) 

135Generate Events : Disabled 

136Global id : 28a3c9f8-ae05-11d0-9025-836896aada5d 

137Last modified : 2015-06-23 22:37:17.363660800+00:00 """ 

138 

139HNAS_RESULT_quota_err = """No quotas matching specified filter criteria. 

140""" 

141 

142HNAS_RESULT_export = """Export name: vvol_test 

143 Export path: /vvol_test 

144 File system label: file_system 

145 File system size: 3.969 GB 

146 File system free space: 1.848 GB 

147 File system state: 

148 formatted = Yes 

149 mounted = Yes 

150 failed = No 

151 thin provisioned = No 

152 Access snapshots: No 

153 Display snapshots: No 

154 Read Caching: Disabled 

155Disaster recovery setting: 

156 Recovered = No 

157 Transfer setting = Use file system default \n 

158 Export configuration:\n 

159127.0.0.2 

160""" 

161 

162HNAS_RESULT_wrong_export = """Export name: wrong_name 

163 Export path: /vvol_test 

164 File system label: file_system 

165 File system size: 3.969 GB 

166 File system free space: 1.848 GB 

167 File system state: 

168 formatted = Yes 

169 mounted = Yes 

170 failed = No 

171 thin provisioned = No 

172 Access snapshots: No 

173 Display snapshots: No 

174 Read Caching: Disabled 

175Disaster recovery setting: 

176 Recovered = No 

177 Transfer setting = Use file system default 

178 Export configuration: 

179127.0.0.1""" 

180 

181HNAS_RESULT_exp_no_fs = """ 

182 Export name: no_fs 

183 Export path: /export_without_fs 

184 File system info: *** not available *** 

185 Access snapshots: Yes 

186 Display snapshots: Yes 

187 Read Caching: Disabled 

188Disaster recovery setting: 

189 Recovered = No 

190 Transfer setting = Use file system default 

191 Export configuration: 

192 """ 

193 

194HNAS_RESULT_export_ip = """ 

195 Export name: vvol_test 

196 Export path: /vvol_test 

197 File system label: fake_fs 

198 File system size: 3.969 GB 

199 File system free space: 1.848 GB 

200 File system state: 

201 formatted = Yes 

202 mounted = Yes 

203 failed = No 

204 thin provisioned = No 

205 Access snapshots: No 

206 Display snapshots: No 

207 Read Caching: Disabled 

208Disaster recovery setting: 

209 Recovered = No 

210 Transfer setting = Use file system default 

211 Export configuration: 

212127.0.0.1(rw) 

213""" 

214 

215HNAS_RESULT_export_ip2 = """ 

216 Export name: vvol_test 

217 Export path: /vvol_test 

218 File system label: fake_fs 

219 File system size: 3.969 GB 

220 File system free space: 1.848 GB 

221 File system state: 

222 formatted = Yes 

223 mounted = Yes 

224 failed = No 

225 thin provisioned = No 

226 Access snapshots: No 

227 Display snapshots: No 

228 Read Caching: Disabled 

229Disaster recovery setting: 

230 Recovered = No 

231 Transfer setting = Use file system default 

232 Export configuration: 

233127.0.0.1(ro) 

234""" 

235 

236HNAS_RESULT_expmod = """Modifying the export '/fake_export' on fs 'fake_fs'... 

237NFS Export Modify: changing configuration options to: 127.0.0.2 NFS 

238Export Modify: Export modified successfully""" 

239 

240HNAS_RESULT_expnotmod = "Export not modified." 

241 

242HNAS_RESULT_job = """tree-operation-job-submit: Request submitted successfully. 

243tree-operation-job-submit: Job id = d933100a-b5f6-11d0-91d9-836896aada5d""" 

244 

245HNAS_RESULT_vvol_list = """vol1 

246 email : 

247 root : /shares/vol1 

248 tag : 10 

249 usage bytes : 0 B files: 1 

250 last modified: 2015-07-27 22:25:02.746426000+00:00 

251vol2 

252 email : 

253 root : /shares/vol2 

254 tag : 13 

255 usage bytes : 0 B files: 1 

256 last modified: 2015-07-28 01:30:21.125671700+00:00 

257vol3 

258 email : 

259 root : /shares/vol3 

260 tag : 14 

261 usage bytes : 5 GB (5368709120 B) files: 2 

262 last modified: 2015-07-28 20:23:05.672404600+00:00""" 

263 

264HNAS_RESULT_tree_job_status_fail = """JOB ID : d933100a-b5f6-11d0-91d9-836896aada5d 

265 Job request 

266 Physical node : 1 

267 EVS : 1 

268 Volume number : 1 

269 File system id : 2ea361c20ed0f80d0000000000000000 

270 File system name : fs1 

271 Source path : "/foo" 

272 Creation time : 2013-09-05 23:16:48-07:00 

273 Destination path : "/clone/bar" 

274 Ensure destination path exists : true 

275 

276 Job state : Job failed 

277 Job info 

278 Started : 2013-09-05 23:16:48-07:00 

279 Ended : 2013-09-05 23:17:02-07:00 

280 Status : Success 

281 Error details : 

282 Directories processed : 220 

283 Files processed : 910 

284 Data bytes processed : 34.5 MB (36174754 B) 

285 Source directories missing : 0 

286 Source files missing : 0 

287 Source files skipped : 801 

288 Skipping details : 104 symlinks, 452 hard links, 

28947 block special devices, 25 character devices""" # noqa 

290 

291HNAS_RESULT_job_completed = """JOB ID : ab4211b8-aac8-11ce-91af-39e0822ea368 

292 Job request 

293 Physical node : 1 

294 EVS : 1 

295 Volume number : 1 

296 File system id : 2ea361c20ed0f80d0000000000000000 

297 File system name : fs1 

298 Source path : "/foo" 

299 Creation time : 2013-09-05 23:16:48-07:00 

300 Destination path : "/clone/bar" 

301 Ensure destination path exists : true 

302 

303 Job state : Job was completed 

304 Job info 

305 Started : 2013-09-05 23:16:48-07:00 

306 Ended : 2013-09-05 23:17:02-07:00 

307 Status : Success 

308 Error details : 

309 Directories processed : 220 

310 Files processed : 910 

311 Data bytes processed : 34.5 MB (36174754 B) 

312 Source directories missing : 0 

313 Source files missing : 0 

314 Source files skipped : 801 

315 Skipping details : 104 symlinks, 452 hard links, 47 \ 

316block special devices, 25 character devices 

317""" 

318 

319HNAS_RESULT_job_running = """JOB ID : ab4211b8-aac8-11ce-91af-39e0822ea368 

320 Job request 

321 Physical node : 1 

322 EVS : 1 

323 Volume number : 1 

324 File system id : 2ea361c20ed0f80d0000000000000000 

325 File system name : fs1 

326 Source path : "/foo" 

327 Creation time : 2013-09-05 23:16:48-07:00 

328 Destination path : "/clone/bar" 

329 Ensure destination path exists : true 

330 

331 Job state : Job is running 

332 Job info 

333 Started : 2013-09-05 23:16:48-07:00 

334 Ended : 2013-09-05 23:17:02-07:00 

335 Status : Success 

336 Error details : 

337 Directories processed : 220 

338 Files processed : 910 

339 Data bytes processed : 34.5 MB (36174754 B) 

340 Source directories missing : 0 

341 Source files missing : 0 

342 Source files skipped : 801 

343 Skipping details : 104 symlinks, 452 hard links, 47 \ 

344block special devices, 25 character devices 

345""" 

346 

347HNAS_RESULT_df = """ 

348 ID Label EVS Size Used Snapshots Deduped \ 

349 Avail Thin ThinSize ThinAvail FS Type 

350---- ------------- --- -------- -------------- --------- ------- \ 

351------------- ---- -------- --------- ------------------- 

3521051 FS-ManilaDev1 3 70.00 GB 10.00 GB (75%) 0 B (0%) NA \ 

35318.3 GB (25%) No 4 KB,WFS-2,128 DSBs 

354""" 

355 

356HNAS_RESULT_df_tb = """ 

357 ID Label EVS Size Used Snapshots Deduped \ 

358 Avail Thin ThinSize ThinAvail FS Type 

359---- ------------- --- -------- -------------- --------- ------- \ 

360------------- ---- -------- --------- ------------------- 

3611051 FS-ManilaDev1 3.00 7.00 TB 2 TB (75%) 0 B (0%) NA \ 

36218.3 GB (25%) No 4 KB,WFS-2,128 DSBs 

363""" 

364 

365HNAS_RESULT_df_dedupe_on = """ 

366 ID Label EVS Size Used Snapshots Deduped \ 

367 Avail Thin ThinSize ThinAvail FS Type 

368---- ------------- --- -------- -------------- --------- ------- \ 

369------------- ---- -------- --------- ------------------- 

3701051 FS-ManilaDev1 3.00 7.00 TB 2 TB (75%) NA 0 B (0%) \ 

37118.3 GB (25%) No 4 KB,WFS-2,128 DSBs,dedupe enabled 

372""" 

373 

374HNAS_RESULT_df_unmounted = """ 

375 ID Label EVS Size Used Snapshots Deduped \ 

376 Avail Thin ThinSize ThinAvail FS Type 

377---- ------------- --- -------- -------------- --------- ------- \ 

378------------- ---- -------- --------- ------------------- 

3791051 FS-ManilaDev1 3 70.00 GB Not mounted 0 B (0%) NA \ 

38018.3 GB (25%) No 4 KB,WFS-2,128 DSBs 

381""" 

382 

383HNAS_RESULT_df_error = """File system file_system not found""" 

384 

385HNAS_RESULT_mounted_filesystem = """ 

386file_system 1055 fake_span Mount 2 4 5 1 

387""" 

388 

389HNAS_RESULT_unmounted_filesystem = """ 

390file_system 1055 fake_span Umount 2 4 5 1 

391""" 

392 

393HNAS_RESULT_cifs_list = """ 

394 Share name: vvol_test 

395 Share path: \\\\shares\\vvol_test 

396 Share users: 2 

397 Share online: Yes 

398 Share comment: 

399 Cache options: Manual local caching for documents 

400 ABE enabled: Yes 

401Continuous Availability: No 

402 Access snapshots: No 

403 Display snapshots: No 

404 ShadowCopy enabled: Yes 

405 Lower case on create: No 

406 Follow symlinks: Yes 

407 Follow global symlinks: No 

408 Scan for viruses: Yes 

409 File system label: file_system 

410 File system size: 9.938 GB 

411File system free space: 6.763 GB 

412 File system state: 

413 formatted = Yes 

414 mounted = Yes 

415 failed = No 

416 thin provisioned = No 

417Disaster recovery setting: 

418 Recovered = No 

419 Transfer setting = Use file system default 

420 Home directories: Off 

421 Mount point options: 

422""" 

423 

424HNAS_RESULT_different_fs_cifs_list = """ 

425 Share name: vvol_test 

426 Share path: \\\\shares\\vvol_test 

427 Share users: 0 

428 Share online: Yes 

429 Share comment: 

430 Cache options: Manual local caching for documents 

431 ABE enabled: Yes 

432Continuous Availability: No 

433 Access snapshots: No 

434 Display snapshots: No 

435 ShadowCopy enabled: Yes 

436 Lower case on create: No 

437 Follow symlinks: Yes 

438 Follow global symlinks: No 

439 Scan for viruses: Yes 

440 File system label: different_filesystem 

441 File system size: 9.938 GB 

442File system free space: 6.763 GB 

443 File system state: 

444 formatted = Yes 

445 mounted = Yes 

446 failed = No 

447 thin provisioned = No 

448Disaster recovery setting: 

449 Recovered = No 

450 Transfer setting = Use file system default 

451 Home directories: Off 

452 Mount point options: 

453""" 

454 

455HNAS_RESULT_list_cifs_permissions = """ \ 

456Displaying the details of the share 'vvol_test' on file system 'filesystem' ... 

457Maximum user count is unlimited 

458Type Permission User/Group 

459U Deny Read NFSv4 user\\user1@domain.com 

460G Deny Change & Read Unix user\\1087 

461U Allow Full Control Unix user\\1088 

462U Allow Read Unix user\\1089 

463? Deny Full Control NFSv4 user\\user2@company.com 

464X Allow Change & Read Unix user\\1090 

465 

466""" 

467 

468HNAS_RESULT_check_snap_error = """ \ 

469path-to-object-number/FS-TestCG: Unable to locate component: share1 

470path-to-object-number/FS-TestCG: Failed to resolve object number""" 

471 

472 

473@ddt.ddt 

474class HNASSSHTestCase(test.TestCase): 

475 def setUp(self): 

476 super(HNASSSHTestCase, self).setUp() 

477 

478 self.ip = '192.168.1.1' 

479 self.port = 22 

480 self.user = 'hnas_user' 

481 self.password = 'hnas_password' 

482 self.default_commands = ['ssc', '127.0.0.1'] 

483 self.fs_name = 'file_system' 

484 self.evs_ip = '172.24.44.1' 

485 self.evs_id = 2 

486 self.ssh_private_key = 'private_key' 

487 self.cluster_admin_ip0 = 'fake' 

488 self.job_timeout = 30 

489 

490 self.mock_log = self.mock_object(ssh, 'LOG') 

491 

492 self._driver_ssh = ssh.HNASSSHBackend(self.ip, self.user, 

493 self.password, 

494 self.ssh_private_key, 

495 self.cluster_admin_ip0, 

496 self.evs_id, self.evs_ip, 

497 self.fs_name, self.job_timeout) 

498 

499 self.vvol = { 

500 'id': 'vvol_test', 

501 'share_proto': 'nfs', 

502 'size': 4, 

503 'host': '127.0.0.1', 

504 } 

505 

506 self.snapshot = { 

507 'id': 'snapshot_test', 

508 'share_proto': 'nfs', 

509 'size': 4, 

510 'share_id': 'vvol_test', 

511 'host': 'ubuntu@hitachi2#HITACHI2', 

512 } 

513 self.mock_log.debug.reset_mock() 

514 

515 def test_get_stats(self): 

516 fake_list_command = ['df', '-a', '-f', self.fs_name] 

517 

518 self.mock_object(ssh.HNASSSHBackend, '_execute', 

519 mock.Mock(return_value=(HNAS_RESULT_df_tb, ""))) 

520 

521 total, free, dedupe = self._driver_ssh.get_stats() 

522 

523 ssh.HNASSSHBackend._execute.assert_called_with(fake_list_command) 

524 self.assertEqual(7168.0, total) 

525 self.assertEqual(5120.0, free) 

526 self.assertFalse(dedupe) 

527 

528 def test_get_stats_dedupe_on(self): 

529 fake_list_command = ['df', '-a', '-f', self.fs_name] 

530 

531 self.mock_object( 

532 ssh.HNASSSHBackend, '_execute', 

533 mock.Mock(return_value=(HNAS_RESULT_df_dedupe_on, ""))) 

534 

535 total, free, dedupe = self._driver_ssh.get_stats() 

536 

537 ssh.HNASSSHBackend._execute.assert_called_with(fake_list_command) 

538 self.assertEqual(7168.0, total) 

539 self.assertEqual(5120.0, free) 

540 self.assertTrue(dedupe) 

541 

542 def test_get_stats_error(self): 

543 

544 fake_list_command = ['df', '-a', '-f', self.fs_name] 

545 

546 self.mock_object(ssh.HNASSSHBackend, '_execute', 

547 mock.Mock(side_effect=putils.ProcessExecutionError)) 

548 

549 self.assertRaises(exception.HNASBackendException, 

550 self._driver_ssh.get_stats) 

551 

552 ssh.HNASSSHBackend._execute.assert_called_with(fake_list_command) 

553 

554 @ddt.data(True, False) 

555 def test_nfs_export_add(self, is_snapshot): 

556 if is_snapshot: 

557 name = '/snapshots/fake_snap' 

558 path = '/snapshots/fake_share/fake_snap' 

559 else: 

560 name = path = '/shares/fake_share' 

561 

562 fake_nfs_command = ['nfs-export', 'add', '-S', 'disable', '-c', 

563 '127.0.0.1', name, self.fs_name, 

564 path] 

565 self.mock_object(ssh.HNASSSHBackend, '_execute') 

566 

567 if is_snapshot: 

568 self._driver_ssh.nfs_export_add('fake_share', 

569 snapshot_id='fake_snap') 

570 else: 

571 self._driver_ssh.nfs_export_add('fake_share') 

572 

573 self._driver_ssh._execute.assert_called_with(fake_nfs_command) 

574 

575 def test_nfs_export_add_error(self): 

576 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

577 side_effect=[putils.ProcessExecutionError(stderr='')])) 

578 

579 self.assertRaises(exception.HNASBackendException, 

580 self._driver_ssh.nfs_export_add, 'vvol_test') 

581 self.assertTrue(self.mock_log.exception.called) 

582 

583 @ddt.data(True, False) 

584 def test_nfs_export_del(self, is_snapshot): 

585 if is_snapshot: 

586 name = '/snapshots/vvol_test' 

587 args = {'snapshot_id': 'vvol_test'} 

588 else: 

589 name = '/shares/vvol_test' 

590 args = {'share_id': 'vvol_test'} 

591 

592 fake_nfs_command = ['nfs-export', 'del', name] 

593 self.mock_object(ssh.HNASSSHBackend, '_execute') 

594 

595 self._driver_ssh.nfs_export_del(**args) 

596 

597 self._driver_ssh._execute.assert_called_with(fake_nfs_command) 

598 

599 def test_nfs_export_del_inexistent_export(self): 

600 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

601 side_effect=[putils.ProcessExecutionError( 

602 stderr='does not exist')])) 

603 

604 self._driver_ssh.nfs_export_del('vvol_test') 

605 

606 self.assertTrue(self.mock_log.warning.called) 

607 

608 def test_nfs_export_del_exception(self): 

609 self.assertRaises(exception.HNASBackendException, 

610 self._driver_ssh.nfs_export_del) 

611 

612 def test_nfs_export_del_execute_error(self): 

613 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

614 side_effect=[putils.ProcessExecutionError(stderr='')])) 

615 

616 self.assertRaises(exception.HNASBackendException, 

617 self._driver_ssh.nfs_export_del, 'vvol_test') 

618 self.assertTrue(self.mock_log.exception.called) 

619 

620 @ddt.data(True, False) 

621 def test_cifs_share_add(self, is_snapshot): 

622 if is_snapshot: 

623 name = 'fake_snap' 

624 path = r'\\snapshots\\fake_share\\fake_snap' 

625 else: 

626 name = 'fake_share' 

627 path = r'\\shares\\fake_share' 

628 

629 fake_cifs_add_command = ['cifs-share', 'add', '-S', 'disable', 

630 '--enable-abe', '--nodefaultsaa', 

631 name, self.fs_name, 

632 path] 

633 self.mock_object(ssh.HNASSSHBackend, '_execute') 

634 

635 if is_snapshot: 

636 self._driver_ssh.cifs_share_add('fake_share', 

637 snapshot_id='fake_snap') 

638 else: 

639 self._driver_ssh.cifs_share_add('fake_share') 

640 

641 self._driver_ssh._execute.assert_called_with(fake_cifs_add_command) 

642 

643 def test_cifs_share_add_error(self): 

644 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

645 side_effect=[putils.ProcessExecutionError(stderr='')])) 

646 

647 self.assertRaises(exception.HNASBackendException, 

648 self._driver_ssh.cifs_share_add, 'vvol_test') 

649 self.assertTrue(self.mock_log.exception.called) 

650 

651 def test_cifs_share_del(self): 

652 fake_cifs_del_command = ['cifs-share', 'del', '--target-label', 

653 self.fs_name, 'vvol_test'] 

654 self.mock_object(ssh.HNASSSHBackend, '_execute') 

655 

656 self._driver_ssh.cifs_share_del('vvol_test') 

657 

658 self._driver_ssh._execute.assert_called_with(fake_cifs_del_command) 

659 

660 def test_cifs_share_del_inexistent_share(self): 

661 fake_cifs_del_command = ['cifs-share', 'del', '--target-label', 

662 self.fs_name, 'vvol_test'] 

663 self.mock_object(ssh.HNASSSHBackend, '_execute', 

664 mock.Mock(side_effect=putils.ProcessExecutionError( 

665 exit_code=1))) 

666 

667 self._driver_ssh.cifs_share_del('vvol_test') 

668 

669 self._driver_ssh._execute.assert_called_with(fake_cifs_del_command) 

670 self.assertTrue(self.mock_log.warning.called) 

671 

672 def test_cifs_share_del_exception(self): 

673 fake_cifs_del_command = ['cifs-share', 'del', '--target-label', 

674 self.fs_name, 'vvol_test'] 

675 self.mock_object(ssh.HNASSSHBackend, '_execute', 

676 mock.Mock(side_effect=putils.ProcessExecutionError)) 

677 

678 self.assertRaises(exception.HNASBackendException, 

679 self._driver_ssh.cifs_share_del, 'vvol_test') 

680 self._driver_ssh._execute.assert_called_with(fake_cifs_del_command) 

681 

682 def test_get_nfs_host_list(self): 

683 self.mock_object(ssh.HNASSSHBackend, "_get_export", mock.Mock( 

684 return_value=[ssh.Export(HNAS_RESULT_export)])) 

685 

686 host_list = self._driver_ssh.get_nfs_host_list('fake_id') 

687 

688 self.assertEqual(['127.0.0.2'], host_list) 

689 

690 def test_update_nfs_access_rule_empty_host_list(self): 

691 fake_export_command = ['nfs-export', 'mod', '-c', '127.0.0.1', 

692 '/snapshots/fake_id'] 

693 self.mock_object(ssh.HNASSSHBackend, "_execute") 

694 

695 self._driver_ssh.update_nfs_access_rule([], snapshot_id="fake_id") 

696 

697 self._driver_ssh._execute.assert_called_with(fake_export_command) 

698 

699 def test_update_nfs_access_rule(self): 

700 fake_export_command = ['nfs-export', 'mod', '-c', 

701 u'"127.0.0.1,127.0.0.2"', '/shares/fake_id'] 

702 self.mock_object(ssh.HNASSSHBackend, "_execute") 

703 

704 self._driver_ssh.update_nfs_access_rule(['127.0.0.1', '127.0.0.2'], 

705 share_id="fake_id") 

706 

707 self._driver_ssh._execute.assert_called_with(fake_export_command) 

708 

709 def test_update_nfs_access_rule_exception_no_share_provided(self): 

710 self.assertRaises(exception.HNASBackendException, 

711 self._driver_ssh.update_nfs_access_rule, 

712 ['127.0.0.1']) 

713 

714 def test_update_nfs_access_rule_exception_error(self): 

715 

716 fake_export_command = ['nfs-export', 'mod', '-c', 

717 u'"127.0.0.1,127.0.0.2"', '/shares/fake_id'] 

718 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

719 side_effect=putils.ProcessExecutionError)) 

720 

721 self.assertRaises(exception.HNASBackendException, 

722 self._driver_ssh.update_nfs_access_rule, 

723 ['127.0.0.1', '127.0.0.2'], share_id="fake_id") 

724 

725 self._driver_ssh._execute.assert_called_with(fake_export_command) 

726 

727 def test_cifs_allow_access(self): 

728 fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label', 

729 self.fs_name, 'vvol_test', 

730 'fake_user', 'ar'] 

731 self.mock_object(ssh.HNASSSHBackend, '_execute') 

732 

733 self._driver_ssh.cifs_allow_access('vvol_test', 'fake_user', 'ar') 

734 

735 self._driver_ssh._execute.assert_called_with(fake_cifs_allow_command) 

736 

737 @ddt.data(True, False) 

738 def test_cifs_allow_access_already_allowed_user(self, is_snapshot): 

739 fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label', 

740 self.fs_name, 'vvol_test', 

741 'fake_user', 'acr'] 

742 if not is_snapshot: 

743 fake_cifs_allow_command2 = ['cifs-saa', 'change', '--target-label', 

744 'file_system', 'vvol_test', 

745 'fake_user', 'acr'] 

746 

747 self.mock_object(ssh.HNASSSHBackend, '_execute', 

748 mock.Mock(side_effect=[putils.ProcessExecutionError( 

749 stderr='already listed as a user'), 

750 "Rule modified."])) 

751 

752 self._driver_ssh.cifs_allow_access('vvol_test', 'fake_user', 'acr', 

753 is_snapshot=is_snapshot) 

754 

755 _execute_calls = [mock.call(fake_cifs_allow_command)] 

756 if not is_snapshot: 

757 _execute_calls.append(mock.call(fake_cifs_allow_command2)) 

758 

759 self._driver_ssh._execute.assert_has_calls(_execute_calls) 

760 self.assertTrue(self.mock_log.debug.called) 

761 

762 @ddt.data(True, False) 

763 def test_cifs_allow_access_exception(self, is_snapshot): 

764 fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label', 

765 self.fs_name, 'vvol_test', 

766 'fake_user', 'acr'] 

767 self.mock_object(ssh.HNASSSHBackend, '_execute', 

768 mock.Mock(side_effect=[putils.ProcessExecutionError( 

769 stderr='Could not add user/group fake_user to ' 

770 'share \'vvol_test\'')])) 

771 

772 self.assertRaises(exception.HNASBackendException, 

773 self._driver_ssh.cifs_allow_access, 'vvol_test', 

774 'fake_user', 'acr', is_snapshot=is_snapshot) 

775 

776 self._driver_ssh._execute.assert_called_with(fake_cifs_allow_command) 

777 

778 def test_cifs_update_access_level_exception(self): 

779 fake_cifs_allow_command = ['cifs-saa', 'add', '--target-label', 

780 self.fs_name, 'vvol_test', 

781 'fake_user', 'acr'] 

782 fake_cifs_allow_command2 = ['cifs-saa', 'change', '--target-label', 

783 'file_system', 'vvol_test', 'fake_user', 

784 'acr'] 

785 

786 self.mock_object(ssh.HNASSSHBackend, '_execute', 

787 mock.Mock(side_effect=[putils.ProcessExecutionError( 

788 stderr='already listed as a user'), 

789 putils.ProcessExecutionError( 

790 stderr='Error when trying to modify rule.')])) 

791 

792 self.assertRaises(exception.HNASBackendException, 

793 self._driver_ssh.cifs_allow_access, 'vvol_test', 

794 'fake_user', 'acr') 

795 

796 self._driver_ssh._execute.assert_has_calls( 

797 [mock.call(fake_cifs_allow_command), 

798 mock.call(fake_cifs_allow_command2)]) 

799 self.assertTrue(self.mock_log.debug.called) 

800 

801 def test_cifs_deny_access(self): 

802 fake_cifs_deny_command = ['cifs-saa', 'delete', '--target-label', 

803 self.fs_name, 'vvol_test', 'fake_user'] 

804 self.mock_object(ssh.HNASSSHBackend, '_execute') 

805 

806 self._driver_ssh.cifs_deny_access('vvol_test', 'fake_user') 

807 

808 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command) 

809 

810 @ddt.data(True, False) 

811 def test_cifs_deny_access_already_deleted_user(self, is_snapshot): 

812 fake_cifs_deny_command = ['cifs-saa', 'delete', '--target-label', 

813 self.fs_name, 'vvol_test', 'fake_user'] 

814 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

815 side_effect=[putils.ProcessExecutionError( 

816 stderr='not listed as a user')])) 

817 

818 self._driver_ssh.cifs_deny_access('vvol_test', 'fake_user', 

819 is_snapshot=is_snapshot) 

820 

821 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command) 

822 self.assertTrue(self.mock_log.warning.called) 

823 

824 def test_cifs_deny_access_backend_exception(self): 

825 fake_cifs_deny_command = ['cifs-saa', 'delete', '--target-label', 

826 self.fs_name, 'vvol_test', 'fake_user'] 

827 self.mock_object(ssh.HNASSSHBackend, '_execute', 

828 mock.Mock(side_effect=[putils.ProcessExecutionError( 

829 stderr='Unexpected error')])) 

830 

831 self.assertRaises(exception.HNASBackendException, 

832 self._driver_ssh.cifs_deny_access, 'vvol_test', 

833 'fake_user') 

834 

835 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command) 

836 

837 def test_list_cifs_permission(self): 

838 fake_cifs_list_command = ['cifs-saa', 'list', '--target-label', 

839 self.fs_name, 'vvol_test'] 

840 

841 expected_out = ssh.CIFSPermissions(HNAS_RESULT_list_cifs_permissions) 

842 

843 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

844 return_value=(HNAS_RESULT_list_cifs_permissions, ''))) 

845 

846 out = self._driver_ssh.list_cifs_permissions('vvol_test') 

847 

848 for i in range(len(expected_out.permission_list)): 

849 self.assertEqual(expected_out.permission_list[i], out[i]) 

850 

851 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command) 

852 

853 def test_list_cifs_no_permissions_added(self): 

854 fake_cifs_list_command = ['cifs-saa', 'list', '--target-label', 

855 self.fs_name, 'vvol_test'] 

856 

857 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

858 side_effect=[putils.ProcessExecutionError( 

859 stderr='No entries for this share')])) 

860 

861 out = self._driver_ssh.list_cifs_permissions('vvol_test') 

862 

863 self.assertEqual([], out) 

864 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command) 

865 self.assertTrue(self.mock_log.debug.called) 

866 

867 def test_list_cifs_exception(self): 

868 fake_cifs_list_command = ['cifs-saa', 'list', '--target-label', 

869 self.fs_name, 'vvol_test'] 

870 

871 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

872 side_effect=[putils.ProcessExecutionError( 

873 stderr='Error.')])) 

874 

875 self.assertRaises(exception.HNASBackendException, 

876 self._driver_ssh.list_cifs_permissions, 

877 "vvol_test") 

878 

879 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command) 

880 self.assertTrue(self.mock_log.exception.called) 

881 

882 def test_tree_clone_nothing_to_clone(self): 

883 fake_tree_clone_command = ['tree-clone-job-submit', '-e', '-f', 

884 self.fs_name, '/src', '/dst'] 

885 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

886 side_effect=[putils.ProcessExecutionError( 

887 stderr='Cannot find any clonable files in the source directory' 

888 )])) 

889 

890 self.assertRaises(exception.HNASNothingToCloneException, 

891 self._driver_ssh.tree_clone, "/src", "/dst") 

892 self._driver_ssh._execute.assert_called_with(fake_tree_clone_command) 

893 

894 def test_tree_clone_error_cloning(self): 

895 fake_tree_clone_command = ['tree-clone-job-submit', '-e', '-f', 

896 self.fs_name, '/src', '/dst'] 

897 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

898 side_effect=[putils.ProcessExecutionError(stderr='')])) 

899 

900 self.assertRaises(exception.HNASBackendException, 

901 self._driver_ssh.tree_clone, "/src", "/dst") 

902 self._driver_ssh._execute.assert_called_with(fake_tree_clone_command) 

903 self.assertTrue(self.mock_log.exception.called) 

904 

905 def test_tree_clone(self): 

906 fake_tree_clone_command = ['tree-clone-job-submit', '-e', '-f', 

907 self.fs_name, '/src', '/dst'] 

908 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

909 side_effect=[(HNAS_RESULT_job, ''), 

910 (HNAS_RESULT_job_completed, '')])) 

911 

912 self._driver_ssh.tree_clone("/src", "/dst") 

913 

914 self._driver_ssh._execute.assert_any_call(fake_tree_clone_command) 

915 self.assertTrue(self.mock_log.debug.called) 

916 

917 def test_tree_clone_job_failed(self): 

918 fake_tree_clone_command = ['tree-clone-job-submit', '-e', '-f', 

919 self.fs_name, '/src', '/dst'] 

920 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

921 side_effect=[(HNAS_RESULT_job, ''), 

922 (HNAS_RESULT_tree_job_status_fail, '')])) 

923 

924 self.assertRaises(exception.HNASBackendException, 

925 self._driver_ssh.tree_clone, "/src", "/dst") 

926 self._driver_ssh._execute.assert_any_call(fake_tree_clone_command) 

927 self.assertTrue(self.mock_log.error.called) 

928 

929 def test_tree_clone_job_timeout(self): 

930 fake_tree_clone_command = ['tree-clone-job-submit', '-e', '-f', 

931 self.fs_name, '/src', '/dst'] 

932 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

933 side_effect=[(HNAS_RESULT_job, ''), 

934 (HNAS_RESULT_job_running, ''), 

935 (HNAS_RESULT_job_running, ''), 

936 (HNAS_RESULT_job_running, ''), 

937 (HNAS_RESULT_empty, '')])) 

938 self.mock_object(time, "time", mock.Mock(side_effect=[0, 0, 200, 200])) 

939 self.mock_object(time, "sleep") 

940 

941 self.assertRaises(exception.HNASBackendException, 

942 self._driver_ssh.tree_clone, "/src", "/dst") 

943 self._driver_ssh._execute.assert_any_call(fake_tree_clone_command) 

944 self.assertTrue(self.mock_log.error.called) 

945 

946 def test_tree_delete_path_does_not_exist(self): 

947 fake_tree_delete_command = ['tree-delete-job-submit', '--confirm', 

948 '-f', self.fs_name, '/path'] 

949 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

950 side_effect=[putils.ProcessExecutionError( 

951 stderr='Source path: Cannot access')] 

952 )) 

953 

954 self._driver_ssh.tree_delete("/path") 

955 

956 self.assertTrue(self.mock_log.warning.called) 

957 self._driver_ssh._execute.assert_called_with(fake_tree_delete_command) 

958 

959 def test_tree_delete_error(self): 

960 fake_tree_delete_command = ['tree-delete-job-submit', '--confirm', 

961 '-f', self.fs_name, '/path'] 

962 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

963 side_effect=[putils.ProcessExecutionError( 

964 stderr='')] 

965 )) 

966 

967 self.assertRaises(exception.HNASBackendException, 

968 self._driver_ssh.tree_delete, "/path") 

969 self.assertTrue(self.mock_log.exception.called) 

970 self._driver_ssh._execute.assert_called_with(fake_tree_delete_command) 

971 

972 def test_create_directory(self): 

973 locked_selectfs_args = ['create', '/path'] 

974 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

975 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

976 mock.Mock(return_value=True)) 

977 

978 self._driver_ssh.create_directory("/path") 

979 

980 self._driver_ssh._locked_selectfs.assert_called_with( 

981 *locked_selectfs_args) 

982 ssh.HNASSSHBackend.check_directory.assert_called_once_with('/path') 

983 self.assertFalse(self.mock_log.warning.called) 

984 

985 def test_create_directory_context_change_fail(self): 

986 locked_selectfs_args = ['create', '/path'] 

987 self.mock_object(time, 'sleep') 

988 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

989 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

990 mock.Mock(return_value=False)) 

991 

992 self.assertRaises(exception.HNASSSCContextChange, 

993 self._driver_ssh.create_directory, "/path") 

994 

995 self._driver_ssh._locked_selectfs.assert_called_with( 

996 *locked_selectfs_args) 

997 ssh.HNASSSHBackend.check_directory.assert_called_with('/path') 

998 self.assertTrue(self.mock_log.warning.called) 

999 

1000 def test_create_directory_context_change_success(self): 

1001 locked_selectfs_args = ['create', '/path'] 

1002 self.mock_object(time, 'sleep') 

1003 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

1004 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

1005 mock.Mock(side_effect=[False, False, True])) 

1006 

1007 self._driver_ssh.create_directory("/path") 

1008 

1009 self._driver_ssh._locked_selectfs.assert_called_with( 

1010 *locked_selectfs_args) 

1011 ssh.HNASSSHBackend.check_directory.assert_called_with('/path') 

1012 self.assertTrue(self.mock_log.warning.called) 

1013 

1014 def test_delete_directory(self): 

1015 locked_selectfs_args = ['delete', '/path'] 

1016 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

1017 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

1018 mock.Mock(return_value=False)) 

1019 

1020 self._driver_ssh.delete_directory("/path") 

1021 

1022 self._driver_ssh._locked_selectfs.assert_called_with( 

1023 *locked_selectfs_args) 

1024 ssh.HNASSSHBackend.check_directory.assert_called_once_with('/path') 

1025 self.assertFalse(self.mock_log.debug.called) 

1026 

1027 def test_delete_directory_directory_not_empty(self): 

1028 locked_selectfs_args = ['delete', '/path'] 

1029 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs", mock.Mock( 

1030 side_effect=exception.HNASDirectoryNotEmpty(msg='fake'))) 

1031 self.mock_object(ssh.HNASSSHBackend, "check_directory") 

1032 

1033 self._driver_ssh.delete_directory("/path") 

1034 

1035 self._driver_ssh._locked_selectfs.assert_called_with( 

1036 *locked_selectfs_args) 

1037 ssh.HNASSSHBackend.check_directory.assert_not_called() 

1038 self.assertFalse(self.mock_log.debug.called) 

1039 

1040 def test_delete_directory_context_change_fail(self): 

1041 locked_selectfs_args = ['delete', '/path'] 

1042 self.mock_object(time, 'sleep') 

1043 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

1044 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

1045 mock.Mock(return_value=True)) 

1046 

1047 self.assertRaises(exception.HNASSSCContextChange, 

1048 self._driver_ssh.delete_directory, "/path") 

1049 

1050 self._driver_ssh._locked_selectfs.assert_called_with( 

1051 *locked_selectfs_args) 

1052 ssh.HNASSSHBackend.check_directory.assert_called_with('/path') 

1053 self.assertTrue(self.mock_log.debug.called) 

1054 

1055 def test_delete_directory_context_change_success(self): 

1056 locked_selectfs_args = ['delete', '/path'] 

1057 self.mock_object(time, 'sleep') 

1058 self.mock_object(ssh.HNASSSHBackend, "_locked_selectfs") 

1059 self.mock_object(ssh.HNASSSHBackend, "check_directory", 

1060 mock.Mock(side_effect=[True, True, False])) 

1061 

1062 self._driver_ssh.delete_directory("/path") 

1063 

1064 self._driver_ssh._locked_selectfs.assert_called_with( 

1065 *locked_selectfs_args) 

1066 ssh.HNASSSHBackend.check_directory.assert_called_with('/path') 

1067 self.assertTrue(self.mock_log.debug.called) 

1068 

1069 def test_check_directory(self): 

1070 path = ("/snapshots/" + self.snapshot['share_id'] + "/" + 

1071 self.snapshot['id']) 

1072 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path] 

1073 

1074 self.mock_object(ssh.HNASSSHBackend, '_execute') 

1075 

1076 out = self._driver_ssh.check_directory(path) 

1077 

1078 self.assertTrue(out) 

1079 self._driver_ssh._execute.assert_called_with(check_snap_args) 

1080 

1081 def test_check_directory_retry(self): 

1082 error_msg = ("Unable to run path-to-object-number as " 

1083 "path-to-object-number is currently running on volume " 

1084 "39.") 

1085 path = ("/snapshots/" + self.snapshot['share_id'] + "/" + 

1086 self.snapshot['id']) 

1087 

1088 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path] 

1089 

1090 self.mock_object(time, "sleep") 

1091 self.mock_object(ssh.HNASSSHBackend, '_execute', 

1092 mock.Mock(side_effect=[putils.ProcessExecutionError( 

1093 stdout=error_msg), putils.ProcessExecutionError( 

1094 stdout=error_msg), 'Object number: 0x45a4'])) 

1095 

1096 out = self._driver_ssh.check_directory(path) 

1097 

1098 self.assertIs(True, out) 

1099 self._driver_ssh._execute.assert_called_with(check_snap_args) 

1100 

1101 def test_check_inexistent_snapshot(self): 

1102 path = "/path/snap1/snapshot07-08-2016" 

1103 

1104 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path] 

1105 

1106 self.mock_object(ssh.HNASSSHBackend, '_execute', 

1107 mock.Mock(side_effect=putils.ProcessExecutionError( 

1108 stdout=HNAS_RESULT_check_snap_error))) 

1109 

1110 out = self._driver_ssh.check_directory(path) 

1111 

1112 self.assertFalse(out) 

1113 self._driver_ssh._execute.assert_called_with(check_snap_args) 

1114 

1115 def test_check_directory_error(self): 

1116 path = "/path/snap1/snapshot07-08-2016" 

1117 

1118 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path] 

1119 

1120 self.mock_object(ssh.HNASSSHBackend, '_execute', 

1121 mock.Mock(side_effect=putils.ProcessExecutionError( 

1122 stdout="Internal Server Error."))) 

1123 

1124 self.assertRaises(exception.HNASBackendException, 

1125 self._driver_ssh.check_directory, path) 

1126 

1127 self._driver_ssh._execute.assert_called_with(check_snap_args) 

1128 

1129 def test_check_fs_mounted_true(self): 

1130 self.mock_object(ssh.HNASSSHBackend, "_execute", 

1131 mock.Mock(return_value=(HNAS_RESULT_df, ''))) 

1132 

1133 self.assertTrue(self._driver_ssh.check_fs_mounted()) 

1134 

1135 def test_check_fs_mounted_false(self): 

1136 self.mock_object( 

1137 ssh.HNASSSHBackend, "_execute", 

1138 mock.Mock(return_value=(HNAS_RESULT_df_unmounted, ''))) 

1139 

1140 self.assertFalse(self._driver_ssh.check_fs_mounted()) 

1141 

1142 def test_check_fs_mounted_error(self): 

1143 self.mock_object( 

1144 ssh.HNASSSHBackend, "_execute", 

1145 mock.Mock(return_value=(HNAS_RESULT_df_error, ''))) 

1146 

1147 self.assertRaises(exception.HNASItemNotFoundException, 

1148 self._driver_ssh.check_fs_mounted) 

1149 

1150 def test_mount_already_mounted(self): 

1151 fake_mount_command = ['mount', self.fs_name] 

1152 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1153 side_effect=putils.ProcessExecutionError(stderr=''))) 

1154 

1155 self.assertRaises( 

1156 exception.HNASBackendException, self._driver_ssh.mount) 

1157 

1158 self._driver_ssh._execute.assert_called_with(fake_mount_command) 

1159 

1160 def test_vvol_create(self): 

1161 fake_vvol_create_command = ['virtual-volume', 'add', '--ensure', 

1162 self.fs_name, 'vvol', '/shares/vvol'] 

1163 self.mock_object(ssh.HNASSSHBackend, "_execute") 

1164 

1165 self._driver_ssh.vvol_create("vvol") 

1166 

1167 self._driver_ssh._execute.assert_called_with(fake_vvol_create_command) 

1168 

1169 def test_vvol_create_error(self): 

1170 fake_vvol_create_command = ['virtual-volume', 'add', '--ensure', 

1171 self.fs_name, 'vvol', '/shares/vvol'] 

1172 self.mock_object(ssh.HNASSSHBackend, "_execute", 

1173 mock.Mock(side_effect=putils.ProcessExecutionError)) 

1174 

1175 self.assertRaises(exception.HNASBackendException, 

1176 self._driver_ssh.vvol_create, "vvol") 

1177 

1178 self._driver_ssh._execute.assert_called_with(fake_vvol_create_command) 

1179 

1180 def test_vvol_delete_vvol_does_not_exist(self): 

1181 fake_vvol_delete_command = ['tree-delete-job-submit', '--confirm', 

1182 '-f', self.fs_name, '/shares/vvol'] 

1183 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1184 side_effect=[putils.ProcessExecutionError( 

1185 stderr='Source path: Cannot access')] 

1186 )) 

1187 

1188 self._driver_ssh.vvol_delete("vvol") 

1189 

1190 self.assertTrue(self.mock_log.warning.called) 

1191 self._driver_ssh._execute.assert_called_with(fake_vvol_delete_command) 

1192 

1193 def test_vvol_delete_error(self): 

1194 fake_vvol_delete_command = ['tree-delete-job-submit', '--confirm', 

1195 '-f', self.fs_name, '/shares/vvol'] 

1196 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1197 side_effect=[putils.ProcessExecutionError( 

1198 stderr='')] 

1199 )) 

1200 

1201 self.assertRaises(exception.HNASBackendException, 

1202 self._driver_ssh.vvol_delete, "vvol") 

1203 self.assertTrue(self.mock_log.exception.called) 

1204 self._driver_ssh._execute.assert_called_with(fake_vvol_delete_command) 

1205 

1206 def test_quota_add(self): 

1207 fake_add_quota_command = ['quota', 'add', '--usage-limit', '1G', 

1208 '--usage-hard-limit', 'yes', 

1209 self.fs_name, 'vvol'] 

1210 self.mock_object(ssh.HNASSSHBackend, "_execute") 

1211 

1212 self._driver_ssh.quota_add('vvol', 1) 

1213 

1214 self._driver_ssh._execute.assert_called_with(fake_add_quota_command) 

1215 

1216 def test_modify_quota(self): 

1217 fake_modify_quota_command = ['quota', 'mod', '--usage-limit', '1G', 

1218 self.fs_name, 'vvol'] 

1219 self.mock_object(ssh.HNASSSHBackend, "_execute") 

1220 

1221 self._driver_ssh.modify_quota('vvol', 1) 

1222 

1223 self._driver_ssh._execute.assert_called_with(fake_modify_quota_command) 

1224 

1225 def test_quota_add_error(self): 

1226 fake_add_quota_command = ['quota', 'add', '--usage-limit', '1G', 

1227 '--usage-hard-limit', 'yes', 

1228 self.fs_name, 'vvol'] 

1229 self.mock_object(ssh.HNASSSHBackend, "_execute", 

1230 mock.Mock(side_effect=putils.ProcessExecutionError)) 

1231 

1232 self.assertRaises(exception.HNASBackendException, 

1233 self._driver_ssh.quota_add, 'vvol', 1) 

1234 

1235 self._driver_ssh._execute.assert_called_with(fake_add_quota_command) 

1236 

1237 def test_modify_quota_error(self): 

1238 fake_modify_quota_command = ['quota', 'mod', '--usage-limit', '1G', 

1239 self.fs_name, 'vvol'] 

1240 self.mock_object(ssh.HNASSSHBackend, "_execute", 

1241 mock.Mock(side_effect=putils.ProcessExecutionError)) 

1242 

1243 self.assertRaises(exception.HNASBackendException, 

1244 self._driver_ssh.modify_quota, 'vvol', 1) 

1245 

1246 self._driver_ssh._execute.assert_called_with(fake_modify_quota_command) 

1247 

1248 def test_check_vvol(self): 

1249 fake_check_vvol_command = ['virtual-volume', 'list', '--verbose', 

1250 self.fs_name, 'vvol'] 

1251 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1252 side_effect=putils.ProcessExecutionError(stderr=''))) 

1253 

1254 self.assertRaises(exception.HNASItemNotFoundException, 

1255 self._driver_ssh.check_vvol, 'vvol') 

1256 self._driver_ssh._execute.assert_called_with(fake_check_vvol_command) 

1257 

1258 def test_check_quota(self): 

1259 fake_check_quota_command = ['quota', 'list', '--verbose', 

1260 self.fs_name, 'vvol'] 

1261 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1262 return_value=('No quotas matching specified filter criteria', ''))) 

1263 

1264 self.assertRaises(exception.HNASItemNotFoundException, 

1265 self._driver_ssh.check_quota, 'vvol') 

1266 self._driver_ssh._execute.assert_called_with(fake_check_quota_command) 

1267 

1268 def test_check_quota_error(self): 

1269 fake_check_quota_command = ['quota', 'list', '--verbose', 

1270 self.fs_name, 'vvol'] 

1271 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1272 side_effect=putils.ProcessExecutionError)) 

1273 

1274 self.assertRaises(exception.HNASBackendException, 

1275 self._driver_ssh.check_quota, 'vvol') 

1276 self._driver_ssh._execute.assert_called_with(fake_check_quota_command) 

1277 

1278 @ddt.data(True, False) 

1279 def test_check_export(self, is_snapshot): 

1280 self.mock_object(ssh.HNASSSHBackend, "_get_export", mock.Mock( 

1281 return_value=[ssh.Export(HNAS_RESULT_export)])) 

1282 

1283 self._driver_ssh.check_export("vvol_test", is_snapshot) 

1284 

1285 def test_check_export_error(self): 

1286 self.mock_object(ssh.HNASSSHBackend, "_get_export", mock.Mock( 

1287 return_value=[ssh.Export(HNAS_RESULT_wrong_export)])) 

1288 

1289 self.assertRaises(exception.HNASItemNotFoundException, 

1290 self._driver_ssh.check_export, "vvol_test") 

1291 

1292 def test_check_cifs(self): 

1293 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test'] 

1294 

1295 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1296 return_value=[HNAS_RESULT_cifs_list, ''])) 

1297 

1298 self._driver_ssh.check_cifs('vvol_test') 

1299 

1300 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1301 

1302 def test_check_cifs_inexistent_share(self): 

1303 check_cifs_share_command = ['cifs-share', 'list', 'wrong_vvol'] 

1304 

1305 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1306 side_effect=[putils.ProcessExecutionError( 

1307 stderr='Export wrong_vvol does not exist on backend ' 

1308 'anymore.')])) 

1309 

1310 self.assertRaises(exception.HNASItemNotFoundException, 

1311 self._driver_ssh.check_cifs, 'wrong_vvol') 

1312 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1313 

1314 def test_check_cifs_exception(self): 

1315 check_cifs_share_command = ['cifs-share', 'list', 'wrong_vvol'] 

1316 

1317 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1318 side_effect=[putils.ProcessExecutionError(stderr='Error.')])) 

1319 

1320 self.assertRaises(exception.HNASBackendException, 

1321 self._driver_ssh.check_cifs, 'wrong_vvol') 

1322 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1323 

1324 def test_check_cifs_different_fs_exception(self): 

1325 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test'] 

1326 

1327 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1328 return_value=[HNAS_RESULT_different_fs_cifs_list, ''])) 

1329 

1330 self.assertRaises(exception.HNASItemNotFoundException, 

1331 self._driver_ssh.check_cifs, 'vvol_test') 

1332 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1333 

1334 def test_is_cifs_in_use(self): 

1335 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test'] 

1336 

1337 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1338 return_value=[HNAS_RESULT_cifs_list, ''])) 

1339 

1340 out = self._driver_ssh.is_cifs_in_use('vvol_test') 

1341 

1342 self.assertTrue(out) 

1343 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1344 

1345 def test_is_cifs_without_use(self): 

1346 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test'] 

1347 

1348 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1349 return_value=[HNAS_RESULT_different_fs_cifs_list, ''])) 

1350 

1351 out = self._driver_ssh.is_cifs_in_use('vvol_test') 

1352 

1353 self.assertFalse(out) 

1354 self._driver_ssh._execute.assert_called_with(check_cifs_share_command) 

1355 

1356 def test_get_share_quota(self): 

1357 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1358 return_value=(HNAS_RESULT_quota, ''))) 

1359 

1360 result = self._driver_ssh.get_share_quota("vvol_test") 

1361 

1362 self.assertEqual(5, result) 

1363 

1364 @ddt.data(HNAS_RESULT_quota_unset, HNAS_RESULT_quota_err) 

1365 def test_get_share_quota_errors(self, hnas_output): 

1366 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1367 return_value=(hnas_output, ''))) 

1368 

1369 result = self._driver_ssh.get_share_quota("vvol_test") 

1370 

1371 self.assertIsNone(result) 

1372 

1373 def test_get_share_quota_tb(self): 

1374 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1375 return_value=(HNAS_RESULT_quota_tb, ''))) 

1376 

1377 result = self._driver_ssh.get_share_quota("vvol_test") 

1378 

1379 self.assertEqual(1024, result) 

1380 

1381 def test_get_share_quota_mb(self): 

1382 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1383 return_value=(HNAS_RESULT_quota_mb, ''))) 

1384 

1385 self.assertRaises(exception.HNASBackendException, 

1386 self._driver_ssh.get_share_quota, "vvol_test") 

1387 

1388 def test_get_share_usage(self): 

1389 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1390 return_value=(HNAS_RESULT_quota, ''))) 

1391 

1392 self.assertEqual(1, self._driver_ssh.get_share_usage("vvol_test")) 

1393 

1394 def test_get_share_usage_error(self): 

1395 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1396 return_value=(HNAS_RESULT_quota_err, ''))) 

1397 

1398 self.assertRaises(exception.HNASItemNotFoundException, 

1399 self._driver_ssh.get_share_usage, "vvol_test") 

1400 

1401 def test_get_share_usage_mb(self): 

1402 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1403 return_value=(HNAS_RESULT_quota_mb, ''))) 

1404 

1405 self.assertEqual(0.01953125, self._driver_ssh.get_share_usage( 

1406 "vvol_test")) 

1407 

1408 def test_get_share_usage_tb(self): 

1409 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1410 return_value=(HNAS_RESULT_quota_tb, ''))) 

1411 

1412 self.assertEqual(1024, self._driver_ssh.get_share_usage("vvol_test")) 

1413 

1414 @ddt.data(True, False) 

1415 def test__get_share_export(self, is_snapshot): 

1416 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1417 return_value=[HNAS_RESULT_export_ip, ''])) 

1418 

1419 export_list = self._driver_ssh._get_export( 

1420 name='fake_name', is_snapshot=is_snapshot) 

1421 path = '/shares/fake_name' 

1422 if is_snapshot: 

1423 path = '/snapshots/fake_name' 

1424 

1425 command = ['nfs-export', 'list ', path] 

1426 

1427 self._driver_ssh._execute.assert_called_with(command) 

1428 self.assertEqual('vvol_test', export_list[0].export_name) 

1429 self.assertEqual('/vvol_test', export_list[0].export_path) 

1430 self.assertEqual('fake_fs', export_list[0].file_system_label) 

1431 self.assertEqual('Yes', export_list[0].mounted) 

1432 self.assertIn('rw', export_list[0].export_configuration[0]) 

1433 

1434 def test__get_share_export_fs_not_available(self): 

1435 

1436 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1437 return_value=[HNAS_RESULT_exp_no_fs, ''])) 

1438 

1439 export_list = self._driver_ssh._get_export(name='fake_name') 

1440 path = '/shares/fake_name' 

1441 

1442 command = ['nfs-export', 'list ', path] 

1443 

1444 self._driver_ssh._execute.assert_called_with(command) 

1445 self.assertEqual('no_fs', export_list[0].export_name) 

1446 self.assertEqual('/export_without_fs', export_list[0].export_path) 

1447 self.assertEqual('*** not available ***', 

1448 export_list[0].file_system_info) 

1449 self.assertEqual([], export_list[0].export_configuration) 

1450 not_in_keys = ['file_system_label', 'file_system_size', 'formatted', 

1451 'file_system_free_space', 'file_system_state', 'failed', 

1452 'mounted', 'thin_provisioned'] 

1453 for key in not_in_keys: 

1454 self.assertNotIn(key, export_list[0].__dict__) 

1455 

1456 def test__get_share_export_exception_not_found(self): 

1457 

1458 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1459 side_effect=putils.ProcessExecutionError( 

1460 stderr="NFS Export List: Export 'id' does not exist."))) 

1461 

1462 self.assertRaises(exception.HNASItemNotFoundException, 

1463 self._driver_ssh._get_export, 'fake_id') 

1464 

1465 def test__get_share_export_exception_error(self): 

1466 

1467 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock( 

1468 side_effect=putils.ProcessExecutionError(stderr="Some error.") 

1469 )) 

1470 

1471 self.assertRaises(exception.HNASBackendException, 

1472 self._driver_ssh._get_export, 'fake_id') 

1473 

1474 def test__execute(self): 

1475 key = self.ssh_private_key 

1476 commands = ['tree-clone-job-submit', '-e', '/src', '/dst'] 

1477 concat_command = ('ssc --smuauth fake console-context --evs 2 ' 

1478 'tree-clone-job-submit -e /src /dst') 

1479 self.mock_object(paramiko.SSHClient, 'connect') 

1480 self.mock_object(putils, 'ssh_execute', 

1481 mock.Mock(return_value=[HNAS_RESULT_job, ''])) 

1482 

1483 output, err = self._driver_ssh._execute(commands) 

1484 

1485 putils.ssh_execute.assert_called_once_with(mock.ANY, concat_command, 

1486 check_exit_code=True) 

1487 paramiko.SSHClient.connect.assert_called_with(self.ip, 

1488 username=self.user, 

1489 key_filename=key, 

1490 look_for_keys=False, 

1491 timeout=None, 

1492 password=self.password, 

1493 port=self.port, 

1494 banner_timeout=None) 

1495 self.assertIn('Request submitted successfully.', output) 

1496 

1497 def test__execute_ssh_exception(self): 

1498 commands = ['tree-clone-job-submit', '-e', '/src', '/dst'] 

1499 concat_command = ('ssc --smuauth fake console-context --evs 2 ' 

1500 'tree-clone-job-submit -e /src /dst') 

1501 msg = 'Failed to establish SSC connection' 

1502 

1503 self.mock_object(time, "sleep") 

1504 self.mock_object(paramiko.SSHClient, 'connect') 

1505 self.mock_object(putils, 'ssh_execute', 

1506 mock.Mock(side_effect=[ 

1507 putils.ProcessExecutionError(stderr=msg), 

1508 putils.ProcessExecutionError(stderr='Invalid!')])) 

1509 self.mock_object(ssh_utils.SSHPool, "item", 

1510 mock.Mock(return_value=paramiko.SSHClient())) 

1511 self.mock_object(paramiko.SSHClient, "set_missing_host_key_policy") 

1512 

1513 self.assertRaises(putils.ProcessExecutionError, 

1514 self._driver_ssh._execute, commands) 

1515 

1516 putils.ssh_execute.assert_called_with(mock.ANY, concat_command, 

1517 check_exit_code=True) 

1518 

1519 self.assertTrue(self.mock_log.debug.called) 

1520 

1521 def test__locked_selectfs_create_operation(self): 

1522 exec_command = ['selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 

1523 'console-context', '--evs', str(self.evs_id), 

1524 'mkdir', '-p', '/path'] 

1525 self.mock_object(ssh.HNASSSHBackend, '_execute') 

1526 

1527 self._driver_ssh._locked_selectfs('create', '/path') 

1528 

1529 self._driver_ssh._execute.assert_called_with(exec_command) 

1530 

1531 def test__locked_selectfs_create_operation_error(self): 

1532 exec_command = ['selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 

1533 'console-context', '--evs', str(self.evs_id), 

1534 'mkdir', '-p', '/path'] 

1535 self.mock_object( 

1536 ssh.HNASSSHBackend, '_execute', 

1537 mock.Mock(side_effect=putils.ProcessExecutionError( 

1538 stderr="some error"))) 

1539 

1540 self.assertRaises(exception.HNASBackendException, 

1541 self._driver_ssh._locked_selectfs, 'create', '/path') 

1542 

1543 self._driver_ssh._execute.assert_called_with(exec_command) 

1544 

1545 def test__locked_selectfs_create_operation_context_change(self): 

1546 exec_command = ['selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 

1547 'console-context', '--evs', str(self.evs_id), 

1548 'mkdir', '-p', '/path'] 

1549 self.mock_object( 

1550 ssh.HNASSSHBackend, '_execute', 

1551 mock.Mock(side_effect=putils.ProcessExecutionError( 

1552 stderr="Current file system invalid: VolumeNotFound"))) 

1553 

1554 self.assertRaises(exception.HNASSSCContextChange, 

1555 self._driver_ssh._locked_selectfs, 'create', '/path') 

1556 

1557 self._driver_ssh._execute.assert_called_with(exec_command) 

1558 self.assertTrue(self.mock_log.debug.called) 

1559 

1560 def test__locked_selectfs_delete_operation_successful(self): 

1561 exec_command = ['selectfs', self.fs_name, '\n', 'ssc', '127.0.0.1', 

1562 'console-context', '--evs', str(self.evs_id), 

1563 'rmdir', '/path'] 

1564 self.mock_object(ssh.HNASSSHBackend, '_execute') 

1565 

1566 self._driver_ssh._locked_selectfs('delete', '/path') 

1567 

1568 self._driver_ssh._execute.assert_called_with(exec_command) 

1569 

1570 def test__locked_selectfs_deleting_not_empty_directory(self): 

1571 msg = 'This path has more snapshot. Currenty DirectoryNotEmpty' 

1572 

1573 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1574 side_effect=[putils.ProcessExecutionError(stderr=msg)])) 

1575 

1576 self.assertRaises(exception.HNASDirectoryNotEmpty, 

1577 self._driver_ssh._locked_selectfs, 'delete', '/path') 

1578 

1579 self.assertTrue(self.mock_log.debug.called) 

1580 

1581 def test__locked_selectfs_delete_exception(self): 

1582 msg = "rmdir: cannot remove '/path'" 

1583 

1584 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1585 side_effect=[putils.ProcessExecutionError(stderr=msg)])) 

1586 

1587 self.assertRaises(exception.HNASBackendException, 

1588 self._driver_ssh._locked_selectfs, 'delete', 'path') 

1589 self.assertTrue(self.mock_log.exception.called) 

1590 

1591 def test__locked_selectfs_delete_not_found(self): 

1592 msg = "rmdir: cannot remove '/path': NotFound" 

1593 

1594 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1595 side_effect=[putils.ProcessExecutionError(stderr=msg)])) 

1596 

1597 self._driver_ssh._locked_selectfs('delete', 'path') 

1598 

1599 self.assertTrue(self.mock_log.warning.called) 

1600 

1601 def test__locked_selectfs_delete_context_change(self): 

1602 msg = "Current file system invalid: VolumeNotFound" 

1603 

1604 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock( 

1605 side_effect=[putils.ProcessExecutionError(stderr=msg)])) 

1606 

1607 self.assertRaises(exception.HNASSSCContextChange, 

1608 self._driver_ssh._locked_selectfs, 'delete', 'path') 

1609 

1610 self.assertTrue(self.mock_log.debug.called)