From: nikeshmahalka Date: Thu, 8 Jan 2015 13:08:19 +0000 (+0530) Subject: Add iSCSI SCST Target support to cinder X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=e7e1b1466c8886b5f84ad72fb9be7152f7993a56;p=openstack-build%2Fcinder-build.git Add iSCSI SCST Target support to cinder Currently cinder supports tgt,lio targets. We are adding SCST Target support in cinder. Change-Id: I449143d125808d28758f3d438d7d1476a544c38b Implements: blueprint scst-iscsi-backend-support --- diff --git a/cinder/exception.py b/cinder/exception.py index b236bc1a3..9cb256b82 100644 --- a/cinder/exception.py +++ b/cinder/exception.py @@ -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 index 000000000..9e8d41c2b --- /dev/null +++ b/cinder/tests/targets/test_scst_driver.py @@ -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) diff --git a/cinder/volume/driver.py b/cinder/volume/driver.py index dc7286e52..f6697c56b 100644 --- a/cinder/volume/driver.py +++ b/cinder/volume/driver.py @@ -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 index 000000000..78b9490af --- /dev/null +++ b/cinder/volume/targets/scst.py @@ -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 + } diff --git a/etc/cinder/rootwrap.conf b/etc/cinder/rootwrap.conf index beac6c4ba..4d280eae5 100644 --- a/etc/cinder/rootwrap.conf +++ b/etc/cinder/rootwrap.conf @@ -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 diff --git a/etc/cinder/rootwrap.d/volume.filters b/etc/cinder/rootwrap.d/volume.filters index a90f8ded1..1af08f3f5 100644 --- a/etc/cinder/rootwrap.d/volume.filters +++ b/etc/cinder/rootwrap.d/volume.filters @@ -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