]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
3PAR: Support extend volume based on snapshot
authorRamy Asselin <ramy.asselin@hp.com>
Tue, 4 Mar 2014 19:06:59 +0000 (11:06 -0800)
committerRamy Asselin <ramy.asselin@hp.com>
Fri, 7 Mar 2014 23:48:37 +0000 (15:48 -0800)
If extend volume fails with an HTTP Forbidden error 150,
convert the volume to a base volume and retry extending.

Change-Id: Id407058e954b2630f4a7f31c6149361301b502f2
Closes-Bug: #1285906

cinder/tests/test_hp3par.py
cinder/volume/drivers/san/hp/hp_3par_common.py

index a2770ec02213a1a1ecaeb2bf1e11eeafac535b2a..9990d4a5fe08a117d4a0591f623d9ff8e0ff096c 100644 (file)
@@ -781,6 +781,52 @@ class HP3PARBaseDriver(object):
 
         mock_client.assert_has_calls(expected)
 
+    def test_extend_volume_non_base(self):
+        extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
+        conf = {
+            'getPorts.return_value': {
+                'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
+            'getTask.return_value': {
+                'status': 1},
+            'getCPG.return_value': {},
+            'copyVolume.return_value': {'taskid': 1},
+            'getVolume.return_value': {},
+            # Throw an exception first time only
+            'growVolume.side_effect': [extend_ex,
+                                       None],
+        }
+
+        mock_client = self.setup_driver(mock_conf=conf)
+        grow_size = 3
+        old_size = self.volume['size']
+        new_size = old_size + grow_size
+        self.driver.extend_volume(self.volume, str(new_size))
+
+        self.assertEqual(2, mock_client.growVolume.call_count)
+
+    def test_extend_volume_non_base_failure(self):
+        extend_ex = hpexceptions.HTTPForbidden(error={'code': 150})
+        conf = {
+            'getPorts.return_value': {
+                'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]},
+            'getTask.return_value': {
+                'status': 1},
+            'getCPG.return_value': {},
+            'copyVolume.return_value': {'taskid': 1},
+            'getVolume.return_value': {},
+            # Always fail
+            'growVolume.side_effect': extend_ex
+        }
+
+        mock_client = self.setup_driver(mock_conf=conf)
+        grow_size = 3
+        old_size = self.volume['size']
+        new_size = old_size + grow_size
+        self.assertRaises(hpexceptions.HTTPForbidden,
+                          self.driver.extend_volume,
+                          self.volume,
+                          str(new_size))
+
     def test_get_ports(self):
         # setup_mock_client drive with default configuration
         # and return the mock HTTP 3PAR client
index b6441bb28a011cdf52b564b5a0558160f1f16972..5dc317a6976c229eef940b30845ff32fb5279308 100644 (file)
@@ -120,10 +120,11 @@ class HP3PARCommon(object):
         2.0.4 - Allow volumes created from snapshots to be larger bug #1279478
         2.0.5 - Fix extend volume units bug #1284368
         2.0.6 - use loopingcall.wait instead of time.sleep
+        2.0.7 - Allow extend volume based on snapshot bug #1285906
 
     """
 
-    VERSION = "2.0.6"
+    VERSION = "2.0.7"
 
     stats = {}
 
@@ -248,14 +249,39 @@ class HP3PARCommon(object):
         volume_name = self._get_3par_vol_name(volume['id'])
         old_size = volume['size']
         growth_size = int(new_size) - old_size
-        LOG.debug("Extending Volume %s from %s to %s, by %s GB." %
-                  (volume_name, old_size, new_size, growth_size))
+        LOG.debug(_("Extending Volume %(vol)s from %(old)s to %(new)s, "
+                    " by %(diff)s GB.") %
+                  {'vol': volume_name, 'old': old_size, 'new': new_size,
+                   'diff': growth_size})
         growth_size_mib = growth_size * units.KiB
+        self._extend_volume(volume, volume_name, growth_size_mib)
+
+    def _extend_volume(self, volume, volume_name, growth_size_mib,
+                       _convert_to_base=False):
         try:
+            if _convert_to_base:
+                LOG.debug(_("Converting to base volume prior to growing."))
+                self._convert_to_base_volume(volume)
             self.client.growVolume(volume_name, growth_size_mib)
-        except Exception:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_("Error extending volume %s") % volume)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception() as ex_ctxt:
+                if (not _convert_to_base and
+                    isinstance(ex, hpexceptions.HTTPForbidden) and
+                        ex.get_code() == 150):
+                        # Error code 150 means 'invalid operation: Cannot grow
+                        # this type of volume'.
+                        # Suppress raising this exception because we can
+                        # resolve it by converting it into a base volume.
+                        # Afterwards, extending the volume should succeed, or
+                        # fail with a different exception/error code.
+                        ex_ctxt.reraise = False
+                        self._extend_volume(volume, volume_name,
+                                            growth_size_mib,
+                                            _convert_to_base=True)
+                else:
+                    LOG.error(_("Error extending volume: %(vol)s. "
+                                "Exception: %(ex)s") %
+                              {'vol': volume_name, 'ex': ex})
 
     def _get_3par_vol_name(self, volume_id):
         """Get converted 3PAR volume name.