Remove old iSCSI initiator utils modules

Now that we have an iSCSI initiator module that uses the iscsidsc
lib directly, there is no need to keep the old modules that were
using iscsicli.exe or WMI for interacting with the MS iSCSI
initiator.

Nova stopped using the old modules since Mitaka. As 2 cycles have
passed, we can now completely remove them along with the according
config option.

Change-Id: Id514a15646aaacb8f90c998ae51381e2a868edd1
This commit is contained in:
Lucian Petrut
2016-01-08 17:02:00 +02:00
parent 76d324ed95
commit 4f05788b66
8 changed files with 3 additions and 915 deletions

View File

@@ -32,7 +32,6 @@ from os_win.utils import hostutils
from os_win.utils.network import networkutils
from os_win.utils import pathutils
from os_win.utils.storage import diskutils
from os_win.utils.storage.initiator import iscsi_cli_utils
from os_win.utils.storage.initiator import iscsi_utils
from os_win.utils.storage import smbutils
from os_win.utils.storage.virtdisk import vhdutils
@@ -107,25 +106,9 @@ class TestHyperVUtilsFactory(test_base.OsWinBaseTestCase):
self._check_get_class(expected_class=rdpconsoleutils.RDPConsoleUtils,
class_type='rdpconsoleutils')
@mock.patch.object(iscsi_utils.ISCSIInitiatorUtils, '__init__',
lambda *args, **kwargs: None)
def test_get_iscsi_initiator_utils(self):
self._test_get_initiator_utils(
expected_class=iscsi_utils.ISCSIInitiatorUtils)
def test_get_iscsi_initiator_utils_force_v1(self):
self._test_get_initiator_utils(
expected_class=iscsi_cli_utils.ISCSIInitiatorCLIUtils,
force_v1=True)
@mock.patch.object(utilsfactory.utils, 'get_windows_version')
def _test_get_initiator_utils(self, mock_get_windows_version,
expected_class, force_v1=False):
CONF.set_override('force_volumeutils_v1', force_v1, 'hyperv')
mock_get_windows_version.return_value = '6.2'
actual_class = type(utilsfactory.get_iscsi_initiator_utils())
self.assertEqual(expected_class, actual_class)
self._check_get_class(expected_class=iscsi_utils.ISCSIInitiatorUtils,
class_type='iscsi_initiator_utils')
@mock.patch('os_win.utils.storage.initiator.fc_utils.FCUtils')
def test_get_fc_utils(self, mock_cls_fcutils):

View File

