]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add iSCSI SCST Target support to cinder
authornikeshmahalka <Nikesh.Mahalka@emulex.com>
Thu, 8 Jan 2015 13:08:19 +0000 (18:38 +0530)
committernikeshmahalka <Nikesh.Mahalka@emulex.com>
Thu, 12 Feb 2015 19:22:11 +0000 (00:52 +0530)
Currently cinder supports tgt,lio targets.
We are adding SCST Target support in cinder.

Change-Id: I449143d125808d28758f3d438d7d1476a544c38b
Implements: blueprint scst-iscsi-backend-support

cinder/exception.py
cinder/tests/targets/test_scst_driver.py [new file with mode: 0644]
cinder/volume/driver.py
cinder/volume/targets/scst.py [new file with mode: 0644]
etc/cinder/rootwrap.conf
etc/cinder/rootwrap.d/volume.filters

index b236bc1a316c28c63540693c50b18e508755b164..9cb256b8236b84293160e3a59c9b539fead01173 100644 (file)
@@ -914,6 +914,10 @@ class ISCSITargetDetachFailed(CinderException):
     message = _("Failed to detach iSCSI target for volume %(volume_id)s.")
 
 
+class ISCSITargetHelperCommandFailed(CinderException):
+    message = _("%(error_message)s")
+
+
 # X-IO driver exception.
 class XIODriverException(VolumeDriverException):
     message = _("X-IO Volume Driver exception!")
