]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add consistency group support for XIV/DS8K cinder driver proxy
authorChao Zheng CZ Li <lichaoz@cn.ibm.com>
Thu, 9 Apr 2015 07:10:39 +0000 (15:10 +0800)
committerChao Zheng CZ Li <lichaoz@cn.ibm.com>
Sat, 7 Mar 2015 02:21:55 +0000 (10:21 +0800)
Change-Id: I0850255a59997dbe44d9a7202ab5f947a3d91a3b
Implements: blueprint xiv-ds8k-consistency-group

cinder/tests/test_ibm_xiv_ds8k.py
cinder/volume/drivers/ibm/xiv_ds8k.py

index 2bb57b9d54e219e8ae9e23459ccc683c3838dba1..88db060ae83e4e8630a1de2a1ad0e5b5e2612dc3 100644 (file)
@@ -32,11 +32,16 @@ from cinder.volume import configuration as conf
 from cinder.volume.drivers.ibm import xiv_ds8k
 from cinder.volume import volume_types
 
-
 FAKE = "fake"
+CANNOT_DELETE = "Can not delete"
+TOO_BIG_VOLUME_SIZE = 12000
+POOL_SIZE = 100
+CONSISTGROUP_ID = 1
 VOLUME = {'size': 16,
           'name': FAKE,
-          'id': 1}
+          'id': 1,
+          'consistencygroup_id': CONSISTGROUP_ID,
+          'status': 'available'}
 
 MANAGED_FAKE = "managed_fake"
 MANAGED_VOLUME = {'size': 16,
@@ -50,6 +55,11 @@ REPLICATED_VOLUME = {'size': 64,
 
 CONTEXT = {}
 
+CONSISTGROUP = {'id': CONSISTGROUP_ID, }
+CG_SNAPSHOT_ID = 1
+CG_SNAPSHOT = {'id': CG_SNAPSHOT_ID,
+               'consistencygroup_id': CONSISTGROUP_ID}
+
 CONNECTOR = {'initiator': "iqn.2012-07.org.fake:01:948f189c4695", }
 
 CONF = cfg.CONF
@@ -68,6 +78,7 @@ class XIVDS8KFakeProxyDriver(object):
             self.xiv_ds8k_iqn = FAKE
 
         self.volumes = {}
+        self.snapshots = {}
         self.driver = driver
 
     def setup(self, context):
@@ -80,7 +91,7 @@ class XIVDS8KFakeProxyDriver(object):
             raise self.exception.HostNotFound(host='fake')
 
     def create_volume(self, volume):
-        if volume['size'] > 100:
+        if volume['size'] > POOL_SIZE:
             raise self.exception.VolumeBackendAPIException(data='blah')
         self.volumes[volume['name']] = volume
 
@@ -172,6 +183,83 @@ class XIVDS8KFakeProxyDriver(object):
         volume['easytier'] = new_type['extra_specs']['easytier']
         return True, volume
 
+    def create_consistencygroup(self, ctxt, group):
+
+        volumes = [volume for k, volume in self.volumes.iteritems()
+                   if volume['consistencygroup_id'] == group['id']]
+
+        if volumes:
+            raise exception.CinderException(
+                message='The consistency group id of volume may be wrong.')
+
+        return {'status': 'available'}
+
+    def delete_consistencygroup(self, ctxt, group):
+        volumes = []
+        for volume in self.volumes.values():
+            if (group.get('id', None)
+                    == volume.get('consistencygroup_id', None)):
+                if volume['name'] == CANNOT_DELETE:
+                    raise exception.VolumeBackendAPIException(
+                        message='Volume can not be deleted')
+                else:
+                    volume['status'] = 'deleted'
+                    volumes.append(volume)
+
+        # Delete snapshots in consistency group
+        self.snapshots = {k: snap for k, snap in self.snapshots.iteritems()
+                          if not(snap.get('consistencygroup_id', None)
+                                 == group.get('id', None))}
+
+        # Delete volume in consistency group
+        self.volumes = {k: vol for k, vol in self.volumes.iteritems()
+                        if not(vol.get('consistencygroup_id', None)
+                               == group.get('id', None))}
+
+        return {'status': 'deleted'}, volumes
+
+    def create_cgsnapshot(self, ctxt, cgsnapshot):
+        snapshots = []
+        for volume in self.volumes.values():
+            if (cgsnapshot.get('consistencygroup_id', None)
+                    == volume.get('consistencygroup_id', None)):
+
+                if volume['size'] > POOL_SIZE / 2:
+                    raise self.exception.VolumeBackendAPIException(data='blah')
+
+                snapshot = copy.deepcopy(volume)
+                snapshot['name'] = CANNOT_DELETE \
+                    if snapshot['name'] == CANNOT_DELETE \
+                    else snapshot['name'] + 'Snapshot'
+                snapshot['status'] = 'available'
+                snapshot['cgsnapshot_id'] = cgsnapshot.get('id', None)
+                snapshot['consistencygroup_id'] = \
+                    cgsnapshot.get('consistencygroup_id', None)
+                self.snapshots[snapshot['name']] = snapshot
+                snapshots.append(snapshot)
+
+        return {'status': 'available'}, snapshots
+
+    def delete_cgsnapshot(self, ctxt, cgsnapshot):
+        snapshots = []
+        for snapshot in self.snapshots.values():
+            if (cgsnapshot.get('id', None)
+                    == snapshot.get('cgsnapshot_id', None)):
+
+                if snapshot['name'] == CANNOT_DELETE:
+                    raise exception.VolumeBackendAPIException(
+                        message='Snapshot can not be deleted')
+                else:
+                    snapshot['status'] = 'deleted'
+                    snapshots.append(snapshot)
+
+        # Delete snapshots in consistency group
+        self.snapshots = {k: snap for k, snap in self.snapshots.iteritems()
+                          if not(snap.get('consistencygroup_id', None)
+                                 == cgsnapshot.get('cgsnapshot_id', None))}
+
+        return {'status': 'deleted'}, snapshots
+
 
 class XIVDS8KVolumeDriverTest(test.TestCase):
     """Test IBM XIV and DS8K volume driver."""
@@ -237,8 +325,10 @@ class XIVDS8KVolumeDriverTest(test.TestCase):
         """Test the volume exist method with a volume that doesn't exist."""
 
         self.driver.do_setup(None)
+
         self.assertFalse(
-            self.driver.xiv_ds8k_proxy.volume_exists({'name': FAKE}))
+            self.driver.xiv_ds8k_proxy.volume_exists({'name': FAKE})
+        )
 
     def test_delete_volume(self):
         """Verify that a volume is deleted."""
@@ -263,7 +353,7 @@ class XIVDS8KVolumeDriverTest(test.TestCase):
                           self.driver.create_volume,
                           {'name': FAKE,
                            'id': 1,
-                           'size': 12000})
+                           'size': TOO_BIG_VOLUME_SIZE})
 
     def test_initialize_connection(self):
         """Test that inititialize connection attaches volume to host."""
