]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Obtain target authentication from database same as LIO target
authorMitsuhiro Tanino <mitsuhiro.tanino@hds.com>
Fri, 25 Sep 2015 22:09:59 +0000 (18:09 -0400)
committerMitsuhiro Tanino <mitsuhiro.tanino@hds.com>
Wed, 30 Sep 2015 22:48:22 +0000 (18:48 -0400)
Currently, tgt, iet and cxt obtain user and password for iSCSI
target by analyzing configuration file.
However this information is already stored in DB and LIO obtains
these authentication from provider_auth in DB.
This way is simple and robust instead of analyzing configuration
file directly.
This patch proposes these two changes:
- Change the way to obtain authentication from configuration
  file to DB at _get_target_chap_auth().
- Move _get_target_chap_auth() into iscsi.py and inherit
  the method at tgt, iet and cxt target because they can use
  same implementation to get authentication from DB.

Co-Authored-By: Anish Bhatt <anish@chelsio.com>
Change-Id: I5188ce5855d206c513f72e01f010175490ec89b2
Partial-Bug: #1499795

12 files changed:
cinder/tests/unit/targets/test_base_iscsi_driver.py
cinder/tests/unit/targets/test_cxt_driver.py
cinder/tests/unit/targets/test_iet_driver.py
cinder/tests/unit/targets/test_lio_driver.py
cinder/tests/unit/targets/test_tgt_driver.py
cinder/tests/unit/test_volume.py
cinder/volume/targets/cxt.py
cinder/volume/targets/fake.py
cinder/volume/targets/iet.py
cinder/volume/targets/iscsi.py
cinder/volume/targets/lio.py
cinder/volume/targets/tgt.py

index 5ab0ddc0b14c08366a53258fc207a5b28535a5ab..267997229ba5195f6c1a89dc386fca95c9b2f757 100644 (file)
@@ -33,6 +33,8 @@ class TestBaseISCSITargetDriver(tf.TargetDriverFixture):
         super(TestBaseISCSITargetDriver, self).setUp()
         self.target = fake.FakeTarget(root_helper=utils.get_root_helper(),
                                       configuration=self.configuration)
+        self.target.db = mock.MagicMock(
+            volume_get=lambda x, y: {'provider_auth': 'CHAP otzL 234Z'})
 
     def test_abc_methods_not_present_fails(self):
         configuration = conf.Configuration(cfg.StrOpt('iscsi_target_prefix',
@@ -154,3 +156,9 @@ class TestBaseISCSITargetDriver(tf.TargetDriverFixture):
         location = self.target._iscsi_location('portal', 1, 'target', 2,
                                                ['portal2'])
         self.assertEqual('portal:3260;portal2:3260,1 target 2', location)
+
+    def test_get_target_chap_auth(self):
+        ctxt = context.get_admin_context()
+        self.assertEqual(('otzL', '234Z'),
+                         self.target._get_target_chap_auth(ctxt,
+                                                           self.test_vol))
index 9ae93219230f687ec7562cf24e14ecf558e9216a..13456ac4368f021905c0b1468358d0518b73264d 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import contextlib
 import os
 
 import mock
-import six
 
 from cinder import context
 from cinder.tests.unit.targets import targets_fixture as tf
@@ -55,43 +53,6 @@ class TestCxtAdmDriver(tf.TargetDriverFixture):
             )
             self.assertTrue(m_exec.called)
 
-    def test_get_target_chap_auth(self):
-        tmp_file = six.StringIO()
-        tmp_file.write(
-            'target:\n'
-            '        TargetName=iqn.2010-10.org.openstack:volume-%(id)s\n'
-            '        TargetDevice=/dev/%(vg)s/volume-%(id)s\n'
-            '        PortalGroup=1@10.9.8.7:3260\n'
-            '        AuthMethod=CHAP\n'
-            '        Auth_CHAP_Policy=Oneway\n'
-            '        Auth_CHAP_Initiator="otzL":"234Z"\n' %
-            {'id': self.VOLUME_ID, 'vg': self.VG}
-        )
-        tmp_file.seek(0)
-
-        expected = ('otzL', '234Z')
-        with mock.patch('six.moves.builtins.open') as mock_open:
-            ctx = context.get_admin_context()
-            mock_open.return_value = contextlib.closing(tmp_file)
-            self.assertEqual(expected,
-                             self.target._get_target_chap_auth(ctx,
-                                                               self.test_vol))
-            self.assertTrue(mock_open.called)
-
-    def test_get_target_chap_auth_negative(self):
-        with mock.patch('six.moves.builtins.open') as mock_open:
-            e = IOError()
-            e.errno = 123
-            mock_open.side_effect = e
-            ctxt = context.get_admin_context()
-            self.assertRaises(IOError,
-                              self.target._get_target_chap_auth,
-                              ctxt, self.test_vol)
-            mock_open.side_effect = ZeroDivisionError()
-            self.assertRaises(ZeroDivisionError,
-                              self.target._get_target_chap_auth,
-                              ctxt, self.test_vol)
-
     @mock.patch('cinder.volume.targets.cxt.CxtAdm._get_target',
                 return_value=1)
     @mock.patch('cinder.utils.execute')
index 92e8a4ea3776ea65788dee0516fb199bc733bf3a..04b749e9f82027653e217f7e83d0836ef4a81995 100644 (file)
@@ -51,32 +51,6 @@ class TestIetAdmDriver(tf.TargetDriverFixture):
                               self.target._get_target,
                               '')
 
