]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add EMC SMI-S FC Driver in Cinder
authorXing Yang <xing.yang@emc.com>
Tue, 11 Feb 2014 05:00:53 +0000 (00:00 -0500)
committerXing Yang <xing.yang@emc.com>
Fri, 14 Feb 2014 06:05:30 +0000 (01:05 -0500)
Add an EMC SMI-S FC Driver in Cinder. It supports all required
driver features.
Also made enhancements to the existing SMI-S iSCSI Driver
(performance enhancement, multiple pool support and thick/thin
provisioning, extend volume).
emc_smis_common.py is the common code used by both iSCSI driver
(emc_smis_iscsi.py) and FC driver (emc_smis_fc.py).

Implements blueprint emc-fibre-channel-volume-driver

Change-Id: Idace398ef60863811911511bcbbfcecb3bf58270

cinder/tests/test_emc.py
cinder/volume/drivers/emc/emc_smis_common.py
cinder/volume/drivers/emc/emc_smis_fc.py [new file with mode: 0644]
cinder/volume/drivers/emc/emc_smis_iscsi.py

index d7bd2801794db2e97a8459214b95df118eb89c85..1a13ba01ca65d8a530ac390e4f5ef1216a3fbd10 100644 (file)
@@ -1,6 +1,4 @@
-
-# Copyright (c) 2012 EMC Corporation, Inc.
-# Copyright (c) 2012 OpenStack Foundation
+# Copyright (c) 2012 - 2014 EMC Corporation, Inc.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+
 import os
 import shutil
 import tempfile
 from xml.dom.minidom import Document
 
-import mox
+import mock
 
 from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder import test
-from cinder.volume import configuration as conf
+from cinder import units
 from cinder.volume.drivers.emc.emc_smis_common import EMCSMISCommon
+from cinder.volume.drivers.emc.emc_smis_fc import EMCSMISFCDriver
 from cinder.volume.drivers.emc.emc_smis_iscsi import EMCSMISISCSIDriver
-
+from cinder.volume import volume_types
 
 CINDER_EMC_CONFIG_FILE = '/etc/cinder/cinder_emc_config.xml'
 LOG = logging.getLogger(__name__)
 
-config_file_name = 'cinder_emc_config.xml'
-storage_system = 'CLARiiON+APM00123456789'
-storage_system_vmax = 'SYMMETRIX+000195900551'
-lunmaskctrl_id = 'CLARiiON+APM00123456789+00aa11bb22cc33dd44ff55gg66hh77ii88jj'
-initiator1 = 'iqn.1993-08.org.debian:01:1a2b3c4d5f6g'
-stconf_service_creationclass = 'Clar_StorageConfigurationService'
-ctrlconf_service_creationclass = 'Clar_ControllerConfigurationService'
-rep_service_creationclass = 'Clar_ReplicationService'
-vol_creationclass = 'Clar_StorageVolume'
-pool_creationclass = 'Clar_UnifiedStoragePool'
-lunmask_creationclass = 'Clar_LunMaskingSCSIProtocolController'
-unit_creationclass = 'CIM_ProtocolControllerForUnit'
-storage_type = 'gold'
-
-test_volume = {'name': 'vol1',
-               'size': 1,
-               'volume_name': 'vol1',
-               'id': '1',
-               'provider_auth': None,
-               'project_id': 'project',
-               'display_name': 'vol1',
-               'display_description': 'test volume',
-               'volume_type_id': None}
-test_failed_volume = {'name': 'failed_vol',
-                      'size': 1,
-                      'volume_name': 'failed_vol',
-                      'id': '4',
-                      'provider_auth': None,
-                      'project_id': 'project',
-                      'display_name': 'failed_vol',
-                      'display_description': 'test failed volume',
-                      'volume_type_id': None}
-test_snapshot = {'name': 'snapshot1',
-                 'size': 1,
-                 'id': '4444',
-                 'volume_name': 'vol1',
-                 'volume_size': 1,
-                 'project_id': 'project'}
-test_clone = {'name': 'clone1',
-              'size': 1,
-              'volume_name': 'vol1',
-              'id': '2',
-              'provider_auth': None,
-              'project_id': 'project',
-              'display_name': 'clone1',
-              'display_description': 'volume created from snapshot',
-              'volume_type_id': None}
-test_clone3 = {'name': 'clone3',
-               'size': 1,
-               'volume_name': 'vol1',
-               'id': '3',
-               'provider_auth': None,
-               'project_id': 'project',
-               'display_name': 'clone3',
-               'display_description': 'cloned volume',
-               'volume_type_id': None}
-test_snapshot_vmax = {'name': 'snapshot_vmax',
-                      'size': 1,
-                      'id': '4445',
-                      'volume_name': 'vol1',
-                      'volume_size': 1,
-                      'project_id': 'project'}
-failed_snapshot_replica = {'name': 'failed_snapshot_replica',
-                           'size': 1,
-                           'volume_name': 'vol1',
-                           'id': '5',
-                           'provider_auth': None,
-                           'project_id': 'project',
-                           'display_name': 'vol1',
-                           'display_description': 'failed snapshot replica',
-                           'volume_type_id': None}
-failed_snapshot_sync = {'name': 'failed_snapshot_sync',
-                        'size': 1,
-                        'volume_name': 'vol1',
-                        'id': '6',
-                        'provider_auth': None,
-                        'project_id': 'project',
-                        'display_name': 'failed_snapshot_sync',
-                        'display_description': 'failed snapshot sync',
-                        'volume_type_id': None}
-failed_clone_replica = {'name': 'failed_clone_replica',
-                        'size': 1,
-                        'volume_name': 'vol1',
-                        'id': '7',
-                        'provider_auth': None,
-                        'project_id': 'project',
-                        'display_name': 'vol1',
-                        'display_description': 'failed clone replica',
-                        'volume_type_id': None}
-failed_clone_sync = {'name': 'failed_clone_sync',
-                     'size': 1,
-                     'volume_name': 'vol1',
-                     'id': '8',
-                     'provider_auth': None,
-                     'project_id': 'project',
-                     'display_name': 'vol1',
-                     'display_description': 'failed clone sync',
-                     'volume_type_id': None}
-failed_delete_vol = {'name': 'failed_delete_vol',
-                     'size': 1,
-                     'volume_name': 'failed_delete_vol',
-                     'id': '99999',
-                     'provider_auth': None,
-                     'project_id': 'project',
-                     'display_name': 'failed delete vol',
-                     'display_description': 'failed delete volume',
-                     'volume_type_id': None}
-
 
 class EMC_StorageVolume(dict):
     pass
@@ -150,26 +42,199 @@ class SE_ConcreteJob(dict):
     pass
 
 
+class SE_StorageHardwareID(dict):
+    pass
+
+
+class FakeCIMInstanceName(dict):
+
+    def fake_getinstancename(self, classname, bindings):
+        instancename = FakeCIMInstanceName()
+        for key in bindings:
+            instancename[key] = bindings[key]
+        instancename.classname = classname
+        instancename.namespace = 'root/emc'
+        return instancename
+
+
+class FakeDB():
+    def volume_update(self, context, volume_id, model_update):
+        pass
+
+    def snapshot_update(self, context, snapshot_id, model_update):
+        pass
+
+    def volume_get(self, context, volume_id):
+        conn = FakeEcomConnection()
+        objectpath = {}
+        objectpath['CreationClassName'] = 'Clar_StorageVolume'
+        if volume_id == 'vol1':
+            device_id = '1'
+            objectpath['DeviceID'] = device_id
+        else:
+            objectpath['DeviceID'] = volume_id
+        return conn.GetInstance(objectpath)
+
+
+class EMCSMISCommonData():
+    connector = {'ip': '10.0.0.2',
+                 'initiator': 'iqn.1993-08.org.debian:01:222',
+                 'wwpns': ["123456789012345", "123456789054321"],
+                 'wwnns': ["223456789012345", "223456789054321"],
+                 'host': 'fakehost'}
+
+    config_file_name = 'cinder_emc_config.xml'
+    storage_system = 'CLARiiON+APM00123456789'
+    storage_system_vmax = 'SYMMETRIX+000195900551'
+    lunmaskctrl_id =\
+        'CLARiiON+APM00123456789+00aa11bb22cc33dd44ff55gg66hh77ii88jj'
+    initiator1 = 'iqn.1993-08.org.debian:01:1a2b3c4d5f6g'
+    stconf_service_creationclass = 'Clar_StorageConfigurationService'
+    ctrlconf_service_creationclass = 'Clar_ControllerConfigurationService'
+    rep_service_creationclass = 'Clar_ReplicationService'
+    vol_creationclass = 'Clar_StorageVolume'
+    pool_creationclass = 'Clar_UnifiedStoragePool'
+    lunmask_creationclass = 'Clar_LunMaskingSCSIProtocolController'
+    unit_creationclass = 'CIM_ProtocolControllerForUnit'
+    storage_type = 'gold'
+
+    test_volume = {'name': 'vol1',
+                   'size': 1,
+                   'volume_name': 'vol1',
+                   'id': '1',
+                   'provider_auth': None,
+                   'project_id': 'project',
+                   'display_name': 'vol1',
+                   'display_description': 'test volume',
+                   'volume_type_id': None}
+    test_failed_volume = {'name': 'failed_vol',
+                          'size': 1,
+                          'volume_name': 'failed_vol',
+                          'id': '4',
+                          'provider_auth': None,
+                          'project_id': 'project',
+                          'display_name': 'failed_vol',
+                          'display_description': 'test failed volume',
+                          'volume_type_id': None}
+    test_snapshot = {'name': 'snapshot1',
+                     'size': 1,
+                     'id': '4444',
+                     'volume_name': 'vol-vol1',
+                     'volume_size': 1,
+                     'project_id': 'project'}
+    test_clone = {'name': 'clone1',
+                  'size': 1,
+                  'volume_name': 'vol1',
+                  'id': '2',
+                  'provider_auth': None,
+                  'project_id': 'project',
+                  'display_name': 'clone1',
+                  'display_description': 'volume created from snapshot',
+                  'volume_type_id': None}
+    test_clone3 = {'name': 'clone3',
+                   'size': 1,
+                   'volume_name': 'vol1',
+                   'id': '3',
+                   'provider_auth': None,
+                   'project_id': 'project',
+                   'display_name': 'clone3',
+                   'display_description': 'cloned volume',
+                   'volume_type_id': None}
+    test_snapshot_vmax = {'name': 'snapshot_vmax',
+                          'size': 1,
+                          'id': '4445',
+                          'volume_name': 'vol-vol1',
+                          'volume_size': 1,
+                          'project_id': 'project'}
+    failed_snapshot_replica = {'name': 'failed_snapshot_replica',
+                               'size': 1,
+                               'volume_name': 'vol-vol1',
+                               'id': '5',
+                               'provider_auth': None,
+                               'project_id': 'project',
+                               'display_name': 'vol1',
+                               'display_description':
+                               'failed snapshot replica',
+                               'volume_type_id': None}
+    failed_snapshot_sync = {'name': 'failed_snapshot_sync',
+                            'size': 1,
+                            'volume_name': 'vol-vol1',
+                            'id': '6',
+                            'provider_auth': None,
+                            'project_id': 'project',
+                            'display_name': 'failed_snapshot_sync',
+                            'display_description': 'failed snapshot sync',
+                            'volume_type_id': None}
+    failed_clone_replica = {'name': 'failed_clone_replica',
+                            'size': 1,
+                            'volume_name': 'vol1',
+                            'id': '7',
+                            'provider_auth': None,
+                            'project_id': 'project',
+                            'display_name': 'vol1',
+                            'display_description': 'failed clone replica',
+                            'volume_type_id': None}
+    failed_clone_sync = {'name': 'failed_clone_sync',
+                         'size': 1,
+                         'volume_name': 'vol1',
+                         'id': '8',
+                         'provider_auth': None,
+                         'project_id': 'project',
+                         'display_name': 'vol1',
+                         'display_description': 'failed clone sync',
+                         'volume_type_id': None}
+    failed_delete_vol = {'name': 'failed_delete_vol',
+                         'size': 1,
+                         'volume_name': 'failed_delete_vol',
+                         'id': '99999',
+                         'provider_auth': None,
+                         'project_id': 'project',
+                         'display_name': 'failed delete vol',
+                         'display_description': 'failed delete volume',
+                         'volume_type_id': None}
+    failed_extend_vol = {'name': 'failed_extend_vol',
+                         'size': 1,
+                         'volume_name': 'failed_extend_vol',
+                         'id': '9',
+                         'provider_auth': None,
+                         'project_id': 'project',
+                         'display_name': 'failed_extend_vol',
+                         'display_description': 'test failed extend volume',
+                         'volume_type_id': None}
+
+
 class FakeEcomConnection():
 
