Merge "VMAX driver - seamless upgrade from SMI-S to REST"

This commit is contained in:
Jenkins 2017-08-05 06:08:11 +00:00 committed by Gerrit Code Review
commit 2a27fe404a
4 changed files with 229 additions and 71 deletions

View File

@ -143,6 +143,20 @@ class VMAXCommonData(object):
provider_location3 = {'array': six.text_type(remote_array), provider_location3 = {'array': six.text_type(remote_array),
'device_id': device_id2} 'device_id': device_id2}
legacy_provider_location = {
'classname': 'Symm_StorageVolume',
'keybindings': {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000197800123',
'DeviceID': device_id,
'SystemCreationClassName': u'Symm_StorageSystem'}}
legacy_provider_location2 = {
'classname': 'Symm_StorageVolume',
'keybindings': {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000197800123',
'DeviceID': device_id2,
'SystemCreationClassName': u'Symm_StorageSystem'}}
snap_location = {'snap_name': '12345', snap_location = {'snap_name': '12345',
'source_id': device_id} 'source_id': device_id}
@ -156,6 +170,12 @@ class VMAXCommonData(object):
volume_type=test_volume_type, host=fake_host, volume_type=test_volume_type, host=fake_host,
replication_driver_data=six.text_type(provider_location3)) replication_driver_data=six.text_type(provider_location3))
test_legacy_vol = fake_volume.fake_volume_obj(
context=ctx, name='vol1', size=2, provider_auth=None,
provider_location=six.text_type(legacy_provider_location),
replication_driver_data=six.text_type(legacy_provider_location2),
host=fake_host, volume_type=test_volume_type)
test_clone_volume = fake_volume.fake_volume_obj( test_clone_volume = fake_volume.fake_volume_obj(
context=ctx, name='vol1', size=2, provider_auth=None, context=ctx, name='vol1', size=2, provider_auth=None,
provider_location=six.text_type(provider_location2), provider_location=six.text_type(provider_location2),
@ -166,6 +186,11 @@ class VMAXCommonData(object):
provider_location=six.text_type(snap_location), provider_location=six.text_type(snap_location),
host=fake_host, volume=test_volume) host=fake_host, volume=test_volume)
test_legacy_snapshot = fake_snapshot.fake_snapshot_obj(
context=ctx, id='12345', name='my_snap', size=2,
provider_location=six.text_type(legacy_provider_location),
host=fake_host, volume=test_volume)
test_failed_snap = fake_snapshot.fake_snapshot_obj( test_failed_snap = fake_snapshot.fake_snapshot_obj(
context=ctx, id='12345', name=failed_resource, size=2, context=ctx, id='12345', name=failed_resource, size=2,
provider_location=six.text_type(snap_location), provider_location=six.text_type(snap_location),
@ -262,7 +287,7 @@ class VMAXCommonData(object):
'parent_sg_name': parent_sg_f, 'parent_sg_name': parent_sg_f,
'srp': srp, 'srp': srp,
'storagetype:disablecompression': False, 'storagetype:disablecompression': False,
'port_group_name': port_group_name_f, utils.PORTGROUPNAME: port_group_name_f,
'slo': slo, 'slo': slo,
'storagegroup_name': storagegroup_name_f, 'storagegroup_name': storagegroup_name_f,
'volume_name': test_volume.name, 'volume_name': test_volume.name,
@ -1172,6 +1197,15 @@ class VMAXUtilsTest(test.TestCase):
is_fo3 = self.utils.is_volume_failed_over(None) is_fo3 = self.utils.is_volume_failed_over(None)
self.assertFalse(is_fo3) self.assertFalse(is_fo3)
def test_add_legacy_pools(self):
pools = [{'pool_name': "Diamond+None+SRP_1+000197800111"},
{'pool_name': "Diamond+OLTP+SRP_1+000197800111"}]
new_pools = self.utils.add_legacy_pools(pools)
ref_pools = [{'pool_name': "Diamond+None+SRP_1+000197800111"},
{'pool_name': "Diamond+OLTP+SRP_1+000197800111"},
{'pool_name': "Diamond+SRP_1+000197800111"}]
self.assertEqual(ref_pools, new_pools)
def test_update_volume_group_name(self): def test_update_volume_group_name(self):
group = self.data.test_group_1 group = self.data.test_group_1
ref_group_name = self.data.test_vol_grp_name ref_group_name = self.data.test_vol_grp_name
@ -2919,6 +2953,10 @@ class VMAXCommonTest(test.TestCase):
model_update = self.common.create_volume_from_snapshot( model_update = self.common.create_volume_from_snapshot(
self.data.test_clone_volume, self.data.test_snapshot) self.data.test_clone_volume, self.data.test_snapshot)
self.assertEqual(ref_model_update, model_update) self.assertEqual(ref_model_update, model_update)
# Test from legacy snapshot
model_update = self.common.create_volume_from_snapshot(
self.data.test_clone_volume, self.data.test_legacy_snapshot)
self.assertEqual(ref_model_update, model_update)
def test_cloned_volume(self): def test_cloned_volume(self):
ref_model_update = ( ref_model_update = (
@ -2953,12 +2991,18 @@ class VMAXCommonTest(test.TestCase):
def test_delete_snapshot_not_found(self): def test_delete_snapshot_not_found(self):
with mock.patch.object(self.common, '_parse_snap_info', with mock.patch.object(self.common, '_parse_snap_info',
return_value=(None, None)): return_value=(None, 'Something')):
with mock.patch.object(self.provision, 'delete_volume_snap'): with mock.patch.object(self.provision, 'delete_volume_snap'):
self.common.delete_snapshot(self.data.test_snapshot, self.common.delete_snapshot(self.data.test_snapshot,
self.data.test_volume) self.data.test_volume)
self.provision.delete_volume_snap.assert_not_called() self.provision.delete_volume_snap.assert_not_called()
def test_delete_legacy_snap(self):
with mock.patch.object(self.common, '_delete_volume') as mock_del:
self.common.delete_snapshot(self.data.test_legacy_snapshot,
self.data.test_legacy_vol)
mock_del.assert_called_once_with(self.data.test_legacy_snapshot)
def test_remove_members(self): def test_remove_members(self):
array = self.data.array array = self.data.array
device_id = self.data.device_id device_id = self.data.device_id
@ -2978,7 +3022,7 @@ class VMAXCommonTest(test.TestCase):
device_id = self.data.device_id device_id = self.data.device_id
volume = self.data.test_volume volume = self.data.test_volume
extra_specs = deepcopy(self.data.extra_specs_intervals_set) extra_specs = deepcopy(self.data.extra_specs_intervals_set)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
connector = self.data.connector connector = self.data.connector
with mock.patch.object(self.common, '_remove_members'): with mock.patch.object(self.common, '_remove_members'):
self.common._unmap_lun(volume, connector) self.common._unmap_lun(volume, connector)
@ -3010,7 +3054,7 @@ class VMAXCommonTest(test.TestCase):
volume = self.data.test_volume volume = self.data.test_volume
connector = self.data.connector connector = self.data.connector
extra_specs = deepcopy(self.data.extra_specs_intervals_set) extra_specs = deepcopy(self.data.extra_specs_intervals_set)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs) volume, connector, extra_specs)
with mock.patch.object(self.common, 'find_host_lun_id', with mock.patch.object(self.common, 'find_host_lun_id',
@ -3028,7 +3072,7 @@ class VMAXCommonTest(test.TestCase):
volume = self.data.test_volume volume = self.data.test_volume
connector = self.data.connector connector = self.data.connector
extra_specs = deepcopy(self.data.extra_specs) extra_specs = deepcopy(self.data.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs) volume, connector, extra_specs)
host_lun = (self.data.maskingview[0]['maskingViewConnection'][0] host_lun = (self.data.maskingview[0]['maskingViewConnection'][0]
@ -3039,7 +3083,7 @@ class VMAXCommonTest(test.TestCase):
'device_id': self.data.device_id} 'device_id': self.data.device_id}
with mock.patch.object(self.masking, 'setup_masking_view', with mock.patch.object(self.masking, 'setup_masking_view',
return_value={ return_value={
'port_group_name': utils.PORTGROUPNAME:
self.data.port_group_name_f}): self.data.port_group_name_f}):
device_info_dict, pg = self.common._attach_volume( device_info_dict, pg = self.common._attach_volume(
volume, connector, extra_specs, masking_view_dict) volume, connector, extra_specs, masking_view_dict)
@ -3049,7 +3093,7 @@ class VMAXCommonTest(test.TestCase):
volume = self.data.test_volume volume = self.data.test_volume
connector = self.data.connector connector = self.data.connector
extra_specs = deepcopy(self.data.extra_specs) extra_specs = deepcopy(self.data.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs) volume, connector, extra_specs)
with mock.patch.object(self.masking, 'setup_masking_view', with mock.patch.object(self.masking, 'setup_masking_view',
@ -3084,7 +3128,7 @@ class VMAXCommonTest(test.TestCase):
device_id = self.data.device_id device_id = self.data.device_id
new_size = self.data.test_volume.size new_size = self.data.test_volume.size
ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set)
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
with mock.patch.object(self.rest, 'is_vol_in_rep_session', with mock.patch.object(self.rest, 'is_vol_in_rep_session',
return_value=(False, False, None)): return_value=(False, False, None)):
self.common.extend_volume(volume, new_size) self.common.extend_volume(volume, new_size)
@ -3166,6 +3210,13 @@ class VMAXCommonTest(test.TestCase):
volume, extra_specs) volume, extra_specs)
self.assertIsNone(founddevice_id) self.assertIsNone(founddevice_id)
def test_find_legacy_device_on_array(self):
volume = self.data.test_legacy_vol
extra_specs = self.data.extra_specs
ref_device_id = self.data.device_id
founddevice_id = self.common._find_device_on_array(volume, extra_specs)
self.assertEqual(ref_device_id, founddevice_id)
def test_find_host_lun_id_attached(self): def test_find_host_lun_id_attached(self):
volume = self.data.test_volume volume = self.data.test_volume
extra_specs = self.data.extra_specs extra_specs = self.data.extra_specs
@ -3222,7 +3273,7 @@ class VMAXCommonTest(test.TestCase):
def test_initial_setup_success(self): def test_initial_setup_success(self):
volume = self.data.test_volume volume = self.data.test_volume
ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set)
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
extra_specs = self.common._initial_setup(volume) extra_specs = self.common._initial_setup(volume)
self.assertEqual(ref_extra_specs, extra_specs) self.assertEqual(ref_extra_specs, extra_specs)
@ -3237,7 +3288,7 @@ class VMAXCommonTest(test.TestCase):
volume = self.data.test_volume volume = self.data.test_volume
connector = self.data.connector connector = self.data.connector
extra_specs = deepcopy(self.data.extra_specs) extra_specs = deepcopy(self.data.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
ref_mv_dict = self.data.masking_view_dict ref_mv_dict = self.data.masking_view_dict
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs) volume, connector, extra_specs)
@ -3251,7 +3302,7 @@ class VMAXCommonTest(test.TestCase):
'workload': None, 'workload': None,
'srp': self.data.srp, 'srp': self.data.srp,
'array': self.data.array, 'array': self.data.array,
'port_group_name': self.data.port_group_name_f} utils.PORTGROUPNAME: self.data.port_group_name_f}
ref_mv_dict = self.data.masking_view_dict_no_slo ref_mv_dict = self.data.masking_view_dict_no_slo
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
volume, connector, extra_specs) volume, connector, extra_specs)
@ -3261,7 +3312,7 @@ class VMAXCommonTest(test.TestCase):
volume = self.data.test_volume volume = self.data.test_volume
connector = self.data.connector connector = self.data.connector
extra_specs = deepcopy(self.data.extra_specs) extra_specs = deepcopy(self.data.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
extra_specs[utils.DISABLECOMPRESSION] = "true" extra_specs[utils.DISABLECOMPRESSION] = "true"
ref_mv_dict = self.data.masking_view_dict_compression_disabled ref_mv_dict = self.data.masking_view_dict_compression_disabled
masking_view_dict = self.common._populate_masking_dict( masking_view_dict = self.common._populate_masking_dict(
@ -3364,7 +3415,7 @@ class VMAXCommonTest(test.TestCase):
device_id = self.data.device_id device_id = self.data.device_id
volume_name = self.data.test_volume.name volume_name = self.data.test_volume.name
ref_extra_specs = self.data.extra_specs_intervals_set ref_extra_specs = self.data.extra_specs_intervals_set
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
volume = self.data.test_volume volume = self.data.test_volume
with mock.patch.object(self.common, '_sync_check'): with mock.patch.object(self.common, '_sync_check'):
with mock.patch.object(self.common, '_delete_from_srp'): with mock.patch.object(self.common, '_delete_from_srp'):
@ -3432,7 +3483,7 @@ class VMAXCommonTest(test.TestCase):
extra_specs = self.common._set_vmax_extra_specs( extra_specs = self.common._set_vmax_extra_specs(
self.data.vol_type_extra_specs, srp_record) self.data.vol_type_extra_specs, srp_record)
ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set)
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
self.assertEqual(ref_extra_specs, extra_specs) self.assertEqual(ref_extra_specs, extra_specs)
def test_set_vmax_extra_specs_no_srp_name(self): def test_set_vmax_extra_specs_no_srp_name(self):
@ -3449,7 +3500,7 @@ class VMAXCommonTest(test.TestCase):
extra_specs = self.common._set_vmax_extra_specs( extra_specs = self.common._set_vmax_extra_specs(
self.data.vol_type_extra_specs_compr_disabled, srp_record) self.data.vol_type_extra_specs_compr_disabled, srp_record)
ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set)
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
ref_extra_specs[utils.DISABLECOMPRESSION] = "true" ref_extra_specs[utils.DISABLECOMPRESSION] = "true"
self.assertEqual(ref_extra_specs, extra_specs) self.assertEqual(ref_extra_specs, extra_specs)
@ -3459,15 +3510,15 @@ class VMAXCommonTest(test.TestCase):
extra_specs = self.common._set_vmax_extra_specs( extra_specs = self.common._set_vmax_extra_specs(
self.data.vol_type_extra_specs_compr_disabled, srp_record) self.data.vol_type_extra_specs_compr_disabled, srp_record)
ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set) ref_extra_specs = deepcopy(self.data.extra_specs_intervals_set)
ref_extra_specs['port_group_name'] = self.data.port_group_name_f ref_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
self.assertEqual(ref_extra_specs, extra_specs) self.assertEqual(ref_extra_specs, extra_specs)
def test_set_vmax_extra_specs_portgroup_as_spec(self): def test_set_vmax_extra_specs_portgroup_as_spec(self):
srp_record = self.utils.parse_file_to_get_array_map( srp_record = self.utils.parse_file_to_get_array_map(
self.fake_xml) self.fake_xml)
extra_specs = self.common._set_vmax_extra_specs( extra_specs = self.common._set_vmax_extra_specs(
{'port_group_name': 'extra_spec_pg'}, srp_record) {utils.PORTGROUPNAME: 'extra_spec_pg'}, srp_record)
self.assertEqual('extra_spec_pg', extra_specs['port_group_name']) self.assertEqual('extra_spec_pg', extra_specs[utils.PORTGROUPNAME])
def test_set_vmax_extra_specs_no_portgroup_set(self): def test_set_vmax_extra_specs_no_portgroup_set(self):
fake_xml = FakeXML().create_fake_config_file( fake_xml = FakeXML().create_fake_config_file(
@ -3698,8 +3749,21 @@ class VMAXCommonTest(test.TestCase):
extra_specs) extra_specs)
mock_break.assert_called_with( mock_break.assert_called_with(
array, target, device_id, snap_name, extra_specs) array, target, device_id, snap_name, extra_specs)
mock_delete.assert_called_with( mock_delete.assert_called_with(array, snap_name, device_id)
array, snap_name, device_id) # Delete legacy temp snap
mock_delete.reset_mock()
snap_name2 = 'EMC_SMI_12345'
sessions = [{'source_vol': device_id,
'snap_name': snap_name2,
'target_vol_list': []}]
with mock.patch.object(self.rest, 'find_snap_vx_sessions',
return_value=sessions):
with mock.patch.object(self.rest, 'get_volume_snap',
return_value=snap_name2):
self.common._sync_check(array, device_id, volume_name,
extra_specs)
mock_delete.assert_called_once_with(
array, snap_name2, device_id)
@mock.patch.object( @mock.patch.object(
provision.VMAXProvision, provision.VMAXProvision,
@ -3828,7 +3892,7 @@ class VMAXCommonTest(test.TestCase):
device_id = self.data.device_id device_id = self.data.device_id
volume_name = self.data.test_volume.name volume_name = self.data.test_volume.name
extra_specs = self.data.extra_specs_intervals_set extra_specs = self.data.extra_specs_intervals_set
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
volume = self.data.test_volume volume = self.data.test_volume
new_type = {'extra_specs': {}} new_type = {'extra_specs': {}}
host = {'host': self.data.new_host} host = {'host': self.data.new_host}
@ -4369,6 +4433,10 @@ class VMAXFCTest(test.TestCase):
zoning_mappings = self.driver._get_zoning_mappings( zoning_mappings = self.driver._get_zoning_mappings(
self.data.test_volume, self.data.connector) self.data.test_volume, self.data.connector)
self.assertEqual(ref_mappings, zoning_mappings) self.assertEqual(ref_mappings, zoning_mappings)
# Legacy vol
zoning_mappings2 = self.driver._get_zoning_mappings(
self.data.test_legacy_vol, self.data.connector)
self.assertEqual(ref_mappings, zoning_mappings2)
def test_get_zoning_mappings_no_mv(self): def test_get_zoning_mappings_no_mv(self):
ref_mappings = {'port_group': None, ref_mappings = {'port_group': None,
@ -4748,7 +4816,7 @@ class VMAXMaskingTest(test.TestCase):
self.driver_fc = driver_fc self.driver_fc = driver_fc
self.mask = self.driver.masking self.mask = self.driver.masking
self.extra_specs = self.data.extra_specs self.extra_specs = self.data.extra_specs
self.extra_specs['port_group_name'] = self.data.port_group_name_i self.extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_i
self.maskingviewdict = self.driver._populate_masking_dict( self.maskingviewdict = self.driver._populate_masking_dict(
self.data.test_volume, self.data.connector, self.extra_specs) self.data.test_volume, self.data.connector, self.extra_specs)
self.maskingviewdict['extra_specs'] = self.extra_specs self.maskingviewdict['extra_specs'] = self.extra_specs
@ -5662,7 +5730,7 @@ class VMAXCommonReplicationTest(test.TestCase):
def test_create_replicated_volume(self): def test_create_replicated_volume(self):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
vol_identifier = self.utils.get_volume_element_name( vol_identifier = self.utils.get_volume_element_name(
self.data.test_volume.id) self.data.test_volume.id)
with mock.patch.object(self.common, '_replicate_volume', with mock.patch.object(self.common, '_replicate_volume',
@ -5675,7 +5743,7 @@ class VMAXCommonReplicationTest(test.TestCase):
def test_create_cloned_replicated_volume(self): def test_create_cloned_replicated_volume(self):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
with mock.patch.object(self.common, '_replicate_volume', with mock.patch.object(self.common, '_replicate_volume',
return_value={}) as mock_rep: return_value={}) as mock_rep:
self.common.create_cloned_volume( self.common.create_cloned_volume(
@ -5687,7 +5755,7 @@ class VMAXCommonReplicationTest(test.TestCase):
def test_create_replicated_volume_from_snap(self): def test_create_replicated_volume_from_snap(self):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
with mock.patch.object(self.common, '_replicate_volume', with mock.patch.object(self.common, '_replicate_volume',
return_value={}) as mock_rep: return_value={}) as mock_rep:
self.common.create_volume_from_snapshot( self.common.create_volume_from_snapshot(
@ -5731,7 +5799,7 @@ class VMAXCommonReplicationTest(test.TestCase):
return_value=True) return_value=True)
def test_unmap_lun_volume_failed_over(self, mock_fo, mock_es, mock_rm): def test_unmap_lun_volume_failed_over(self, mock_fo, mock_es, mock_rm):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
rep_config = self.utils.get_replication_config( rep_config = self.utils.get_replication_config(
[self.replication_device]) [self.replication_device])
self.common._unmap_lun(self.data.test_volume, self.data.connector) self.common._unmap_lun(self.data.test_volume, self.data.connector)
@ -5741,9 +5809,9 @@ class VMAXCommonReplicationTest(test.TestCase):
return_value=True) return_value=True)
def test_initialize_connection_vol_failed_over(self, mock_fo): def test_initialize_connection_vol_failed_over(self, mock_fo):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
rep_extra_specs = deepcopy(VMAXCommonData.rep_extra_specs) rep_extra_specs = deepcopy(VMAXCommonData.rep_extra_specs)
rep_extra_specs['port_group_name'] = self.data.port_group_name_f rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
rep_config = self.utils.get_replication_config( rep_config = self.utils.get_replication_config(
[self.replication_device]) [self.replication_device])
with mock.patch.object(self.common, '_get_replication_extra_specs', with mock.patch.object(self.common, '_get_replication_extra_specs',
@ -5755,7 +5823,7 @@ class VMAXCommonReplicationTest(test.TestCase):
@mock.patch.object(common.VMAXCommon, '_sync_check') @mock.patch.object(common.VMAXCommon, '_sync_check')
def test_extend_volume_rep_enabled(self, mock_sync): def test_extend_volume_rep_enabled(self, mock_sync):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
volume_name = self.data.test_volume.name volume_name = self.data.test_volume.name
with mock.patch.object(self.rest, 'is_vol_in_rep_session', with mock.patch.object(self.rest, 'is_vol_in_rep_session',
return_value=(False, False, None)): return_value=(False, False, None)):
@ -5773,7 +5841,7 @@ class VMAXCommonReplicationTest(test.TestCase):
def test_populate_masking_dict_is_re(self): def test_populate_masking_dict_is_re(self):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
masking_dict = self.common._populate_masking_dict( masking_dict = self.common._populate_masking_dict(
self.data.test_volume, self.data.connector, extra_specs) self.data.test_volume, self.data.connector, extra_specs)
self.assertTrue(masking_dict['replication_enabled']) self.assertTrue(masking_dict['replication_enabled'])
@ -5785,7 +5853,7 @@ class VMAXCommonReplicationTest(test.TestCase):
return_value={}) return_value={})
def test_manage_existing_is_replicated(self, mock_rep): def test_manage_existing_is_replicated(self, mock_rep):
extra_specs = deepcopy(self.extra_specs) extra_specs = deepcopy(self.extra_specs)
extra_specs['port_group_name'] = self.data.port_group_name_f extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
external_ref = {u'source-name': u'00002'} external_ref = {u'source-name': u'00002'}
volume_name = self.utils.get_volume_element_name( volume_name = self.utils.get_volume_element_name(
self.data.test_volume.id) self.data.test_volume.id)
@ -5822,7 +5890,7 @@ class VMAXCommonReplicationTest(test.TestCase):
@mock.patch.object(common.VMAXCommon, '_cleanup_remote_target') @mock.patch.object(common.VMAXCommon, '_cleanup_remote_target')
def test_cleanup_lun_replication_success(self, mock_clean, mock_rm): def test_cleanup_lun_replication_success(self, mock_clean, mock_rm):
rep_extra_specs = deepcopy(self.data.rep_extra_specs) rep_extra_specs = deepcopy(self.data.rep_extra_specs)
rep_extra_specs['port_group_name'] = self.data.port_group_name_f rep_extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
self.common.cleanup_lun_replication( self.common.cleanup_lun_replication(
self.data.test_volume, "1", self.data.device_id, self.data.test_volume, "1", self.data.device_id,
self.extra_specs) self.extra_specs)
@ -5833,6 +5901,14 @@ class VMAXCommonReplicationTest(test.TestCase):
mock_rm.assert_called_once_with( mock_rm.assert_called_once_with(
self.data.remote_array, self.data.device_id2, "1", self.data.remote_array, self.data.device_id2, "1",
rep_extra_specs, False) rep_extra_specs, False)
# Cleanup legacy replication
self.common.cleanup_lun_replication(
self.data.test_legacy_vol, "1", self.data.device_id,
self.extra_specs)
mock_clean.assert_called_once_with(
self.data.array, self.data.remote_array, self.data.device_id,
self.data.device_id2, self.data.rdf_group_no, "1",
rep_extra_specs)
@mock.patch.object(common.VMAXCommon, '_cleanup_remote_target') @mock.patch.object(common.VMAXCommon, '_cleanup_remote_target')
def test_cleanup_lun_replication_no_target(self, mock_clean): def test_cleanup_lun_replication_no_target(self, mock_clean):
@ -5928,6 +6004,19 @@ class VMAXCommonReplicationTest(test.TestCase):
self.data.test_volume, False, self.extra_specs) self.data.test_volume, False, self.extra_specs)
self.assertEqual(ref_model_update2, model_update2) self.assertEqual(ref_model_update2, model_update2)
def test_failover_legacy_volume(self):
ref_model_update = {
'volume_id': self.data.test_volume.id,
'updates':
{'replication_status': fields.ReplicationStatus.FAILED_OVER,
'replication_driver_data': six.text_type(
self.data.legacy_provider_location),
'provider_location': six.text_type(
self.data.legacy_provider_location2)}}
model_update = self.common._failover_volume(
self.data.test_legacy_vol, True, self.extra_specs)
self.assertEqual(ref_model_update, model_update)
def test_failover_volume_exception(self): def test_failover_volume_exception(self):
with mock.patch.object( with mock.patch.object(
self.provision, 'failover_volume', self.provision, 'failover_volume',
@ -6056,13 +6145,13 @@ class VMAXCommonReplicationTest(test.TestCase):
extra_specs1 = deepcopy(self.extra_specs) extra_specs1 = deepcopy(self.extra_specs)
extra_specs1[utils.DISABLECOMPRESSION] = "true" extra_specs1[utils.DISABLECOMPRESSION] = "true"
ref_specs1 = deepcopy(self.data.rep_extra_specs) ref_specs1 = deepcopy(self.data.rep_extra_specs)
ref_specs1['port_group_name'] = self.data.port_group_name_f ref_specs1[utils.PORTGROUPNAME] = self.data.port_group_name_f
rep_extra_specs1 = self.common._get_replication_extra_specs( rep_extra_specs1 = self.common._get_replication_extra_specs(
extra_specs1, rep_config) extra_specs1, rep_config)
self.assertEqual(ref_specs1, rep_extra_specs1) self.assertEqual(ref_specs1, rep_extra_specs1)
# Path two - disable compression, not all flash # Path two - disable compression, not all flash
ref_specs2 = deepcopy(self.data.rep_extra_specs) ref_specs2 = deepcopy(self.data.rep_extra_specs)
ref_specs2['port_group_name'] = self.data.port_group_name_f ref_specs2[utils.PORTGROUPNAME] = self.data.port_group_name_f
with mock.patch.object(self.rest, 'is_compression_capable', with mock.patch.object(self.rest, 'is_compression_capable',
return_value=False): return_value=False):
rep_extra_specs2 = self.common._get_replication_extra_specs( rep_extra_specs2 = self.common._get_replication_extra_specs(

View File

@ -281,11 +281,16 @@ class VMAXCommon(object):
""" """
LOG.debug("Entering create_volume_from_snapshot.") LOG.debug("Entering create_volume_from_snapshot.")
model_update = {} model_update = {}
extra_specs = self._initial_setup(snapshot) extra_specs = self._initial_setup(volume)
# Check if legacy snapshot
sourcedevice_id = self._find_device_on_array(
snapshot, extra_specs)
from_snapvx = False if sourcedevice_id else True
clone_dict = self._create_cloned_volume( clone_dict = self._create_cloned_volume(
volume, snapshot, extra_specs, is_snapshot=False, volume, snapshot, extra_specs, is_snapshot=False,
from_snapvx=True) from_snapvx=from_snapvx)
# Set-up volume replication, if enabled # Set-up volume replication, if enabled
if self.utils.is_replication_enabled(extra_specs): if self.utils.is_replication_enabled(extra_specs):
@ -305,7 +310,7 @@ class VMAXCommon(object):
:returns: model_update, dict :returns: model_update, dict
""" """
model_update = {} model_update = {}
extra_specs = self._initial_setup(source_volume) extra_specs = self._initial_setup(clone_volume)
clone_dict = self._create_cloned_volume(clone_volume, source_volume, clone_dict = self._create_cloned_volume(clone_volume, source_volume,
extra_specs) extra_specs)
@ -381,7 +386,15 @@ class VMAXCommon(object):
extra_specs = self._initial_setup(volume) extra_specs = self._initial_setup(volume)
sourcedevice_id, snap_name = self._parse_snap_info( sourcedevice_id, snap_name = self._parse_snap_info(
extra_specs[utils.ARRAY], snapshot) extra_specs[utils.ARRAY], snapshot)
if not sourcedevice_id or not snap_name: if not sourcedevice_id and not snap_name:
# Check if legacy snapshot
sourcedevice_id = self._find_device_on_array(
snapshot, extra_specs)
if sourcedevice_id:
self._delete_volume(snapshot)
else:
LOG.info("No snapshot found on the array")
elif not sourcedevice_id or not snap_name:
LOG.info("No snapshot found on the array") LOG.info("No snapshot found on the array")
else: else:
self.provision.delete_volume_snap_check_for_links( self.provision.delete_volume_snap_check_for_links(
@ -589,7 +602,7 @@ class VMAXCommon(object):
raise exception.VolumeBackendAPIException( raise exception.VolumeBackendAPIException(
data=exception_message) data=exception_message)
return device_info_dict, rollback_dict['port_group_name'] return device_info_dict, rollback_dict[utils.PORTGROUPNAME]
def terminate_connection(self, volume, connector): def terminate_connection(self, volume, connector):
"""Disallow connection from connector. """Disallow connection from connector.
@ -761,7 +774,7 @@ class VMAXCommon(object):
self.utils.get_default_oversubscription_ratio( self.utils.get_default_oversubscription_ratio(
max_oversubscription_ratio)) max_oversubscription_ratio))
pools.append(pool) pools.append(pool)
pools = self.utils.add_legacy_pools(pools)
data = {'vendor_name': "Dell EMC", data = {'vendor_name': "Dell EMC",
'driver_version': self.version, 'driver_version': self.version,
'storage_protocol': 'unknown', 'storage_protocol': 'unknown',
@ -861,10 +874,12 @@ class VMAXCommon(object):
if isinstance(loc, six.string_types): if isinstance(loc, six.string_types):
name = ast.literal_eval(loc) name = ast.literal_eval(loc)
array = extra_specs[utils.ARRAY] array = extra_specs[utils.ARRAY]
try: if name.get('device_id'):
device_id = name['device_id'] device_id = name['device_id']
except KeyError: elif name.get('keybindings'):
device_id = name['keybindings']['DeviceID'] device_id = name['keybindings']['DeviceID']
else:
device_id = None
element_name = self.utils.get_volume_element_name( element_name = self.utils.get_volume_element_name(
volume_name) volume_name)
admin_metadata = {} admin_metadata = {}
@ -1212,8 +1227,13 @@ class VMAXCommon(object):
if isinstance(loc, six.string_types): if isinstance(loc, six.string_types):
name = ast.literal_eval(loc) name = ast.literal_eval(loc)
try:
sourcedevice_id = name['source_id'] sourcedevice_id = name['source_id']
snap_name = name['snap_name'] snap_name = name['snap_name']
except KeyError:
LOG.info("Error retrieving snapshot details. Assuming "
"legacy structure of snapshot...")
return None, None
# Ensure snapvx is on the array. # Ensure snapvx is on the array.
try: try:
snap_details = self.rest.get_volume_snap( snap_details = self.rest.get_volume_snap(
@ -1358,8 +1378,8 @@ class VMAXCommon(object):
The pool_name extra spec must be set, otherwise a default slo/workload The pool_name extra spec must be set, otherwise a default slo/workload
will be chosen. The portgroup can either be passed as an extra spec will be chosen. The portgroup can either be passed as an extra spec
on the volume type (e.g. 'port_group_name = os-pg1-pg'), or can on the volume type (e.g. 'storagetype:portgroupname = os-pg1-pg'), or
be chosen from a list which must be provided in the xml file, e.g.: can be chosen from a list provided in the xml file, e.g.:
<PortGroups> <PortGroups>
<PortGroup>OS-PORTGROUP1-PG</PortGroup> <PortGroup>OS-PORTGROUP1-PG</PortGroup>
<PortGroup>OS-PORTGROUP2-PG</PortGroup> <PortGroup>OS-PORTGROUP2-PG</PortGroup>
@ -1376,8 +1396,9 @@ class VMAXCommon(object):
extra_specs[utils.PORTGROUPNAME] = pool_record['PortGroup'] extra_specs[utils.PORTGROUPNAME] = pool_record['PortGroup']
if not extra_specs[utils.PORTGROUPNAME]: if not extra_specs[utils.PORTGROUPNAME]:
error_message = (_("Port group name has not been provided - " error_message = (_("Port group name has not been provided - "
"please configure the 'port_group_name' extra " "please configure the "
"spec on the volume type, or enter a list of " "'storagetype:portgroupname' extra spec on "
"the volume type, or enter a list of "
"portgroups to the xml file associated with " "portgroups to the xml file associated with "
"this backend e.g." "this backend e.g."
"<PortGroups>" "<PortGroups>"
@ -1397,25 +1418,36 @@ class VMAXCommon(object):
# Set pool_name slo and workload # Set pool_name slo and workload
if 'pool_name' in extra_specs: if 'pool_name' in extra_specs:
pool_name = extra_specs['pool_name'] pool_name = extra_specs['pool_name']
else:
slo_list = self.rest.get_slo_list(pool_record['SerialNumber'])
if 'Optimized' in slo_list:
slo = 'Optimized'
elif 'Diamond' in slo_list:
slo = 'Diamond'
else:
slo = 'None'
pool_name = ("%(slo)s+%(workload)s+%(srpName)s+%(array)s"
% {'slo': slo,
'workload': 'None',
'srpName': pool_record['srpName'],
'array': pool_record['SerialNumber']})
LOG.warning("Pool_name is not present in the extra_specs "
"- using default pool %(pool_name)s.",
{'pool_name': pool_name})
pool_details = pool_name.split('+') pool_details = pool_name.split('+')
slo_from_extra_spec = pool_details[0] slo_from_extra_spec = pool_details[0]
workload_from_extra_spec = pool_details[1] workload_from_extra_spec = pool_details[1]
# Check if legacy pool chosen
if workload_from_extra_spec == pool_record['srpName']:
workload_from_extra_spec = 'NONE'
elif pool_record.get('ServiceLevel'):
slo_from_extra_spec = pool_record['ServiceLevel']
workload_from_extra_spec = pool_record.get('Workload', 'None')
LOG.info("Pool_name is not present in the extra_specs "
"- using slo/ workload from xml file: %(slo)s/%(wl)s.",
{'slo': slo_from_extra_spec,
'wl': workload_from_extra_spec})
else:
slo_list = self.rest.get_slo_list(pool_record['SerialNumber'])
if 'Optimized' in slo_list:
slo_from_extra_spec = 'Optimized'
elif 'Diamond' in slo_list:
slo_from_extra_spec = 'Diamond'
else:
slo_from_extra_spec = 'None'
workload_from_extra_spec = 'NONE'
LOG.warning("Pool_name is not present in the extra_specs"
"and no slo/ workload information is present "
"in the xml file - using default slo/ workload "
"combination: %(slo)s/%(wl)s.",
{'slo': slo_from_extra_spec,
'wl': workload_from_extra_spec})
# Standardize slo and workload 'NONE' naming conventions # Standardize slo and workload 'NONE' naming conventions
if workload_from_extra_spec.lower() == 'none': if workload_from_extra_spec.lower() == 'none':
workload_from_extra_spec = 'NONE' workload_from_extra_spec = 'NONE'
@ -1432,10 +1464,8 @@ class VMAXCommon(object):
else: else:
extra_specs.pop(utils.DISABLECOMPRESSION, None) extra_specs.pop(utils.DISABLECOMPRESSION, None)
LOG.debug("SRP is: %(srp)s " LOG.debug("SRP is: %(srp)s, Array is: %(array)s "
"Array is: %(array)s " "SLO is: %(slo)s, Workload is: %(workload)s.",
"SLO is: %(slo)s "
"Workload is: %(workload)s.",
{'srp': extra_specs[utils.SRP], {'srp': extra_specs[utils.SRP],
'array': extra_specs[utils.ARRAY], 'array': extra_specs[utils.ARRAY],
'slo': extra_specs[utils.SLO], 'slo': extra_specs[utils.SLO],
@ -2127,7 +2157,11 @@ class VMAXCommon(object):
if (isinstance(loc, six.string_types) if (isinstance(loc, six.string_types)
and isinstance(rep_data, six.string_types)): and isinstance(rep_data, six.string_types)):
name = ast.literal_eval(loc) name = ast.literal_eval(loc)
try:
array = name['array'] array = name['array']
except KeyError:
array = (name['keybindings']
['SystemName'].split('+')[1].strip('-'))
rep_extra_specs = self._get_replication_extra_specs( rep_extra_specs = self._get_replication_extra_specs(
extra_specs, self.rep_config) extra_specs, self.rep_config)
(target_device, remote_array, rdf_group_no, (target_device, remote_array, rdf_group_no,
@ -2320,7 +2354,11 @@ class VMAXCommon(object):
try: try:
name = ast.literal_eval(loc) name = ast.literal_eval(loc)
replication_keybindings = ast.literal_eval(rep_data) replication_keybindings = ast.literal_eval(rep_data)
try:
array = name['array'] array = name['array']
except KeyError:
array = (name['keybindings']
['SystemName'].split('+')[1].strip('-'))
device_id = self._find_device_on_array(vol, {utils.ARRAY: array}) device_id = self._find_device_on_array(vol, {utils.ARRAY: array})
(target_device, remote_array, rdf_group, (target_device, remote_array, rdf_group,

View File

@ -289,8 +289,12 @@ class VMAXFCDriver(driver.FibreChannelDriver):
loc = volume.provider_location loc = volume.provider_location
name = ast.literal_eval(loc) name = ast.literal_eval(loc)
host = connector['host'] host = connector['host']
try:
array = name['array'] array = name['array']
device_id = name['device_id'] device_id = name['device_id']
except KeyError:
array = name['keybindings']['SystemName'].split('+')[1].strip('-')
device_id = name['keybindings']['DeviceID']
LOG.debug("Start FC detach process for volume: %(volume)s.", LOG.debug("Start FC detach process for volume: %(volume)s.",
{'volume': volume.name}) {'volume': volume.name})

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from copy import deepcopy
import datetime import datetime
import hashlib import hashlib
import random import random
@ -45,7 +46,7 @@ ARRAY = 'array'
SLO = 'slo' SLO = 'slo'
WORKLOAD = 'workload' WORKLOAD = 'workload'
SRP = 'srp' SRP = 'srp'
PORTGROUPNAME = 'port_group_name' PORTGROUPNAME = 'storagetype:portgroupname'
DEVICE_ID = 'device_id' DEVICE_ID = 'device_id'
INITIATOR_CHECK = 'initiator_check' INITIATOR_CHECK = 'initiator_check'
SG_NAME = 'storagegroup_name' SG_NAME = 'storagegroup_name'
@ -340,6 +341,8 @@ class VMAXUtils(object):
if srp_name is None: if srp_name is None:
LOG.error("SRP Name must be in the file %(file)s.", LOG.error("SRP Name must be in the file %(file)s.",
{'file': file_name}) {'file': file_name})
slo = self._process_tag(dom, 'ServiceLevel')
workload = self._process_tag(dom, 'Workload')
kwargs = ( kwargs = (
{'RestServerIp': connargs['RestServerIp'], {'RestServerIp': connargs['RestServerIp'],
'RestServerPort': connargs['RestServerPort'], 'RestServerPort': connargs['RestServerPort'],
@ -350,6 +353,8 @@ class VMAXUtils(object):
'SerialNumber': serialnumber, 'SerialNumber': serialnumber,
'srpName': srp_name, 'srpName': srp_name,
'PortGroup': portgroup}) 'PortGroup': portgroup})
if slo is not None:
kwargs.update({'ServiceLevel': slo, 'Workload': workload})
except IndexError: except IndexError:
pass pass
@ -658,3 +663,25 @@ class VMAXUtils(object):
LOG.debug("Retries are set at: %(retries)s.", LOG.debug("Retries are set at: %(retries)s.",
{'retries': retries}) {'retries': retries})
return extra_specs return extra_specs
@staticmethod
def add_legacy_pools(pools):
"""Add legacy pools to allow extending a volume after upgrade.
:param pools: the pool list
:return: pools - the updated pool list
"""
extra_pools = []
for pool in pools:
if 'none' in pool['pool_name'].lower():
extra_pools.append(pool)
for pool in extra_pools:
slo = pool['pool_name'].split('+')[0]
srp = pool['pool_name'].split('+')[2]
array = pool['pool_name'].split('+')[3]
new_pool_name = ('%(slo)s+%(srp)s+%(array)s'
% {'slo': slo, 'srp': srp, 'array': array})
new_pool = deepcopy(pool)
new_pool['pool_name'] = new_pool_name
pools.append(new_pool)
return pools