@@ -1,181 +0,0 @@
# Copyright 2014 Cloudbase Solutions Srl
#
# All Rights Reserved.
#
# 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 mock
from os_win.tests.unit import test_base
from os_win.utils.storage.initiator import base_iscsi_utils
class BaseISCSIInitiatorUtilsTestCase(test_base.OsWinBaseTestCase):
"""Unit tests for the Hyper-V BaseISCSIInitiatorUtils class."""
_FAKE_COMPUTER_NAME = "fake_computer_name"
_FAKE_DOMAIN_NAME = "fake_domain_name"
_FAKE_INITIATOR_NAME = "fake_initiator_name"
_FAKE_INITIATOR_IQN_NAME = "iqn.1991-05.com.microsoft:fake_computer_name"
_FAKE_DISK_PATH = 'fake_path DeviceID="123\\\\2"'
_FAKE_MOUNT_DEVICE = '/dev/fake/mount'
_FAKE_DEVICE_NAME = '/dev/fake/path'
_FAKE_SWAP = {'device_name': _FAKE_DISK_PATH}
def setUp(self):
self._utils = base_iscsi_utils.BaseISCSIInitiatorUtils()
self._utils._conn_wmi = mock.MagicMock()
self._utils._conn_cimv2 = mock.MagicMock()
super(BaseISCSIInitiatorUtilsTestCase, self).setUp()
def test_get_iscsi_initiator_ok(self):
self._check_get_iscsi_initiator(
self._FAKE_INITIATOR_NAME)
def test_get_iscsi_initiator_exception(self):
initiator_name = "%(iqn)s.%(domain)s" % {
'iqn': self._FAKE_INITIATOR_IQN_NAME,
'domain': self._FAKE_DOMAIN_NAME
}
self._check_get_iscsi_initiator(initiator_name,
side_effect=Exception)
def _check_get_iscsi_initiator(self, expected=None, side_effect=None):
mock_computer = mock.MagicMock()
mock_computer.name = self._FAKE_COMPUTER_NAME
mock_computer.Domain = self._FAKE_DOMAIN_NAME
self._utils._conn_cimv2.Win32_ComputerSystem.return_value = [
mock_computer]
expected_key_path = (
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
"iSCSI\\Discovery")
with mock.patch.object(base_iscsi_utils,
'winreg', create=True) as mock_winreg:
mock_winreg.CloseKey.side_effect = side_effect
mock_winreg.QueryValueEx.return_value = [expected]
mock_winreg.OpenKey.return_value = mock.sentinel.key
initiator_name = self._utils.get_iscsi_initiator()
self.assertEqual(expected, initiator_name)
mock_winreg.OpenKey.assert_called_once_with(
mock_winreg.HKEY_LOCAL_MACHINE,
expected_key_path,
0,
mock_winreg.KEY_WOW64_64KEY + mock_winreg.KEY_ALL_ACCESS)
mock_winreg.QueryValueEx.assert_called_once_with(
mock.sentinel.key, "DefaultInitiatorName")
mock_winreg.CloseKey.assert_called_once_with(mock.sentinel.key)
def test_get_drive_number_from_disk_path(self):
fake_disk_path = (
'\\\\WIN-I5BTVHOIFGK\\root\\virtualization\\v2:Msvm_DiskDrive.'
'CreationClassName="Msvm_DiskDrive",DeviceID="Microsoft:353B3BE8-'
'310C-4cf4-839E-4E1B14616136\\\\1",SystemCreationClassName='
'"Msvm_ComputerSystem",SystemName="WIN-I5BTVHOIFGK"')
expected_disk_number = 1
ret_val = self._utils._get_drive_number_from_disk_path(
fake_disk_path)
self.assertEqual(expected_disk_number, ret_val)
def test_get_drive_number_not_found(self):
fake_disk_path = 'fake_disk_path'
ret_val = self._utils._get_drive_number_from_disk_path(
fake_disk_path)
self.assertFalse(ret_val)
@mock.patch.object(base_iscsi_utils.BaseISCSIInitiatorUtils,
"_get_drive_number_from_disk_path")
def test_get_session_id_from_mounted_disk(self, mock_get_session_id):
mock_get_session_id.return_value = mock.sentinel.FAKE_DEVICE_NUMBER
mock_initiator_session = self._create_initiator_session()
mock_ses_class = self._utils._conn_wmi.MSiSCSIInitiator_SessionClass
mock_ses_class.return_value = [mock_initiator_session]
session_id = self._utils.get_session_id_from_mounted_disk(
self._FAKE_DISK_PATH)
self.assertEqual(mock.sentinel.FAKE_SESSION_ID, session_id)
def test_get_devices_for_target(self):
init_session = self._create_initiator_session()
mock_ses_class = self._utils._conn_wmi.MSiSCSIInitiator_SessionClass
mock_ses_class.return_value = [init_session]
devices = self._utils._get_devices_for_target(
mock.sentinel.FAKE_IQN)
self.assertEqual(init_session.Devices, devices)
def test_get_devices_for_target_not_found(self):
mock_ses_class = self._utils._conn_wmi.MSiSCSIInitiator_SessionClass
mock_ses_class.return_value = []
devices = self._utils._get_devices_for_target(mock.sentinel.FAKE_IQN)
self.assertEqual(0, len(devices))
@mock.patch.object(base_iscsi_utils.BaseISCSIInitiatorUtils,
'_get_devices_for_target')
def test_get_device_number_for_target(self, fake_get_devices):
init_session = self._create_initiator_session()
fake_get_devices.return_value = init_session.Devices
mock_ses_class = self._utils._conn_wmi.MSiSCSIInitiator_SessionClass
mock_ses_class.return_value = [init_session]
device_number = self._utils.get_device_number_for_target(
mock.sentinel.FAKE_IQN, mock.sentinel.FAKE_LUN)
self.assertEqual(mock.sentinel.FAKE_DEVICE_NUMBER, device_number)
@mock.patch.object(base_iscsi_utils.BaseISCSIInitiatorUtils,
'_get_devices_for_target')
def test_get_target_lun_count(self, fake_get_devices):
init_session = self._create_initiator_session()
# Only disk devices are being counted.
disk_device = mock.Mock(DeviceType=self._utils._FILE_DEVICE_DISK)
init_session.Devices.append(disk_device)
fake_get_devices.return_value = init_session.Devices
lun_count = self._utils.get_target_lun_count(mock.sentinel.FAKE_IQN)
self.assertEqual(1, lun_count)
@mock.patch.object(base_iscsi_utils.BaseISCSIInitiatorUtils,
"_get_drive_number_from_disk_path")
def test_get_target_from_disk_path(self, mock_get_session_id):
mock_get_session_id.return_value = mock.sentinel.FAKE_DEVICE_NUMBER
init_sess = self._create_initiator_session()
mock_ses_class = self._utils._conn_wmi.MSiSCSIInitiator_SessionClass
mock_ses_class.return_value = [init_sess]
(target_name, scsi_lun) = self._utils.get_target_from_disk_path(
self._FAKE_DISK_PATH)
self.assertEqual(mock.sentinel.FAKE_TARGET_NAME, target_name)
self.assertEqual(mock.sentinel.FAKE_LUN, scsi_lun)
def _create_initiator_session(self):
device = mock.MagicMock()
device.ScsiLun = mock.sentinel.FAKE_LUN
device.DeviceNumber = mock.sentinel.FAKE_DEVICE_NUMBER
device.TargetName = mock.sentinel.FAKE_TARGET_NAME
init_session = mock.MagicMock()
init_session.Devices = [device]
init_session.SessionId = mock.sentinel.FAKE_SESSION_ID
return init_session

