Delete iSCSI devices after volume detached
Previously, after detaching from a volume, the iSCSI device remains attached on the compute node until all LUNs for a given IQN are detached -- causing issues when LUNs are reused for different volumes. This change will delete the device(s) associated with the detached volume so LUNs can be reused. Fixes bug 1112483 Change-Id: Icae3ec4d1ee2036fbba7b9eb5c03a1c86014fcc0
This commit is contained in:
parent
62e549eb02
commit
8a6ee48087
|
@ -209,3 +209,6 @@ rpc.mountd: CommandFilter, rpc.mountd, root
|
|||
|
||||
# nova/virt/libvirt/utils.py:
|
||||
rbd: CommandFilter, rbd, root
|
||||
|
||||
# nova/virt/libvirt/volume.py: 'cp', '/dev/stdin', delete_control..
|
||||
cp: CommandFilter, cp, root
|
||||
|
|
|
@ -260,6 +260,8 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
('iscsiadm', '-m', 'node', '-T', self.iqn,
|
||||
'-p', self.location, '--op', 'update',
|
||||
'-n', 'node.startup', '-v', 'automatic'),
|
||||
('iscsiadm', '-m', 'node', '-T', self.iqn,
|
||||
'-p', self.location, '--rescan'),
|
||||
('iscsiadm', '-m', 'node', '-T', self.iqn,
|
||||
'-p', self.location, '--op', 'update',
|
||||
'-n', 'node.startup', '-v', 'manual'),
|
||||
|
@ -274,15 +276,16 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
# NOTE(vish) exists is to make driver assume connecting worked
|
||||
self.stubs.Set(os.path, 'exists', lambda x: True)
|
||||
libvirt_driver = volume.LibvirtISCSIVolumeDriver(self.fake_conn)
|
||||
devs = ['/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (self.location,
|
||||
name = 'volume-00000001'
|
||||
devs = ['/dev/disk/by-path/ip-%s-iscsi-%s-lun-2' % (self.location,
|
||||
self.iqn)]
|
||||
self.stubs.Set(self.fake_conn, 'get_all_block_devices', lambda: devs)
|
||||
vol = {'id': 1, 'name': self.name}
|
||||
connection_info = self.iscsi_connection(vol, self.location, self.iqn)
|
||||
conf = libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
dev_str = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (self.location,
|
||||
self.iqn)
|
||||
dev_name = 'ip-%s-iscsi-%s-lun-1' % (self.location, self.iqn)
|
||||
dev_str = '/dev/disk/by-path/%s' % dev_name
|
||||
self.assertEqual(tree.get('type'), 'block')
|
||||
self.assertEqual(tree.find('./source').get('dev'), dev_str)
|
||||
libvirt_driver.disconnect_volume(connection_info, "vde")
|
||||
|
@ -293,7 +296,11 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
'-p', self.location, '--login'),
|
||||
('iscsiadm', '-m', 'node', '-T', self.iqn,
|
||||
'-p', self.location, '--op', 'update',
|
||||
'-n', 'node.startup', '-v', 'automatic')]
|
||||
'-n', 'node.startup', '-v', 'automatic'),
|
||||
('iscsiadm', '-m', 'node', '-T', self.iqn,
|
||||
'-p', self.location, '--rescan'),
|
||||
('cp', '/dev/stdin',
|
||||
'/sys/block/%s/device/delete' % dev_name)]
|
||||
self.assertEqual(self.executes, expected_commands)
|
||||
|
||||
def iser_connection(self, volume, location, iqn):
|
||||
|
@ -481,6 +488,51 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
|
|||
expected_multipath_cmd = ('multipath', '-f', 'foo')
|
||||
self.assertIn(expected_multipath_cmd, self.executes)
|
||||
|
||||
def test_libvirt_kvm_volume_with_multipath_still_in_use(self):
|
||||
name = 'volume-00000001'
|
||||
location = '10.0.2.15:3260'
|
||||
iqn = 'iqn.2010-10.org.openstack:%s' % name
|
||||
mpdev_filepath = '/dev/mapper/foo'
|
||||
|
||||
def _get_multipath_device_name(path):
|
||||
if '%s-lun-1' % iqn in path:
|
||||
return mpdev_filepath
|
||||
return '/dev/mapper/donotdisconnect'
|
||||
|
||||
self.flags(iscsi_use_multipath=True, group='libvirt')
|
||||
self.stubs.Set(os.path, 'exists', lambda x: True)
|
||||
|
||||
libvirt_driver = volume.LibvirtISCSIVolumeDriver(self.fake_conn)
|
||||
libvirt_driver._get_multipath_device_name =\
|
||||
lambda x: _get_multipath_device_name(x)
|
||||
|
||||
block_devs = ['/dev/disks/by-path/%s-iscsi-%s-lun-2' % (location, iqn)]
|
||||
self.stubs.Set(self.fake_conn, 'get_all_block_devices',
|
||||
lambda: block_devs)
|
||||
|
||||
vol = {'id': 1, 'name': name}
|
||||
connection_info = self.iscsi_connection(vol, location, iqn)
|
||||
connection_info['data']['device_path'] = mpdev_filepath
|
||||
|
||||
libvirt_driver._get_multipath_iqn = lambda x: iqn
|
||||
|
||||
iscsi_devs = ['1.2.3.4-iscsi-%s-lun-1' % iqn,
|
||||
'%s-iscsi-%s-lun-1' % (location, iqn),
|
||||
'%s-iscsi-%s-lun-2' % (location, iqn)]
|
||||
libvirt_driver._get_iscsi_devices = lambda: iscsi_devs
|
||||
|
||||
# Set up disconnect volume mock expectations
|
||||
self.mox.StubOutWithMock(libvirt_driver, '_delete_device')
|
||||
self.mox.StubOutWithMock(libvirt_driver, '_rescan_multipath')
|
||||
libvirt_driver._rescan_multipath()
|
||||
libvirt_driver._delete_device('/dev/disk/by-path/%s' % iscsi_devs[0])
|
||||
libvirt_driver._delete_device('/dev/disk/by-path/%s' % iscsi_devs[1])
|
||||
libvirt_driver._rescan_multipath()
|
||||
|
||||
# Ensure that the mpath devices are deleted
|
||||
self.mox.ReplayAll()
|
||||
libvirt_driver.disconnect_volume(connection_info, 'vde')
|
||||
|
||||
def test_libvirt_kvm_volume_with_multipath_getmpdev(self):
|
||||
self.flags(iscsi_use_multipath=True, group='libvirt')
|
||||
self.stubs.Set(os.path, 'exists', lambda x: True)
|
||||
|
|
|
@ -290,6 +290,9 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
else:
|
||||
self._connect_to_iscsi_portal(iscsi_properties)
|
||||
|
||||
# Detect new/resized LUNs for existing sessions
|
||||
self._run_iscsiadm(iscsi_properties, ("--rescan",))
|
||||
|
||||
host_device = self._get_host_device(iscsi_properties)
|
||||
|
||||
# The /dev/disk/by-path/... node is not always present immediately
|
||||
|
@ -336,12 +339,9 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
def disconnect_volume(self, connection_info, disk_dev):
|
||||
"""Detach the volume from instance_name."""
|
||||
iscsi_properties = connection_info['data']
|
||||
host_device = self._get_host_device(iscsi_properties)
|
||||
multipath_device = None
|
||||
if self.use_multipath:
|
||||
host_device = ("/dev/disk/by-path/ip-%s-iscsi-%s-lun-%s" %
|
||||
(iscsi_properties['target_portal'],
|
||||
iscsi_properties['target_iqn'],
|
||||
iscsi_properties.get('target_lun', 0)))
|
||||
multipath_device = self._get_multipath_device_name(host_device)
|
||||
|
||||
super(LibvirtISCSIVolumeDriver,
|
||||
|
@ -360,6 +360,19 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
devices = [dev for dev in devices if dev.startswith(device_prefix)]
|
||||
if not devices:
|
||||
self._disconnect_from_iscsi_portal(iscsi_properties)
|
||||
elif host_device not in devices:
|
||||
# Delete device if LUN is not in use by another instance
|
||||
self._delete_device(host_device)
|
||||
|
||||
def _delete_device(self, device_path):
|
||||
device_name = os.path.basename(os.path.realpath(device_path))
|
||||
delete_control = '/sys/block/' + device_name + '/device/delete'
|
||||
if os.path.exists(delete_control):
|
||||
# Copy '1' from stdin to the device delete control file
|
||||
utils.execute('cp', '/dev/stdin', delete_control,
|
||||
process_input='1', run_as_root=True)
|
||||
else:
|
||||
LOG.warn(_("Unable to delete volume device %s"), device_name)
|
||||
|
||||
def _remove_multipath_device_descriptor(self, disk_descriptor):
|
||||
disk_descriptor = disk_descriptor.replace('/dev/mapper/', '')
|
||||
|
@ -401,6 +414,9 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
# disconnect if no other multipath devices with same iqn
|
||||
self._disconnect_mpath(iscsi_properties)
|
||||
return
|
||||
elif multipath_device not in devices:
|
||||
# delete the devices associated w/ the unused multipath
|
||||
self._delete_mpath(iscsi_properties, multipath_device)
|
||||
|
||||
# else do not disconnect iscsi portals,
|
||||
# as they are used for other luns,
|
||||
|
@ -497,6 +513,16 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
return []
|
||||
return [entry for entry in devices if entry.startswith("ip-")]
|
||||
|
||||
def _delete_mpath(self, iscsi_properties, multipath_device):
|
||||
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)
|
||||
|
||||
self._rescan_multipath()
|
||||
|
||||
def _disconnect_mpath(self, iscsi_properties):
|
||||
entries = self._get_iscsi_devices()
|
||||
ips = [ip.split("-")[1] for ip in entries
|
||||
|
|
Loading…
Reference in New Issue