-    @mock.patch('os.path.exists', return_value=True)
-    @mock.patch('cinder.utils.temporary_chown')
-    def test_get_target_chap_auth(self, mock_chown, mock_exists):
-        tmp_file = six.StringIO()
-        tmp_file.write(
-            'Target iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n'  # noqa
-            '    IncomingUser otzLy2UYbYfnP4zXLG5z 234Zweo38VGBBvrpK9nt\n'
-            '    Lun 0 Path=/dev/stack-volumes-lvmdriver-1/volume-83c2e877-feed-46be-8435-77884fe55b45,Type=fileio\n'  # noqa
-        )
-        tmp_file.seek(0)
-        expected = ('otzLy2UYbYfnP4zXLG5z', '234Zweo38VGBBvrpK9nt')
-        with mock.patch('__builtin__.open') as mock_open:
-            ictx = context.get_admin_context()
-            mock_open.return_value = contextlib.closing(tmp_file)
-            self.assertEqual(expected,
-                             self.target._get_target_chap_auth(ictx,
-                                                               self.test_vol))
-            self.assertTrue(mock_open.called)
-
-            # Test the failure case: Failed to handle the config file
-            mock_open.side_effect = StandardError()
-            self.assertRaises(StandardError,
-                              self.target._get_target_chap_auth,
-                              ictx,
-                              self.test_vol)
-
     @mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
                 return_value=0)
     @mock.patch('cinder.utils.execute')
index bf933e6b75dbd3aff8d2d48267cd8f015c5560fb..68b63e115bb21e54aa5730829e036d83207363e9 100644 (file)
@@ -28,8 +28,6 @@ class TestLioAdmDriver(tf.TargetDriverFixture):
         with mock.patch.object(lio.LioAdm, '_verify_rtstool'):
             self.target = lio.LioAdm(root_helper=utils.get_root_helper(),
                                      configuration=self.configuration)
-        self.target.db = mock.MagicMock(
-            volume_get=lambda x, y: {'provider_auth': 'IncomingUser foo bar'})
 
     @mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
     @mock.patch.object(lio.LioAdm, '_persist_configuration')
@@ -57,12 +55,6 @@ class TestLioAdmDriver(tf.TargetDriverFixture):
         self.assertEqual(expected,
                          self.target._get_target_and_lun(ctxt, self.testvol))
 
-    def test_get_target_chap_auth(self):
-        ctxt = context.get_admin_context()
-        self.assertEqual(('foo', 'bar'),
-                         self.target._get_target_chap_auth(ctxt,
-                                                           self.test_vol))
-
     @mock.patch.object(lio.LioAdm, '_execute', side_effect=lio.LioAdm._execute)
     @mock.patch.object(lio.LioAdm, '_persist_configuration')
     @mock.patch('cinder.utils.execute')
