Fix disconnecting necessary iSCSI sessions issue

In Icehouse with "iscsi_use_multipath=true", detaching a multipath
iSCSI volume kills all iSCSI volumes visible from the nova compute node.
When we use different targets(IQNs) associated with same portal for
each different multipath device, all of the targets will be deleted
via disconnect_volume().

This patch fixes the behavior of detaching volume:

  1. Extract the targets for the detached multipath device.
  2. Delete/disconnect the targets for the detached multipath device.

Closes-Bug: #1382440

(cherry picked from commit 36aeedfd5e)

Conflicts:
        nova/tests/unit/virt/libvirt/test_volume.py

Change-Id: I38eafdaee03d136282cfde1fd013e322a4256cc4
This commit is contained in:
Hiroyuki Eguchi 2014-12-04 15:12:11 +09:00
parent 3dcdce39e5
commit 52103be3e1
2 changed files with 86 additions and 2 deletions

View File

@ -13,10 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
import contextlib
import os
import time
import fixtures
import mock
from oslo.config import cfg
from nova import exception
@ -480,6 +482,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
iscsi_devs = ['ip-%s-iscsi-%s-lun-0' % (self.location, self.iqn)]
self.stubs.Set(libvirt_driver, '_get_iscsi_devices',
lambda: iscsi_devs)
self.stubs.Set(libvirt_driver,
'_get_target_portals_from_iscsiadm_output',
lambda x: [[self.location, self.iqn]])
@ -540,6 +545,66 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
self.mox.ReplayAll()
libvirt_driver.disconnect_volume(connection_info, 'vde')
def test_libvirt_kvm_volume_with_multipath_disconnected(self):
libvirt_driver = volume.LibvirtISCSIVolumeDriver(self.fake_conn)
volumes = [{'name': self.name,
'location': self.location,
'iqn': self.iqn,
'mpdev_filepath': '/dev/mapper/disconnect'},
{'name': 'volume-00000002',
'location': '10.0.2.15:3260',
'iqn': 'iqn.2010-10.org.openstack:volume-00000002',
'mpdev_filepath': '/dev/mapper/donotdisconnect'}]
iscsi_devs = ['ip-%s-iscsi-%s-lun-1' % (volumes[0]['location'],
volumes[0]['iqn']),
'ip-%s-iscsi-%s-lun-1' % (volumes[1]['location'],
volumes[1]['iqn'])]
def _get_multipath_device_name(path):
if '%s-lun-1' % volumes[0]['iqn'] in path:
return volumes[0]['mpdev_filepath']
else:
return volumes[1]['mpdev_filepath']
def _get_multipath_iqn(mpdev):
if volumes[0]['mpdev_filepath'] == mpdev:
return volumes[0]['iqn']
else:
return volumes[1]['iqn']
with contextlib.nested(
mock.patch.object(os.path, 'exists', return_value=True),
mock.patch.object(self.fake_conn, 'get_all_block_devices',
retrun_value=[volumes[1]['mpdev_filepath']]),
mock.patch.object(libvirt_driver, '_get_multipath_device_name',
_get_multipath_device_name),
mock.patch.object(libvirt_driver, '_get_multipath_iqn',
_get_multipath_iqn),
mock.patch.object(libvirt_driver, '_get_iscsi_devices',
return_value=iscsi_devs),
mock.patch.object(libvirt_driver,
'_get_target_portals_from_iscsiadm_output',
return_value=[[volumes[0]['location'],
volumes[0]['iqn']],
[volumes[1]['location'],
volumes[1]['iqn']]]),
mock.patch.object(libvirt_driver, '_disconnect_mpath')
) as (mock_exists, mock_devices, mock_device_name, mock_get_iqn,
mock_iscsi_devices, mock_get_portals, mock_disconnect_mpath):
vol = {'id': 1, 'name': volumes[0]['name']}
connection_info = self.iscsi_connection(vol,
volumes[0]['location'],
volumes[0]['iqn'])
connection_info['data']['device_path'] =\
volumes[0]['mpdev_filepath']
libvirt_driver.use_multipath = True
libvirt_driver.disconnect_volume(connection_info, 'vde')
# Ensure that the mpath device is disconnected.
ips_iqns = []
ips_iqns.append([volumes[0]['location'], volumes[0]['iqn']])
mock_disconnect_mpath.assert_called_once_with(
connection_info['data'], ips_iqns)
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)
@ -585,6 +650,9 @@ class LibvirtVolumeTestCase(test.NoDBTestCase):
}
target_portals = ['fake_portal1', 'fake_portal2']
libvirt_driver._get_multipath_device_name = lambda x: mpdev_filepath
iscsi_devs = ['ip-%s-iscsi-%s-lun-0' % (location, iqn)]
self.stubs.Set(libvirt_driver, '_get_iscsi_devices',
lambda: iscsi_devs)
self.stubs.Set(libvirt_driver,
'_get_target_portals_from_iscsiadm_output',
lambda x: [[location, iqn]])

View File

@ -415,7 +415,23 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
check_exit_code=[0, 255])[0] \
or ""
ips_iqns = self._get_target_portals_from_iscsiadm_output(out)
# Extract targets for the current multipath device.
ips_iqns = []
entries = self._get_iscsi_devices()
for ip, iqn in self._get_target_portals_from_iscsiadm_output(out):
ip_iqn = "%s-iscsi-%s" % (ip.split(",")[0], iqn)
for entry in entries:
entry_ip_iqn = entry.split("-lun-")[0]
if entry_ip_iqn[:3] == "ip-":
entry_ip_iqn = entry_ip_iqn[3:]
if (ip_iqn != entry_ip_iqn):
continue
entry_real_path = os.path.realpath("/dev/disk/by-path/%s" %
entry)
entry_mpdev = self._get_multipath_device_name(entry_real_path)
if entry_mpdev == multipath_device:
ips_iqns.append([ip, iqn])
break
if not devices:
# disconnect if no other multipath devices