]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Support SMI-S provider v8.0.3 in VMAX driver
authorXing Yang <xing.yang@emc.com>
Fri, 19 Jun 2015 13:47:59 +0000 (09:47 -0400)
committerXing Yang <xing.yang@emc.com>
Fri, 10 Jul 2015 04:17:39 +0000 (00:17 -0400)
This patch made changes in the VMAX driver to support
interface changes in SMI-S provider v8.0.3.

* Accommodate System Name prefix change from 'SYMMETRIX+' to
  'SYMMETRIX-+-'.
* Delete composite volume - changed from using
  ReturnElementsToStoragePool to EMCReturnToStoragePool.
* Extend volume - removed InPool parameter from EMCUnBindElements
  method.
* Create snapshot/clone with multi meta members -
  EMC_PartialAllocOfConcreteExtentCIM was deprecated;
  used 'GetCompositeElements' instead and changed how we get meta
  members' capacities.
* Create CG snapshot - A new WaitForCopyState parameter was added.
* Added helper function to determine SMI-S provider and SE version
  Note: SMI-S provider is bundled with SE (Solutions Enabler) and
  they have the same version number starting from v8.0.
  The EMC SMI-S Provider supports the SNIA Storage Management Initiative
  (SMI), an ANSI standard for storage management. It supports the VMAX
  storage system. SE is a software product that discovers and manages
  VMAX storage system.

Closes-Bug: #1463217
Change-Id: Ia6333396a8111f110f540c367e5018cfad6e93e4

cinder/tests/unit/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_fc.py
cinder/volume/drivers/emc/emc_vmax_https.py
cinder/volume/drivers/emc/emc_vmax_iscsi.py
cinder/volume/drivers/emc/emc_vmax_provision.py
cinder/volume/drivers/emc/emc_vmax_utils.py

index b996ad41358b359c44189c8aa66f1e4fd5ef1914..892df08839f79b0612eb1c0c2533eaecdf71f142 100644 (file)
@@ -294,6 +294,7 @@ class EMCVMAXCommonData(object):
     properties = {'ConsumableBlocks': '12345',
                   'BlockSize': '512'}
 
+    block_size = 512
     test_volume = {'name': 'vol1',
                    'size': 1,
                    'volume_name': 'vol1',
@@ -308,7 +309,7 @@ class EMCVMAXCommonData(object):
                    'status': 'available',
                    'host': 'fake-host',
                    'NumberOfBlocks': 100,
-                   'BlockSize': 512
+                   'BlockSize': block_size
                    }
 
     test_volume_v2 = {'name': 'vol1',
@@ -325,7 +326,7 @@ class EMCVMAXCommonData(object):
                       'status': 'available',
                       'host': 'fake-host',
                       'NumberOfBlocks': 100,
-                      'BlockSize': 512
+                      'BlockSize': block_size
                       }
 
     test_volume_v3 = {'name': 'vol1',
@@ -342,9 +343,17 @@ class EMCVMAXCommonData(object):
                       'status': 'available',
                       'host': 'fake-host',
                       'NumberOfBlocks': 100,
-                      'BlockSize': 512
+                      'BlockSize': block_size
                       }
-
+    metaHead_volume = {'DeviceID': 10,
+                       'ConsumableBlocks': 1000
+                       }
+    meta_volume1 = {'DeviceID': 11,
+                    'ConsumableBlocks': 200
+                    }
+    meta_volume2 = {'DeviceID': 12,
+                    'ConsumableBlocks': 300
+                    }
     test_volume_CG = {'name': 'volInCG',
                       'consistencygroup_id': 'abc',
                       'size': 1,
@@ -435,6 +444,9 @@ class EMCVMAXCommonData(object):
                    'storagetype:slo': u'Bronze',
                    'storagetype:array': u'0123456789',
                    'isV3': True}
+    majorVersion = 1
+    minorVersion = 2
+    revNumber = 3
 
 
 class FakeLookupService(object):
@@ -466,7 +478,8 @@ class FakeEcomConnection(object):
                      Type=None, EMCSRP=None, EMCSLO=None, EMCWorkload=None,
                      EMCCollections=None, InitiatorMaskingGroup=None,
                      DeviceMaskingGroup=None, TargetMaskingGroup=None,
-                     ProtocolController=None, StorageID=None, IDType=None):
+                     ProtocolController=None, StorageID=None, IDType=None,
+                     WaitForCopyState=None):
 
         rc = 0
         myjob = SE_ConcreteJob()
@@ -491,7 +504,7 @@ class FakeEcomConnection(object):
 
         elif TheElements and \
                 TheElements[0]['DeviceID'] == '99999' and \
-                MethodName == 'EMCReturnToStoragePool':
+                MethodName == 'ReturnElementsToStoragePool':
             rc = 10
             myjob['status'] = 'failure'
         elif HardwareId:
@@ -519,6 +532,13 @@ class FakeEcomConnection(object):
             rc = 0
             ret['HardwareID'] = self.data.iscsi_initiator
             return rc, ret
