From 23eea5313a6b0417f30db316299a59d764aef97a Mon Sep 17 00:00:00 2001 From: Helen Walsh Date: Mon, 27 Mar 2017 20:35:46 +0100 Subject: [PATCH] VMAX driver - Live Migration is dropping connection When Live migrating from one compute node to another the connection drops and requires the instance to be rebooted. To prevent this from happening we need to share the storage group and port group between masking views. Change-Id: I1483ca38362c5ff1724940c2abf1179e75e02c8e Closes-Bug: #1676459 (cherry picked from commit 069dd5b80db1af365052e97a811b26a72d47a094) --- .../unit/volume/drivers/emc/test_emc_vmax.py | 293 +++++++++++------- cinder/volume/drivers/emc/emc_vmax_common.py | 155 ++++++--- cinder/volume/drivers/emc/emc_vmax_iscsi.py | 2 +- cinder/volume/drivers/emc/emc_vmax_masking.py | 271 ++++++++-------- cinder/volume/drivers/emc/emc_vmax_utils.py | 73 ----- 5 files changed, 442 insertions(+), 352 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py index eac906ff1..10f39f958 100644 --- a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py +++ b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py @@ -15,7 +15,6 @@ import os import shutil -import sys import tempfile import unittest from xml.dom import minidom @@ -269,6 +268,10 @@ class EMCVMAXCommonData(object): 'CreationClassName': 'CIM_DeviceMaskingGroup', 'ElementName': 'OS_default_GOLD1_SG', 'SystemName': 'SYMMETRIX+000195900551'} + sg_instance_name = { + 'CreationClassName': 'CIM_DeviceMaskingGroup', + 'ElementName': 'OS-fakehost-SRP_1-Bronze-DSS-I-SG', + 'SystemName': 'SYMMETRIX+000195900551'} storage_system = 'SYMMETRIX+000195900551' storage_system_v3 = 'SYMMETRIX-+-000197200056' port_group = 'OS-portgroup-PG' @@ -2193,8 +2196,8 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): def test_find_device_number(self): host = 'fakehost' - data = ( - self.driver.common.find_device_number(self.data.test_volume_v2, + data, __, __ = ( + self.driver.common.find_device_number(self.data.test_volume, host)) self.assertEqual('OS-fakehost-MV', data['maskingview']) @@ -2204,16 +2207,16 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): return_value=[]) def test_find_device_number_false(self, mock_ref_name): host = 'bogushost' - data = ( - self.driver.common.find_device_number(self.data.test_volume_v2, + data, __, __ = ( + self.driver.common.find_device_number(self.data.test_volume, host)) self.assertFalse(data) def test_find_device_number_long_host(self): # Long host name host = 'myhost.mydomain.com' - data = ( - self.driver.common.find_device_number(self.data.test_volume_v2, + data, __, __ = ( + self.driver.common.find_device_number(self.data.test_volume, host)) self.assertEqual('OS-myhost-MV', data['maskingview']) @@ -2225,7 +2228,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): v2_host_over_38 = self.data.test_volume_v2.copy() # Pool aware scheduler enabled v2_host_over_38['host'] = host - data = ( + data, __, __ = ( self.driver.common.find_device_number(v2_host_over_38, host)) self.assertEqual(amended, data['maskingview']) @@ -3234,12 +3237,13 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'hostlunid': 1, - 'storagesystem': EMCVMAXCommonData.storage_system}) + return_value=({'hostlunid': 1, + 'storagesystem': EMCVMAXCommonData.storage_system}, + False, {})) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_wrap_get_storage_group_from_volume', - return_value=None) + return_value=({}, False, {})) @mock.patch.object( volume_types, 'get_volume_type_extra_specs', @@ -3272,10 +3276,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): self, _mock_volume_type, mock_wrap_group, mock_storage_group, mock_add_volume): self.driver.common._wrap_find_device_number = mock.Mock( - return_value={}) + return_value=({}, False, {})) self.driver.initialize_connection(self.data.test_volume, self.data.connector) + @mock.patch.object( + emc_vmax_common.EMCVMAXCommon, + '_get_port_group_from_source', + return_value={'CreationClassName': 'CIM_TargetMaskingGroup', + 'ElementName': 'OS-portgroup-PG'}) + @mock.patch.object( + emc_vmax_common.EMCVMAXCommon, + '_get_storage_group_from_source', + return_value=EMCVMAXCommonData.default_sg_instance_name) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_check_adding_volume_to_storage_group', @@ -3291,8 +3304,17 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'hostlunid': 1, - 'storagesystem': EMCVMAXCommonData.storage_system}) + return_value=({'hostlunid': 1, + 'storagesystem': EMCVMAXCommonData.storage_system}, + True, + {'hostlunid': 1, + 'storagesystem': EMCVMAXCommonData.storage_system})) + @mock.patch.object( + emc_vmax_common.EMCVMAXCommon, + '_wrap_find_device_number', + return_value=({}, True, + {'hostlunid': 1, + 'storagesystem': EMCVMAXCommonData.storage_system})) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_wrap_get_storage_group_from_volume', @@ -3305,11 +3327,12 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): _mock_volume_type, mock_wrap_group, mock_wrap_device, + mock_device, mock_storage_group, mock_same_host, - mock_check): - emc_vmax_utils.LIVE_MIGRATION_FILE = (self.tempdir + - '/livemigrationarray') + mock_check, + mock_sg_from_mv, + mock_pg_from_mv): self.driver.initialize_connection(self.data.test_volume, self.data.connector) @@ -3342,7 +3365,8 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'storagesystem': EMCVMAXCommonData.storage_system}) + return_value=({'storagesystem': EMCVMAXCommonData.storage_system}, + False, {})) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_wrap_get_storage_group_from_volume', @@ -4223,8 +4247,9 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'hostlunid': 1, - 'storagesystem': EMCVMAXCommonData.storage_system}) + return_value=({'hostlunid': 1, + 'storagesystem': EMCVMAXCommonData.storage_system}, + False, {})) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_wrap_get_storage_group_from_volume', @@ -4244,7 +4269,8 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'storagesystem': EMCVMAXCommonData.storage_system}) + return_value=({'storagesystem': EMCVMAXCommonData.storage_system}, + False, {})) @mock.patch.object( emc_vmax_masking.EMCVMAXMasking, '_wrap_get_storage_group_from_volume', @@ -4856,7 +4882,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'Name': "0001"}) + return_value=({'Name': "0001"}, False, {})) @mock.patch.object( volume_types, 'get_volume_type_extra_specs', @@ -5089,7 +5115,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase): return_value=volumeInstanceName) masking = self.driver.common.masking masking.get_masking_view_from_storage_group = mock.Mock( - return_value=None) + return_value={}) self.driver.manage_existing(volume, external_ref) utils.rename_volume.assert_called_once_with( common.conn, volumeInstanceName, volume['name']) @@ -5448,7 +5474,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'Name': "0001"}) + return_value=({'Name': "0001"}, False, {})) @mock.patch.object( volume_types, 'get_volume_type_extra_specs', @@ -6522,7 +6548,7 @@ class EMCV3DriverTestCase(test.TestCase): @mock.patch.object( emc_vmax_common.EMCVMAXCommon, 'find_device_number', - return_value={'Name': "0001"}) + return_value=({'Name': "0001"}, False, {})) @mock.patch.object( volume_types, 'get_volume_type_extra_specs', @@ -8041,6 +8067,37 @@ class EMCVMAXMaskingTest(test.TestCase): masking.get_devices_from_storage_group.assert_called_with( conn, storageGroupInstanceName) + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + '_get_storage_group_from_masking_view_instance', + return_value=EMCVMAXCommonData.sg_instance_name) + def test_check_existing_storage_group(self, mock_sg_from_mv): + common = self.driver.common + conn = self.fake_ecom_connection() + mv_instance_name = {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'} + masking = common.masking + sgFromMvInstanceName, msg = ( + masking._check_existing_storage_group(conn, mv_instance_name)) + self.assertEqual(EMCVMAXCommonData.sg_instance_name, + sgFromMvInstanceName) + self.assertIsNone(msg) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + '_get_storage_group_from_masking_view_instance', + return_value=None) + def test_check_existing_storage_group_none(self, mock_sg_from_mv): + common = self.driver.common + conn = self.fake_ecom_connection() + mv_instance_name = {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'} + masking = common.masking + sgFromMvInstanceName, msg = ( + masking._check_existing_storage_group(conn, mv_instance_name)) + self.assertIsNone(sgFromMvInstanceName) + self.assertIsNotNone(msg) + class EMCVMAXFCTest(test.TestCase): def setUp(self): @@ -8406,87 +8463,6 @@ class EMCVMAXUtilsTest(test.TestCase): self.assertEqual('CIM_DeviceMaskingGroup', modifiedInstance['CreationClassName']) - @mock.patch.object( - emc_vmax_common.EMCVMAXCommon, - '_find_lun', - return_value={'SystemName': EMCVMAXCommonData.storage_system}) - @mock.patch('builtins.open' if sys.version_info >= (3,) - else '__builtin__.open') - def test_insert_live_migration_record(self, mock_open, mock_lun): - conn = FakeEcomConnection() - self.driver.common.conn = conn - extraSpecs = self.data.extra_specs - connector = {'initiator': self.data.iscsi_initiator, - 'ip': '10.0.0.2', - 'platform': u'x86_64', - 'host': 'fakehost', - 'os_type': 'linux2', - 'multipath': False} - maskingviewdict = self.driver.common._populate_masking_dict( - self.data.test_volume, self.data.connector, extraSpecs) - emc_vmax_utils.LIVE_MIGRATION_FILE = ('/tempdir/livemigrationarray') - self.driver.utils.insert_live_migration_record( - self.data.test_volume, maskingviewdict, connector, extraSpecs) - mock_open.assert_called_once_with( - emc_vmax_utils.LIVE_MIGRATION_FILE, "wb") - - @mock.patch.object( - emc_vmax_common.EMCVMAXCommon, - '_find_lun', - return_value={'SystemName': EMCVMAXCommonData.storage_system}) - def test_delete_live_migration_record(self, mock_lun): - conn = FakeEcomConnection() - self.driver.common.conn = conn - extraSpecs = self.data.extra_specs - connector = {'initiator': self.data.iscsi_initiator, - 'ip': '10.0.0.2', - 'platform': u'x86_64', - 'host': 'fakehost', - 'os_type': 'linux2', - 'multipath': False} - maskingviewdict = self.driver.common._populate_masking_dict( - self.data.test_volume, self.data.connector, extraSpecs) - tempdir = tempfile.mkdtemp() - emc_vmax_utils.LIVE_MIGRATION_FILE = (tempdir + - '/livemigrationarray') - m = mock.mock_open() - with mock.patch('{}.open'.format(__name__), m, create=True): - with open(emc_vmax_utils.LIVE_MIGRATION_FILE, "wb") as f: - f.write('live migration details') - self.driver.utils.insert_live_migration_record( - self.data.test_volume, maskingviewdict, connector, extraSpecs) - self.driver.utils.delete_live_migration_record(self.data.test_volume) - m.assert_called_once_with(emc_vmax_utils.LIVE_MIGRATION_FILE, "wb") - shutil.rmtree(tempdir) - - @mock.patch.object( - emc_vmax_common.EMCVMAXCommon, - '_find_lun', - return_value={'SystemName': EMCVMAXCommonData.storage_system}) - def test_get_live_migration_record(self, mock_lun): - conn = FakeEcomConnection() - self.driver.common.conn = conn - extraSpecs = self.data.extra_specs - connector = {'initiator': self.data.iscsi_initiator, - 'ip': '10.0.0.2', - 'platform': u'x86_64', - 'host': 'fakehost', - 'os_type': 'linux2', - 'multipath': False} - maskingviewdict = self.driver.common._populate_masking_dict( - self.data.test_volume, self.data.connector, extraSpecs) - tempdir = tempfile.mkdtemp() - emc_vmax_utils.LIVE_MIGRATION_FILE = (tempdir + - '/livemigrationarray') - self.driver.utils.insert_live_migration_record( - self.data.test_volume, maskingviewdict, connector, extraSpecs) - record = self.driver.utils.get_live_migration_record( - self.data.test_volume, False) - self.assertEqual(maskingviewdict, record[0]) - self.assertEqual(connector, record[1]) - os.remove(emc_vmax_utils.LIVE_MIGRATION_FILE) - shutil.rmtree(tempdir) - def test_get_iqn(self): conn = FakeEcomConnection() iqn = "iqn.1992-04.com.emc:600009700bca30c01b9c012000000003,t,0x0001" @@ -8712,6 +8688,102 @@ class EMCVMAXCommonTest(test.TestCase): EMCVMAXCommonData.test_volume_type_QOS.get('specs'), extraSpecs[ 'qos']) + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_associated_masking_groups_from_device', + return_value=[EMCVMAXCommonData.sg_instance_name]) + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_masking_view_from_storage_group', + return_value=[{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'}]) + def test_is_volume_multiple_masking_views_false(self, mock_mv_from_sg, + mock_sg_from_dev): + common = self.driver.common + common.conn = FakeEcomConnection() + volumeInstanceName = ( + common.conn.EnumerateInstanceNames("EMC_StorageVolume")[0]) + volumeInstance = common.conn.GetInstance(volumeInstanceName) + self.assertFalse( + common._is_volume_multiple_masking_views(volumeInstance)) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_associated_masking_groups_from_device', + return_value=[EMCVMAXCommonData.sg_instance_name]) + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_masking_view_from_storage_group', + return_value=[{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'}, + {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-bronze-I-MV'}]) + def test_is_volume_multiple_masking_views_true(self, mock_mv_from_sg, + mock_sg_from_dev): + common = self.driver.common + common.conn = FakeEcomConnection() + volumeInstanceName = ( + common.conn.EnumerateInstanceNames("EMC_StorageVolume")[0]) + volumeInstance = common.conn.GetInstance(volumeInstanceName) + self.assertTrue( + common._is_volume_multiple_masking_views(volumeInstance)) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + '_get_storage_group_from_masking_view_instance', + return_value=EMCVMAXCommonData.sg_instance_name) + def test_get_storage_group_from_source(self, mock_sg_from_mv): + common = self.driver.common + common.conn = FakeEcomConnection() + mv_instance_name = {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'} + deviceInfoDict = {'controller': mv_instance_name} + self.assertEqual(EMCVMAXCommonData.sg_instance_name, + common._get_storage_group_from_source( + deviceInfoDict)) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + '_get_storage_group_from_masking_view_instance', + return_value=EMCVMAXCommonData.sg_instance_name) + def test_get_storage_group_from_source_except(self, mock_sg_from_mv): + common = self.driver.common + common.conn = FakeEcomConnection() + deviceInfoDict = {} + self.assertRaises( + exception.VolumeBackendAPIException, + common._get_storage_group_from_source, deviceInfoDict) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_port_group_from_masking_view_instance', + return_value={'CreationClassName': 'CIM_TargetMaskingGroup', + 'ElementName': 'OS-portgroup-PG'}) + def test_get_port_group_from_source(self, mock_pg_from_mv): + common = self.driver.common + common.conn = FakeEcomConnection() + pg_instance_name = {'CreationClassName': 'CIM_TargetMaskingGroup', + 'ElementName': 'OS-portgroup-PG'} + mv_instance_name = {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'OS-fakehost-gold-I-MV'} + deviceInfoDict = {'controller': mv_instance_name} + self.assertEqual(pg_instance_name, + common._get_port_group_from_source( + deviceInfoDict)) + + @mock.patch.object( + emc_vmax_masking.EMCVMAXMasking, + 'get_port_group_from_masking_view_instance', + return_value={'CreationClassName': 'CIM_TargetMaskingGroup', + 'ElementName': 'OS-portgroup-PG'}) + def test_get_port_group_from_source_except(self, mock_pg_from_mv): + common = self.driver.common + common.conn = FakeEcomConnection() + deviceInfoDict = {} + self.assertRaises( + exception.VolumeBackendAPIException, + common._get_port_group_from_source, deviceInfoDict) + class EMCVMAXProvisionTest(test.TestCase): def setUp(self): @@ -8801,10 +8873,11 @@ class EMCVMAXISCSITest(test.TestCase): driver.db = FakeDB() self.driver = driver - def test_smis_get_iscsi_properties(self): - device_info = {'hostlunid': 1} - self.driver.common.find_device_number = ( - mock.Mock(return_value=device_info)) + @mock.patch.object( + emc_vmax_common.EMCVMAXCommon, + 'find_device_number', + return_value=({'hostlunid': 1}, False, {})) + def test_smis_get_iscsi_properties(self, mock_device): iqns_and_ips = ( [{'iqn': 'iqn.1992-04.com.emc:50000973f006dd80,t,0x0001', 'ip': '10.10.0.50'}, diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py index e48077a1f..10847c0ac 100644 --- a/cinder/volume/drivers/emc/emc_vmax_common.py +++ b/cinder/volume/drivers/emc/emc_vmax_common.py @@ -338,7 +338,8 @@ class EMCVMAXCommon(object): LOG.info(_LI("Unmap volume: %(volume)s."), {'volume': volumename}) - device_info = self.find_device_number(volume, connector['host']) + device_info, __, __ = self.find_device_number( + volume, connector['host']) if 'hostlunid' not in device_info: LOG.info(_LI("Volume %s is not mapped. No volume to unmap."), volumename) @@ -347,6 +348,9 @@ class EMCVMAXCommon(object): vol_instance = self._find_lun(volume) storage_system = vol_instance['SystemName'] + if self._is_volume_multiple_masking_views(vol_instance): + return + configservice = self.utils.find_controller_configuration_service( self.conn, storage_system) if configservice is None: @@ -358,16 +362,23 @@ class EMCVMAXCommon(object): self._remove_members(configservice, vol_instance, connector, extraSpecs) - livemigrationrecord = self.utils.get_live_migration_record(volume, - False) - if livemigrationrecord: - live_maskingviewdict = livemigrationrecord[0] - live_connector = livemigrationrecord[1] - live_extraSpecs = livemigrationrecord[2] - self._attach_volume( - volume, live_connector, live_extraSpecs, - live_maskingviewdict, True) - self.utils.delete_live_migration_record(volume) + + def _is_volume_multiple_masking_views(self, vol_instance): + """Check if volume is in more than one MV. + + :param vol_instance: the volume instance + :returns: boolean + """ + storageGroupInstanceNames = ( + self.masking.get_associated_masking_groups_from_device( + self.conn, vol_instance.path)) + + for storageGroupInstanceName in storageGroupInstanceNames: + mvInstanceNames = self.masking.get_masking_view_from_storage_group( + self.conn, storageGroupInstanceName) + if len(mvInstanceNames) > 1: + return True + return False def initialize_connection(self, volume, connector): """Initializes the connection and returns device and connection info. @@ -406,35 +417,35 @@ class EMCVMAXCommon(object): LOG.info(_LI("Initialize connection: %(volume)s."), {'volume': volumeName}) self.conn = self._get_ecom_connection() - deviceInfoDict = self._wrap_find_device_number( - volume, connector['host']) + deviceInfoDict, isLiveMigration, sourceInfoDict = ( + self._wrap_find_device_number( + volume, connector['host'])) maskingViewDict = self._populate_masking_dict( volume, connector, extraSpecs) if ('hostlunid' in deviceInfoDict and deviceInfoDict['hostlunid'] is not None): - isSameHost = self._is_same_host(connector, deviceInfoDict) - if isSameHost: - # Device is already mapped to same host so we will leave - # the state as is. - - deviceNumber = deviceInfoDict['hostlunid'] - LOG.info(_LI("Volume %(volume)s is already mapped. " - "The device number is %(deviceNumber)s."), - {'volume': volumeName, - 'deviceNumber': deviceNumber}) - # Special case, we still need to get the iscsi ip address. - portGroupName = ( - self._get_correct_port_group( - deviceInfoDict, maskingViewDict['storageSystemName'])) - - else: + deviceNumber = deviceInfoDict['hostlunid'] + LOG.info(_LI("Volume %(volume)s is already mapped. " + "The device number is %(deviceNumber)s."), + {'volume': volumeName, + 'deviceNumber': deviceNumber}) + # Special case, we still need to get the iscsi ip address. + portGroupName = ( + self._get_correct_port_group( + deviceInfoDict, maskingViewDict['storageSystemName'])) + else: + if isLiveMigration: + maskingViewDict['storageGroupInstanceName'] = ( + self._get_storage_group_from_source(sourceInfoDict)) + maskingViewDict['portGroupInstanceName'] = ( + self._get_port_group_from_source(sourceInfoDict)) deviceInfoDict, portGroupName = self._attach_volume( volume, connector, extraSpecs, maskingViewDict, True) - else: - deviceInfoDict, portGroupName = ( - self._attach_volume( - volume, connector, extraSpecs, maskingViewDict)) + else: + deviceInfoDict, portGroupName = ( + self._attach_volume( + volume, connector, extraSpecs, maskingViewDict)) if self.protocol.lower() == 'iscsi': deviceInfoDict['ip_and_iqn'] = ( @@ -462,12 +473,8 @@ class EMCVMAXCommon(object): :raises: VolumeBackendAPIException """ volumeName = volume['name'] - maskingViewDict = self._populate_masking_dict( - volume, connector, extraSpecs) if isLiveMigration: maskingViewDict['isLiveMigration'] = True - self.utils.insert_live_migration_record(volume, maskingViewDict, - connector, extraSpecs) else: maskingViewDict['isLiveMigration'] = False @@ -475,7 +482,8 @@ class EMCVMAXCommon(object): self.conn, maskingViewDict, extraSpecs) # Find host lun id again after the volume is exported to the host. - deviceInfoDict = self.find_device_number(volume, connector['host']) + deviceInfoDict, __, __ = self.find_device_number( + volume, connector['host']) if 'hostlunid' not in deviceInfoDict: # Did not successfully attach to host, # so a rollback for FAST is required. @@ -485,7 +493,6 @@ class EMCVMAXCommon(object): (rollbackDict['isV3'] is not None)): (self.masking._check_if_rollback_action_for_masking_required( self.conn, rollbackDict)) - self.utils.delete_live_migration_record(volume) exception_message = (_("Error Attaching volume %(vol)s.") % {'vol': volumeName}) raise exception.VolumeBackendAPIException( @@ -554,6 +561,52 @@ class EMCVMAXCommon(object): data=exception_message) return portGroupName + def _get_storage_group_from_source(self, deviceInfoDict): + """Get the storage group from the existing masking view. + + :params deviceInfoDict: the device info dictionary + :returns: storage group instance + """ + storageGroupInstanceName = None + if ('controller' in deviceInfoDict and + deviceInfoDict['controller'] is not None): + maskingViewInstanceName = deviceInfoDict['controller'] + + # Get the storage group from masking view + storageGroupInstanceName = ( + self.masking._get_storage_group_from_masking_view_instance( + self.conn, + maskingViewInstanceName)) + else: + exception_message = (_("Cannot get the storage group from " + "the masking view.")) + raise exception.VolumeBackendAPIException( + data=exception_message) + return storageGroupInstanceName + + def _get_port_group_from_source(self, deviceInfoDict): + """Get the port group from the existing masking view. + + :params deviceInfoDict: the device info dictionary + :returns: port group instance + """ + portGroupInstanceName = None + if ('controller' in deviceInfoDict and + deviceInfoDict['controller'] is not None): + maskingViewInstanceName = deviceInfoDict['controller'] + + # Get the port group from masking view + portGroupInstanceName = ( + self.masking.get_port_group_from_masking_view_instance( + self.conn, + maskingViewInstanceName)) + else: + exception_message = (_("Cannot get the port group from " + "the masking view.")) + raise exception.VolumeBackendAPIException( + data=exception_message) + return portGroupInstanceName + def check_ig_instance_name(self, initiatorGroupInstanceName): """Check if an initiator group instance is on the array. @@ -570,8 +623,10 @@ class EMCVMAXCommon(object): :params connector: the connector Object """ volumename = volume['name'] - LOG.info(_LI("Terminate connection: %(volume)s."), - {'volume': volumename}) + LOG.info(_LI("Terminate connection: %(volume)s from " + "host %(host)s."), + {'volume': volumename, + 'host': connector['host']}) self._unmap_lun(volume, connector) @@ -1587,6 +1642,8 @@ class EMCVMAXCommon(object): volumeName = volume['name'] volumeInstance = self._find_lun(volume) storageSystemName = volumeInstance['SystemName'] + isLiveMigration = False + source_data = {} unitnames = self.conn.ReferenceNames( volumeInstance.path, @@ -1631,14 +1688,15 @@ class EMCVMAXCommon(object): data = maskedvol if not data: if len(maskedvols) > 0: - data = maskedvols[0] + source_data = maskedvols[0] LOG.warning(_LW( "Volume is masked but not to host %(host)s as is " "expected. Assuming live migration."), {'host': hoststr}) + isLiveMigration = True LOG.debug("Device info: %(data)s.", {'data': data}) - return data + return data, isLiveMigration, source_data def get_target_wwns(self, storageSystem, connector): """Find target WWNs. @@ -1927,6 +1985,10 @@ class EMCVMAXCommon(object): maskingViewDict['maskingViewName'] = ("%(prefix)s-MV" % {'prefix': prefix}) + + maskingViewDict['maskingViewNameLM'] = ("%(prefix)s-%(volid)s-MV" + % {'prefix': prefix, + 'volid': volume['id'][:8]}) volumeName = volume['name'] volumeInstance = self._find_lun(volume) storageSystemName = volumeInstance['SystemName'] @@ -4188,9 +4250,10 @@ class EMCVMAXCommon(object): self.conn, volumeInstanceName)) for sgInstanceName in sgInstanceNames: - mvInstanceName = self.masking.get_masking_view_from_storage_group( - self.conn, sgInstanceName) - if mvInstanceName: + mvInstanceNames = ( + self.masking.get_masking_view_from_storage_group( + self.conn, sgInstanceName)) + for mvInstanceName in mvInstanceNames: exceptionMessage = (_( "Unable to import volume %(deviceId)s to cinder. " "Volume is in masking view %(mv)s.") diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py index 6c7964238..eed5444cf 100644 --- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py +++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py @@ -241,7 +241,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver): meaning use CHAP with the specified credentials. """ - device_info = self.common.find_device_number( + device_info, __, __ = self.common.find_device_number( volume, connector['host']) isError = False diff --git a/cinder/volume/drivers/emc/emc_vmax_masking.py b/cinder/volume/drivers/emc/emc_vmax_masking.py index acce8d2bd..165108d19 100644 --- a/cinder/volume/drivers/emc/emc_vmax_masking.py +++ b/cinder/volume/drivers/emc/emc_vmax_masking.py @@ -89,7 +89,11 @@ class EMCVMAXMasking(object): defaultStorageGroupInstanceName = None fastPolicyName = None storageGroupInstanceName = None - if isLiveMigration is False: + if isLiveMigration: + maskingViewDict['maskingViewName'] = ( + maskingViewDict['maskingViewNameLM']) + maskingViewName = maskingViewDict['maskingViewNameLM'] + else: if isV3: defaultStorageGroupInstanceName = ( self._get_v3_default_storagegroup_instancename( @@ -106,11 +110,6 @@ class EMCVMAXMasking(object): volumeInstance.path, volumeName, fastPolicyName, extraSpecs)) - else: - # Live Migration - self.remove_and_reset_members( - conn, controllerConfigService, volumeInstance, volumeName, - extraSpecs, maskingViewDict['connector'], False) # If anything has gone wrong with the masking view we rollback try: @@ -118,6 +117,8 @@ class EMCVMAXMasking(object): self._validate_masking_view(conn, maskingViewDict, defaultStorageGroupInstanceName, extraSpecs)) + instance = conn.GetInstance(storageGroupInstanceName) + maskingViewDict['sgGroupName'] = instance['ElementName'] LOG.debug( "The masking view in the attach operation is " "%(maskingViewInstanceName)s. The storage group " @@ -279,17 +280,38 @@ class EMCVMAXMasking(object): maskingViewName = maskingViewDict['maskingViewName'] pgGroupName = maskingViewDict['pgGroupName'] - storageGroupInstanceName, errorMessage = ( - self._check_storage_group( - conn, maskingViewDict, defaultStorageGroupInstanceName)) - if errorMessage: - return None, storageGroupInstanceName, errorMessage + if maskingViewDict['isLiveMigration']: + try: + # We are sharing the storage group and port group + # between host and target + storageGroupInstanceName = ( + maskingViewDict['storageGroupInstanceName']) + storageGroupinstance = conn.GetInstance( + storageGroupInstanceName) + maskingViewDict['sgGroupName'] = ( + storageGroupinstance['ElementName']) + portGroupInstanceName = ( + maskingViewDict['portGroupInstanceName']) + portGroupInstance = conn.GetInstance( + portGroupInstanceName) + maskingViewDict['pgGroupName'] = ( + portGroupInstance['ElementName']) + except Exception: + errorMessage = (_( + "Unable to get storage group for live migration.")) + return None, None, errorMessage + else: + storageGroupInstanceName, errorMessage = ( + self._check_storage_group( + conn, maskingViewDict, defaultStorageGroupInstanceName)) + if errorMessage: + return None, storageGroupInstanceName, errorMessage - portGroupInstanceName, errorMessage = ( - self._check_port_group(conn, controllerConfigService, - pgGroupName)) - if errorMessage: - return None, storageGroupInstanceName, errorMessage + portGroupInstanceName, errorMessage = ( + self._check_port_group(conn, controllerConfigService, + pgGroupName)) + if errorMessage: + return None, storageGroupInstanceName, errorMessage initiatorGroupInstanceName, errorMessage = ( self._check_initiator_group(conn, controllerConfigService, @@ -330,7 +352,6 @@ class EMCVMAXMasking(object): """ storageGroupInstanceName = None controllerConfigService = maskingViewDict['controllerConfigService'] - sgGroupName = maskingViewDict['sgGroupName'] igGroupName = maskingViewDict['igGroupName'] connector = maskingViewDict['connector'] storageSystemName = maskingViewDict['storageSystemName'] @@ -346,10 +367,9 @@ class EMCVMAXMasking(object): if errorMessage: return storageGroupInstanceName, errorMessage + # Get the storage group from masking view storageGroupInstanceName, errorMessage = ( - self._check_existing_storage_group( - conn, controllerConfigService, sgGroupName, - maskingViewInstanceName)) + self._check_existing_storage_group(conn, maskingViewInstanceName)) return storageGroupInstanceName, errorMessage @@ -378,19 +398,15 @@ class EMCVMAXMasking(object): return storageGroupInstanceName, msg def _check_existing_storage_group( - self, conn, controllerConfigService, - sgGroupName, maskingViewInstanceName): + self, conn, maskingViewInstanceName): """Check that we can get the existing storage group. :param conn: the ecom connection - :param controllerConfigService: controller configuration service - :param sgGroupName: the storage group name :param maskingViewInstanceName: the masking view instance name :returns: storageGroupInstanceName :returns: string -- msg, the error message """ msg = None - sgFromMvInstanceName = ( self._get_storage_group_from_masking_view_instance( conn, maskingViewInstanceName)) @@ -398,10 +414,9 @@ class EMCVMAXMasking(object): if sgFromMvInstanceName is None: # This may be used in exception hence _ instead of _LE. msg = (_( - "Cannot get storage group: %(sgGroupName)s " - "from masking view %(maskingViewInstanceName)s. ") % - {'sgGroupName': sgGroupName, - 'maskingViewInstanceName': maskingViewInstanceName}) + "Cannot get storage group from masking view " + "%(maskingViewInstanceName)s. ") % + {'maskingViewInstanceName': maskingViewInstanceName}) LOG.error(msg) return sgFromMvInstanceName, msg @@ -636,27 +651,12 @@ class EMCVMAXMasking(object): :raises: VolumeBackendAPIException """ - assocVolumeInstanceNames = self.get_devices_from_storage_group( - conn, storageGroupInstanceName) - LOG.debug( - "There are %(length)lu associated with the default storage group " - "before removing volume %(volumeName)s.", - {'length': len(assocVolumeInstanceNames), - 'volumeName': volumeName}) volInstance = conn.GetInstance(volumeInstanceName, LocalOnly=False) self._remove_volume_from_sg( conn, controllerConfigService, storageGroupInstanceName, volInstance, maskingViewDict['extraSpecs']) - assocVolumeInstanceNames = self.get_devices_from_storage_group( - conn, storageGroupInstanceName) - LOG.debug( - "There are %(length)lu associated with the default storage group " - "after removing volume %(volumeName)s.", - {'length': len(assocVolumeInstanceNames), - 'volumeName': volumeName}) - # Required for unit tests. emptyStorageGroupInstanceName = ( self._wrap_get_storage_group_from_volume( @@ -1616,19 +1616,36 @@ class EMCVMAXMasking(object): foundView = self._find_masking_view( conn, maskingViewName, storageSystemName) if foundView: - groups = conn.AssociatorNames( - foundView, - ResultClass='CIM_TargetMaskingGroup') - if len(groups) > 0: - foundPortMaskingGroupInstanceName = groups[0] + foundPortMaskingGroupInstanceName = ( + self.get_port_group_from_masking_view_instance( + conn, foundView)) LOG.debug( - "Masking view: %(view)s InitiatorMaskingGroup: %(masking)s.", + "Masking view: %(view)s portMaskingGroup: %(masking)s.", {'view': maskingViewName, 'masking': foundPortMaskingGroupInstanceName}) return foundPortMaskingGroupInstanceName + def get_port_group_from_masking_view_instance( + self, conn, maskingViewInstanceName): + """Given the masking view name get the port group from it. + + :param conn: connection to the ecom server + :param maskingViewInstanceName: the masking view instance name + :returns: instance name foundPortMaskingGroupInstanceName + """ + + foundPortMaskingGroupInstanceName = None + + groups = conn.AssociatorNames( + maskingViewInstanceName, + ResultClass='CIM_TargetMaskingGroup') + if len(groups) > 0: + foundPortMaskingGroupInstanceName = groups[0] + + return foundPortMaskingGroupInstanceName + def _delete_masking_view( self, conn, controllerConfigService, maskingViewName, maskingViewInstanceName, extraSpecs): @@ -1670,14 +1687,11 @@ class EMCVMAXMasking(object): :param storageGroupInstanceName: the storage group instance name :returns: instance name foundMaskingViewInstanceName """ - foundMaskingViewInstanceName = None maskingViews = conn.AssociatorNames( storageGroupInstanceName, ResultClass='Symm_LunMaskingView') - if len(maskingViews) > 0: - foundMaskingViewInstanceName = maskingViews[0] - return foundMaskingViewInstanceName + return maskingViews def add_volume_to_storage_group( self, conn, controllerConfigService, storageGroupInstanceName, @@ -1908,12 +1922,10 @@ class EMCVMAXMasking(object): """ instance = conn.GetInstance(storageGroupInstanceName, LocalOnly=False) storageGroupName = instance['ElementName'] - mvInstanceName = self.get_masking_view_from_storage_group( + mvInstanceNames = self.get_masking_view_from_storage_group( conn, storageGroupInstanceName) - if mvInstanceName is None: - LOG.debug("Unable to get masking view %(maskingView)s " - "from storage group.", - {'maskingView': mvInstanceName}) + if not mvInstanceNames: + LOG.debug("Unable to get masking views from storage group.") @coordination.synchronized("emc-sg-{storageGroup}") def do_remove_volume_from_sg(storageGroup): @@ -1943,43 +1955,46 @@ class EMCVMAXMasking(object): return do_remove_volume_from_sg(storageGroupName) else: - # need to lock masking view when we are locking the storage - # group to avoid possible deadlock situations from concurrent - # processes - maskingViewInstance = conn.GetInstance( - mvInstanceName, LocalOnly=False) - maskingViewName = maskingViewInstance['ElementName'] + for mvInstanceName in mvInstanceNames: + # need to lock masking view when we are locking the storage + # group to avoid possible deadlock situations from concurrent + # processes + maskingViewInstance = conn.GetInstance( + mvInstanceName, LocalOnly=False) + maskingViewName = maskingViewInstance['ElementName'] - @coordination.synchronized("emc-sg-{maskingView}") - def do_remove_volume_from_sg(maskingView): - @coordination.synchronized("emc-mv-{storageGroup}") - def inner_do_remove_volume_from_sg(storageGroup): - volumeInstanceNames = self.get_devices_from_storage_group( - conn, storageGroupInstanceName) - numVolInStorageGroup = len(volumeInstanceNames) - LOG.debug( - "There are %(numVol)d volumes in the storage group " - "%(maskingGroup)s associated with %(mvName)s", - {'numVol': numVolInStorageGroup, - 'maskingGroup': storageGroup, - 'mvName': maskingViewName}) + @coordination.synchronized("emc-sg-{maskingView}") + def do_remove_volume_from_sg(maskingView): + @coordination.synchronized("emc-mv-{storageGroup}") + def inner_do_remove_volume_from_sg(storageGroup): + volumeInstanceNames = ( + self.get_devices_from_storage_group( + conn, storageGroupInstanceName)) + numVolInStorageGroup = len(volumeInstanceNames) + LOG.debug( + "There are %(numVol)d volumes in the storage " + "group %(sg)s associated with %(mvName)s", + {'numVol': numVolInStorageGroup, + 'sg': storageGroup, + 'mvName': maskingViewName}) - if numVolInStorageGroup == 1: - # Last volume in the storage group. - self._last_vol_in_SG( - conn, controllerConfigService, - storageGroupInstanceName, - storageGroupName, volumeInstance, - volumeInstance['ElementName'], extraSpecs) - else: - # Not the last volume so remove it from storage group - self._multiple_vols_in_SG( - conn, controllerConfigService, - storageGroupInstanceName, - volumeInstance, volumeInstance['ElementName'], - numVolInStorageGroup, extraSpecs) - return inner_do_remove_volume_from_sg(storageGroupName) - return do_remove_volume_from_sg(maskingViewName) + if numVolInStorageGroup == 1: + # Last volume in the storage group. + self._last_vol_in_SG( + conn, controllerConfigService, + storageGroupInstanceName, + storageGroupName, volumeInstance, + volumeInstance['ElementName'], extraSpecs) + else: + # Not the last volume so remove it from storage + # group + self._multiple_vols_in_SG( + conn, controllerConfigService, + storageGroupInstanceName, + volumeInstance, volumeInstance['ElementName'], + numVolInStorageGroup, extraSpecs) + return inner_do_remove_volume_from_sg(storageGroupName) + return do_remove_volume_from_sg(maskingViewName) def _last_vol_in_SG( self, conn, controllerConfigService, storageGroupInstanceName, @@ -2007,9 +2022,9 @@ class EMCVMAXMasking(object): LOG.debug("Only one volume remains in storage group " "%(sgname)s. Driver will attempt cleanup.", {'sgname': storageGroupName}) - mvInstanceName = self.get_masking_view_from_storage_group( + mvInstanceNames = self.get_masking_view_from_storage_group( conn, storageGroupInstanceName) - if mvInstanceName is None: + if not mvInstanceNames: # Remove the volume from the storage group and delete the SG. self._remove_last_vol_and_delete_sg( conn, controllerConfigService, @@ -2018,18 +2033,21 @@ class EMCVMAXMasking(object): volumeName, extraSpecs) status = True else: - maskingViewInstance = conn.GetInstance( - mvInstanceName, LocalOnly=False) - maskingViewName = maskingViewInstance['ElementName'] + mv_count = len(mvInstanceNames) + for mvInstanceName in mvInstanceNames: + maskingViewInstance = conn.GetInstance( + mvInstanceName, LocalOnly=False) + maskingViewName = maskingViewInstance['ElementName'] - def do_delete_mv_ig_and_sg(): - return self._delete_mv_ig_and_sg( - conn, controllerConfigService, mvInstanceName, - maskingViewName, storageGroupInstanceName, - storageGroupName, volumeInstance, volumeName, - extraSpecs) - do_delete_mv_ig_and_sg() - status = True + def do_delete_mv_ig_and_sg(): + return self._delete_mv_ig_and_sg( + conn, controllerConfigService, mvInstanceName, + maskingViewName, storageGroupInstanceName, + storageGroupName, volumeInstance, volumeName, + extraSpecs, mv_count) + do_delete_mv_ig_and_sg() + status = True + mv_count -= 1 return status def _multiple_vols_in_SG( @@ -2067,7 +2085,7 @@ class EMCVMAXMasking(object): def _delete_mv_ig_and_sg( self, conn, controllerConfigService, mvInstanceName, maskingViewName, storageGroupInstanceName, storageGroupName, - volumeInstance, volumeName, extraSpecs): + volumeInstance, volumeName, extraSpecs, mv_count): """Delete the Masking view, the storage Group and the initiator group. :param conn: connection to the ecom server @@ -2079,6 +2097,7 @@ class EMCVMAXMasking(object): :param volumeInstance: the volume Instance :param volumeName: the volume name :param extraSpecs: extra specs + :param mv_count: number of masking views """ isV3 = extraSpecs[ISV3] fastPolicyName = extraSpecs.get(FASTPOLICY, None) @@ -2106,16 +2125,20 @@ class EMCVMAXMasking(object): storageSystemInstanceName['Name'], storageGroupInstanceName, extraSpecs) - self._remove_last_vol_and_delete_sg( - conn, controllerConfigService, storageGroupInstanceName, - storageGroupName, volumeInstance.path, volumeName, - extraSpecs) + if mv_count == 1: + if self._is_volume_in_storage_group( + conn, storageGroupInstanceName, + volumeInstance, storageGroupName): + self._remove_last_vol_and_delete_sg( + conn, controllerConfigService, storageGroupInstanceName, + storageGroupName, volumeInstance.path, volumeName, + extraSpecs) - LOG.debug( - "Volume %(volumeName)s successfully removed from SG and " - "Storage Group %(storageGroupName)s successfully deleted. ", - {'volumeName': volumeName, - 'storageGroupName': storageGroupName}) + LOG.debug( + "Volume %(volumeName)s successfully removed from SG and " + "Storage Group %(storageGroupName)s successfully deleted. ", + {'volumeName': volumeName, + 'storageGroupName': storageGroupName}) def _return_back_to_default_sg( self, conn, controllerConfigService, volumeInstance, volumeName, @@ -2466,10 +2489,10 @@ class EMCVMAXMasking(object): # Get the SG by IGs. for sgInstanceName in storageGroupInstanceNames: # Get maskingview from storage group. - mvInstanceName = self.get_masking_view_from_storage_group( + mvInstanceNames = self.get_masking_view_from_storage_group( conn, sgInstanceName) # Get initiator group from masking view. - if mvInstanceName: + for mvInstanceName in mvInstanceNames: LOG.debug("Found masking view associated with SG " "%(storageGroup)s: %(maskingview)s", {'maskingview': mvInstanceName, @@ -2628,9 +2651,13 @@ class EMCVMAXMasking(object): :param hardwareIdManagementService - hardware id management service :param hardwareIdPath - The path of the initiator object """ - ret = conn.InvokeMethod('DeleteStorageHardwareID', - hardwareIdManagementService, - HardwareID = hardwareIdPath) + ret = -1 + try: + ret = conn.InvokeMethod('DeleteStorageHardwareID', + hardwareIdManagementService, + HardwareID = hardwareIdPath) + except Exception: + pass if ret == 0: LOG.debug("Deletion of initiator path %(hardwareIdPath)s " "is successful.", {'hardwareIdPath': hardwareIdPath}) diff --git a/cinder/volume/drivers/emc/emc_vmax_utils.py b/cinder/volume/drivers/emc/emc_vmax_utils.py index 54addb8cb..85acd466b 100644 --- a/cinder/volume/drivers/emc/emc_vmax_utils.py +++ b/cinder/volume/drivers/emc/emc_vmax_utils.py @@ -15,8 +15,6 @@ import datetime import hashlib -import os -import pickle import random import re from xml.dom import minidom @@ -2727,77 +2725,6 @@ class EMCVMAXUtils(object): PropertyList=propertylist) return modifiedInstance - def insert_live_migration_record(self, volume, maskingviewdict, - connector, extraSpecs): - """Insert a record of live migration destination into a temporary file - - :param volume: the volume dictionary - :param maskingviewdict: the storage group instance name - :param connector: the connector Object - :param extraSpecs: the extraSpecs dict - """ - live_migration_details = self.get_live_migration_record(volume, True) - if live_migration_details: - if volume['id'] not in live_migration_details: - live_migration_details[volume['id']] = [maskingviewdict, - connector, extraSpecs] - else: - live_migration_details = {volume['id']: [maskingviewdict, - connector, extraSpecs]} - try: - with open(LIVE_MIGRATION_FILE, "wb") as f: - pickle.dump(live_migration_details, f) - except Exception: - exceptionMessage = (_( - "Error in processing live migration file.")) - LOG.exception(exceptionMessage) - raise exception.VolumeBackendAPIException( - data=exceptionMessage) - - def delete_live_migration_record(self, volume): - """Delete record of live migration - - Delete record of live migration destination from file and if - after deletion of record, delete file if empty. - - :param volume: the volume dictionary - """ - live_migration_details = self.get_live_migration_record(volume, True) - if live_migration_details: - if volume['id'] in live_migration_details: - del live_migration_details[volume['id']] - with open(LIVE_MIGRATION_FILE, "wb") as f: - pickle.dump(live_migration_details, f) - else: - LOG.debug("%(Volume)s doesn't exist in live migration " - "record.", - {'Volume': volume['id']}) - if not live_migration_details: - os.remove(LIVE_MIGRATION_FILE) - - def get_live_migration_record(self, volume, returnallrecords): - """get record of live migration destination from a temporary file - - :param volume: the volume dictionary - :param returnallrecords: if true, return all records in file - :returns: returns a single record or all records depending on - returnallrecords flag - """ - returned_record = None - if os.path.isfile(LIVE_MIGRATION_FILE): - with open(LIVE_MIGRATION_FILE, "rb") as f: - live_migration_details = pickle.load(f) - if returnallrecords: - returned_record = live_migration_details - else: - if volume['id'] in live_migration_details: - returned_record = live_migration_details[volume['id']] - else: - LOG.debug("%(Volume)s doesn't exist in live migration " - "record.", - {'Volume': volume['id']}) - return returned_record - def get_iqn(self, conn, ipendpointinstancename): """Get the IPv4Address from the ip endpoint instance name.