]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Adds SSH communication to HNAS drivers
authorErlon Cruz <erlon.cruz@fit-tecnologia.org.br>
Thu, 12 Jun 2014 10:55:38 +0000 (07:55 -0300)
committerErlon R. Cruz <erlon.cruz@fit-tecnologia.org.br>
Thu, 5 Mar 2015 18:32:48 +0000 (15:32 -0300)
This commit adds SSH communication to HNAS iSCSI and NFS drivers. The user now
has the option to export the server public keys to HNAS and configure the
authentication via public keys. This avoid exposing HNAS passwords in the plain text
config file and exposing the private HNAS internal network.

Implements: blueprint hds-hnas-ssh-backend
Change-Id: Iafb5d7a4b8607128437b4e606e9ed478c24a43a0

cinder/tests/test_hds_hnas_backend.py
cinder/volume/drivers/hds/hnas_backend.py
cinder/volume/drivers/hds/iscsi.py
cinder/volume/drivers/hds/nfs.py

index 160fe1d3f432c4ef1ac50865fa9ddc8948880fe7..f4c1142fb8f78380ceb8f4432b693aa4c138341d 100644 (file)
 #
 
 import mock
+from oslo_concurrency import processutils
 from oslo_config import cfg
 
 from cinder.openstack.common import log as logging
 from cinder import test
 from cinder import utils
 from cinder.volume.drivers.hds import hnas_backend
-
+from cinder.volume.drivers.hds import nfs
 
 CONF = cfg.CONF
 
@@ -39,7 +40,9 @@ FS ID        FS Label        FS Permanent ID     EVS ID     EVS Label\n\
  1031         cinder2     0xaadfcf7e0769a6bc          1      EVSTest1\n\
  1024      fs02-husvm     0xaac8715e2e9406cd          2      EVSTest2\n\
 \n"
+
 HNAS_RESULT2 = "cluster MAC: 83-68-96-AA-DA-5D"
+
 HNAS_RESULT3 = "\n\
 Model: HNAS 4040                                             \n\
 Software: 11.2.3319.14 (built 2013-09-19 12:34:24+01:00)     \n\
@@ -54,6 +57,7 @@ Serial no    B1339745 (Thu Jan  1 00:00:50 2009)             \n\
 board        MCP                                             \n\
 Serial no    B1339109 (Thu Jan  1 00:00:49 2009)             \n\
 \n"
+
 HNAS_RESULT4 = "\n\
 EVS Type    Label            IP Address          Mask             Port  \n\
 ----------  ---------------  ------------------  ---------------  ------\n\
@@ -63,38 +67,52 @@ evs 1       EVSTest1         172.24.44.20        255.255.255.0    ag1   \n\
 evs 1       EVSTest1         10.0.0.20           255.255.255.0    ag1   \n\
 evs 2       EVSTest2         172.24.44.21        255.255.255.0    ag1   \n\
 \n"
+
 HNAS_RESULT5 = "\n\
- ID         Label  EVS     Size     Used       Snapshots  Deduped    Avail\
-         Thin  ThinSize  ThinAvail                             FS Type \n\
-----  -----------  ---  ------- -------------  ---------  -------- -------\
-------  ----  --------  ---------  ---------------------------------- \n\
-1025   fs01-husvm    1   250 GB 21.4 GB (9%)  0 B (0%)   NA         228 GB\
- (91%)   No                                       32 KB,WFS-2,128 DSBs\n\
-1026         gold    1  19.9 GB 2.30 GB (12%    NA       0 B (0%)   17.6 GB\
- (88%)  No                         4 KB,WFS-2,128 DSBs,dedupe enabled\n\
-1027  large-files    1  19.8 GB 2.43 GB (12%) 0 B (0%)   NA         17.3 GB\
- (88%)  No                                       32 KB,WFS-2,128 DSBs\n\
-1028     platinun    1  19.9 GB 2.30 GB (12%)   NA       0 B (0%)   17.6 GB\
- (88%)  No                         4 KB,WFS-2,128 DSBs,dedupe enabled\n\
-1029       silver    1  19.9 GB 3.19 GB (16%) 0 B (0%)   NA         6.7 GB\
- (84%)   No                                        4 KB,WFS-2,128 DSBs\n\
-1030      cinder1    1  40.8 GB 2.24 GB (5%)  0 B (0%)   NA         38.5 GB\
- (95%)  No                                        4 KB,WFS-2,128 DSBs\n\
-1031      cinder2    1  39.8 GB 2.23 GB (6%)  0 B (0%)   NA         37.6 GB\
- (94%)  No                                        4 KB,WFS-2,128 DSBs\n\
-1024   fs02-husvm    2  49.8 GB 3.54 GB (7%)  0 B (0%)   NA         46.2 GB\
- (93%)  No                                       32 KB,WFS-2,128 DSBs\n\
-1032         test    2  3.97 GB 2.12 GB (53%) 0 B (0%)   NA         1.85 GB\
- (47%)  No                                        4 KB,WFS-2,128 DSBs\n\
+ ID         Label  EVS     Size     Used       Snapshots  Deduped\
+     Avail        Thin  ThinSize  ThinAvail                      \
+            FS Type \n\
+----  -----------  ---  ------- -------------  ---------  -------\
+- -------------  ----  --------  ---------  ---------------------\
+------------- \n\
+1025   fs01-husvm    1   250 GB 21.4 GB (9%)  0 B (0%)   NA      \
+   228 GB (91%)   No                                       32 KB,\
+   WFS-2,128 DSBs\n\
+1026         gold    1  19.9 GB 2.30 GB (12%    NA       0 B (0%)\
+   17.6 GB (88%)  No                         4 KB,WFS-2,128 DSBs,\
+   dedupe enabled\n\
+1027  large-files    1  19.8 GB 2.43 GB (12%) 0 B (0%)   NA      \
+   17.3 GB (88%)  No                                       32 KB,\
+   WFS-2,128 DSBs\n\
+1028     platinun    1  19.9 GB 2.30 GB (12%)   NA       0 B (0%)\
+   17.6 GB (88%)  No                         4 KB,WFS-2,128 DSBs,\
+   dedupe enabled\n\
+1029       silver    1  19.9 GB 3.19 GB (16%) 0 B (0%)   NA      \
+   6.7 GB (84%)   No                                        4 KB,\
+   WFS-2,128 DSBs\n\
+1030      cinder1    1  40.8 GB 2.24 GB (5%)  0 B (0%)   NA      \
+   38.5 GB (95%)  No                                        4 KB,\
+   WFS-2,128 DSBs\n\
+1031      cinder2    1  39.8 GB 2.23 GB (6%)  0 B (0%)   NA      \
+   37.6 GB (94%)  No                                        4 KB,\
+   WFS-2,128 DSBs\n\
+1024   fs02-husvm    2  49.8 GB 3.54 GB (7%)  0 B (0%)   NA      \
+   46.2 GB (93%)  No                                       32 KB,\
+   WFS-2,128 DSBs\n\
+1032         test    2  3.97 GB 2.12 GB (53%) 0 B (0%)   NA      \
+   1.85 GB (47%)  No                                        4 KB,\
+   WFS-2,128 DSBs\n\
 \n"