View File

@@ -1,160 +0,0 @@
# Copyright 2014 Cloudbase Solutions Srl
#
# All Rights Reserved.
#
# 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 mock
from oslotest import base
from os_win import exceptions
from os_win.utils.storage.initiator import iscsi_cli_utils
class ISCSIInitiatorCLIUtilsTestCase(base.BaseTestCase):
"""Unit tests for the Hyper-V ISCSIInitiatorCLIUtils class."""
_FAKE_PORTAL_ADDR = '10.1.1.1'
_FAKE_PORTAL_PORT = '3260'
_FAKE_LUN = 0
_FAKE_TARGET = 'iqn.2010-10.org.openstack:fake_target'
_FAKE_STDOUT_VALUE = 'The operation completed successfully'
def setUp(self):
super(ISCSIInitiatorCLIUtilsTestCase, self).setUp()
self._initiator = iscsi_cli_utils.ISCSIInitiatorCLIUtils()
self._initiator._conn_wmi = mock.MagicMock()
self._initiator._conn_cimv2 = mock.MagicMock()
def _test_login_target_portal(self, portal_connected):
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
self._FAKE_PORTAL_PORT)
self._initiator.execute = mock.MagicMock()
if portal_connected:
exec_output = 'Address and Socket: %s %s' % (
self._FAKE_PORTAL_ADDR, self._FAKE_PORTAL_PORT)
else:
exec_output = ''
self._initiator.execute.return_value = exec_output
self._initiator._login_target_portal(fake_portal)
call_list = self._initiator.execute.call_args_list
all_call_args = [arg for call in call_list for arg in call[0]]
if portal_connected:
self.assertIn('RefreshTargetPortal', all_call_args)
else:
self.assertIn('AddTargetPortal', all_call_args)
def test_login_connected_portal(self):
self._test_login_target_portal(True)
def test_login_new_portal(self):
self._test_login_target_portal(False)
@mock.patch.object(iscsi_cli_utils, 'CONF')
def _test_login_target(self, mock_CONF, target_connected=False,
raise_exception=False, use_chap=False):
mock_CONF.hyperv.volume_attach_retry_count = 4
mock_CONF.hyperv.volume_attach_retry_interval = 0
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
self._FAKE_PORTAL_PORT)
self._initiator.execute = mock.MagicMock()
self._initiator._login_target_portal = mock.MagicMock()
if use_chap:
username, password = (mock.sentinel.username,
mock.sentinel.password)
else:
username, password = None, None
if target_connected:
self._initiator.execute.return_value = self._FAKE_TARGET
elif raise_exception:
self._initiator.execute.return_value = ''
else:
self._initiator.execute.side_effect = (
['', '', '', self._FAKE_TARGET, ''])
if raise_exception:
self.assertRaises(exceptions.HyperVException,
self._initiator.login_storage_target,
self._FAKE_LUN, self._FAKE_TARGET,
fake_portal, username, password)
else:
self._initiator.login_storage_target(self._FAKE_LUN,
self._FAKE_TARGET,
fake_portal,
username, password)
if target_connected:
call_list = self._initiator.execute.call_args_list
all_call_args = [arg for call in call_list for arg in call[0]]
self.assertNotIn('qlogintarget', all_call_args)
else:
self._initiator.execute.assert_any_call(
'iscsicli.exe', 'qlogintarget',
self._FAKE_TARGET, username, password)
def test_login_connected_target(self):
self._test_login_target(target_connected=True)
def test_login_disconncted_target(self):
self._test_login_target()
def test_login_target_exception(self):
self._test_login_target(raise_exception=True)
def test_login_target_using_chap(self):
self._test_login_target(use_chap=True)
def _test_execute_wrapper(self, raise_exception):
fake_cmd = ('iscsicli.exe', 'ListTargetPortals')
if raise_exception:
output = 'fake error'
else:
output = 'The operation completed successfully'
with mock.patch('os_win._utils.execute') as fake_execute:
fake_execute.return_value = (output, None)
if raise_exception:
self.assertRaises(exceptions.HyperVException,
self._initiator.execute,
*fake_cmd)
else:
ret_val = self._initiator.execute(*fake_cmd)
self.assertEqual(output, ret_val)
def test_execute_raise_exception(self):
self._test_execute_wrapper(True)
def test_execute_exception(self):
self._test_execute_wrapper(False)
@mock.patch.object(iscsi_cli_utils, '_utils')
def test_logout_storage_target(self, mock_utils):
mock_utils.execute.return_value = (self._FAKE_STDOUT_VALUE,
mock.sentinel.FAKE_STDERR_VALUE)
session = mock.MagicMock()
session.SessionId = mock.sentinel.FAKE_SESSION_ID
self._initiator._conn_wmi.query.return_value = [session]
self._initiator.logout_storage_target(mock.sentinel.FAKE_IQN)
mock_utils.execute.assert_called_once_with(
'iscsicli.exe', 'logouttarget', mock.sentinel.FAKE_SESSION_ID)

