Implement IET target driver
When introducing Change-Id: I43190d1dac33748fe55fa00f260f32ab209be656, IET driver was not implemented. This patch adds IET target for new iscsi target driver model. Certification results: https://bugs.launchpad.net/cinder/+bug/1428758 Closes-Bug: #1409918 Closes-Bug: #1329139 Co-Authored-By: Anish Bhatt <anish@chelsio.com> Change-Id: I165a592bb3a39728fcc3d8ee4162b579c13ba928
This commit is contained in:
parent
ef6cacd3c1
commit
8bb865a0aa
|
@ -0,0 +1,283 @@
|
|||
# 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 contextlib
|
||||
import os
|
||||
import shutil
|
||||
import StringIO
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.openstack.common import fileutils
|
||||
from cinder import test
|
||||
from cinder import utils
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.targets import iet
|
||||
|
||||
|
||||
class TestIetAdmDriver(test.TestCase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestIetAdmDriver, self).__init__(*args, **kwargs)
|
||||
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_project_id = 'ed2c1fd4-5fc0-11e4-aa15-123b93f75cba'
|
||||
self.fake_volume_id = '83c2e877-feed-46be-8435-77884fe55b45'
|
||||
self.target = iet.IetAdm(root_helper=utils.get_root_helper(),
|
||||
configuration=self.configuration)
|
||||
self.testvol =\
|
||||
{'project_id': self.fake_project_id,
|
||||
'name': 'testvol',
|
||||
'size': 1,
|
||||
'id': self.fake_volume_id,
|
||||
'volume_type_id': None,
|
||||
'provider_location': '10.9.8.7:3260 '
|
||||
'iqn.2010-10.org.openstack:'
|
||||
'volume-%s 0' % self.fake_volume_id,
|
||||
'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
|
||||
'c76370d66b 2FE0CQ8J196R',
|
||||
'provider_geometry': '512 512',
|
||||
'created_at': timeutils.utcnow(),
|
||||
'host': 'fake_host@lvm#lvm'}
|
||||
|
||||
self.expected_iscsi_properties = \
|
||||
{'auth_method': 'CHAP',
|
||||
'auth_password': '2FE0CQ8J196R',
|
||||
'auth_username': 'stack-1-a60e2611875f40199931f2c76370d66b',
|
||||
'encrypted': False,
|
||||
'logical_block_size': '512',
|
||||
'physical_block_size': '512',
|
||||
'target_discovered': False,
|
||||
'target_iqn': 'iqn.2010-10.org.openstack:volume-%s' %
|
||||
self.fake_volume_id,
|
||||
'target_lun': 0,
|
||||
'target_portal': '10.10.7.1:3260',
|
||||
'volume_id': self.fake_volume_id}
|
||||
|
||||
def setUp(self):
|
||||
super(TestIetAdmDriver, self).setUp()
|
||||
self.fake_volumes_dir = tempfile.mkdtemp()
|
||||
fileutils.ensure_tree(self.fake_volumes_dir)
|
||||
self.addCleanup(self._cleanup)
|
||||
|
||||
self.exec_patcher = mock.patch.object(utils, 'execute')
|
||||
self.mock_execute = self.exec_patcher.start()
|
||||
self.addCleanup(self.exec_patcher.stop)
|
||||
|
||||
def _cleanup(self):
|
||||
if os.path.exists(self.fake_volumes_dir):
|
||||
shutil.rmtree(self.fake_volumes_dir)
|
||||
|
||||
def test_get_target(self):
|
||||
tmp_file = StringIO.StringIO()
|
||||
tmp_file.write(
|
||||
'tid:1 name:iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n' # noqa
|
||||
' sid:844427031282176 initiator:iqn.1994-05.com.redhat:5a6894679665\n' # noqa
|
||||
' cid:0 ip:10.9.8.7 state:active hd:none dd:none')
|
||||
tmp_file.seek(0)
|
||||
with mock.patch('__builtin__.open') as mock_open:
|
||||
mock_open.return_value = contextlib.closing(tmp_file)
|
||||
self.assertEqual('1',
|
||||
self.target._get_target(
|
||||
'iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45' # noqa
|
||||
))
|
||||
|
||||
# Test the failure case: Failed to handle the config file
|
||||
mock_open.side_effect = StandardError()
|
||||
self.assertRaises(StandardError,
|
||||
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 = StringIO.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)
|
||||
test_vol = ('iqn.2010-10.org.openstack:'
|
||||
'volume-83c2e877-feed-46be-8435-77884fe55b45')
|
||||
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, 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,
|
||||
test_vol)
|
||||
|
||||
@mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
|
||||
return_value=0)
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('os.path.exists', return_value=True)
|
||||
@mock.patch('cinder.utils.temporary_chown')
|
||||
def test_create_iscsi_target(self, mock_chown, mock_exists,
|
||||
mock_execute, mock_get_targ):
|
||||
mock_execute.return_value = ('', '')
|
||||
tmp_file = StringIO.StringIO()
|
||||
test_vol = ('iqn.2010-10.org.openstack:'
|
||||
'volume-83c2e877-feed-46be-8435-77884fe55b45')
|
||||
with mock.patch('__builtin__.open') as mock_open:
|
||||
mock_open.return_value = contextlib.closing(tmp_file)
|
||||
self.assertEqual(
|
||||
0,
|
||||
self.target.create_iscsi_target(
|
||||
test_vol,
|
||||
0,
|
||||
0,
|
||||
self.fake_volumes_dir))
|
||||
self.assertTrue(mock_execute.called)
|
||||
self.assertTrue(mock_open.called)
|
||||
self.assertTrue(mock_get_targ.called)
|
||||
|
||||
# Test the failure case: Failed to chown the config file
|
||||
mock_open.side_effect = putils.ProcessExecutionError
|
||||
self.assertRaises(exception.ISCSITargetCreateFailed,
|
||||
self.target.create_iscsi_target,
|
||||
test_vol,
|
||||
0,
|
||||
0,
|
||||
self.fake_volumes_dir)
|
||||
|
||||
# Test the failure case: Failed to set new auth
|
||||
mock_execute.side_effect = putils.ProcessExecutionError
|
||||
self.assertRaises(exception.ISCSITargetCreateFailed,
|
||||
self.target.create_iscsi_target,
|
||||
test_vol,
|
||||
0,
|
||||
0,
|
||||
self.fake_volumes_dir)
|
||||
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_update_config_file_failure(self, mock_exists, mock_execute):
|
||||
test_vol = ('iqn.2010-10.org.openstack:'
|
||||
'volume-83c2e877-feed-46be-8435-77884fe55b45')
|
||||
|
||||
# Test the failure case: conf file does not exist
|
||||
mock_exists.return_value = False
|
||||
mock_execute.side_effect = putils.ProcessExecutionError
|
||||
self.assertRaises(exception.ISCSITargetCreateFailed,
|
||||
self.target.update_config_file,
|
||||
test_vol,
|
||||
0,
|
||||
self.fake_volumes_dir,
|
||||
"foo bar")
|
||||
|
||||
@mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
|
||||
return_value=1)
|
||||
@mock.patch('cinder.utils.execute')
|
||||
def test_create_iscsi_target_already_exists(self, mock_execute,
|
||||
mock_get_targ):
|
||||
mock_execute.return_value = ('fake out', 'fake err')
|
||||
test_vol = 'iqn.2010-10.org.openstack:'\
|
||||
'volume-83c2e877-feed-46be-8435-77884fe55b45'
|
||||
self.assertEqual(
|
||||
1,
|
||||
self.target.create_iscsi_target(
|
||||
test_vol,
|
||||
1,
|
||||
0,
|
||||
self.fake_volumes_dir))
|
||||
self.assertTrue(mock_get_targ.called)
|
||||
self.assertTrue(mock_execute.called)
|
||||
|
||||
@mock.patch('cinder.volume.targets.iet.IetAdm._find_sid_cid_for_target',
|
||||
return_value=None)
|
||||
@mock.patch('os.path.exists', return_value=False)
|
||||
@mock.patch.object(utils, 'execute')
|
||||
def test_remove_iscsi_target(self, mock_execute, mock_exists, mock_find):
|
||||
|
||||
# Test the normal case
|
||||
self.target.remove_iscsi_target(1,
|
||||
0,
|
||||
self.testvol['id'],
|
||||
self.testvol['name'])
|
||||
mock_execute.assert_any_calls('ietadm',
|
||||
'--op',
|
||||
'delete',
|
||||
'--tid=1',
|
||||
run_as_root=True)
|
||||
|
||||
# Test the failure case: putils.ProcessExecutionError
|
||||
mock_execute.side_effect = putils.ProcessExecutionError
|
||||
self.assertRaises(exception.ISCSITargetRemoveFailed,
|
||||
self.target.remove_iscsi_target,
|
||||
1,
|
||||
0,
|
||||
self.testvol['id'],
|
||||
self.testvol['name'])
|
||||
|
||||
def test_find_sid_cid_for_target(self):
|
||||
tmp_file = StringIO.StringIO()
|
||||
tmp_file.write(
|
||||
'tid:1 name:iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45\n' # noqa
|
||||
' sid:844427031282176 initiator:iqn.1994-05.com.redhat:5a6894679665\n' # noqa
|
||||
' cid:0 ip:10.9.8.7 state:active hd:none dd:none')
|
||||
tmp_file.seek(0)
|
||||
with mock.patch('__builtin__.open') as mock_open:
|
||||
mock_open.return_value = contextlib.closing(tmp_file)
|
||||
self.assertEqual(('844427031282176', '0'),
|
||||
self.target._find_sid_cid_for_target(
|
||||
'1',
|
||||
'iqn.2010-10.org.openstack:volume-83c2e877-feed-46be-8435-77884fe55b45', # noqa
|
||||
'volume-83c2e877-feed-46be-8435-77884fe55b45' # noqa
|
||||
))
|
||||
|
||||
@mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
|
||||
return_value=1)
|
||||
@mock.patch('cinder.utils.execute')
|
||||
@mock.patch.object(iet.IetAdm, '_get_target_chap_auth')
|
||||
def test_create_export(self, mock_get_chap, mock_execute,
|
||||
mock_get_targ):
|
||||
mock_execute.return_value = ('', '')
|
||||
mock_get_chap.return_value = ('QZJbisGmn9AL954FNF4D',
|
||||
'P68eE7u9eFqDGexd28DQ')
|
||||
expected_result = {'location': '10.9.8.7:3260,1 '
|
||||
'iqn.2010-10.org.openstack:testvol 0',
|
||||
'auth': 'CHAP '
|
||||
'QZJbisGmn9AL954FNF4D P68eE7u9eFqDGexd28DQ'}
|
||||
ctxt = context.get_admin_context()
|
||||
self.assertEqual(expected_result,
|
||||
self.target.create_export(ctxt,
|
||||
self.testvol,
|
||||
self.fake_volumes_dir))
|
||||
self.assertTrue(mock_execute.called)
|
||||
|
||||
@mock.patch('cinder.volume.targets.iet.IetAdm._get_target',
|
||||
return_value=1)
|
||||
def test_ensure_export(self, mock_get_target):
|
||||
ctxt = context.get_admin_context()
|
||||
with mock.patch.object(self.target, 'create_iscsi_target'):
|
||||
self.target.ensure_export(ctxt,
|
||||
self.testvol,
|
||||
self.fake_volumes_dir)
|
||||
self.target.create_iscsi_target.assert_called_once_with(
|
||||
'iqn.2010-10.org.openstack:testvol',
|
||||
1, 0, self.fake_volumes_dir, None,
|
||||
check_exit_code=False,
|
||||
old_name=None)
|
|
@ -10,24 +10,307 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
|
||||
class IetAdm(object):
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log as logging
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _LI, _LE, _LW
|
||||
from cinder import utils
|
||||
from cinder.volume.targets import iscsi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IetAdm(iscsi.ISCSITarget):
|
||||
VERSION = '0.1'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(IetAdm, self).__init__(*args, **kwargs)
|
||||
self.iet_conf = self.configuration.safe_get('iet_conf')
|
||||
self.iscsi_iotype = self.configuration.safe_get('iscsi_iotype')
|
||||
self.auth_type = 'IncomingUser'
|
||||
self.iet_sessions = '/proc/net/iet/session'
|
||||
|
||||
def _get_target_chap_auth(self, name):
|
||||
def _get_target(self, iqn):
|
||||
|
||||
# Find existing iSCSI target session from /proc/net/iet/session
|
||||
#
|
||||
# tid:2 name:iqn.2010-10.org:volume-222
|
||||
# sid:562950561399296 initiator:iqn.1994-05.com:5a6894679665
|
||||
# cid:0 ip:192.168.122.1 state:active hd:none dd:none
|
||||
# tid:1 name:iqn.2010-10.org:volume-111
|
||||
# sid:281475567911424 initiator:iqn.1994-05.com:5a6894679665
|
||||
# cid:0 ip:192.168.122.1 state:active hd:none dd:none
|
||||
|
||||
iscsi_target = 0
|
||||
try:
|
||||
with open(self.iet_sessions, 'r') as f:
|
||||
sessions = f.read()
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed to open iet session list for %s"), iqn)
|
||||
raise
|
||||
|
||||
session_list = re.split('^tid:(?m)', sessions)[1:]
|
||||
for ses in session_list:
|
||||
m = re.match('(\d+) name:(\S+)\s+', ses)
|
||||
if m and iqn in m.group(2):
|
||||
return m.group(1)
|
||||
|
||||
return iscsi_target
|
||||
|
||||
def _get_iscsi_target(self, context, vol_id):
|
||||
pass
|
||||
|
||||
def ensure_export(self, context, volume, volume_path):
|
||||
pass
|
||||
def _get_target_and_lun(self, context, volume):
|
||||
|
||||
def create_export(self, context, volume, volume_path):
|
||||
pass
|
||||
# For ietadm dev starts at lun 0
|
||||
lun = 0
|
||||
|
||||
def remove_export(self, context, volume):
|
||||
pass
|
||||
# Using 0, ietadm tries to search empty tid for creating
|
||||
# new iSCSI target
|
||||
iscsi_target = 0
|
||||
|
||||
def initialize_connection(self, volume, connector):
|
||||
pass
|
||||
# Find existing iSCSI target based on iqn
|
||||
iqn = '%svolume-%s' % (self.iscsi_target_prefix, volume['id'])
|
||||
iscsi_target = self._get_target(iqn)
|
||||
|
||||
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.warn(_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):
|
||||
|
||||
config_auth = None
|
||||
vol_id = name.split(':')[1]
|
||||
|
||||
# Check the target is already existing.
|
||||
tmp_tid = self._get_target(name)
|
||||
|
||||
# Create a new iSCSI target. If a target already exists,
|
||||
# the command returns 234, but we ignore it.
|
||||
try:
|
||||
self._new_target(name, tid)
|
||||
tid = self._get_target(name)
|
||||
self._new_logicalunit(tid, lun, path)
|
||||
|
||||
if chap_auth is not None:
|
||||
(username, password) = chap_auth
|
||||
config_auth = ' '.join((self.auth_type,) + chap_auth)
|
||||
self._new_auth(tid, self.auth_type, username, password)
|
||||
except putils.ProcessExecutionError:
|
||||
LOG.exception(_LE("Failed to create iscsi target for volume "
|
||||
"id:%s"), vol_id)
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
|
||||
# Update config file only if new scsi target is created.
|
||||
if not tmp_tid:
|
||||
self.update_config_file(name, tid, path, config_auth)
|
||||
|
||||
return tid
|
||||
|
||||
def update_config_file(self, name, tid, path, config_auth):
|
||||
|
||||
conf_file = self.iet_conf
|
||||
vol_id = name.split(':')[1]
|
||||
|
||||
# If config file does not exist, create a blank conf file and
|
||||
# add configuration for the volume on the new file.
|
||||
if not os.path.exists(conf_file):
|
||||
try:
|
||||
utils.execute("truncate", conf_file, "--size=0",
|
||||
run_as_root=True)
|
||||
except putils.ProcessExecutionError:
|
||||
LOG.exception(_LE("Failed to create %(conf)s for volume "
|
||||
"id:%(vol_id)s"),
|
||||
{'conf': conf_file, 'vol_id': vol_id})
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
|
||||
try:
|
||||
volume_conf = """
|
||||
Target %s
|
||||
%s
|
||||
Lun 0 Path=%s,Type=%s
|
||||
""" % (name, config_auth, path, self._iotype(path))
|
||||
|
||||
with utils.temporary_chown(conf_file):
|
||||
with open(conf_file, 'a+') as f:
|
||||
f.write(volume_conf)
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed to update %(conf)s for volume "
|
||||
"id:%(vol_id)s"),
|
||||
{'conf': conf_file, 'vol_id': vol_id})
|
||||
raise exception.ISCSITargetCreateFailed(volume_id=vol_id)
|
||||
|
||||
def remove_iscsi_target(self, tid, lun, vol_id, vol_name, **kwargs):
|
||||
LOG.info(_LI("Removing iscsi_target for volume: %s"), vol_id)
|
||||
|
||||
try:
|
||||
self._delete_logicalunit(tid, lun)
|
||||
session_info = self._find_sid_cid_for_target(tid, vol_name, vol_id)
|
||||
if session_info:
|
||||
sid, cid = session_info
|
||||
self._force_delete_target(tid, sid, cid)
|
||||
|
||||
self._delete_target(tid)
|
||||
except putils.ProcessExecutionError:
|
||||
LOG.exception(_LE("Failed to remove iscsi target for volume "
|
||||
"id:%s"), vol_id)
|
||||
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
||||
|
||||
vol_uuid_file = vol_name
|
||||
conf_file = self.iet_conf
|
||||
if os.path.exists(conf_file):
|
||||
try:
|
||||
with utils.temporary_chown(conf_file):
|
||||
with open(conf_file, 'r+') as iet_conf_text:
|
||||
full_txt = iet_conf_text.readlines()
|
||||
new_iet_conf_txt = []
|
||||
count = 0
|
||||
for line in full_txt:
|
||||
if count > 0:
|
||||
count -= 1
|
||||
continue
|
||||
elif vol_uuid_file in line:
|
||||
count = 2
|
||||
continue
|
||||
else:
|
||||
new_iet_conf_txt.append(line)
|
||||
|
||||
iet_conf_text.seek(0)
|
||||
iet_conf_text.truncate(0)
|
||||
iet_conf_text.writelines(new_iet_conf_txt)
|
||||
except Exception:
|
||||
LOG.exception(_LE("Failed to update %(conf)s for volume id "
|
||||
"%(vol_id) after removing iscsi target"),
|
||||
{'conf': conf_file, 'vol_id': vol_id})
|
||||
raise exception.ISCSITargetRemoveFailed(volume_id=vol_id)
|
||||
else:
|
||||
LOG.warn(_LW("Failed to update %(conf)s for volume id %(vol_id) "
|
||||
"after removing iscsi target. "
|
||||
"%(conf)s does not exist."),
|
||||
{'conf': conf_file, 'vol_id': vol_id})
|
||||
|
||||
def _find_sid_cid_for_target(self, tid, name, vol_id):
|
||||
"""Find sid, cid for existing iscsi target"""
|
||||
|
||||
try:
|
||||
with open(self.iet_sessions, 'r') as f:
|
||||
sessions = f.read()
|
||||
except Exception as e:
|
||||
LOG.info(_LI("Failed to open iet session list for "
|
||||
"%(vol_id)s: %(e)s"),
|
||||
{'vol_id': vol_id, 'e': e})
|
||||
return None
|
||||
|
||||
session_list = re.split('^tid:(?m)', sessions)[1:]
|
||||
for ses in session_list:
|
||||
m = re.match('(\d+) name:(\S+)\s+sid:(\d+).+\s+cid:(\d+)', ses)
|
||||
if m and tid in m.group(1) and name in m.group(2):
|
||||
return m.group(3), m.group(4)
|
||||
|
||||
def _is_block(self, path):
|
||||
mode = os.stat(path).st_mode
|
||||
return stat.S_ISBLK(mode)
|
||||
|
||||
def _iotype(self, path):
|
||||
if self.iscsi_iotype == 'auto':
|
||||
return 'blockio' if self._is_block(path) else 'fileio'
|
||||
else:
|
||||
return self.iscsi_iotype
|
||||
|
||||
def _new_target(self, name, tid):
|
||||
"""Create new scsi target using specified parameters.
|
||||
|
||||
If the target already exists, ietadm returns
|
||||
'Invalid argument' and error code '234'.
|
||||
This should be ignored for ensure export case.
|
||||
"""
|
||||
utils.execute('ietadm', '--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--params', 'Name=%s' % name,
|
||||
run_as_root=True, check_exit_code=[0, 234])
|
||||
|
||||
def _delete_target(self, tid):
|
||||
utils.execute('ietadm', '--op', 'delete',
|
||||
'--tid=%s' % tid,
|
||||
run_as_root=True)
|
||||
|
||||
def _force_delete_target(self, tid, sid, cid):
|
||||
utils.execute('ietadm', '--op', 'delete',
|
||||
'--tid=%s' % tid,
|
||||
'--sid=%s' % sid,
|
||||
'--cid=%s' % cid,
|
||||
run_as_root=True)
|
||||
|
||||
def show_target(self, tid, iqn=None):
|
||||
utils.execute('ietadm', '--op', 'show',
|
||||
'--tid=%s' % tid,
|
||||
run_as_root=True)
|
||||
|
||||
def _new_logicalunit(self, tid, lun, path):
|
||||
"""Attach a new volume to scsi target as a logical unit.
|
||||
|
||||
If a logical unit exists on the specified target lun,
|
||||
ietadm returns 'File exists' and error code '239'.
|
||||
This should be ignored for ensure export case.
|
||||
"""
|
||||
|
||||
utils.execute('ietadm', '--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--lun=%d' % lun,
|
||||
'--params',
|
||||
'Path=%s,Type=%s' % (path, self._iotype(path)),
|
||||
run_as_root=True, check_exit_code=[0, 239])
|
||||
|
||||
def _delete_logicalunit(self, tid, lun):
|
||||
utils.execute('ietadm', '--op', 'delete',
|
||||
'--tid=%s' % tid,
|
||||
'--lun=%d' % lun,
|
||||
run_as_root=True)
|
||||
|
||||
def _new_auth(self, tid, type, username, password):
|
||||
utils.execute('ietadm', '--op', 'new',
|
||||
'--tid=%s' % tid,
|
||||
'--user',
|
||||
'--params=%s=%s,Password=%s' % (type,
|
||||
username,
|
||||
password),
|
||||
run_as_root=True)
|
||||
|
|
Loading…
Reference in New Issue