+    def __init__(self, *args, **kwargs):
+        self.data = EMCSMISCommonData()
+
     def InvokeMethod(self, MethodName, Service, ElementName=None, InPool=None,
                      ElementType=None, Size=None,
                      SyncType=None, SourceElement=None,
                      Operation=None, Synchronization=None,
-                     TheElements=None,
+                     TheElements=None, TheElement=None,
                      LUNames=None, InitiatorPortIDs=None, DeviceAccesses=None,
                      ProtocolControllers=None,
-                     MaskingGroup=None, Members=None):
+                     MaskingGroup=None, Members=None,
+                     HardwareId=None):
 
         rc = 0L
         myjob = SE_ConcreteJob()
         myjob.classname = 'SE_ConcreteJob'
         myjob['InstanceID'] = '9999'
         myjob['status'] = 'success'
+        myjob['type'] = ElementName
         if ElementName == 'failed_vol' and \
                 MethodName == 'CreateOrModifyElementFromStoragePool':
             rc = 10L
             myjob['status'] = 'failure'
+        elif TheElement and TheElement['ElementName'] == 'failed_extend_vol' \
+                and MethodName == 'CreateOrModifyElementFromStoragePool':
+            rc = 10L
+            myjob['status'] = 'failure'
+        elif MethodName == 'CreateOrModifyElementFromStoragePool':
+            rc = 0L
+            myjob['status'] = 'success'
         elif ElementName == 'failed_snapshot_replica' and \
                 MethodName == 'CreateElementReplica':
             rc = 10L
@@ -195,6 +260,18 @@ class FakeEcomConnection():
                 MethodName == 'EMCReturnToStoragePool':
             rc = 10L
             myjob['status'] = 'failure'
+        elif HardwareId:
+            rc = 0L
+            targetendpoints = {}
+            endpoints = []
+            endpoint = {}
+            endpoint['Name'] = '1234567890123'
+            endpoints.append(endpoint)
+            endpoint2 = {}
+            endpoint2['Name'] = '0987654321321'
+            endpoints.append(endpoint2)
+            targetendpoints['TargetEndpoints'] = endpoints
+            return rc, targetendpoints
 
         job = {'Job': myjob}
         return rc, job
@@ -223,6 +300,8 @@ class FakeEcomConnection():
             result = self._enum_lunmaskctrls()
         elif name == 'EMC_StorageProcessorSystem':
             result = self._enum_processors()
+        elif name == 'EMC_StorageHardwareIDManagementService':
+            result = self._enum_hdwidmgmts()
         else:
             result = self._default_enum()
         return result
@@ -233,6 +312,8 @@ class FakeEcomConnection():
             result = self._enum_pool_details()
         elif name == 'EMC_UnifiedStoragePool':
             result = self._enum_pool_details()
+        elif name == 'SE_StorageHardwareID':
+            result = self._enum_storhdwids()
         else:
             result = self._default_enum()
         return result
@@ -243,7 +324,7 @@ class FakeEcomConnection():
         except KeyError:
             name = objectpath.classname
         result = None
-        if name == 'Clar_StorageVolume':
+        if name == 'Clar_StorageVolume' or name == 'Symm_StorageVolume':
             result = self._getinstance_storagevolume(objectpath)
         elif name == 'CIM_ProtocolControllerForUnit':
             result = self._getinstance_unit(objectpath)
@@ -263,6 +344,9 @@ class FakeEcomConnection():
             result = self._assoc_hdwid()
         elif resultClass == 'EMC_iSCSIProtocolEndpoint':
             result = self._assoc_endpoint()
+        # Added test for EMC_StorageVolume
+        elif resultClass == 'EMC_StorageVolume':
+            result = self._assoc_storagevolume(objectpath)
         else:
             result = self._default_assoc(objectpath)
         return result
@@ -286,26 +370,26 @@ class FakeEcomConnection():
         return result
 
     def _ref_unitnames(self):
-        units = []
-        unit = {}
+        unitnames = []
+        unitname = {}
 
         dependent = {}
-        dependent['CreationClassName'] = vol_creationclass
-        dependent['DeviceID'] = test_volume['id']
-        dependent['ElementName'] = test_volume['name']
-        dependent['SystemName'] = storage_system
+        dependent['CreationClassName'] = self.data.vol_creationclass
+        dependent['DeviceID'] = self.data.test_volume['id']
+        dependent['ElementName'] = self.data.test_volume['name']
+        dependent['SystemName'] = self.data.storage_system
 
         antecedent = {}
-        antecedent['CreationClassName'] = lunmask_creationclass
-        antecedent['DeviceID'] = lunmaskctrl_id
-        antecedent['SystemName'] = storage_system
+        antecedent['CreationClassName'] = self.data.lunmask_creationclass
+        antecedent['DeviceID'] = self.data.lunmaskctrl_id
+        antecedent['SystemName'] = self.data.storage_system
 
-        unit['Dependent'] = dependent
-        unit['Antecedent'] = antecedent
-        unit['CreationClassName'] = unit_creationclass
-        units.append(unit)
+        unitname['Dependent'] = dependent
+        unitname['Antecedent'] = antecedent
+        unitname['CreationClassName'] = self.data.unit_creationclass
+        unitnames.append(unitname)
 
-        return units
+        return unitnames
 
     def _default_ref(self, objectpath):
         return objectpath
@@ -313,7 +397,7 @@ class FakeEcomConnection():
     def _assoc_hdwid(self):
         assocs = []
         assoc = {}
-        assoc['StorageID'] = initiator1
+        assoc['StorageID'] = self.data.initiator1
         assocs.append(assoc)
         return assocs
 
@@ -321,7 +405,42 @@ class FakeEcomConnection():
         assocs = []
         assoc = {}
         assoc['Name'] = 'iqn.1992-04.com.emc:cx.apm00123907237.a8,t,0x0001'
-        assoc['SystemName'] = storage_system + '+SP_A+8'
+        assoc['SystemName'] = self.data.storage_system + '+SP_A+8'
+        assocs.append(assoc)
+        return assocs
+
+    # Added test for EMC_StorageVolume associators
+    def _assoc_storagevolume(self, objectpath):
+        assocs = []
+        if objectpath['type'] == 'failed_delete_vol':
+            vol = self.data.failed_delete_vol
+        elif objectpath['type'] == 'vol1':
+            vol = self.data.test_volume
+        elif objectpath['type'] == 'failed_vol':
+            vol = self.data.test_failed_volume
+        elif objectpath['type'] == 'failed_clone_sync':
+            vol = self.data.failed_clone_sync
+        elif objectpath['type'] == 'failed_clone_replica':
+            vol = self.data.failed_clone_replica
+        elif objectpath['type'] == 'failed_snapshot_replica':
+            vol = self.data.failed_snapshot_replica
+        elif objectpath['type'] == 'failed_snapshot_sync':
+            vol = self.data.failed_snapshot_sync
+        elif objectpath['type'] == 'clone1':
+            vol = self.data.test_clone
+        elif objectpath['type'] == 'clone3':
+            vol = self.data.test_clone3
+        elif objectpath['type'] == 'snapshot1':
+            vol = self.data.test_snapshot
+        elif objectpath['type'] == 'snapshot_vmax':
+            vol = self.data.test_snapshot_vmax
+        elif objectpath['type'] == 'failed_extend_vol':
+            vol = self.data.failed_extend_vol
+        else:
+            return None
+
+        vol['DeviceID'] = vol['id']
+        assoc = self._getinstance_storagevolume(vol)
         assocs.append(assoc)
         return assocs
 
@@ -335,13 +454,18 @@ class FakeEcomConnection():
         return objectpath
 
     def _getinstance_storagevolume(self, objectpath):
+        foundinstance = None
         instance = EMC_StorageVolume()
         vols = self._enum_storagevolumes()
         for vol in vols:
             if vol['DeviceID'] == objectpath['DeviceID']:
                 instance = vol
                 break
-        return instance
+        if not instance:
+            foundinstance = None
+        else:
+            foundinstance = instance
+        return foundinstance
 
     def _getinstance_syncsvsv(self, objectpath):
         foundsync = None
@@ -355,28 +479,28 @@ class FakeEcomConnection():
 
     def _getinstance_lunmask(self):
         lunmask = {}