View File

@@ -1,161 +0,0 @@
# Copyright 2014 Cloudbase Solutions Srl
#
# 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 mock
from os_win import exceptions
from os_win.tests.unit import test_base
from os_win.utils.storage.initiator import iscsi_wmi_utils
class ISCSIInitiatorWMIUtilsTestCase(test_base.OsWinBaseTestCase):
"""Unit tests for the Hyper-V ISCSIInitiatorWMIUtils class."""
_FAKE_PORTAL_ADDR = '10.1.1.1'
_FAKE_PORTAL_PORT = '3260'
_FAKE_LUN = 0
_FAKE_TARGET = 'iqn.2010-10.org.openstack:fake_target'
def setUp(self):
super(ISCSIInitiatorWMIUtilsTestCase, self).setUp()
self._initiator = iscsi_wmi_utils.ISCSIInitiatorWMIUtils()
self._initiator._conn_storage = mock.MagicMock()
self._initiator._conn_wmi = mock.MagicMock()
def _test_login_target_portal(self, portal_connected):
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
self._FAKE_PORTAL_PORT)
fake_portal_object = mock.MagicMock()
_query = self._initiator._conn_storage.query
self._initiator._conn_storage.MSFT_iSCSITargetPortal = (
fake_portal_object)
if portal_connected:
_query.return_value = [fake_portal_object]
else:
_query.return_value = None
self._initiator._login_target_portal(fake_portal)
if portal_connected:
fake_portal_object.Update.assert_called_once_with()
else:
fake_portal_object.New.assert_called_once_with(
TargetPortalAddress=self._FAKE_PORTAL_ADDR,
TargetPortalPortNumber=self._FAKE_PORTAL_PORT)
def test_login_connected_portal(self):
self._test_login_target_portal(True)
def test_login_new_portal(self):
self._test_login_target_portal(False)
@mock.patch.object(iscsi_wmi_utils, 'CONF')
def _test_login_target(self, mock_CONF, target_connected=False,
raise_exception=False, use_chap=False):
mock_CONF.hyperv.volume_attach_retry_count = 4
mock_CONF.hyperv.volume_attach_retry_interval = 0
fake_portal = '%s:%s' % (self._FAKE_PORTAL_ADDR,
self._FAKE_PORTAL_PORT)
fake_target_object = mock.MagicMock()
if target_connected:
fake_target_object.IsConnected = True
elif not raise_exception:
type(fake_target_object).IsConnected = mock.PropertyMock(
side_effect=[False, True])
else:
fake_target_object.IsConnected = False
_query = self._initiator._conn_storage.query
_query.return_value = [fake_target_object]
self._initiator._conn_storage.MSFT_iSCSITarget = (
fake_target_object)
if use_chap:
username, password = (mock.sentinel.username,
mock.sentinel.password)
auth = {
'AuthenticationType': self._initiator._CHAP_AUTH_TYPE,
'ChapUsername': username,
'ChapSecret': password,
}
else:
username, password = None, None
auth = {}
if raise_exception:
self.assertRaises(exceptions.HyperVException,
self._initiator.login_storage_target,
self._FAKE_LUN, self._FAKE_TARGET, fake_portal)
else:
self._initiator.login_storage_target(self._FAKE_LUN,
self._FAKE_TARGET,
fake_portal,
username, password)
if target_connected:
fake_target_object.Update.assert_called_with()
else:
fake_target_object.Connect.assert_called_once_with(
IsPersistent=True, NodeAddress=self._FAKE_TARGET, **auth)
def test_login_connected_target(self):
self._test_login_target(target_connected=True)
def test_login_disconncted_target(self):
self._test_login_target()
def test_login_target_exception(self):
self._test_login_target(raise_exception=True)
def test_login_target_using_chap(self):
self._test_login_target(use_chap=True)
def test_logout_storage_target(self):
mock_msft_target = self._initiator._conn_storage.MSFT_iSCSITarget
mock_msft_session = self._initiator._conn_storage.MSFT_iSCSISession
mock_target = mock.MagicMock()
mock_target.IsConnected = True
mock_msft_target.return_value = [mock_target]
mock_session = mock.MagicMock()
mock_session.IsPersistent = True
mock_msft_session.return_value = [mock_session]
self._initiator.logout_storage_target(self._FAKE_TARGET)
mock_msft_target.assert_called_once_with(NodeAddress=self._FAKE_TARGET)
mock_msft_session.assert_called_once_with(
TargetNodeAddress=self._FAKE_TARGET)
mock_session.Unregister.assert_called_once_with()
mock_target.Disconnect.assert_called_once_with()
@mock.patch.object(iscsi_wmi_utils.ISCSIInitiatorWMIUtils,
'logout_storage_target')
def test_execute_log_out(self, mock_logout_target):
sess_class = self._initiator._conn_wmi.MSiSCSIInitiator_SessionClass
mock_session = mock.MagicMock()
sess_class.return_value = [mock_session]
self._initiator.execute_log_out(mock.sentinel.FAKE_SESSION_ID)
sess_class.assert_called_once_with(
SessionId=mock.sentinel.FAKE_SESSION_ID)
mock_logout_target.assert_called_once_with(mock_session.TargetName)

