Merge "Hyper-V: Skip logging out in-use targets"

This commit is contained in:
Jenkins
2014-11-12 19:56:39 +00:00
committed by Gerrit Code Review
7 changed files with 129 additions and 44 deletions

View File

@@ -96,14 +96,41 @@ class BaseVolumeUtilsTestCase(test.NoDBTestCase):
self.assertEqual(mock.sentinel.FAKE_SESSION_ID, session_id)
def test_get_device_number_for_target(self):
def test_get_devices_for_target(self):
init_session = self._create_initiator_session()
self._volutils._conn_wmi.query.return_value = [init_session]
devices = self._volutils._get_devices_for_target(
mock.sentinel.FAKE_IQN)
self.assertEqual(init_session.Devices, devices)
def test_get_devices_for_target_not_found(self):
self._volutils._conn_wmi.query.return_value = None
devices = self._volutils._get_devices_for_target(
mock.sentinel.FAKE_IQN)
self.assertEqual(0, len(devices))
@mock.patch.object(basevolumeutils.BaseVolumeUtils,
'_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
device_number = self._volutils.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(basevolumeutils.BaseVolumeUtils,
'_get_devices_for_target')
def test_get_target_lun_count(self, fake_get_devices):
init_session = self._create_initiator_session()
fake_get_devices.return_value = [init_session]
lun_count = self._volutils.get_target_lun_count(
mock.sentinel.FAKE_IQN)
self.assertEqual(len(init_session.Devices), lun_count)
@mock.patch.object(basevolumeutils.BaseVolumeUtils,
"_get_drive_number_from_disk_path")
def test_get_target_from_disk_path(self, mock_get_session_id):

View File

@@ -229,6 +229,8 @@ class HyperVAPIBaseTestCase(test.NoDBTestCase):
'get_device_number_for_target')
self._mox.StubOutWithMock(basevolumeutils.BaseVolumeUtils,
'get_target_from_disk_path')
self._mox.StubOutWithMock(basevolumeutils.BaseVolumeUtils,
'get_target_lun_count')
self._mox.StubOutWithMock(volumeutils.VolumeUtils,
'login_storage_target')
@@ -639,6 +641,10 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
self._mox.VerifyAll()
def _setup_destroy_mocks(self, destroy_disks=True):
fake_volume_drives = ['fake_volume_drive']
fake_target_iqn = 'fake_target_iqn'
fake_target_lun = 'fake_target_lun'
m = vmutils.VMUtils.vm_exists(mox.Func(self._check_instance_name))
m.AndReturn(True)
@@ -648,10 +654,16 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
self._setup_delete_vm_log_mocks()
m = vmutils.VMUtils.get_vm_storage_paths(func)
m.AndReturn(([], []))
m.AndReturn(([], fake_volume_drives))
vmutils.VMUtils.destroy_vm(func)
m = self._conn._volumeops.get_target_from_disk_path(
fake_volume_drives[0])
m.AndReturn((fake_target_iqn, fake_target_lun))
self._mock_logout_storage_target(fake_target_iqn)
if destroy_disks:
m = fake.PathUtils.get_instance_dir(mox.IsA(str),
create_dir=False,
@@ -678,11 +690,16 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
def test_live_migration_with_volumes(self):
self._test_live_migration(with_volumes=True)
def test_live_migration_with_multiple_luns_per_target(self):
self._test_live_migration(with_volumes=True,
other_luns_available=True)
def test_live_migration_with_target_failure(self):
self._test_live_migration(test_failure=True)
def _test_live_migration(self, test_failure=False,
with_volumes=False,
other_luns_available=False,
unsupported_os=False):
dest_server = 'fake_server'
@@ -700,7 +717,7 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
if with_volumes:
fake_target_iqn = 'fake_target_iqn'
fake_target_lun = 1
fake_target_lun_count = 1
if not unsupported_os:
m = fake.PathUtils.get_vm_console_log_paths(mox.IsA(str))
@@ -725,10 +742,12 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
m.AndRaise(vmutils.HyperVException('Simulated failure'))
if with_volumes:
m.AndReturn([(fake_target_iqn, fake_target_lun)])
volumeutils.VolumeUtils.logout_storage_target(fake_target_iqn)
m.AndReturn({fake_target_iqn: fake_target_lun_count})
self._mock_logout_storage_target(fake_target_iqn,
other_luns_available)
else:
m.AndReturn([])
m.AndReturn({})
self._mox.ReplayAll()
try:
@@ -1349,7 +1368,7 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
fake_mounted_disk,
fake_device_number)
volumeutils.VolumeUtils.logout_storage_target(target_iqn)
self._mock_logout_storage_target(target_iqn)
def test_attach_volume_logout(self):
instance_data = self._get_instance_data()
@@ -1386,7 +1405,8 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
self.assertRaises(vmutils.HyperVException, self._conn.attach_volume,
None, connection_info, instance_data, mount_point)
def _mock_detach_volume(self, target_iqn, target_lun):
def _mock_detach_volume(self, target_iqn, target_lun,
other_luns_available=False):
fake_mounted_disk = "fake_mounted_disk"
fake_device_number = 0
m = volumeutils.VolumeUtils.get_device_number_for_target(target_iqn,
@@ -1399,9 +1419,18 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
vmutils.VMUtils.detach_vm_disk(mox.IsA(str), fake_mounted_disk)
volumeutils.VolumeUtils.logout_storage_target(mox.IsA(str))
self._mock_logout_storage_target(target_iqn, other_luns_available)
def test_detach_volume(self):
def _mock_logout_storage_target(self, target_iqn,
other_luns_available=False):
m = volumeutils.VolumeUtils.get_target_lun_count(target_iqn)
m.AndReturn(1 + int(other_luns_available))
if not other_luns_available:
volumeutils.VolumeUtils.logout_storage_target(target_iqn)
def _test_detach_volume(self, other_luns_available=False):
instance_data = self._get_instance_data()
self.assertIn('name', instance_data)
@@ -1414,12 +1443,18 @@ class HyperVAPITestCase(HyperVAPIBaseTestCase):
mount_point = '/dev/sdc'
self._mock_detach_volume(target_iqn, target_lun)
self._mock_detach_volume(target_iqn, target_lun, other_luns_available)
self._mox.ReplayAll()
self._conn.detach_volume(connection_info, instance_data, mount_point)
self._mox.VerifyAll()
def test_detach_volume(self):
self._test_detach_volume()
def test_detach_volume_multiple_luns_per_target(self):
# The iSCSI target should not be disconnected in this case.
self._test_detach_volume(other_luns_available=True)
def test_boot_from_volume(self):
block_device_info = db_fakes.get_fake_block_device_info(
self._volume_target_portal, self._volume_id)

View File

@@ -118,23 +118,27 @@ class BaseVolumeUtils(object):
if device_number == drive_number:
return initiator_session.SessionId
def get_device_number_for_target(self, target_iqn, target_lun):
def _get_devices_for_target(self, target_iqn):
initiator_sessions = self._conn_wmi.query("SELECT * FROM "
"MSiSCSIInitiator_Session"
"Class WHERE TargetName='%s'"
% target_iqn)
if not initiator_sessions:
return None
return []
devices = initiator_sessions[0].Devices
return initiator_sessions[0].Devices
if not devices:
return None
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)
return len(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)

