From: nikeshmahalka Date: Thu, 19 Feb 2015 12:33:01 +0000 (+0530) Subject: Add CHAP persistence to SCST target helper X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=7fe917a740878d8587c87f2e6f489654d2cec52e;p=openstack-build%2Fcinder-build.git Add CHAP persistence to SCST target helper 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 --- diff --git a/cinder/tests/targets/test_scst_driver.py b/cinder/tests/targets/test_scst_driver.py index 9e8d41c2b..f8947c167 100644 --- a/cinder/tests/targets/test_scst_driver.py +++ b/cinder/tests/targets/test_scst_driver.py @@ -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) diff --git a/cinder/volume/targets/scst.py b/cinder/volume/targets/scst.py index c8f5aeb4e..2e18a879b 100644 --- a/cinder/volume/targets/scst.py +++ b/cinder/volume/targets/scst.py @@ -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):