View File

@@ -1,132 +0,0 @@
#
# Copyright 2012 Pedro Navarro Perez
# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# 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.
"""
Helper methods for operations related to the management of volumes,
and storage repositories
"""
import abc
import re
import sys
from oslo_log import log as logging
from os_win._i18n import _LI
from os_win.utils import baseutils
if sys.platform == 'win32':
from six.moves import winreg
LOG = logging.getLogger(__name__)
class BaseISCSIInitiatorUtils(baseutils.BaseUtils):
_FILE_DEVICE_DISK = 7
def __init__(self, host='.'):
self._conn_wmi = self._get_wmi_conn('//%s/root/wmi' % host)
self._conn_cimv2 = self._get_wmi_conn('//%s/root/cimv2' % host)
self._drive_number_regex = re.compile(r'DeviceID=\"[^,]*\\(\d+)\"')
@abc.abstractmethod
def login_storage_target(self, target_lun, target_iqn, target_portal):
pass
@abc.abstractmethod
def logout_storage_target(self, target_iqn):
pass
@abc.abstractmethod
def execute_log_out(self, session_id):
pass
def get_iscsi_initiator(self):
"""Get iscsi initiator name for this machine."""
computer_system = self._conn_cimv2.Win32_ComputerSystem()[0]
hostname = computer_system.name
keypath = ("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
"iSCSI\\Discovery")
try:
key = winreg.OpenKey(
winreg.HKEY_LOCAL_MACHINE,
keypath,
0,
winreg.KEY_WOW64_64KEY + winreg.KEY_ALL_ACCESS)
temp = winreg.QueryValueEx(key, 'DefaultInitiatorName')
initiator_name = str(temp[0])
winreg.CloseKey(key)
except Exception:
LOG.info(_LI("The ISCSI initiator name can't be found. "
"Choosing the default one"))
initiator_name = "iqn.1991-05.com.microsoft:" + hostname.lower()
if computer_system.PartofDomain:
initiator_name += '.' + computer_system.Domain.lower()
return initiator_name
def _get_drive_number_from_disk_path(self, disk_path):
drive_number = self._drive_number_regex.findall(disk_path)
if drive_number:
return int(drive_number[0])
def get_session_id_from_mounted_disk(self, physical_drive_path):
drive_number = self._get_drive_number_from_disk_path(
physical_drive_path)
if not drive_number:
return None
initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass()
for initiator_session in initiator_sessions:
devices = initiator_session.Devices
for device in devices:
device_number = device.DeviceNumber
if device_number == drive_number:
return initiator_session.SessionId
def _get_devices_for_target(self, target_iqn):
initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass(
TargetName=target_iqn)
if not initiator_sessions:
return []
return initiator_sessions[0].Devices
def get_device_number_for_target(self, target_iqn, target_lun):
devices = self._get_devices_for_target(target_iqn)
for device in devices:
if device.ScsiLun == target_lun:
return device.DeviceNumber
def get_target_lun_count(self, target_iqn):
devices = self._get_devices_for_target(target_iqn)
disk_devices = [device for device in devices
if device.DeviceType == self._FILE_DEVICE_DISK]
return len(disk_devices)
def get_target_from_disk_path(self, disk_path):
initiator_sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass()
drive_number = self._get_drive_number_from_disk_path(disk_path)
if not drive_number:
return None
for initiator_session in initiator_sessions:
devices = initiator_session.Devices
for device in devices:
if device.DeviceNumber == drive_number:
return (device.TargetName, device.ScsiLun)