View File

@@ -68,8 +68,9 @@ class LiveMigrationOps(object):
self._vmops.copy_vm_console_logs(instance_name, dest)
iscsi_targets = self._livemigrutils.live_migrate_vm(instance_name,
dest)
for (target_iqn, target_lun) in iscsi_targets:
self._volumeops.logout_storage_target(target_iqn)
for target_iqn, target_luns_count in iscsi_targets.items():
self._volumeops.logout_storage_target(target_iqn,
target_luns_count)
except Exception:
with excutils.save_and_reraise_exception():
LOG.debug("Calling live migration recover_method "

View File

@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import sys
if sys.platform == 'win32':
@@ -120,18 +121,23 @@ class LiveMigrationUtils(object):
volutils_remote = volumeutilsv2.VolumeUtilsV2(dest_host)
disk_paths_remote = {}
iscsi_targets = []
iscsi_targets = collections.defaultdict(int)
for (rasd_rel_path, disk_path) in disk_paths.items():
(target_iqn,
target_lun) = self._volutils.get_target_from_disk_path(disk_path)
iscsi_targets.append((target_iqn, target_lun))
target = self._volutils.get_target_from_disk_path(disk_path)
if target:
(target_iqn, target_lun) = target
dev_num = volutils_remote.get_device_number_for_target(target_iqn,
target_lun)
disk_path_remote = vmutils_remote.get_mounted_disk_by_drive_number(
dev_num)
iscsi_targets[target_iqn] += 1
disk_paths_remote[rasd_rel_path] = disk_path_remote
dev_num = volutils_remote.get_device_number_for_target(
target_iqn, target_lun)
disk_path_remote = (
vmutils_remote.get_mounted_disk_by_drive_number(dev_num))
disk_paths_remote[rasd_rel_path] = disk_path_remote
else:
LOG.debug("Could not retrieve iSCSI target "
"from disk path: %s", disk_path)
return (disk_paths_remote, iscsi_targets)
@@ -223,7 +229,7 @@ class LiveMigrationUtils(object):
rmt_ip_addr_list = self._get_remote_ip_address_list(conn_v2_remote,
dest_host)
iscsi_targets = []
iscsi_targets = {}
planned_vm = None
disk_paths = self._get_physical_disk_paths(vm_name)
if disk_paths:

View File

@@ -390,10 +390,6 @@ class VMOps(object):
except KeyError:
raise exception.InvalidDiskFormat(disk_format=configdrive_ext)
def _disconnect_volumes(self, volume_drives):
for volume_drive in volume_drives:
self._volumeops.disconnect_volume(volume_drive)
def _delete_disk_files(self, instance_name):
self._pathutils.get_instance_dir(instance_name,
create_dir=False,
@@ -413,7 +409,7 @@ class VMOps(object):
(disk_files, volume_drives) = storage
self._vmutils.destroy_vm(instance_name)
self._disconnect_volumes(volume_drives)
self._volumeops.disconnect_volumes(volume_drives)
else:
LOG.debug("Instance not found", instance=instance)

View File

@@ -17,6 +17,7 @@
"""
Management class for Storage-related functions (attach, detach, etc).
"""
import collections
import time
from oslo.config import cfg
@@ -152,7 +153,7 @@ class VolumeOps(object):
LOG.error(_LE('Unable to attach volume to instance %s'),
instance_name)
if target_iqn:
self._volutils.logout_storage_target(target_iqn)
self.logout_storage_target(target_iqn)
def _get_free_controller_slot(self, scsi_controller_path):
attached_disks = self._vmutils.get_attached_disks(scsi_controller_path)
@@ -168,9 +169,16 @@ class VolumeOps(object):
for vol in mapping:
self.detach_volume(vol['connection_info'], instance_name)
def logout_storage_target(self, target_iqn):
LOG.debug("Logging off storage target %s", target_iqn)
self._volutils.logout_storage_target(target_iqn)
def logout_storage_target(self, target_iqn, disconnected_luns_count=1):
total_available_luns = self._volutils.get_target_lun_count(
target_iqn)
if total_available_luns == disconnected_luns_count:
LOG.debug("Logging off storage target %s", target_iqn)
self._volutils.logout_storage_target(target_iqn)
else:
LOG.debug("Skipping disconnecting target %s as there "
"are LUNs still being used.", target_iqn)
def detach_volume(self, connection_info, instance_name):
"""Detach a volume to the SCSI controller."""
@@ -241,12 +249,20 @@ class VolumeOps(object):
'for target_iqn: %s') % target_iqn)
return mounted_disk_path
def disconnect_volume(self, physical_drive_path):
# Get the session_id of the ISCSI connection
session_id = self._volutils.get_session_id_from_mounted_disk(
physical_drive_path)
# Logging out the target
self._volutils.execute_log_out(session_id)
def disconnect_volumes(self, volume_drives):
targets = collections.defaultdict(int)
for volume_drive in volume_drives:
target = self._volutils.get_target_from_disk_path(
volume_drive)
if target:
target_iqn = target[0]
targets[target_iqn] += 1
else:
LOG.debug("Could not retrieve iSCSI target from disk path: ",
volume_drive)
for target_iqn in targets:
self.logout_storage_target(target_iqn, targets[target_iqn])
def get_target_from_disk_path(self, physical_drive_path):
return self._volutils.get_target_from_disk_path(physical_drive_path)