SolidFire Generic Groups Support

In order to satisfy the changes around generic groups in Cinder, the
consistency group calls within the SolidFire driver were wrapped in the
appropriate generic group calls. Asides from a few key changes, there
is no functional change in the underlying methods.

Change-Id: I150133c02f5c35a7ef226f99fe30f34c484c4341
Closes-Bug: #1682241
Implements: blueprint solidfire-add-cg-to-generic-volume-groups
This commit is contained in:
Chris Morrell 2017-04-26 15:52:42 -06:00
parent 4d71d60da6
commit 1cbf119420
3 changed files with 225 additions and 37 deletions

View File

@ -1587,7 +1587,7 @@ class SolidFireVolumeTestCase(test.TestCase):
self.assertEqual(1, rem_vag.call_count)
rem_vag.assert_called_with(1)
def test_create_group_snapshot(self):
def test_sf_create_group_snapshot(self):
# Sunny day group snapshot creation.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
name = 'great_gsnap_name'
@ -1598,7 +1598,7 @@ class SolidFireVolumeTestCase(test.TestCase):
with mock.patch.object(sfv,
'_issue_api_request',
return_value=fake_result) as fake_api:
res = sfv._create_group_snapshot(name, sf_volumes)
res = sfv._sf_create_group_snapshot(name, sf_volumes)
self.assertEqual('contrived_test', res)
fake_api.assert_called_with('CreateGroupSnapshot',
expected_params,
@ -1616,7 +1616,7 @@ class SolidFireVolumeTestCase(test.TestCase):
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_create_group_snapshot',
'_sf_create_group_snapshot',
return_value=None) as create:
sfv._group_snapshot_creator(gsnap_name, vol_uuids)
create.assert_called_with(gsnap_name,
@ -1732,13 +1732,13 @@ class SolidFireVolumeTestCase(test.TestCase):
mock.patch.object(sfv,
'_do_clone_volume',
return_value=kek):
model, vol_models = sfv.create_consistencygroup_from_src(
model, vol_models = sfv._create_consistencygroup_from_src(
ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg, source_vols)
get_snap.assert_called_with(name)
self.assertEqual(
{'status': fields.ConsistencyGroupStatus.AVAILABLE}, model)
{'status': fields.GroupStatus.AVAILABLE}, model)
def test_create_consisgroup_from_src_source_cg(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
@ -1768,14 +1768,14 @@ class SolidFireVolumeTestCase(test.TestCase):
return_value=kek),\
mock.patch.object(sfv,
'_delete_cgsnapshot_by_name'):
model, vol_models = sfv.create_consistencygroup_from_src(
model, vol_models = sfv._create_consistencygroup_from_src(
ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg,
source_vols)
get_snap.assert_called_with(source_cg['id'])
self.assertEqual(
{'status': fields.ConsistencyGroupStatus.AVAILABLE}, model)
{'status': fields.GroupStatus.AVAILABLE}, model)
def test_create_cgsnapshot(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
@ -1790,8 +1790,8 @@ class SolidFireVolumeTestCase(test.TestCase):
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_create_group_snapshot') as create_gsnap:
sfv.create_cgsnapshot(ctxt, cgsnapshot, snapshots)
'_sf_create_group_snapshot') as create_gsnap:
sfv._create_cgsnapshot(ctxt, cgsnapshot, snapshots)
create_gsnap.assert_called_with(pfx + cgsnapshot['id'],
active_vols)
@ -1807,9 +1807,9 @@ class SolidFireVolumeTestCase(test.TestCase):
'_get_all_active_volumes',
return_value=active_vols),\
mock.patch.object(sfv,
'_create_group_snapshot'):
'_sf_create_group_snapshot'):
self.assertRaises(exception.SolidFireDriverException,
sfv.create_cgsnapshot,
sfv._create_cgsnapshot,
ctxt,
cgsnapshot,
snapshots)
@ -1818,11 +1818,11 @@ class SolidFireVolumeTestCase(test.TestCase):
# cgsnaps on the backend yield numerous identically named snapshots.
# create_volume_from_snapshot now searches for the correct snapshot.
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
source = {'cgsnapshot_id': 'typical_cgsnap_id',
source = {'group_snapshot_id': 'typical_cgsnap_id',
'volume_id': 'typical_vol_id',
'id': 'no_id_4_u'}
name = (self.configuration.sf_volume_prefix
+ source.get('cgsnapshot_id'))
+ source.get('group_snapshot_id'))
with mock.patch.object(sfv,
'_get_group_snapshot_by_name',
return_value={}) as get,\
@ -1833,6 +1833,136 @@ class SolidFireVolumeTestCase(test.TestCase):
get.assert_called_once_with(name)
self.assertEqual('model', result)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_cg(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
result = sfv.create_group(self.ctxt, group)
self.assertEqual(result,
{'status': fields.GroupStatus.AVAILABLE})
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
self.assertRaises(NotImplementedError,
sfv.create_group,
self.ctxt, group)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
volumes = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.create_group_from_src,
self.ctxt, group, volumes)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_from_src_cg(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
volumes = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_create_consistencygroup_from_src',
return_value=ret):
result = sfv.create_group_from_src(self.ctxt,
group,
volumes)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_snapshot_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group_snapshot = mock.MagicMock()
snapshots = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.create_group_snapshot,
self.ctxt,
group_snapshot,
snapshots)
group_cg_test.assert_called_once_with(group_snapshot)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_group_snapshot(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group_snapshot = mock.MagicMock()
snapshots = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_create_cgsnapshot',
return_value=ret):
result = sfv.create_group_snapshot(self.ctxt,
group_snapshot,
snapshots)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group_snapshot)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_delete_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
volumes = [mock.MagicMock()]
self.assertRaises(NotImplementedError,
sfv.delete_group,
self.ctxt,
group,
volumes)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_delete_group(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
volumes = [mock.MagicMock()]
ret = 'things'
with mock.patch.object(sfv,
'_delete_consistencygroup',
return_value=ret):
result = sfv.delete_group(self.ctxt,
group,
volumes)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_update_group_rainy(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = False
group = mock.MagicMock()
self.assertRaises(NotImplementedError,
sfv.update_group,
self.ctxt,
group)
group_cg_test.assert_called_once_with(group)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_update_group(self, group_cg_test):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
group_cg_test.return_value = True
group = mock.MagicMock()
ret = 'things'
with mock.patch.object(sfv,
'_update_consistencygroup',
return_value=ret):
result = sfv.update_group(self.ctxt,
group)
self.assertEqual(ret, result)
group_cg_test.assert_called_once_with(group)
def test_getattr_failure(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
try:

View File

@ -42,6 +42,7 @@ from cinder import utils
from cinder.volume.drivers.san import san
from cinder.volume import qos_specs
from cinder.volume.targets import iscsi as iscsi_driver
from cinder.volume import utils as vol_utils
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
@ -1482,16 +1483,16 @@ class SolidFireDriver(san.SanISCSIDriver):
@locked_source_id_operation
def create_volume_from_snapshot(self, volume, source):
"""Create a volume from the specified snapshot."""
if source.get('cgsnapshot_id'):
if source.get('group_snapshot_id'):
# We're creating a volume from a snapshot that resulted from a
# consistency group snapshot. Because of the way that SolidFire
# creates cgsnaps, we have to search for the correct snapshot.
cgsnapshot_id = source.get('cgsnapshot_id')
group_snapshot_id = source.get('group_snapshot_id')
snapshot_id = source.get('volume_id')
sf_name = self.configuration.sf_volume_prefix + cgsnapshot_id
sf_name = self.configuration.sf_volume_prefix + group_snapshot_id
sf_group_snap = self._get_group_snapshot_by_name(sf_name)
return self._create_clone_from_sf_snapshot(snapshot_id,
cgsnapshot_id,
group_snapshot_id,
sf_group_snap,
volume)
@ -1502,7 +1503,7 @@ class SolidFireDriver(san.SanISCSIDriver):
return model
# Consistency group helpers
def _create_group_snapshot(self, name, sf_volumes):
def _sf_create_group_snapshot(self, name, sf_volumes):
# Group snapshot is our version of a consistency group snapshot.
vol_ids = [vol['volumeID'] for vol in sf_volumes]
params = {'name': name,
@ -1527,7 +1528,7 @@ class SolidFireDriver(san.SanISCSIDriver):
"des": len(src_vol_ids)})
raise exception.SolidFireDriverException(msg)
result = self._create_group_snapshot(gsnap_name, target_vols)
result = self._sf_create_group_snapshot(gsnap_name, target_vols)
return result
def _create_temp_group_snapshot(self, source_cg, source_vols):
@ -1607,17 +1608,68 @@ class SolidFireDriver(san.SanISCSIDriver):
self.configuration.sf_volume_prefix)[1]
return vlist
# Required consistency group functions
def create_consistencygroup(self, ctxt, group):
# SolidFire does not have a viable means for storing consistency group
# volume associations. So, we're just going to play along with the
# consistency group song and dance. There will be a lot of no-ops
# because of this.
return {'status': fields.ConsistencyGroupStatus.AVAILABLE}
# Generic Volume Groups.
def create_group(self, ctxt, group):
# SolidFire does not have the concept of volume groups. We're going to
# play along with the group song and dance. There will be a lot of
# no-ops because of this.
if vol_utils.is_group_a_cg_snapshot_type(group):
return {'status': fields.GroupStatus.AVAILABLE}
def create_consistencygroup_from_src(self, ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg, source_vols):
# Blatantly ripping off this pattern from other drivers.
raise NotImplementedError()
def create_group_from_src(self, ctxt, group, volumes, group_snapshots=None,
snapshots=None, source_group=None,
source_vols=None):
# At this point this is just a pass-through.
if vol_utils.is_group_a_cg_snapshot_type(group):
return self._create_consistencygroup_from_src(
ctxt,
group,
volumes,
group_snapshots,
snapshots,
source_group,
source_vols)
# Default implementation handles other scenarios.
raise NotImplementedError()
def create_group_snapshot(self, ctxt, group_snapshot, snapshots):
# This is a pass-through to the old consistency group stuff.
if vol_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self._create_cgsnapshot(ctxt, group_snapshot, snapshots)
# Default implementation handles other scenarios.
raise NotImplementedError()
def delete_group(self, ctxt, group, volumes):
# Delete a volume group. SolidFire does not track volume groups,
# however we do need to actually remove the member volumes of the
# group. Right now only consistent volume groups are supported.
if vol_utils.is_group_a_cg_snapshot_type(group):
return self._delete_consistencygroup(ctxt, group, volumes)
# Default implementation handles other scenarios.
raise NotImplementedError()
def update_group(self, ctxt, group, add_volumes=None, remove_volumes=None):
# Regarding consistency groups SolidFire does not track volumes, so
# this is a no-op. In the future with replicated volume groups this
# might actually do something.
if vol_utils.is_group_a_cg_snapshot_type(group):
return self._update_consistencygroup(ctxt,
group,
add_volumes,
remove_volumes)
# Default implementation handles other scenarios.
raise NotImplementedError()
def _create_consistencygroup_from_src(self, ctxt, group, volumes,
cgsnapshot, snapshots,
source_cg, source_vols):
if cgsnapshot and snapshots:
sf_name = self.configuration.sf_volume_prefix + cgsnapshot['id']
sf_group_snap = self._get_group_snapshot_by_name(sf_name)
@ -1630,7 +1682,7 @@ class SolidFireDriver(san.SanISCSIDriver):
snap['id'],
sf_group_snap,
vol))
return ({'status': fields.ConsistencyGroupStatus.AVAILABLE},
return ({'status': fields.GroupStatus.AVAILABLE},
vol_models)
elif source_cg and source_vols:
@ -1649,9 +1701,9 @@ class SolidFireDriver(san.SanISCSIDriver):
vol))
finally:
self._delete_cgsnapshot_by_name(gsnap_name)
return {'status': 'available'}, vol_models
return {'status': fields.GroupStatus.AVAILABLE}, vol_models
def create_cgsnapshot(self, ctxt, cgsnapshot, snapshots):
def _create_cgsnapshot(self, ctxt, cgsnapshot, snapshots):
vol_ids = [snapshot['volume_id'] for snapshot in snapshots]
vol_names = [self.configuration.sf_volume_prefix + vol_id
for vol_id in vol_ids]
@ -1665,21 +1717,23 @@ class SolidFireDriver(san.SanISCSIDriver):
"des": len(snapshots)})
raise exception.SolidFireDriverException(msg)
snap_name = self.configuration.sf_volume_prefix + cgsnapshot['id']
self._create_group_snapshot(snap_name, target_vols)
self._sf_create_group_snapshot(snap_name, target_vols)
return None, None
def update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
def _update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
# Similar to create_consistencygroup, SolidFire's lack of a consistency
# group object means there is nothing to update on the cluster.
return None, None, None
def delete_cgsnapshot(self, ctxt, cgsnapshot, snapshots):
def _delete_cgsnapshot(self, ctxt, cgsnapshot, snapshots):
snap_name = self.configuration.sf_volume_prefix + cgsnapshot['id']
self._delete_cgsnapshot_by_name(snap_name)
return None, None
def delete_consistencygroup(self, ctxt, group, volumes):
def _delete_consistencygroup(self, ctxt, group, volumes):
# TODO(chris_morrell): exception handling and return correctly updated
# volume_models.
for vol in volumes:
self.delete_volume(vol)
@ -1732,6 +1786,7 @@ class SolidFireDriver(san.SanISCSIDriver):
data["driver_version"] = self.VERSION
data["storage_protocol"] = 'iSCSI'
data['consistencygroup_support'] = True
data['consistent_group_snapshot_enabled'] = True
data['replication_enabled'] = self.replication_enabled
if self.replication_enabled:
data['replication'] = 'enabled'

View File

@ -0,0 +1,3 @@
---
features:
- Add consistent group capability to generic volume groups in the SolidFire driver.