+        elif MethodName == 'GetCompositeElements':
+            ret = {}
+            rc = 0
+            ret['OutElements'] = [self.data.metaHead_volume,
+                                  self.data.meta_volume1,
+                                  self.data.meta_volume2]
+            return rc, ret
 
         job = {'Job': myjob}
         return rc, job
@@ -561,6 +581,8 @@ class FakeEcomConnection(object):
             result = self._enum_repservcpbls()
         elif name == 'SE_StorageSynchronized_SV_SV':
             result = self._enum_storageSyncSvSv()
+        elif name == 'Symm_SRPStoragePool':
+            result = self._enum_srpstoragepool()
         else:
             result = self._default_enum()
         return result
@@ -571,6 +593,8 @@ class FakeEcomConnection(object):
             result = self._enum_pool_details()
         elif name == 'SE_StorageHardwareID':
             result = self._enum_storhdwids()
+        elif name == 'SE_ManagementServerSoftwareIdentity':
+            result = self._enum_sw_identity()
         else:
             result = self._default_enum()
         return result
@@ -960,7 +984,7 @@ class FakeEcomConnection(object):
     def _getinstance_pool(self, objectpath):
         pool = {}
         pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
-        pool['ElementName'] = 'gold'
+        pool['ElementName'] = self.data.poolname
         pool['SystemName'] = self.data.storage_system
         pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
         pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
@@ -977,6 +1001,7 @@ class FakeEcomConnection(object):
         srpstoragepool = SYMM_SrpStoragePool()
         srpstoragepool['CreationClassName'] = (
             self.data.srpstoragepool_creationclass)
