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
« 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.
16import time
17from unittest import mock
19import ddt
20from oslo_concurrency import processutils as putils
21from oslo_config import cfg
22import paramiko
24from manila import exception
25from manila.share.drivers.hitachi.hnas import ssh
26from manila import ssh_utils
27from manila import test
30CONF = cfg.CONF
32HNAS_RESULT_empty = ""
34HNAS_RESULT_limits = """
35Filesystem Ensure on span fake_fs:
37Current capacity 50GiB
39Thin provision: disabled
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) """
52HNAS_RESULT_expdel = """Deleting the export '/dir1' on fs 'fake_fs'...
53NFS Export Delete: Export successfully deleted"""
55HNAS_RESULT_vvoldel = """
56Warning: Clearing dangling space trackers from empty vivol"""
58HNAS_RESULT_selectfs = "Current selected file system: fake_fs, number(1)"
60HNAS_RESULT_expadd = "NFS Export Add: Export added successfully"
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"""
69HNAS_RESULT_vvol_error = "The virtual volume does not exist."
71HNAS_RESULT_mount = """ \
72Request to mount file system fake_fs submitted successfully.
73File system fake_fs successfully mounted."""
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 """
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 """
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 """
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 """
139HNAS_RESULT_quota_err = """No quotas matching specified filter criteria.
140"""
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"""
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"""
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 """
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"""
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"""
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"""
240HNAS_RESULT_expnotmod = "Export not modified."
242HNAS_RESULT_job = """tree-operation-job-submit: Request submitted successfully.
243tree-operation-job-submit: Job id = d933100a-b5f6-11d0-91d9-836896aada5d"""
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"""
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
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
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
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"""
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
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"""
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"""
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"""
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"""
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"""
383HNAS_RESULT_df_error = """File system file_system not found"""
385HNAS_RESULT_mounted_filesystem = """
386file_system 1055 fake_span Mount 2 4 5 1
387"""
389HNAS_RESULT_unmounted_filesystem = """
390file_system 1055 fake_span Umount 2 4 5 1
391"""
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"""
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"""
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
466"""
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"""
473@ddt.ddt
474class HNASSSHTestCase(test.TestCase):
475 def setUp(self):
476 super(HNASSSHTestCase, self).setUp()
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
490 self.mock_log = self.mock_object(ssh, 'LOG')
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)
499 self.vvol = {
500 'id': 'vvol_test',
501 'share_proto': 'nfs',
502 'size': 4,
503 'host': '127.0.0.1',
504 }
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()
515 def test_get_stats(self):
516 fake_list_command = ['df', '-a', '-f', self.fs_name]
518 self.mock_object(ssh.HNASSSHBackend, '_execute',
519 mock.Mock(return_value=(HNAS_RESULT_df_tb, "")))
521 total, free, dedupe = self._driver_ssh.get_stats()
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)
528 def test_get_stats_dedupe_on(self):
529 fake_list_command = ['df', '-a', '-f', self.fs_name]
531 self.mock_object(
532 ssh.HNASSSHBackend, '_execute',
533 mock.Mock(return_value=(HNAS_RESULT_df_dedupe_on, "")))
535 total, free, dedupe = self._driver_ssh.get_stats()
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)
542 def test_get_stats_error(self):
544 fake_list_command = ['df', '-a', '-f', self.fs_name]
546 self.mock_object(ssh.HNASSSHBackend, '_execute',
547 mock.Mock(side_effect=putils.ProcessExecutionError))
549 self.assertRaises(exception.HNASBackendException,
550 self._driver_ssh.get_stats)
552 ssh.HNASSSHBackend._execute.assert_called_with(fake_list_command)
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'
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')
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')
573 self._driver_ssh._execute.assert_called_with(fake_nfs_command)
575 def test_nfs_export_add_error(self):
576 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
577 side_effect=[putils.ProcessExecutionError(stderr='')]))
579 self.assertRaises(exception.HNASBackendException,
580 self._driver_ssh.nfs_export_add, 'vvol_test')
581 self.assertTrue(self.mock_log.exception.called)
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'}
592 fake_nfs_command = ['nfs-export', 'del', name]
593 self.mock_object(ssh.HNASSSHBackend, '_execute')
595 self._driver_ssh.nfs_export_del(**args)
597 self._driver_ssh._execute.assert_called_with(fake_nfs_command)
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')]))
604 self._driver_ssh.nfs_export_del('vvol_test')
606 self.assertTrue(self.mock_log.warning.called)
608 def test_nfs_export_del_exception(self):
609 self.assertRaises(exception.HNASBackendException,
610 self._driver_ssh.nfs_export_del)
612 def test_nfs_export_del_execute_error(self):
613 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
614 side_effect=[putils.ProcessExecutionError(stderr='')]))
616 self.assertRaises(exception.HNASBackendException,
617 self._driver_ssh.nfs_export_del, 'vvol_test')
618 self.assertTrue(self.mock_log.exception.called)
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'
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')
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')
641 self._driver_ssh._execute.assert_called_with(fake_cifs_add_command)
643 def test_cifs_share_add_error(self):
644 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
645 side_effect=[putils.ProcessExecutionError(stderr='')]))
647 self.assertRaises(exception.HNASBackendException,
648 self._driver_ssh.cifs_share_add, 'vvol_test')
649 self.assertTrue(self.mock_log.exception.called)
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')
656 self._driver_ssh.cifs_share_del('vvol_test')
658 self._driver_ssh._execute.assert_called_with(fake_cifs_del_command)
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)))
667 self._driver_ssh.cifs_share_del('vvol_test')
669 self._driver_ssh._execute.assert_called_with(fake_cifs_del_command)
670 self.assertTrue(self.mock_log.warning.called)
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))
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)
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)]))
686 host_list = self._driver_ssh.get_nfs_host_list('fake_id')
688 self.assertEqual(['127.0.0.2'], host_list)
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")
695 self._driver_ssh.update_nfs_access_rule([], snapshot_id="fake_id")
697 self._driver_ssh._execute.assert_called_with(fake_export_command)
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")
704 self._driver_ssh.update_nfs_access_rule(['127.0.0.1', '127.0.0.2'],
705 share_id="fake_id")
707 self._driver_ssh._execute.assert_called_with(fake_export_command)
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'])
714 def test_update_nfs_access_rule_exception_error(self):
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))
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")
725 self._driver_ssh._execute.assert_called_with(fake_export_command)
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')
733 self._driver_ssh.cifs_allow_access('vvol_test', 'fake_user', 'ar')
735 self._driver_ssh._execute.assert_called_with(fake_cifs_allow_command)
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']
747 self.mock_object(ssh.HNASSSHBackend, '_execute',
748 mock.Mock(side_effect=[putils.ProcessExecutionError(
749 stderr='already listed as a user'),
750 "Rule modified."]))
752 self._driver_ssh.cifs_allow_access('vvol_test', 'fake_user', 'acr',
753 is_snapshot=is_snapshot)
755 _execute_calls = [mock.call(fake_cifs_allow_command)]
756 if not is_snapshot:
757 _execute_calls.append(mock.call(fake_cifs_allow_command2))
759 self._driver_ssh._execute.assert_has_calls(_execute_calls)
760 self.assertTrue(self.mock_log.debug.called)
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\'')]))
772 self.assertRaises(exception.HNASBackendException,
773 self._driver_ssh.cifs_allow_access, 'vvol_test',
774 'fake_user', 'acr', is_snapshot=is_snapshot)
776 self._driver_ssh._execute.assert_called_with(fake_cifs_allow_command)
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']
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.')]))
792 self.assertRaises(exception.HNASBackendException,
793 self._driver_ssh.cifs_allow_access, 'vvol_test',
794 'fake_user', 'acr')
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)
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')
806 self._driver_ssh.cifs_deny_access('vvol_test', 'fake_user')
808 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command)
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')]))
818 self._driver_ssh.cifs_deny_access('vvol_test', 'fake_user',
819 is_snapshot=is_snapshot)
821 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command)
822 self.assertTrue(self.mock_log.warning.called)
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')]))
831 self.assertRaises(exception.HNASBackendException,
832 self._driver_ssh.cifs_deny_access, 'vvol_test',
833 'fake_user')
835 self._driver_ssh._execute.assert_called_with(fake_cifs_deny_command)
837 def test_list_cifs_permission(self):
838 fake_cifs_list_command = ['cifs-saa', 'list', '--target-label',
839 self.fs_name, 'vvol_test']
841 expected_out = ssh.CIFSPermissions(HNAS_RESULT_list_cifs_permissions)
843 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
844 return_value=(HNAS_RESULT_list_cifs_permissions, '')))
846 out = self._driver_ssh.list_cifs_permissions('vvol_test')
848 for i in range(len(expected_out.permission_list)):
849 self.assertEqual(expected_out.permission_list[i], out[i])
851 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command)
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']
857 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
858 side_effect=[putils.ProcessExecutionError(
859 stderr='No entries for this share')]))
861 out = self._driver_ssh.list_cifs_permissions('vvol_test')
863 self.assertEqual([], out)
864 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command)
865 self.assertTrue(self.mock_log.debug.called)
867 def test_list_cifs_exception(self):
868 fake_cifs_list_command = ['cifs-saa', 'list', '--target-label',
869 self.fs_name, 'vvol_test']
871 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
872 side_effect=[putils.ProcessExecutionError(
873 stderr='Error.')]))
875 self.assertRaises(exception.HNASBackendException,
876 self._driver_ssh.list_cifs_permissions,
877 "vvol_test")
879 self._driver_ssh._execute.assert_called_with(fake_cifs_list_command)
880 self.assertTrue(self.mock_log.exception.called)
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 )]))
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)
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='')]))
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)
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, '')]))
912 self._driver_ssh.tree_clone("/src", "/dst")
914 self._driver_ssh._execute.assert_any_call(fake_tree_clone_command)
915 self.assertTrue(self.mock_log.debug.called)
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, '')]))
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)
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")
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)
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 ))
954 self._driver_ssh.tree_delete("/path")
956 self.assertTrue(self.mock_log.warning.called)
957 self._driver_ssh._execute.assert_called_with(fake_tree_delete_command)
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 ))
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)
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))
978 self._driver_ssh.create_directory("/path")
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)
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))
992 self.assertRaises(exception.HNASSSCContextChange,
993 self._driver_ssh.create_directory, "/path")
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)
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]))
1007 self._driver_ssh.create_directory("/path")
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)
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))
1020 self._driver_ssh.delete_directory("/path")
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)
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")
1033 self._driver_ssh.delete_directory("/path")
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)
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))
1047 self.assertRaises(exception.HNASSSCContextChange,
1048 self._driver_ssh.delete_directory, "/path")
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)
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]))
1062 self._driver_ssh.delete_directory("/path")
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)
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]
1074 self.mock_object(ssh.HNASSSHBackend, '_execute')
1076 out = self._driver_ssh.check_directory(path)
1078 self.assertTrue(out)
1079 self._driver_ssh._execute.assert_called_with(check_snap_args)
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'])
1088 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
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']))
1096 out = self._driver_ssh.check_directory(path)
1098 self.assertIs(True, out)
1099 self._driver_ssh._execute.assert_called_with(check_snap_args)
1101 def test_check_inexistent_snapshot(self):
1102 path = "/path/snap1/snapshot07-08-2016"
1104 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
1106 self.mock_object(ssh.HNASSSHBackend, '_execute',
1107 mock.Mock(side_effect=putils.ProcessExecutionError(
1108 stdout=HNAS_RESULT_check_snap_error)))
1110 out = self._driver_ssh.check_directory(path)
1112 self.assertFalse(out)
1113 self._driver_ssh._execute.assert_called_with(check_snap_args)
1115 def test_check_directory_error(self):
1116 path = "/path/snap1/snapshot07-08-2016"
1118 check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
1120 self.mock_object(ssh.HNASSSHBackend, '_execute',
1121 mock.Mock(side_effect=putils.ProcessExecutionError(
1122 stdout="Internal Server Error.")))
1124 self.assertRaises(exception.HNASBackendException,
1125 self._driver_ssh.check_directory, path)
1127 self._driver_ssh._execute.assert_called_with(check_snap_args)
1129 def test_check_fs_mounted_true(self):
1130 self.mock_object(ssh.HNASSSHBackend, "_execute",
1131 mock.Mock(return_value=(HNAS_RESULT_df, '')))
1133 self.assertTrue(self._driver_ssh.check_fs_mounted())
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, '')))
1140 self.assertFalse(self._driver_ssh.check_fs_mounted())
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, '')))
1147 self.assertRaises(exception.HNASItemNotFoundException,
1148 self._driver_ssh.check_fs_mounted)
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='')))
1155 self.assertRaises(
1156 exception.HNASBackendException, self._driver_ssh.mount)
1158 self._driver_ssh._execute.assert_called_with(fake_mount_command)
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")
1165 self._driver_ssh.vvol_create("vvol")
1167 self._driver_ssh._execute.assert_called_with(fake_vvol_create_command)
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))
1175 self.assertRaises(exception.HNASBackendException,
1176 self._driver_ssh.vvol_create, "vvol")
1178 self._driver_ssh._execute.assert_called_with(fake_vvol_create_command)
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 ))
1188 self._driver_ssh.vvol_delete("vvol")
1190 self.assertTrue(self.mock_log.warning.called)
1191 self._driver_ssh._execute.assert_called_with(fake_vvol_delete_command)
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 ))
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)
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")
1212 self._driver_ssh.quota_add('vvol', 1)
1214 self._driver_ssh._execute.assert_called_with(fake_add_quota_command)
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")
1221 self._driver_ssh.modify_quota('vvol', 1)
1223 self._driver_ssh._execute.assert_called_with(fake_modify_quota_command)
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))
1232 self.assertRaises(exception.HNASBackendException,
1233 self._driver_ssh.quota_add, 'vvol', 1)
1235 self._driver_ssh._execute.assert_called_with(fake_add_quota_command)
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))
1243 self.assertRaises(exception.HNASBackendException,
1244 self._driver_ssh.modify_quota, 'vvol', 1)
1246 self._driver_ssh._execute.assert_called_with(fake_modify_quota_command)
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='')))
1254 self.assertRaises(exception.HNASItemNotFoundException,
1255 self._driver_ssh.check_vvol, 'vvol')
1256 self._driver_ssh._execute.assert_called_with(fake_check_vvol_command)
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', '')))
1264 self.assertRaises(exception.HNASItemNotFoundException,
1265 self._driver_ssh.check_quota, 'vvol')
1266 self._driver_ssh._execute.assert_called_with(fake_check_quota_command)
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))
1274 self.assertRaises(exception.HNASBackendException,
1275 self._driver_ssh.check_quota, 'vvol')
1276 self._driver_ssh._execute.assert_called_with(fake_check_quota_command)
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)]))
1283 self._driver_ssh.check_export("vvol_test", is_snapshot)
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)]))
1289 self.assertRaises(exception.HNASItemNotFoundException,
1290 self._driver_ssh.check_export, "vvol_test")
1292 def test_check_cifs(self):
1293 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test']
1295 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1296 return_value=[HNAS_RESULT_cifs_list, '']))
1298 self._driver_ssh.check_cifs('vvol_test')
1300 self._driver_ssh._execute.assert_called_with(check_cifs_share_command)
1302 def test_check_cifs_inexistent_share(self):
1303 check_cifs_share_command = ['cifs-share', 'list', 'wrong_vvol']
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.')]))
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)
1314 def test_check_cifs_exception(self):
1315 check_cifs_share_command = ['cifs-share', 'list', 'wrong_vvol']
1317 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1318 side_effect=[putils.ProcessExecutionError(stderr='Error.')]))
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)
1324 def test_check_cifs_different_fs_exception(self):
1325 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test']
1327 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1328 return_value=[HNAS_RESULT_different_fs_cifs_list, '']))
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)
1334 def test_is_cifs_in_use(self):
1335 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test']
1337 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1338 return_value=[HNAS_RESULT_cifs_list, '']))
1340 out = self._driver_ssh.is_cifs_in_use('vvol_test')
1342 self.assertTrue(out)
1343 self._driver_ssh._execute.assert_called_with(check_cifs_share_command)
1345 def test_is_cifs_without_use(self):
1346 check_cifs_share_command = ['cifs-share', 'list', 'vvol_test']
1348 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1349 return_value=[HNAS_RESULT_different_fs_cifs_list, '']))
1351 out = self._driver_ssh.is_cifs_in_use('vvol_test')
1353 self.assertFalse(out)
1354 self._driver_ssh._execute.assert_called_with(check_cifs_share_command)
1356 def test_get_share_quota(self):
1357 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1358 return_value=(HNAS_RESULT_quota, '')))
1360 result = self._driver_ssh.get_share_quota("vvol_test")
1362 self.assertEqual(5, result)
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, '')))
1369 result = self._driver_ssh.get_share_quota("vvol_test")
1371 self.assertIsNone(result)
1373 def test_get_share_quota_tb(self):
1374 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1375 return_value=(HNAS_RESULT_quota_tb, '')))
1377 result = self._driver_ssh.get_share_quota("vvol_test")
1379 self.assertEqual(1024, result)
1381 def test_get_share_quota_mb(self):
1382 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1383 return_value=(HNAS_RESULT_quota_mb, '')))
1385 self.assertRaises(exception.HNASBackendException,
1386 self._driver_ssh.get_share_quota, "vvol_test")
1388 def test_get_share_usage(self):
1389 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1390 return_value=(HNAS_RESULT_quota, '')))
1392 self.assertEqual(1, self._driver_ssh.get_share_usage("vvol_test"))
1394 def test_get_share_usage_error(self):
1395 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1396 return_value=(HNAS_RESULT_quota_err, '')))
1398 self.assertRaises(exception.HNASItemNotFoundException,
1399 self._driver_ssh.get_share_usage, "vvol_test")
1401 def test_get_share_usage_mb(self):
1402 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1403 return_value=(HNAS_RESULT_quota_mb, '')))
1405 self.assertEqual(0.01953125, self._driver_ssh.get_share_usage(
1406 "vvol_test"))
1408 def test_get_share_usage_tb(self):
1409 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1410 return_value=(HNAS_RESULT_quota_tb, '')))
1412 self.assertEqual(1024, self._driver_ssh.get_share_usage("vvol_test"))
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, '']))
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'
1425 command = ['nfs-export', 'list ', path]
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])
1434 def test__get_share_export_fs_not_available(self):
1436 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1437 return_value=[HNAS_RESULT_exp_no_fs, '']))
1439 export_list = self._driver_ssh._get_export(name='fake_name')
1440 path = '/shares/fake_name'
1442 command = ['nfs-export', 'list ', path]
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__)
1456 def test__get_share_export_exception_not_found(self):
1458 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1459 side_effect=putils.ProcessExecutionError(
1460 stderr="NFS Export List: Export 'id' does not exist.")))
1462 self.assertRaises(exception.HNASItemNotFoundException,
1463 self._driver_ssh._get_export, 'fake_id')
1465 def test__get_share_export_exception_error(self):
1467 self.mock_object(ssh.HNASSSHBackend, "_execute", mock.Mock(
1468 side_effect=putils.ProcessExecutionError(stderr="Some error.")
1469 ))
1471 self.assertRaises(exception.HNASBackendException,
1472 self._driver_ssh._get_export, 'fake_id')
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, '']))
1483 output, err = self._driver_ssh._execute(commands)
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)
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'
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")
1513 self.assertRaises(putils.ProcessExecutionError,
1514 self._driver_ssh._execute, commands)
1516 putils.ssh_execute.assert_called_with(mock.ANY, concat_command,
1517 check_exit_code=True)
1519 self.assertTrue(self.mock_log.debug.called)
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')
1527 self._driver_ssh._locked_selectfs('create', '/path')
1529 self._driver_ssh._execute.assert_called_with(exec_command)
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")))
1540 self.assertRaises(exception.HNASBackendException,
1541 self._driver_ssh._locked_selectfs, 'create', '/path')
1543 self._driver_ssh._execute.assert_called_with(exec_command)
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")))
1554 self.assertRaises(exception.HNASSSCContextChange,
1555 self._driver_ssh._locked_selectfs, 'create', '/path')
1557 self._driver_ssh._execute.assert_called_with(exec_command)
1558 self.assertTrue(self.mock_log.debug.called)
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')
1566 self._driver_ssh._locked_selectfs('delete', '/path')
1568 self._driver_ssh._execute.assert_called_with(exec_command)
1570 def test__locked_selectfs_deleting_not_empty_directory(self):
1571 msg = 'This path has more snapshot. Currenty DirectoryNotEmpty'
1573 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1574 side_effect=[putils.ProcessExecutionError(stderr=msg)]))
1576 self.assertRaises(exception.HNASDirectoryNotEmpty,
1577 self._driver_ssh._locked_selectfs, 'delete', '/path')
1579 self.assertTrue(self.mock_log.debug.called)
1581 def test__locked_selectfs_delete_exception(self):
1582 msg = "rmdir: cannot remove '/path'"
1584 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1585 side_effect=[putils.ProcessExecutionError(stderr=msg)]))
1587 self.assertRaises(exception.HNASBackendException,
1588 self._driver_ssh._locked_selectfs, 'delete', 'path')
1589 self.assertTrue(self.mock_log.exception.called)
1591 def test__locked_selectfs_delete_not_found(self):
1592 msg = "rmdir: cannot remove '/path': NotFound"
1594 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1595 side_effect=[putils.ProcessExecutionError(stderr=msg)]))
1597 self._driver_ssh._locked_selectfs('delete', 'path')
1599 self.assertTrue(self.mock_log.warning.called)
1601 def test__locked_selectfs_delete_context_change(self):
1602 msg = "Current file system invalid: VolumeNotFound"
1604 self.mock_object(ssh.HNASSSHBackend, '_execute', mock.Mock(
1605 side_effect=[putils.ProcessExecutionError(stderr=msg)]))
1607 self.assertRaises(exception.HNASSSCContextChange,
1608 self._driver_ssh._locked_selectfs, 'delete', 'path')
1610 self.assertTrue(self.mock_log.debug.called)