HPE Lefthand: add CG capability to generic groups

This patch adds consistency group capability to generic volume group
in Lefthand driver. Re-use the CG implementations if it is a CG
type group. And if the group being created isn't a CG then we
bail out and the generic volume group will take care of it.

Implements: blueprint hpe-lefthand-generic-volume-group
Change-Id: Ib4b353331bad30e7296264542511b1d25a88cd0f
This commit is contained in:
Kushal 2017-03-29 07:28:27 -04:00 committed by Kushal Wathore
parent 3ecfc460b3
commit 81ece6a9f2
3 changed files with 217 additions and 127 deletions

View File

@ -182,18 +182,18 @@ class HPELeftHandBaseDriver(object):
class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
CONSIS_GROUP_ID = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
CGSNAPSHOT_ID = '5351d914-6c90-43e7-9a8e-7e84610927da'
GROUPSNAPSHOT_ID = '5351d914-6c90-43e7-9a8e-7e84610927da'
class fake_consistencygroup_object(object):
volume_type_id = '371c64d5-b92a-488c-bc14-1e63cef40e08'
name = 'cg_name'
cgsnapshot_id = None
class fake_group_object(object):
volume_type_ids = '371c64d5-b92a-488c-bc14-1e63cef40e08'
name = 'group_name'
groupsnapshot_id = None
id = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
description = 'consistency group'
description = 'group'
class fake_cgsnapshot_object(object):
consistencygroup_id = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
description = 'cgsnapshot'
class fake_groupsnapshot_object(object):
group_id = '3470cc4c-63b3-4c7a-8120-8a0693b45838'
description = 'groupsnapshot'
id = '5351d914-6c90-43e7-9a8e-7e84610927da'
readOnly = False
@ -2638,8 +2638,15 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
mock_client.assert_has_calls(expected)
def test_create_consistencygroup(self):
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group(self, cg_ss_enabled, mock_get_volume_type):
cg_ss_enabled.side_effect = [False, True, True]
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2647,15 +2654,39 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
# create a group object
group = self.fake_group_object()
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group with consistent_group_snapshot_enabled flag to
# False
self.assertRaises(NotImplementedError,
self.driver.create_group, ctxt, group)
def test_delete_consistencygroup(self):
# create a group with consistent_group_snapshot_enabled flag to
# True
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# create a group with replication enabled on volume type
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': '<is> True'}}
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.ERROR,
model_update['status'])
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_delete_group(self, cg_ss_enabled, mock_get_volume_type):
cg_ss_enabled.return_value = True
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2666,21 +2697,29 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group
group = self.fake_group_object()
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# delete the consistency group
group.status = fields.ConsistencyGroupStatus.DELETING
cg, vols = self.driver.delete_consistencygroup(ctxt, group,
volumes)
self.assertEqual(fields.ConsistencyGroupStatus.DELETING,
cg['status'])
# delete the group
group.status = fields.GroupStatus.DELETING
model_update, vols = self.driver.delete_group(ctxt, group,
volumes)
self.assertEqual(fields.GroupStatus.DELETING,
model_update['status'])
def test_update_consistencygroup_add_vol_delete_cg(self):
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_update_group_add_vol_delete_group(self, cg_ss_enabled,
mock_get_volume_type):
cg_ss_enabled.return_value = True
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2698,26 +2737,33 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group
group = self.fake_group_object()
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# add volume to consistency group
cg = self.driver.update_consistencygroup(
# add volume to group
model_update = self.driver.update_group(
ctxt, group, add_volumes=[self.volume], remove_volumes=None)
# delete the consistency group
group.status = fields.ConsistencyGroupStatus.DELETING
cg, vols = self.driver.delete_consistencygroup(ctxt, group,
volumes)
self.assertEqual(fields.ConsistencyGroupStatus.DELETING,
cg['status'])
# delete the group
group.status = fields.GroupStatus.DELETING
model_update, vols = self.driver.delete_group(ctxt, group,
volumes)
self.assertEqual(fields.GroupStatus.DELETING,
model_update['status'])
def test_update_consistencygroup_remove_vol_delete_cg(self):
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_update_group_remove_vol_delete_group(self, cg_ss_enabled,
mock_get_volume_type):
cg_ss_enabled.return_value = True
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2734,30 +2780,36 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group
group = self.fake_group_object()
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# add volume to consistency group
cg = self.driver.update_consistencygroup(
# add volume to group
model_update = self.driver.update_group(
ctxt, group, add_volumes=[self.volume], remove_volumes=None)
# remove volume from consistency group
cg = self.driver.update_consistencygroup(
# remove volume from group
model_update = self.driver.update_group(
ctxt, group, add_volumes=None, remove_volumes=[self.volume])
# delete the consistency group
group.status = fields.ConsistencyGroupStatus.DELETING
cg, vols = self.driver.delete_consistencygroup(ctxt, group,
volumes)
self.assertEqual(fields.ConsistencyGroupStatus.DELETING,
cg['status'])
# delete the group
group.status = fields.GroupStatus.DELETING
model_update, vols = self.driver.delete_group(ctxt, group,
volumes)
self.assertEqual(fields.GroupStatus.DELETING,
model_update['status'])
def test_create_cgsnapshot(self):
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_groupsnapshot(self, cg_ss_enabled, mock_get_volume_type):
cg_ss_enabled.return_value = True
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2772,21 +2824,21 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group
group = self.fake_group_object()
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# create volume and add it to the consistency group
self.driver.update_consistencygroup(
# create volume and add it to the group
self.driver.update_group(
ctxt, group, add_volumes=[self.volume], remove_volumes=None)
# create the conistency group snapshot
cgsnapshot = self.fake_cgsnapshot_object()
cgsnap, snaps = self.driver.create_cgsnapshot(
ctxt, cgsnapshot, expected_snaps)
self.assertEqual('available', cgsnap['status'])
# create the group snapshot
groupsnapshot = self.fake_groupsnapshot_object()
madel_update, snaps = self.driver.create_group_snapshot(
ctxt, groupsnapshot, expected_snaps)
self.assertEqual('available', madel_update['status'])
# mock HTTPServerError (array failure)
mock_client.createSnapshotSet.side_effect = (
@ -2794,8 +2846,8 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
self.driver.create_cgsnapshot,
ctxt, cgsnapshot, expected_snaps)
self.driver.create_group_snapshot,
ctxt, groupsnapshot, expected_snaps)
# mock HTTPServerError (array failure)
mock_client.getVolumeByName.side_effect = (
@ -2803,12 +2855,18 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
# ensure the raised exception is a cinder exception
self.assertRaises(
exception.VolumeBackendAPIException,
self.driver.create_cgsnapshot,
ctxt, cgsnapshot, expected_snaps)
self.driver.create_group_snapshot,
ctxt, groupsnapshot, expected_snaps)
def test_delete_cgsnapshot(self):
@mock.patch.object(volume_types, 'get_volume_type')
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_delete_groupsnapshot(self, cg_ss_enabled, mock_get_volume_type):
cg_ss_enabled.return_value = True
ctxt = context.get_admin_context()
mock_get_volume_type.return_value = {
'name': 'gold',
'extra_specs': {
'replication_enabled': ' False'}}
# set up driver with default config
mock_client = self.setup_driver()
@ -2823,22 +2881,22 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
'_create_client') as mock_do_setup:
mock_do_setup.return_value = mock_client
# create a consistency group
group = self.fake_consistencygroup_object()
cg = self.driver.create_consistencygroup(ctxt, group)
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
cg['status'])
# create a group
group = self.fake_group_object()
model_update = self.driver.create_group(ctxt, group)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'])
# create volume and add it to the consistency group
self.driver.update_consistencygroup(
# create volume and add it to the group
self.driver.update_group(
ctxt, group, add_volumes=[self.volume], remove_volumes=None)
# delete the consistency group snapshot
cgsnapshot = self.fake_cgsnapshot_object()
cgsnapshot.status = 'deleting'
cgsnap, snaps = self.driver.delete_cgsnapshot(
ctxt, cgsnapshot, expected_snaps)
self.assertEqual('deleting', cgsnap['status'])
# delete the group snapshot
groupsnapshot = self.fake_groupsnapshot_object()
groupsnapshot.status = 'deleting'
model_update, snaps = self.driver.delete_group_snapshot(
ctxt, groupsnapshot, expected_snaps)
self.assertEqual('deleting', model_update['status'])
# mock HTTPServerError
ex = hpeexceptions.HTTPServerError({
@ -2847,16 +2905,16 @@ class TestHPELeftHandISCSIDriver(HPELeftHandBaseDriver, test.TestCase):
' duh.'})
mock_client.getSnapshotByName.side_effect = ex
# ensure the raised exception is a cinder exception
cgsnap, snaps = self.driver.delete_cgsnapshot(
ctxt, cgsnapshot, expected_snaps)
cgsnap, snaps = self.driver.delete_group_snapshot(
ctxt, groupsnapshot, expected_snaps)
self.assertEqual('error', snaps[0]['status'])
# mock HTTP other errors
ex = hpeexceptions.HTTPConflict({'message': 'Some message.'})
mock_client.getSnapshotByName.side_effect = ex
# ensure the raised exception is a cinder exception
cgsnap, snaps = self.driver.delete_cgsnapshot(
ctxt, cgsnapshot, expected_snaps)
cgsnap, snaps = self.driver.delete_group_snapshot(
ctxt, groupsnapshot, expected_snaps)
self.assertEqual('error', snaps[0]['status'])
@mock.patch.object(volume_types, 'get_volume_type')

View File

@ -161,9 +161,10 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
2.0.9 - Fix terminate connection on failover
2.0.10 - Add entry point tracing
2.0.11 - Fix extend volume if larger than snapshot bug #1560654
2.0.12 - add CG capability to generic volume groups.
"""
VERSION = "2.0.11"
VERSION = "2.0.12"
CI_WIKI_NAME = "HPE_Storage_CI"
@ -469,24 +470,41 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
self._logout(client)
@cinder_utils.trace
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE}
return model_update
def create_group(self, context, group):
"""Creates a group."""
LOG.debug("Creating group.")
if not utils.is_group_a_cg_snapshot_type(group):
raise NotImplementedError()
for vol_type_id in group.volume_type_ids:
replication_type = self._volume_of_replicated_type(
None, vol_type_id)
if replication_type:
# An unsupported configuration
LOG.error('Unable to create group: create group with '
'replication volume type is not supported.')
model_update = {'status': fields.GroupStatus.ERROR}
return model_update
return {'status': fields.GroupStatus.AVAILABLE}
@cinder_utils.trace
def create_consistencygroup_from_src(self, context, group, volumes,
cgsnapshot=None, snapshots=None,
source_cg=None, source_vols=None):
"""Creates a consistency group from a source"""
msg = _("Creating a consistency group from a source is not "
"currently supported.")
LOG.error(msg)
raise NotImplementedError(msg)
def create_group_from_src(self, context, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
"""Creates a group from a source"""
msg = _("Creating a group from a source is not "
"supported when consistent_group_snapshot_enabled to true.")
if not utils.is_group_a_cg_snapshot_type(group):
raise NotImplementedError()
else:
raise exception.VolumeBackendAPIException(data=msg)
@cinder_utils.trace
def delete_consistencygroup(self, context, group, volumes):
"""Deletes a consistency group."""
def delete_group(self, context, group, volumes):
"""Deletes a group."""
if not utils.is_group_a_cg_snapshot_type(group):
raise NotImplementedError()
volume_model_updates = []
for volume in volumes:
volume_update = {'id': volume.id}
@ -506,25 +524,31 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
return model_update, volume_model_updates
@cinder_utils.trace
def update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates a consistency group.
def update_group(self, context, group, add_volumes=None,
remove_volumes=None):
"""Updates a group.
Because the backend has no concept of volume grouping, cinder will
maintain all volume/consistency group relationships. Because of this
maintain all volume/group relationships. Because of this
functionality, there is no need to make any client calls; instead
simply returning out of this function allows cinder to properly
add/remove volumes from the consistency group.
add/remove volumes from the group.
"""
LOG.debug("Updating group.")
if not utils.is_group_a_cg_snapshot_type(group):
raise NotImplementedError()
return None, None, None
@cinder_utils.trace
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a consistency group snapshot."""
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group snapshot."""
if not utils.is_group_a_cg_snapshot_type(group_snapshot):
raise NotImplementedError()
client = self._login()
try:
snap_set = []
snapshot_base_name = "snapshot-" + cgsnapshot.id
snapshot_base_name = "snapshot-" + group_snapshot.id
snapshot_model_updates = []
for i, snapshot in enumerate(snapshots):
volume = snapshot.volume
@ -551,7 +575,7 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
source_volume_id = snap_set[0]['volumeId']
optional = {'inheritAccess': True}
description = cgsnapshot.description
description = group_snapshot.description
if description:
optional['description'] = description
@ -574,11 +598,12 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
return model_update, snapshot_model_updates
@cinder_utils.trace
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a consistency group snapshot."""
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group snapshot."""
if not utils.is_group_a_cg_snapshot_type(group_snapshot):
raise NotImplementedError()
client = self._login()
snap_name_base = "snapshot-" + cgsnapshot.id
snap_name_base = "snapshot-" + group_snapshot.id
snapshot_model_updates = []
for i, snapshot in enumerate(snapshots):
@ -605,7 +630,7 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
self._logout(client)
model_update = {'status': cgsnapshot.status}
model_update = {'status': group_snapshot.status}
return model_update, snapshot_model_updates
@ -705,7 +730,7 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
data['total_volumes'] = total_volumes
data['filter_function'] = self.get_filter_function()
data['goodness_function'] = self.get_goodness_function()
data['consistencygroup_support'] = True
data['consistent_group_snapshot_enabled'] = True
data['replication_enabled'] = self._replication_enabled
data['replication_type'] = ['periodic']
data['replication_count'] = len(self._replication_targets)
@ -1747,9 +1772,12 @@ class HPELeftHandISCSIDriver(driver.ISCSIDriver):
rep_flag = False
return rep_flag
def _volume_of_replicated_type(self, volume):
def _volume_of_replicated_type(self, volume, vol_type_id=None):
# TODO(kushal) : we will use volume.volume_types when we re-write
# the design for unit tests to use objects instead of dicts.
replicated_type = False
volume_type_id = volume.get('volume_type_id')
volume_type_id = vol_type_id if vol_type_id else volume.get(
'volume_type_id')
if volume_type_id:
volume_type = self._get_volume_type(volume_type_id)

View File

@ -0,0 +1,4 @@
---
features:
- Add consistent group capability to generic volume groups in Lefthand
driver.