-        lunmask['CreationClassName'] = lunmask_creationclass
-        lunmask['DeviceID'] = lunmaskctrl_id
-        lunmask['SystemName'] = storage_system
+        lunmask['CreationClassName'] = self.data.lunmask_creationclass
+        lunmask['DeviceID'] = self.data.lunmaskctrl_id
+        lunmask['SystemName'] = self.data.storage_system
         return lunmask
 
     def _getinstance_unit(self, objectpath):
         unit = {}
 
         dependent = {}
-        dependent['CreationClassName'] = vol_creationclass
-        dependent['DeviceID'] = test_volume['id']
-        dependent['ElementName'] = test_volume['name']
-        dependent['SystemName'] = storage_system
+        dependent['CreationClassName'] = self.data.vol_creationclass
+        dependent['DeviceID'] = self.data.test_volume['id']
+        dependent['ElementName'] = self.data.test_volume['name']
+        dependent['SystemName'] = self.data.storage_system
 
         antecedent = {}
-        antecedent['CreationClassName'] = lunmask_creationclass
-        antecedent['DeviceID'] = lunmaskctrl_id
-        antecedent['SystemName'] = storage_system
+        antecedent['CreationClassName'] = self.data.lunmask_creationclass
+        antecedent['DeviceID'] = self.data.lunmaskctrl_id
+        antecedent['SystemName'] = self.data.storage_system
 
         unit['Dependent'] = dependent
         unit['Antecedent'] = antecedent
-        unit['CreationClassName'] = unit_creationclass
+        unit['CreationClassName'] = self.data.unit_creationclass
         unit['DeviceNumber'] = '0'
 
         return unit
@@ -400,31 +524,34 @@ class FakeEcomConnection():
     def _enum_replicationservices(self):
         rep_services = []
         rep_service = {}
-        rep_service['SystemName'] = storage_system
-        rep_service['CreationClassName'] = rep_service_creationclass
+        rep_service['SystemName'] = self.data.storage_system
+        rep_service['CreationClassName'] = self.data.rep_service_creationclass
         rep_services.append(rep_service)
         return rep_services
 
     def _enum_stconfsvcs(self):
         conf_services = []
         conf_service = {}
-        conf_service['SystemName'] = storage_system
-        conf_service['CreationClassName'] = stconf_service_creationclass
+        conf_service['SystemName'] = self.data.storage_system
+        conf_service['CreationClassName'] =\
+            self.data.stconf_service_creationclass
         conf_services.append(conf_service)
         return conf_services
 
     def _enum_ctrlconfsvcs(self):
         conf_services = []
         conf_service = {}
-        conf_service['SystemName'] = storage_system
-        conf_service['CreationClassName'] = ctrlconf_service_creationclass
+        conf_service['SystemName'] = self.data.storage_system
+        conf_service['CreationClassName'] =\
+            self.data.ctrlconf_service_creationclass
         conf_services.append(conf_service)
         return conf_services
 
     def _enum_pools(self):
         pools = []
         pool = {}
-        pool['InstanceID'] = storage_system + '+U+' + storage_type
+        pool['InstanceID'] = self.data.storage_system + '+U+' +\
+            self.data.storage_type
         pool['CreationClassName'] = 'Clar_UnifiedStoragePool'
         pools.append(pool)
         return pools
@@ -432,7 +559,8 @@ class FakeEcomConnection():
     def _enum_pool_details(self):
         pools = []
         pool = {}
-        pool['InstanceID'] = storage_system + '+U+' + storage_type
+        pool['InstanceID'] = self.data.storage_system + '+U+' +\
+            self.data.storage_type
         pool['CreationClassName'] = 'Clar_UnifiedStoragePool'
         pool['TotalManagedSpace'] = 12345678
         pool['RemainingManagedSpace'] = 123456
@@ -441,90 +569,239 @@ class FakeEcomConnection():
 
     def _enum_storagevolumes(self):
         vols = []
+
         vol = EMC_StorageVolume()
+        vol['name'] = self.data.test_volume['name']
         vol['CreationClassName'] = 'Clar_StorageVolume'