index f103cc72e5d2f198bb7cb74e30811d10cc7c6150..65be5e7f21d7762154500841d7cf185b81aa24f9 100644 (file)
@@ -142,39 +142,6 @@ class TestTgtAdmDriver(tf.TargetDriverFixture):
         self.assertEqual(expected,
                          self.target._get_target_and_lun(ctxt, self.testvol))
 
-    def test_get_target_chap_auth(self):
-        persist_file =\
-            '<target iqn.2010-10.org.openstack:volume-%(id)s>\n'\
-            '    backing-store %(bspath)s\n'\
-            '    driver iscsi\n'\
-            '    incominguser otzL 234Z\n'\
-            '    write-cache on\n'\
-            '</target>' % {'id': self.VOLUME_ID,
-                           'bspath': self.testvol_path}
-        with open(os.path.join(self.fake_volumes_dir,
-                               self.test_vol.split(':')[1]),
-                  'w') as tmp_file:
-            tmp_file.write(persist_file)
-        ctxt = context.get_admin_context()
-        expected = ('otzL', '234Z')
-        self.assertEqual(expected,
-                         self.target._get_target_chap_auth(ctxt,
-                                                           self.test_vol))
-
-    def test_get_target_chap_auth_negative(self):
-        with mock.patch('six.moves.builtins.open') as mock_open:
-            e = IOError()
-            e.errno = 123
-            mock_open.side_effect = e
-            ctxt = context.get_admin_context()
-            self.assertRaises(IOError,
-                              self.target._get_target_chap_auth,
-                              ctxt, self.test_vol)
-            mock_open.side_effect = ZeroDivisionError()
-            self.assertRaises(ZeroDivisionError,
-                              self.target._get_target_chap_auth,
-                              ctxt, self.test_vol)
-
     def test_create_iscsi_target(self):
         with mock.patch('cinder.utils.execute', return_value=('', '')),\
                 mock.patch.object(self.target, '_get_target',
index 227b1b530423657fcafa747a78a8f8d3a8c59142..9358b2c213c61d5052129f40ee91fac57ef48d0b 100644 (file)
@@ -2014,13 +2014,16 @@ class VolumeTestCase(BaseVolumeTestCase):
                           image_id='fake_id',
                           source_volume='fake_id')
 
+    @mock.patch.object(cinder.volume.targets.iscsi.ISCSITarget,
+                       '_get_target_chap_auth')
     @mock.patch.object(db, 'volume_admin_metadata_get')
     @mock.patch.object(db, 'volume_get')
     @mock.patch.object(db, 'volume_update')
     def test_initialize_connection_fetchqos(self,
                                             _mock_volume_update,
                                             _mock_volume_get,
-                                            _mock_volume_admin_metadata_get):
+                                            _mock_volume_admin_metadata_get,
+                                            mock_get_target):
         """Make sure initialize_connection returns correct information."""
         _fake_admin_meta = {'fake-key': 'fake-value'}
         _fake_volume = {'volume_type_id': 'fake_type_id',
@@ -2046,6 +2049,7 @@ class VolumeTestCase(BaseVolumeTestCase):
                               'initialize_connection') as driver_init:
             type_qos.return_value = dict(qos_specs=qos_values)
             driver_init.return_value = {'data': {}}
+            mock_get_target.return_value = None
             qos_specs_expected = {'key1': 'value1',
                                   'key2': 'value2'}
             # initialize_connection() passes qos_specs that is designated to
@@ -2098,6 +2102,8 @@ class VolumeTestCase(BaseVolumeTestCase):
                           'fake_volume_id',
                           connector)
 
+    @mock.patch.object(cinder.volume.targets.iscsi.ISCSITarget,
+                       '_get_target_chap_auth')
     @mock.patch.object(db, 'volume_admin_metadata_get')
     @mock.patch.object(db, 'volume_update')
     @mock.patch.object(db, 'volume_get')
@@ -2109,7 +2115,8 @@ class VolumeTestCase(BaseVolumeTestCase):
                                                   mock_driver_init,
                                                   mock_volume_get,
                                                   mock_volume_update,
