From cca94d032b7ee72a5ff98aa664353ef50e877454 Mon Sep 17 00:00:00 2001 From: Hiroyuki Eguchi Date: Thu, 20 Nov 2014 10:41:36 +0900 Subject: [PATCH] Fix connecting unnecessary iSCSI sessions issue In Icehouse with "iscsi_use_multipath=true", attaching a multipath iSCSI volume may create unnecessary iSCSI sessions. The iscsiadm discovery command in connect_volume() returns all of the targets in the Cinder node, not just the ones related to the multipath volume which is specified by iqn. If the storage has many targets, connecting to all these volumes will also result in many unnecessary connections. There are two types of iSCSI multipath devices. One which shares the same iqn between multiple portals, and the other which use different iqns on different portals. connect_volume() needs to identify the type by checking iscsiadm the output if the iqn is used by multiple portals. This patch changes the behavior of attaching volume: 1. Identify the type by checking the iscsiadm output. 2. Connect to the correct targets by connect_to_iscsi_portal(). Closes-Bug: #1382440 (cherry picked from commit fb0de106f2f15604750bafc318ba06c41070cc35) Conflicts: nova/tests/unit/virt/libvirt/test_volume.py Change-Id: I488ad0c09bf26a609e27d67b9ef60b65bb45e0ad --- .../tests/virt/libvirt/test_libvirt_volume.py | 44 ++++++++++++++++++- nova/virt/libvirt/volume.py | 25 +++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/nova/tests/virt/libvirt/test_libvirt_volume.py b/nova/tests/virt/libvirt/test_libvirt_volume.py index c6333ce32667..449780d1c16b 100644 --- a/nova/tests/virt/libvirt/test_libvirt_volume.py +++ b/nova/tests/virt/libvirt/test_libvirt_volume.py @@ -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 @@ -491,6 +493,46 @@ class LibvirtVolumeTestCase(test.NoDBTestCase): expected_multipath_cmd = ('multipath', '-f', 'foo') self.assertIn(expected_multipath_cmd, self.executes) + def test_libvirt_kvm_volume_with_multipath_connecting(self): + libvirt_driver = volume.LibvirtISCSIVolumeDriver(self.fake_conn) + ip_iqns = [[self.location, self.iqn], + ['10.0.2.16:3260', self.iqn], + [self.location, + 'iqn.2010-10.org.openstack:volume-00000002']] + + with contextlib.nested( + mock.patch.object(os.path, 'exists', return_value=True), + mock.patch.object(libvirt_driver, '_run_iscsiadm_bare'), + mock.patch.object(libvirt_driver, + '_get_target_portals_from_iscsiadm_output', + return_value=ip_iqns), + mock.patch.object(libvirt_driver, '_connect_to_iscsi_portal'), + mock.patch.object(libvirt_driver, '_rescan_iscsi'), + mock.patch.object(libvirt_driver, '_get_host_device', + return_value='fake-device'), + mock.patch.object(libvirt_driver, '_rescan_multipath'), + mock.patch.object(libvirt_driver, '_get_multipath_device_name', + return_value='/dev/mapper/fake-mpath-devname') + ) as (mock_exists, mock_run_iscsiadm_bare, mock_get_portals, + mock_connect_iscsi, mock_rescan_iscsi, mock_host_device, + mock_rescan_multipath, mock_device_name): + vol = {'id': 1, 'name': self.name} + connection_info = self.iscsi_connection(vol, self.location, + self.iqn) + libvirt_driver.use_multipath = True + libvirt_driver.connect_volume(connection_info, self.disk_info) + + # Verify that the supplied iqn is used when it shares the same + # iqn between multiple portals. + connection_info = self.iscsi_connection(vol, self.location, + self.iqn) + props1 = connection_info['data'].copy() + props2 = connection_info['data'].copy() + props2['target_portal'] = '10.0.2.16:3260' + expected_calls = [mock.call(props1), mock.call(props2), + mock.call(props1)] + self.assertEqual(expected_calls, mock_connect_iscsi.call_args_list) + def test_libvirt_kvm_volume_with_multipath_still_in_use(self): name = 'volume-00000001' location = '10.0.2.15:3260' diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index 21f74e6b5746..232bea32c0dd 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -281,10 +281,29 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver): check_exit_code=[0, 255])[0] \ or "" - for ip, iqn in self._get_target_portals_from_iscsiadm_output(out): + # There are two types of iSCSI multipath devices. One which shares + # the same iqn between multiple portals, and the other which use + # different iqns on different portals. Try to identify the type by + # checking the iscsiadm output if the iqn is used by multiple + # portals. If it is, it's the former, so use the supplied iqn. + # Otherwise, it's the latter, so try the ip,iqn combinations to + # find the targets which constitutes the multipath device. + ips_iqns = self._get_target_portals_from_iscsiadm_output(out) + same_portal = False + all_portals = set() + match_portals = set() + for ip, iqn in ips_iqns: + all_portals.add(ip) + if iqn == iscsi_properties['target_iqn']: + match_portals.add(ip) + if len(all_portals) == len(match_portals): + same_portal = True + + for ip, iqn in ips_iqns: props = iscsi_properties.copy() - props['target_portal'] = ip - props['target_iqn'] = iqn + props['target_portal'] = ip.split(",")[0] + if not same_portal: + props['target_iqn'] = iqn self._connect_to_iscsi_portal(props) self._rescan_iscsi()