diff --git a/cinder/tests/targets/test_scst_driver.py b/cinder/tests/targets/test_scst_driver.py
new file mode 100644 (file)
index 0000000..9e8d41c
--- /dev/null
@@ -0,0 +1,237 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+import tempfile
+
+import mock
+from oslo_utils import timeutils
+
+from cinder import context
+from cinder import test
+from cinder import utils
+from cinder.volume import configuration as conf
+from cinder.volume.targets import scst
+from cinder.volume import utils as vutils
+
+
+class TestSCSTAdmDriver(test.TestCase):
+
+    def setUp(self):
+        super(TestSCSTAdmDriver, self).setUp()
+        self.configuration = conf.Configuration(None)
+        self.configuration.append_config_values = mock.Mock(return_value=0)
+        self.configuration.iscsi_ip_address = '10.9.8.7'
+        self.fake_volumes_dir = tempfile.mkdtemp()
+        self.fake_id_1 = 'ed2c1fd4-5fc0-11e4-aa15-123b93f75cba'
+        self.fake_id_2 = 'ed2c2222-5fc0-11e4-aa15-123b93f75cba'
+        self.fake_id_3 = 'ed2c3333-5fc0-11e4-aa15-123b93f75cba'
+        self.fake_id_4 = 'ed2c4444-5fc0-11e4-aa15-123b93f75cba'
+        self.stubs.Set(self.configuration, 'safe_get', self.fake_safe_get)
+
+        self.target = scst.SCSTAdm(root_helper=utils.get_root_helper(),
+                                   configuration=self.configuration)
+        self.testvol_1 =\
+            {'project_id': self.fake_id_1,
+             'name': 'testvol',
+             'size': 1,
+             'id': self.fake_id_2,
+             'volume_type_id': None,
+             'provider_location': '10.9.8.7:3260 '
+                                  'iqn.2010-10.org.openstack:'
+                                  'volume-%s 1' % self.fake_id_2,
+             'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
+                              'c76370d66b 2FE0CQ8J196R',
+             'provider_geometry': '512 512',
+             'created_at': timeutils.utcnow(),
+             'host': 'fake_host@lvm#lvm'}
+        self.testvol_2 =\
+            {'project_id': self.fake_id_3,
+             'name': 'testvol2',
+             'size': 1,
+             'id': self.fake_id_4,
+             'volume_type_id': None,
+             'provider_location': '10.9.8.7:3260 '
+                                  'iqn.2010-10.org.openstack:'
+                                  'volume-%s 2' % self.fake_id_4,
+             'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
+                              'c76370d66b 2FE0CQ8J196R',
+             'provider_geometry': '512 512',
+             'created_at': timeutils.utcnow(),
+             'host': 'fake_host@lvm#lvm'}
+
+        self.fake_iscsi_scan = \
+            ('Collecting current configuration: done.\n'
+             'Driver Target\n'
+             '----------------------------------------------\n'
+             'iscsi iqn.2010-10.org.openstack:'
+             'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba\n'
+             'All done.\n')
+
+        self.fake_iscsi_attribute_scan = \
+            ('Collecting current configuration: done.\n'
+             'Attribute      Value     Writable      KEY\n'
+             '------------------------------------------\n'
+             'rel_tgt_id     1         Yes           Yes\n'
+             'Dynamic attributes available\n'
+             '----------------------------\n'
+             'IncomingUser\n'
+             'OutgoingUser\n'
+             'allowed_portal\n'
+             'LUN CREATE attributes available\n'
+             '-------------------------------\n'
+             'read_only\n'
+             'All done.\n')
+        self.fake_list_group = \
+            ('org.openstack:volume-vedams\n'
+             'Collecting current configuration: done.\n'
+             'Driver: iscsi\n'
+             'Target: iqn.2010-10.org.openstack:volume-vedams\n'
+             'Driver/target \'iscsi/iqn.2010-10.org.openstack:volume-vedams\''
+             'has no associated LUNs.\n'
+             'Group: iqn.1993-08.org.debian:01:626bf14ebdc\n'
+             'Assigned LUNs:\n'
+             'LUN  Device\n'
+             '------------------\n'
+             '1    1b67387810256\n'
+             '2    2a0f1cc9cd595\n'
+             'Assigned Initiators:\n'
+             'Initiator\n'
+             '-------------------------------------\n'
+             'iqn.1993-08.org.debian:01:626bf14ebdc\n'
+             'All done.\n')
+
+        self.target.db = mock.MagicMock(
+            volume_get=lambda x, y: {'provider_auth': 'IncomingUser foo bar'})
+
+    def fake_safe_get(self, value):
+        if value == 'volumes_dir':
+            return self.fake_volumes_dir
+
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_target_attribute')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_get_target(self, mock_execute,
+                        mock_target_attribute,
+                        mock_scst_execute):
+        mock_target_attribute.return_value = 1
+        mock_execute.return_value = (self.fake_iscsi_scan, None)
+        expected = 1
+        self.assertEqual(expected, self.target._get_target(
+            'iqn.2010-10.org.openstack:'
+            'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba'))
+
+    @mock.patch.object(utils, 'execute')
+    def test_target_attribute(self, mock_execute):
+        mock_execute.return_value = (self.fake_iscsi_attribute_scan, None)
+        self.assertEqual(str(1), self.target._target_attribute(
+            'iqn.2010-10.org.openstack:'
+            'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba'))
+
+    def test_single_lun_get_target_and_lun(self):
+        ctxt = context.get_admin_context()
+        self.assertEqual((0, 1), self.target._get_target_and_lun(
+            ctxt, self.testvol_1))
+
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_get_group')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_multi_lun_get_target_and_lun(self, mock_execute, mock_get_group,
+                                          mock_scst_execute):
+        mock_execute.return_value = (self.fake_list_group, None)
+        mock_get_group.return_value = self.fake_list_group
+
+        self.stubs.Set(self.target,
+                       'target_name',
+                       'iqn.2010-10.org.openstack:volume-vedams')
+        ctxt = context.get_admin_context()
+
+        self.assertEqual((0, 3), self.target._get_target_and_lun(
+            ctxt, self.testvol_1))
+
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_get_target')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_create_iscsi_target(self, mock_execute, mock_get_target,
+                                 mock_scst_execute):
+        mock_execute.return_value = (None, None)
+        mock_get_target.return_value = 1
+
+        self.assertEqual(1,
+                         self.target.create_iscsi_target(
+                             'iqn.2010-10.org.openstack:'
+                             'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba',
+                             'vol1',
+                             0, 1, self.fake_volumes_dir))
+
+    @mock.patch.object(utils, 'execute')
+    @mock.patch.object(scst.SCSTAdm, '_get_target')
+    @mock.patch.object(scst.SCSTAdm, 'scst_execute')
+    def test_create_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
+
+        def _fake_get_target_and_lun(*args, **kwargs):
+            return 0, 1
+
+        def _fake_iscsi_location(*args, **kwargs):
+            return '10.9.8.7:3260,1 iqn.2010-10.org.openstack:' \
+                   'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba 1'
+
+        self.stubs.Set(self.target,
+                       '_get_target_and_lun',
+                       _fake_get_target_and_lun)
+
+        self.stubs.Set(self.target,
+                       'initiator_iqn',
+                       'iqn.1993-08.org.debian:01:626bf14ebdc')
+        self.stubs.Set(self.target,
+                       '_iscsi_location',
+                       _fake_iscsi_location)
+        self.stubs.Set(self.target,
+                       '_get_target_chap_auth',
+                       lambda x: None)
+        self.stubs.Set(self.target,
+                       'target_driver',
+                       'iscsi')
+        self.stubs.Set(self.target,
+                       'initiator_iqn',
+                       'iqn.1993-08.org.debian:01:626bf14ebdc')
+        self.stubs.Set(vutils,
+                       'generate_username',
+                       lambda: 'QZJbisGmn9AL954FNF4D')
+        self.stubs.Set(vutils,
+                       'generate_password',
+                       lambda: 'P68eE7u9eFqDGexd28DQ')
+
+        ctxt = context.get_admin_context()
+        expected_result = {'location': '10.9.8.7:3260,1 '
+                           'iqn.2010-10.org.openstack:'
+                           'volume-ed2c2222-5fc0-11e4-aa15-123b93f75cba 1',
+                           'auth': 'CHAP '
+                           'QZJbisGmn9AL954FNF4D P68eE7u9eFqDGexd28DQ'}
+        self.assertEqual(expected_result,
+                         self.target.create_export(ctxt,
+                                                   self.testvol_1,
+                                                   self.fake_volumes_dir))
+
+    def test_ensure_export(self):
+        ctxt = context.get_admin_context()
+        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)
index dc7286e526bb145698a80f39248c29bfd492e562..f6697c56bb1f7aaa6f13ee175e4608a0fb73899b 100644 (file)
@@ -147,6 +147,20 @@ volume_opts = [
                       'provisioned capacity cannot exceed the total physical '
                       'capacity. A ratio lower than 1.0 will be ignored and '
                       'the default value will be used instead.'),
