Dell PowerMax Driver - Add support for Unisphere for PowerMax 10.0
Changes are indicated below: - endpoint uri added to support v10 U4P (version checked first) - 'fba_srp_capacity' instead of 'srp_capacity' - 'microcode' instead of 'ucode' Implements: blueprint powermax-v4-support Change-Id: Ie9561eeb30a54539cbdfa3e0152ac5383bb02ec6
This commit is contained in:
parent
acb8711dcf
commit
567f2a1b08
@ -79,7 +79,8 @@ class PowerMaxData(object):
|
|||||||
rdf_group_no_2 = '71'
|
rdf_group_no_2 = '71'
|
||||||
rdf_group_no_3 = '72'
|
rdf_group_no_3 = '72'
|
||||||
rdf_group_no_4 = '73'
|
rdf_group_no_4 = '73'
|
||||||
u4v_version = '92'
|
u4p_version = '92'
|
||||||
|
u4p_100_endpoint = '100'
|
||||||
storagegroup_name_source = 'Grp_source_sg'
|
storagegroup_name_source = 'Grp_source_sg'
|
||||||
storagegroup_name_target = 'Grp_target_sg'
|
storagegroup_name_target = 'Grp_target_sg'
|
||||||
group_snapshot_name = 'Grp_snapshot'
|
group_snapshot_name = 'Grp_snapshot'
|
||||||
@ -944,6 +945,9 @@ class PowerMaxData(object):
|
|||||||
powermax_model_details = {'symmetrixId': array,
|
powermax_model_details = {'symmetrixId': array,
|
||||||
'model': 'PowerMax_2000',
|
'model': 'PowerMax_2000',
|
||||||
'ucode': '5978.1091.1092'}
|
'ucode': '5978.1091.1092'}
|
||||||
|
powermax_model_100 = {'symmetrixId': array,
|
||||||
|
'model': 'PowerMax_2500',
|
||||||
|
'microcode': '6079.65.0'}
|
||||||
vmax_slo_details = {'sloId': ['Diamond', 'Optimized']}
|
vmax_slo_details = {'sloId': ['Diamond', 'Optimized']}
|
||||||
vmax_model_details = {'model': 'VMAX450F'}
|
vmax_model_details = {'model': 'VMAX450F'}
|
||||||
|
|
||||||
@ -1348,6 +1352,7 @@ class PowerMaxData(object):
|
|||||||
platform = 'Linux-4.4.0-104-generic-x86_64-with-Ubuntu-16.04-xenial'
|
platform = 'Linux-4.4.0-104-generic-x86_64-with-Ubuntu-16.04-xenial'
|
||||||
unisphere_version = u'V9.2.0.0'
|
unisphere_version = u'V9.2.0.0'
|
||||||
unisphere_version_90 = "V9.0.0.1"
|
unisphere_version_90 = "V9.0.0.1"
|
||||||
|
unisphere_version_100 = "V10.0.0.0"
|
||||||
openstack_release = '12.0.0.0b3.dev401'
|
openstack_release = '12.0.0.0b3.dev401'
|
||||||
openstack_version = '12.0.0'
|
openstack_version = '12.0.0'
|
||||||
python_version = '2.7.12'
|
python_version = '2.7.12'
|
||||||
|
@ -1802,6 +1802,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
array, portgroup_name, initiator_group_name)
|
array, portgroup_name, initiator_group_name)
|
||||||
|
|
||||||
def test_get_iscsi_ip_iqn_port(self):
|
def test_get_iscsi_ip_iqn_port(self):
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_100_endpoint
|
||||||
phys_port = '%(dir)s:%(port)s' % {'dir': self.data.iscsi_dir,
|
phys_port = '%(dir)s:%(port)s' % {'dir': self.data.iscsi_dir,
|
||||||
'port': self.data.iscsi_port}
|
'port': self.data.iscsi_port}
|
||||||
ref_ip_iqn = [{'iqn': self.data.initiator,
|
ref_ip_iqn = [{'iqn': self.data.initiator,
|
||||||
@ -1817,6 +1818,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.assertEqual(ref_ip_iqn, ip_iqn_list)
|
self.assertEqual(ref_ip_iqn, ip_iqn_list)
|
||||||
|
|
||||||
def test_find_ip_and_iqns(self):
|
def test_find_ip_and_iqns(self):
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_100_endpoint
|
||||||
ref_ip_iqn = [{'iqn': self.data.initiator,
|
ref_ip_iqn = [{'iqn': self.data.initiator,
|
||||||
'ip': self.data.ip,
|
'ip': self.data.ip,
|
||||||
'physical_port': self.data.iscsi_dir_port}]
|
'physical_port': self.data.iscsi_dir_port}]
|
||||||
|
@ -99,6 +99,7 @@ class PowerMaxISCSITest(test.TestCase):
|
|||||||
'iqn': self.data.initiator,
|
'iqn': self.data.initiator,
|
||||||
'physical_port': phys_port}],
|
'physical_port': phys_port}],
|
||||||
'is_multipath': False}
|
'is_multipath': False}
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_version
|
||||||
with mock.patch.object(self.driver, 'get_iscsi_dict') as mock_get:
|
with mock.patch.object(self.driver, 'get_iscsi_dict') as mock_get:
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.common, 'get_port_group_from_masking_view',
|
self.common, 'get_port_group_from_masking_view',
|
||||||
@ -109,6 +110,7 @@ class PowerMaxISCSITest(test.TestCase):
|
|||||||
ref_dict, self.data.test_volume)
|
ref_dict, self.data.test_volume)
|
||||||
|
|
||||||
def test_get_iscsi_dict_success(self):
|
def test_get_iscsi_dict_success(self):
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_version
|
||||||
ip_and_iqn = self.common._find_ip_and_iqns(
|
ip_and_iqn = self.common._find_ip_and_iqns(
|
||||||
self.data.array, self.data.port_group_name_i)
|
self.data.array, self.data.port_group_name_i)
|
||||||
host_lun_id = self.data.iscsi_device_info['hostlunid']
|
host_lun_id = self.data.iscsi_device_info['hostlunid']
|
||||||
@ -131,6 +133,7 @@ class PowerMaxISCSITest(test.TestCase):
|
|||||||
device_info, self.data.test_volume)
|
device_info, self.data.test_volume)
|
||||||
|
|
||||||
def test_get_iscsi_dict_metro(self):
|
def test_get_iscsi_dict_metro(self):
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_version
|
||||||
ip_and_iqn = self.common._find_ip_and_iqns(
|
ip_and_iqn = self.common._find_ip_and_iqns(
|
||||||
self.data.array, self.data.port_group_name_i)
|
self.data.array, self.data.port_group_name_i)
|
||||||
host_lun_id = self.data.iscsi_device_info_metro['hostlunid']
|
host_lun_id = self.data.iscsi_device_info_metro['hostlunid']
|
||||||
@ -148,6 +151,7 @@ class PowerMaxISCSITest(test.TestCase):
|
|||||||
|
|
||||||
def test_vmax_get_iscsi_properties_one_target_no_auth(self):
|
def test_vmax_get_iscsi_properties_one_target_no_auth(self):
|
||||||
vol = deepcopy(self.data.test_volume)
|
vol = deepcopy(self.data.test_volume)
|
||||||
|
self.common.rest.u4p_version = self.data.u4p_version
|
||||||
ip_and_iqn = self.common._find_ip_and_iqns(
|
ip_and_iqn = self.common._find_ip_and_iqns(
|
||||||
self.data.array, self.data.port_group_name_i)
|
self.data.array, self.data.port_group_name_i)
|
||||||
host_lun_id = self.data.iscsi_device_info['hostlunid']
|
host_lun_id = self.data.iscsi_device_info['hostlunid']
|
||||||
|
@ -862,20 +862,18 @@ class PowerMaxMaskingTest(test.TestCase):
|
|||||||
'_delete_cascaded_storage_groups')
|
'_delete_cascaded_storage_groups')
|
||||||
@mock.patch.object(rest.PowerMaxRest, 'get_num_vols_in_sg',
|
@mock.patch.object(rest.PowerMaxRest, 'get_num_vols_in_sg',
|
||||||
side_effect=[1, 3])
|
side_effect=[1, 3])
|
||||||
@mock.patch.object(rest.PowerMaxRest, 'delete_storage_group')
|
|
||||||
@mock.patch.object(masking.PowerMaxMasking, 'get_parent_sg_from_child',
|
@mock.patch.object(masking.PowerMaxMasking, 'get_parent_sg_from_child',
|
||||||
side_effect=[None, 'parent_sg_name', 'parent_sg_name'])
|
side_effect=[None, 'parent_sg_name', 'parent_sg_name'])
|
||||||
def test_last_vol_no_masking_views(
|
def test_last_vol_no_masking_views(
|
||||||
self, mock_get_parent, mock_delete, mock_num_vols,
|
self, mock_get_parent, mock_num_vols,
|
||||||
mock_delete_casc, mock_remove):
|
mock_delete_casc, mock_remove):
|
||||||
for x in range(0, 3):
|
for x in range(0, 3):
|
||||||
self.mask._last_vol_no_masking_views(
|
self.mask._last_vol_no_masking_views(
|
||||||
self.data.array, self.data.storagegroup_name_i,
|
self.data.array, self.data.storagegroup_name_i,
|
||||||
self.device_id, self.volume_name, self.extra_specs,
|
self.device_id, self.volume_name, self.extra_specs,
|
||||||
False)
|
False)
|
||||||
self.assertEqual(1, mock_delete.call_count)
|
|
||||||
self.assertEqual(1, mock_delete_casc.call_count)
|
self.assertEqual(1, mock_delete_casc.call_count)
|
||||||
self.assertEqual(1, mock_remove.call_count)
|
self.assertEqual(2, mock_remove.call_count)
|
||||||
|
|
||||||
@mock.patch.object(masking.PowerMaxMasking,
|
@mock.patch.object(masking.PowerMaxMasking,
|
||||||
'_remove_last_vol_and_delete_sg')
|
'_remove_last_vol_and_delete_sg')
|
||||||
|
@ -179,6 +179,8 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
'metro_hostlunid': 3}
|
'metro_hostlunid': 3}
|
||||||
self.assertEqual(ref_dict, info_dict)
|
self.assertEqual(ref_dict, info_dict)
|
||||||
|
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, 'get_ip_interface_physical_port',
|
||||||
|
return_value="FA-1D:1")
|
||||||
@mock.patch.object(rest.PowerMaxRest, 'get_iscsi_ip_address_and_iqn',
|
@mock.patch.object(rest.PowerMaxRest, 'get_iscsi_ip_address_and_iqn',
|
||||||
return_value=([tpd.PowerMaxData.ip],
|
return_value=([tpd.PowerMaxData.ip],
|
||||||
tpd.PowerMaxData.initiator))
|
tpd.PowerMaxData.initiator))
|
||||||
@ -187,7 +189,7 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
@mock.patch.object(utils.PowerMaxUtils, 'is_metro_device',
|
@mock.patch.object(utils.PowerMaxUtils, 'is_metro_device',
|
||||||
return_value=True)
|
return_value=True)
|
||||||
def test_initialize_connection_vol_metro_iscsi(self, mock_md, mock_es,
|
def test_initialize_connection_vol_metro_iscsi(self, mock_md, mock_es,
|
||||||
mock_ip):
|
mock_ip, mock_dp):
|
||||||
metro_connector = deepcopy(self.data.connector)
|
metro_connector = deepcopy(self.data.connector)
|
||||||
metro_connector['multipath'] = True
|
metro_connector['multipath'] = True
|
||||||
phys_port = '%(dir)s:%(port)s' % {
|
phys_port = '%(dir)s:%(port)s' % {
|
||||||
|
@ -48,7 +48,8 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
self.driver = driver
|
self.driver = driver
|
||||||
self.common = self.driver.common
|
self.common = self.driver.common
|
||||||
self.rest = self.common.rest
|
self.rest = self.common.rest
|
||||||
self.mock_object(self.rest, 'is_snap_id', True)
|
self.rest.is_snap_id = True
|
||||||
|
self.rest.u4p_version = rest.U4P_100_VERSION
|
||||||
self.utils = self.common.utils
|
self.utils = self.common.utils
|
||||||
|
|
||||||
def test_rest_request_no_response(self):
|
def test_rest_request_no_response(self):
|
||||||
@ -256,7 +257,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
|
|
||||||
def test_get_uni_version_success(self):
|
def test_get_uni_version_success(self):
|
||||||
ret_val = (200, tpd.PowerMaxData.version_details)
|
ret_val = (200, tpd.PowerMaxData.version_details)
|
||||||
current_major_version = tpd.PowerMaxData.u4v_version
|
current_major_version = tpd.PowerMaxData.u4p_version
|
||||||
with mock.patch.object(self.rest, 'request', return_value=ret_val):
|
with mock.patch.object(self.rest, 'request', return_value=ret_val):
|
||||||
version, major_version = self.rest.get_uni_version()
|
version, major_version = self.rest.get_uni_version()
|
||||||
self.assertIsNotNone(version)
|
self.assertIsNotNone(version)
|
||||||
@ -417,7 +418,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
},
|
},
|
||||||
"volume_size": 1,
|
"volume_size": 1,
|
||||||
"capacityUnit": "GB"}]}}}})
|
"capacityUnit": "GB"}]}}}})
|
||||||
version = self.data.u4v_version
|
endpoint_version = self.data.u4p_100_endpoint
|
||||||
with mock.patch.object(self.rest, 'modify_resource',
|
with mock.patch.object(self.rest, 'modify_resource',
|
||||||
return_value=(200,
|
return_value=(200,
|
||||||
return_message)) as mock_modify:
|
return_message)) as mock_modify:
|
||||||
@ -425,7 +426,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
array, storagegroup, payload)
|
array, storagegroup, payload)
|
||||||
mock_modify.assert_called_once_with(
|
mock_modify.assert_called_once_with(
|
||||||
self.data.array, 'sloprovisioning', 'storagegroup',
|
self.data.array, 'sloprovisioning', 'storagegroup',
|
||||||
payload, version, resource_name=storagegroup)
|
payload, endpoint_version, resource_name=storagegroup)
|
||||||
self.assertEqual(1, mock_modify.call_count)
|
self.assertEqual(1, mock_modify.call_count)
|
||||||
self.assertEqual(200, status_code)
|
self.assertEqual(200, status_code)
|
||||||
self.assertEqual(return_message, message)
|
self.assertEqual(return_message, message)
|
||||||
@ -1902,6 +1903,14 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
ucode = self.rest.get_array_ucode_version(array)
|
ucode = self.rest.get_array_ucode_version(array)
|
||||||
self.assertEqual(self.data.powermax_model_details['ucode'], ucode)
|
self.assertEqual(self.data.powermax_model_details['ucode'], ucode)
|
||||||
|
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, 'get_array_detail',
|
||||||
|
return_value=tpd.PowerMaxData.powermax_model_100)
|
||||||
|
def test_get_array_microcode(self, mck_ucode):
|
||||||
|
array = self.data.array
|
||||||
|
microcode = self.rest.get_array_ucode_version(array)
|
||||||
|
self.assertEqual(self.data.powermax_model_100.get(
|
||||||
|
'microcode'), microcode)
|
||||||
|
|
||||||
def test_validate_unisphere_version_suceess(self):
|
def test_validate_unisphere_version_suceess(self):
|
||||||
version = tpd.PowerMaxData.unisphere_version
|
version = tpd.PowerMaxData.unisphere_version
|
||||||
returned_version = {'version': version}
|
returned_version = {'version': version}
|
||||||
@ -1931,7 +1940,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
valid_version = self.rest.validate_unisphere_version()
|
valid_version = self.rest.validate_unisphere_version()
|
||||||
self.assertFalse(valid_version)
|
self.assertFalse(valid_version)
|
||||||
request_count = mock_req.call_count
|
request_count = mock_req.call_count
|
||||||
self.assertEqual(2, request_count)
|
self.assertEqual(1, request_count)
|
||||||
|
|
||||||
@mock.patch.object(rest.PowerMaxRest, 'get_resource',
|
@mock.patch.object(rest.PowerMaxRest, 'get_resource',
|
||||||
return_value=tpd.PowerMaxData.sg_rdf_group_details)
|
return_value=tpd.PowerMaxData.sg_rdf_group_details)
|
||||||
@ -2390,7 +2399,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
array_id, sg_name, rdf_group_no, rep_extra_specs)
|
array_id, sg_name, rdf_group_no, rep_extra_specs)
|
||||||
|
|
||||||
def test_validate_unisphere_version_unofficial_success(self):
|
def test_validate_unisphere_version_unofficial_success(self):
|
||||||
version = 'T9.2.0.1054'
|
version = 'x10.0.0.425'
|
||||||
returned_version = {'version': version}
|
returned_version = {'version': version}
|
||||||
with mock.patch.object(self.rest, "request",
|
with mock.patch.object(self.rest, "request",
|
||||||
return_value=(200,
|
return_value=(200,
|
||||||
@ -2402,6 +2411,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
|
|
||||||
def test_validate_unisphere_version_unofficial_failure(self):
|
def test_validate_unisphere_version_unofficial_failure(self):
|
||||||
version = 'T9.0.0.1054'
|
version = 'T9.0.0.1054'
|
||||||
|
self.rest.u4p_version = 'T9.0.0.1054'
|
||||||
returned_version = {'version': version}
|
returned_version = {'version': version}
|
||||||
with mock.patch.object(self.rest, "request",
|
with mock.patch.object(self.rest, "request",
|
||||||
return_value=(200,
|
return_value=(200,
|
||||||
@ -2410,7 +2420,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
self.assertFalse(valid_version)
|
self.assertFalse(valid_version)
|
||||||
|
|
||||||
def test_validate_unisphere_version_unofficial_greater_than(self):
|
def test_validate_unisphere_version_unofficial_greater_than(self):
|
||||||
version = 'T9.2.0.1054'
|
version = 'x10.0.0.425'
|
||||||
returned_version = {'version': version}
|
returned_version = {'version': version}
|
||||||
with mock.patch.object(self.rest, "request",
|
with mock.patch.object(self.rest, "request",
|
||||||
return_value=(200,
|
return_value=(200,
|
||||||
@ -2449,7 +2459,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
resource_name='test-sg')
|
resource_name='test-sg')
|
||||||
expected_uri = (
|
expected_uri = (
|
||||||
'/%(ver)s/sloprovisioning/symmetrix/%(arr)s/storagegroup/test-sg' %
|
'/%(ver)s/sloprovisioning/symmetrix/%(arr)s/storagegroup/test-sg' %
|
||||||
{'ver': rest.U4V_VERSION, 'arr': self.data.array})
|
{'ver': rest.U4P_100_VERSION, 'arr': self.data.array})
|
||||||
self.assertEqual(target_uri, expected_uri)
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
def test_build_uri_kwargs_private_no_version(self):
|
def test_build_uri_kwargs_private_no_version(self):
|
||||||
@ -2460,7 +2470,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
|
|
||||||
def test_build_uri_kwargs_public_version(self):
|
def test_build_uri_kwargs_public_version(self):
|
||||||
target_uri = self.rest._build_uri_kwargs(category='test')
|
target_uri = self.rest._build_uri_kwargs(category='test')
|
||||||
expected_uri = '/%(ver)s/test' % {'ver': rest.U4V_VERSION}
|
expected_uri = '/%(ver)s/test' % {'ver': rest.U4P_100_VERSION}
|
||||||
self.assertEqual(target_uri, expected_uri)
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
def test_build_uri_kwargs_full_uri(self):
|
def test_build_uri_kwargs_full_uri(self):
|
||||||
@ -2472,7 +2482,7 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
object_type='obj', object_type_id='id4')
|
object_type='obj', object_type_id='id4')
|
||||||
expected_uri = (
|
expected_uri = (
|
||||||
'/%(ver)s/test-cat/res-level/id1/res-type/id2/res/id3/obj/id4' % {
|
'/%(ver)s/test-cat/res-level/id1/res-type/id2/res/id3/obj/id4' % {
|
||||||
'ver': rest.U4V_VERSION})
|
'ver': rest.U4P_100_VERSION})
|
||||||
self.assertEqual(target_uri, expected_uri)
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
@mock.patch.object(
|
@mock.patch.object(
|
||||||
@ -2544,69 +2554,3 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
self.data.array, self.data.device_id,
|
self.data.array, self.data.device_id,
|
||||||
self.data.test_snapshot_snap_name)
|
self.data.test_snapshot_snap_name)
|
||||||
self.assertEqual('0', snap_id)
|
self.assertEqual('0', snap_id)
|
||||||
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_list')
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_rep',
|
|
||||||
side_effect=[{'rdf': False}, None])
|
|
||||||
def test_get_or_rename_storage_group_rep(
|
|
||||||
self, mock_sg_rep, mock_sg_list):
|
|
||||||
# Success - no need for rename
|
|
||||||
rep_info = self.rest.get_or_rename_storage_group_rep(
|
|
||||||
self.data.array, self.data.storagegroup_name_f,
|
|
||||||
self.data.extra_specs)
|
|
||||||
mock_sg_list.assert_not_called()
|
|
||||||
self.assertIsNotNone(rep_info)
|
|
||||||
|
|
||||||
# Fail - cannot find sg but no filter set
|
|
||||||
rep_info = self.rest.get_or_rename_storage_group_rep(
|
|
||||||
self.data.array, self.data.storagegroup_name_f,
|
|
||||||
self.data.extra_specs)
|
|
||||||
mock_sg_list.assert_not_called()
|
|
||||||
self.assertIsNone(rep_info)
|
|
||||||
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, '_rename_storage_group')
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_list',
|
|
||||||
return_value=({'storageGroupId': ['user-name+uuid']}))
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_rep',
|
|
||||||
side_effect=[None, ({'rdf': False}), ({'rdf': False})])
|
|
||||||
def test_get_or_rename_storage_group_rep_exists(
|
|
||||||
self, mock_sg_rep, mock_sg_list, mock_rename):
|
|
||||||
sg_filter = '<like>uuid'
|
|
||||||
rep_info = self.rest.get_or_rename_storage_group_rep(
|
|
||||||
self.data.array, self.data.storagegroup_name_f,
|
|
||||||
self.data.extra_specs, sg_filter=sg_filter)
|
|
||||||
mock_sg_list.assert_called_once_with(
|
|
||||||
self.data.array,
|
|
||||||
params={'storageGroupId': sg_filter})
|
|
||||||
group_list_return = {'storageGroupId': ['user-name+uuid']}
|
|
||||||
mock_rename.assert_called_once_with(
|
|
||||||
self.data.array,
|
|
||||||
group_list_return['storageGroupId'][0],
|
|
||||||
self.data.storagegroup_name_f,
|
|
||||||
self.data.extra_specs)
|
|
||||||
self.assertIsNotNone(rep_info)
|
|
||||||
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, '_rename_storage_group')
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_list',
|
|
||||||
return_value=({'storageGroupId': ['user-name+uuid']}))
|
|
||||||
@mock.patch.object(
|
|
||||||
rest.PowerMaxRest, 'get_storage_group_rep',
|
|
||||||
side_effect=[None, None])
|
|
||||||
def test_get_or_rename_storage_group_rep_does_not_exist(
|
|
||||||
self, mock_sg_rep, mock_sg_list, mock_rename):
|
|
||||||
sg_filter = '<like>uuid'
|
|
||||||
rep_info = self.rest.get_or_rename_storage_group_rep(
|
|
||||||
self.data.array, self.data.storagegroup_name_f,
|
|
||||||
self.data.extra_specs, sg_filter=sg_filter)
|
|
||||||
mock_sg_list.assert_called_once_with(
|
|
||||||
self.data.array,
|
|
||||||
params={'storageGroupId': sg_filter})
|
|
||||||
mock_rename.assert_not_called()
|
|
||||||
self.assertIsNone(rep_info)
|
|
||||||
|
@ -218,7 +218,6 @@ class PowerMaxCommon(object):
|
|||||||
self._get_u4p_failover_info()
|
self._get_u4p_failover_info()
|
||||||
self._gather_info()
|
self._gather_info()
|
||||||
self._get_performance_config()
|
self._get_performance_config()
|
||||||
self.rest.validate_unisphere_version()
|
|
||||||
|
|
||||||
def _gather_info(self):
|
def _gather_info(self):
|
||||||
"""Gather the relevant information for update_volume_stats."""
|
"""Gather the relevant information for update_volume_stats."""
|
||||||
@ -230,10 +229,13 @@ class PowerMaxCommon(object):
|
|||||||
"configuration and note that the xml file is no "
|
"configuration and note that the xml file is no "
|
||||||
"longer supported.")
|
"longer supported.")
|
||||||
self.rest.set_rest_credentials(array_info)
|
self.rest.set_rest_credentials(array_info)
|
||||||
|
self.rest.validate_unisphere_version()
|
||||||
|
|
||||||
if array_info:
|
if array_info:
|
||||||
serial_number = array_info['SerialNumber']
|
serial_number = array_info['SerialNumber']
|
||||||
self.array_model, self.next_gen = (
|
self.array_model, self.next_gen = (
|
||||||
self.rest.get_array_model_info(serial_number))
|
self.rest.get_array_model_info(serial_number))
|
||||||
|
self.rest.set_residuals(serial_number)
|
||||||
self.ucode_level = self.rest.get_array_ucode_version(serial_number)
|
self.ucode_level = self.rest.get_array_ucode_version(serial_number)
|
||||||
if self.replication_enabled:
|
if self.replication_enabled:
|
||||||
if serial_number in self.replication_targets:
|
if serial_number in self.replication_targets:
|
||||||
@ -857,9 +859,9 @@ class PowerMaxCommon(object):
|
|||||||
mv_list, sg_list = (
|
mv_list, sg_list = (
|
||||||
self._get_mvs_and_sgs_from_volume(
|
self._get_mvs_and_sgs_from_volume(
|
||||||
extra_specs[utils.ARRAY],
|
extra_specs[utils.ARRAY],
|
||||||
device_info['device_id']))
|
device_info.get('device_id')))
|
||||||
self.volume_metadata.capture_detach_info(
|
self.volume_metadata.capture_detach_info(
|
||||||
volume, extra_specs, device_info['device_id'], mv_list,
|
volume, extra_specs, device_info.get('device_id'), mv_list,
|
||||||
sg_list)
|
sg_list)
|
||||||
|
|
||||||
def _unmap_lun_promotion(self, volume, connector):
|
def _unmap_lun_promotion(self, volume, connector):
|
||||||
@ -1270,12 +1272,12 @@ class PowerMaxCommon(object):
|
|||||||
if rep_enabled:
|
if rep_enabled:
|
||||||
__, r2_array = self.get_rdf_details(array, rep_config)
|
__, r2_array = self.get_rdf_details(array, rep_config)
|
||||||
r2_ucode = self.rest.get_array_ucode_version(r2_array)
|
r2_ucode = self.rest.get_array_ucode_version(r2_array)
|
||||||
if int(r1_ucode[2]) > utils.UCODE_5978_ELMSR:
|
if self.utils.ode_capable(r1_ucode):
|
||||||
r1_ode_metro = True
|
r1_ode_metro = True
|
||||||
r2_ucode = r2_ucode.split('.')
|
r2_ucode = r2_ucode.split('.')
|
||||||
if self.rest.is_next_gen_array(r2_array):
|
if self.rest.is_next_gen_array(r2_array):
|
||||||
r2_ode = True
|
r2_ode = True
|
||||||
if int(r2_ucode[2]) > utils.UCODE_5978_ELMSR:
|
if self.utils.ode_capable(r2_ucode):
|
||||||
r2_ode_metro = True
|
r2_ode_metro = True
|
||||||
|
|
||||||
return r1_ode, r1_ode_metro, r2_ode, r2_ode_metro
|
return r1_ode, r1_ode_metro, r2_ode, r2_ode_metro
|
||||||
@ -3006,6 +3008,15 @@ class PowerMaxCommon(object):
|
|||||||
array, device_id)
|
array, device_id)
|
||||||
|
|
||||||
if snapvx_src or snapvx_tgt:
|
if snapvx_src or snapvx_tgt:
|
||||||
|
LOG.debug("Device %(dev)s is involved into a SnapVX session",
|
||||||
|
{'dev': device_id})
|
||||||
|
if snapvx_src:
|
||||||
|
LOG.debug("Device %(dev)s is the SnapVX source volume",
|
||||||
|
{'dev': device_id})
|
||||||
|
else:
|
||||||
|
LOG.debug("Device %(dev)s is the SnapVX target volume",
|
||||||
|
{'dev': device_id})
|
||||||
|
|
||||||
@coordination.synchronized("emc-source-{src_device_id}")
|
@coordination.synchronized("emc-source-{src_device_id}")
|
||||||
def do_unlink_and_delete_snap(src_device_id):
|
def do_unlink_and_delete_snap(src_device_id):
|
||||||
src_sessions, tgt_session = self.rest.find_snap_vx_sessions(
|
src_sessions, tgt_session = self.rest.find_snap_vx_sessions(
|
||||||
|
@ -1584,8 +1584,10 @@ class PowerMaxMasking(object):
|
|||||||
self.add_volume_to_default_storage_group(
|
self.add_volume_to_default_storage_group(
|
||||||
serial_number, device_id, volume_name,
|
serial_number, device_id, volume_name,
|
||||||
extra_specs, src_sg=storagegroup_name)
|
extra_specs, src_sg=storagegroup_name)
|
||||||
# Delete the storage group.
|
# Remove last volume and delete the storage group.
|
||||||
self.rest.delete_storage_group(serial_number, storagegroup_name)
|
self._remove_last_vol_and_delete_sg(
|
||||||
|
serial_number, device_id, volume_name,
|
||||||
|
storagegroup_name, extra_specs)
|
||||||
status = True
|
status = True
|
||||||
else:
|
else:
|
||||||
num_vols_parent = self.rest.get_num_vols_in_sg(
|
num_vols_parent = self.rest.get_num_vols_in_sg(
|
||||||
@ -1716,10 +1718,20 @@ class PowerMaxMasking(object):
|
|||||||
serial_number, device_id, "",
|
serial_number, device_id, "",
|
||||||
extra_specs, src_sg=child_sg_name)
|
extra_specs, src_sg=child_sg_name)
|
||||||
if child_sg_name != parent_sg_name:
|
if child_sg_name != parent_sg_name:
|
||||||
|
self.rest.remove_child_sg_from_parent_sg(
|
||||||
|
serial_number, child_sg_name, parent_sg_name,
|
||||||
|
extra_specs)
|
||||||
self.rest.delete_storage_group(serial_number, parent_sg_name)
|
self.rest.delete_storage_group(serial_number, parent_sg_name)
|
||||||
LOG.debug("Storage Group %(storagegroup_name)s "
|
LOG.debug("Storage Group %(storagegroup_name)s "
|
||||||
"successfully deleted.",
|
"successfully deleted.",
|
||||||
{'storagegroup_name': parent_sg_name})
|
{'storagegroup_name': parent_sg_name})
|
||||||
|
# Remove last volume and delete the storage group.
|
||||||
|
if self.rest.is_volume_in_storagegroup(
|
||||||
|
serial_number, device_id, child_sg_name):
|
||||||
|
self._remove_last_vol_and_delete_sg(
|
||||||
|
serial_number, device_id, 'last_vol',
|
||||||
|
child_sg_name, extra_specs)
|
||||||
|
else:
|
||||||
self.rest.delete_storage_group(serial_number, child_sg_name)
|
self.rest.delete_storage_group(serial_number, child_sg_name)
|
||||||
|
|
||||||
LOG.debug("Storage Group %(storagegroup_name)s successfully deleted.",
|
LOG.debug("Storage Group %(storagegroup_name)s successfully deleted.",
|
||||||
|
@ -133,7 +133,8 @@ class PowerMaxVolumeMetadata(object):
|
|||||||
self.version_dict['serial_number'] = serial_number
|
self.version_dict['serial_number'] = serial_number
|
||||||
array_info_dict = self.rest.get_array_detail(serial_number)
|
array_info_dict = self.rest.get_array_detail(serial_number)
|
||||||
self.version_dict['storage_firmware_version'] = (
|
self.version_dict['storage_firmware_version'] = (
|
||||||
array_info_dict['ucode'])
|
array_info_dict.get(
|
||||||
|
'ucode', array_info_dict.get('microcode')))
|
||||||
self.version_dict['storage_model'] = array_info_dict['model']
|
self.version_dict['storage_model'] = array_info_dict['model']
|
||||||
self.version_dict['powermax_cinder_driver_version'] = (
|
self.version_dict['powermax_cinder_driver_version'] = (
|
||||||
self.powermax_driver_version)
|
self.powermax_driver_version)
|
||||||
|
@ -475,6 +475,16 @@ class PowerMaxProvision(object):
|
|||||||
subscribed_capacity_gb = (
|
subscribed_capacity_gb = (
|
||||||
srp_capacity['subscribed_total_tb'] * units.Ki)
|
srp_capacity['subscribed_total_tb'] * units.Ki)
|
||||||
array_reserve_percent = srp_details['reserved_cap_percent']
|
array_reserve_percent = srp_details['reserved_cap_percent']
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
srp_capacity = srp_details['fba_srp_capacity']
|
||||||
|
effective_capacity = srp_capacity['effective']
|
||||||
|
total_capacity_gb = effective_capacity['total_tb'] * units.Ki
|
||||||
|
remaining_capacity_gb = (
|
||||||
|
effective_capacity['free_tb'] * units.Ki)
|
||||||
|
array_reserve_percent = srp_details['reserved_cap_percent']
|
||||||
|
subscribed_capacity_gb = (
|
||||||
|
effective_capacity['used_tb'] * units.Ki)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -36,8 +36,10 @@ LOG = logging.getLogger(__name__)
|
|||||||
SLOPROVISIONING = 'sloprovisioning'
|
SLOPROVISIONING = 'sloprovisioning'
|
||||||
REPLICATION = 'replication'
|
REPLICATION = 'replication'
|
||||||
SYSTEM = 'system'
|
SYSTEM = 'system'
|
||||||
U4V_VERSION = '92'
|
U4P_100_VERSION = '100'
|
||||||
MIN_U4P_VERSION = '9.2.0.0'
|
MIN_U4P_100_VERSION = '10.0.0.0'
|
||||||
|
U4P_92_VERSION = '92'
|
||||||
|
MIN_U4P_92_VERSION = '9.2.0.0'
|
||||||
UCODE_5978 = '5978'
|
UCODE_5978 = '5978'
|
||||||
retry_exc_tuple = (exception.VolumeBackendAPIException,)
|
retry_exc_tuple = (exception.VolumeBackendAPIException,)
|
||||||
u4p_failover_max_wait = 120
|
u4p_failover_max_wait = 120
|
||||||
@ -60,6 +62,11 @@ SUCCEEDED = 'succeeded'
|
|||||||
CREATE_VOL_STRING = "Creating new Volumes"
|
CREATE_VOL_STRING = "Creating new Volumes"
|
||||||
POPULATE_SG_LIST = "Populating Storage Group(s) with volumes"
|
POPULATE_SG_LIST = "Populating Storage Group(s) with volumes"
|
||||||
|
|
||||||
|
# Sequence of beta microcode (in order)
|
||||||
|
DEV_CODE = 'x'
|
||||||
|
TEST_CODE = 't'
|
||||||
|
QUAL_CODE = 'v'
|
||||||
|
|
||||||
|
|
||||||
class PowerMaxRest(object):
|
class PowerMaxRest(object):
|
||||||
"""Rest class based on Unisphere for PowerMax Rest API."""
|
"""Rest class based on Unisphere for PowerMax Rest API."""
|
||||||
@ -85,6 +92,7 @@ class PowerMaxRest(object):
|
|||||||
self.ucode_major_level = None
|
self.ucode_major_level = None
|
||||||
self.ucode_minor_level = None
|
self.ucode_minor_level = None
|
||||||
self.is_snap_id = False
|
self.is_snap_id = False
|
||||||
|
self.u4p_version = None
|
||||||
|
|
||||||
def set_rest_credentials(self, array_info):
|
def set_rest_credentials(self, array_info):
|
||||||
"""Given the array record set the rest server credentials.
|
"""Given the array record set the rest server credentials.
|
||||||
@ -100,12 +108,18 @@ class PowerMaxRest(object):
|
|||||||
self.base_uri = ("https://%(ip_port)s/univmax/restapi" % {
|
self.base_uri = ("https://%(ip_port)s/univmax/restapi" % {
|
||||||
'ip_port': ip_port})
|
'ip_port': ip_port})
|
||||||
self.session = self._establish_rest_session()
|
self.session = self._establish_rest_session()
|
||||||
|
|
||||||
|
def set_residuals(self, serial_number):
|
||||||
|
"""Set ucode and snapid information.
|
||||||
|
|
||||||
|
:param serial_number: the array serial number
|
||||||
|
"""
|
||||||
self.ucode_major_level, self.ucode_minor_level = (
|
self.ucode_major_level, self.ucode_minor_level = (
|
||||||
self.get_major_minor_ucode(array_info['SerialNumber']))
|
self.get_major_minor_ucode(serial_number))
|
||||||
self.is_snap_id = self._is_snapid_enabled()
|
self.is_snap_id = self._is_snapid_enabled()
|
||||||
|
|
||||||
def set_u4p_failover_config(self, failover_info):
|
def set_u4p_failover_config(self, failover_info):
|
||||||
"""Set the environment failover Unisphere targets and configuration..
|
"""Set the environment failover Unisphere targets and configuration.
|
||||||
|
|
||||||
:param failover_info: failover target record
|
:param failover_info: failover target record
|
||||||
"""
|
"""
|
||||||
@ -372,7 +386,7 @@ class PowerMaxRest(object):
|
|||||||
rc -- int, status -- string, task -- list of dicts
|
rc -- int, status -- string, task -- list of dicts
|
||||||
"""
|
"""
|
||||||
complete, rc, status, result, task = False, 0, None, None, None
|
complete, rc, status, result, task = False, 0, None, None, None
|
||||||
job_url = "/%s/system/job/%s" % (U4V_VERSION, job_id)
|
job_url = "/%s/system/job/%s" % (self.u4p_version, job_id)
|
||||||
job = self.get_request(job_url, 'job')
|
job = self.get_request(job_url, 'job')
|
||||||
if job:
|
if job:
|
||||||
status = job['status']
|
status = job['status']
|
||||||
@ -448,8 +462,7 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
return target_uri
|
return target_uri
|
||||||
|
|
||||||
@staticmethod
|
def _build_uri_legacy_args(self, *args, **kwargs):
|
||||||
def _build_uri_legacy_args(*args, **kwargs):
|
|
||||||
"""Build the target URI using legacy args & kwargs.
|
"""Build the target URI using legacy args & kwargs.
|
||||||
|
|
||||||
Expected format:
|
Expected format:
|
||||||
@ -458,7 +471,7 @@ class PowerMaxRest(object):
|
|||||||
arg[2]: the resource type e.g. 'maskingview' -- str
|
arg[2]: the resource type e.g. 'maskingview' -- str
|
||||||
kwarg resource_name: the name of a specific resource -- str
|
kwarg resource_name: the name of a specific resource -- str
|
||||||
kwarg private: if endpoint is private -- bool
|
kwarg private: if endpoint is private -- bool
|
||||||
kwarg version: U4V REST endpoint version -- int/str
|
kwarg version: U4P REST endpoint version -- int/str
|
||||||
kwarg no_version: if endpoint should be versionless -- bool
|
kwarg no_version: if endpoint should be versionless -- bool
|
||||||
|
|
||||||
:param args: input args -- see above
|
:param args: input args -- see above
|
||||||
@ -470,7 +483,7 @@ class PowerMaxRest(object):
|
|||||||
# Extract keyword args following legacy _build_uri() format
|
# Extract keyword args following legacy _build_uri() format
|
||||||
resource_name = kwargs.get('resource_name')
|
resource_name = kwargs.get('resource_name')
|
||||||
private = kwargs.get('private')
|
private = kwargs.get('private')
|
||||||
version = kwargs.get('version', U4V_VERSION)
|
version = kwargs.get('version', self.u4p_version)
|
||||||
if kwargs.get('no_version'):
|
if kwargs.get('no_version'):
|
||||||
version = None
|
version = None
|
||||||
|
|
||||||
@ -489,8 +502,7 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
return target_uri
|
return target_uri
|
||||||
|
|
||||||
@staticmethod
|
def _build_uri_kwargs(self, **kwargs):
|
||||||
def _build_uri_kwargs(**kwargs):
|
|
||||||
"""Build the target URI using kwargs.
|
"""Build the target URI using kwargs.
|
||||||
|
|
||||||
Expected kwargs:
|
Expected kwargs:
|
||||||
@ -517,7 +529,7 @@ class PowerMaxRest(object):
|
|||||||
:param kwargs: input keyword args -- see above
|
:param kwargs: input keyword args -- see above
|
||||||
:return: target URI -- str
|
:return: target URI -- str
|
||||||
"""
|
"""
|
||||||
version = kwargs.get('version', U4V_VERSION)
|
version = kwargs.get('version', self.u4p_version)
|
||||||
if kwargs.get('no_version'):
|
if kwargs.get('no_version'):
|
||||||
version = None
|
version = None
|
||||||
|
|
||||||
@ -607,7 +619,7 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
def get_resource(self, array, category, resource_type,
|
def get_resource(self, array, category, resource_type,
|
||||||
resource_name=None, params=None, private=False,
|
resource_name=None, params=None, private=False,
|
||||||
version=U4V_VERSION):
|
version=None):
|
||||||
"""Get resource details from array.
|
"""Get resource details from array.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
@ -621,7 +633,7 @@ class PowerMaxRest(object):
|
|||||||
"""
|
"""
|
||||||
target_uri = self.build_uri(
|
target_uri = self.build_uri(
|
||||||
array, category, resource_type, resource_name=resource_name,
|
array, category, resource_type, resource_name=resource_name,
|
||||||
private=private, version=version)
|
private=private, version=self.u4p_version)
|
||||||
return self.get_request(target_uri, resource_type, params)
|
return self.get_request(target_uri, resource_type, params)
|
||||||
|
|
||||||
def create_resource(self, array, category, resource_type, payload,
|
def create_resource(self, array, category, resource_type, payload,
|
||||||
@ -645,7 +657,7 @@ class PowerMaxRest(object):
|
|||||||
return status_code, message
|
return status_code, message
|
||||||
|
|
||||||
def modify_resource(
|
def modify_resource(
|
||||||
self, array, category, resource_type, payload, version=U4V_VERSION,
|
self, array, category, resource_type, payload, version=None,
|
||||||
resource_name=None, private=False):
|
resource_name=None, private=False):
|
||||||
"""Modify a resource.
|
"""Modify a resource.
|
||||||
|
|
||||||
@ -660,7 +672,7 @@ class PowerMaxRest(object):
|
|||||||
"""
|
"""
|
||||||
target_uri = self.build_uri(
|
target_uri = self.build_uri(
|
||||||
array, category, resource_type, resource_name=resource_name,
|
array, category, resource_type, resource_name=resource_name,
|
||||||
private=private, version=version)
|
private=private, version=self.u4p_version)
|
||||||
status_code, message = self.request(target_uri, PUT,
|
status_code, message = self.request(target_uri, PUT,
|
||||||
request_object=payload)
|
request_object=payload)
|
||||||
operation = 'modify %(res)s resource' % {'res': resource_type}
|
operation = 'modify %(res)s resource' % {'res': resource_type}
|
||||||
@ -695,7 +707,7 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
:returns arrays -- list
|
:returns arrays -- list
|
||||||
"""
|
"""
|
||||||
target_uri = '/%s/sloprovisioning/symmetrix' % U4V_VERSION
|
target_uri = '/%s/sloprovisioning/symmetrix' % self.u4p_version
|
||||||
array_details = self.get_request(target_uri, 'sloprovisioning')
|
array_details = self.get_request(target_uri, 'sloprovisioning')
|
||||||
if not array_details:
|
if not array_details:
|
||||||
LOG.error("Could not get array details from Unisphere instance.")
|
LOG.error("Could not get array details from Unisphere instance.")
|
||||||
@ -708,7 +720,7 @@ class PowerMaxRest(object):
|
|||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
:returns: array_details -- dict or None
|
:returns: array_details -- dict or None
|
||||||
"""
|
"""
|
||||||
target_uri = '/%s/system/symmetrix/%s' % (U4V_VERSION, array)
|
target_uri = '/%s/system/symmetrix/%s' % (self.u4p_version, array)
|
||||||
array_details = self.get_request(target_uri, 'system')
|
array_details = self.get_request(target_uri, 'system')
|
||||||
if not array_details:
|
if not array_details:
|
||||||
LOG.error("Cannot connect to array %(array)s.",
|
LOG.error("Cannot connect to array %(array)s.",
|
||||||
@ -721,7 +733,8 @@ class PowerMaxRest(object):
|
|||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
:returns: tag list -- list or empty list
|
:returns: tag list -- list or empty list
|
||||||
"""
|
"""
|
||||||
target_uri = '/%s/system/tag?array_id=%s' % (U4V_VERSION, array)
|
target_uri = '/%s/system/tag?array_id=%s' % (
|
||||||
|
self.u4p_version, array)
|
||||||
array_tags = self.get_request(target_uri, 'system')
|
array_tags = self.get_request(target_uri, 'system')
|
||||||
return array_tags.get('tag_name')
|
return array_tags.get('tag_name')
|
||||||
|
|
||||||
@ -734,7 +747,8 @@ class PowerMaxRest(object):
|
|||||||
is_next_gen = False
|
is_next_gen = False
|
||||||
array_details = self.get_array_detail(array)
|
array_details = self.get_array_detail(array)
|
||||||
if array_details:
|
if array_details:
|
||||||
ucode_version = array_details['ucode'].split('.')[0]
|
ucode = array_details.get('ucode', array_details.get('microcode'))
|
||||||
|
ucode_version = ucode.split('.')[0] if ucode else None
|
||||||
if ucode_version >= UCODE_5978:
|
if ucode_version >= UCODE_5978:
|
||||||
is_next_gen = True
|
is_next_gen = True
|
||||||
return is_next_gen
|
return is_next_gen
|
||||||
@ -749,7 +763,7 @@ class PowerMaxRest(object):
|
|||||||
if response and response.get('version'):
|
if response and response.get('version'):
|
||||||
version = response['version']
|
version = response['version']
|
||||||
version_list = version.split('.')
|
version_list = version.split('.')
|
||||||
major_version = version_list[0][1] + version_list[1]
|
major_version = version_list[0][1:] + version_list[1]
|
||||||
return version, major_version
|
return version, major_version
|
||||||
|
|
||||||
def get_unisphere_version(self):
|
def get_unisphere_version(self):
|
||||||
@ -757,12 +771,8 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
:returns: version dict
|
:returns: version dict
|
||||||
"""
|
"""
|
||||||
post_90_endpoint = '/version'
|
version_endpoint = '/version'
|
||||||
pre_91_endpoint = '/system/version'
|
status_code, version_dict = self.request(version_endpoint, GET)
|
||||||
|
|
||||||
status_code, version_dict = self.request(post_90_endpoint, GET)
|
|
||||||
if status_code is not STATUS_200:
|
|
||||||
status_code, version_dict = self.request(pre_91_endpoint, GET)
|
|
||||||
|
|
||||||
if not version_dict:
|
if not version_dict:
|
||||||
LOG.error("Unisphere version info not found.")
|
LOG.error("Unisphere version info not found.")
|
||||||
@ -844,7 +854,8 @@ class PowerMaxRest(object):
|
|||||||
if system_info and system_info.get('model'):
|
if system_info and system_info.get('model'):
|
||||||
array_model = system_info.get('model')
|
array_model = system_info.get('model')
|
||||||
if system_info:
|
if system_info:
|
||||||
ucode_version = system_info['ucode'].split('.')[0]
|
ucode = system_info.get('ucode', system_info.get('microcode'))
|
||||||
|
ucode_version = ucode.split('.')[0]
|
||||||
if ucode_version >= UCODE_5978:
|
if ucode_version >= UCODE_5978:
|
||||||
is_next_gen = True
|
is_next_gen = True
|
||||||
return array_model, is_next_gen
|
return array_model, is_next_gen
|
||||||
@ -858,7 +869,8 @@ class PowerMaxRest(object):
|
|||||||
ucode_version = None
|
ucode_version = None
|
||||||
system_info = self.get_array_detail(array)
|
system_info = self.get_array_detail(array)
|
||||||
if system_info:
|
if system_info:
|
||||||
ucode_version = system_info['ucode']
|
ucode_version = system_info.get(
|
||||||
|
'ucode', system_info.get('microcode'))
|
||||||
return ucode_version
|
return ucode_version
|
||||||
|
|
||||||
def is_compression_capable(self, array):
|
def is_compression_capable(self, array):
|
||||||
@ -869,7 +881,7 @@ class PowerMaxRest(object):
|
|||||||
"""
|
"""
|
||||||
is_compression_capable = False
|
is_compression_capable = False
|
||||||
target_uri = ("/%s/sloprovisioning/symmetrix?compressionCapable=true"
|
target_uri = ("/%s/sloprovisioning/symmetrix?compressionCapable=true"
|
||||||
% U4V_VERSION)
|
% self.u4p_version)
|
||||||
status_code, message = self.request(target_uri, GET)
|
status_code, message = self.request(target_uri, GET)
|
||||||
self.check_status_code_success(
|
self.check_status_code_success(
|
||||||
"Check if compression enabled", status_code, message)
|
"Check if compression enabled", status_code, message)
|
||||||
@ -1018,7 +1030,7 @@ class PowerMaxRest(object):
|
|||||||
return storagegroup_name
|
return storagegroup_name
|
||||||
|
|
||||||
def modify_storage_group(self, array, storagegroup, payload,
|
def modify_storage_group(self, array, storagegroup, payload,
|
||||||
version=U4V_VERSION):
|
version=None):
|
||||||
"""Modify a storage group (PUT operation).
|
"""Modify a storage group (PUT operation).
|
||||||
|
|
||||||
:param version: the uv4 version
|
:param version: the uv4 version
|
||||||
@ -1028,8 +1040,8 @@ class PowerMaxRest(object):
|
|||||||
:returns: status_code -- int, message -- string, server response
|
:returns: status_code -- int, message -- string, server response
|
||||||
"""
|
"""
|
||||||
return self.modify_resource(
|
return self.modify_resource(
|
||||||
array, SLOPROVISIONING, 'storagegroup', payload, version,
|
array, SLOPROVISIONING, 'storagegroup', payload,
|
||||||
resource_name=storagegroup)
|
self.u4p_version, resource_name=storagegroup)
|
||||||
|
|
||||||
def modify_storage_array(self, array, payload):
|
def modify_storage_array(self, array, payload):
|
||||||
"""Modify a storage array (PUT operation).
|
"""Modify a storage array (PUT operation).
|
||||||
@ -1038,7 +1050,8 @@ class PowerMaxRest(object):
|
|||||||
:param payload: the request payload
|
:param payload: the request payload
|
||||||
:returns: status_code -- int, message -- string, server response
|
:returns: status_code -- int, message -- string, server response
|
||||||
"""
|
"""
|
||||||
target_uri = '/%s/sloprovisioning/symmetrix/%s' % (U4V_VERSION, array)
|
target_uri = '/%s/sloprovisioning/symmetrix/%s' % (
|
||||||
|
self.u4p_version, array)
|
||||||
status_code, message = self.request(target_uri, PUT,
|
status_code, message = self.request(target_uri, PUT,
|
||||||
request_object=payload)
|
request_object=payload)
|
||||||
operation = 'modify %(res)s resource' % {'res': 'symmetrix'}
|
operation = 'modify %(res)s resource' % {'res': 'symmetrix'}
|
||||||
@ -1417,13 +1430,8 @@ class PowerMaxRest(object):
|
|||||||
:returns: volume dict
|
:returns: volume dict
|
||||||
:raises: VolumeBackendAPIException
|
:raises: VolumeBackendAPIException
|
||||||
"""
|
"""
|
||||||
if not device_id:
|
|
||||||
LOG.warning('No device id supplied to get_volume.')
|
|
||||||
return dict()
|
|
||||||
version = self.get_uni_version()[1]
|
|
||||||
volume_dict = self.get_resource(
|
volume_dict = self.get_resource(
|
||||||
array, SLOPROVISIONING, 'volume', resource_name=device_id,
|
array, SLOPROVISIONING, 'volume', resource_name=device_id)
|
||||||
version=version)
|
|
||||||
if not volume_dict:
|
if not volume_dict:
|
||||||
exception_message = (_("Volume %(deviceID)s not found.")
|
exception_message = (_("Volume %(deviceID)s not found.")
|
||||||
% {'deviceID': device_id})
|
% {'deviceID': device_id})
|
||||||
@ -1570,7 +1578,8 @@ class PowerMaxRest(object):
|
|||||||
raise exception.VolumeBackendAPIException(exception_message)
|
raise exception.VolumeBackendAPIException(exception_message)
|
||||||
|
|
||||||
if ((self.ucode_major_level >= utils.UCODE_5978)
|
if ((self.ucode_major_level >= utils.UCODE_5978)
|
||||||
and (self.ucode_minor_level > utils.UCODE_5978_ELMSR)):
|
and (self.ucode_minor_level > utils.UCODE_5978_ELMSR) or (
|
||||||
|
self.ucode_major_level >= utils.UCODE_6079)):
|
||||||
# Use Rapid TDEV Deallocation to delete after ELMSR
|
# Use Rapid TDEV Deallocation to delete after ELMSR
|
||||||
try:
|
try:
|
||||||
self.delete_resource(array, SLOPROVISIONING,
|
self.delete_resource(array, SLOPROVISIONING,
|
||||||
@ -1807,13 +1816,23 @@ class PowerMaxRest(object):
|
|||||||
:param ip_address: the ip address associated with the port -- str
|
:param ip_address: the ip address associated with the port -- str
|
||||||
:returns: physical director:port -- str
|
:returns: physical director:port -- str
|
||||||
"""
|
"""
|
||||||
|
if self.u4p_version == U4P_100_VERSION:
|
||||||
|
target_key = 'iscsi_endpoint'
|
||||||
|
elif self.u4p_version == U4P_92_VERSION:
|
||||||
|
target_key = 'iscsi_target'
|
||||||
|
else:
|
||||||
|
msg = (_(
|
||||||
|
"Unable to determine the target_key for version %(ver)s." % {
|
||||||
|
'ver': self.u4p_version}
|
||||||
|
))
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(message=msg)
|
||||||
director_id = virtual_port.split(':')[0]
|
director_id = virtual_port.split(':')[0]
|
||||||
params = {'ip_list': ip_address, 'iscsi_target': False}
|
params = {'ip_list': ip_address, target_key: False}
|
||||||
target_uri = self.build_uri(
|
target_uri = self.build_uri(
|
||||||
category=SYSTEM, resource_level='symmetrix',
|
category=SYSTEM, resource_level='symmetrix',
|
||||||
resource_level_id=array_id, resource_type='director',
|
resource_level_id=array_id, resource_type='director',
|
||||||
resource_type_id=director_id, resource='port')
|
resource_type_id=director_id, resource='port')
|
||||||
|
|
||||||
port_info = self.get_request(
|
port_info = self.get_request(
|
||||||
target_uri, 'port IP interface', params)
|
target_uri, 'port IP interface', params)
|
||||||
if not port_info:
|
if not port_info:
|
||||||
@ -2110,7 +2129,7 @@ class PowerMaxRest(object):
|
|||||||
"""
|
"""
|
||||||
array_capabilities = None
|
array_capabilities = None
|
||||||
target_uri = ("/%s/replication/capabilities/symmetrix"
|
target_uri = ("/%s/replication/capabilities/symmetrix"
|
||||||
% U4V_VERSION)
|
% self.u4p_version)
|
||||||
capabilities = self.get_request(
|
capabilities = self.get_request(
|
||||||
target_uri, 'replication capabilities')
|
target_uri, 'replication capabilities')
|
||||||
if capabilities:
|
if capabilities:
|
||||||
@ -3449,19 +3468,25 @@ class PowerMaxRest(object):
|
|||||||
:returns: unisphere_meets_min_req -- boolean
|
:returns: unisphere_meets_min_req -- boolean
|
||||||
"""
|
"""
|
||||||
running_version, major_version = self.get_uni_version()
|
running_version, major_version = self.get_uni_version()
|
||||||
minimum_version = MIN_U4P_VERSION
|
if major_version == U4P_100_VERSION:
|
||||||
|
self.u4p_version = U4P_100_VERSION
|
||||||
|
minimum_version = MIN_U4P_100_VERSION
|
||||||
|
elif major_version:
|
||||||
|
self.u4p_version = U4P_92_VERSION
|
||||||
|
minimum_version = MIN_U4P_92_VERSION
|
||||||
unisphere_meets_min_req = False
|
unisphere_meets_min_req = False
|
||||||
|
|
||||||
if running_version and (running_version[0].isalpha()):
|
if running_version and (running_version[0].isalpha()):
|
||||||
# remove leading letter
|
# remove leading letter
|
||||||
if running_version.lower()[0] == 'v':
|
if running_version.lower()[0] == QUAL_CODE:
|
||||||
version = running_version[1:]
|
version = running_version[1:]
|
||||||
unisphere_meets_min_req = (
|
unisphere_meets_min_req = (
|
||||||
self.utils.version_meet_req(version, minimum_version))
|
self.utils.version_meet_req(version, minimum_version))
|
||||||
elif running_version.lower()[0] == 't':
|
elif running_version.lower()[0] == TEST_CODE or (
|
||||||
|
running_version.lower()[0] == DEV_CODE):
|
||||||
LOG.warning("%(version)s This is not a official release of "
|
LOG.warning("%(version)s This is not a official release of "
|
||||||
"Unisphere.", {'version': running_version})
|
"Unisphere.", {'version': running_version})
|
||||||
return major_version >= U4V_VERSION
|
return int(major_version) >= int(self.u4p_version)
|
||||||
|
|
||||||
if unisphere_meets_min_req:
|
if unisphere_meets_min_req:
|
||||||
LOG.info("Unisphere version %(running_version)s meets minimum "
|
LOG.info("Unisphere version %(running_version)s meets minimum "
|
||||||
@ -3522,7 +3547,8 @@ class PowerMaxRest(object):
|
|||||||
ucode_minor_level = 0
|
ucode_minor_level = 0
|
||||||
|
|
||||||
if array_details:
|
if array_details:
|
||||||
split_ucode_level = array_details['ucode'].split('.')
|
ucode = array_details.get('ucode', array_details.get('microcode'))
|
||||||
|
split_ucode_level = ucode.split('.')
|
||||||
ucode_level = [int(level) for level in split_ucode_level]
|
ucode_level = [int(level) for level in split_ucode_level]
|
||||||
ucode_major_level = ucode_level[0]
|
ucode_major_level = ucode_level[0]
|
||||||
ucode_minor_level = ucode_level[1]
|
ucode_minor_level = ucode_level[1]
|
||||||
@ -3534,21 +3560,5 @@ class PowerMaxRest(object):
|
|||||||
:returns: boolean
|
:returns: boolean
|
||||||
"""
|
"""
|
||||||
return (self.ucode_major_level >= utils.UCODE_5978 and
|
return (self.ucode_major_level >= utils.UCODE_5978 and
|
||||||
self.ucode_minor_level >= utils.UCODE_5978_HICKORY)
|
self.ucode_minor_level >= utils.UCODE_5978_HICKORY) or (
|
||||||
|
self.ucode_major_level >= utils.UCODE_6079)
|
||||||
def _rename_storage_group(
|
|
||||||
self, array, old_name, new_name, extra_specs):
|
|
||||||
"""Rename the storage group.
|
|
||||||
|
|
||||||
:param array: the array serial number
|
|
||||||
:param old_name: the original name
|
|
||||||
:param new_name: the new name
|
|
||||||
:param extra_specs: the extra specifications
|
|
||||||
"""
|
|
||||||
payload = {"editStorageGroupActionParam": {
|
|
||||||
"renameStorageGroupParam": {
|
|
||||||
"new_storage_Group_name": new_name}}}
|
|
||||||
status_code, job = self.modify_storage_group(
|
|
||||||
array, old_name, payload)
|
|
||||||
self.wait_for_job(
|
|
||||||
'Rename storage group', status_code, job, extra_specs)
|
|
||||||
|
@ -45,6 +45,7 @@ TRUNCATE_27 = 27
|
|||||||
UCODE_5978_ELMSR = 221
|
UCODE_5978_ELMSR = 221
|
||||||
UCODE_5978_HICKORY = 660
|
UCODE_5978_HICKORY = 660
|
||||||
UCODE_5978 = 5978
|
UCODE_5978 = 5978
|
||||||
|
UCODE_6079 = 6079
|
||||||
UPPER_HOST_CHARS = 16
|
UPPER_HOST_CHARS = 16
|
||||||
UPPER_PORT_GROUP_CHARS = 12
|
UPPER_PORT_GROUP_CHARS = 12
|
||||||
|
|
||||||
@ -140,7 +141,8 @@ PORT_GROUP_LABEL = 'port_group_label'
|
|||||||
# Array Models, Service Levels & Workloads
|
# Array Models, Service Levels & Workloads
|
||||||
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
||||||
VMAX_AFA_MODELS = ['VMAX250F', 'VMAX450F', 'VMAX850F', 'VMAX950F']
|
VMAX_AFA_MODELS = ['VMAX250F', 'VMAX450F', 'VMAX850F', 'VMAX950F']
|
||||||
PMAX_MODELS = ['PowerMax_2000', 'PowerMax_8000']
|
PMAX_MODELS = ['PowerMax_2000', 'PowerMax_8000', 'PowerMax_8500',
|
||||||
|
'PowerMax_2500']
|
||||||
|
|
||||||
HYBRID_SLS = ['Diamond', 'Platinum', 'Gold', 'Silver', 'Bronze', 'Optimized',
|
HYBRID_SLS = ['Diamond', 'Platinum', 'Gold', 'Silver', 'Bronze', 'Optimized',
|
||||||
'None', 'NONE']
|
'None', 'NONE']
|
||||||
@ -2124,3 +2126,14 @@ class PowerMaxUtils(object):
|
|||||||
:returns: str
|
:returns: str
|
||||||
"""
|
"""
|
||||||
return in_value if isinstance(in_value, str) else str(in_value)
|
return in_value if isinstance(in_value, str) else str(in_value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ode_capable(in_value):
|
||||||
|
"""Check if online device expansion capable
|
||||||
|
|
||||||
|
:param in_value: microcode
|
||||||
|
:returns: Boolean
|
||||||
|
"""
|
||||||
|
return ((int(in_value[0]) == UCODE_5978 and
|
||||||
|
int(in_value[2]) > UCODE_5978_ELMSR) or
|
||||||
|
(int(in_value[0]) > UCODE_5978))
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Dell PowerMax driver now supports Unisphere for PowerMax 10.0
|
Loading…
Reference in New Issue
Block a user