From e6ea26ea1272f9407879b822b3913b4308e3f2f8 Mon Sep 17 00:00:00 2001 From: Navneet Singh Date: Thu, 27 Feb 2014 13:09:03 +0530 Subject: [PATCH] NetApp fix for controller preferred path This patch optimizes the data path by choosing the preferred path to access the volume by selecting the controller owning the volume. Closes-Bug: #1365881 Change-Id: I7069111dfd22d4fca92615b5730854f6d696ec13 --- cinder/tests/test_netapp_eseries_iscsi.py | 46 ++++++++++++++++++- cinder/volume/drivers/netapp/eseries/iscsi.py | 40 ++++++++++++---- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/cinder/tests/test_netapp_eseries_iscsi.py b/cinder/tests/test_netapp_eseries_iscsi.py index f345bb4021d..7b1784514fe 100644 --- a/cinder/tests/test_netapp_eseries_iscsi.py +++ b/cinder/tests/test_netapp_eseries_iscsi.py @@ -118,6 +118,32 @@ class FakeEseriesServerHandler(object): "currentControllerId": "070000000000000000000001", "protectionInformationCapable": false, "mapped": false, "reconPriority": 1, "protectionType": + "type1Protection"}, + {"extremeProtection": false, "pitBaseVolume": true, + "dssMaxSegmentSize": 131072, + "totalSizeInBytes": "1073741824", "raidLevel": "raid6", + "volumeRef": "0200000060080E500023BB34000003FB515C2293", + "listOfMappings": [], "sectorOffset": "15", + "id": "0200000060080E500023BB34000003FB515C2293", + "wwn": "60080E500023BB3400001FC352D14CB2", + "capacity": "2147483648", "mgmtClientAttribute": 0, + "label": "CFDXJ67BLJH25DXCZFZD4NSF54", + "volumeFull": false, + "blkSize": 512, "volumeCopyTarget": false, + "volumeGroupRef": + "0400000060080E500023BB3400001F9F52CECC3F", + "preferredControllerId": "070000000000000000000001", + "currentManager": "070000000000000000000001", + "applicationTagOwned": false, "status": "optimal", + "segmentSize": 131072, "volumeUse": "standardVolume", + "action": "none", "preferredManager": + "070000000000000000000001", "volumeHandle": 15, + "offline": false, "preReadRedundancyCheckEnabled": false, + "dssPreallocEnabled": false, "name": "bdm-vc-test-1", + "worldWideName": "60080E500023BB3400001FC352D14CB2", + "currentControllerId": "070000000000000000000001", + "protectionInformationCapable": false, "mapped": false, + "reconPriority": 1, "protectionType": "type1Protection"}]""" elif re.match("^/storage-systems/[0-9a-zA-Z]+/volumes/[0-9A-Za-z]+$", path): @@ -680,7 +706,7 @@ class NetAppEseriesIscsiDriverTestCase(test.TestCase): maps = [{'lunMappingRef': 'hdkjsdhjsdh', 'mapRef': '8400000060080E500023C73400300381515BFBA3', - 'volumeRef': 'CFDXJ67BLJH25DXCZFZD4NSF54', + 'volumeRef': '0200000060080E500023BB34000003FB515C2293', 'lun': 2}] self.driver._get_host_mapping_for_vol_frm_array = mock.Mock( return_value=maps) @@ -834,3 +860,21 @@ class NetAppEseriesIscsiDriverTestCase(test.TestCase): self.driver._create_volume, self.fake_eseries_pool_label, self.fake_eseries_volume_label, self.fake_size_gb) + + def test_portal_for_vol_controller(self): + volume = {'id': 'vol_id', 'currentManager': 'ctrl1'} + vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'} + portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'}, + {'controller': 'ctrl1', 'iqn': 'iqn1'}] + portal = self.driver._get_iscsi_portal_for_vol(volume, portals) + self.assertEqual(portal, {'controller': 'ctrl1', 'iqn': 'iqn1'}) + portal = self.driver._get_iscsi_portal_for_vol(vol_nomatch, portals) + self.assertEqual(portal, {'controller': 'ctrl2', 'iqn': 'iqn2'}) + + def test_portal_for_vol_any_false(self): + vol_nomatch = {'id': 'vol_id', 'currentManager': 'ctrl3'} + portals = [{'controller': 'ctrl2', 'iqn': 'iqn2'}, + {'controller': 'ctrl1', 'iqn': 'iqn1'}] + self.assertRaises(exception.NetAppDriverException, + self.driver._get_iscsi_portal_for_vol, + vol_nomatch, portals, False) diff --git a/cinder/volume/drivers/netapp/eseries/iscsi.py b/cinder/volume/drivers/netapp/eseries/iscsi.py index 1b1f97c7b06..bfb08d79ff3 100644 --- a/cinder/volume/drivers/netapp/eseries/iscsi.py +++ b/cinder/volume/drivers/netapp/eseries/iscsi.py @@ -254,11 +254,15 @@ class Driver(driver.ISCSIDriver): try: return self._get_cached_volume(label) except KeyError: - for vol in self._client.list_volumes(): - if vol.get('label') == label: - self._cache_volume(vol) - break - return self._get_cached_volume(label) + return self._get_latest_volume(uid) + + def _get_latest_volume(self, uid): + label = utils.convert_uuid_to_es_fmt(uid) + for vol in self._client.list_volumes(): + if vol.get('label') == label: + self._cache_volume(vol) + return self._get_cached_volume(label) + raise exception.NetAppDriverException(_("Volume %s not found."), uid) def _get_cached_volume(self, label): vol_id = self._objects['volumes']['label_ref'][label] @@ -504,8 +508,9 @@ class Driver(driver.ISCSIDriver): def initialize_connection(self, volume, connector): """Allow connection to connector and return connection info.""" initiator_name = connector['initiator'] - vol = self._get_volume(volume['id']) - iscsi_det = self._get_iscsi_service_details() + vol = self._get_latest_volume(volume['id']) + iscsi_details = self._get_iscsi_service_details() + iscsi_det = self._get_iscsi_portal_for_vol(vol, iscsi_details) mapping = self._map_volume_to_host(vol, initiator_name) lun_id = mapping['lun'] self._cache_vol_mapping(mapping) @@ -535,6 +540,7 @@ class Driver(driver.ISCSIDriver): def _get_iscsi_service_details(self): """Gets iscsi iqn, ip and port information.""" + ports = [] hw_inventory = self._client.list_hardware_inventory() iscsi_ports = hw_inventory.get('iscsiPorts') if iscsi_ports: @@ -550,9 +556,23 @@ class Driver(driver.ISCSIDriver): iscsi_det['ip'] =\ port['ipv4Data']['ipv4AddressData']['ipv4Address'] iscsi_det['iqn'] = port['iqn'] - iscsi_det['tcp_port'] = port.get('tcpListenPort', '3260') - return iscsi_det - msg = _('No good iscsi portal information found for %s.') + iscsi_det['tcp_port'] = port.get('tcpListenPort') + iscsi_det['controller'] = port.get('controllerId') + ports.append(iscsi_det) + if not ports: + msg = _('No good iscsi portals found for %s.') + raise exception.NetAppDriverException( + msg % self._client.get_system_id()) + return ports + + def _get_iscsi_portal_for_vol(self, volume, portals, anyController=True): + """Get the iscsi portal info relevant to volume.""" + for portal in portals: + if portal.get('controller') == volume.get('currentManager'): + return portal + if anyController and portals: + return portals[0] + msg = _('No good iscsi portal found in supplied list for %s.') raise exception.NetAppDriverException( msg % self._client.get_system_id())