]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add CHAP persistence to SCST target helper
authornikeshmahalka <Nikesh.Mahalka@emulex.com>
Thu, 19 Feb 2015 12:33:01 +0000 (18:03 +0530)
committernikeshmahalka <Nikesh.Mahalka@emulex.com>
Wed, 25 Feb 2015 14:46:33 +0000 (20:16 +0530)
The SCST target helper currently does not have a mechanism to
persist CHAP records. Added this support so that LUNs will
get re-exported if Cinder service restarts.

Change-Id: I3daa48e7f460feef9764b79b47d7be308579fe40
Closes-Bug: #1421570

cinder/tests/targets/test_scst_driver.py
cinder/volume/targets/scst.py

index 9e8d41c2b9b6e71359705cea9f0a086367d57668..f8947c16791f1fe16ab7346113869423ec870a87 100644 (file)
@@ -188,6 +188,9 @@ class TestSCSTAdmDriver(test.TestCase):
             return '10.9.8.7:3260,1 iqn.2010-10.org.openstack:' \
                    'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba 1'
 
+        def _fake_get_target_chap_auth(*args, **kwargs):
+            return ('QZJbisGmn9AL954FNF4D', 'P68eE7u9eFqDGexd28DQ')
+
         self.stubs.Set(self.target,
                        '_get_target_and_lun',
                        _fake_get_target_and_lun)
@@ -200,7 +203,7 @@ class TestSCSTAdmDriver(test.TestCase):
                        _fake_iscsi_location)
         self.stubs.Set(self.target,
                        '_get_target_chap_auth',
-                       lambda x: None)
+                       _fake_get_target_chap_auth)
         self.stubs.Set(self.target,
                        'target_driver',
                        'iscsi')
@@ -225,13 +228,68 @@ class TestSCSTAdmDriver(test.TestCase):
                                                    self.testvol_1,
                                                    self.fake_volumes_dir))
 
-    def test_ensure_export(self):
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_get_target')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_ensure_export(self, mock_execute,
+                           mock_get_target,
+                           mock_scst_execute):
+        mock_execute.return_value = (None, None)
+        mock_scst_execute.return_value = (None, None)
+        mock_get_target.return_value = 1
         ctxt = context.get_admin_context()
+
+        def _fake_get_target_and_lun(*args, **kwargs):
+            return 0, 1
+
+        def _fake_get_target_chap_auth(*args, **kwargs):
+            return ('QZJbisGmn9AL954FNF4D', 'P68eE7u9eFqDGexd28DQ')
+
+        self.stubs.Set(self.target,
+                       '_get_target_chap_auth',
+                       _fake_get_target_chap_auth)
+        self.stubs.Set(self.target,
+                       '_get_target_and_lun',
+                       _fake_get_target_and_lun)
+
         with mock.patch.object(self.target, 'create_iscsi_target'):
             self.target.ensure_export(ctxt,
                                       self.testvol_1,
                                       self.fake_volumes_dir)
+            self.target.create_iscsi_target.assert_called_once_with(
+                'iqn.2010-10.org.openstack:testvol',
+                'ed2c2222-5fc0-11e4-aa15-123b93f75cba',
+                0, 1, self.fake_volumes_dir, _fake_get_target_chap_auth())
 
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_get_target')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_ensure_export_chap(self, mock_execute,
+                                mock_get_target,
+                                mock_scst_execute):
+        mock_execute.return_value = (None, None)
+        mock_scst_execute.return_value = (None, None)
+        mock_get_target.return_value = 1
+        ctxt = context.get_admin_context()
+
+        def _fake_get_target_and_lun(*args, **kwargs):
+            return 0, 1
+
+        def _fake_get_target_chap_auth(*args, **kwargs):
+            return None
+
+        self.stubs.Set(self.target,
+                       '_get_target_chap_auth',
+                       _fake_get_target_chap_auth)
+        self.stubs.Set(self.target,
+                       '_get_target_and_lun',
+                       _fake_get_target_and_lun)
+
+        with mock.patch.object(self.target, 'create_iscsi_target'):
+            self.target.ensure_export(ctxt,
+                                      self.testvol_1,
+                                      self.fake_volumes_dir)
             self.target.create_iscsi_target.assert_called_once_with(
-                'iqn.2010-10.org.openstack:testvol', 'testvol',
-                1, 0, self.fake_volumes_dir)
+                'iqn.2010-10.org.openstack:testvol',
+                'ed2c2222-5fc0-11e4-aa15-123b93f75cba',
+                0, 1, self.fake_volumes_dir, None)
index c8f5aeb4e4aefc8fed5f480290249a7b63a4141d..2e18a879bac0a5aaf8ddd245f557f4d373126eb3 100644 (file)
@@ -141,6 +141,24 @@ class SCSTAdm(iscsi.ISCSITarget):
                               "SCST target %s"), e)
                 raise exception.ISCSITargetHelperCommandFailed(
                     error_mesage="Failed to enable SCST Target.")
