From fb25917fe17fc80c1ae704759c8f6487ac2e9a22 Mon Sep 17 00:00:00 2001 From: Ramy Asselin Date: Tue, 4 Mar 2014 11:06:59 -0800 Subject: [PATCH] 3PAR: Support extend volume based on snapshot 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 | 46 +++++++++++++++++++ .../volume/drivers/san/hp/hp_3par_common.py | 38 ++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/cinder/tests/test_hp3par.py b/cinder/tests/test_hp3par.py index a2770ec02..9990d4a5f 100644 --- a/cinder/tests/test_hp3par.py +++ b/cinder/tests/test_hp3par.py @@ -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 diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index b6441bb28..5dc317a69 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -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. -- 2.45.2