+
 HNAS_RESULT6 = "\n\
-ID       Label   EVS    Size          Used  Snapshots  Deduped         Avail\
-  Thin  ThinSize  ThinAvail               FS Type\n\
-----  ---------- ---  ------  ------------  ---------  -------  ------------\
-  ----  --------  ---------  --------------------\n\
-1025  fs01-husvm   1  250 GB  21.4 GB (9%)   0 B (0%)       NA  228 GB (91%)\
-    No                       32 KB,WFS-2,128 DSBs\n\
+ID       Label   EVS    Size          Used  Snapshots  Deduped         Avail  \
+Thin  ThinSize  ThinAvail               FS Type\n\
+----  ---------- ---  ------  ------------  ---------  -------  ------------  \
+----  --------  ---------  --------------------\n\
+1025  fs01-husvm   1  250 GB  21.4 GB (9%)   0 B (0%)       NA  228 GB (91%)  \
+  No                       32 KB,WFS-2,128 DSBs\n\
 \n"
+
 HNAS_RESULT7 = "\n\
 Export configuration:                       \n\
 Export name: /export01-husvm                \n\
@@ -114,10 +132,12 @@ Disaster recovery setting:                  \n\
 Recovered = No                              \n\
 Transfer setting = Use file system default  \n\
 \n"
+
 HNAS_RESULT8 = "Logical unit creation started at 2014-12-24 00:38:30+00:00."
 HNAS_RESULT9 = "Logical unit deleted successfully."
 HNAS_RESULT10 = ""
 HNAS_RESULT11 = "Logical unit expansion started at 2014-12-24 01:25:03+00:00."
+
 HNAS_RESULT12 = "\n\
 Alias               : test_iqn                                       \n\
 Globally unique name: iqn.2014-12.10.10.10.10:evstest1.cinder-silver \n\
@@ -126,10 +146,12 @@ Secret              : test_secret                                    \n\
 Authentication      : Enabled                                        \n\
 Logical units       : No logical units.                              \n\
 \n"
+
 HNAS_RESULT13 = "Logical unit added successfully."
 HNAS_RESULT14 = "Logical unit removed successfully."
 HNAS_RESULT15 = "Target created successfully."
 HNAS_RESULT16 = ""
+
 HNAS_RESULT17 = "\n\
 EVS Type    Label            IP Address          Mask             Port  \n\
 ----------  ---------------  ------------------  ---------------  ------\n\
@@ -163,72 +185,129 @@ Logical units       : No logical units.                              \n\
 \n"
 
 HNAS_CMDS = {
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'evsfs',
-     u'list'): ["%s" % HNAS_RESULT1, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
-     'cluster-getmac'): ["%s" % HNAS_RESULT2, ""],
-    ('ssc', '-version',): ["%s" % HNAS_RESULT18, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'ver',):
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'evsfs', 'list'):
+        ["%s" % HNAS_RESULT1, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'cluster-getmac',):
+        ["%s" % HNAS_RESULT2, ""],
+    ('ssh', '-version',): ["%s" % HNAS_RESULT18, ""],
+    ('ssh', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'ver',):
     ["%s" % HNAS_RESULT3, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'evsipaddr',
-     '-l'): ["%s" % HNAS_RESULT4, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'df', '-a'):
-    ["%s" % HNAS_RESULT5, ""],
-    ('df', '-f', 'test_hdp'): ["%s" % HNAS_RESULT6, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'for-each-evs',
-     '-q', 'nfs-export', 'list'): ["%s" % HNAS_RESULT7, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'ver',):
+        ["%s" % HNAS_RESULT3, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'evsipaddr', '-l'):
+        ["%s" % HNAS_RESULT4, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'df', '-a'):
+        ["%s" % HNAS_RESULT5, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'df', '-f', 'test_hdp'):
+        ["%s" % HNAS_RESULT6, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'for-each-evs', '-q',
+     'nfs-export', 'list'):
+        ["%s" % HNAS_RESULT7, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-lu', 'add', '-e', 'test_name',
-     'test_hdp', '/.cinder/test_name.iscsi', '1M'): ["%s" % HNAS_RESULT8, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+     'test_hdp', '/.cinder/test_name.iscsi',
+     '1M'):
+        ["%s" % HNAS_RESULT8, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-lu', 'del', '-d', '-f',
-     'test_lun'): ["%s" % HNAS_RESULT9, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+     'test_lun'):
+        ["%s" % HNAS_RESULT9, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'file-clone-create', '-f', 'fs01-husvm',
-     '/.cinder/test_lu.iscsi', 'cloned_lu'): ["%s" % HNAS_RESULT10, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+     '/.cinder/test_lu.iscsi', 'cloned_lu'):
+        ["%s" % HNAS_RESULT10, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-lu', 'expand', 'expanded_lu',
-     '1M'): ["%s" % HNAS_RESULT11, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
-     'console-context', '--evs', '1', 'iscsi-target', 'list',
-     'test_iqn'): ["%s" % HNAS_RESULT12, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+     '1M'):
+        ["%s" % HNAS_RESULT11, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
+     'console-context', '--evs', '1', 'iscsi-target', 'list', 'test_iqn'):
+        ["%s" % HNAS_RESULT12, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-target', 'addlu', 'test_iqn',
-     'test_lun', 0): ["%s" % HNAS_RESULT13, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
-     'console-context', '--evs', '1', 'iscsi-target', 'dellu', 'test_iqn', 0):
-    ["%s" % HNAS_RESULT14, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
-     'console-context', '--evs', '1', 'iscsi-target', 'add', 'test_iqn',
-     'test_secret'): ["%s" % HNAS_RESULT15, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+     'test_lun', 0):
+        ["%s" % HNAS_RESULT13, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
+     'console-context', '--evs', '1', 'iscsi-target', 'dellu', 'test_iqn',
+     0):
+        ["%s" % HNAS_RESULT14, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
+     'console-context', '--evs', '1', 'iscsi-target', 'add', 'myTarget',
+     'secret'):
+        ["%s" % HNAS_RESULT15, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-target', 'mod', '-s',
      'test_secret', '-a', 'enable', 'test_iqn'): ["%s" % HNAS_RESULT15, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0',
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor',
      'console-context', '--evs', '1', 'iscsi-lu', 'clone', '-e', 'test_lu',
-     'test_clone', '/.cinder/test_clone.iscsi'): ["%s" % HNAS_RESULT16, ""],
-    ('ssc', '-u', 'supervisor', '-p', 'supervisor', '0.0.0.0', 'evsipaddr',
-     '-e', '1'): ["%s" % HNAS_RESULT17, ""]
+     'test_clone',
+     '/.cinder/test_clone.iscsi'):
+        ["%s" % HNAS_RESULT16, ""],
+    ('ssh', '0.0.0.0', 'supervisor', 'supervisor', 'evsipaddr', '-e', '1'):
+        ["%s" % HNAS_RESULT17, ""]
 }
 