+            if chap_auth and self.target_name:
+                try:
+                    chap_string = self._iscsi_authentication('IncomingUser=',
+                                                             *chap_auth)
+                    (out, _err) = self.scst_execute('-noprompt',
+                                                    '-set_tgt_attr', name,
+                                                    '-driver',
+                                                    self.target_driver,
+                                                    '-attributes',
+                                                    chap_string)
+                    LOG.debug("StdOut from scstadmin set target attribute:"
+                              " %s.", out)
+                except putils.ProcessExecutionError:
+                    msg = _("Failed to set attribute 'Incoming user' for "
+                            "SCST target.")
+                    LOG.exception(msg)
+                    raise exception.ISCSITargetHelperCommandFailed(
+                        error_mesage=msg)
 
         if self.target_name:
             if self._get_group() is None:
@@ -224,12 +242,39 @@ class SCSTAdm(iscsi.ISCSITarget):
 
     def _get_target_chap_auth(self, context, iscsi_name):
         # FIXME(jdg): Need to implement abc method
-        pass
+
+        if self._get_target(iscsi_name) is None:
+            return None
+        (out, _err) = self.scst_execute('-list_tgt_attr', iscsi_name,
+                                        '-driver', self.target_driver)
+        first = "KEY"
+        last = "Dynamic attributes"
+        start = out.index(first) + len(first)
+        end = out.index(last, start)
+        out = out[start:end]
+        out = out.split("\n")[2]
+        if "IncomingUser" in out:
+            out = out.split(" ")
+            out = filter(lambda a: a != "", out)
+            return (out[1], out[2])
+        else:
+            return None
 
     def ensure_export(self, context, volume, volume_path):
-        iscsi_name = "%s%s" % (self.configuration.iscsi_target_prefix,
-                               volume['name'])
-        self.create_iscsi_target(iscsi_name, volume['name'], 1, 0, volume_path)
+        iscsi_target, lun = self._get_target_and_lun(context, volume)
+        if self.target_name is None:
+            iscsi_name = "%s%s" % (self.configuration.iscsi_target_prefix,
+                                   volume['name'])
+        else:
+            iscsi_name = self.target_name
+
+        if self.chap_username and self.chap_password:
+            chap_auth = (self.chap_username, self.chap_password)
+        else:
+            chap_auth = self._get_target_chap_auth(context, iscsi_name)
+
+        self.create_iscsi_target(iscsi_name, volume['id'], iscsi_target,
+                                 lun, volume_path, chap_auth)
 
     def create_export(self, context, volume, volume_path):
         """Creates an export for a logical volume."""
@@ -241,14 +286,12 @@ class SCSTAdm(iscsi.ISCSITarget):
             iscsi_name = self.target_name
 
         if self.chap_username and self.chap_password:
-            chap_username = self.chap_username
-            chap_password = self.chap_password
+            chap_auth = (self.chap_username, self.chap_password)
         else:
-            chap_username = vutils.generate_username()
-            chap_password = vutils.generate_password()
-
-        chap_auth = self._iscsi_authentication('IncomingUser', chap_username,
-                                               chap_password)
+            chap_auth = self._get_target_chap_auth(context, iscsi_name)
+            if not chap_auth:
+                chap_auth = (vutils.generate_username(),
+                             vutils.generate_password())
         tid = self.create_iscsi_target(iscsi_name, volume['id'], iscsi_target,
                                        lun, volume_path, chap_auth)
 
@@ -257,7 +300,7 @@ class SCSTAdm(iscsi.ISCSITarget):
             self.configuration.iscsi_ip_address, tid, iscsi_name, lun)
         LOG.debug('Set provider_location to: %s', data['location'])
         data['auth'] = self._iscsi_authentication(
-            'CHAP', chap_username, chap_password)
+            'CHAP', *chap_auth)
         return data
 
     def remove_export(self, context, volume):