-                                                  mock_metadata_get):
+                                                  mock_metadata_get,
+                                                  mock_get_target):
 
         fake_admin_meta = {'fake-key': 'fake-value'}
         fake_volume = {'volume_type_id': None,
@@ -2122,6 +2129,7 @@ class VolumeTestCase(BaseVolumeTestCase):
 
         mock_volume_get.return_value = fake_volume
         mock_volume_update.return_value = fake_volume
+        mock_get_target.return_value = None
         connector = {'ip': 'IP', 'initiator': 'INITIATOR'}
         mock_driver_init.return_value = {
             'driver_volume_type': 'iscsi',
index 8ab161a701b63d14e5250308b30cafef57cf6050..9a4b4c4cfe07ca7e9b42ddb64149deb7b88c030a 100644 (file)
@@ -15,7 +15,6 @@
 
 
 import os
-import re
 
 from oslo_concurrency import processutils as putils
 from oslo_log import log as logging
@@ -88,33 +87,6 @@ class CxtAdm(iscsi.ISCSITarget):
         iscsi_target = 1
         return iscsi_target, lun
 
-    def _get_target_chap_auth(self, context, name):
-        volumes_dir = self._get_volumes_dir()
-        vol_id = name.split(':')[1]
-        volume_path = os.path.join(volumes_dir, vol_id)
-
-        try:
-            with open(volume_path, 'r') as f:
-                volume_conf = f.read()
-        except IOError as e_fnf:
-            LOG.debug('Failed to open config for %(vol_id)s: %(e)s',
-                      {'vol_id': vol_id, 'e': e_fnf})
-            # We don't run on anything non-linux
-            if e_fnf.errno == 2:
-                return None
-            else:
-                raise
-        except Exception as e_vol:
-            LOG.error(_LE('Failed to open config for %(vol_id)s: %(e)s'),
-                      {'vol_id': vol_id, 'e': e_vol})
-            raise
-
-        m = re.search('Auth_CHAP_Initiator="(\w+)":"(\w+)"', volume_conf)
-        if m:
-            return (m.group(1), m.group(2))
-        LOG.debug('Failed to find CHAP auth from config for %s', vol_id)
-        return None
-
     @staticmethod
     def _get_portal(ip, port=None):
         # ipv6 addresses use [ip]:port format, ipv4 use ip:port
index 1d89afb0830d9b15783976114ebd9d6da1a07714..17883dd79f61160293a78526eb85719a0ba644a4 100644 (file)
@@ -22,9 +22,6 @@ class FakeTarget(iscsi.ISCSITarget):
     def _get_target_and_lun(self, context, volume):
         return(0, 0)
 
-    def _get_target_chap_auth(self, context, iscsi_name):
-        pass
-
     def create_iscsi_target(self, name, tid, lun, path,
                             chap_auth, **kwargs):
         pass
index 93e79bf7c9009160680b24637dc4896f621180ea..abd5da05c2167f2641ac2dda92059960f9114f20 100644 (file)
@@ -80,42 +80,6 @@ class IetAdm(iscsi.ISCSITarget):
 
         return iscsi_target, lun
 
-    def _get_target_chap_auth(self, context, name):
-
-        vol_id = name.split(':')[1]
-        if os.path.exists(self.iet_conf):
-            try:
-                with utils.temporary_chown(self.iet_conf):
-                    with open(self.iet_conf, 'r') as f:
-                        iet_conf_text = f.readlines()
-            except Exception:
-                # If we fail to handle config file, raise exception here to
-                # prevent unexpected behavior during subsequent operations.
-                LOG.exception(_LE("Failed to open config for %s."), vol_id)
-                raise
-
-            target_found = False
-            for line in iet_conf_text:
-                if target_found:
-                    m = re.search('(\w+) (\w+) (\w+)', line)
-                    if m:
-                        return (m.group(2), m.group(3))
-                    else:
-                        LOG.debug("Failed to find CHAP auth from config "
-                                  "for %s", vol_id)
-                        return None
-                elif name in line:
-                    target_found = True
-        else:
-            # Missing config file is unxepected sisuation. But we will create
-            # new config file during create_iscsi_target(). Just we warn the
-            # operator here.
-            LOG.warning(_LW("Failed to find CHAP auth from config for "
-                            "%(vol_id)s. Config file %(conf)s does not "
-                            "exist."),
-                        {'vol_id': vol_id, 'conf': self.iet_conf})
-            return None
-
     def create_iscsi_target(self, name, tid, lun, path,
                             chap_auth=None, **kwargs):
 
index 792c06eec669a08e69c4d94c68a9c79347c7bcb9..60f02f53d87fb31c0d5ffd3469b95ed19ecb5544 100644 (file)
@@ -326,15 +326,23 @@ class ISCSITarget(driver.Target):
         if tid is None:
             raise exception.NotFound()
 
+    def _get_target_chap_auth(self, context, iscsi_name):
+        """Get the current chap auth username and password."""
+        try:
+            # 'iscsi_name': 'iqn.2010-10.org.openstack:volume-00000001'
+            vol_id = iscsi_name.split(':volume-')[1]
+            volume_info = self.db.volume_get(context, vol_id)
+            # 'provider_auth': 'CHAP user_id password'
+            if volume_info['provider_auth']:
+                return tuple(volume_info['provider_auth'].split(' ', 3)[1:])
+        except exception.NotFound:
+            LOG.debug('Failed to get CHAP auth from DB for %s.', vol_id)
+
     @abc.abstractmethod
     def _get_target_and_lun(self, context, volume):
         """Get iscsi target and lun."""
         pass
 
-    @abc.abstractmethod
-    def _get_target_chap_auth(self, context, iscsi_name):
-        pass
-
     @abc.abstractmethod
     def create_iscsi_target(self, name, tid, lun, path,
                             chap_auth, **kwargs):
index 7bd2a5108fcd4093dc0b8072b036f19f8bb4abe1..05346af69bdb0ea5a252138559cbd8ea611191e0 100644 (file)
@@ -33,18 +33,6 @@ class LioAdm(iscsi.ISCSITarget):
 
         self._verify_rtstool()
 
-    def _get_target_chap_auth(self, context, iscsi_name):
-        """Get the current chap auth username and password."""
-        try:
-            # 'iscsi_name': 'iqn.2010-10.org.openstack:volume-00000001'
-            vol_id = iscsi_name.split(':volume-')[1]
-            volume_info = self.db.volume_get(context, vol_id)
-            # 'provider_auth': 'CHAP user_id password'
-            if volume_info['provider_auth']:
-                return tuple(volume_info['provider_auth'].split(' ', 3)[1:])
-        except exception.NotFound:
-            LOG.debug('Failed to get CHAP auth from DB for %s', vol_id)
-
     def _verify_rtstool(self):
         try:
             # This call doesn't need locking
index 5d6ac6132ca0dad089341eb2a30f9ca8bd97cb39..d1e8169992cd20c6119769c595771a45285f8fc4 100644 (file)
@@ -11,7 +11,6 @@
 #    under the License.
 
 import os
-import re
 import textwrap
 import time
 
@@ -115,34 +114,6 @@ class TgtAdm(iscsi.ISCSITarget):
         iscsi_target = 0  # NOTE(jdg): Not used by tgtadm
         return iscsi_target, lun
 
-    def _get_target_chap_auth(self, context, iscsi_name):
-        """Get the current chap auth username and password."""
-        volumes_dir = self.volumes_dir
-        vol_id = iscsi_name.split(':')[1]
-        volume_path = os.path.join(volumes_dir, vol_id)
-
-        try:
-            with open(volume_path, 'r') as f:
-                volume_conf = f.read()
-        except IOError as e_fnf:
-            LOG.debug('Failed to open config for Volume %(vol_id)s: %(e)s',
-                      {'vol_id': vol_id, 'e': e_fnf})
-            # tgt is linux specific
-            if e_fnf.errno == 2:
-                return None
-            else:
-                raise
-        except Exception as e_vol:
-            LOG.error(_LE('Failed to open config for %(vol_id)s: %(e)s'),
-                      {'vol_id': vol_id, 'e': e_vol})
-            raise
-
-        m = re.search('incominguser (\w+) (\w+)', volume_conf)
-        if m:
-            return (m.group(1), m.group(2))
-        LOG.debug('Failed to find CHAP auth from config for %s', vol_id)
-        return None
-
     @utils.retry(putils.ProcessExecutionError)
     def _do_tgt_update(self, name):
             (out, err) = utils.execute('tgt-admin', '--update', name,