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:
parent
4d71d60da6
commit
1cbf119420
@ -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:
|
||||
|
@ -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'
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Add consistent group capability to generic volume groups in the SolidFire driver.
|
Loading…
Reference in New Issue
Block a user