Dell EMC SC: Support generic groups
Add CG capability to Generic Volume Groups in Dell EMC SC driver. Removed driver interfaces for consistency groups as they won't be called in Pike. Implements: blueprint add-generic-groups-to-dell-emc-sc-driver Change-Id: I208144e6796166576a80a8f74e0bb00658c2950f
This commit is contained in:
parent
aa13d63d1b
commit
bd619f2cea
@ -579,15 +579,15 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'create_volume',
|
||||
return_value=VOLUME)
|
||||
def test_create_volume_consistency_group(self,
|
||||
mock_create_volume,
|
||||
mock_update_cg_volumes,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
def test_create_volume_with_group(self,
|
||||
mock_create_volume,
|
||||
mock_update_cg_volumes,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
volume = {'id': fake.VOLUME_ID, 'size': 1,
|
||||
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID}
|
||||
'group_id': fake.GROUP_ID}
|
||||
self.driver.create_volume(volume)
|
||||
mock_create_volume.assert_called_once_with(
|
||||
fake.VOLUME_ID, 1, None, None, None, None, None)
|
||||
@ -1681,7 +1681,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
model_update = {'something': 'something'}
|
||||
mock_create_replications.return_value = model_update
|
||||
volume = {'id': fake.VOLUME_ID,
|
||||
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID, 'size': 1}
|
||||
'group_id': fake.GROUP_ID, 'size': 1}
|
||||
snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID,
|
||||
'volume_size': 1}
|
||||
res = self.driver.create_volume_from_snapshot(volume, snapshot)
|
||||
@ -1975,7 +1975,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
volume = {'id': fake.VOLUME_ID,
|
||||
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'group_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'size': 1}
|
||||
src_vref = {'id': fake.VOLUME2_ID, 'size': 1}
|
||||
self.driver.create_cloned_volume(volume, src_vref)
|
||||
@ -2254,31 +2254,48 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'create_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_create_consistencygroup(self,
|
||||
mock_create_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group(self,
|
||||
mock_is_cg,
|
||||
mock_create_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
||||
self.driver.create_consistencygroup(context, group)
|
||||
mock_create_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
group = {'id': fake.GROUP_ID}
|
||||
model_update = self.driver.create_group(context, group)
|
||||
mock_create_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
self.assertEqual({'status': 'available'}, model_update)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_create_group_not_a_cg(self,
|
||||
mock_is_cg,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
self.assertRaises(NotImplementedError, self.driver.create_group,
|
||||
context, group)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'create_replay_profile',
|
||||
return_value=None)
|
||||
def test_create_consistencygroup_fail(self,
|
||||
mock_create_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_fail(self,
|
||||
mock_is_cg,
|
||||
mock_create_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.create_consistencygroup, context, group)
|
||||
mock_create_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
group = {'id': fake.GROUP_ID}
|
||||
model_update = self.driver.create_group(context, group)
|
||||
mock_create_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
self.assertEqual({'status': 'error'}, model_update)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'delete_replay_profile')
|
||||
@ -2287,28 +2304,43 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
return_value=SCRPLAYPROFILE)
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'delete_volume')
|
||||
def test_delete_consistencygroup(self,
|
||||
mock_delete_volume,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_delete_group(self,
|
||||
mock_is_cg,
|
||||
mock_delete_volume,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
volume = {'id': fake.VOLUME_ID}
|
||||
expected_volumes = [{'id': fake.VOLUME_ID,
|
||||
'status': 'deleted'}]
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID,
|
||||
group = {'id': fake.GROUP_ID,
|
||||
'status': fields.ConsistencyGroupStatus.DELETED}
|
||||
model_update, volumes = self.driver.delete_consistencygroup(
|
||||
model_update, volumes = self.driver.delete_group(
|
||||
context, group, [volume])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_delete_replay_profile.assert_called_once_with(self.SCRPLAYPROFILE)
|
||||
mock_delete_volume.assert_called_once_with(volume)
|
||||
self.assertEqual(group['status'], model_update['status'])
|
||||
self.assertEqual(expected_volumes, volumes)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_delete_group_not_a_cg(
|
||||
self, mock_is_cg, mock_close_connection,
|
||||
mock_open_connection, mock_init):
|
||||
|
||||
volume = {'id': fake.VOLUME_ID}
|
||||
context = {}
|
||||
group = {'id': fake.GROUP_ID,
|
||||
'status': fields.ConsistencyGroupStatus.DELETED}
|
||||
self.assertRaises(NotImplementedError, self.driver.delete_group,
|
||||
context, group, [volume])
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'delete_replay_profile')
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
@ -2316,21 +2348,21 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
return_value=None)
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'delete_volume')
|
||||
def test_delete_consistencygroup_not_found(self,
|
||||
mock_delete_volume,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_delete_group_not_found(self,
|
||||
mock_is_cg,
|
||||
mock_delete_volume,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID,
|
||||
group = {'id': fake.GROUP_ID,
|
||||
'status': fields.ConsistencyGroupStatus.DELETED}
|
||||
model_update, volumes = self.driver.delete_consistencygroup(context,
|
||||
group,
|
||||
[])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
model_update, volumes = self.driver.delete_group(context, group, [])
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
self.assertFalse(mock_delete_replay_profile.called)
|
||||
self.assertFalse(mock_delete_volume.called)
|
||||
self.assertEqual(group['status'], model_update['status'])
|
||||
@ -2342,49 +2374,65 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_update_consistencygroup(self,
|
||||
mock_find_replay_profile,
|
||||
mock_update_cg_volumes,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_update_group(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_update_cg_volumes,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||
rt1, rt2, rt3 = self.driver.update_consistencygroup(context,
|
||||
group,
|
||||
add_volumes,
|
||||
remove_volumes)
|
||||
rt1, rt2, rt3 = self.driver.update_group(context, group, add_volumes,
|
||||
remove_volumes)
|
||||
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
add_volumes,
|
||||
remove_volumes)
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
self.assertIsNone(rt1)
|
||||
self.assertIsNone(rt2)
|
||||
self.assertIsNone(rt3)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_update_group_not_a_cg(self,
|
||||
mock_is_cg,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||
self.assertRaises(NotImplementedError, self.driver.update_group,
|
||||
context, group, add_volumes, remove_volumes)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=None)
|
||||
def test_update_consistencygroup_not_found(self,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_update_group_not_found(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.update_consistencygroup,
|
||||
self.driver.update_group,
|
||||
context,
|
||||
group,
|
||||
add_volumes,
|
||||
remove_volumes)
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'update_cg_volumes',
|
||||
@ -2392,53 +2440,139 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_update_consistencygroup_error(self,
|
||||
mock_find_replay_profile,
|
||||
mock_update_cg_volumes,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_update_group_error(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_update_cg_volumes,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.update_consistencygroup,
|
||||
self.driver.update_group,
|
||||
context,
|
||||
group,
|
||||
add_volumes,
|
||||
remove_volumes)
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
add_volumes,
|
||||
remove_volumes)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'update_group')
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'create_group')
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'create_cloned_volume')
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_from_src(
|
||||
self, mock_is_cg, mock_create_cloned_volume, mock_create_group,
|
||||
mock_update_group, mock_close_connection, mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP2_ID}
|
||||
volumes = [{'id': fake.VOLUME3_ID}, {'id': fake.VOLUME4_ID}]
|
||||
source_group = {'id': fake.GROUP_ID}
|
||||
source_volumes = [{'id': fake.VOLUME_ID}, {'id': fake.VOLUME2_ID}]
|
||||
mock_create_cloned_volume.side_effect = [{'id': fake.VOLUME3_ID},
|
||||
{'id': fake.VOLUME4_ID}]
|
||||
mock_create_group.return_value = {'status': 'available'}
|
||||
model_update, volumes_model_update = self.driver.create_group_from_src(
|
||||
context, group, volumes, group_snapshot=None, snapshots=None,
|
||||
source_group=source_group, source_vols=source_volumes)
|
||||
expected = [{'id': fake.VOLUME3_ID, 'status': 'available'},
|
||||
{'id': fake.VOLUME4_ID, 'status': 'available'}]
|
||||
self.assertEqual({'status': 'available'}, model_update)
|
||||
self.assertEqual(expected, volumes_model_update)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'update_group')
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'create_group')
|
||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||
'create_volume_from_snapshot')
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_from_src_from_snapshot(
|
||||
self, mock_is_cg, mock_create_volume_from_snapshot,
|
||||
mock_create_group, mock_update_group, mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP_ID}
|
||||
volumes = [{'id': fake.VOLUME_ID}, {'id': fake.VOLUME2_ID}]
|
||||
group_snapshot = {'id': fake.GROUP_SNAPSHOT_ID}
|
||||
source_snapshots = [{'id': fake.SNAPSHOT_ID},
|
||||
{'id': fake.SNAPSHOT2_ID}]
|
||||
mock_create_volume_from_snapshot.side_effect = [
|
||||
{'id': fake.VOLUME_ID}, {'id': fake.VOLUME2_ID}]
|
||||
mock_create_group.return_value = {'status': 'available'}
|
||||
model_update, volumes_model_update = self.driver.create_group_from_src(
|
||||
context, group, volumes,
|
||||
group_snapshot=group_snapshot, snapshots=source_snapshots,
|
||||
source_group=None, source_vols=None)
|
||||
expected = [{'id': fake.VOLUME_ID, 'status': 'available'},
|
||||
{'id': fake.VOLUME2_ID, 'status': 'available'}]
|
||||
self.assertEqual({'status': 'available'}, model_update)
|
||||
self.assertEqual(expected, volumes_model_update)
|
||||
|
||||
def test_create_group_from_src_bad_input(
|
||||
self, mock_close_connection, mock_open_connection, mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP2_ID}
|
||||
volumes = [{'id': fake.VOLUME3_ID}, {'id': fake.VOLUME4_ID}]
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.driver.create_group_from_src,
|
||||
context, group, volumes, None, None, None, None)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_create_group_from_src_not_a_cg(
|
||||
self, mock_is_cg, mock_close_connection,
|
||||
mock_open_connection, mock_init):
|
||||
context = {}
|
||||
group = {'id': fake.GROUP2_ID}
|
||||
volumes = [{'id': fake.VOLUME3_ID}, {'id': fake.VOLUME4_ID}]
|
||||
source_group = {'id': fake.GROUP_ID}
|
||||
source_volumes = [{'id': fake.VOLUME_ID}, {'id': fake.VOLUME2_ID}]
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.create_group_from_src,
|
||||
context, group, volumes, None, None,
|
||||
source_group, source_volumes)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'snap_cg_replay',
|
||||
return_value={'instanceId': '100'})
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_create_cgsnapshot(self,
|
||||
mock_find_replay_profile,
|
||||
mock_snap_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_snapshot(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_snap_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
mock_snapshot = mock.MagicMock()
|
||||
mock_snapshot.id = fake.SNAPSHOT_ID
|
||||
expected_snapshots = [{'id': fake.SNAPSHOT_ID, 'status': 'available'}]
|
||||
|
||||
context = {}
|
||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID}
|
||||
model_update, snapshots = self.driver.create_cgsnapshot(
|
||||
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||
model_update, snapshots = self.driver.create_group_snapshot(
|
||||
context, cggrp, [mock_snapshot])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_snap_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
fake.CGSNAPSHOT_ID,
|
||||
fake.GROUP_SNAPSHOT_ID,
|
||||
0)
|
||||
self.assertEqual('available', model_update['status'])
|
||||
self.assertEqual(expected_snapshots, snapshots)
|
||||
@ -2446,19 +2580,33 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=None)
|
||||
def test_create_cgsnapshot_profile_not_found(self,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_snapshot_profile_not_found(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID}
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.create_cgsnapshot,
|
||||
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||
model_update, snapshot_updates = self.driver.create_group_snapshot(
|
||||
context, cggrp, [])
|
||||
self.assertEqual({'status': 'error'}, model_update)
|
||||
self.assertIsNone(snapshot_updates)
|
||||
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_create_group_snapshot_not_a_cg(
|
||||
self, mock_is_cg, mock_close_connection,
|
||||
mock_open_connection, mock_init):
|
||||
context = {}
|
||||
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.create_group_snapshot,
|
||||
context, cggrp, [])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'snap_cg_replay',
|
||||
@ -2466,22 +2614,24 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_create_cgsnapshot_fail(self,
|
||||
mock_find_replay_profile,
|
||||
mock_snap_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_create_group_snapshot_fail(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_snap_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
context = {}
|
||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID}
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.create_cgsnapshot,
|
||||
context, cggrp, [])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||
model_update, snapshot_updates = self.driver.create_group_snapshot(
|
||||
context, cggrp, [])
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_snap_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
fake.CGSNAPSHOT_ID, 0)
|
||||
fake.GROUP_SNAPSHOT_ID, 0)
|
||||
self.assertEqual({'status': 'error'}, model_update)
|
||||
self.assertIsNone(snapshot_updates)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'delete_cg_replay',
|
||||
@ -2489,22 +2639,24 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_delete_cgsnapshot(self,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_delete_group_snapshot(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
mock_snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
||||
context = {}
|
||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID, 'status': 'deleted'}
|
||||
model_update, snapshots = self.driver.delete_cgsnapshot(
|
||||
context, cgsnap, [snapshot])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
cgsnap = {'group_id': fake.GROUP_ID,
|
||||
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'deleted'}
|
||||
model_update, snapshots = self.driver.delete_group_snapshot(
|
||||
context, cgsnap, [mock_snapshot])
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_delete_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
fake.CGSNAPSHOT_ID)
|
||||
fake.GROUP_SNAPSHOT_ID)
|
||||
self.assertEqual({'status': cgsnap['status']}, model_update)
|
||||
self.assertEqual([{'id': fake.SNAPSHOT_ID, 'status': 'deleted'}],
|
||||
snapshots)
|
||||
@ -2514,24 +2666,25 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=None)
|
||||
def test_delete_cgsnapshot_profile_not_found(self,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_delete_group_snapshot_profile_not_found(self,
|
||||
mock_is_cg,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
||||
context = {}
|
||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID, 'status': 'deleted'}
|
||||
model_update, snapshots = self.driver.delete_cgsnapshot(
|
||||
cgsnap = {'group_id': fake.GROUP_ID,
|
||||
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'available'}
|
||||
model_update, snapshots = self.driver.delete_group_snapshot(
|
||||
context, cgsnap, [snapshot])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
self.assertFalse(mock_delete_cg_replay.called)
|
||||
self.assertEqual({'status': cgsnap['status']}, model_update)
|
||||
self.assertEqual([{'id': fake.SNAPSHOT_ID, 'status': 'deleted'}],
|
||||
snapshots)
|
||||
self.assertEqual({'status': 'error'}, model_update)
|
||||
self.assertIsNone(snapshots)
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'delete_cg_replay',
|
||||
@ -2539,24 +2692,32 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_replay_profile',
|
||||
return_value=SCRPLAYPROFILE)
|
||||
def test_delete_cgsnapshot_profile_failed_delete(self,
|
||||
mock_find_replay_profile,
|
||||
mock_delete_cg_replay,
|
||||
mock_close_connection,
|
||||
mock_open_connection,
|
||||
mock_init):
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
def test_delete_group_snapshot_profile_failed_delete(
|
||||
self, mock_is_cg, mock_find_replay_profile, mock_delete_cg_replay,
|
||||
mock_close_connection, mock_open_connection, mock_init):
|
||||
context = {}
|
||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
||||
'id': fake.CGSNAPSHOT_ID, 'status': 'available'}
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.delete_cgsnapshot,
|
||||
context,
|
||||
cgsnap,
|
||||
[])
|
||||
mock_find_replay_profile.assert_called_once_with(
|
||||
fake.CONSISTENCY_GROUP_ID)
|
||||
cgsnap = {'group_id': fake.GROUP_ID,
|
||||
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'available'}
|
||||
model_update, snapshot_updates = self.driver.delete_group_snapshot(
|
||||
context, cgsnap, [])
|
||||
self.assertEqual({'status': 'error_deleting'}, model_update)
|
||||
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||
mock_delete_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||
fake.CGSNAPSHOT_ID)
|
||||
fake.GROUP_SNAPSHOT_ID)
|
||||
|
||||
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||
return_value=False)
|
||||
def test_delete_group_snapshot_not_a_cg(
|
||||
self, mock_is_cg, mock_close_connection,
|
||||
mock_open_connection, mock_init):
|
||||
context = {}
|
||||
cgsnap = {'group_id': fake.GROUP_ID,
|
||||
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'available'}
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.driver.delete_group_snapshot,
|
||||
context, cgsnap, [])
|
||||
|
||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||
'find_volume',
|
||||
|
@ -25,6 +25,7 @@ from cinder.objects import fields
|
||||
from cinder.volume import driver
|
||||
from cinder.volume.drivers.dell import dell_storagecenter_api
|
||||
from cinder.volume.drivers.san.san import san_opts
|
||||
from cinder.volume import utils as volume_utils
|
||||
from cinder.volume import volume_types
|
||||
|
||||
|
||||
@ -155,17 +156,20 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
|
||||
return {}
|
||||
|
||||
def _add_volume_to_consistency_group(self, api, scvolume, volume):
|
||||
"""Just a helper to add a volume to a consistency group.
|
||||
def _add_volume_to_group(self, api, scvolume, volume):
|
||||
"""Just a helper to add a volume to a group.
|
||||
|
||||
:param api: Dell SC API opbject.
|
||||
:param scvolume: Dell SC Volume object.
|
||||
:param volume: Cinder Volume object.
|
||||
:returns: Nothing.
|
||||
"""
|
||||
if scvolume and volume.get('consistencygroup_id'):
|
||||
if scvolume and volume.get('group_id'):
|
||||
profile = api.find_replay_profile(
|
||||
volume.get('consistencygroup_id'))
|
||||
volume.get('group_id'))
|
||||
# If there is a profile then we need to add our
|
||||
# volumes to it. If there isn't then it was a normal
|
||||
# group.
|
||||
if profile:
|
||||
api.update_cg_volumes(profile, [volume])
|
||||
|
||||
@ -310,8 +314,8 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
message=_('Unable to create volume %s') %
|
||||
volume_name)
|
||||
|
||||
# Update Consistency Group
|
||||
self._add_volume_to_consistency_group(api, scvolume, volume)
|
||||
# Update Group
|
||||
self._add_volume_to_group(api, scvolume, volume)
|
||||
|
||||
# Create replications. (Or not. It checks.)
|
||||
model_update = self._create_replications(api, volume, scvolume)
|
||||
@ -480,11 +484,12 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
src_volume_name = snapshot.get('volume_id')
|
||||
# This snapshot could have been created on its own or as part of a
|
||||
# cgsnapshot. If it was a cgsnapshot it will be identified on the Dell
|
||||
# backend under cgsnapshot_id. Given the volume ID and the
|
||||
# cgsnapshot_id we can find the appropriate snapshot.
|
||||
# So first we look for cgsnapshot_id. If that is blank then it must
|
||||
# have been a normal snapshot which will be found under snapshot_id.
|
||||
snapshot_id = snapshot.get('cgsnapshot_id')
|
||||
# backend under group_snapshot_id. Given the volume ID and the
|
||||
# group_snapshot_id we can find the appropriate snapshot.
|
||||
# So first we look for group_snapshot_id. If that is blank then it
|
||||
# must have been a normal snapshot which will be found under
|
||||
# snapshot_id.
|
||||
snapshot_id = snapshot.get('group_snapshot_id')
|
||||
if not snapshot_id:
|
||||
snapshot_id = snapshot.get('id')
|
||||
LOG.debug(
|
||||
@ -522,10 +527,8 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
{'name': volume_name,
|
||||
'snap': snapshot_id})
|
||||
|
||||
# Update Consistency Group
|
||||
self._add_volume_to_consistency_group(api,
|
||||
scvolume,
|
||||
volume)
|
||||
# Update Group
|
||||
self._add_volume_to_group(api, scvolume, volume)
|
||||
# Replicate if we are supposed to.
|
||||
model_update = self._create_replications(api,
|
||||
volume,
|
||||
@ -587,10 +590,8 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
{'name': volume_name,
|
||||
'vol': src_volume_name})
|
||||
|
||||
# Update Consistency Group
|
||||
self._add_volume_to_consistency_group(api,
|
||||
scvolume,
|
||||
volume)
|
||||
# Update Group
|
||||
self._add_volume_to_group(api, scvolume, volume)
|
||||
# Replicate if we are supposed to.
|
||||
model_update = self._create_replications(api,
|
||||
volume,
|
||||
@ -708,6 +709,7 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
data['storage_protocol'] = self.storage_protocol
|
||||
data['reserved_percentage'] = 0
|
||||
data['consistencygroup_support'] = True
|
||||
data['consistent_group_snapshot_enabled'] = True
|
||||
data['thin_provisioning_support'] = True
|
||||
data['QoS_support'] = False
|
||||
data['replication_enabled'] = self.replication_enabled
|
||||
@ -783,57 +785,115 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
|
||||
return {'_name_id': new_volume['_name_id'] or new_volume['id']}
|
||||
|
||||
def create_consistencygroup(self, context, group):
|
||||
"""This creates a replay profile on the storage backend.
|
||||
def create_group(self, context, group):
|
||||
"""Creates a group.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param group: the dictionary of the consistency group to be created.
|
||||
:returns: Nothing on success.
|
||||
:raises VolumeBackendAPIException:
|
||||
:param group: the Group object of the group to be created.
|
||||
:returns: model_update
|
||||
|
||||
model_update will be in this format: {'status': xxx, ......}.
|
||||
|
||||
If the status in model_update is 'error', the manager will throw
|
||||
an exception and it will be caught in the try-except block in the
|
||||
manager. If the driver throws an exception, the manager will also
|
||||
catch it in the try-except block. The group status in the db will
|
||||
be changed to 'error'.
|
||||
|
||||
For a successful operation, the driver can either build the
|
||||
model_update and return it or return None. The group status will
|
||||
be set to 'available'.
|
||||
"""
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupStatus.ERROR}
|
||||
gid = group['id']
|
||||
with self._client.open_connection() as api:
|
||||
cgroup = api.create_replay_profile(gid)
|
||||
if cgroup:
|
||||
LOG.info('Created Consistency Group %s', gid)
|
||||
return
|
||||
msg = _('Unable to create consistency group %s') % gid
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
LOG.info('Created group %s', gid)
|
||||
model_update['status'] = fields.GroupStatus.AVAILABLE
|
||||
return model_update
|
||||
|
||||
def delete_consistencygroup(self, context, group, volumes):
|
||||
"""Delete the Dell SC profile associated with this consistency group.
|
||||
def delete_group(self, context, group, volumes):
|
||||
"""Deletes a group.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param group: the dictionary of the consistency group to be created.
|
||||
:returns: Updated model_update, volumes.
|
||||
:param group: the Group object of the group to be deleted.
|
||||
:param volumes: a list of Volume objects in the group.
|
||||
:returns: model_update, volumes_model_update
|
||||
|
||||
param volumes is a list of objects retrieved from the db. It cannot
|
||||
be assigned to volumes_model_update. volumes_model_update is a list
|
||||
of dictionaries. It has to be built by the driver. An entry will be
|
||||
in this format: {'id': xxx, 'status': xxx, ......}. model_update
|
||||
will be in this format: {'status': xxx, ......}.
|
||||
|
||||
The driver should populate volumes_model_update and model_update
|
||||
and return them.
|
||||
|
||||
The manager will check volumes_model_update and update db accordingly
|
||||
for each volume. If the driver successfully deleted some volumes
|
||||
but failed to delete others, it should set statuses of the volumes
|
||||
accordingly so that the manager can update db correctly.
|
||||
|
||||
If the status in any entry of volumes_model_update is 'error_deleting'
|
||||
or 'error', the status in model_update will be set to the same if it
|
||||
is not already 'error_deleting' or 'error'.
|
||||
|
||||
If the status in model_update is 'error_deleting' or 'error', the
|
||||
manager will raise an exception and the status of the group will be
|
||||
set to 'error' in the db. If volumes_model_update is not returned by
|
||||
the driver, the manager will set the status of every volume in the
|
||||
group to 'error' in the except block.
|
||||
|
||||
If the driver raises an exception during the operation, it will be
|
||||
caught by the try-except block in the manager. The statuses of the
|
||||
group and all volumes in it will be set to 'error'.
|
||||
|
||||
For a successful operation, the driver can either build the
|
||||
model_update and volumes_model_update and return them or
|
||||
return None, None. The statuses of the group and all volumes
|
||||
will be set to 'deleted' after the manager deletes them from db.
|
||||
"""
|
||||
gid = group['id']
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupStatus.DELETED}
|
||||
with self._client.open_connection() as api:
|
||||
gid = group['id']
|
||||
profile = api.find_replay_profile(gid)
|
||||
if profile:
|
||||
api.delete_replay_profile(profile)
|
||||
# If we are here because we found no profile that should be fine
|
||||
# as we are trying to delete it anyway.
|
||||
try:
|
||||
api.delete_replay_profile(profile)
|
||||
except exception.VolumeBackendAPIException:
|
||||
LOG.error('delete_group: error deleting %s', gid)
|
||||
model_update['status'] = fields.GroupStatus.ERROR
|
||||
|
||||
# Trundle through the list deleting the volumes.
|
||||
volume_updates = []
|
||||
volumes_model_update = []
|
||||
for volume in volumes:
|
||||
self.delete_volume(volume)
|
||||
volume_updates.append({'id': volume['id'],
|
||||
'status': 'deleted'})
|
||||
status = fields.GroupStatus.ERROR
|
||||
try:
|
||||
if self.delete_volume(volume):
|
||||
status = fields.GroupStatus.DELETED
|
||||
except (exception.VolumeBackendAPIException,
|
||||
exception.VolumeIsBusy):
|
||||
LOG.error('delete_group: error deleting volume %s',
|
||||
volume['id'])
|
||||
volumes_model_update.append({'id': volume['id'], 'status': status})
|
||||
|
||||
model_update = {'status': group['status']}
|
||||
return model_update, volumes_model_update
|
||||
|
||||
return model_update, volume_updates
|
||||
|
||||
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.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param group: the dictionary of the consistency group to be updated.
|
||||
:param add_volumes: a list of volume dictionaries to be added.
|
||||
:param remove_volumes: a list of volume dictionaries to be removed.
|
||||
:param group: the Group object of the group to be updated.
|
||||
:param add_volumes: a list of Volume objects to be added.
|
||||
:param remove_volumes: a list of Volume objects to be removed.
|
||||
:returns: model_update, add_volumes_update, remove_volumes_update
|
||||
|
||||
model_update is a dictionary that the driver wants the manager
|
||||
@ -846,96 +906,226 @@ class DellCommonDriver(driver.ManageableVD,
|
||||
volume entry can be updated. If None is returned, the volume will
|
||||
remain its original status. Also note that you cannot directly
|
||||
assign add_volumes to add_volumes_update as add_volumes is a list of
|
||||
cinder.db.sqlalchemy.models.Volume objects and cannot be used for
|
||||
db update directly. Same with remove_volumes.
|
||||
volume objects and cannot be used for db update directly. Same with
|
||||
remove_volumes.
|
||||
|
||||
If the driver throws an exception, the status of the group as well as
|
||||
those of the volumes to be added/removed will be set to 'error'.
|
||||
"""
|
||||
gid = group['id']
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
with self._client.open_connection() as api:
|
||||
gid = group['id']
|
||||
profile = api.find_replay_profile(gid)
|
||||
if not profile:
|
||||
LOG.error('Cannot find Consistency Group %s', gid)
|
||||
LOG.error('update_group: Cannot find volume Group %s', gid)
|
||||
elif api.update_cg_volumes(profile,
|
||||
add_volumes,
|
||||
remove_volumes):
|
||||
LOG.info('Updated Consistency Group %s', gid)
|
||||
LOG.info('update_group: Updated volume group %s', gid)
|
||||
# we need nothing updated above us so just return None.
|
||||
return None, None, None
|
||||
# Things did not go well so throw.
|
||||
msg = _('Unable to update consistency group %s') % gid
|
||||
msg = _('Unable to update group %s') % gid
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
|
||||
"""Takes a snapshot of the consistency group.
|
||||
def create_group_from_src(self, context, group, volumes,
|
||||
group_snapshot=None, snapshots=None,
|
||||
source_group=None, source_vols=None):
|
||||
"""Creates a group from source.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param cgsnapshot: Information about the snapshot to take.
|
||||
:param snapshots: List of snapshots for this cgsnapshot.
|
||||
:returns: Updated model_update, snapshots.
|
||||
:raises VolumeBackendAPIException:
|
||||
"""
|
||||
cgid = cgsnapshot['consistencygroup_id']
|
||||
snapshotid = cgsnapshot['id']
|
||||
:param group: the Group object to be created.
|
||||
:param volumes: a list of Volume objects in the group.
|
||||
:param group_snapshot: the GroupSnapshot object as source.
|
||||
:param snapshots: a list of Snapshot objects in group_snapshot.
|
||||
:param source_group: the Group object as source.
|
||||
:param source_vols: a list of Volume objects in the source_group.
|
||||
:returns: model_update, volumes_model_update
|
||||
|
||||
The source can be group_snapshot or a source_group.
|
||||
|
||||
param volumes is a list of objects retrieved from the db. It cannot
|
||||
be assigned to volumes_model_update. volumes_model_update is a list
|
||||
of dictionaries. It has to be built by the driver. An entry will be
|
||||
in this format: {'id': xxx, 'status': xxx, ......}. model_update
|
||||
will be in this format: {'status': xxx, ......}.
|
||||
|
||||
To be consistent with other volume operations, the manager will
|
||||
assume the operation is successful if no exception is thrown by
|
||||
the driver. For a successful operation, the driver can either build
|
||||
the model_update and volumes_model_update and return them or
|
||||
return None, None.
|
||||
"""
|
||||
if not (group_snapshot and snapshots and not source_group or
|
||||
source_group and source_vols and not group_snapshot):
|
||||
msg = _("create_group_from_src only supports a "
|
||||
"group_snapshot source or a group source. "
|
||||
"Multiple sources cannot be used.")
|
||||
raise exception.InvalidInput(msg)
|
||||
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
# Mark us as working. If we are a CG then that will be settled below.
|
||||
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||
volumes_model_update = []
|
||||
if source_group:
|
||||
for volume, src_vol in zip(volumes, source_vols):
|
||||
update = self.create_cloned_volume(volume, src_vol)
|
||||
update['status'] = fields.GroupStatus.AVAILABLE
|
||||
volumes_model_update.append(update.copy())
|
||||
else:
|
||||
for volume, src_snap in zip(volumes, snapshots):
|
||||
update = self.create_volume_from_snapshot(volume, src_snap)
|
||||
update['status'] = fields.GroupStatus.AVAILABLE
|
||||
volumes_model_update.append(update.copy())
|
||||
|
||||
# So, in theory, everything has been created. Now is the time to
|
||||
# add the volumes to the group.
|
||||
model_update = self.create_group(context, group)
|
||||
if model_update['status'] == fields.GroupStatus.AVAILABLE:
|
||||
self.update_group(context, group, volumes, None)
|
||||
|
||||
return model_update, volumes_model_update
|
||||
|
||||
def create_group_snapshot(self, context, group_snapshot, snapshots):
|
||||
"""Creates a group_snapshot.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param group_snapshot: the GroupSnapshot object to be created.
|
||||
:param snapshots: a list of Snapshot objects in the group_snapshot.
|
||||
:returns: model_update, snapshots_model_update
|
||||
|
||||
param snapshots is a list of Snapshot objects. It cannot be assigned
|
||||
to snapshots_model_update. snapshots_model_update is a list of
|
||||
dictionaries. It has to be built by the driver. An entry will be
|
||||
in this format: {'id': xxx, 'status': xxx, ......}. model_update
|
||||
will be in this format: {'status': xxx, ......}.
|
||||
|
||||
The driver should populate snapshots_model_update and model_update
|
||||
and return them.
|
||||
|
||||
The manager will check snapshots_model_update and update db accordingly
|
||||
for each snapshot. If the driver successfully deleted some snapshots
|
||||
but failed to delete others, it should set statuses of the snapshots
|
||||
accordingly so that the manager can update db correctly.
|
||||
|
||||
If the status in any entry of snapshots_model_update is 'error', the
|
||||
status in model_update will be set to the same if it is not already
|
||||
'error'.
|
||||
|
||||
If the status in model_update is 'error', the manager will raise an
|
||||
exception and the status of group_snapshot will be set to 'error' in
|
||||
the db. If snapshots_model_update is not returned by the driver, the
|
||||
manager will set the status of every snapshot to 'error' in the except
|
||||
block.
|
||||
|
||||
If the driver raises an exception during the operation, it will be
|
||||
caught by the try-except block in the manager and the statuses of
|
||||
group_snapshot and all snapshots will be set to 'error'.
|
||||
|
||||
For a successful operation, the driver can either build the
|
||||
model_update and snapshots_model_update and return them or
|
||||
return None, None. The statuses of group_snapshot and all snapshots
|
||||
will be set to 'available' at the end of the manager function.
|
||||
"""
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupSnapshotStatus.ERROR}
|
||||
snapshot_updates = None
|
||||
with self._client.open_connection() as api:
|
||||
profile = api.find_replay_profile(cgid)
|
||||
if profile:
|
||||
LOG.debug('profile %s replayid %s', profile, snapshotid)
|
||||
if api.snap_cg_replay(profile, snapshotid, 0):
|
||||
gid = group_snapshot['group_id']
|
||||
snapshotid = group_snapshot['id']
|
||||
profile = api.find_replay_profile(gid)
|
||||
if not profile:
|
||||
LOG.error('create_group_snapshot: profile %s not found', gid)
|
||||
else:
|
||||
if not api.snap_cg_replay(profile, snapshotid, 0):
|
||||
LOG.error('create_group_snapshot: '
|
||||
'failed to snap %(ss)s on %(pro)s',
|
||||
{'ss': snapshotid, 'pro': profile})
|
||||
else:
|
||||
LOG.info('create_group_snapshot: '
|
||||
'created %(ss)s on %(pro)s',
|
||||
{'ss': snapshotid, 'pro': profile})
|
||||
# Set our returns
|
||||
model_update['status'] = (
|
||||
fields.GroupSnapshotStatus.AVAILABLE)
|
||||
snapshot_updates = []
|
||||
for snapshot in snapshots:
|
||||
snapshot_updates.append({
|
||||
'id': snapshot.id,
|
||||
'status': fields.SnapshotStatus.AVAILABLE
|
||||
})
|
||||
'status': fields.SnapshotStatus.AVAILABLE})
|
||||
|
||||
model_update = {'status': fields.SnapshotStatus.AVAILABLE}
|
||||
return model_update, snapshot_updates
|
||||
|
||||
return model_update, snapshot_updates
|
||||
|
||||
# That didn't go well. Tell them why. Then bomb out.
|
||||
LOG.error('Failed to snap Consistency Group %s', cgid)
|
||||
else:
|
||||
LOG.error('Cannot find Consistency Group %s', cgid)
|
||||
|
||||
msg = _('Unable to snap Consistency Group %s') % cgid
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
|
||||
"""Deletes a cgsnapshot.
|
||||
|
||||
If profile isn't found return success. If failed to delete the
|
||||
replay (the snapshot) then raise an exception.
|
||||
def delete_group_snapshot(self, context, group_snapshot, snapshots):
|
||||
"""Deletes a group_snapshot.
|
||||
|
||||
:param context: the context of the caller.
|
||||
:param cgsnapshot: Information about the snapshot to delete.
|
||||
:returns: Updated model_update, snapshots.
|
||||
:raises VolumeBackendAPIException.:
|
||||
:param group_snapshot: the GroupSnapshot object to be deleted.
|
||||
:param snapshots: a list of Snapshot objects in the group_snapshot.
|
||||
:returns: model_update, snapshots_model_update
|
||||
|
||||
param snapshots is a list of objects. It cannot be assigned to
|
||||
snapshots_model_update. snapshots_model_update is a list of of
|
||||
dictionaries. It has to be built by the driver. An entry will be
|
||||
in this format: {'id': xxx, 'status': xxx, ......}. model_update
|
||||
will be in this format: {'status': xxx, ......}.
|
||||
|
||||
The driver should populate snapshots_model_update and model_update
|
||||
and return them.
|
||||
|
||||
The manager will check snapshots_model_update and update db accordingly
|
||||
for each snapshot. If the driver successfully deleted some snapshots
|
||||
but failed to delete others, it should set statuses of the snapshots
|
||||
accordingly so that the manager can update db correctly.
|
||||
|
||||
If the status in any entry of snapshots_model_update is
|
||||
'error_deleting' or 'error', the status in model_update will be set to
|
||||
the same if it is not already 'error_deleting' or 'error'.
|
||||
|
||||
If the status in model_update is 'error_deleting' or 'error', the
|
||||
manager will raise an exception and the status of group_snapshot will
|
||||
be set to 'error' in the db. If snapshots_model_update is not returned
|
||||
by the driver, the manager will set the status of every snapshot to
|
||||
'error' in the except block.
|
||||
|
||||
If the driver raises an exception during the operation, it will be
|
||||
caught by the try-except block in the manager and the statuses of
|
||||
group_snapshot and all snapshots will be set to 'error'.
|
||||
|
||||
For a successful operation, the driver can either build the
|
||||
model_update and snapshots_model_update and return them or
|
||||
return None, None. The statuses of group_snapshot and all snapshots
|
||||
will be set to 'deleted' after the manager deletes them from db.
|
||||
"""
|
||||
cgid = cgsnapshot['consistencygroup_id']
|
||||
snapshotid = cgsnapshot['id']
|
||||
# Setup a generic error return.
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupSnapshotStatus.ERROR}
|
||||
snapshot_updates = None
|
||||
with self._client.open_connection() as api:
|
||||
profile = api.find_replay_profile(cgid)
|
||||
snapshotid = group_snapshot['id']
|
||||
profile = api.find_replay_profile(group_snapshot['group_id'])
|
||||
if profile:
|
||||
LOG.info('Deleting snapshot %(ss)s from %(pro)s',
|
||||
{'ss': snapshotid,
|
||||
'pro': profile})
|
||||
LOG.info('delete_group_snapshot: %(ss)s from %(pro)s',
|
||||
{'ss': snapshotid, 'pro': profile})
|
||||
if not api.delete_cg_replay(profile, snapshotid):
|
||||
msg = (_('Unable to delete Consistency Group snapshot %s')
|
||||
% snapshotid)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
snapshot_updates = []
|
||||
for snapshot in snapshots:
|
||||
snapshot_updates.append(
|
||||
{'id': snapshot['id'],
|
||||
'status': fields.SnapshotStatus.DELETED})
|
||||
|
||||
model_update = {'status': fields.SnapshotStatus.DELETED}
|
||||
|
||||
return model_update, snapshot_updates
|
||||
model_update['status'] = (
|
||||
fields.GroupSnapshotStatus.ERROR_DELETING)
|
||||
else:
|
||||
model_update['status'] = fields.GroupSnapshotStatus.DELETED
|
||||
snapshot_updates = []
|
||||
for snapshot in snapshots:
|
||||
snapshot_updates.append(
|
||||
{'id': snapshot['id'],
|
||||
'status': fields.SnapshotStatus.DELETED})
|
||||
return model_update, snapshot_updates
|
||||
|
||||
def manage_existing(self, volume, existing_ref):
|
||||
"""Brings an existing backend storage object under Cinder management.
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add consistency group capability to Generic Volume Groups in the
|
||||
Dell EMC SC driver.
|
Loading…
Reference in New Issue
Block a user