+DRV_CONF = {'ssh_enabled': 'True',
+            'mgmt_ip0': '0.0.0.0',
+            'cluster_admin_ip0': None,
+            'ssh_port': '22',
+            'ssh_private_key': 'test_key',
+            'username': 'supervisor',
+            'password': 'supervisor'}
+
+UTILS_EXEC_OUT = ["output: test_cmd", ""]
+
 
-def m_execute(*args, **kargs):
+def m_run_cmd(*args, **kargs):
+    print(args)
+    print(HNAS_CMDS.get(args))
     return HNAS_CMDS.get(args)
 
 
 class HDSHNASBendTest(test.TestCase):
-    """Test HNAS NFS volume driver."""
 
     def __init__(self, *args, **kwargs):
         super(HDSHNASBendTest, self).__init__(*args, **kwargs)
 
-    def setUp(self):
+    @mock.patch.object(nfs, 'factory_bend')
+    def setUp(self, m_factory_bend):
         super(HDSHNASBendTest, self).setUp()
-        self.hnas_bend = hnas_backend.HnasBackend()
+        self.hnas_bend = hnas_backend.HnasBackend(DRV_CONF)
+
+    @mock.patch('__builtin__.open')
+    @mock.patch('os.path.isfile', return_value=True)
+    @mock.patch('paramiko.RSAKey.from_private_key_file')
+    @mock.patch('paramiko.SSHClient')
+    @mock.patch.object(processutils, 'ssh_execute',
+                       return_value=(HNAS_RESULT5, ''))
+    def test_run_cmd(self, m_ssh_exec, m_ssh_cli, m_pvt_key, m_file, m_open):
+        save_hkey_file = CONF.ssh_hosts_key_file
+        save_spath = CONF.state_path
+        CONF.ssh_hosts_key_file = '/var/lib/cinder/ssh_known_hosts'
+        CONF.state_path = '/var/lib/cinder'
+
+        out, err = self.hnas_bend.run_cmd('ssh', '0.0.0.0',
+                                          'supervisor', 'supervisor',
+                                          'df', '-a')
+        self.assertIn('fs01-husvm', out)
+        self.assertIn('WFS-2,128 DSBs', out)
+
+        CONF.state_path = save_spath
+        CONF.ssh_hosts_key_file = save_hkey_file
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_get_iscsi_info(self, m_mock):
-        out = self.hnas_bend.get_iscsi_info("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    @mock.patch.object(utils, 'execute', return_value=UTILS_EXEC_OUT)
+    def test_get_version(self, m_cmd, m_exec):
+        out = self.hnas_bend.get_version("ssh", "1.0", "0.0.0.0", "supervisor",
+                                         "supervisor")
+        self.assertIn('11.2.3319.14', out)
+        self.assertIn('83-68-96-AA-DA-5D', out)
+
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_get_iscsi_info(self, m_execute):
+        out = self.hnas_bend.get_iscsi_info("ssh", "0.0.0.0", "supervisor",
                                             "supervisor")
 
         self.assertIn('172.24.44.20', out)
@@ -236,19 +315,10 @@ class HDSHNASBendTest(test.TestCase):
         self.assertIn('10.0.0.20', out)
         self.assertEqual(len(out.split('\n')), 4)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_get_version(self, m_execute):
-        out = self.hnas_bend.get_version("ssc", "1.0", "0.0.0.0", "supervisor",
-                                         "supervisor")
-
-        self.assertIn('11.2.3319.14', out)
-        self.assertIn('11.1.3225.01', out)
-        self.assertIn('83-68-96-AA-DA-5D', out)
-
-    @mock.patch.object(utils, 'execute')
-    def test_get_hdp_info(self, m_exec):
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd')
+    def test_get_hdp_info(self, m_run_cmd):
         # tests when there is two or more evs
-        m_exec.return_value = (HNAS_RESULT5, "")
+        m_run_cmd.return_value = (HNAS_RESULT5, "")
         out = self.hnas_bend.get_hdp_info("ssh", "0.0.0.0", "supervisor",
                                           "supervisor")
 
@@ -259,7 +329,7 @@ class HDSHNASBendTest(test.TestCase):
         self.assertEqual(len(line1.split()), 12)
 
         # test when there is only one evs
-        m_exec.return_value = (HNAS_RESULT19, "")
+        m_run_cmd.return_value = (HNAS_RESULT19, "")
         out = self.hnas_bend.get_hdp_info("ssh", "0.0.0.0", "supervisor",
                                           "supervisor")
         self.assertEqual(len(out.split('\n')), 3)
@@ -268,9 +338,10 @@ class HDSHNASBendTest(test.TestCase):
         line1 = out.split('\n')[0]
         self.assertEqual(len(line1.split()), 12)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_get_nfs_info(self, m_execute):
-        out = self.hnas_bend.get_nfs_info("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_get_nfs_info(self, m_run_cmd):
+        out = self.hnas_bend.get_nfs_info("ssh", "0.0.0.0", "supervisor",
                                           "supervisor")
 
         self.assertEqual(len(out.split('\n')), 2)
@@ -278,78 +349,89 @@ class HDSHNASBendTest(test.TestCase):
         self.assertIn('172.24.44.20', out)
         self.assertIn('10.0.0.20', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_create_lu(self, m_execute):
-        out = self.hnas_bend.create_lu("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_create_lu(self, m_cmd):
+        out = self.hnas_bend.create_lu("ssh", "0.0.0.0", "supervisor",
                                        "supervisor", "test_hdp", "1",
                                        "test_name")
 
         self.assertIn('successfully created', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_delete_lu(self, m_execute):
-        out = self.hnas_bend.delete_lu("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_delete_lu(self, m_cmd):
+        out = self.hnas_bend.delete_lu("ssh", "0.0.0.0", "supervisor",
                                        "supervisor", "test_hdp", "test_lun")
 
         self.assertIn('deleted successfully', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_create_dup(self, m_execute):
-        out = self.hnas_bend.create_dup("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_create_dup(self, m_cmd):
+
+        out = self.hnas_bend.create_dup("ssh", "0.0.0.0", "supervisor",
                                         "supervisor", "test_lu", "test_hdp",
                                         "1", "test_clone")
 
         self.assertIn('successfully created', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_file_clone(self, m_execute):
-        out = self.hnas_bend.file_clone("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_file_clone(self, m_cmd):
+        out = self.hnas_bend.file_clone("ssh", "0.0.0.0", "supervisor",
                                         "supervisor", "fs01-husvm",
                                         "/.cinder/test_lu.iscsi", "cloned_lu")
 
         self.assertIn('LUN cloned_lu HDP', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_extend_vol(self, m_execute):
-        out = self.hnas_bend.extend_vol("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_extend_vol(self, m_cmd):
+        out = self.hnas_bend.extend_vol("ssh", "0.0.0.0", "supervisor",
                                         "supervisor", "test_hdp", "test_lun",
                                         "1", "expanded_lu")
 
         self.assertIn('successfully extended', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_add_iscsi_conn(self, m_execute):
-        out = self.hnas_bend.add_iscsi_conn("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_add_iscsi_conn(self, m_cmd):
+        out = self.hnas_bend.add_iscsi_conn("ssh", "0.0.0.0", "supervisor",
                                             "supervisor", "test_lun",
                                             "test_hdp", "test_port",
                                             "test_iqn", "test_init")
 
         self.assertIn('successfully paired', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_del_iscsi_conn(self, m_execute):
-        out = self.hnas_bend.del_iscsi_conn("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_del_iscsi_conn(self, m_cmd):
+        out = self.hnas_bend.del_iscsi_conn("ssh", "0.0.0.0", "supervisor",
                                             "supervisor", "1", "test_iqn", 0)
 
         self.assertIn('already deleted', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
-    def test_get_targetiqn(self, m_execute):
-        out = self.hnas_bend.get_targetiqn("ssc", "0.0.0.0", "supervisor",
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
+    def test_get_targetiqn(self, m_cmd):
+        out = self.hnas_bend.get_targetiqn("ssh", "0.0.0.0", "supervisor",
                                            "supervisor", "test_iqn",
                                            "test_hdp", "test_secret")
+
         self.assertEqual('test_iqn', out)
 
-    @mock.patch.object(utils, 'execute', side_effect=m_execute)
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd',
+                       side_effect=m_run_cmd)
     def test_set_targetsecret(self, m_execute):
-        self.hnas_bend.set_targetsecret("ssc", "0.0.0.0", "supervisor",
+        self.hnas_bend.set_targetsecret("ssh", "0.0.0.0", "supervisor",
                                         "supervisor", "test_iqn",
                                         "test_hdp", "test_secret")
 
-    @mock.patch.object(utils, 'execute')
-    def test_get_targetsecret(self, m_exec):
+    @mock.patch.object(hnas_backend.HnasBackend, 'run_cmd')
+    def test_get_targetsecret(self, m_run_cmd):
         # test when target has secret
-        m_exec.return_value = (HNAS_RESULT12, "")
+        m_run_cmd.return_value = (HNAS_RESULT12, "")
         out = self.hnas_bend.get_targetsecret("ssh", "0.0.0.0", "supervisor",
                                               "supervisor", "test_iqn",
                                               "test_hdp")
@@ -357,7 +439,7 @@ class HDSHNASBendTest(test.TestCase):
         self.assertEqual('test_secret', out)
 
         # test when target don't have secret
-        m_exec.return_value = (HNAS_RESULT20, "")
+        m_run_cmd.return_value = (HNAS_RESULT20, "")
         out = self.hnas_bend.get_targetsecret("ssh", "0.0.0.0", "supervisor",
                                               "supervisor", "test_iqn",
                                               "test_hdp")
index 7ac2fb8ec14923bf41cbfd4bb5dfa1b0e1e0d55f..c0751ada46f986b3aed760ae4ddd46c17f8678f4 100644 (file)
@@ -20,16 +20,80 @@ Hitachi Unified Storage (HUS-HNAS) platform. Backend operations.
 
 import re
 
+from oslo_concurrency import processutils
 from oslo_utils import units
+import six
 
+from cinder.i18n import _LE, _LW, _LI
 from cinder.openstack.common import log as logging
+from cinder import ssh_utils
 from cinder import utils
 
 LOG = logging.getLogger("cinder.volume.driver")
 
 
-class HnasBackend():
+class HnasBackend(object):
     """Back end. Talks to HUS-HNAS."""
+    def __init__(self, drv_configs):
+        self.drv_configs = drv_configs
+        self.sshpool = None
+
+    def run_cmd(self, cmd, ip0, user, pw, *args, **kwargs):
+        """Run a command on SMU or using SSH
+
+        :param cmd: the command that will be run on SMU
+        :param ip0: string IP address of controller
+        :param user: string user authentication for array
+        :param pw: string password authentication for array
+        :returns: formated string with version information
+        """
+        LOG.debug('Enable ssh: %s',
+                  six.text_type(self.drv_configs['ssh_enabled']))
+
+        if self.drv_configs['ssh_enabled'] != 'True':
+            # Direct connection via ssc
+            args = (cmd, '-u', user, '-p', pw, ip0) + args
+            out, err = utils.execute(*args, **kwargs)
+            LOG.debug("command %(cmd)s result: out = %(out)s - err = "
+                      "%(err)s", {'cmd': cmd, 'out': out, 'err': err})
+            return out, err
+        else:
+            if self.drv_configs['cluster_admin_ip0'] is None:
+                # Connect to SMU through SSH and run ssc locally
+                args = (cmd, 'localhost') + args
+            else:
+                args = (cmd, '--smuauth',
+                        self.drv_configs['cluster_admin_ip0']) + args
+
+            utils.check_ssh_injection(args)
+            command = ' '.join(args)
+            command = command.replace('"', '\\"')
+
+            if not self.sshpool:
+                server = self.drv_configs['mgmt_ip0']
+                port = int(self.drv_configs['ssh_port'])
+                username = self.drv_configs['username']
+                # We only accept private/public key auth
+                password = ""
+                privatekey = self.drv_configs['ssh_private_key']
+                self.sshpool = ssh_utils.SSHPool(server,
+                                                 port,
+                                                 None,
+                                                 username,
+                                                 password=password,
+                                                 privatekey=privatekey)
+
+            with self.sshpool.item() as ssh:
+                try:
+                    out, err = processutils.ssh_execute(ssh, command,
+                                                        check_exit_code=True)
+                    LOG.debug("command %(cmd)s result: out = %(out)s - err = "
+                              "%(err)s", {'cmd': cmd, 'out': out, 'err': err})
+                    return out, err
+                except processutils.ProcessExecutionError:
+                    LOG.error(_LE("Error running SSH command."))
+                    raise
+
     def get_version(self, cmd, ver, ip0, user, pw):
         """Gets version information from the storage unit
 
@@ -39,19 +103,20 @@ class HnasBackend():
        :param pw: string password authentication for array
        :returns: formated string with version information
        """
-        out, err = utils.execute(cmd,
-                                 "-version",
-                                 check_exit_code=True)
-        util = out.split()[1]
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw, ip0,
-                                 "cluster-getmac",
-                                 check_exit_code=True)
+        if (self.drv_configs['ssh_enabled'] == 'True' and
+                self.drv_configs['cluster_admin_ip0'] is not None):
+                util = 'SMU ' + cmd
+        else:
+            out, err = utils.execute(cmd,
+                                     "-version",
+                                     check_exit_code=True)
+            util = out.split()[1]
+
+        out, err = self.run_cmd(cmd, ip0, user, pw, "cluster-getmac",
+                                check_exit_code=True)
         hardware = out.split()[2]
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, 'ver',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "ver",
+                                check_exit_code=True)
         lines = out.split('\n')
 
         model = ""
@@ -64,7 +129,7 @@ class HnasBackend():
         out = "Array_ID: %s (%s) version: %s LU: 256 RG: 0 RG_LU: 0 \
                Utility_version: %s" % (hardware, model, ver, util)
 
-        LOG.debug('get_version: ' + out + ' -- ' + err)
+        LOG.debug('get_version: %(out)s -- %(err)s', {'out': out, 'err': err})
         return out
 
     def get_iscsi_info(self, cmd, ip0, user, pw):
@@ -76,10 +141,9 @@ class HnasBackend():
         :returns: formated string with iSCSI information
         """
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw, ip0,
-                                 'evsipaddr', '-l',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw,
+                                'evsipaddr', '-l',
+                                check_exit_code=True)
         lines = out.split('\n')
 
         newout = ""
@@ -90,7 +154,8 @@ class HnasBackend():
                 newout += "CTL: %s Port: 0 IP: %s Port: 3260 Link: Up\n" \
                           % (evsnum, ip)
 
-        LOG.debug('get_iscsi_info: ' + out + ' -- ' + err)
+        LOG.debug('get_iscsi_info: %(out)s -- %(err)s',
+                  {'out': out, 'err': err})
         return newout
 
     def get_hdp_info(self, cmd, ip0, user, pw):
@@ -102,10 +167,8 @@ class HnasBackend():
         :returns: formated string with filesystems and fsids
         """
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, 'df', '-a',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, 'df', '-a',
+                                check_exit_code=True)
         lines = out.split('\n')
         single_evs = True
 
@@ -145,17 +208,16 @@ class HnasBackend():
                              int(float(used) * usedmultiplier),
                              int(percent), fslabel)
 
-        LOG.debug('get_hdp_info: ' + newout + ' -- ' + err)
+        LOG.debug('get_hdp_info: %(out)s -- %(err)s',
+                  {'out': newout, 'err': err})
         return newout
 
     def _get_evs(self, cmd, ip0, user, pw, fsid):
         """Gets the EVSID for the named filesystem."""
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw, ip0,
-                                 "evsfs", "list",
-                                 check_exit_code=True)
-        LOG.debug('get_evs: out ' + out)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "evsfs", "list",
+                                check_exit_code=True)
+        LOG.debug('get_evs: out %s', out)
 
         lines = out.split('\n')
         for line in lines:
@@ -163,16 +225,16 @@ class HnasBackend():
             if fsid in line and (fsid == inf[0] or fsid == inf[1]):
                 return inf[3]
 
-        LOG.warn('get_evs: ' + out + ' -- ' + 'No info for ' + fsid)
+        LOG.warning(_LW('get_evs: %(out)s -- No find for %(fsid)s'),
+                    {'out': out, 'fsid': fsid})
         return 0
 
     def _get_evsips(self, cmd, ip0, user, pw, evsid):
         """Gets the EVS IPs for the named filesystem."""
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw, ip0,
-                                 'evsipaddr', '-e', evsid,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw,
+                                'evsipaddr', '-e', evsid,
+                                check_exit_code=True)
 
         iplist = ""
         lines = out.split('\n')
@@ -181,26 +243,25 @@ class HnasBackend():
             if 'evs' in line:
                 iplist += inf[3] + ' '
 
-        LOG.debug('get_evsips: ' + iplist)
+        LOG.debug('get_evsips: %s', iplist)
         return iplist
 
     def _get_fsid(self, cmd, ip0, user, pw, fslabel):
         """Gets the FSID for the named filesystem."""
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, 'evsfs', 'list',
-                                 check_exit_code=True)
-        LOG.debug('get_fsid: out ' + out)
+        out, err = self.run_cmd(cmd, ip0, user, pw, 'evsfs', 'list',
+                                check_exit_code=True)
+        LOG.debug('get_fsid: out %s', out)
 
         lines = out.split('\n')
         for line in lines:
             inf = line.split()
             if fslabel in line and fslabel == inf[1]:
-                LOG.debug('get_fsid: ' + line)
+                LOG.debug('get_fsid: %s', line)
                 return inf[0]
 
-        LOG.warn('get_fsid: ' + out + ' -- ' + 'No infor for ' + fslabel)
+        LOG.warning(_LW('get_fsid: %(out)s -- No info for %(fslabel)s'),
+                    {'out': out, 'fslabel': fslabel})
         return 0
 
     def get_nfs_info(self, cmd, ip0, user, pw):
@@ -212,11 +273,10 @@ class HnasBackend():
         :returns: formated string
         """
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw, ip0,
-                                 'for-each-evs', '-q',
-                                 'nfs-export', 'list',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw,
+                                'for-each-evs', '-q',
+                                'nfs-export', 'list',
+                                check_exit_code=True)
 
         lines = out.split('\n')
         newout = ""
@@ -241,7 +301,8 @@ class HnasBackend():
                            % (export, path, fs, fsid, evsid, ips)
                 fs = ""
 
-        LOG.debug('get_nfs_info: ' + newout + ' -- ' + err)
+        LOG.debug('get_nfs_info: %(out)s -- %(err)s',
+                  {'out': newout, 'err': err})
         return newout
 
     def create_lu(self, cmd, ip0, user, pw, hdp, size, name):
@@ -261,20 +322,18 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-lu', 'add', "-e",
-                                 name, hdp,
-                                 '/.cinder/' + name + '.iscsi',
-                                 size + 'M',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-lu', 'add', "-e",
+                                name, hdp,
+                                '/.cinder/' + name + '.iscsi',
+                                size + 'M',
+                                check_exit_code=True)
 
         out = "LUN %s HDP: %s size: %s MB, is successfully created" \
               % (name, hdp, size)
 
-        LOG.debug('create_lu: ' + out)
+        LOG.debug('create_lu: %s', out)
         return out
 
     def delete_lu(self, cmd, ip0, user, pw, hdp, lun):
@@ -289,15 +348,13 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-lu', 'del', '-d',
-                                 '-f', lun,
-                                 check_exit_code=True)
-
-        LOG.debug('delete_lu: ' + out + ' -- ' + err)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-lu', 'del', '-d',
+                                '-f', lun,
+                                check_exit_code=True)
+
+        LOG.debug('delete_lu: %(out)s -- %(err)s', {'out': out, 'err': err})
         return out
 
     def create_dup(self, cmd, ip0, user, pw, src_lun, hdp, size, name):
@@ -316,19 +373,17 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-lu', 'clone', '-e',
-                                 src_lun, name,
-                                 '/.cinder/' + name + '.iscsi',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-lu', 'clone', '-e',
+                                src_lun, name,
+                                '/.cinder/' + name + '.iscsi',
+                                check_exit_code=True)
 
         out = "LUN %s HDP: %s size: %s MB, is successfully created" \
               % (name, hdp, size)
 
-        LOG.debug('create_dup: ' + out + ' -- ' + err)
+        LOG.debug('create_dup: %(out)s -- %(err)s', {'out': out, 'err': err})
         return out
 
     def file_clone(self, cmd, ip0, user, pw, fslabel, src, name):
@@ -347,17 +402,15 @@ class HnasBackend():
 
         _fsid = self._get_fsid(cmd, ip0, user, pw, fslabel)
         _evsid = self._get_evs(cmd, ip0, user, pw, _fsid)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'file-clone-create', '-f', fslabel,
-                                 src, name,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'file-clone-create', '-f', fslabel,
+                                src, name,
+                                check_exit_code=True)
 
         out = "LUN %s HDP: %s Clone: %s -> %s" % (name, _fsid, src, name)
 
-        LOG.debug('file_clone: ' + out + ' -- ' + err)
+        LOG.debug('file_clone: %(out)s -- %(err)s', {'out': out, 'err': err})
         return out
 
     def extend_vol(self, cmd, ip0, user, pw, hdp, lun, new_size, name):
@@ -373,17 +426,15 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-lu', 'expand',
-                                 name, new_size + 'M',
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-lu', 'expand',
+                                name, new_size + 'M',
+                                check_exit_code=True)
 
         out = ("LUN: %s successfully extended to %s MB" % (name, new_size))
 
-        LOG.debug('extend_vol: ' + out)
+        LOG.debug('extend_vol: %s', out)
         return out
 
     def add_iscsi_conn(self, cmd, ip0, user, pw, lun, hdp,
@@ -401,12 +452,10 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-target', 'list', iqn,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-target', 'list', iqn,
+                                check_exit_code=True)
 
         # even though ssc uses the target alias, need to return the full iqn
         fulliqn = ""
@@ -424,14 +473,15 @@ class HnasBackend():
                 if lunline[0].isdigit():
                     # see if already mounted
                     if vol[:29] == lun[:29]:
-                        LOG.info('lun: %s already mounted %s' % (lun, lunline))
+                        LOG.info(_LI('lun: %(lun)s already mounted (lline)%s'),
+                                 {'lun': lun, 'lline': lunline})
                         conn = (int(lunline), lun, initiator, hlun, fulliqn,
                                 hlun, hdp, port)
                         out = "H-LUN: %d alreadymapped LUN: %s, iSCSI \
                                Initiator: %s @ index: %d, and Target: %s \
                                @ index %d is successfully paired  @ CTL: \
                                %s, Port: %s" % conn
-                        LOG.debug('add_iscsi_conn: returns ' + out)
+                        LOG.debug('add_iscsi_conn: returns %s', out)
                         return out
 
                     if int(lunline) == hlun:
@@ -440,13 +490,11 @@ class HnasBackend():
                         # found a hole
                         break
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-target', 'addlu',
-                                 iqn, lun, hlun,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-target', 'addlu',
+                                iqn, lun, hlun,
+                                check_exit_code=True)
 
         conn = (int(hlun), lun, initiator, int(hlun), fulliqn, int(hlun),
                 hdp, port)
@@ -454,7 +502,7 @@ class HnasBackend():
                @ index: %d, and Target: %s @ index %d is \
                successfully paired  @ CTL: %s, Port: %s" % conn
 
-        LOG.debug('add_iscsi_conn: returns ' + out)
+        LOG.debug('add_iscsi_conn: returns %s', out)
         return out
 
     def del_iscsi_conn(self, cmd, ip0, user, pw, evsid, iqn, hlun):
@@ -469,12 +517,10 @@ class HnasBackend():
         :return: formated string
         """
 
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", evsid,
-                                 'iscsi-target', 'list', iqn,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", evsid,
+                                'iscsi-target', 'list', iqn,
+                                check_exit_code=True)
 
         lines = out.split('\n')
         out = ("H-LUN: %d already deleted from target %s" % (int(hlun), iqn))
@@ -488,22 +534,20 @@ class HnasBackend():
 
         if out != "":
             # hlun wasn't found
-            LOG.info('del_iscsi_conn: hlun not found' + out)
+            LOG.info(_LI('del_iscsi_conn: hlun not found %s'), out)
             return out
 
         # remove the LU from the target
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", evsid,
-                                 'iscsi-target', 'dellu',
-                                 '-f', iqn, hlun,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", evsid,
+                                'iscsi-target', 'dellu',
+                                '-f', iqn, hlun,
+                                check_exit_code=True)
 
         out = "H-LUN: %d successfully deleted from target %s" \
               % (int(hlun), iqn)
 
-        LOG.debug('del_iscsi_conn: ' + out + ' -- ')
+        LOG.debug('del_iscsi_conn: %s', out)
         return out
 
     def get_targetiqn(self, cmd, ip0, user, pw, targetalias, hdp, secret):
@@ -521,31 +565,25 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-target', 'list', targetalias,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-target', 'list', targetalias,
+                                check_exit_code=True)
 
         if "does not exist" in out:
             if secret == "":
                 secret = '""'
-                out, err = utils.execute(cmd,
-                                         '-u', user, '-p', pw,
-                                         ip0, "console-context",
-                                         "--evs", _evsid,
-                                         'iscsi-target', 'add',
-                                         targetalias, secret,
-                                         check_exit_code=True)
+                out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                        "--evs", _evsid,
+                                        'iscsi-target', 'add',
+                                        targetalias, secret,
+                                        check_exit_code=True)
             else:
-                out, err = utils.execute(cmd,
-                                         '-u', user, '-p', pw,
-                                         ip0, "console-context",
-                                         "--evs", _evsid,
-                                         'iscsi-target', 'add',
-                                         targetalias, secret,
-                                         check_exit_code=True)
+                out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                        "--evs", _evsid,
+                                        'iscsi-target', 'add',
+                                        targetalias, secret,
+                                        check_exit_code=True)
 
         lines = out.split('\n')
         # returns the first iqn
@@ -565,32 +603,26 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-target', 'list',
-                                 targetalias,
-                                 check_exit_code=False)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-target', 'list',
+                                targetalias,
+                                check_exit_code=False)
 
         if "does not exist" in out:
-            out, err = utils.execute(cmd,
-                                     '-u', user, '-p', pw,
-                                     ip0, "console-context",
-                                     "--evs", _evsid,
-                                     'iscsi-target', 'add',
-                                     targetalias, secret,
-                                     check_exit_code=True)
+            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                    "--evs", _evsid,
+                                    'iscsi-target', 'add',
+                                    targetalias, secret,
+                                    check_exit_code=True)
         else:
-            LOG.info('targetlist: ' + targetalias + ' -- ' + out)
-            out, err = utils.execute(cmd,
-                                     '-u', user, '-p', pw,
-                                     ip0, "console-context",
-                                     "--evs", _evsid,
-                                     'iscsi-target', 'mod',
-                                     '-s', secret, '-a', 'enable',
-                                     targetalias,
-                                     check_exit_code=True)
+            LOG.info(_LI('targetlist: %s'), targetalias)
+            out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                    "--evs", _evsid,
+                                    'iscsi-target', 'mod',
+                                    '-s', secret, '-a', 'enable',
+                                    targetalias,
+                                    check_exit_code=True)
 
     def get_targetsecret(self, cmd, ip0, user, pw, targetalias, hdp):
         """Returns the chap secret for the specified target.
@@ -603,12 +635,10 @@ class HnasBackend():
         """
 
         _evsid = self._get_evs(cmd, ip0, user, pw, hdp)
-        out, err = utils.execute(cmd,
-                                 '-u', user, '-p', pw,
-                                 ip0, "console-context",
-                                 "--evs", _evsid,
-                                 'iscsi-target', 'list', targetalias,
-                                 check_exit_code=True)
+        out, err = self.run_cmd(cmd, ip0, user, pw, "console-context",
+                                "--evs", _evsid,
+                                'iscsi-target', 'list', targetalias,
+                                check_exit_code=True)
 
         enabled = ""
         secret = ""
index 9ddebc5a2caa832069bc447c095ee9fe1ca65e9f..80d52828950b7491d364e0709b088b8b4335be50 100644 (file)
@@ -32,7 +32,7 @@ from cinder.volume.drivers.hds import hnas_backend
 from cinder.volume import utils
 
 
-HDS_HNAS_ISCSI_VERSION = '1.0.0'
+HDS_HNAS_ISCSI_VERSION = '2.2.0'
 
 LOG = logging.getLogger(__name__)
 
@@ -44,11 +44,13 @@ iSCSI_OPTS = [
 CONF = cfg.CONF
 CONF.register_opts(iSCSI_OPTS)
 
-HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc', 'chap_enabled': 'True'}
+HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc',
+                       'chap_enabled': 'True',
+                       'ssh_port': '22'}
 
 
-def factory_bend(type):
-    return hnas_backend.HnasBackend()
+def factory_bend(drv_configs):
+    return hnas_backend.HnasBackend(drv_configs)
 
 
 def _loc_info(loc):
@@ -100,14 +102,31 @@ def _read_config(xml_config_file):
 
     # mandatory parameters
     config = {}
-    arg_prereqs = ['mgmt_ip0', 'username', 'password']
+    arg_prereqs = ['mgmt_ip0', 'username']
     for req in arg_prereqs:
         config[req] = _xml_read(root, req, 'check')
 
     # optional parameters
-    for opt in ['hnas_cmd', 'chap_enabled']:
-        config[opt] = _xml_read(root, opt) or\
-            HNAS_DEFAULT_CONFIG[opt]
+    opt_parameters = ['hnas_cmd', 'ssh_enabled', 'chap_enabled',
+                      'cluster_admin_ip0']
+    for req in opt_parameters:
+        config[req] = _xml_read(root, req)
+
+    if config['chap_enabled'] is None:
+        config['chap_enabled'] = HNAS_DEFAULT_CONFIG['chap_enabled']
+
+    if config['ssh_enabled'] == 'True':
+        config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', 'check')
+        config['ssh_port'] = _xml_read(root, 'ssh_port')
+        config['password'] = _xml_read(root, 'password')
+        if config['ssh_port'] is None:
+            config['ssh_port'] = HNAS_DEFAULT_CONFIG['ssh_port']
+    else:
+        # password is mandatory when not using SSH
+        config['password'] = _xml_read(root, 'password', 'check')
+
+    if config['hnas_cmd'] is None:
+        config['hnas_cmd'] = HNAS_DEFAULT_CONFIG['hnas_cmd']
 
     config['hdp'] = {}
     config['services'] = {}
@@ -132,7 +151,11 @@ def _read_config(xml_config_file):
 
 
 class HDSISCSIDriver(driver.ISCSIDriver):
-    """HDS HNAS volume driver."""
+    """HDS HNAS volume driver.
+
+    Version 1.0.0: Initial driver version
+    Version 2.2.0:  Added support to SSH authentication
+    """
 
     def __init__(self, *args, **kwargs):
         """Initialize, read different config parameters."""
@@ -147,7 +170,7 @@ class HDSISCSIDriver(driver.ISCSIDriver):
 
         self.platform = self.type.lower()
         LOG.info(_LI("Backend type: %s"), self.type)
-        self.bend = factory_bend(self.type)
+        self.bend = factory_bend(self.config)
 
     def _array_info_get(self):
         """Get array parameters."""
index 36a19cc85f9997bf4874c309c2f2ac8bed3959d8..55e7aeb88cd25f10e597f7fb692c95654d646320 100644 (file)
@@ -34,7 +34,7 @@ from cinder.volume.drivers.hds import hnas_backend
 from cinder.volume.drivers import nfs
 
 
-HDS_HNAS_NFS_VERSION = '1.0.0'
+HDS_HNAS_NFS_VERSION = '2.2.0'
 
 LOG = logging.getLogger(__name__)
 
@@ -46,7 +46,7 @@ NFS_OPTS = [
 CONF = cfg.CONF
 CONF.register_opts(NFS_OPTS)
 
-HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc'}
+HNAS_DEFAULT_CONFIG = {'hnas_cmd': 'ssc', 'ssh_port': '22'}
 
 
 def _xml_read(root, element, check=None):
@@ -92,13 +92,27 @@ def _read_config(xml_config_file):
 
     # mandatory parameters
     config = {}
-    arg_prereqs = ['mgmt_ip0', 'username', 'password']
+    arg_prereqs = ['mgmt_ip0', 'username']
     for req in arg_prereqs:
         config[req] = _xml_read(root, req, 'check')
 
     # optional parameters
-    config['hnas_cmd'] = _xml_read(root, 'hnas_cmd') or\
-        HNAS_DEFAULT_CONFIG['hnas_cmd']
+    opt_parameters = ['hnas_cmd', 'ssh_enabled', 'cluster_admin_ip0']
+    for req in opt_parameters:
+        config[req] = _xml_read(root, req)
+
+    if config['ssh_enabled'] == 'True':
+        config['ssh_private_key'] = _xml_read(root, 'ssh_private_key', 'check')
+        config['password'] = _xml_read(root, 'password')
+        config['ssh_port'] = _xml_read(root, 'ssh_port')
+        if config['ssh_port'] is None:
+            config['ssh_port'] = HNAS_DEFAULT_CONFIG['ssh_port']
+    else:
+        # password is mandatory when not using SSH
+        config['password'] = _xml_read(root, 'password', 'check')
+
+    if config['hnas_cmd'] is None:
+        config['hnas_cmd'] = HNAS_DEFAULT_CONFIG['hnas_cmd']
 
     config['hdp'] = {}
     config['services'] = {}
@@ -122,15 +136,19 @@ def _read_config(xml_config_file):
     return config
 
 
-def factory_bend():
+def factory_bend(drv_config):
     """Factory over-ride in self-tests."""
 
-    return hnas_backend.HnasBackend()
+    return hnas_backend.HnasBackend(drv_config)
 
 
 class HDSNFSDriver(nfs.NfsDriver):
     """Base class for Hitachi NFS driver.
-      Executes commands relating to Volumes.
+    Executes commands relating to Volumes.
+
+    Version 1.0.0: Initial driver version
+    Version 2.2.0:  Added support to SSH authentication
+
     """
 
     def __init__(self, *args, **kwargs):
@@ -145,8 +163,7 @@ class HDSNFSDriver(nfs.NfsDriver):
                 self.configuration.hds_hnas_nfs_config_file)
 
         super(HDSNFSDriver, self).__init__(*args, **kwargs)
-        self.bend = factory_bend()
-        (self.arid, self.nfs_name, self.lumax) = self._array_info_get()
+        self.bend = factory_bend(self.config)
 
     def _array_info_get(self):
         """Get array parameters."""