@@ -563,3 +653,194 @@ class XIVDS8KVolumeDriverTest(test.TestCase):
             self.driver.retype,
             ctxt, volume, new_type, diff, host
         )
+
+    def test_create_consistencygroup(self):
+        """Test that create_consistencygroup return successfully."""
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        model_update = self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        self.assertEqual('available',
+                         model_update['status'],
+                         "Consistency Group created failed")
+
+    def test_create_consistencygroup_fail_on_cg_not_empty(self):
+        """Test that create_consistencygroup fail
+        when consistency group is not empty.
+        """
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create volumes
+        # And add the volumes into the consistency group before creating cg
+        self.driver.create_volume(VOLUME)
+
+        self.assertRaises(exception.CinderException,
+                          self.driver.create_consistencygroup,
+                          ctxt, CONSISTGROUP)
+
+    def test_delete_consistencygroup(self):
+        """Test that delete_consistencygroup return successfully."""
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(VOLUME)
+
+        # Delete consistency group
+        model_update, volumes = \
+            self.driver.delete_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Verify the result
+        self.assertEqual('deleted',
+                         model_update['status'],
+                         'Consistency Group deleted failed')
+        for volume in volumes:
+            self.assertEqual('deleted',
+                             volume['status'],
+                             'Consistency Group deleted failed')
+
+    def test_delete_consistencygroup_fail_on_volume_not_delete(self):
+        """Test that delete_consistencygroup return fail
+        when the volume can not be deleted.
+        """
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Set the volume not to be deleted
+        volume = copy.deepcopy(VOLUME)
+        volume['name'] = CANNOT_DELETE
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(volume)
+
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.delete_consistencygroup,
+                          ctxt, CONSISTGROUP)
+
+    def test_create_cgsnapshot(self):
+        """Test that create_cgsnapshot return successfully."""
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(VOLUME)
+
+        # Create consistency group snapshot
+        model_update, snapshots = \
+            self.driver.create_cgsnapshot(ctxt, CG_SNAPSHOT)
+
+        # Verify the result
+        self.assertEqual('available',
+                         model_update['status'],
+                         'Consistency Group Snapshot created failed')
+        for snap in snapshots:
+            self.assertEqual('available',
+                             snap['status'])
+
+        # Clean the environment
+        self.driver.delete_cgsnapshot(ctxt, CG_SNAPSHOT)
+        self.driver.delete_consistencygroup(ctxt, CONSISTGROUP)
+
+    def test_create_cgsnapshot_fail_on_no_pool_space_left(self):
+        """Test that create_cgsnapshot return fail when no pool space left."""
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Set the volume size
+        volume = copy.deepcopy(VOLUME)
+        volume['size'] = POOL_SIZE / 2 + 1
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(volume)
+
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_cgsnapshot,
+                          ctxt, CG_SNAPSHOT)
+
+        # Clean the environment
+        self.driver.volumes = None
+        self.driver.delete_consistencygroup(ctxt, CONSISTGROUP)
+
+    def test_delete_cgsnapshot(self):
+        """Test that delete_cgsnapshot return successfully."""
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(VOLUME)
+
+        # Create consistency group snapshot
+        self.driver.create_cgsnapshot(ctxt, CG_SNAPSHOT)
+
+        # Delete consistency group snapshot
+        model_update, snapshots = \
+            self.driver.delete_cgsnapshot(ctxt, CG_SNAPSHOT)
+
+        # Verify the result
+        self.assertEqual('deleted',
+                         model_update['status'],
+                         'Consistency Group Snapshot deleted failed')
+        for snap in snapshots:
+            self.assertEqual('deleted',
+                             snap['status'])
+
+        # Clean the environment
+        self.driver.delete_consistencygroup(ctxt, CONSISTGROUP)
+
+    def test_delete_cgsnapshot_fail_on_snapshot_not_delete(self):
+        """Test that delete_cgsnapshot return fail
+        when the snapshot can not be deleted.
+        """
+
+        self.driver.do_setup(None)
+
+        ctxt = context.get_admin_context()
+
+        # Create consistency group
+        self.driver.create_consistencygroup(ctxt, CONSISTGROUP)
+
+        # Set the snapshot not to be deleted
+        volume = copy.deepcopy(VOLUME)
+        volume['name'] = CANNOT_DELETE
+
+        # Create volumes and add them to consistency group
+        self.driver.create_volume(volume)
+
+        # Create consistency group snapshot
+        self.driver.create_cgsnapshot(ctxt, CG_SNAPSHOT)
+
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.delete_cgsnapshot,
+                          ctxt, CG_SNAPSHOT)
index 56747d324ece59bd7869baa190d00f46eb34241c..93228a940025e13de14c08e49f7c65d441a87f61 100644 (file)
@@ -227,3 +227,23 @@ class XIVDS8KDriver(san.SanDriver):
         """Convert the volume to be of the new type."""
 
         return self.xiv_ds8k_proxy.retype(ctxt, volume, new_type, diff, host)
+
+    def create_consistencygroup(self, context, group):
+        """Creates a consistency group."""
+
+        return self.xiv_ds8k_proxy.create_consistencygroup(context, group)
+
+    def delete_consistencygroup(self, context, group):
+        """Deletes a consistency group."""
+
+        return self.xiv_ds8k_proxy.delete_consistencygroup(context, group)
+
+    def create_cgsnapshot(self, context, cgsnapshot):
+        """Creates a consistency group snapshot."""
+
+        return self.xiv_ds8k_proxy.create_cgsnapshot(context, cgsnapshot)
+
+    def delete_cgsnapshot(self, context, cgsnapshot):
+        """Deletes a consistency group snapshot."""
+
+        return self.xiv_ds8k_proxy.delete_cgsnapshot(context, cgsnapshot)