Merge "PowerMax driver - check cylinder count of source and target volumes"
This commit is contained in:
commit
b1de0652ca
@ -564,6 +564,7 @@ class PowerMaxData(object):
|
|||||||
'SLO': 'Diamond'}
|
'SLO': 'Diamond'}
|
||||||
|
|
||||||
volume_details = [{'cap_gb': 2,
|
volume_details = [{'cap_gb': 2,
|
||||||
|
'cap_cyl': 1092,
|
||||||
'num_of_storage_groups': 1,
|
'num_of_storage_groups': 1,
|
||||||
'volumeId': device_id,
|
'volumeId': device_id,
|
||||||
'volume_identifier': 'OS-%s' % test_volume.id,
|
'volume_identifier': 'OS-%s' % test_volume.id,
|
||||||
@ -573,6 +574,7 @@ class PowerMaxData(object):
|
|||||||
'storageGroupId': [defaultstoragegroup_name,
|
'storageGroupId': [defaultstoragegroup_name,
|
||||||
storagegroup_name_f]},
|
storagegroup_name_f]},
|
||||||
{'cap_gb': 1,
|
{'cap_gb': 1,
|
||||||
|
'cap_cyl': 546,
|
||||||
'num_of_storage_groups': 1,
|
'num_of_storage_groups': 1,
|
||||||
'volumeId': device_id2,
|
'volumeId': device_id2,
|
||||||
'volume_identifier': 'OS-%s' % test_volume.id,
|
'volume_identifier': 'OS-%s' % test_volume.id,
|
||||||
@ -580,11 +582,13 @@ class PowerMaxData(object):
|
|||||||
'storageGroupId': [defaultstoragegroup_name,
|
'storageGroupId': [defaultstoragegroup_name,
|
||||||
storagegroup_name_f]},
|
storagegroup_name_f]},
|
||||||
{'cap_gb': 1,
|
{'cap_gb': 1,
|
||||||
|
'cap_cyl': 546,
|
||||||
'num_of_storage_groups': 0,
|
'num_of_storage_groups': 0,
|
||||||
'volumeId': device_id3,
|
'volumeId': device_id3,
|
||||||
'volume_identifier': '123',
|
'volume_identifier': '123',
|
||||||
'wwn': '600012345'},
|
'wwn': '600012345'},
|
||||||
{'cap_gb': 1,
|
{'cap_gb': 1,
|
||||||
|
'cap_cyl': 546,
|
||||||
'num_of_storage_groups': 1,
|
'num_of_storage_groups': 1,
|
||||||
'volumeId': device_id4,
|
'volumeId': device_id4,
|
||||||
'volume_identifier': 'random_name',
|
'volume_identifier': 'random_name',
|
||||||
|
@ -1179,7 +1179,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.data.extra_specs, snap_name)
|
self.data.extra_specs, snap_name)
|
||||||
mock_cleanup.assert_called_once_with(
|
mock_cleanup.assert_called_once_with(
|
||||||
array, device_id, device_id, clone_name, snap_name,
|
array, device_id, device_id, clone_name, snap_name,
|
||||||
extra_specs)
|
extra_specs, target_volume=clone_volume)
|
||||||
|
|
||||||
def test_create_replica_failed_no_target(self):
|
def test_create_replica_failed_no_target(self):
|
||||||
array = self.data.array
|
array = self.data.array
|
||||||
@ -1196,6 +1196,27 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
source_device_id, self.data.extra_specs, snap_name)
|
source_device_id, self.data.extra_specs, snap_name)
|
||||||
mock_cleanup.assert_not_called()
|
mock_cleanup.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(
|
||||||
|
utils.PowerMaxUtils,
|
||||||
|
'compare_cylinders',
|
||||||
|
side_effect=exception.VolumeBackendAPIException)
|
||||||
|
def test_create_replica_cylinder_mismatch(self, mock_cyl):
|
||||||
|
array = self.data.array
|
||||||
|
clone_volume = self.data.test_clone_volume
|
||||||
|
source_device_id = self.data.device_id
|
||||||
|
snap_name = self.data.snap_location['snap_name']
|
||||||
|
clone_name = 'OS-' + clone_volume.id
|
||||||
|
with mock.patch.object(
|
||||||
|
self.common, '_cleanup_target') as mock_cleanup:
|
||||||
|
self.assertRaises(
|
||||||
|
Exception, self.common._create_replica, array,
|
||||||
|
clone_volume, source_device_id,
|
||||||
|
self.data.extra_specs, snap_name) # noqa: ignore=H202
|
||||||
|
mock_cleanup.assert_called_once_with(
|
||||||
|
array, source_device_id, source_device_id,
|
||||||
|
clone_name, snap_name, self.data.extra_specs,
|
||||||
|
target_volume=clone_volume)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
masking.PowerMaxMasking,
|
masking.PowerMaxMasking,
|
||||||
'remove_and_reset_members')
|
'remove_and_reset_members')
|
||||||
@ -1220,7 +1241,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
array, target_device_id, source_device_id,
|
array, target_device_id, source_device_id,
|
||||||
snap_name, extra_specs, generation)
|
snap_name, extra_specs, generation)
|
||||||
|
|
||||||
def test_cleanup_target_no_sync(self):
|
@mock.patch.object(masking.PowerMaxMasking, 'remove_volume_from_sg')
|
||||||
|
def test_cleanup_target_no_sync(self, mock_remove):
|
||||||
array = self.data.array
|
array = self.data.array
|
||||||
clone_volume = self.data.test_clone_volume
|
clone_volume = self.data.test_clone_volume
|
||||||
source_device_id = self.data.device_id
|
source_device_id = self.data.device_id
|
||||||
|
@ -523,3 +523,20 @@ class PowerMaxUtilsTest(test.TestCase):
|
|||||||
ret_prop_dict = self.utils.validate_qos_distribution_type(
|
ret_prop_dict = self.utils.validate_qos_distribution_type(
|
||||||
sg_value, qos_extra_spec, input_prop_dict)
|
sg_value, qos_extra_spec, input_prop_dict)
|
||||||
self.assertEqual(input_prop_dict, ret_prop_dict)
|
self.assertEqual(input_prop_dict, ret_prop_dict)
|
||||||
|
|
||||||
|
def test_compare_cylinders(self):
|
||||||
|
source_cylinders = '12345'
|
||||||
|
target_cylinders = '12345'
|
||||||
|
self.utils.compare_cylinders(source_cylinders, target_cylinders)
|
||||||
|
|
||||||
|
def test_compare_cylinders_target_larger(self):
|
||||||
|
source_cylinders = '12345'
|
||||||
|
target_cylinders = '12346'
|
||||||
|
self.utils.compare_cylinders(source_cylinders, target_cylinders)
|
||||||
|
|
||||||
|
def test_compare_cylinders_source_larger(self):
|
||||||
|
source_cylinders = '12347'
|
||||||
|
target_cylinders = '12346'
|
||||||
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
|
self.utils.compare_cylinders, source_cylinders,
|
||||||
|
target_cylinders)
|
||||||
|
@ -2162,13 +2162,21 @@ class PowerMaxCommon(object):
|
|||||||
clone_id = clone_volume.id
|
clone_id = clone_volume.id
|
||||||
clone_name = self.utils.get_volume_element_name(clone_id)
|
clone_name = self.utils.get_volume_element_name(clone_id)
|
||||||
create_snap = False
|
create_snap = False
|
||||||
|
|
||||||
|
volume_dict = self.rest.get_volume(array, source_device_id)
|
||||||
# PowerMax/VMAX supports using a target volume that is bigger than
|
# PowerMax/VMAX supports using a target volume that is bigger than
|
||||||
# the source volume, so we create the target volume the desired
|
# the source volume, so we create the target volume the desired
|
||||||
# size at this point to avoid having to extend later
|
# size at this point to avoid having to extend later
|
||||||
try:
|
try:
|
||||||
clone_dict = self._create_volume(
|
clone_dict = self._create_volume(
|
||||||
clone_name, clone_volume.size, extra_specs)
|
clone_name, clone_volume.size, extra_specs)
|
||||||
|
|
||||||
target_device_id = clone_dict['device_id']
|
target_device_id = clone_dict['device_id']
|
||||||
|
if target_device_id:
|
||||||
|
clone_volume_dict = self.rest.get_volume(
|
||||||
|
array, target_device_id)
|
||||||
|
self.utils.compare_cylinders(
|
||||||
|
volume_dict['cap_cyl'], clone_volume_dict['cap_cyl'])
|
||||||
LOG.info("The target device id is: %(device_id)s.",
|
LOG.info("The target device id is: %(device_id)s.",
|
||||||
{'device_id': target_device_id})
|
{'device_id': target_device_id})
|
||||||
if not snap_name:
|
if not snap_name:
|
||||||
@ -2185,7 +2193,8 @@ class PowerMaxCommon(object):
|
|||||||
{'cloneName': clone_name, 'e': e})
|
{'cloneName': clone_name, 'e': e})
|
||||||
self._cleanup_target(
|
self._cleanup_target(
|
||||||
array, target_device_id, source_device_id,
|
array, target_device_id, source_device_id,
|
||||||
clone_name, snap_name, extra_specs)
|
clone_name, snap_name, extra_specs,
|
||||||
|
target_volume=clone_volume)
|
||||||
# Re-throw the exception.
|
# Re-throw the exception.
|
||||||
raise
|
raise
|
||||||
# add source id and snap_name to the clone dict
|
# add source id and snap_name to the clone dict
|
||||||
@ -2195,7 +2204,8 @@ class PowerMaxCommon(object):
|
|||||||
|
|
||||||
def _cleanup_target(
|
def _cleanup_target(
|
||||||
self, array, target_device_id, source_device_id,
|
self, array, target_device_id, source_device_id,
|
||||||
clone_name, snap_name, extra_specs, generation=0):
|
clone_name, snap_name, extra_specs, generation=0,
|
||||||
|
target_volume=None):
|
||||||
"""Cleanup target volume on failed clone/ snapshot creation.
|
"""Cleanup target volume on failed clone/ snapshot creation.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
@ -2204,6 +2214,7 @@ class PowerMaxCommon(object):
|
|||||||
:param clone_name: the name of the clone volume
|
:param clone_name: the name of the clone volume
|
||||||
:param extra_specs: the extra specifications
|
:param extra_specs: the extra specifications
|
||||||
:param generation: the generation number of the snapshot
|
:param generation: the generation number of the snapshot
|
||||||
|
:param target_volume: the target volume object
|
||||||
"""
|
"""
|
||||||
snap_session = self.rest.get_sync_session(
|
snap_session = self.rest.get_sync_session(
|
||||||
array, source_device_id, snap_name, target_device_id, generation)
|
array, source_device_id, snap_name, target_device_id, generation)
|
||||||
@ -2211,6 +2222,8 @@ class PowerMaxCommon(object):
|
|||||||
self.provision.break_replication_relationship(
|
self.provision.break_replication_relationship(
|
||||||
array, target_device_id, source_device_id,
|
array, target_device_id, source_device_id,
|
||||||
snap_name, extra_specs, generation)
|
snap_name, extra_specs, generation)
|
||||||
|
self._remove_vol_and_cleanup_replication(
|
||||||
|
array, target_device_id, clone_name, extra_specs, target_volume)
|
||||||
self._delete_from_srp(
|
self._delete_from_srp(
|
||||||
array, target_device_id, clone_name, extra_specs)
|
array, target_device_id, clone_name, extra_specs)
|
||||||
|
|
||||||
|
@ -1880,7 +1880,7 @@ class PowerMaxRest(object):
|
|||||||
snapshot = None
|
snapshot = None
|
||||||
snap_info = self.get_volume_snap_info(array, device_id)
|
snap_info = self.get_volume_snap_info(array, device_id)
|
||||||
if snap_info:
|
if snap_info:
|
||||||
if (snap_info.get('snapshotSrcs') and
|
if (snap_info.get('snapshotSrcs', None) and
|
||||||
bool(snap_info['snapshotSrcs'])):
|
bool(snap_info['snapshotSrcs'])):
|
||||||
for snap in snap_info['snapshotSrcs']:
|
for snap in snap_info['snapshotSrcs']:
|
||||||
if snap['snapshotName'] == snap_name:
|
if snap['snapshotName'] == snap_name:
|
||||||
@ -1899,7 +1899,8 @@ class PowerMaxRest(object):
|
|||||||
snapshot_list = []
|
snapshot_list = []
|
||||||
snap_info = self.get_volume_snap_info(array, source_device_id)
|
snap_info = self.get_volume_snap_info(array, source_device_id)
|
||||||
if snap_info:
|
if snap_info:
|
||||||
if bool(snap_info['snapshotSrcs']):
|
if (snap_info.get('snapshotSrcs', None) and
|
||||||
|
bool(snap_info['snapshotSrcs'])):
|
||||||
snapshot_list = snap_info['snapshotSrcs']
|
snapshot_list = snap_info['snapshotSrcs']
|
||||||
return snapshot_list
|
return snapshot_list
|
||||||
|
|
||||||
|
@ -949,3 +949,21 @@ class PowerMaxUtils(object):
|
|||||||
raise exception.VolumeBackendAPIException(
|
raise exception.VolumeBackendAPIException(
|
||||||
message=exception_message)
|
message=exception_message)
|
||||||
return property_dict
|
return property_dict
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def compare_cylinders(cylinders_source, cylinder_target):
|
||||||
|
"""Compare number of cylinders of source and target.
|
||||||
|
|
||||||
|
:param cylinders_source: number of cylinders on source
|
||||||
|
:param cylinders_target: number of cylinders on target
|
||||||
|
"""
|
||||||
|
if float(cylinders_source) > float(cylinder_target):
|
||||||
|
exception_message = (
|
||||||
|
_("The number of source cylinders %(cylinders_source)s "
|
||||||
|
"cannot be greater than the number of target cylinders "
|
||||||
|
"%(cylinder_target)s. Please extend your source volume by "
|
||||||
|
"at least 1GiB.") % {
|
||||||
|
'cylinders_source': cylinders_source,
|
||||||
|
'cylinder_target': cylinder_target})
|
||||||
|
raise exception.VolumeBackendAPIException(
|
||||||
|
message=exception_message)
|
||||||
|
Loading…
Reference in New Issue
Block a user