-        vol['ElementName'] = test_volume['name']
-        vol['DeviceID'] = test_volume['id']
-        vol['SystemName'] = storage_system
-        vol.path = {'DeviceID': vol['DeviceID']}
+        vol['ElementName'] = self.data.test_volume['name']
+        vol['DeviceID'] = self.data.test_volume['id']
+        vol['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        vol.path = vol
+        vol.path.classname = vol['CreationClassName']
+
+        name = {}
+        name['classname'] = 'Clar_StorageVolume'
+        keys = {}
+        keys['CreationClassName'] = 'Clar_StorageVolume'
+        keys['SystemName'] = self.data.storage_system
+        keys['DeviceID'] = vol['DeviceID']
+        keys['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name['keybindings'] = keys
+        vol['provider_location'] = str(name)
+
         vols.append(vol)
 
         snap_vol = EMC_StorageVolume()
+        snap_vol['name'] = self.data.test_snapshot['name']
         snap_vol['CreationClassName'] = 'Clar_StorageVolume'
-        snap_vol['ElementName'] = test_snapshot['name']
-        snap_vol['DeviceID'] = test_snapshot['id']
-        snap_vol['SystemName'] = storage_system
-        snap_vol.path = {'DeviceID': snap_vol['DeviceID']}
+        snap_vol['ElementName'] = self.data.test_snapshot['name']
+        snap_vol['DeviceID'] = self.data.test_snapshot['id']
+        snap_vol['SystemName'] = self.data.storage_system
+        # Added vol to path
+        snap_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        snap_vol.path = snap_vol
+        snap_vol.path.classname = snap_vol['CreationClassName']
+
+        name2 = {}
+        name2['classname'] = 'Clar_StorageVolume'
+        keys2 = {}
+        keys2['CreationClassName'] = 'Clar_StorageVolume'
+        keys2['SystemName'] = self.data.storage_system
+        keys2['DeviceID'] = snap_vol['DeviceID']
+        keys2['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name2['keybindings'] = keys2
+        snap_vol['provider_location'] = str(name2)
+
         vols.append(snap_vol)
 
         clone_vol = EMC_StorageVolume()
+        clone_vol['name'] = self.data.test_clone['name']
         clone_vol['CreationClassName'] = 'Clar_StorageVolume'
-        clone_vol['ElementName'] = test_clone['name']
-        clone_vol['DeviceID'] = test_clone['id']
-        clone_vol['SystemName'] = storage_system
-        clone_vol.path = {'DeviceID': clone_vol['DeviceID']}
+        clone_vol['ElementName'] = self.data.test_clone['name']
+        clone_vol['DeviceID'] = self.data.test_clone['id']
+        clone_vol['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        clone_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        clone_vol.path = clone_vol
+        clone_vol.path.classname = clone_vol['CreationClassName']
         vols.append(clone_vol)
 
         clone_vol3 = EMC_StorageVolume()
+        clone_vol3['name'] = self.data.test_clone3['name']
         clone_vol3['CreationClassName'] = 'Clar_StorageVolume'
-        clone_vol3['ElementName'] = test_clone3['name']
-        clone_vol3['DeviceID'] = test_clone3['id']
-        clone_vol3['SystemName'] = storage_system
-        clone_vol3.path = {'DeviceID': clone_vol3['DeviceID']}
+        clone_vol3['ElementName'] = self.data.test_clone3['name']
+        clone_vol3['DeviceID'] = self.data.test_clone3['id']
+        clone_vol3['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        clone_vol3['SystemCreationClassName'] = 'Clar_StorageSystem'
+        clone_vol3.path = clone_vol3
+        clone_vol3.path.classname = clone_vol3['CreationClassName']
         vols.append(clone_vol3)
 
         snap_vol_vmax = EMC_StorageVolume()
+        snap_vol_vmax['name'] = self.data.test_snapshot_vmax['name']
         snap_vol_vmax['CreationClassName'] = 'Symm_StorageVolume'
-        snap_vol_vmax['ElementName'] = test_snapshot_vmax['name']
-        snap_vol_vmax['DeviceID'] = test_snapshot_vmax['id']
-        snap_vol_vmax['SystemName'] = storage_system_vmax
-        snap_vol_vmax.path = {'DeviceID': snap_vol_vmax['DeviceID']}
+        snap_vol_vmax['ElementName'] = self.data.test_snapshot_vmax['name']
+        snap_vol_vmax['DeviceID'] = self.data.test_snapshot_vmax['id']
+        snap_vol_vmax['SystemName'] = self.data.storage_system_vmax
+        # Added vol to vol.path
+        snap_vol_vmax['SystemCreationClassName'] = 'Symm_StorageSystem'
+        snap_vol_vmax.path = snap_vol_vmax
+        snap_vol_vmax.path.classname = snap_vol_vmax['CreationClassName']
+
+        name3 = {}
+        name3['classname'] = 'Clar_StorageVolume'
+        keys3 = {}
+        keys3['CreationClassName'] = 'Clar_StorageVolume'
+        keys3['SystemName'] = self.data.storage_system
+        keys3['DeviceID'] = snap_vol_vmax['DeviceID']
+        keys3['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name3['keybindings'] = keys3
+        snap_vol_vmax['provider_location'] = str(name3)
+
         vols.append(snap_vol_vmax)
 
         failed_snap_replica = EMC_StorageVolume()
+        failed_snap_replica['name'] = self.data.failed_snapshot_replica['name']
         failed_snap_replica['CreationClassName'] = 'Clar_StorageVolume'
-        failed_snap_replica['ElementName'] = failed_snapshot_replica['name']
-        failed_snap_replica['DeviceID'] = failed_snapshot_replica['id']
-        failed_snap_replica['SystemName'] = storage_system
-        failed_snap_replica.path = {
-            'DeviceID': failed_snap_replica['DeviceID']}
+        failed_snap_replica['ElementName'] =\
+            self.data.failed_snapshot_replica['name']
+        failed_snap_replica['DeviceID'] =\
+            self.data.failed_snapshot_replica['id']
+        failed_snap_replica['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_snap_replica['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_snap_replica.path = failed_snap_replica
+        failed_snap_replica.path.classname =\
+            failed_snap_replica['CreationClassName']
+
+        name4 = {}
+        name4['classname'] = 'Clar_StorageVolume'
+        keys4 = {}
+        keys4['CreationClassName'] = 'Clar_StorageVolume'
+        keys4['SystemName'] = self.data.storage_system
+        keys4['DeviceID'] = failed_snap_replica['DeviceID']
+        keys4['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name4['keybindings'] = keys4
+        failed_snap_replica['provider_location'] = str(name4)
+
         vols.append(failed_snap_replica)
 
         failed_snap_sync = EMC_StorageVolume()
+        failed_snap_sync['name'] = self.data.failed_snapshot_sync['name']
         failed_snap_sync['CreationClassName'] = 'Clar_StorageVolume'
-        failed_snap_sync['ElementName'] = failed_snapshot_sync['name']
-        failed_snap_sync['DeviceID'] = failed_snapshot_sync['id']
-        failed_snap_sync['SystemName'] = storage_system
-        failed_snap_sync.path = {
-            'DeviceID': failed_snap_sync['DeviceID']}
+        failed_snap_sync['ElementName'] =\
+            self.data.failed_snapshot_sync['name']
+        failed_snap_sync['DeviceID'] = self.data.failed_snapshot_sync['id']
+        failed_snap_sync['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_snap_sync['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_snap_sync.path = failed_snap_sync
+        failed_snap_sync.path.classname =\
+            failed_snap_sync['CreationClassName']
+
+        name5 = {}
+        name5['classname'] = 'Clar_StorageVolume'
+        keys5 = {}
+        keys5['CreationClassName'] = 'Clar_StorageVolume'
+        keys5['SystemName'] = self.data.storage_system
+        keys5['DeviceID'] = failed_snap_sync['DeviceID']
+        keys5['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name5['keybindings'] = keys5
+        failed_snap_sync['provider_location'] = str(name5)
+
         vols.append(failed_snap_sync)
 
         failed_clone_rep = EMC_StorageVolume()
+        failed_clone_rep['name'] = self.data.failed_clone_replica['name']
         failed_clone_rep['CreationClassName'] = 'Clar_StorageVolume'
-        failed_clone_rep['ElementName'] = failed_clone_replica['name']
-        failed_clone_rep['DeviceID'] = failed_clone_replica['id']
-        failed_clone_rep['SystemName'] = storage_system
-        failed_clone_rep.path = {
-            'DeviceID': failed_clone_rep['DeviceID']}
+        failed_clone_rep['ElementName'] =\
+            self.data.failed_clone_replica['name']
+        failed_clone_rep['DeviceID'] = self.data.failed_clone_replica['id']
+        failed_clone_rep['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_clone_rep['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_clone_rep.path = failed_clone_rep
+        failed_clone_rep.path.classname =\
+            failed_clone_rep['CreationClassName']
         vols.append(failed_clone_rep)
 
         failed_clone_s = EMC_StorageVolume()
+        failed_clone_s['name'] = self.data.failed_clone_sync['name']
         failed_clone_s['CreationClassName'] = 'Clar_StorageVolume'
-        failed_clone_s['ElementName'] = failed_clone_sync['name']
-        failed_clone_s['DeviceID'] = failed_clone_sync['id']
-        failed_clone_s['SystemName'] = storage_system
-        failed_clone_s.path = {
-            'DeviceID': failed_clone_s['DeviceID']}
+        failed_clone_s['ElementName'] = self.data.failed_clone_sync['name']
+        failed_clone_s['DeviceID'] = self.data.failed_clone_sync['id']
+        failed_clone_s['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_clone_s['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_clone_s.path = failed_clone_s
+        failed_clone_s.path.classname =\
+            failed_clone_s['CreationClassName']
         vols.append(failed_clone_s)
 
         failed_delete_vol = EMC_StorageVolume()
+        failed_delete_vol['name'] = 'failed_delete_vol'
         failed_delete_vol['CreationClassName'] = 'Clar_StorageVolume'
         failed_delete_vol['ElementName'] = 'failed_delete_vol'
         failed_delete_vol['DeviceID'] = '99999'
-        failed_delete_vol['SystemName'] = storage_system
-        failed_delete_vol.path = {'DeviceID': failed_delete_vol['DeviceID']}
+        failed_delete_vol['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_delete_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_delete_vol.path = failed_delete_vol
+        failed_delete_vol.path.classname =\
+            failed_delete_vol['CreationClassName']
         vols.append(failed_delete_vol)
 
+        failed_vol = EMC_StorageVolume()
+        failed_vol['name'] = 'failed__vol'
+        failed_vol['CreationClassName'] = 'Clar_StorageVolume'
+        failed_vol['ElementName'] = 'failed_vol'
+        failed_vol['DeviceID'] = '4'
+        failed_vol['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_vol.path = failed_vol
+        failed_vol.path.classname =\
+            failed_vol['CreationClassName']
+
+        name_failed = {}
+        name_failed['classname'] = 'Clar_StorageVolume'
+        keys_failed = {}
+        keys_failed['CreationClassName'] = 'Clar_StorageVolume'
+        keys_failed['SystemName'] = self.data.storage_system
+        keys_failed['DeviceID'] = failed_vol['DeviceID']
+        keys_failed['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name_failed['keybindings'] = keys_failed
+        failed_vol['provider_location'] = str(name_failed)
+
+        vols.append(failed_vol)
+
+        failed_extend_vol = EMC_StorageVolume()
+        failed_extend_vol['name'] = 'failed_extend_vol'
+        failed_extend_vol['CreationClassName'] = 'Clar_StorageVolume'
+        failed_extend_vol['ElementName'] = 'failed_extend_vol'
+        failed_extend_vol['DeviceID'] = '9'
+        failed_extend_vol['SystemName'] = self.data.storage_system
+        # Added vol to vol.path
+        failed_extend_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        failed_extend_vol.path = failed_extend_vol
+        failed_extend_vol.path.classname =\
+            failed_extend_vol['CreationClassName']
+
+        name_extend_failed = {}
+        name_extend_failed['classname'] = 'Clar_StorageVolume'
+        keys_extend_failed = {}
+        keys_extend_failed['CreationClassName'] = 'Clar_StorageVolume'
+        keys_extend_failed['SystemName'] = self.data.storage_system
+        keys_extend_failed['DeviceID'] = failed_extend_vol['DeviceID']
+        keys_extend_failed['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name_extend_failed['keybindings'] = keys_extend_failed
+        failed_extend_vol['provider_location'] = str(name_extend_failed)
+
+        vols.append(failed_extend_vol)
+
         return vols
 
     def _enum_syncsvsvs(self):
@@ -573,9 +850,9 @@ class FakeEcomConnection():
     def _enum_lunmaskctrls(self):
         ctrls = []
         ctrl = {}
-        ctrl['CreationClassName'] = lunmask_creationclass
-        ctrl['DeviceID'] = lunmaskctrl_id
-        ctrl['SystemName'] = storage_system
+        ctrl['CreationClassName'] = self.data.lunmask_creationclass
+        ctrl['DeviceID'] = self.data.lunmaskctrl_id
+        ctrl['SystemName'] = self.data.storage_system
         ctrls.append(ctrl)
         return ctrls
 
@@ -583,10 +860,26 @@ class FakeEcomConnection():
         ctrls = []
         ctrl = {}
         ctrl['CreationClassName'] = 'Clar_StorageProcessorSystem'
-        ctrl['Name'] = storage_system + '+SP_A'
+        ctrl['Name'] = self.data.storage_system + '+SP_A'
         ctrls.append(ctrl)
         return ctrls
 
+    def _enum_hdwidmgmts(self):
+        services = []
+        srv = {}
+        srv['SystemName'] = self.data.storage_system
+        services.append(srv)
+        return services
+
+    def _enum_storhdwids(self):
+        storhdwids = []
+        hdwid = SE_StorageHardwareID()
+        hdwid['StorageID'] = self.data.connector['wwpns'][0]
+
+        hdwid.path = hdwid
+        storhdwids.append(hdwid)
+        return storhdwids
+
     def _default_enum(self):
         names = []
         name = {}
@@ -598,23 +891,30 @@ class FakeEcomConnection():
 class EMCSMISISCSIDriverTestCase(test.TestCase):
 
     def setUp(self):
+
+        self.data = EMCSMISCommonData()
+
         self.tempdir = tempfile.mkdtemp()
         super(EMCSMISISCSIDriverTestCase, self).setUp()
         self.config_file_path = None
         self.create_fake_config_file()
 
-        configuration = mox.MockObject(conf.Configuration)
+        configuration = mock.Mock()
         configuration.cinder_emc_config_file = self.config_file_path
-        configuration.append_config_values(mox.IgnoreArg())
 
         self.stubs.Set(EMCSMISISCSIDriver, '_do_iscsi_discovery',
                        self.fake_do_iscsi_discovery)
         self.stubs.Set(EMCSMISCommon, '_get_ecom_connection',
                        self.fake_ecom_connection)
+        instancename = FakeCIMInstanceName()
+        self.stubs.Set(EMCSMISCommon, '_getinstancename',
+                       instancename.fake_getinstancename)
         driver = EMCSMISISCSIDriver(configuration=configuration)
+        driver.db = FakeDB()
         self.driver = driver
 
     def create_fake_config_file(self):
+
         doc = Document()
         emc = doc.createElement("EMC")
         doc.appendChild(emc)
@@ -644,7 +944,7 @@ class EMCSMISISCSIDriverTestCase(test.TestCase):
         emc.appendChild(ecompassword)
         ecompassword.appendChild(ecompasswordtext)
 
-        self.config_file_path = self.tempdir + '/' + config_file_name
+        self.config_file_path = self.tempdir + '/' + self.data.config_file_name
         f = open(self.config_file_path, 'w')
         doc.writexml(f)
         f.close()
@@ -665,95 +965,116 @@ class EMCSMISISCSIDriverTestCase(test.TestCase):
         self.driver.get_volume_stats(True)
 
     def test_create_destroy(self):
-        self.driver.create_volume(test_volume)
-        self.driver.delete_volume(test_volume)
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_snapshot_destroy(self):
-        self.driver.create_volume(test_volume)
-        self.driver.create_snapshot(test_snapshot)
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
         self.driver.create_volume_from_snapshot(
-            test_clone, test_snapshot)
+            self.data.test_clone, self.data.test_snapshot)
         self.driver.create_cloned_volume(
-            test_clone3, test_volume)
-        self.driver.delete_volume(test_clone)
-        self.driver.delete_volume(test_clone3)
-        self.driver.delete_snapshot(test_snapshot)
-        self.driver.delete_volume(test_volume)
+            self.data.test_clone3, self.data.test_volume)
+        self.driver.delete_volume(self.data.test_clone)
+        self.driver.delete_volume(self.data.test_clone3)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_map_unmap(self):
-        self.driver.create_volume(test_volume)
-        export = self.driver.create_export(None, test_volume)
-        test_volume['provider_location'] = export['provider_location']
-        test_volume['EMCCurrentOwningStorageProcessor'] = 'SP_A'
-        connector = {'initiator': initiator1}
-        connection_info = self.driver.initialize_connection(test_volume,
-                                                            connector)
-        self.driver.terminate_connection(test_volume, connector)
-        self.driver.remove_export(None, test_volume)
-        self.driver.delete_volume(test_volume)
+        self.driver.create_volume(self.data.test_volume)
+        self.data.test_volume['EMCCurrentOwningStorageProcessor'] = 'SP_A'
+        connection_info = self.driver.initialize_connection(
+            self.data.test_volume,
+            self.data.connector)
+        self.driver.terminate_connection(self.data.test_volume,
+                                         self.data.connector)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_failed(self):
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume,
-                          test_failed_volume)
+                          self.data.test_failed_volume)
 
     def test_create_volume_snapshot_unsupported(self):
-        self.driver.create_volume(test_volume)
-        self.driver.create_snapshot(test_snapshot_vmax)
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot_vmax)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume_from_snapshot,
-                          test_clone,
-                          test_snapshot_vmax)
-        self.driver.delete_snapshot(test_snapshot_vmax)
-        self.driver.delete_volume(test_volume)
+                          self.data.test_clone,
+                          self.data.test_snapshot_vmax)
+        self.driver.delete_snapshot(self.data.test_snapshot_vmax)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_snapshot_replica_failed(self):
-        self.driver.create_volume(test_volume)
-        self.driver.create_snapshot(test_snapshot)
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume_from_snapshot,
-                          failed_snapshot_replica,
-                          test_snapshot)
-        self.driver.delete_snapshot(test_snapshot)
-        self.driver.delete_volume(test_volume)
+                          self.data.failed_snapshot_replica,
+                          self.data.test_snapshot)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_snapshot_sync_failed(self):
-        self.driver.create_volume(test_volume)
-        self.driver.create_snapshot(test_snapshot)
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume_from_snapshot,
-                          failed_snapshot_sync,
-                          test_snapshot)
-        self.driver.delete_snapshot(test_snapshot)
-        self.driver.delete_volume(test_volume)
+                          self.data.failed_snapshot_sync,
+                          self.data.test_snapshot)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_clone_replica_failed(self):
-        self.driver.create_volume(test_volume)
+        self.driver.create_volume(self.data.test_volume)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_cloned_volume,
-                          failed_clone_replica,
-                          test_volume)
-        self.driver.delete_volume(test_volume)
+                          self.data.failed_clone_replica,
+                          self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_create_volume_clone_sync_failed(self):
-        self.driver.create_volume(test_volume)
+        self.driver.create_volume(self.data.test_volume)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_cloned_volume,
-                          failed_clone_sync,
-                          test_volume)
-        self.driver.delete_volume(test_volume)
+                          self.data.failed_clone_sync,
+                          self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
 
     def test_delete_volume_notfound(self):
         notfound_delete_vol = {}
         notfound_delete_vol['name'] = 'notfound_delete_vol'
         notfound_delete_vol['id'] = '10'
+        notfound_delete_vol['CreationClassName'] = 'Clar_StorageVolume'
+        notfound_delete_vol['SystemName'] = self.data.storage_system
+        notfound_delete_vol['DeviceID'] = notfound_delete_vol['id']
+        notfound_delete_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name = {}
+        name['classname'] = 'Clar_StorageVolume'
+        keys = {}
+        keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
+        keys['SystemName'] = notfound_delete_vol['SystemName']
+        keys['DeviceID'] = notfound_delete_vol['DeviceID']
+        keys['SystemCreationClassName'] =\
+            notfound_delete_vol['SystemCreationClassName']
+        name['keybindings'] = keys
+        notfound_delete_vol['provider_location'] = str(name)
         self.driver.delete_volume(notfound_delete_vol)
 
     def test_delete_volume_failed(self):
-        self.driver.create_volume(failed_delete_vol)
+        self.driver.create_volume(self.data.failed_delete_vol)
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.delete_volume,
-                          failed_delete_vol)
+                          self.data.failed_delete_vol)
+
+    def test_extend_volume(self):
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.extend_volume(self.data.test_volume, '10')
+        self.driver.create_volume(self.data.failed_extend_vol)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.extend_volume,
+                          self.data.failed_extend_vol,
+                          '10')
 
     def _cleanup(self):
         bExists = os.path.exists(self.config_file_path)
@@ -764,3 +1085,240 @@ class EMCSMISISCSIDriverTestCase(test.TestCase):
     def tearDown(self):
         self._cleanup()
         super(EMCSMISISCSIDriverTestCase, self).tearDown()
+
+
+class EMCSMISFCDriverTestCase(test.TestCase):
+
+    def setUp(self):
+
+        self.data = EMCSMISCommonData()
+
+        self.tempdir = tempfile.mkdtemp()
+        super(EMCSMISFCDriverTestCase, self).setUp()
+        self.config_file_path = None
+        self.create_fake_config_file()
+
+        configuration = mock.Mock()
+        configuration.cinder_emc_config_file = self.config_file_path
+
+        self.stubs.Set(EMCSMISCommon, '_get_ecom_connection',
+                       self.fake_ecom_connection)
+        instancename = FakeCIMInstanceName()
+        self.stubs.Set(EMCSMISCommon, '_getinstancename',
+                       instancename.fake_getinstancename)
+        driver = EMCSMISFCDriver(configuration=configuration)
+        driver.db = FakeDB()
+        self.driver = driver
+
+    def create_fake_config_file(self):
+
+        doc = Document()
+        emc = doc.createElement("EMC")
+        doc.appendChild(emc)
+
+        storagetype = doc.createElement("StorageType")
+        storagetypetext = doc.createTextNode("gold")
+        emc.appendChild(storagetype)
+        storagetype.appendChild(storagetypetext)
+
+        ecomserverip = doc.createElement("EcomServerIp")
+        ecomserveriptext = doc.createTextNode("1.1.1.1")
+        emc.appendChild(ecomserverip)
+        ecomserverip.appendChild(ecomserveriptext)
+
+        ecomserverport = doc.createElement("EcomServerPort")
+        ecomserverporttext = doc.createTextNode("10")
+        emc.appendChild(ecomserverport)
+        ecomserverport.appendChild(ecomserverporttext)
+
+        ecomusername = doc.createElement("EcomUserName")
+        ecomusernametext = doc.createTextNode("user")
+        emc.appendChild(ecomusername)
+        ecomusername.appendChild(ecomusernametext)
+
+        ecompassword = doc.createElement("EcomPassword")
+        ecompasswordtext = doc.createTextNode("pass")
+        emc.appendChild(ecompassword)
+        ecompassword.appendChild(ecompasswordtext)
+
+        self.config_file_path = self.tempdir + '/' + self.data.config_file_name
+        f = open(self.config_file_path, 'w')
+        doc.writexml(f)
+        f.close()
+
+    def fake_ecom_connection(self):
+        conn = FakeEcomConnection()
+        return conn
+
+    def test_get_volume_stats(self):
+        self.driver.get_volume_stats(True)
+
+    def test_create_destroy(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_snapshot_destroy(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
+        self.driver.create_volume_from_snapshot(
+            self.data.test_clone, self.data.test_snapshot)
+        self.driver.create_cloned_volume(
+            self.data.test_clone3, self.data.test_volume)
+        self.driver.delete_volume(self.data.test_clone)
+        self.driver.delete_volume(self.data.test_clone3)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_map_unmap(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+
+        output = {'driver_volume_type': 'fibre_channel',
+                  'data': {'target_lun': 0,
+                           'target_wwn': ['1234567890123', '0987654321321'],
+                           'target_discovered': True}}
+        connection_info = self.driver.initialize_connection(
+            self.data.test_volume,
+            self.data.connector)
+        self.assertEqual(connection_info, output)
+
+        self.driver.terminate_connection(self.data.test_volume,
+                                         self.data.connector)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_failed(self):
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume,
+                          self.data.test_failed_volume)
+
+    def test_create_volume_snapshot_unsupported(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot_vmax)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume_from_snapshot,
+                          self.data.test_clone,
+                          self.data.test_snapshot_vmax)
+        self.driver.delete_snapshot(self.data.test_snapshot_vmax)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_snapshot_replica_failed(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume_from_snapshot,
+                          self.data.failed_snapshot_replica,
+                          self.data.test_snapshot)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_snapshot_sync_failed(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.create_snapshot(self.data.test_snapshot)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_volume_from_snapshot,
+                          self.data.failed_snapshot_sync,
+                          self.data.test_snapshot)
+        self.driver.delete_snapshot(self.data.test_snapshot)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_clone_replica_failed(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_cloned_volume,
+                          self.data.failed_clone_replica,
+                          self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_create_volume_clone_sync_failed(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_cloned_volume,
+                          self.data.failed_clone_sync,
+                          self.data.test_volume)
+        self.driver.delete_volume(self.data.test_volume)
+
+    def test_delete_volume_notfound(self):
+        notfound_delete_vol = {}
+        notfound_delete_vol['name'] = 'notfound_delete_vol'
+        notfound_delete_vol['id'] = '10'
+        notfound_delete_vol['CreationClassName'] = 'Clar_StorageVolume'
+        notfound_delete_vol['SystemName'] = self.data.storage_system
+        notfound_delete_vol['DeviceID'] = notfound_delete_vol['id']
+        notfound_delete_vol['SystemCreationClassName'] = 'Clar_StorageSystem'
+        name = {}
+        name['classname'] = 'Clar_StorageVolume'
+        keys = {}
+        keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
+        keys['SystemName'] = notfound_delete_vol['SystemName']
+        keys['DeviceID'] = notfound_delete_vol['DeviceID']
+        keys['SystemCreationClassName'] =\
+            notfound_delete_vol['SystemCreationClassName']
+        name['keybindings'] = keys
+        notfound_delete_vol['provider_location'] = str(name)
+        self.driver.delete_volume(notfound_delete_vol)
+
+    def test_delete_volume_failed(self):
+        self.driver.create_volume(self.data.failed_delete_vol)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.delete_volume,
+                          self.data.failed_delete_vol)
+
+    def test_extend_volume(self):
+        self.data.test_volume['volume_type_id'] = None
+        self.driver.create_volume(self.data.test_volume)
+        self.driver.extend_volume(self.data.test_volume, '10')
+        self.driver.create_volume(self.data.failed_extend_vol)
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.extend_volume,
+                          self.data.failed_extend_vol,
+                          '10')
+
+    @mock.patch.object(
+        volume_types,
+        'get_volume_type_extra_specs',
+        return_value={'storagetype:pool': 'gold',
+                      'storagetype:provisioning': 'thick'})
+    def test_create_volume_with_volume_type(self, _mock_volume_type):
+        volume_with_vt = self.data.test_volume
+        volume_with_vt['volume_type_id'] = 1
+        self.driver.create_volume(volume_with_vt)
+
+        configservice = {'CreationClassName':
+                         'Clar_StorageConfigurationService',
+                         'SystemName': 'CLARiiON+APM00123456789'}
+
+        pool = {'InstanceID': 'CLARiiON+APM00123456789+U+gold',
+                'CreationClassName': 'Clar_UnifiedStoragePool'}
+
+        volumesize = int(volume_with_vt['size']) * units.GiB
+
+        storage_type = {'storagetype:provisioning': 'thick',
+                        'storagetype:pool': 'gold'}
+
+        expected = [
+            mock.call._get_storage_type(volume_with_vt),
+            mock.call._find_pool('gold'),
+            mock.call.get_provisioning(storage_type),
+            mock.call.InvokeMethod('CreateOrModifyElementFromStoragePool',
+                                   configservice, volume_with_vt['name'],
+                                   pool,
+                                   self.driver.common._getnum(2, '16'),
+                                   self.driver.common._getnum(volumesize,
+                                                              '64'))]
+
+    def _cleanup(self):
+        bExists = os.path.exists(self.config_file_path)
+        if bExists:
+            os.remove(self.config_file_path)
+        shutil.rmtree(self.tempdir)
+
+    def tearDown(self):
+        self._cleanup()
+        super(EMCSMISFCDriverTestCase, self).tearDown()
index f5aefeb7bcea44e96e73d090c3a914d322ff1ed7..b15d76c1df825b0660bba94600b1b8e0f8c78961 100644 (file)
@@ -1,5 +1,4 @@
-# Copyright (c) 2012 EMC Corporation.
-# Copyright (c) 2012 OpenStack Foundation
+# Copyright (c) 2012 - 2014 EMC Corporation.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -29,6 +28,7 @@ from xml.dom.minidom import parseString
 from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder import units
+from cinder.volume import volume_types
 
 LOG = logging.getLogger(__name__)
 
@@ -41,6 +41,9 @@ except ImportError:
                'Install PyWBEM using the python-pywbem package.'))
 
 CINDER_EMC_CONFIG_FILE = '/etc/cinder/cinder_emc_config.xml'
+EMC_ROOT = 'root/emc'
+PROVISIONING = 'storagetype:provisioning'
+POOL = 'storagetype:pool'
 
 emc_opts = [
     cfg.StrOpt('cinder_emc_config_file',
@@ -64,6 +67,7 @@ class EMCSMISCommon():
              'volume_backend_name': None}
 
     def __init__(self, prtcl, configuration=None):
+
         self.protocol = prtcl
         self.configuration = configuration
         self.configuration.append_config_values(emc_opts)
@@ -75,7 +79,6 @@ class EMCSMISCommon():
 
     def create_volume(self, volume):
         """Creates a EMC(VMAX/VNX) volume."""
-
         LOG.debug(_('Entering create_volume.'))
         volumesize = int(volume['size']) * units.GiB
         volumename = volume['name']
@@ -86,14 +89,14 @@ class EMCSMISCommon():
 
         self.conn = self._get_ecom_connection()
 
-        storage_type = self._get_storage_type()
+        storage_type = self._get_storage_type(volume)
 
         LOG.debug(_('Create Volume: %(volume)s  '
                   'Storage type: %(storage_type)s')
                   % {'volume': volumename,
                      'storage_type': storage_type})
 
-        pool, storage_system = self._find_pool(storage_type)
+        pool, storage_system = self._find_pool(storage_type[POOL])
 
         LOG.debug(_('Create Volume: %(volume)s  Pool: %(pool)s  '
                   'Storage System: %(storage_system)s')
@@ -112,19 +115,22 @@ class EMCSMISCommon():
             LOG.error(exception_message)
             raise exception.VolumeBackendAPIException(data=exception_message)
 
+        provisioning = self._get_provisioning(storage_type)
+
         LOG.debug(_('Create Volume: %(name)s  Method: '
                   'CreateOrModifyElementFromStoragePool  ConfigServicie: '
                   '%(service)s  ElementName: %(name)s  InPool: %(pool)s  '
-                  'ElementType: 5  Size: %(size)lu')
+                  'ElementType: %(provisioning)s  Size: %(size)lu')
                   % {'service': str(configservice),
                      'name': volumename,
                      'pool': str(pool),
+                     'provisioning': provisioning,
                      'size': volumesize})
 
         rc, job = self.conn.InvokeMethod(
             'CreateOrModifyElementFromStoragePool',
             configservice, ElementName=volumename, InPool=pool,
-            ElementType=self._getnum(5, '16'),
+            ElementType=self._getnum(provisioning, '16'),
             Size=self._getnum(volumesize, '64'))
 
         LOG.debug(_('Create Volume: %(volumename)s  Return code: %(rc)lu')
@@ -141,10 +147,28 @@ class EMCSMISCommon():
                              'error': errordesc})
                 raise exception.VolumeBackendAPIException(data=errordesc)
 
+        # Find the newly created volume
+        associators = self.conn.Associators(
+            job['Job'],
+            resultClass='EMC_StorageVolume')
+        volpath = associators[0].path
+        name = {}
+        name['classname'] = volpath.classname
+        keys = {}
+        keys['CreationClassName'] = volpath['CreationClassName']
+        keys['SystemName'] = volpath['SystemName']
+        keys['DeviceID'] = volpath['DeviceID']
+        keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
+        name['keybindings'] = keys
+
         LOG.debug(_('Leaving create_volume: %(volumename)s  '
-                  'Return code: %(rc)lu')
+                  'Return code: %(rc)lu '
+                  'volume instance: %(name)s')
                   % {'volumename': volumename,
-                     'rc': rc})
+                     'rc': rc,
+                     'name': name})
+
+        return name
 
     def create_volume_from_snapshot(self, volume, snapshot):
         """Creates a volume from a snapshot."""
@@ -227,14 +251,29 @@ class EMCSMISCommon():
                 raise exception.VolumeBackendAPIException(
                     data=exception_message)
 
+        # Find the newly created volume
+        associators = self.conn.Associators(
+            job['Job'],
+            resultClass='EMC_StorageVolume')
+        volpath = associators[0].path
+        name = {}
+        name['classname'] = volpath.classname
+        keys = {}
+        keys['CreationClassName'] = volpath['CreationClassName']
+        keys['SystemName'] = volpath['SystemName']
+        keys['DeviceID'] = volpath['DeviceID']
+        keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
+        name['keybindings'] = keys
+
         LOG.debug(_('Create Volume from Snapshot: Volume: %(volumename)s  '
                   'Snapshot: %(snapshotname)s.  Successfully clone volume '
                   'from snapshot.  Finding the clone relationship.')
                   % {'volumename': volumename,
                      'snapshotname': snapshotname})
 
+        volume['provider_location'] = str(name)
         sync_name, storage_system = self._find_storage_sync_sv_sv(
-            volumename, snapshotname)
+            volume, snapshot)
 
         # Remove the Clone relationshop so it can be used as a regular lun
         # 8 - Detach operation
@@ -282,6 +321,8 @@ class EMCSMISCommon():
                      'snapshotname': snapshotname,
                      'rc': rc})
 
+        return name
+
     def create_cloned_volume(self, volume, src_vref):
         """Creates a clone of the specified volume."""
         LOG.debug(_('Entering create_cloned_volume.'))
@@ -351,14 +392,29 @@ class EMCSMISCommon():
                 raise exception.VolumeBackendAPIException(
                     data=exception_message)
 
+        # Find the newly created volume
+        associators = self.conn.Associators(
+            job['Job'],
+            resultClass='EMC_StorageVolume')
+        volpath = associators[0].path
+        name = {}
+        name['classname'] = volpath.classname
+        keys = {}
+        keys['CreationClassName'] = volpath['CreationClassName']
+        keys['SystemName'] = volpath['SystemName']
+        keys['DeviceID'] = volpath['DeviceID']
+        keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
+        name['keybindings'] = keys
+
         LOG.debug(_('Create Cloned Volume: Volume: %(volumename)s  '
                   'Source Volume: %(srcname)s.  Successfully cloned volume '
                   'from source volume.  Finding the clone relationship.')
                   % {'volumename': volumename,
                      'srcname': srcname})
 
+        volume['provider_location'] = str(name)
         sync_name, storage_system = self._find_storage_sync_sv_sv(
-            volumename, srcname)
+            volume, src_vref)
 
         # Remove the Clone relationshop so it can be used as a regular lun
         # 8 - Detach operation
@@ -406,6 +462,8 @@ class EMCSMISCommon():
                      'srcname': srcname,
                      'rc': rc})
 
+        return name
+
     def delete_volume(self, volume):
         """Deletes an EMC volume."""
         LOG.debug(_('Entering delete_volume.'))
@@ -467,7 +525,7 @@ class EMCSMISCommon():
                   % {'volumename': volumename,
                      'rc': rc})
 
-    def create_snapshot(self, snapshot):
+    def create_snapshot(self, snapshot, volume):
         """Creates a snapshot."""
         LOG.debug(_('Entering create_snapshot.'))
 
@@ -479,10 +537,8 @@ class EMCSMISCommon():
 
         self.conn = self._get_ecom_connection()
 
-        volume = {}
-        volume['name'] = volumename
-        volume['provider_location'] = None
         vol_instance = self._find_lun(volume)
+
         device_id = vol_instance['DeviceID']
         storage_system = vol_instance['SystemName']
         LOG.debug(_('Device ID: %(deviceid)s: Storage System: '
@@ -532,11 +588,27 @@ class EMCSMISCommon():
                 raise exception.VolumeBackendAPIException(
                     data=exception_message)
 
+        # Find the newly created volume
+        associators = self.conn.Associators(
+            job['Job'],
+            resultClass='EMC_StorageVolume')
+        volpath = associators[0].path
+        name = {}
+        name['classname'] = volpath.classname
+        keys = {}
+        keys['CreationClassName'] = volpath['CreationClassName']
+        keys['SystemName'] = volpath['SystemName']
+        keys['DeviceID'] = volpath['DeviceID']
+        keys['SystemCreationClassName'] = volpath['SystemCreationClassName']
+        name['keybindings'] = keys
+
         LOG.debug(_('Leaving create_snapshot: Snapshot: %(snapshot)s '
                   'Volume: %(volume)s  Return code: %(rc)lu.') %
                   {'snapshot': snapshotname, 'volume': volumename, 'rc': rc})
 
-    def delete_snapshot(self, snapshot):
+        return name
+
+    def delete_snapshot(self, snapshot, volume):
         """Deletes a snapshot."""
         LOG.debug(_('Entering delete_snapshot.'))
 
@@ -554,7 +626,7 @@ class EMCSMISCommon():
                      'volume': volumename})
 
         sync_name, storage_system =\
-            self._find_storage_sync_sv_sv(snapshotname, volumename, False)
+            self._find_storage_sync_sv_sv(snapshot, volume, False)
         if sync_name is None:
             LOG.error(_('Snapshot: %(snapshot)s: volume: %(volume)s '
                       'not found on the array. No snapshot to delete.')
@@ -614,22 +686,6 @@ class EMCSMISCommon():
                      'snapshotname': snapshotname,
                      'rc': rc})
 
-    def create_export(self, context, volume):
-        """Driver entry point to get the export info for a new volume."""
-        self.conn = self._get_ecom_connection()
-        volumename = volume['name']
-        LOG.info(_('Create export: %(volume)s')
-                 % {'volume': volumename})
-        vol_instance = self._find_lun(volume)
-        device_id = vol_instance['DeviceID']
-
-        LOG.debug(_('create_export: Volume: %(volume)s  Device ID: '
-                  '%(device_id)s')
-                  % {'volume': volumename,
-                     'device_id': device_id})
-
-        return {'provider_location': device_id}
-
     # Mapping method for VNX
     def _expose_paths(self, configservice, vol_instance,
                       connector):
@@ -854,22 +910,98 @@ class EMCSMISCommon():
         self.conn = self._get_ecom_connection()
         self._unmap_lun(volume, connector)
 
+    def extend_volume(self, volume, new_size):
+        """Extends an existing  volume."""
+        LOG.debug(_('Entering extend_volume.'))
+        volumesize = int(new_size) * units.GiB
+        volumename = volume['name']
+
+        LOG.info(_('Extend Volume: %(volume)s  New size: %(size)lu')
+                 % {'volume': volumename,
+                    'size': volumesize})
+
+        self.conn = self._get_ecom_connection()
+
+        storage_type = self._get_storage_type(volume)
+
+        vol_instance = self._find_lun(volume)
+
+        device_id = vol_instance['DeviceID']
+        storage_system = vol_instance['SystemName']
+        LOG.debug(_('Device ID: %(deviceid)s: Storage System: '
+                  '%(storagesystem)s')
+                  % {'deviceid': device_id,
+                     'storagesystem': storage_system})
+
+        configservice = self._find_storage_configuration_service(
+            storage_system)
+        if configservice is None:
+            exception_message = (_("Error Extend Volume: %(volumename)s. "
+                                 "Storage Configuration Service not found.")
+                                 % {'volumename': volumename})
+            LOG.error(exception_message)
+            raise exception.VolumeBackendAPIException(data=exception_message)
+
+        provisioning = self._get_provisioning(storage_type)
+
+        LOG.debug(_('Extend Volume: %(name)s  Method: '
+                  'CreateOrModifyElementFromStoragePool  ConfigServicie: '
+                  '%(service)s ElementType: %(provisioning)s  Size: %(size)lu'
+                  'Volume path: %(volumepath)s')
+                  % {'service': str(configservice),
+                     'name': volumename,
+                     'provisioning': provisioning,
+                     'size': volumesize,
+                     'volumepath': vol_instance.path})
+
+        rc, job = self.conn.InvokeMethod(
+            'CreateOrModifyElementFromStoragePool',
+            configservice, ElementType=self._getnum(provisioning, '16'),
+            Size=self._getnum(volumesize, '64'),
+            TheElement=vol_instance.path)
+
+        LOG.debug(_('Extend Volume: %(volumename)s  Return code: %(rc)lu')
+                  % {'volumename': volumename,
+                     'rc': rc})
+
+        if rc != 0L:
+            rc, errordesc = self._wait_for_job_complete(job)
+            if rc != 0L:
+                LOG.error(_('Error Extend Volume: %(volumename)s.  '
+                          'Return code: %(rc)lu.  Error: %(error)s')
+                          % {'volumename': volumename,
+                             'rc': rc,
+                             'error': errordesc})
+                raise exception.VolumeBackendAPIException(data=errordesc)
+
+        LOG.debug(_('Leaving extend_volume: %(volumename)s  '
+                  'Return code: %(rc)lu ')
+                  % {'volumename': volumename,
+                     'rc': rc})
+
     def update_volume_stats(self):
         """Retrieve stats info."""
         LOG.debug(_("Updating volume stats"))
-        self.conn = self._get_ecom_connection()
-        storage_type = self._get_storage_type()
+        self.stats['total_capacity_gb'] = 'unknown'
+        self.stats['free_capacity_gb'] = 'unknown'
 
-        pool, storagesystem = self._find_pool(storage_type, True)
+        return self.stats
 
-        self.stats['total_capacity_gb'] = pool['TotalManagedSpace']
-        self.stats['free_capacity_gb'] = pool['RemainingManagedSpace']
+    def _get_storage_type(self, volume, filename=None):
+        """Get storage type.
 
-        return self.stats
+        Look for user input volume type first.
+        If not available, fall back to finding it in conf file.
+        """
+        specs = self._get_volumetype_extraspecs(volume)
+        if not specs:
+            specs = self._get_storage_type_conffile()
+        LOG.debug(_("Storage Type: %s") % (specs))
+        return specs
 
-    def _get_storage_type(self, filename=None):
+    def _get_storage_type_conffile(self, filename=None):
         """Get the storage type from the config file."""
-        if filename is None:
+        if filename == None:
             filename = self.configuration.cinder_emc_config_file
 
         file = open(filename, 'r')
@@ -881,8 +1013,11 @@ class EMCSMISCommon():
             storageType = storageTypes[0].toxml()
             storageType = storageType.replace('<StorageType>', '')
             storageType = storageType.replace('</StorageType>', '')
-            LOG.debug(_("Found Storage Type: %s") % (storageType))
-            return storageType
+            LOG.debug(_("Found Storage Type in config file: %s")
+                      % (storageType))
+            specs = {}
+            specs[POOL] = storageType
+            return specs
         else:
             exception_message = (_("Storage type not found."))
             LOG.error(exception_message)
@@ -1085,30 +1220,13 @@ class EMCSMISCommon():
 
     def _find_lun(self, volume):
         foundinstance = None
-        try:
-            device_id = volume['provider_location']
-        except Exception:
-            device_id = None
 
         volumename = volume['name']
-
-        names = self.conn.EnumerateInstanceNames('EMC_StorageVolume')
-
-        for n in names:
-            if device_id is not None:
-                if n['DeviceID'] == device_id:
-                    vol_instance = self.conn.GetInstance(n)
-                    foundinstance = vol_instance
-                    break
-                else:
-                    continue
-
-            else:
-                vol_instance = self.conn.GetInstance(n)
-                if vol_instance['ElementName'] == volumename:
-                    foundinstance = vol_instance
-                    volume['provider_location'] = foundinstance['DeviceID']
-                    break
+        loc = volume['provider_location']
+        name = eval(loc)
+        instancename = self._getinstancename(name['classname'],
+                                             name['keybindings'])
+        foundinstance = self.conn.GetInstance(instancename)
 
         if foundinstance is None:
             LOG.debug(_("Volume %(volumename)s not found on the array.")
@@ -1121,33 +1239,24 @@ class EMCSMISCommon():
 
         return foundinstance
 
-    def _find_storage_sync_sv_sv(self, snapshotname, volumename,
+    def _find_storage_sync_sv_sv(self, snapshot, volume,
                                  waitforsync=True):
         foundsyncname = None
         storage_system = None
         percent_synced = 0
 
+        snapshotname = snapshot['name']
+        volumename = volume['name']
         LOG.debug(_("Source: %(volumename)s  Target: %(snapshotname)s.")
                   % {'volumename': volumename, 'snapshotname': snapshotname})
 
-        names = self.conn.EnumerateInstanceNames(
-            'SE_StorageSynchronized_SV_SV')
-
-        for n in names:
-            snapshot_instance = self.conn.GetInstance(n['SyncedElement'],
-                                                      LocalOnly=False)
-            if snapshotname != snapshot_instance['ElementName']:
-                continue
-
-            vol_instance = self.conn.GetInstance(n['SystemElement'],
-                                                 LocalOnly=False)
-            if vol_instance['ElementName'] == volumename:
-                foundsyncname = n
-                storage_system = vol_instance['SystemName']
-                if waitforsync:
-                    sync_instance = self.conn.GetInstance(n, LocalOnly=False)
-                    percent_synced = sync_instance['PercentSynced']
-                break
+        snapshot_instance = self._find_lun(snapshot)
+        volume_instance = self._find_lun(volume)
+        storage_system = volume_instance['SystemName']
+        classname = 'SE_StorageSynchronized_SV_SV'
+        bindings = {'SyncedElement': snapshot_instance.path,
+                    'SystemElement': volume_instance.path}
+        foundsyncname = self._getinstancename(classname, bindings)
 
         if foundsyncname is None:
             LOG.debug(_("Source: %(volumename)s  Target: %(snapshotname)s. "
@@ -1293,7 +1402,7 @@ class EMCSMISCommon():
         return foundCtrl
 
     # Find out how many volumes are mapped to a host
-    # associated to the LunMaskingSCSIProtocolController
+    # assoociated to the LunMaskingSCSIProtocolController
     def get_num_volumes_mapped(self, volume, connector):
         numVolumesMapped = 0
         volumename = volume['name']
@@ -1510,6 +1619,18 @@ class EMCSMISCommon():
 
         return result
 
+    def _getinstancename(self, classname, bindings):
+        instancename = None
+        try:
+            instancename = pywbem.CIMInstanceName(
+                classname,
+                namespace=EMC_ROOT,
+                keybindings=bindings)
+        except NameError:
+            instancename = None
+
+        return instancename
+
     # Find target WWNs
     def get_target_wwns(self, storage_system, connector):
         target_wwns = []
@@ -1543,8 +1664,8 @@ class EMCSMISCommon():
             for targetendpoint in endpoints:
                 wwn = targetendpoint['Name']
                 # Add target wwn to the list if it is not already there
-                if not any(d.get('wwn', None) == wwn for d in target_wwns):
-                    target_wwns.append({'wwn': wwn})
+                if not any(d == wwn for d in target_wwns):
+                    target_wwns.append(wwn)
                 LOG.debug(_('Add target WWN: %s.') % wwn)
 
         LOG.debug(_('Target WWNs: %s.') % target_wwns)
@@ -1569,3 +1690,29 @@ class EMCSMISCommon():
                      'foundInstances': str(foundInstances)})
 
         return foundInstances
+
+    def _get_volumetype_extraspecs(self, volume):
+        specs = {}
+        type_id = volume['volume_type_id']
+        if type_id is not None:
+            specs = volume_types.get_volume_type_extra_specs(type_id)
+            # If specs['storagetype:pool'] not defined,
+            # set specs to {} so we can ready from config file later
+            if POOL not in specs:
+                specs = {}
+
+        return specs
+
+    def _get_provisioning(self, storage_type):
+        # provisioning is thin (5) by default
+        provisioning = 5
+        thick_str = 'thick'
+        try:
+            type_prov = storage_type[PROVISIONING]
+            if type_prov.lower() == thick_str.lower():
+                provisioning = 2
+        except KeyError:
+            # Default to thin if not defined
+            pass
+
+        return provisioning
diff --git a/cinder/volume/drivers/emc/emc_smis_fc.py b/cinder/volume/drivers/emc/emc_smis_fc.py
new file mode 100644 (file)
index 0000000..7420449
--- /dev/null
@@ -0,0 +1,198 @@
+# Copyright (c) 2014 EMC Corporation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+"""
+FC Drivers for EMC VNX and VMAX arrays based on SMI-S.
+
+"""
+
+from cinder import context
+from cinder.openstack.common import log as logging
+from cinder.volume import driver
+from cinder.volume.drivers.emc import emc_smis_common
+
+LOG = logging.getLogger(__name__)
+
+
+class EMCSMISFCDriver(driver.FibreChannelDriver):
+    """EMC FC Drivers for VMAX and VNX using SMI-S.
+
+    Version history:
+        1.0.0 - Initial driver
+        1.1.0 - Multiple pools and thick/thin provisioning,
+                performance enhancement.
+    """
+
+    VERSION = "1.1.0"
+
+    def __init__(self, *args, **kwargs):
+
+        super(EMCSMISFCDriver, self).__init__(*args, **kwargs)
+        self.common = emc_smis_common.EMCSMISCommon(
+            'FC',
+            configuration=self.configuration)
+
+    def check_for_setup_error(self):
+        pass
+
+    def create_volume(self, volume):
+        """Creates a EMC(VMAX/VNX) volume."""
+        volpath = self.common.create_volume(volume)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
+
+    def create_volume_from_snapshot(self, volume, snapshot):
+        """Creates a volume from a snapshot."""
+        volpath = self.common.create_volume_from_snapshot(volume, snapshot)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
+
+    def create_cloned_volume(self, volume, src_vref):
+        """Creates a cloned volume."""
+        volpath = self.common.create_cloned_volume(volume, src_vref)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
+
+    def delete_volume(self, volume):
+        """Deletes an EMC volume."""
+        self.common.delete_volume(volume)
+
+    def create_snapshot(self, snapshot):
+        """Creates a snapshot."""
+        ctxt = context.get_admin_context()
+        volumename = snapshot['volume_name']
+        index = volumename.index('-')
+        volumeid = volumename[index + 1:]
+        volume = self.db.volume_get(ctxt, volumeid)
+
+        volpath = self.common.create_snapshot(snapshot, volume)
+
+        model_update = {}
+        snapshot['provider_location'] = str(volpath)
+        model_update['provider_location'] = snapshot['provider_location']
+        return model_update
+
+    def delete_snapshot(self, snapshot):
+        """Deletes a snapshot."""
+        ctxt = context.get_admin_context()
+        volumename = snapshot['volume_name']
+        index = volumename.index('-')
+        volumeid = volumename[index + 1:]
+        volume = self.db.volume_get(ctxt, volumeid)
+
+        self.common.delete_snapshot(snapshot, volume)
+
+    def ensure_export(self, context, volume):
+        """Driver entry point to get the export info for an existing volume."""
+        pass
+
+    def create_export(self, context, volume):
+        """Driver entry point to get the export info for a new volume."""
+        pass
+
+    def remove_export(self, context, volume):
+        """Driver entry point to remove an export for a volume."""
+        pass
+
+    def check_for_export(self, context, volume_id):
+        """Make sure volume is exported."""
+        pass
+
+    def initialize_connection(self, volume, connector):
+        """Initializes the connection and returns connection info.
+
+        Assign any created volume to a compute node/host so that it can be
+        used from that host.
+
+        The  driver returns a driver_volume_type of 'fibre_channel'.
+        The target_wwn can be a single entry or a list of wwns that
+        correspond to the list of remote wwn(s) that will export the volume.
+        Example return values:
+
+            {
+                'driver_volume_type': 'fibre_channel'
+                'data': {
+                    'target_discovered': True,
+                    'target_lun': 1,
+                    'target_wwn': '1234567890123',
+                }
+            }
+
+            or
+
+             {
+                'driver_volume_type': 'fibre_channel'
+                'data': {
+                    'target_discovered': True,
+                    'target_lun': 1,
+                    'target_wwn': ['1234567890123', '0987654321321'],
+                }
+            }
+
+        """
+        device_info = self.common.initialize_connection(volume,
+                                                        connector)
+        device_number = device_info['hostlunid']
+        storage_system = device_info['storagesystem']
+        ports = self.common.get_target_wwns(storage_system, connector)
+
+        data = {'driver_volume_type': 'fibre_channel',
+                'data': {'target_lun': device_number,
+                         'target_discovered': True,
+                         'target_wwn': ports}}
+
+        LOG.debug(_('Return FC data: %(data)s.')
+                  % {'data': data})
+
+        return data
+
+    def terminate_connection(self, volume, connector, **kwargs):
+        """Disallow connection from connector."""
+        self.common.terminate_connection(volume, connector)
+
+    def extend_volume(self, volume, new_size):
+        """Extend an existing volume."""
+        self.common.extend_volume(volume, new_size)
+
+    def get_volume_stats(self, refresh=False):
+        """Get volume stats.
+
+        If 'refresh' is True, run update the stats first.
+        """
+        if refresh:
+            self.update_volume_stats()
+
+        return self._stats
+
+    def update_volume_stats(self):
+        """Retrieve stats info from volume group."""
+        LOG.debug(_("Updating volume stats"))
+        data = self.common.update_volume_stats()
+        backend_name = self.configuration.safe_get('volume_backend_name')
+        data['volume_backend_name'] = backend_name or 'EMCSMISFCDriver'
+        data['storage_protocol'] = 'FC'
+        data['driver_version'] = self.VERSION
+        self._stats = data
index 550d64ed4c8c6679cf71156979b6380a8270de57..9548d49c0bd8341943298de2c9e3c0676c852f9a 100644 (file)
@@ -1,5 +1,4 @@
-# Copyright (c) 2012 EMC Corporation.
-# Copyright (c) 2012 OpenStack Foundation
+# Copyright (c) 2012 - 2014 EMC Corporation.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,6 +18,7 @@ ISCSI Drivers for EMC VNX and VMAX arrays based on SMI-S.
 """
 
 
+from cinder import context
 from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder.volume import driver
@@ -28,9 +28,15 @@ LOG = logging.getLogger(__name__)
 
 
 class EMCSMISISCSIDriver(driver.ISCSIDriver):
-    """EMC ISCSI Drivers for VMAX and VNX using SMI-S."""
+    """EMC ISCSI Drivers for VMAX and VNX using SMI-S.
 
-    VERSION = "1.0.0"
+    Version history:
+        1.0.0 - Initial driver
+        1.1.0 - Multiple pools and thick/thin provisioning,
+                performance enhancement.
+    """
+
+    VERSION = "1.1.0"
 
     def __init__(self, *args, **kwargs):
 
@@ -44,15 +50,33 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver):
 
     def create_volume(self, volume):
         """Creates a EMC(VMAX/VNX) volume."""
-        self.common.create_volume(volume)
+        volpath = self.common.create_volume(volume)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
 
     def create_volume_from_snapshot(self, volume, snapshot):
         """Creates a volume from a snapshot."""
-        self.common.create_volume_from_snapshot(volume, snapshot)
+        volpath = self.common.create_volume_from_snapshot(volume, snapshot)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
 
     def create_cloned_volume(self, volume, src_vref):
         """Creates a cloned volume."""
-        self.common.create_cloned_volume(volume, src_vref)
+        volpath = self.common.create_cloned_volume(volume, src_vref)
+
+        ctxt = context.get_admin_context()
+        model_update = {}
+        volume['provider_location'] = str(volpath)
+        model_update['provider_location'] = volume['provider_location']
+        return model_update
 
     def delete_volume(self, volume):
         """Deletes an EMC volume."""
@@ -60,11 +84,28 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver):
 
     def create_snapshot(self, snapshot):
         """Creates a snapshot."""
-        self.common.create_snapshot(snapshot)
+        ctxt = context.get_admin_context()
+        volumename = snapshot['volume_name']
+        index = volumename.index('-')
+        volumeid = volumename[index + 1:]
+        volume = self.db.volume_get(ctxt, volumeid)
+
+        volpath = self.common.create_snapshot(snapshot, volume)
+
+        model_update = {}
+        snapshot['provider_location'] = str(volpath)
+        model_update['provider_location'] = snapshot['provider_location']
+        return model_update
 
     def delete_snapshot(self, snapshot):
         """Deletes a snapshot."""
-        self.common.delete_snapshot(snapshot)
+        ctxt = context.get_admin_context()
+        volumename = snapshot['volume_name']
+        index = volumename.index('-')
+        volumeid = volumename[index + 1:]
+        volume = self.db.volume_get(ctxt, volumeid)
+
+        self.common.delete_snapshot(snapshot, volume)
 
     def ensure_export(self, context, volume):
         """Driver entry point to get the export info for an existing volume."""
@@ -72,7 +113,7 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver):
 
     def create_export(self, context, volume):
         """Driver entry point to get the export info for a new volume."""
-        return self.common.create_export(context, volume)
+        pass
 
     def remove_export(self, context, volume):
         """Driver entry point to remove an export for a volume."""
@@ -220,6 +261,10 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver):
         """Disallow connection from connector."""
         self.common.terminate_connection(volume, connector)
 
+    def extend_volume(self, volume, new_size):
+        """Extend an existing volume."""
+        self.common.extend_volume(volume, new_size)
+
     def get_volume_stats(self, refresh=False):
         """Get volume stats.
 
@@ -237,4 +282,5 @@ class EMCSMISISCSIDriver(driver.ISCSIDriver):
         backend_name = self.configuration.safe_get('volume_backend_name')
         data['volume_backend_name'] = backend_name or 'EMCSMISISCSIDriver'
         data['storage_protocol'] = 'iSCSI'
+        data['driver_version'] = self.VERSION
         self._stats = data