Merge "VMAX driver - Live Migration is dropping connection" into stable/newton
This commit is contained in:
commit
e4dc6ada79
|
@ -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'},
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue