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,7 +579,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'create_volume',
|
'create_volume',
|
||||||
return_value=VOLUME)
|
return_value=VOLUME)
|
||||||
def test_create_volume_consistency_group(self,
|
def test_create_volume_with_group(self,
|
||||||
mock_create_volume,
|
mock_create_volume,
|
||||||
mock_update_cg_volumes,
|
mock_update_cg_volumes,
|
||||||
mock_find_replay_profile,
|
mock_find_replay_profile,
|
||||||
@ -587,7 +587,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
volume = {'id': fake.VOLUME_ID, 'size': 1,
|
volume = {'id': fake.VOLUME_ID, 'size': 1,
|
||||||
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID}
|
'group_id': fake.GROUP_ID}
|
||||||
self.driver.create_volume(volume)
|
self.driver.create_volume(volume)
|
||||||
mock_create_volume.assert_called_once_with(
|
mock_create_volume.assert_called_once_with(
|
||||||
fake.VOLUME_ID, 1, None, None, None, None, None)
|
fake.VOLUME_ID, 1, None, None, None, None, None)
|
||||||
@ -1681,7 +1681,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
model_update = {'something': 'something'}
|
model_update = {'something': 'something'}
|
||||||
mock_create_replications.return_value = model_update
|
mock_create_replications.return_value = model_update
|
||||||
volume = {'id': fake.VOLUME_ID,
|
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,
|
snapshot = {'id': fake.SNAPSHOT_ID, 'volume_id': fake.VOLUME_ID,
|
||||||
'volume_size': 1}
|
'volume_size': 1}
|
||||||
res = self.driver.create_volume_from_snapshot(volume, snapshot)
|
res = self.driver.create_volume_from_snapshot(volume, snapshot)
|
||||||
@ -1975,7 +1975,7 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
volume = {'id': fake.VOLUME_ID,
|
volume = {'id': fake.VOLUME_ID,
|
||||||
'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
'group_id': fake.CONSISTENCY_GROUP_ID,
|
||||||
'size': 1}
|
'size': 1}
|
||||||
src_vref = {'id': fake.VOLUME2_ID, 'size': 1}
|
src_vref = {'id': fake.VOLUME2_ID, 'size': 1}
|
||||||
self.driver.create_cloned_volume(volume, src_vref)
|
self.driver.create_cloned_volume(volume, src_vref)
|
||||||
@ -2254,31 +2254,48 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'create_replay_profile',
|
'create_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_create_consistencygroup(self,
|
@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_create_replay_profile,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
group = {'id': fake.GROUP_ID}
|
||||||
self.driver.create_consistencygroup(context, group)
|
model_update = self.driver.create_group(context, group)
|
||||||
mock_create_replay_profile.assert_called_once_with(
|
mock_create_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'create_replay_profile',
|
'create_replay_profile',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
def test_create_consistencygroup_fail(self,
|
@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_create_replay_profile,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
group = {'id': fake.GROUP_ID}
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
model_update = self.driver.create_group(context, group)
|
||||||
self.driver.create_consistencygroup, context, group)
|
mock_create_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
mock_create_replay_profile.assert_called_once_with(
|
self.assertEqual({'status': 'error'}, model_update)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
|
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'delete_replay_profile')
|
'delete_replay_profile')
|
||||||
@ -2287,7 +2304,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||||
'delete_volume')
|
'delete_volume')
|
||||||
def test_delete_consistencygroup(self,
|
@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_delete_volume,
|
||||||
mock_find_replay_profile,
|
mock_find_replay_profile,
|
||||||
mock_delete_replay_profile,
|
mock_delete_replay_profile,
|
||||||
@ -2298,17 +2318,29 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
expected_volumes = [{'id': fake.VOLUME_ID,
|
expected_volumes = [{'id': fake.VOLUME_ID,
|
||||||
'status': 'deleted'}]
|
'status': 'deleted'}]
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID,
|
group = {'id': fake.GROUP_ID,
|
||||||
'status': fields.ConsistencyGroupStatus.DELETED}
|
'status': fields.ConsistencyGroupStatus.DELETED}
|
||||||
model_update, volumes = self.driver.delete_consistencygroup(
|
model_update, volumes = self.driver.delete_group(
|
||||||
context, group, [volume])
|
context, group, [volume])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_delete_replay_profile.assert_called_once_with(self.SCRPLAYPROFILE)
|
mock_delete_replay_profile.assert_called_once_with(self.SCRPLAYPROFILE)
|
||||||
mock_delete_volume.assert_called_once_with(volume)
|
mock_delete_volume.assert_called_once_with(volume)
|
||||||
self.assertEqual(group['status'], model_update['status'])
|
self.assertEqual(group['status'], model_update['status'])
|
||||||
self.assertEqual(expected_volumes, volumes)
|
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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'delete_replay_profile')
|
'delete_replay_profile')
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
@ -2316,7 +2348,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
return_value=None)
|
return_value=None)
|
||||||
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
@mock.patch.object(dell_storagecenter_iscsi.DellStorageCenterISCSIDriver,
|
||||||
'delete_volume')
|
'delete_volume')
|
||||||
def test_delete_consistencygroup_not_found(self,
|
@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_delete_volume,
|
||||||
mock_find_replay_profile,
|
mock_find_replay_profile,
|
||||||
mock_delete_replay_profile,
|
mock_delete_replay_profile,
|
||||||
@ -2324,13 +2359,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID,
|
group = {'id': fake.GROUP_ID,
|
||||||
'status': fields.ConsistencyGroupStatus.DELETED}
|
'status': fields.ConsistencyGroupStatus.DELETED}
|
||||||
model_update, volumes = self.driver.delete_consistencygroup(context,
|
model_update, volumes = self.driver.delete_group(context, group, [])
|
||||||
group,
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
[])
|
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
self.assertFalse(mock_delete_replay_profile.called)
|
self.assertFalse(mock_delete_replay_profile.called)
|
||||||
self.assertFalse(mock_delete_volume.called)
|
self.assertFalse(mock_delete_volume.called)
|
||||||
self.assertEqual(group['status'], model_update['status'])
|
self.assertEqual(group['status'], model_update['status'])
|
||||||
@ -2342,49 +2374,65 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_update_consistencygroup(self,
|
@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_find_replay_profile,
|
||||||
mock_update_cg_volumes,
|
mock_update_cg_volumes,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
group = {'id': fake.GROUP_ID}
|
||||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||||
rt1, rt2, rt3 = self.driver.update_consistencygroup(context,
|
rt1, rt2, rt3 = self.driver.update_group(context, group, add_volumes,
|
||||||
group,
|
|
||||||
add_volumes,
|
|
||||||
remove_volumes)
|
remove_volumes)
|
||||||
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||||
add_volumes,
|
add_volumes,
|
||||||
remove_volumes)
|
remove_volumes)
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
self.assertIsNone(rt1)
|
self.assertIsNone(rt1)
|
||||||
self.assertIsNone(rt2)
|
self.assertIsNone(rt2)
|
||||||
self.assertIsNone(rt3)
|
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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
def test_update_consistencygroup_not_found(self,
|
@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_find_replay_profile,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
group = {'id': fake.GROUP_ID}
|
||||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
self.driver.update_consistencygroup,
|
self.driver.update_group,
|
||||||
context,
|
context,
|
||||||
group,
|
group,
|
||||||
add_volumes,
|
add_volumes,
|
||||||
remove_volumes)
|
remove_volumes)
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
|
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'update_cg_volumes',
|
'update_cg_volumes',
|
||||||
@ -2392,35 +2440,123 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_update_consistencygroup_error(self,
|
@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_find_replay_profile,
|
||||||
mock_update_cg_volumes,
|
mock_update_cg_volumes,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
group = {'id': fake.CONSISTENCY_GROUP_ID}
|
group = {'id': fake.GROUP_ID}
|
||||||
add_volumes = [{'id': fake.VOLUME_ID}]
|
add_volumes = [{'id': fake.VOLUME_ID}]
|
||||||
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
remove_volumes = [{'id': fake.VOLUME2_ID}]
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
self.driver.update_consistencygroup,
|
self.driver.update_group,
|
||||||
context,
|
context,
|
||||||
group,
|
group,
|
||||||
add_volumes,
|
add_volumes,
|
||||||
remove_volumes)
|
remove_volumes)
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
mock_update_cg_volumes.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||||
add_volumes,
|
add_volumes,
|
||||||
remove_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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'snap_cg_replay',
|
'snap_cg_replay',
|
||||||
return_value={'instanceId': '100'})
|
return_value={'instanceId': '100'})
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_create_cgsnapshot(self,
|
@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_find_replay_profile,
|
||||||
mock_snap_cg_replay,
|
mock_snap_cg_replay,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
@ -2431,14 +2567,12 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
expected_snapshots = [{'id': fake.SNAPSHOT_ID, 'status': 'available'}]
|
expected_snapshots = [{'id': fake.SNAPSHOT_ID, 'status': 'available'}]
|
||||||
|
|
||||||
context = {}
|
context = {}
|
||||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||||
'id': fake.CGSNAPSHOT_ID}
|
model_update, snapshots = self.driver.create_group_snapshot(
|
||||||
model_update, snapshots = self.driver.create_cgsnapshot(
|
|
||||||
context, cggrp, [mock_snapshot])
|
context, cggrp, [mock_snapshot])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_snap_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
mock_snap_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
||||||
fake.CGSNAPSHOT_ID,
|
fake.GROUP_SNAPSHOT_ID,
|
||||||
0)
|
0)
|
||||||
self.assertEqual('available', model_update['status'])
|
self.assertEqual('available', model_update['status'])
|
||||||
self.assertEqual(expected_snapshots, snapshots)
|
self.assertEqual(expected_snapshots, snapshots)
|
||||||
@ -2446,19 +2580,33 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
def test_create_cgsnapshot_profile_not_found(self,
|
@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_find_replay_profile,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||||
'id': fake.CGSNAPSHOT_ID}
|
model_update, snapshot_updates = self.driver.create_group_snapshot(
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
context, cggrp, [])
|
||||||
self.driver.create_cgsnapshot,
|
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, [])
|
context, cggrp, [])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
|
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'snap_cg_replay',
|
'snap_cg_replay',
|
||||||
@ -2466,22 +2614,24 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_create_cgsnapshot_fail(self,
|
@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_find_replay_profile,
|
||||||
mock_snap_cg_replay,
|
mock_snap_cg_replay,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
context = {}
|
context = {}
|
||||||
cggrp = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cggrp = {'group_id': fake.GROUP_ID, 'id': fake.GROUP_SNAPSHOT_ID}
|
||||||
'id': fake.CGSNAPSHOT_ID}
|
model_update, snapshot_updates = self.driver.create_group_snapshot(
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
|
||||||
self.driver.create_cgsnapshot,
|
|
||||||
context, cggrp, [])
|
context, cggrp, [])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_snap_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'delete_cg_replay',
|
'delete_cg_replay',
|
||||||
@ -2489,22 +2639,24 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_delete_cgsnapshot(self,
|
@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_find_replay_profile,
|
||||||
mock_delete_cg_replay,
|
mock_delete_cg_replay,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
mock_open_connection,
|
mock_open_connection,
|
||||||
mock_init):
|
mock_init):
|
||||||
snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
mock_snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
||||||
context = {}
|
context = {}
|
||||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cgsnap = {'group_id': fake.GROUP_ID,
|
||||||
'id': fake.CGSNAPSHOT_ID, 'status': 'deleted'}
|
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'deleted'}
|
||||||
model_update, snapshots = self.driver.delete_cgsnapshot(
|
model_update, snapshots = self.driver.delete_group_snapshot(
|
||||||
context, cgsnap, [snapshot])
|
context, cgsnap, [mock_snapshot])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_delete_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
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({'status': cgsnap['status']}, model_update)
|
||||||
self.assertEqual([{'id': fake.SNAPSHOT_ID, 'status': 'deleted'}],
|
self.assertEqual([{'id': fake.SNAPSHOT_ID, 'status': 'deleted'}],
|
||||||
snapshots)
|
snapshots)
|
||||||
@ -2514,7 +2666,10 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=None)
|
return_value=None)
|
||||||
def test_delete_cgsnapshot_profile_not_found(self,
|
@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_find_replay_profile,
|
||||||
mock_delete_cg_replay,
|
mock_delete_cg_replay,
|
||||||
mock_close_connection,
|
mock_close_connection,
|
||||||
@ -2522,16 +2677,14 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
mock_init):
|
mock_init):
|
||||||
snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
snapshot = {'id': fake.SNAPSHOT_ID, 'status': 'available'}
|
||||||
context = {}
|
context = {}
|
||||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cgsnap = {'group_id': fake.GROUP_ID,
|
||||||
'id': fake.CGSNAPSHOT_ID, 'status': 'deleted'}
|
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'available'}
|
||||||
model_update, snapshots = self.driver.delete_cgsnapshot(
|
model_update, snapshots = self.driver.delete_group_snapshot(
|
||||||
context, cgsnap, [snapshot])
|
context, cgsnap, [snapshot])
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
self.assertFalse(mock_delete_cg_replay.called)
|
self.assertFalse(mock_delete_cg_replay.called)
|
||||||
self.assertEqual({'status': cgsnap['status']}, model_update)
|
self.assertEqual({'status': 'error'}, model_update)
|
||||||
self.assertEqual([{'id': fake.SNAPSHOT_ID, 'status': 'deleted'}],
|
self.assertIsNone(snapshots)
|
||||||
snapshots)
|
|
||||||
|
|
||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'delete_cg_replay',
|
'delete_cg_replay',
|
||||||
@ -2539,24 +2692,32 @@ class DellSCSanISCSIDriverTestCase(test.TestCase):
|
|||||||
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_replay_profile',
|
'find_replay_profile',
|
||||||
return_value=SCRPLAYPROFILE)
|
return_value=SCRPLAYPROFILE)
|
||||||
def test_delete_cgsnapshot_profile_failed_delete(self,
|
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type',
|
||||||
mock_find_replay_profile,
|
return_value=True)
|
||||||
mock_delete_cg_replay,
|
def test_delete_group_snapshot_profile_failed_delete(
|
||||||
mock_close_connection,
|
self, mock_is_cg, mock_find_replay_profile, mock_delete_cg_replay,
|
||||||
mock_open_connection,
|
mock_close_connection, mock_open_connection, mock_init):
|
||||||
mock_init):
|
|
||||||
context = {}
|
context = {}
|
||||||
cgsnap = {'consistencygroup_id': fake.CONSISTENCY_GROUP_ID,
|
cgsnap = {'group_id': fake.GROUP_ID,
|
||||||
'id': fake.CGSNAPSHOT_ID, 'status': 'available'}
|
'id': fake.GROUP_SNAPSHOT_ID, 'status': 'available'}
|
||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
model_update, snapshot_updates = self.driver.delete_group_snapshot(
|
||||||
self.driver.delete_cgsnapshot,
|
context, cgsnap, [])
|
||||||
context,
|
self.assertEqual({'status': 'error_deleting'}, model_update)
|
||||||
cgsnap,
|
mock_find_replay_profile.assert_called_once_with(fake.GROUP_ID)
|
||||||
[])
|
|
||||||
mock_find_replay_profile.assert_called_once_with(
|
|
||||||
fake.CONSISTENCY_GROUP_ID)
|
|
||||||
mock_delete_cg_replay.assert_called_once_with(self.SCRPLAYPROFILE,
|
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,
|
@mock.patch.object(dell_storagecenter_api.StorageCenterApi,
|
||||||
'find_volume',
|
'find_volume',
|
||||||
|
@ -25,6 +25,7 @@ from cinder.objects import fields
|
|||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
from cinder.volume.drivers.dell import dell_storagecenter_api
|
from cinder.volume.drivers.dell import dell_storagecenter_api
|
||||||
from cinder.volume.drivers.san.san import san_opts
|
from cinder.volume.drivers.san.san import san_opts
|
||||||
|
from cinder.volume import utils as volume_utils
|
||||||
from cinder.volume import volume_types
|
from cinder.volume import volume_types
|
||||||
|
|
||||||
|
|
||||||
@ -155,17 +156,20 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _add_volume_to_consistency_group(self, api, scvolume, volume):
|
def _add_volume_to_group(self, api, scvolume, volume):
|
||||||
"""Just a helper to add a volume to a consistency group.
|
"""Just a helper to add a volume to a group.
|
||||||
|
|
||||||
:param api: Dell SC API opbject.
|
:param api: Dell SC API opbject.
|
||||||
:param scvolume: Dell SC Volume object.
|
:param scvolume: Dell SC Volume object.
|
||||||
:param volume: Cinder Volume object.
|
:param volume: Cinder Volume object.
|
||||||
:returns: Nothing.
|
:returns: Nothing.
|
||||||
"""
|
"""
|
||||||
if scvolume and volume.get('consistencygroup_id'):
|
if scvolume and volume.get('group_id'):
|
||||||
profile = api.find_replay_profile(
|
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:
|
if profile:
|
||||||
api.update_cg_volumes(profile, [volume])
|
api.update_cg_volumes(profile, [volume])
|
||||||
|
|
||||||
@ -310,8 +314,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
message=_('Unable to create volume %s') %
|
message=_('Unable to create volume %s') %
|
||||||
volume_name)
|
volume_name)
|
||||||
|
|
||||||
# Update Consistency Group
|
# Update Group
|
||||||
self._add_volume_to_consistency_group(api, scvolume, volume)
|
self._add_volume_to_group(api, scvolume, volume)
|
||||||
|
|
||||||
# Create replications. (Or not. It checks.)
|
# Create replications. (Or not. It checks.)
|
||||||
model_update = self._create_replications(api, volume, scvolume)
|
model_update = self._create_replications(api, volume, scvolume)
|
||||||
@ -480,11 +484,12 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
src_volume_name = snapshot.get('volume_id')
|
src_volume_name = snapshot.get('volume_id')
|
||||||
# This snapshot could have been created on its own or as part of a
|
# 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
|
# cgsnapshot. If it was a cgsnapshot it will be identified on the Dell
|
||||||
# backend under cgsnapshot_id. Given the volume ID and the
|
# backend under group_snapshot_id. Given the volume ID and the
|
||||||
# cgsnapshot_id we can find the appropriate snapshot.
|
# group_snapshot_id we can find the appropriate snapshot.
|
||||||
# So first we look for cgsnapshot_id. If that is blank then it must
|
# So first we look for group_snapshot_id. If that is blank then it
|
||||||
# have been a normal snapshot which will be found under snapshot_id.
|
# must have been a normal snapshot which will be found under
|
||||||
snapshot_id = snapshot.get('cgsnapshot_id')
|
# snapshot_id.
|
||||||
|
snapshot_id = snapshot.get('group_snapshot_id')
|
||||||
if not snapshot_id:
|
if not snapshot_id:
|
||||||
snapshot_id = snapshot.get('id')
|
snapshot_id = snapshot.get('id')
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
@ -522,10 +527,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
{'name': volume_name,
|
{'name': volume_name,
|
||||||
'snap': snapshot_id})
|
'snap': snapshot_id})
|
||||||
|
|
||||||
# Update Consistency Group
|
# Update Group
|
||||||
self._add_volume_to_consistency_group(api,
|
self._add_volume_to_group(api, scvolume, volume)
|
||||||
scvolume,
|
|
||||||
volume)
|
|
||||||
# Replicate if we are supposed to.
|
# Replicate if we are supposed to.
|
||||||
model_update = self._create_replications(api,
|
model_update = self._create_replications(api,
|
||||||
volume,
|
volume,
|
||||||
@ -587,10 +590,8 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
{'name': volume_name,
|
{'name': volume_name,
|
||||||
'vol': src_volume_name})
|
'vol': src_volume_name})
|
||||||
|
|
||||||
# Update Consistency Group
|
# Update Group
|
||||||
self._add_volume_to_consistency_group(api,
|
self._add_volume_to_group(api, scvolume, volume)
|
||||||
scvolume,
|
|
||||||
volume)
|
|
||||||
# Replicate if we are supposed to.
|
# Replicate if we are supposed to.
|
||||||
model_update = self._create_replications(api,
|
model_update = self._create_replications(api,
|
||||||
volume,
|
volume,
|
||||||
@ -708,6 +709,7 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
data['storage_protocol'] = self.storage_protocol
|
data['storage_protocol'] = self.storage_protocol
|
||||||
data['reserved_percentage'] = 0
|
data['reserved_percentage'] = 0
|
||||||
data['consistencygroup_support'] = True
|
data['consistencygroup_support'] = True
|
||||||
|
data['consistent_group_snapshot_enabled'] = True
|
||||||
data['thin_provisioning_support'] = True
|
data['thin_provisioning_support'] = True
|
||||||
data['QoS_support'] = False
|
data['QoS_support'] = False
|
||||||
data['replication_enabled'] = self.replication_enabled
|
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']}
|
return {'_name_id': new_volume['_name_id'] or new_volume['id']}
|
||||||
|
|
||||||
def create_consistencygroup(self, context, group):
|
def create_group(self, context, group):
|
||||||
"""This creates a replay profile on the storage backend.
|
"""Creates a group.
|
||||||
|
|
||||||
:param context: the context of the caller.
|
:param context: the context of the caller.
|
||||||
:param group: the dictionary of the consistency group to be created.
|
:param group: the Group object of the group to be created.
|
||||||
:returns: Nothing on success.
|
:returns: model_update
|
||||||
:raises VolumeBackendAPIException:
|
|
||||||
|
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']
|
gid = group['id']
|
||||||
with self._client.open_connection() as api:
|
with self._client.open_connection() as api:
|
||||||
cgroup = api.create_replay_profile(gid)
|
cgroup = api.create_replay_profile(gid)
|
||||||
if cgroup:
|
if cgroup:
|
||||||
LOG.info('Created Consistency Group %s', gid)
|
LOG.info('Created group %s', gid)
|
||||||
return
|
model_update['status'] = fields.GroupStatus.AVAILABLE
|
||||||
msg = _('Unable to create consistency group %s') % gid
|
return model_update
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
|
||||||
|
|
||||||
def delete_consistencygroup(self, context, group, volumes):
|
def delete_group(self, context, group, volumes):
|
||||||
"""Delete the Dell SC profile associated with this consistency group.
|
"""Deletes a group.
|
||||||
|
|
||||||
:param context: the context of the caller.
|
:param context: the context of the caller.
|
||||||
:param group: the dictionary of the consistency group to be created.
|
:param group: the Group object of the group to be deleted.
|
||||||
:returns: Updated model_update, volumes.
|
: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:
|
with self._client.open_connection() as api:
|
||||||
|
gid = group['id']
|
||||||
profile = api.find_replay_profile(gid)
|
profile = api.find_replay_profile(gid)
|
||||||
if profile:
|
if profile:
|
||||||
|
try:
|
||||||
api.delete_replay_profile(profile)
|
api.delete_replay_profile(profile)
|
||||||
# If we are here because we found no profile that should be fine
|
except exception.VolumeBackendAPIException:
|
||||||
# as we are trying to delete it anyway.
|
LOG.error('delete_group: error deleting %s', gid)
|
||||||
|
model_update['status'] = fields.GroupStatus.ERROR
|
||||||
|
|
||||||
# Trundle through the list deleting the volumes.
|
# Trundle through the list deleting the volumes.
|
||||||
volume_updates = []
|
volumes_model_update = []
|
||||||
for volume in volumes:
|
for volume in volumes:
|
||||||
self.delete_volume(volume)
|
status = fields.GroupStatus.ERROR
|
||||||
volume_updates.append({'id': volume['id'],
|
try:
|
||||||
'status': 'deleted'})
|
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_group(self, context, group,
|
||||||
|
|
||||||
def update_consistencygroup(self, context, group,
|
|
||||||
add_volumes=None, remove_volumes=None):
|
add_volumes=None, remove_volumes=None):
|
||||||
"""Updates a consistency group.
|
"""Updates a group.
|
||||||
|
|
||||||
:param context: the context of the caller.
|
:param context: the context of the caller.
|
||||||
:param group: the dictionary of the consistency group to be updated.
|
:param group: the Group object of the group to be updated.
|
||||||
:param add_volumes: a list of volume dictionaries to be added.
|
:param add_volumes: a list of Volume objects to be added.
|
||||||
:param remove_volumes: a list of volume dictionaries to be removed.
|
:param remove_volumes: a list of Volume objects to be removed.
|
||||||
:returns: model_update, add_volumes_update, remove_volumes_update
|
:returns: model_update, add_volumes_update, remove_volumes_update
|
||||||
|
|
||||||
model_update is a dictionary that the driver wants the manager
|
model_update is a dictionary that the driver wants the manager
|
||||||
@ -846,95 +906,225 @@ class DellCommonDriver(driver.ManageableVD,
|
|||||||
volume entry can be updated. If None is returned, the volume will
|
volume entry can be updated. If None is returned, the volume will
|
||||||
remain its original status. Also note that you cannot directly
|
remain its original status. Also note that you cannot directly
|
||||||
assign add_volumes to add_volumes_update as add_volumes is a list of
|
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
|
volume objects and cannot be used for db update directly. Same with
|
||||||
db update directly. Same with remove_volumes.
|
remove_volumes.
|
||||||
|
|
||||||
If the driver throws an exception, the status of the group as well as
|
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'.
|
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:
|
with self._client.open_connection() as api:
|
||||||
|
gid = group['id']
|
||||||
profile = api.find_replay_profile(gid)
|
profile = api.find_replay_profile(gid)
|
||||||
if not profile:
|
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,
|
elif api.update_cg_volumes(profile,
|
||||||
add_volumes,
|
add_volumes,
|
||||||
remove_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.
|
# we need nothing updated above us so just return None.
|
||||||
return None, None, None
|
return None, None, None
|
||||||
# Things did not go well so throw.
|
# 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)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
|
def create_group_from_src(self, context, group, volumes,
|
||||||
"""Takes a snapshot of the consistency group.
|
group_snapshot=None, snapshots=None,
|
||||||
|
source_group=None, source_vols=None):
|
||||||
|
"""Creates a group from source.
|
||||||
|
|
||||||
:param context: the context of the caller.
|
:param context: the context of the caller.
|
||||||
:param cgsnapshot: Information about the snapshot to take.
|
:param group: the Group object to be created.
|
||||||
:param snapshots: List of snapshots for this cgsnapshot.
|
:param volumes: a list of Volume objects in the group.
|
||||||
:returns: Updated model_update, snapshots.
|
:param group_snapshot: the GroupSnapshot object as source.
|
||||||
:raises VolumeBackendAPIException:
|
:param snapshots: a list of Snapshot objects in group_snapshot.
|
||||||
"""
|
:param source_group: the Group object as source.
|
||||||
cgid = cgsnapshot['consistencygroup_id']
|
:param source_vols: a list of Volume objects in the source_group.
|
||||||
snapshotid = cgsnapshot['id']
|
: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:
|
with self._client.open_connection() as api:
|
||||||
profile = api.find_replay_profile(cgid)
|
gid = group_snapshot['group_id']
|
||||||
if profile:
|
snapshotid = group_snapshot['id']
|
||||||
LOG.debug('profile %s replayid %s', profile, snapshotid)
|
profile = api.find_replay_profile(gid)
|
||||||
if api.snap_cg_replay(profile, snapshotid, 0):
|
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 = []
|
snapshot_updates = []
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
snapshot_updates.append({
|
snapshot_updates.append({
|
||||||
'id': snapshot.id,
|
'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.
|
def delete_group_snapshot(self, context, group_snapshot, snapshots):
|
||||||
LOG.error('Failed to snap Consistency Group %s', cgid)
|
"""Deletes a group_snapshot.
|
||||||
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.
|
|
||||||
|
|
||||||
:param context: the context of the caller.
|
:param context: the context of the caller.
|
||||||
:param cgsnapshot: Information about the snapshot to delete.
|
:param group_snapshot: the GroupSnapshot object to be deleted.
|
||||||
:returns: Updated model_update, snapshots.
|
:param snapshots: a list of Snapshot objects in the group_snapshot.
|
||||||
:raises VolumeBackendAPIException.:
|
:returns: model_update, snapshots_model_update
|
||||||
"""
|
|
||||||
cgid = cgsnapshot['consistencygroup_id']
|
|
||||||
snapshotid = cgsnapshot['id']
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
# 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:
|
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:
|
if profile:
|
||||||
LOG.info('Deleting snapshot %(ss)s from %(pro)s',
|
LOG.info('delete_group_snapshot: %(ss)s from %(pro)s',
|
||||||
{'ss': snapshotid,
|
{'ss': snapshotid, 'pro': profile})
|
||||||
'pro': profile})
|
|
||||||
if not api.delete_cg_replay(profile, snapshotid):
|
if not api.delete_cg_replay(profile, snapshotid):
|
||||||
msg = (_('Unable to delete Consistency Group snapshot %s')
|
model_update['status'] = (
|
||||||
% snapshotid)
|
fields.GroupSnapshotStatus.ERROR_DELETING)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
else:
|
||||||
|
model_update['status'] = fields.GroupSnapshotStatus.DELETED
|
||||||
snapshot_updates = []
|
snapshot_updates = []
|
||||||
for snapshot in snapshots:
|
for snapshot in snapshots:
|
||||||
snapshot_updates.append(
|
snapshot_updates.append(
|
||||||
{'id': snapshot['id'],
|
{'id': snapshot['id'],
|
||||||
'status': fields.SnapshotStatus.DELETED})
|
'status': fields.SnapshotStatus.DELETED})
|
||||||
|
|
||||||
model_update = {'status': fields.SnapshotStatus.DELETED}
|
|
||||||
|
|
||||||
return model_update, snapshot_updates
|
return model_update, snapshot_updates
|
||||||
|
|
||||||
def manage_existing(self, volume, existing_ref):
|
def manage_existing(self, volume, existing_ref):
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add consistency group capability to Generic Volume Groups in the
|
||||||
|
Dell EMC SC driver.
|
Loading…
x
Reference in New Issue
Block a user