Add revert to snapshot support in VxFlex OS driver
Add support for reverting to snapshot in VxFlex OS driver. Implements: blueprint vxflexos-revert-to-snapshot Change-Id: I0ddde05a6270e06e1aee7e1e3bd5de34cc25eb3b
This commit is contained in:
parent
521a49f04c
commit
1a3ad6d8c6
@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.tests.unit import fake_constants as fake
|
||||
from cinder.tests.unit import fake_snapshot
|
||||
from cinder.tests.unit import fake_volume
|
||||
from cinder.tests.unit.volume.drivers.dell_emc import vxflexos
|
||||
|
||||
|
||||
class TestRevertVolume(vxflexos.TestVxFlexOSDriver):
|
||||
"""Test cases for ``VxFlexOSDriver.revert_to_snapshot()``"""
|
||||
|
||||
def setUp(self):
|
||||
"""Setup a test case environment.
|
||||
|
||||
Creates a fake volume object and sets up the required API responses.
|
||||
"""
|
||||
|
||||
super(TestRevertVolume, self).setUp()
|
||||
ctx = context.RequestContext('fake', 'fake', auth_token=True)
|
||||
host = 'host@backend#{}:{}'.format(
|
||||
self.PROT_DOMAIN_NAME,
|
||||
self.STORAGE_POOL_NAME)
|
||||
self.volume = fake_volume.fake_volume_obj(
|
||||
ctx, **{'provider_id': fake.PROVIDER_ID, 'host': host,
|
||||
'volume_type_id': fake.VOLUME_TYPE_ID,
|
||||
'size': 8})
|
||||
self.snapshot = fake_snapshot.fake_snapshot_obj(
|
||||
ctx, **{'volume_id': self.volume.id,
|
||||
'volume_size': self.volume.size}
|
||||
)
|
||||
self.HTTPS_MOCK_RESPONSES = {
|
||||
self.RESPONSE_MODE.Valid: {
|
||||
'instances/Volume::{}/action/overwriteVolumeContent'.format(
|
||||
self.volume.provider_id
|
||||
): {},
|
||||
},
|
||||
self.RESPONSE_MODE.Invalid: {
|
||||
'version': "2.6",
|
||||
},
|
||||
self.RESPONSE_MODE.BadStatus: {
|
||||
'instances/Volume::{}/action/overwriteVolumeContent'.format(
|
||||
self.volume.provider_id
|
||||
): self.BAD_STATUS_RESPONSE
|
||||
},
|
||||
}
|
||||
|
||||
self.volume_is_replicated_mock = self.mock_object(
|
||||
self.volume, 'is_replicated',
|
||||
return_value=False
|
||||
)
|
||||
|
||||
def test_revert_to_snapshot(self):
|
||||
self.driver.revert_to_snapshot(None, self.volume, self.snapshot)
|
||||
|
||||
def test_revert_to_snapshot_badstatus_response(self):
|
||||
self.set_https_response_mode(self.RESPONSE_MODE.BadStatus)
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.revert_to_snapshot,
|
||||
None, self.volume, self.snapshot)
|
||||
|
||||
def test_revert_to_snapshot_use_generic(self):
|
||||
self.set_https_response_mode(self.RESPONSE_MODE.Invalid)
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.revert_to_snapshot,
|
||||
None, self.volume, self.snapshot)
|
||||
|
||||
def test_revert_to_snapshot_replicated_volume(self):
|
||||
self.volume_is_replicated_mock.return_value = True
|
||||
self.assertRaisesRegexp(
|
||||
exception.InvalidVolume,
|
||||
'Reverting replicated volume is not allowed.',
|
||||
self.driver.revert_to_snapshot,
|
||||
None, self.volume, self.snapshot
|
||||
)
|
||||
|
||||
def test_revert_to_snapshot_size_not_equal(self):
|
||||
patched_volume = mock.MagicMock()
|
||||
patched_volume.id = self.volume.id
|
||||
patched_volume.size = 16
|
||||
patched_volume.is_replicated.return_value = False
|
||||
self.assertRaisesRegexp(
|
||||
exception.InvalidVolume,
|
||||
('Volume %s size is not equal to snapshot %s size.' %
|
||||
(self.volume.id, self.snapshot.id)),
|
||||
self.driver.revert_to_snapshot,
|
||||
None, patched_volume, self.snapshot
|
||||
)
|
@ -89,9 +89,10 @@ class VxFlexOSDriver(driver.VolumeDriver):
|
||||
3.5.0 - Add support for VxFlex OS 3.5.x
|
||||
3.5.1 - Add volume replication v2.1 support for VxFlex OS 3.5.x
|
||||
3.5.2 - Add volume migration support
|
||||
3.5.3 - Add revert volume to snapshot support
|
||||
"""
|
||||
|
||||
VERSION = "3.5.2"
|
||||
VERSION = "3.5.3"
|
||||
# ThirdPartySystems wiki
|
||||
CI_WIKI_NAME = "DellEMC_VxFlexOS_CI"
|
||||
|
||||
@ -1502,6 +1503,33 @@ class VxFlexOSDriver(driver.VolumeDriver):
|
||||
location = new_volume.provider_location
|
||||
return {"_name_id": name_id, "provider_location": location}
|
||||
|
||||
def revert_to_snapshot(self, context, volume, snapshot):
|
||||
"""Revert VxFlex OS volume to the specified snapshot."""
|
||||
|
||||
LOG.info("Revert volume %(vol_id)s to snapshot %(snap_id)s.",
|
||||
{"vol_id": volume.id, "snap_id": snapshot.id})
|
||||
|
||||
client = self._get_client()
|
||||
|
||||
if not flex_utils.version_gte(client.query_rest_api_version(), "3.0"):
|
||||
LOG.debug("VxFlex OS versions less than v3.0 do not "
|
||||
"support reverting volume to snapshot. "
|
||||
"Falling back to generic revert to snapshot method.")
|
||||
raise NotImplementedError
|
||||
elif volume.is_replicated():
|
||||
msg = _("Reverting replicated volume is not allowed.")
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidVolume(reason=msg)
|
||||
elif snapshot.volume_size != volume.size:
|
||||
msg = (_("Volume %(vol_id)s size is not equal to snapshot "
|
||||
"%(snap_id)s size. Revert to snapshot operation is not "
|
||||
"allowed.") %
|
||||
{"vol_id": volume.id, "snap_id": snapshot.id})
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidVolume(reason=msg)
|
||||
|
||||
client.overwrite_volume_content(volume, snapshot)
|
||||
|
||||
def _query_vxflexos_volume(self, volume, existing_ref):
|
||||
type_id = volume.get("volume_type_id")
|
||||
if "source-id" not in existing_ref:
|
||||
|
@ -686,3 +686,20 @@ class RestClient(object):
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(msg)
|
||||
return response
|
||||
|
||||
def overwrite_volume_content(self, volume, snapshot):
|
||||
url = "/instances/Volume::%(vol_id)s/action/overwriteVolumeContent"
|
||||
|
||||
params = {"srcVolumeId": snapshot.provider_id}
|
||||
r, response = self.execute_vxflexos_post_request(
|
||||
url,
|
||||
params=params,
|
||||
vol_id=volume.provider_id
|
||||
)
|
||||
if r.status_code != http_client.OK:
|
||||
msg = (_("Failed to revert volume %(vol_id)s to snapshot "
|
||||
"%(snap_id)s: %(err)s.") % {"vol_id": volume.id,
|
||||
"snap_id": snapshot.id,
|
||||
"err": response["message"]})
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(msg)
|
||||
|
@ -73,6 +73,8 @@ Supported operations
|
||||
|
||||
* Create a volume from a snapshot
|
||||
|
||||
* Revert a volume to a snapshot
|
||||
|
||||
* Copy an image to a volume
|
||||
|
||||
* Copy a volume to an image
|
||||
|
@ -739,7 +739,7 @@ driver.dell_emc_unity=complete
|
||||
driver.dell_emc_vmax_af=complete
|
||||
driver.dell_emc_vmax_3=complete
|
||||
driver.dell_emc_vnx=complete
|
||||
driver.dell_emc_vxflexos=missing
|
||||
driver.dell_emc_vxflexos=complete
|
||||
driver.dell_emc_xtremio=missing
|
||||
driver.fujitsu_eternus=missing
|
||||
driver.hpe_3par=complete
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
VxFlex OS driver now supports storage-assisted revert volume to snapshot.
|
Loading…
Reference in New Issue
Block a user