View File

@@ -1,119 +0,0 @@
# Copyright 2012 Pedro Navarro Perez
# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# 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.
"""
Helper methods for operations related to the management of volumes,
and storage repositories
Official Microsoft iSCSI Initiator and iSCSI command line interface
documentation can be retrieved at:
http://www.microsoft.com/en-us/download/details.aspx?id=34750
"""
import re
import time
from oslo_config import cfg
from oslo_log import log as logging
from six.moves import range # noqa
from os_win._i18n import _
from os_win import _utils
from os_win import exceptions
from os_win.utils.storage.initiator import base_iscsi_utils
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class ISCSIInitiatorCLIUtils(base_iscsi_utils.BaseISCSIInitiatorUtils):
def execute(self, *args, **kwargs):
stdout_value, stderr_value = _utils.execute(*args, **kwargs)
if stdout_value.find('The operation completed successfully') == -1:
raise exceptions.HyperVException
(_('An error has occurred when calling the iscsi initiator: %s')
% stdout_value)
return stdout_value
def _login_target_portal(self, target_portal):
(target_address,
target_port) = _utils.parse_server_string(target_portal)
output = self.execute('iscsicli.exe', 'ListTargetPortals')
pattern = r'Address and Socket *: (.*)'
portals = [addr.split() for addr in re.findall(pattern, output)]
LOG.debug("Ensuring connection to portal: %s" % target_portal)
if [target_address, str(target_port)] in portals:
self.execute('iscsicli.exe', 'RefreshTargetPortal',
target_address, target_port)
else:
# Adding target portal to iscsi initiator. Sending targets
self.execute('iscsicli.exe', 'AddTargetPortal',
target_address, target_port,
'*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*',
'*', '*')
def login_storage_target(self, target_lun, target_iqn, target_portal,
auth_username=None, auth_password=None):
"""Ensure that the target is logged in."""
self._login_target_portal(target_portal)
# Listing targets
self.execute('iscsicli.exe', 'ListTargets')
retry_count = CONF.hyperv.volume_attach_retry_count
# If the target is not connected, at least two iterations are needed:
# one for performing the login and another one for checking if the
# target was logged in successfully.
if retry_count < 2:
retry_count = 2
for attempt in range(retry_count):
try:
session_info = self.execute('iscsicli.exe', 'SessionList')
if session_info.find(target_iqn) == -1:
# Sending login
self.execute('iscsicli.exe', 'qlogintarget', target_iqn,
auth_username, auth_password)
else:
return
except exceptions.HyperVException as exc:
LOG.debug("Attempt %(attempt)d to connect to target "
"%(target_iqn)s failed. Retrying. "
"Exceptipn: %(exc)s ",
{'target_iqn': target_iqn,
'exc': exc,
'attempt': attempt})
time.sleep(CONF.hyperv.volume_attach_retry_interval)
raise exceptions.HyperVException(_('Failed to login target %s') %
target_iqn)
def logout_storage_target(self, target_iqn):
"""Logs out storage target through its session id."""
sessions = self._conn_wmi.query("SELECT * FROM "
"MSiSCSIInitiator_SessionClass "
"WHERE TargetName='%s'" % target_iqn)
for session in sessions:
self.execute_log_out(session.SessionId)
def execute_log_out(self, session_id):
"""Executes log out of the session described by its session ID."""
self.execute('iscsicli.exe', 'logouttarget', session_id)

