From 2a985a6ae9febf3469bace65f7a7f2b31b2578fc Mon Sep 17 00:00:00 2001
From: Tom Swanson <tom_swanson@dell.com>
Date: Mon, 15 Feb 2016 11:42:34 -0600
Subject: [PATCH] Dell: Failed vol create could leave dead volumes

Added _cleanup_failed_create_volume which tries to delete any
partially created volumes.

Change-Id: I1f2f549b364a013b713e193591d08b9e9376569e
Closes-Bug: 1546161
---
 cinder/tests/unit/test_dellsc.py              | 38 +++++++++++++++++++
 .../drivers/dell/dell_storagecenter_common.py | 34 +++++++++++++----
 2 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/cinder/tests/unit/test_dellsc.py b/cinder/tests/unit/test_dellsc.py
index 41da54303..d636bdad0 100644
--- a/cinder/tests/unit/test_dellsc.py
+++ b/cinder/tests/unit/test_dellsc.py
@@ -537,7 +537,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
     @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
                        'find_sc',
                        return_value=12345)
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'delete_volume')
     def test_create_volume_failure(self,
+                                   mock_delete_volume,
                                    mock_find_sc,
                                    mock_create_volume,
                                    mock_close_connection,
@@ -546,6 +549,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
         volume = {'id': self.volume_name, 'size': 1}
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.create_volume, volume)
+        self.assertTrue(mock_delete_volume.called)
 
     @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
                        '_delete_replications')
@@ -1076,7 +1080,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
     @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
                        'create_view_volume',
                        return_value=None)
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'delete_volume')
     def test_create_volume_from_snapshot_failed(self,
+                                                mock_delete_volume,
                                                 mock_create_view_volume,
                                                 mock_find_replay_profile,
                                                 mock_find_replay,
@@ -1093,6 +1100,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
         self.assertTrue(mock_find_replay.called)
         self.assertTrue(mock_find_volume.called)
         self.assertFalse(mock_find_replay_profile.called)
+        self.assertTrue(mock_delete_volume.called)
 
     @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
                        '_create_replications')
@@ -1189,6 +1197,36 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
         self.assertTrue(mock_find_volume.called)
         self.assertEqual({}, ret)
 
+    @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
+                       '_create_replications',
+                       return_value={})
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'find_sc',
+                       return_value=12345)
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'find_volume',
+                       return_value=VOLUME)
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'create_cloned_volume',
+                       return_value=None)
+    @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
+                       'delete_volume')
+    def test_create_cloned_volume_failed(self,
+                                         mock_delete_volume,
+                                         mock_create_cloned_volume,
+                                         mock_find_volume,
+                                         mock_find_sc,
+                                         mock_create_replications,
+                                         mock_close_connection,
+                                         mock_open_connection,
+                                         mock_init):
+        volume = {'id': self.volume_name + '_clone'}
+        src_vref = {'id': self.volume_name}
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_cloned_volume,
+                          volume, src_vref)
+        self.assertTrue(mock_delete_volume.called)
+
     @mock.patch.object(dell_storagecenter_api.StorageCenterApi,
                        'delete_volume')
     @mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
diff --git a/cinder/volume/drivers/dell/dell_storagecenter_common.py b/cinder/volume/drivers/dell/dell_storagecenter_common.py
index fad3edd53..888811930 100644
--- a/cinder/volume/drivers/dell/dell_storagecenter_common.py
+++ b/cinder/volume/drivers/dell/dell_storagecenter_common.py
@@ -199,6 +199,13 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
                             'replication_driver_data': replication_driver_data}
         return model_update
 
+    @staticmethod
+    def _cleanup_failed_create_volume(api, volumename):
+        try:
+            api.delete_volume(volumename)
+        except exception.VolumeBackendAPIException as ex:
+            LOG.info(_LI('Non fatal cleanup error: %s.'), ex.msg)
+
     def create_volume(self, volume):
         """Create a volume."""
         model_update = {}
@@ -224,6 +231,10 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
                                                  volume_size,
                                                  storage_profile,
                                                  replay_profile_string)
+                    if scvolume is None:
+                        raise exception.VolumeBackendAPIException(
+                            message=_('Unable to create volume %s') %
+                            volume_name)
 
                 # Update Consistency Group
                 self._add_volume_to_consistency_group(api, scvolume, volume)
@@ -234,14 +245,13 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
             except Exception:
                 # if we actually created a volume but failed elsewhere
                 # clean up the volume now.
-                if scvolume:
-                    api.delete_volume(volume_name)
+                self._cleanup_failed_create_volume(api, volume_name)
                 with excutils.save_and_reraise_exception():
                     LOG.error(_LE('Failed to create volume %s'),
                               volume_name)
         if scvolume is None:
             raise exception.VolumeBackendAPIException(
-                data=_('Unable to create volume'))
+                data=_('Unable to create volume. Backend down.'))
 
         return model_update
 
@@ -366,6 +376,12 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
                                 'storagetype:replayprofiles')
                             scvolume = api.create_view_volume(
                                 volume_name, replay, replay_profile_string)
+                            if scvolume is None:
+                                raise exception.VolumeBackendAPIException(
+                                    message=_('Unable to create volume '
+                                              '%(name)s from %(snap)s.') %
+                                    {'name': volume_name,
+                                     'snap': snapshot_id})
 
                             # Update Consistency Group
                             self._add_volume_to_consistency_group(api,
@@ -378,8 +394,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
 
             except Exception:
                 # Clean up after ourselves.
-                if scvolume:
-                    api.delete_volume(volume_name)
+                self._cleanup_failed_create_volume(api, volume_name)
                 with excutils.save_and_reraise_exception():
                     LOG.error(_LE('Failed to create volume %s'),
                               volume_name)
@@ -414,6 +429,12 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
                         # Create our volume
                         scvolume = api.create_cloned_volume(
                             volume_name, srcvol, replay_profile_string)
+                        if scvolume is None:
+                            raise exception.VolumeBackendAPIException(
+                                message=_('Unable to create volume '
+                                          '%(name)s from %(vol)s.') %
+                                {'name': volume_name,
+                                 'vol': src_volume_name})
 
                         # Update Consistency Group
                         self._add_volume_to_consistency_group(api,
@@ -425,8 +446,7 @@ class DellCommonDriver(driver.ConsistencyGroupVD, driver.ManageableVD,
                                                                  scvolume)
             except Exception:
                 # Clean up after ourselves.
-                if scvolume:
-                    api.delete_volume(volume_name)
+                self._cleanup_failed_create_volume(api, volume_name)
                 with excutils.save_and_reraise_exception():
                     LOG.error(_LE('Failed to create volume %s'),
                               volume_name)
-- 
2.45.2