+        srpstoragepool['ElementName'] = 'SRP_1'
 
         classcimproperty = Fake_CIMProperty()
         totalManagedSpace = (
@@ -1213,6 +1238,31 @@ class FakeEcomConnection(object):
 
         vols.append(failed_vol)
 
+        volumeHead = EMC_StorageVolume()
+        volumeHead.classname = 'Symm_StorageVolume'
+        blockSize = self.data.block_size
+        volumeHead['ConsumableBlocks'] = (
+            self.data.metaHead_volume['ConsumableBlocks'])
+        volumeHead['BlockSize'] = blockSize
+        volumeHead['DeviceID'] = self.data.metaHead_volume['DeviceID']
+        vols.append(volumeHead)
+
+        metaMember1 = EMC_StorageVolume()
+        metaMember1.classname = 'Symm_StorageVolume'
+        metaMember1['ConsumableBlocks'] = (
+            self.data.meta_volume1['ConsumableBlocks'])
+        metaMember1['BlockSize'] = blockSize
+        metaMember1['DeviceID'] = self.data.meta_volume1['DeviceID']
+        vols.append(metaMember1)
+
+        metaMember2 = EMC_StorageVolume()
+        metaMember2.classname = 'Symm_StorageVolume'
+        metaMember2['ConsumableBlocks'] = (
+            self.data.meta_volume2['ConsumableBlocks'])
+        metaMember2['BlockSize'] = blockSize
+        metaMember2['DeviceID'] = self.data.meta_volume2['DeviceID']
+        vols.append(metaMember2)
+
         return vols
 
     def _enum_initiatorMaskingGroup(self):
@@ -1463,6 +1513,15 @@ class FakeEcomConnection(object):
         svInstances.append(svInstance)
         return svInstances
 
+    def _enum_sw_identity(self):
+        swIdentities = []
+        swIdentity = {}
+        swIdentity['MajorVersion'] = self.data.majorVersion
+        swIdentity['MinorVersion'] = self.data.minorVersion
+        swIdentity['RevisionNumber'] = self.data.revNumber
+        swIdentities.append(swIdentity)
+        return swIdentities
+
     def _default_enum(self):
         names = []
         name = {}
@@ -1688,18 +1747,39 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         self.assertEqual(storageHardwareIDInstanceNames[0],
                          self.data.iscsi_initiator)
 
-    def test_format_system_name(self):
-        v2array = ['SYMMETRIX', '000195900551', 'U', 'gold']
-        systemnameV2 = self.driver.utils._format_system_name(v2array[0],
-                                                             v2array[1],
-                                                             '+')
-        self.assertEqual('SYMMETRIX+000195900551', systemnameV2)
-
-        v3array = ['SYMMETRIX', '000197200056', 'SRP_1']
-        systemnameV3 = self.driver.utils._format_system_name(v3array[0],
-                                                             v3array[1],
-                                                             '-+-')
-        self.assertEqual('SYMMETRIX-+-000197200056', systemnameV3)
+    def test_get_pool_instance_and_system_name(self):
+        conn = self.fake_ecom_connection()
+        # V2 - old '+' separator
+        storagesystem = {}
+        storagesystem['SystemName'] = self.data.storage_system
+        storagesystem['Name'] = self.data.storage_system
+        pools = conn.EnumerateInstanceNames("EMC_VirtualProvisioningPool")
+        poolname = 'gold'
+        poolinstancename, systemname = (
+            self.driver.common.utils._get_pool_instance_and_system_name(
+                conn, pools, storagesystem, poolname))
+        self.assertEqual(self.data.storage_system, systemname)
+        self.assertEqual(self.data.storagepoolid,
+                         poolinstancename['InstanceID'])
+        # V3 - note: V2 can also have the '-+-' separator
+        storagesystem = {}
+        storagesystem['SystemName'] = self.data.storage_system_v3
+        storagesystem['Name'] = self.data.storage_system_v3
+        pools = conn.EnumerateInstanceNames('Symm_SRPStoragePool')
+        poolname = 'SRP_1'
+        poolinstancename, systemname = (
+            self.driver.common.utils._get_pool_instance_and_system_name(
+                conn, pools, storagesystem, poolname))
+        self.assertEqual(self.data.storage_system_v3, systemname)
+        self.assertEqual('SYMMETRIX-+-000197200056-+-SRP_1',
+                         poolinstancename['InstanceID'])
+        # Invalid poolname
+        poolname = 'bogus'
+        poolinstancename, systemname = (
+            self.driver.common.utils._get_pool_instance_and_system_name(
+                conn, pools, storagesystem, poolname))
+        self.assertIsNone(poolinstancename)
+        self.assertEqual(self.data.storage_system_v3, systemname)
 
     def test_get_hardware_type(self):
         iqn_initiator = 'iqn.1992-04.com.emc: 50000973f006dd80'
@@ -1916,19 +1996,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         volume2 = EMC_StorageVolume()
         volume2['name'] = 'myVol'
         volume2['provider_location'] = six.text_type(provider_location2)
-        verify_orig = self.driver.common.utils.get_existing_instance
-        self.driver.common.utils.get_existing_instance = mock.Mock(
+        verify_orig = self.driver.common.conn.GetInstance
+        self.driver.common.conn.GetInstance = mock.Mock(
             return_value=None)
         findlun2 = self.driver.common._find_lun(volume2)
         # Not found.
         self.assertIsNone(findlun2)
-        instancename2 = self.driver.utils.get_instance_name(
+        self.driver.utils.get_instance_name(
             provider_location2['classname'],
             keybindings2)
-        self.driver.common.utils.get_existing_instance.assert_called_once_with(
-            self.driver.common.conn, instancename2)
-        self.driver.common.utils.get_existing_instance.reset_mock()
-        self.driver.common.utils.get_existing_instance = verify_orig
+        self.driver.common.conn.GetInstance.assert_called_once_with(
+            keybindings2)
+        self.driver.common.conn.GetInstance.reset_mock()
+        self.driver.common.conn.GetInstance = verify_orig
 
         keybindings3 = {'CreationClassName': u'Symm_StorageVolume',
                         'SystemName': u'SYMMETRIX+000195900551',
@@ -2652,7 +2732,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -2687,7 +2767,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
 
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -2748,7 +2828,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         FakeDB,
@@ -2908,7 +2988,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -3019,6 +3099,74 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
             conn, volumeInstance, originalName)
         self.assertEqual(originalName, volumeInstance['ElementName'])
 
+    def test_get_smi_version(self):
+        conn = self.fake_ecom_connection()
+        utils = self.driver.common.utils
+        version = utils.get_smi_version(conn)
+        expected = int(str(self.data.majorVersion)
+                       + str(self.data.minorVersion)
+                       + str(self.data.revNumber))
+        self.assertEqual(version, expected)
+
+    def test_get_pool_name(self):
+        conn = self.fake_ecom_connection()
+        utils = self.driver.common.utils
+        poolInstanceName = {}
+        poolInstanceName['InstanceID'] = "SATA_GOLD1"
+        poolInstanceName['CreationClassName'] = 'Symm_VirtualProvisioningPool'
+        poolName = utils._get_pool_name(conn, poolInstanceName)
+        self.assertEqual(poolName, self.data.poolname)
+
+    def test_get_meta_members_capacity_in_byte(self):
+        conn = self.fake_ecom_connection()
+        utils = self.driver.common.utils
+        memberVolumeInstanceNames = []
+        volumeHead = EMC_StorageVolume()
+        volumeHead.classname = 'Symm_StorageVolume'
+        blockSize = self.data.block_size
+        volumeHead['ConsumableBlocks'] = (
+            self.data.metaHead_volume['ConsumableBlocks'])
+        volumeHead['BlockSize'] = blockSize
+        volumeHead['DeviceID'] = self.data.metaHead_volume['DeviceID']
+        memberVolumeInstanceNames.append(volumeHead)
+        metaMember1 = EMC_StorageVolume()
+        metaMember1.classname = 'Symm_StorageVolume'
+        metaMember1['ConsumableBlocks'] = (
+            self.data.meta_volume1['ConsumableBlocks'])
+        metaMember1['BlockSize'] = blockSize
+        metaMember1['DeviceID'] = self.data.meta_volume1['DeviceID']
+        memberVolumeInstanceNames.append(metaMember1)
+        metaMember2 = EMC_StorageVolume()
+        metaMember2.classname = 'Symm_StorageVolume'
+        metaMember2['ConsumableBlocks'] = (
+            self.data.meta_volume2['ConsumableBlocks'])
+        metaMember2['BlockSize'] = blockSize
+        metaMember2['DeviceID'] = self.data.meta_volume2['DeviceID']
+        memberVolumeInstanceNames.append(metaMember2)
+        capacities = utils.get_meta_members_capacity_in_byte(
+            conn, memberVolumeInstanceNames)
+        headSize = (
+            volumeHead['ConsumableBlocks'] -
+            metaMember1['ConsumableBlocks'] -
+            metaMember2['ConsumableBlocks'])
+        expected = [headSize * blockSize,
+                    metaMember1['ConsumableBlocks'] * blockSize,
+                    metaMember2['ConsumableBlocks'] * blockSize]
+        self.assertEqual(capacities, expected)
+
+    def test_get_composite_elements(self):
+        conn = self.fake_ecom_connection()
+        utils = self.driver.common.utils
+        volumeInstanceName = (
+            conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
+        volumeInstance = conn.GetInstance(volumeInstanceName)
+        memberVolumeInstanceNames = utils.get_composite_elements(
+            conn, volumeInstance)
+        expected = [self.data.metaHead_volume,
+                    self.data.meta_volume1,
+                    self.data.meta_volume2]
+        self.assertEqual(memberVolumeInstanceNames, expected)
+
     def _cleanup(self):
         if self.config_file_path:
             bExists = os.path.exists(self.config_file_path)
@@ -3393,7 +3541,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -3431,7 +3579,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
 
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -3481,7 +3629,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -4184,7 +4332,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
                   'status': 'available',
                   'host': self.data.fake_host,
                   'NumberOfBlocks': 100,
-                  'BlockSize': 512
+                  'BlockSize': self.data.block_size
                   }
         common = self.driver.common
         common._initial_setup = mock.Mock(
@@ -4217,7 +4365,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
                   'status': 'available',
                   'host': self.data.fake_host,
                   'NumberOfBlocks': 100,
-                  'BlockSize': 512
+                  'BlockSize': self.data.block_size
                   }
         common = self.driver.common
         common._initial_setup = mock.Mock(
@@ -4601,7 +4749,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -4639,7 +4787,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
 
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -4708,7 +4856,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
         return_value=(None, EMCVMAXCommonData.storage_system))
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
-        'get_meta_members_capacity_in_bit',
+        'get_meta_members_capacity_in_byte',
         return_value=[1234567, 7654321])
     @mock.patch.object(
         emc_vmax_utils.EMCVMAXUtils,
@@ -5167,7 +5315,7 @@ class EMCV3DriverTestCase(test.TestCase):
         cloneVol['volume_type_id'] = 'abc'
         cloneVol['provider_location'] = None
         cloneVol['NumberOfBlocks'] = 100
-        cloneVol['BlockSize'] = 512
+        cloneVol['BlockSize'] = self.data.block_size
         self.driver.create_cloned_volume(cloneVol, self.data.test_volume)
 
     @mock.patch.object(
index 604341335d8a8ebc3a27f1eb95c4140e376a1ea7..36d071f266323a109d5bf87dcc0675bd0338125a 100644 (file)
@@ -55,6 +55,7 @@ STRIPECOUNT = 'storagetype:stripecount'
 MEMBERCOUNT = 'storagetype:membercount'
 STRIPED = 'striped'
 CONCATENATED = 'concatenated'
+SMI_VERSION_8 = 800
 # V3
 SLO = 'storagetype:slo'
 WORKLOAD = 'storagetype:workload'
@@ -1319,13 +1320,23 @@ class EMCVMAXCommon(object):
 
         if isinstance(loc, six.string_types):
             name = eval(loc)
+            keys = name['keybindings']
+            systemName = keys['SystemName']
+
+            prefix1 = 'SYMMETRIX+'
+            prefix2 = 'SYMMETRIX-+-'
+            smiversion = self.utils.get_smi_version(self.conn)
+            if smiversion > SMI_VERSION_8 and prefix1 in systemName:
+                keys['SystemName'] = systemName.replace(prefix1, prefix2)
+                name['keybindings'] = keys
 
             instancename = self.utils.get_instance_name(
                 name['classname'], name['keybindings'])
-
             # Handle the case where volume cannot be found.
-            foundVolumeinstance = self.utils.get_existing_instance(
-                self.conn, instancename)
+            try:
+                foundVolumeinstance = self.conn.GetInstance(instancename)
+            except Exception:
+                foundVolumeinstance = None
 
         if foundVolumeinstance is None:
             LOG.debug("Volume %(volumename)s not found on the array.",
@@ -1835,8 +1846,8 @@ class EMCVMAXCommon(object):
         if 'True' in isVolumeBound:
             appendVolumeInstance = (
                 self._unbind_and_get_volume_from_storage_pool(
-                    conn, storageConfigService, assocPoolInstanceName,
-                    appendVolumeInstance.path, 'appendVolume', extraSpecs))
+                    conn, storageConfigService, appendVolumeInstance.path,
+                    'appendVolume', extraSpecs))
 
         return appendVolumeInstance
 
@@ -1862,27 +1873,33 @@ class EMCVMAXCommon(object):
         return volumeInstance
 
     def _unbind_and_get_volume_from_storage_pool(
-            self, conn, storageConfigService, poolInstanceName,
-            volumeInstanceName, volumeName, extraSpecs):
+            self, conn, storageConfigService, volumeInstanceName,
+            volumeName, extraSpecs):
         """Unbind a volume from a pool and return the unbound volume.
 
         :param conn: the connection information to the ecom server
         :param storageConfigService: the storage config service instance name
-        :param poolInstanceName: the pool instance name
         :param volumeInstanceName: the volume instance name
         :param volumeName: string the volumeName
         :param extraSpecs: extra specifications
         :returns: unboundVolumeInstance -- the unbound volume instance
         """
 
-        _rc, job = (
+        rc, job = (
             self.provision.unbind_volume_from_storage_pool(
-                conn, storageConfigService, poolInstanceName,
-                volumeInstanceName,
+                conn, storageConfigService, volumeInstanceName,
                 volumeName, extraSpecs))
-        volumeDict = self.provision.get_volume_dict_from_job(conn, job['Job'])
-        volumeInstance = self.utils.find_volume_instance(
-            self.conn, volumeDict, volumeName)
+        # Check that the volume is unbound
+        volumeInstance = conn.GetInstance(volumeInstanceName)
+        isVolumeBound = self.utils.is_volume_bound_to_pool(
+            conn, volumeInstance)
+        if 'False' not in isVolumeBound:
+            exceptionMessage = (_(
+                "Failed to unbind volume: %(volume)s")
+                % {'volume': volumeInstanceName})
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
         return volumeInstance
 
     def _modify_and_get_composite_volume_instance(
@@ -3334,12 +3351,8 @@ class EMCVMAXCommon(object):
                 controllerConfigurationService,
                 volumeInstance.path, volumeName, extraSpecs)
 
-        LOG.debug("Delete Volume: %(name)s  Method: EMCReturnToStoragePool "
-                  "ConfigService: %(service)s  TheElement: %(vol_instance)s "
-                  "DeviceId: %(deviceId)s.",
-                  {'service': storageConfigService,
-                   'name': volumeName,
-                   'vol_instance': volumeInstance.path,
+        LOG.debug("Deleting Volume: %(name)s with deviceId: %(deviceId)s.",
+                  {'name': volumeName,
                    'deviceId': deviceId})
         try:
             rc = self.provision.delete_volume_from_pool(
@@ -3466,9 +3479,9 @@ class EMCVMAXCommon(object):
         else:  # Composite volume with meta device members.
             # Check if the meta members capacity.
             metaMemberInstanceNames = (
-                self.utils.get_meta_members_of_composite_volume(
-                    self.conn, metaHeadInstanceName))
-            volumeCapacities = self.utils.get_meta_members_capacity_in_bit(
+                self.utils.get_composite_elements(
+                    self.conn, sourceInstance))
+            volumeCapacities = self.utils.get_meta_members_capacity_in_byte(
                 self.conn, metaMemberInstanceNames)
             LOG.debug("Volume capacities:  %(metasizes)s.",
                       {'metasizes': volumeCapacities})
index 728df347ff4e8feadd0f30aee88be95596c1fa7d..8f031a2d0a95df765218efe18cabaa1843285bcf 100644 (file)
@@ -38,9 +38,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
         2.1.2 - Clean up failed clones (bug #1440154)
         2.1.3 - Fixed a problem with FAST support (bug #1435069)
         2.2.0 - Add manage/unmanage
+        2.2.1 - Support for SE 8.0.3
     """
 
-    VERSION = "2.2.0"
+    VERSION = "2.2.1"
 
     def __init__(self, *args, **kwargs):
 
index 46b0a583ff8cb80cdabf744561502d41f392af4f..ea08ebfc003d140b20950d4ace58488415fb82d3 100644 (file)
 #    under the License.
 
 import base64
+import httplib
 import os
 import socket
 import ssl
 import string
 import struct
+import urllib
 
 from eventlet import patcher
 import OpenSSL
 from oslo_log import log as logging
 import six
-from six.moves import http_client
-from six.moves import urllib
 
 from cinder.i18n import _, _LI
 
@@ -74,7 +74,7 @@ def get_default_ca_certs():
 class OpenSSLConnectionDelegator(object):
     """An OpenSSL.SSL.Connection delegator.
 
-    Supplies an additional 'makefile' method which http_client requires
+    Supplies an additional 'makefile' method which httplib requires
     and is not present in OpenSSL.SSL.Connection.
     Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
     a delegator must be used.
@@ -89,7 +89,7 @@ class OpenSSLConnectionDelegator(object):
         return socket._fileobject(self.connection, *args, **kwargs)
 
 
-class HTTPSConnection(http_client.HTTPSConnection):
+class HTTPSConnection(httplib.HTTPSConnection):
     def __init__(self, host, port=None, key_file=None, cert_file=None,
                  strict=None, ca_certs=None, no_verification=False):
         if not pywbemAvailable:
@@ -101,9 +101,9 @@ class HTTPSConnection(http_client.HTTPSConnection):
         else:
             excp_lst = ()
         try:
-            http_client.HTTPSConnection.__init__(self, host, port,
-                                                 key_file=key_file,
-                                                 cert_file=cert_file)
+            httplib.HTTPSConnection.__init__(self, host, port,
+                                             key_file=key_file,
+                                             cert_file=cert_file)
 
             self.key_file = None if key_file is None else key_file
             self.cert_file = None if cert_file is None else cert_file
@@ -255,7 +255,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
     """Send request over HTTP.
 
     Send XML data over HTTP to the specified url. Return the
-    response in XML.  Uses Python's build-in http_client.  x509 may be a
+    response in XML.  Uses Python's build-in httplib.  x509 may be a
     dictionary containing the location of the SSL certificate and key
     files.
     """
@@ -274,7 +274,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
     localAuthHeader = None
     tryLimit = 5
 
-    if isinstance(data, six.text_type):
+    if isinstance(data, unicode):
         data = data.encode('utf-8')
     data = '<?xml version="1.0" encoding="utf-8" ?>\n' + data
 
@@ -309,10 +309,10 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
             h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin)
 
         for hdr in headers:
-            if isinstance(hdr, six.text_type):
+            if isinstance(hdr, unicode):
                 hdr = hdr.encode('utf-8')
             s = map(lambda x: string.strip(x), string.split(hdr, ":", 1))
-            h.putheader(urllib.parse.quote(s[0]), urllib.parse.quote(s[1]))
+            h.putheader(urllib.quote(s[0]), urllib.quote(s[1]))
 
         try:
             h.endheaders()
@@ -328,7 +328,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
             if response.status != 200:
                 raise pywbem.cim_http.Error('HTTP error')
 
-        except http_client.BadStatusLine as arg:
+        except httplib.BadStatusLine as arg:
             msg = (_("Bad Status line returned: %(arg)s.")
                    % {'arg': arg})
             raise pywbem.cim_http.Error(msg)
index 148c03d09241df2a75779214937ff8784cbed0c0..35c6a2d47fddcd355414c5962756c4aea690c202 100644 (file)
@@ -46,9 +46,10 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         2.1.2 - Clean up failed clones (bug #1440154)
         2.1.3 - Fixed a problem with FAST support (bug #1435069)
         2.2.0 - Add manage/unmanage
+        2.2.1 - Support for SE 8.0.3
     """
 
-    VERSION = "2.2.0"
+    VERSION = "2.2.1"
 
     def __init__(self, *args, **kwargs):
 
index 0ef3361fbcd4e0e3011c53fa6044379d30c706db..9a234490a0129289752e97914ac13fbfe120acfd 100644 (file)
@@ -64,7 +64,7 @@ class EMCVMAXProvision(object):
             theElements = [volumeInstanceName]
 
         rc, job = conn.InvokeMethod(
-            'EMCReturnToStoragePool', storageConfigservice,
+            'ReturnElementsToStoragePool', storageConfigservice,
             TheElements=theElements)
 
         if rc != 0:
@@ -338,14 +338,13 @@ class EMCVMAXProvision(object):
                                                       time.time())})
 
     def unbind_volume_from_storage_pool(
-            self, conn, storageConfigService, poolInstanceName,
-            volumeInstanceName, volumeName, extraSpecs):
+            self, conn, storageConfigService, volumeInstanceName,
+            volumeName, extraSpecs):
         """Unbind a volume from a pool and return the unbound volume.
 
         :param conn: the connection information to the ecom server
         :param storageConfigService: the storage configuration service
             instance name
-        :param poolInstanceName: the pool instance name
         :param volumeInstanceName: the volume instance name
         :param volumeName: the volume name
         :param extraSpecs: additional info
@@ -358,7 +357,6 @@ class EMCVMAXProvision(object):
         rc, job = conn.InvokeMethod(
             'EMCUnBindElement',
             storageConfigService,
-            InPool=poolInstanceName,
             TheElement=volumeInstanceName)
 
         if rc != 0:
@@ -1070,14 +1068,16 @@ class EMCVMAXProvision(object):
              'relationName': relationName,
              'srcGroup': srcGroupInstanceName,
              'tgtGroup': tgtGroupInstanceName})
-        # 8 for clone.
+        # SyncType 8 - clone.
+        # CopyState 4 - Synchronized.
         rc, job = conn.InvokeMethod(
             'CreateGroupReplica',
             replicationService,
             RelationshipName=relationName,
             SourceGroup=srcGroupInstanceName,
             TargetGroup=tgtGroupInstanceName,
-            SyncType=self.utils.get_num(8, '16'))
+            SyncType=self.utils.get_num(8, '16'),
+            WaitForCopyState=self.utils.get_num(4, '16'))
 
         if rc != 0:
             rc, errordesc = self.utils.wait_for_job_complete(conn, job,
index 6f2f487c4dafd80dc600f6823e4ef40964459fef..99992dfbac9cbd4d0a4300a43ac23c1fa9df1c2c 100644 (file)
@@ -1224,13 +1224,14 @@ class EMCVMAXUtils(object):
         LOG.debug(
             "storagePoolName: %(poolName)s, storageSystemName: %(array)s.",
             {'poolName': storagePoolName, 'array': storageSystemName})
-        poolInstanceNames = conn.EnumerateInstanceNames(
-            'EMC_VirtualProvisioningPool')
+        storageSystemInstanceName = self.find_storageSystem(conn,
+                                                            storageSystemName)
+        poolInstanceNames = conn.AssociatorNames(
+            storageSystemInstanceName,
+            ResultClass='EMC_VirtualProvisioningPool')
         for poolInstanceName in poolInstanceNames:
-            poolName, systemName = (
-                self.parse_pool_instance_id(poolInstanceName['InstanceID']))
-            if (poolName == storagePoolName and
-                    storageSystemName in systemName):
+            poolName = self._get_pool_name(conn, poolInstanceName)
+            if (poolName == storagePoolName):
                 # Check that the pool hasn't been recently deleted.
                 instance = self.get_existing_instance(conn, poolInstanceName)
                 if instance is None:
@@ -1622,27 +1623,13 @@ class EMCVMAXUtils(object):
         :returns: foundPoolInstanceName
         :returns: string -- systemNameStr
         """
-        foundPoolInstanceName = None
         vpoolInstanceNames = conn.AssociatorNames(
             storageSystemInstanceName,
             ResultClass='EMC_VirtualProvisioningPool')
 
-        for vpoolInstanceName in vpoolInstanceNames:
-            poolInstanceId = vpoolInstanceName['InstanceID']
-            # Example: SYMMETRIX+000195900551+TP+Sol_Innov
-            poolnameStr, systemNameStr = self.parse_pool_instance_id(
-                poolInstanceId)
-            if poolnameStr is not None and systemNameStr is not None:
-                if six.text_type(poolNameInStr) == six.text_type(poolnameStr):
-                    # check that the pool hasn't recently been deleted.
-                    try:
-                        conn.GetInstance(vpoolInstanceName)
-                        foundPoolInstanceName = vpoolInstanceName
-                    except Exception:
-                        foundPoolInstanceName = None
-                    break
-
-        return foundPoolInstanceName, systemNameStr
+        return self._get_pool_instance_and_system_name(
+            conn, vpoolInstanceNames, storageSystemInstanceName,
+            poolNameInStr)
 
     def get_pool_and_system_name_v3(
             self, conn, storageSystemInstanceName, poolNameInStr):
@@ -1654,27 +1641,54 @@ class EMCVMAXUtils(object):
         :returns: foundPoolInstanceName
         :returns: string -- systemNameStr
         """
-        foundPoolInstanceName = None
         srpPoolInstanceNames = conn.AssociatorNames(
             storageSystemInstanceName,
             ResultClass='Symm_SRPStoragePool')
 
-        for srpPoolInstanceName in srpPoolInstanceNames:
-            poolInstanceID = srpPoolInstanceName['InstanceID']
+        return self._get_pool_instance_and_system_name(
+            conn, srpPoolInstanceNames, storageSystemInstanceName,
+            poolNameInStr)
+
+    def _get_pool_instance_and_system_name(
+            self, conn, poolInstanceNames, storageSystemInstanceName,
+            poolname):
+        """Get the pool instance and the system name
+
+        :param conn: the ecom connection
+        :param poolInstanceNames: list of pool instances
+        :param storageSystemInstanceName: storage system instance name
+        :param poolname: pool name (string)
+        :returns: foundPoolInstanceName, systemNameStr
+        """
+        foundPoolInstanceName = None
+        poolnameStr = None
+        systemNameStr = storageSystemInstanceName['Name']
+        for poolInstanceName in poolInstanceNames:
             # Example: SYMMETRIX-+-000196700535-+-SR-+-SRP_1
-            poolnameStr, systemNameStr = self.parse_pool_instance_id_v3(
-                poolInstanceID)
-            if poolnameStr is not None and systemNameStr is not None:
-                if six.text_type(poolNameInStr) == six.text_type(poolnameStr):
-                    try:
-                        conn.GetInstance(srpPoolInstanceName)
-                        foundPoolInstanceName = srpPoolInstanceName
-                    except Exception:
-                        foundPoolInstanceName = None
+            # Example: SYMMETRIX+000195900551+TP+Sol_Innov
+            poolnameStr = self._get_pool_name(conn, poolInstanceName)
+            if poolnameStr is not None:
+                if six.text_type(poolname) == six.text_type(poolnameStr):
+                    foundPoolInstanceName = poolInstanceName
                     break
 
         return foundPoolInstanceName, systemNameStr
 
+    def _get_pool_name(self, conn, poolInstanceName):
+        """The pool name from the instance
+
+        :param conn: the ecom connection
+        :param poolInstanceName: the pool instance
+        :returns: poolnameStr
+        """
+        poolnameStr = None
+        try:
+            poolInstance = conn.GetInstance(poolInstanceName)
+            poolnameStr = poolInstance['ElementName']
+        except Exception:
+            pass
+        return poolnameStr
+
     def find_storageSystem(self, conn, arrayStr):
         """Find an array instance name given the array name.
 
@@ -1831,21 +1845,28 @@ class EMCVMAXUtils(object):
         LOG.debug("metaMembers: %(members)s.", {'members': metaMembers})
         return metaMembers
 
-    def get_meta_members_capacity_in_bit(self, conn, volumeInstanceNames):
-        """Get the capacity in bits of all meta device member volumes.
+    def get_meta_members_capacity_in_byte(self, conn, volumeInstanceNames):
+        """Get the capacity in byte of all meta device member volumes.
 
         :param conn: the ecom connection
         :param volumeInstanceNames: array contains meta device member volumes
         :returns: array contains capacities of each member device in bits
         """
-        capacitiesInBit = []
+        capacitiesInByte = []
+        headVolume = conn.GetInstance(volumeInstanceNames[0])
+        totalSizeInByte = (
+            headVolume['ConsumableBlocks'] * headVolume['BlockSize'])
+        volumeInstanceNames.pop(0)
         for volumeInstanceName in volumeInstanceNames:
             volumeInstance = conn.GetInstance(volumeInstanceName)
             numOfBlocks = volumeInstance['ConsumableBlocks']
             blockSize = volumeInstance['BlockSize']
-            volumeSizeInbits = numOfBlocks * blockSize
-            capacitiesInBit.append(volumeSizeInbits)
-        return capacitiesInBit
+            volumeSizeInByte = numOfBlocks * blockSize
+            capacitiesInByte.append(volumeSizeInByte)
+            totalSizeInByte = totalSizeInByte - volumeSizeInByte
+
+        capacitiesInByte.insert(0, totalSizeInByte)
+        return capacitiesInByte
 
     def get_existing_instance(self, conn, instanceName):
         """Check that the instance name still exists and return the instance.
@@ -2112,3 +2133,48 @@ class EMCVMAXUtils(object):
                 {'source': sourceDeviceId, 'storageSystem': storageSystem})
 
         return foundSyncInstanceName
+
+    def get_smi_version(self, conn):
+        intVersion = 0
+        swIndentityInstances = conn.EnumerateInstances(
+            'SE_ManagementServerSoftwareIdentity')
+        if swIndentityInstances:
+            swIndentityInstance = swIndentityInstances[0]
+            majorVersion = swIndentityInstance['MajorVersion']
+            minorVersion = swIndentityInstance['MinorVersion']
+            revisionNumber = swIndentityInstance['RevisionNumber']
+
+            intVersion = int(str(majorVersion) + str(minorVersion)
+                             + str(revisionNumber))
+
+            LOG.debug("Major version: %(majV)lu, Minor version: %(minV)lu, "
+                      "Revision number: %(revNum)lu, Version: %(intV)lu.",
+                      {'majV': majorVersion,
+                       'minV': minorVersion,
+                       'revNum': revisionNumber,
+                       'intV': intVersion})
+        return intVersion
+
+    def get_composite_elements(
+            self, conn, volumeInstance):
+        """Get the meta members of a composite volume.
+
+        :param conn: ECOM connection
+        :param volumeInstance: the volume instance
+        :returns memberVolumes: a list of meta members
+        """
+        memberVolumes = None
+        storageSystemName = volumeInstance['SystemName']
+        elementCompositionService = self.find_element_composition_service(
+            conn, storageSystemName)
+        rc, ret = conn.InvokeMethod(
+            'GetCompositeElements',
+            elementCompositionService,
+            TheElement=volumeInstance.path)
+
+        if 'OutElements' in ret:
+            LOG.debug("Get composite elements of volume "
+                      "%(volume)s rc=%(rc)d, ret=%(ret)s",
+                      {'volume': volumeInstance.path, 'rc': rc, 'ret': ret})
+            memberVolumes = ret['OutElements']
+        return memberVolumes