+    cfg.StrOpt('scst_target_iqn_name',
+               default=None,
+               help='Certain ISCSI targets have predefined target names, '
+                    'SCST target driver uses this name.'),
+    cfg.StrOpt('scst_target_driver',
+               default='iscsi',
+               help='SCST target implementation can choose from multiple '
+                    'SCST target drivers.'),
+    cfg.StrOpt('chap_username',
+               default=None,
+               help='CHAP username to use for iSCSI Targets'),
+    cfg.StrOpt('chap_password',
+               default=None,
+               help='CHAP password to use for iSCSI Targets'),
 ]
 
 # for backward compatibility
@@ -227,7 +241,8 @@ class VolumeDriver(object):
             'ietadm': 'cinder.volume.targets.iet.IetAdm',
             'iseradm': 'cinder.volume.targets.iser.ISERTgtAdm',
             'lioadm': 'cinder.volume.targets.lio.LioAdm',
-            'tgtadm': 'cinder.volume.targets.tgt.TgtAdm', }
+            'tgtadm': 'cinder.volume.targets.tgt.TgtAdm',
+            'scstadmin': 'cinder.volume.targets.scst.SCSTAdm', }
 
         # set True by manager after successful check_for_setup
         self._initialized = False
diff --git a/cinder/volume/targets/scst.py b/cinder/volume/targets/scst.py
new file mode 100644 (file)
index 0000000..78b9490
--- /dev/null
@@ -0,0 +1,349 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_concurrency import processutils as putils
+
+from cinder import exception
+from cinder import utils
+from cinder.i18n import _, _LE
+from cinder.openstack.common import log as logging
+from cinder.volume.targets import iscsi
+from cinder.volume import utils as vutils
+
+LOG = logging.getLogger(__name__)
+
+
+class SCSTAdm(iscsi.ISCSITarget):
+
+    def __init__(self, *args, **kwargs):
+        super(SCSTAdm, self).__init__(*args, **kwargs)
+        self.volumes_dir = self.configuration.safe_get('volumes_dir')
+        self.iscsi_target_prefix = self.configuration.safe_get(
+            'iscsi_target_prefix')
+        self.target_name = self.configuration.safe_get('scst_target_iqn_name')
+        self.target_driver = self.configuration.safe_get('scst_target_driver')
+        self.chap_username = self.configuration.safe_get('chap_username')
+        self.chap_password = self.configuration.safe_get('chap_password')
+        self.initiator_iqn = None
+        self.remove_initiator_iqn = None
+
+    def scst_execute(self, *args):
+        return utils.execute('scstadmin', *args, run_as_root=True)
+
+    def validate_connector(self, connector):
+        # iSCSI drivers require the initiator information
+        if 'initiator' not in connector:
+            err_msg = _('The volume driver requires the iSCSI initiator '
+                        'name in the connector.')
+            LOG.error(err_msg)
+            raise exception.VolumeBackendAPIException(data=err_msg)
+        self.initiator_iqn = connector['initiator']
+
+    def terminate_connection(self, volume, connector, **kwargs):
+        self.remove_initiator_iqn = connector['initiator']
+
+    def _get_target(self, iqn):
+        (out, _err) = self.scst_execute('-list_target')
+        if iqn in out:
+            return self._target_attribute(iqn)
+        return None
+
+    def _target_attribute(self, iqn):
+        (out, _err) = self.scst_execute('-list_tgt_attr', iqn,
+                                        '-driver', self.target_driver)
+        lines = out.split('\n')
+        for line in lines:
+            if "rel_tgt_id" in line:
+                parsed = line.split()
+                return parsed[1]
+
+    def _get_group(self):
+        scst_group = self.initiator_iqn
+        (out, _err) = self.scst_execute('-list_group')
+        if scst_group in out:
+            return out
+        return None
+
+    def _get_luns_info(self):
+        scst_group = self.initiator_iqn
+        (out, _err) = self.scst_execute('-list_group', scst_group,
+                                        '-driver', self.target_driver,
+                                        '-target', self.target_name)
+
+        first = "Assigned LUNs:"
+        last = "Assigned Initiators:"
+        start = out.index(first) + len(first)
+        end = out.index(last, start)
+        out = out[start:end]
+
+        luns = []
+        for line in out.strip().split("\n")[2:]:
+            luns.append(int(line.strip().split(" ")[0]))
+        luns = sorted(set(luns))
+        return luns
+
+    def _get_target_and_lun(self, context, volume):
+        iscsi_target = 0
+        if not self.target_name or not self._get_group():
+            lun = 1
+            return iscsi_target, lun
+
+        luns = self._get_luns_info()
+        if (not luns) or (luns[0] != 1):
+            lun = 1
+            return iscsi_target, lun
+        else:
+            for lun in luns:
+                if (luns[-1] == lun) or (luns[lun - 1] + 1 != luns[lun]):
+                    return iscsi_target, (lun + 1)
+
+    def create_iscsi_target(self, name, vol_id, tid, lun, path,
+                            chap_auth=None):
+        scst_group = self.initiator_iqn
+        vol_name = path.split("/")[3]
+        try:
+            (out, _err) = self.scst_execute('-noprompt',
+                                            '-set_drv_attr',
+                                            self.target_driver,
+                                            '-attributes',
+                                            'enabled=1')
+            LOG.debug('StdOut from set driver attribute: %s', out)
+        except putils.ProcessExecutionError as e:
+            LOG.error(_LE("Failed to set attribute for enable target driver "
+                          "%s"), e)
+            raise exception.ISCSITargetHelperCommandFailed(
+                error_message="Failed to enable SCST Target driver.")
+
+        if self._get_target(name) is None:
+            try:
+                (out, _err) = self.scst_execute('-add_target', name,
+                                                '-driver', self.target_driver)
+                LOG.debug("StdOut from scstadmin create target: %s", out)
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to create iscsi target for volume "
+                          "id:%(vol_id)s: %(e)s"), {'vol_id': name, 'e': e})
+                raise exception.ISCSITargetCreateFailed(volume_id=vol_name)
+            try:
+                (out, _err) = self.scst_execute('-enable_target', name,
+                                                '-driver', self.target_driver)
+                LOG.debug("StdOut from scstadmin enable target: %s", out)
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to set 'enable' attribute for "
+                              "SCST target %s"), e)
+                raise exception.ISCSITargetHelperCommandFailed(
+                    error_mesage="Failed to enable SCST Target.")
+
+        if self.target_name:
+            if self._get_group() is None:
+                try:
+                    (out, _err) = self.scst_execute('-add_group', scst_group,
+                                                    '-driver',
+                                                    self.target_driver,
+                                                    '-target', name)
+                    LOG.debug("StdOut from scstadmin create group: %s", out)
+                except putils.ProcessExecutionError as e:
+                    LOG.error(_LE("Failed to create group to SCST target "
+                                  "%s"), e)
+                    raise exception.ISCSITargetHelperCommandFailed(
+                        error_message="Failed to create group to SCST target.")
+            try:
+                (out, _err) = self.scst_execute('-add_init',
+                                                self.initiator_iqn,
+                                                '-driver', self.target_driver,
+                                                '-target', name,
+                                                '-group', scst_group)
+                LOG.debug("StdOut from scstadmin add initiator: %s", out)
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to add initiator to group "
+                          " for SCST target %s"), e)
+                raise exception.ISCSITargetHelperCommandFailed(
+                    error_message="Failed to add Initiator to group for "
+                                  "SCST target.")
+
+        tid = self._get_target(name)
+        if self.target_name is None:
+            disk_id = "disk%s" % tid
+        else:
+            disk_id = "%s%s" % (lun, vol_id.split('-')[-1])
+
+        try:
+            self.scst_execute('-open_dev', disk_id,
+                              '-handler', 'vdisk_fileio',
+                              '-attributes', 'filename=%s' % path)
+        except putils.ProcessExecutionError as e:
+            LOG.error(_LE("Failed to add device to handler %s"), e)
+            raise exception.ISCSITargetHelperCommandFailed(
+                error_message="Failed to add device to SCST handler.")
+
+        try:
+            if self.target_name:
+                self.scst_execute('-add_lun', lun,
+                                  '-driver', self.target_driver,
+                                  '-target', name,
+                                  '-device', disk_id,
+                                  '-group', scst_group)
+            else:
+                self.scst_execute('-add_lun', lun,
+                                  '-driver', self.target_driver,
+                                  '-target', name,
+                                  '-device', disk_id)
+        except putils.ProcessExecutionError as e:
+            LOG.error(_LE("Failed to add lun to SCST target "
+                      "id:%(vol_id)s: %(e)s"), {'vol_id': name, 'e': e})
+            raise exception.ISCSITargetHelperCommandFailed(
+                error_message="Failed to add LUN to SCST Target for "
+                              "volume " + vol_name)
+
+            # SCST uses /etc/scst.conf as the default configuration when it
+            # starts
+        try:
+            self.scst_execute('-write_config', '/etc/scst.conf')
+        except putils.ProcessExecutionError as e:
+            LOG.error(_LE("Failed to write in /etc/scst.conf."))
+            raise exception.ISCSITargetHelperCommandFailed(
+                error_message="Failed to write in /etc/scst.conf.")
+
+        return tid
+
+    def _iscsi_location(self, ip, target, iqn, lun=None):
+        return "%s:%s,%s %s %s" % (ip, self.configuration.iscsi_port,
+                                   target, iqn, lun)
+
+    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)
+
+    def create_export(self, context, volume, volume_path):
+        """Creates an export for a logical volume."""
+        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_username = self.chap_username
+            chap_password = self.chap_password
+        else:
+            chap_username = vutils.generate_username()
+            chap_password = vutils.generate_password()
+
+        chap_auth = self._iscsi_authentication('IncomingUser', chap_username,
+                                               chap_password)
+        tid = self.create_iscsi_target(iscsi_name, volume['id'], iscsi_target,
+                                       lun, volume_path, chap_auth)
+
+        data = {}
+        data['location'] = self._iscsi_location(
+            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)
+        return data
+
+    def remove_export(self, context, volume):
+        try:
+            location = volume['provider_location'].split(' ')
+            iqn = location[1]
+            iscsi_target = self._get_target(iqn)
+            self.show_target(iscsi_target, iqn)
+
+        except Exception:
+            LOG.error(_LE("Skipping remove_export. No iscsi_target is"
+                          "presently exported for volume: %s"), volume['id'])
+            return
+        vol = self.db.volume_get(context, volume['id'])
+        lun = "".join(vol['provider_location'].split(" ")[-1:])
+
+        self.remove_iscsi_target(iscsi_target, lun,
+                                 volume['id'], volume['name'])
+
+    def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs):
+        disk_id = "%s%s" % (lun, vol_id.split('-')[-1])
+        vol_uuid_file = vol_name
+        if self.target_name is None:
+            iqn = '%s%s' % (self.iscsi_target_prefix, vol_uuid_file)
+        else:
+            iqn = self.target_name
+
+        if self.target_name is None:
+            try:
+                self.scst_execute('-noprompt',
+                                  '-rem_target', iqn,
+                                  '-driver', 'iscsi')
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to remove iscsi target for volume "
+                          "id:%(vol_id)s: %(e)s"), {'vol_id': vol_id, 'e': e})
+                raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
+            try:
+                self.scst_execute('-noprompt',
+                                  '-close_dev', "disk%s" % tid,
+                                  '-handler', 'vdisk_fileio')
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to close disk device %s"), e)
+                raise exception.ISCSITargetHelperCommandFailed(
+                    error_message="Failed to close disk device for "
+                                  "SCST handler.")
+
+            if self._get_target(iqn):
+                try:
+                    self.scst_execute('-noprompt',
+                                      '-rem_target', iqn,
+                                      '-driver', self.target_driver)
+                except putils.ProcessExecutionError as e:
+                    LOG.error(_LE("Failed to remove iscsi target for "
+                                  "volume id:%(vol_id)s: %(e)s"),
+                              {'vol_id': vol_id, 'e': e})
+                    raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
+        else:
+            if not int(lun) in self._get_luns_info():
+                raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
+            try:
+                self.scst_execute('-noprompt', '-rem_lun', lun,
+                                  '-driver', self.target_driver,
+                                  '-target', iqn, '-group',
+                                  self.remove_initiator_iqn)
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to remove LUN %s"), e)
+                raise exception.ISCSITargetHelperCommandFailed(
+                    error_message="Failed to remove LUN for SCST Target.")
+
+            try:
+                self.scst_execute('-noprompt',
+                                  '-close_dev', disk_id,
+                                  '-handler', 'vdisk_fileio')
+            except putils.ProcessExecutionError as e:
+                LOG.error(_LE("Failed to close disk device %s"), e)
+                raise exception.ISCSITargetHelperCommandFailed(
+                    error_message="Failed to close disk device for "
+                                  "SCST handler.")
+
+        self.scst_execute('-write_config', '/etc/scst.conf')
+
+    def show_target(self, tid, iqn):
+        if iqn is None:
+            raise exception.InvalidParameterValue(
+                err=_('valid iqn needed for show_target'))
+
+        tid = self._get_target(iqn)
+        if tid is None:
+            raise exception.ISCSITargetHelperCommandFailed(
+                error_message="Target not found")
+
+    def initialize_connection(self, volume, connector):
+        iscsi_properties = self._get_iscsi_properties(volume)
+        return {
+            'driver_volume_type': 'iscsi',
+            'data': iscsi_properties
+        }
index beac6c4bab1a652efba24336dea5e4b2cd7b0d46..4d280eae5c82e86b21f786a556eda14198770561 100644 (file)
@@ -10,7 +10,7 @@ filters_path=/etc/cinder/rootwrap.d,/usr/share/cinder/rootwrap
 # explicitely specify a full path (separated by ',')
 # If not specified, defaults to system PATH environment variable.
 # These directories MUST all be only writeable by root !
-exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin,/usr/local/bin
+exec_dirs=/sbin,/usr/sbin,/bin,/usr/bin,/usr/local/bin,/usr/local/sbin
 
 # Enable logging to syslog
 # Default value is False
index a90f8ded16b7ec5637f0e735d19369b3075e8fae..1af08f3f5c313016e4e560ea77c25f26742c6096 100644 (file)
@@ -7,6 +7,7 @@ ietadm: CommandFilter, ietadm, root
 tgtadm: CommandFilter, tgtadm, root
 tgt-admin: CommandFilter, tgt-admin, root
 cinder-rtstool: CommandFilter, cinder-rtstool, root
+scstadmin: CommandFilter, scstadmin, root
 
 # LVM related show commands
 pvs: EnvFilter, env, root, LC_ALL=C, pvs