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
This commit is contained in:
parent
734f98f309
commit
e7e1b1466c
@ -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!")
|
||||
|
237
cinder/tests/targets/test_scst_driver.py
Normal file
237
cinder/tests/targets/test_scst_driver.py
Normal file
@ -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)
|
@ -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
|
||||
|
349
cinder/volume/targets/scst.py
Normal file
349
cinder/volume/targets/scst.py
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user