From 5043773390e11719b17d4f999a1df58dcab36f7b Mon Sep 17 00:00:00 2001 From: Jim Branen Date: Mon, 3 Mar 2014 10:53:17 -0800 Subject: [PATCH] Fix HP LeftHand migration with snapshots Check for snapshots associated with volumes during assisted migration, and fail if a snapshot is found. When a volume is cloned on the HP LeftHand array, a snapshot is created and the cloned volume is created from the snapshot. The snapshot remains associated with the newly created volume. Therefore, backend assisted migration should check for volumes with snapshots and not use native APIs to migrate any volume that has a snapshot. Change-Id: I29fc3dbd1f24c01968e7c3d043cdf6b9d1b89ee9 Closes-Bug:#1285829 --- cinder/tests/test_hplefthand.py | 46 +++++++++++++++++++ .../drivers/san/hp/hp_lefthand_rest_proxy.py | 37 ++++++++++----- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/cinder/tests/test_hplefthand.py b/cinder/tests/test_hplefthand.py index 3fc7b96d1..36428c564 100644 --- a/cinder/tests/test_hplefthand.py +++ b/cinder/tests/test_hplefthand.py @@ -1230,6 +1230,8 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase): mock_client.getVolumeByName.return_value = {'id': self.volume_id, 'iscsiSessions': None} + mock_client.getVolume.return_value = {'snapshots': { + 'resource': None}} location = (self.driver.proxy.DRIVER_LOCATION % { 'cluster': 'New_CloudCluster', @@ -1247,6 +1249,9 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase): expected = self.driver_startup_call_stack + [ mock.call.getClusterByName('New_CloudCluster'), mock.call.getVolumeByName('fakevolume'), + mock.call.getVolume( + 1, + 'fields=snapshots,snapshots[resource[members[name]]]'), mock.call.modifyVolume(1, {'clusterName': 'New_CloudCluster'})] mock_client.assert_has_calls(expected) @@ -1254,3 +1259,44 @@ class TestHPLeftHandRESTISCSIDriver(HPLeftHandBaseDriver, test.TestCase): self.assertEqual( len(expected), len(mock_client.method_calls)) + + def test_migrate_with_Snapshots(self): + # setup drive with default configuration + # and return the mock HTTP LeftHand client + mock_client = self.setup_driver() + mock_client.getClusterByName.return_value = { + "virtualIPAddresses": [{ + "ipV4Address": "10.10.10.111", + "ipV4NetMask": "255.255.240.0"}]} + + mock_client.getVolumeByName.return_value = { + 'id': self.volume_id, + 'iscsiSessions': None} + mock_client.getVolume.return_value = {'snapshots': { + 'resource': 'snapfoo'}} + + location = (self.driver.proxy.DRIVER_LOCATION % { + 'cluster': 'New_CloudCluster', + 'vip': '10.10.10.111'}) + + host = { + 'host': self.serverName, + 'capabilities': {'location_info': location}} + (migrated, update) = self.driver.migrate_volume( + None, + self.volume, + host) + self.assertFalse(migrated) + + expected = self.driver_startup_call_stack + [ + mock.call.getClusterByName('New_CloudCluster'), + mock.call.getVolumeByName('fakevolume'), + mock.call.getVolume( + 1, + 'fields=snapshots,snapshots[resource[members[name]]]')] + + mock_client.assert_has_calls(expected) + # and nothing else + self.assertEqual( + len(expected), + len(mock_client.method_calls)) diff --git a/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py b/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py index 30468a99b..80c08ac84 100644 --- a/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py +++ b/cinder/volume/drivers/san/hp/hp_lefthand_rest_proxy.py @@ -85,9 +85,11 @@ class HPLeftHandRESTProxy(ISCSIDriver): 1.0.0 - Initial REST iSCSI proxy 1.0.1 - Added support for retype 1.0.2 - Added support for volume migrate + 1.0.3 - Fixed bug #1285829, HP LeftHand backend assisted migration + should check for snapshots """ - VERSION = "1.0.2" + VERSION = "1.0.3" device_stats = {} @@ -467,38 +469,49 @@ class HPLeftHandRESTProxy(ISCSIDriver): virtual_ips = cluster_info['virtualIPAddresses'] if driver != self.__class__.__name__: - LOG.info(_("Can not provide backend assisted migration for " - "volume:%s because volume is from a different " + LOG.info(_("Cannot provide backend assisted migration for " + "volume: %s because volume is from a different " "backend.") % volume['name']) return false_ret if vip != virtual_ips[0]['ipV4Address']: - LOG.info(_("Can not provide backend assisted migration for " - "volume:%s because cluster exists in different " + LOG.info(_("Cannot provide backend assisted migration for " + "volume: %s because cluster exists in different " "management group.") % volume['name']) return false_ret except hpexceptions.HTTPNotFound: - LOG.info(_("Can not provide backend assisted migration for " - "volume:%s because cluster exists in different " + LOG.info(_("Cannot provide backend assisted migration for " + "volume: %s because cluster exists in different " "management group.") % volume['name']) return false_ret try: - options = {'clusterName': cluster} volume_info = self.client.getVolumeByName(volume['name']) LOG.debug(_('Volume info: %s') % volume_info) # can't migrate if server is attached if volume_info['iscsiSessions'] is not None: - LOG.info(_("Can not provide backend assisted migration " - "for volume:%s because the volume has been " + LOG.info(_("Cannot provide backend assisted migration " + "for volume: %s because the volume has been " "exported.") % volume['name']) return false_ret + # can't migrate if volume has snapshots + snap_info = self.client.getVolume( + volume_info['id'], + 'fields=snapshots,snapshots[resource[members[name]]]') + LOG.debug(_('Snapshot info: %s') % snap_info) + if snap_info['snapshots']['resource'] is not None: + LOG.info(_("Cannot provide backend assisted migration " + "for volume: %s because the volume has " + "snapshots.") % volume['name']) + return false_ret + + options = {'clusterName': cluster} self.client.modifyVolume(volume_info['id'], options) except hpexceptions.HTTPNotFound: - LOG.info(_("Can not provide backend assisted migration for " - "volume:%s because volume does not exist in this " + LOG.info(_("Cannot provide backend assisted migration for " + "volume: %s because volume does not exist in this " "management group.") % volume['name']) return false_ret except hpexceptions.HTTPServerError as ex: -- 2.45.2