View File

@@ -1,127 +0,0 @@
# Copyright 2012 Pedro Navarro Perez
# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# 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.
"""
Helper methods for operations related to the management of volumes
and storage repositories on Windows Server 2012 and above
"""
import time
from oslo_config import cfg
from oslo_log import log as logging
from six.moves import range # noqa
from os_win._i18n import _
from os_win import _utils
from os_win import exceptions
from os_win.utils.storage.initiator import base_iscsi_utils
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class ISCSIInitiatorWMIUtils(base_iscsi_utils.BaseISCSIInitiatorUtils):
_CHAP_AUTH_TYPE = 'ONEWAYCHAP'
def __init__(self, host='.'):
super(ISCSIInitiatorWMIUtils, self).__init__(host)
storage_namespace = '//%s/root/microsoft/windows/storage' % host
self._conn_storage = self._get_wmi_conn(storage_namespace)
def _login_target_portal(self, target_portal):
(target_address,
target_port) = _utils.parse_server_string(target_portal)
# Checking if the portal is already connected.
portal = self._conn_storage.query("SELECT * FROM "
"MSFT_iSCSITargetPortal "
"WHERE TargetPortalAddress='%s' "
"AND TargetPortalPortNumber='%s'"
% (target_address, target_port))
if portal:
portal[0].Update()
else:
# Adding target portal to iscsi initiator. Sending targets
portal = self._conn_storage.MSFT_iSCSITargetPortal
portal.New(TargetPortalAddress=target_address,
TargetPortalPortNumber=target_port)
def login_storage_target(self, target_lun, target_iqn, target_portal,
auth_username=None, auth_password=None):
"""Ensure that the target is logged in."""
self._login_target_portal(target_portal)
retry_count = CONF.hyperv.volume_attach_retry_count
# If the target is not connected, at least two iterations are needed:
# one for performing the login and another one for checking if the
# target was logged in successfully.
if retry_count < 2:
retry_count = 2
for attempt in range(retry_count):
target = self._conn_storage.query("SELECT * FROM MSFT_iSCSITarget "
"WHERE NodeAddress='%s' " %
target_iqn)
if target and target[0].IsConnected:
if attempt == 0:
# The target was already connected but an update may be
# required
target[0].Update()
return
try:
target = self._conn_storage.MSFT_iSCSITarget
auth = {}
if auth_username and auth_password:
auth['AuthenticationType'] = self._CHAP_AUTH_TYPE
auth['ChapUsername'] = auth_username
auth['ChapSecret'] = auth_password
target.Connect(NodeAddress=target_iqn,
IsPersistent=True, **auth)
time.sleep(CONF.hyperv.volume_attach_retry_interval)
except exceptions.x_wmi as exc:
LOG.debug("Attempt %(attempt)d to connect to target "
"%(target_iqn)s failed. Retrying. "
"WMI exception: %(exc)s " %
{'target_iqn': target_iqn,
'exc': exc,
'attempt': attempt})
raise exceptions.HyperVException(_('Failed to login target %s') %
target_iqn)
def logout_storage_target(self, target_iqn):
"""Logs out storage target through its session id."""
targets = self._conn_storage.MSFT_iSCSITarget(NodeAddress=target_iqn)
if targets:
target = targets[0]
if target.IsConnected:
sessions = self._conn_storage.MSFT_iSCSISession(
TargetNodeAddress=target_iqn)
for session in sessions:
if session.IsPersistent:
session.Unregister()
target.Disconnect()
def execute_log_out(self, session_id):
sessions = self._conn_wmi.MSiSCSIInitiator_SessionClass(
SessionId=session_id)
if sessions:
self.logout_storage_target(sessions[0].TargetName)

View File

@@ -13,24 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from oslo_utils import importutils
from os_win._i18n import _, _LW # noqa
from os_win import exceptions
from os_win.utils import hostutils
from os_win.utils.io import namedpipe
from os_win.utils.storage.initiator import iscsi_cli_utils
hyper_opts = [
cfg.BoolOpt('force_volumeutils_v1',
default=False,
help='DEPRECATED: Force V1 volume utility class',
deprecated_for_removal=True),
]
CONF = cfg.CONF
CONF.register_opts(hyper_opts, 'hyperv')
utils = hostutils.HostUtils()
@@ -196,10 +184,7 @@ def get_pathutils():
return _get_class(class_type='pathutils')
def get_iscsi_initiator_utils(use_iscsi_cli=False):
use_iscsi_cli = use_iscsi_cli or CONF.hyperv.force_volumeutils_v1
if use_iscsi_cli:
return iscsi_cli_utils.ISCSIInitiatorCLIUtils()
def get_iscsi_initiator_utils():
return _get_class(class_type='iscsi_initiator_utils')