Fixed a problem in iSCSI multipath
Multipathing for attach volume and detach volume operations doesn't work properly if there are different targets associated with different portals for a mulitpath device. Change-Id: Iab316526cf64de60325d90043f73fa3e83fafb4e Closes-Bug: #1266051
This commit is contained in:
parent
7646c28315
commit
429ac4dedd
|
@ -480,6 +480,9 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
connection_info['data']['device_path'] = mpdev_filepath
|
||||
target_portals = ['fake_portal1', 'fake_portal2']
|
||||
libvirt_driver._get_multipath_device_name = lambda x: mpdev_filepath
|
||||
self.stubs.Set(libvirt_driver,
|
||||
'_get_target_portals_from_iscsiadm_output',
|
||||
lambda x: [[self.location, self.iqn]])
|
||||
conf = libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
self.assertEqual(tree.find('./source').get('dev'), mpdev_filepath)
|
||||
|
@ -521,6 +524,10 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
'%s-iscsi-%s-lun-2' % (location, iqn)]
|
||||
libvirt_driver._get_iscsi_devices = lambda: iscsi_devs
|
||||
|
||||
self.stubs.Set(libvirt_driver,
|
||||
'_get_target_portals_from_iscsiadm_output',
|
||||
lambda x: [[location, iqn]])
|
||||
|
||||
# Set up disconnect volume mock expectations
|
||||
self.mox.StubOutWithMock(libvirt_driver, '_delete_device')
|
||||
self.mox.StubOutWithMock(libvirt_driver, '_rescan_multipath')
|
||||
|
@ -549,6 +556,9 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
mpdev_filepath = '/dev/mapper/foo'
|
||||
target_portals = ['fake_portal1', 'fake_portal2']
|
||||
libvirt_driver._get_multipath_device_name = lambda x: mpdev_filepath
|
||||
self.stubs.Set(libvirt_driver,
|
||||
'_get_target_portals_from_iscsiadm_output',
|
||||
lambda x: [['fake_portal1', 'fake_iqn1']])
|
||||
conf = libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
self.assertEqual(tree.find('./source').get('dev'), mpdev_filepath)
|
||||
|
@ -574,6 +584,9 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
}
|
||||
target_portals = ['fake_portal1', 'fake_portal2']
|
||||
libvirt_driver._get_multipath_device_name = lambda x: mpdev_filepath
|
||||
self.stubs.Set(libvirt_driver,
|
||||
'_get_target_portals_from_iscsiadm_output',
|
||||
lambda x: [[location, iqn]])
|
||||
conf = libvirt_driver.connect_volume(connection_info, disk_info)
|
||||
tree = conf.format_dom()
|
||||
self.assertEqual(tree.find('./source').get('dev'), mpdev_filepath)
|
||||
|
@ -608,6 +621,9 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
}
|
||||
target_portals = ['fake_portal1', 'fake_portal2']
|
||||
libvirt_driver._get_multipath_device_name = lambda x: mpdev_filepath
|
||||
self.stubs.Set(libvirt_driver,
|
||||
'_get_target_portals_from_iscsiadm_output',
|
||||
lambda x: [['fake_portal1', 'fake_iqn1']])
|
||||
conf = libvirt_driver.connect_volume(connection_info, disk_info)
|
||||
tree = conf.format_dom()
|
||||
self.assertEqual(tree.find('./source').get('dev'), mpdev_filepath)
|
||||
|
|
|
@ -256,7 +256,8 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
return self._run_iscsiadm(iscsi_properties, iscsi_command, **kwargs)
|
||||
|
||||
def _get_target_portals_from_iscsiadm_output(self, output):
|
||||
return [line.split()[0] for line in output.splitlines()]
|
||||
# return both portals and iqns
|
||||
return [line.split() for line in output.splitlines()]
|
||||
|
||||
@utils.synchronized('connect_volume')
|
||||
def connect_volume(self, connection_info, disk_info):
|
||||
|
@ -280,9 +281,10 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
check_exit_code=[0, 255])[0] \
|
||||
or ""
|
||||
|
||||
for ip in self._get_target_portals_from_iscsiadm_output(out):
|
||||
for ip, iqn in self._get_target_portals_from_iscsiadm_output(out):
|
||||
props = iscsi_properties.copy()
|
||||
props['target_portal'] = ip
|
||||
props['target_iqn'] = iqn
|
||||
self._connect_to_iscsi_portal(props)
|
||||
|
||||
self._rescan_iscsi()
|
||||
|
@ -401,21 +403,46 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
if mpdev:
|
||||
devices.append(mpdev)
|
||||
|
||||
# Do a discovery to find all targets.
|
||||
# Targets for multiple paths for the same multipath device
|
||||
# may not be the same.
|
||||
out = self._run_iscsiadm_bare(['-m',
|
||||
'discovery',
|
||||
'-t',
|
||||
'sendtargets',
|
||||
'-p',
|
||||
iscsi_properties['target_portal']],
|
||||
check_exit_code=[0, 255])[0] \
|
||||
or ""
|
||||
|
||||
ips_iqns = self._get_target_portals_from_iscsiadm_output(out)
|
||||
|
||||
if not devices:
|
||||
# disconnect if no other multipath devices
|
||||
self._disconnect_mpath(iscsi_properties)
|
||||
self._disconnect_mpath(iscsi_properties, ips_iqns)
|
||||
return
|
||||
|
||||
# Get a target for all other multipath devices
|
||||
other_iqns = [self._get_multipath_iqn(device)
|
||||
for device in devices]
|
||||
# Get all the targets for the current multipath device
|
||||
current_iqns = [iqn for ip, iqn in ips_iqns]
|
||||
|
||||
if iscsi_properties['target_iqn'] not in other_iqns:
|
||||
in_use = False
|
||||
for current in current_iqns:
|
||||
if current in other_iqns:
|
||||
in_use = True
|
||||
break
|
||||
|
||||
# If no other multipath device attached has the same iqn
|
||||
# as the current device
|
||||
if not in_use:
|
||||
# disconnect if no other multipath devices with same iqn
|
||||
self._disconnect_mpath(iscsi_properties)
|
||||
self._disconnect_mpath(iscsi_properties, ips_iqns)
|
||||
return
|
||||
elif multipath_device not in devices:
|
||||
# delete the devices associated w/ the unused multipath
|
||||
self._delete_mpath(iscsi_properties, multipath_device)
|
||||
self._delete_mpath(iscsi_properties, multipath_device, ips_iqns)
|
||||
|
||||
# else do not disconnect iscsi portals,
|
||||
# as they are used for other luns,
|
||||
|
@ -512,23 +539,26 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
return []
|
||||
return [entry for entry in devices if entry.startswith("ip-")]
|
||||
|
||||
def _delete_mpath(self, iscsi_properties, multipath_device):
|
||||
def _delete_mpath(self, iscsi_properties, multipath_device, ips_iqns):
|
||||
entries = self._get_iscsi_devices()
|
||||
iqn_lun = '%s-lun-%s' % (iscsi_properties['target_iqn'],
|
||||
iscsi_properties.get('target_lun', 0))
|
||||
for dev in ['/dev/disk/by-path/%s' % dev for dev in entries
|
||||
if iqn_lun in dev]:
|
||||
self._delete_device(dev)
|
||||
# Loop through ips_iqns to construct all paths
|
||||
iqn_luns = []
|
||||
for ip, iqn in ips_iqns:
|
||||
iqn_lun = '%s-lun-%s' % (iqn,
|
||||
iscsi_properties.get('target_lun', 0))
|
||||
iqn_luns.append(iqn_lun)
|
||||
for dev in ['/dev/disk/by-path/%s' % dev for dev in entries]:
|
||||
for iqn_lun in iqn_luns:
|
||||
if iqn_lun in dev:
|
||||
self._delete_device(dev)
|
||||
|
||||
self._rescan_multipath()
|
||||
|
||||
def _disconnect_mpath(self, iscsi_properties):
|
||||
entries = self._get_iscsi_devices()
|
||||
ips = [ip.split("-")[1] for ip in entries
|
||||
if iscsi_properties['target_iqn'] in ip]
|
||||
for ip in ips:
|
||||
def _disconnect_mpath(self, iscsi_properties, ips_iqns):
|
||||
for ip, iqn in ips_iqns:
|
||||
props = iscsi_properties.copy()
|
||||
props['target_portal'] = ip
|
||||
props['target_iqn'] = iqn
|
||||
self._disconnect_from_iscsi_portal(props)
|
||||
|
||||
self._rescan_multipath()
|
||||
|
|
Loading…
Reference in New Issue