Merge "PowerMax Driver - Changing from 91 to 92 REST endpoints"
This commit is contained in:
commit
d68649855d
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -78,7 +78,7 @@ 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 = '91'
|
u4v_version = '92'
|
||||||
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'
|
||||||
|
@ -988,7 +988,7 @@ class PowerMaxData(object):
|
||||||
{'symmetrixId': array_herc,
|
{'symmetrixId': array_herc,
|
||||||
'model': 'PowerMax 2000',
|
'model': 'PowerMax 2000',
|
||||||
'ucode': '5978.1091.1092'}]
|
'ucode': '5978.1091.1092'}]
|
||||||
version_details = {'version': 'V9.1.0.1054'}
|
version_details = {'version': 'V9.2.0.0'}
|
||||||
|
|
||||||
headroom = {'headroom': [{'headroomCapacity': 20348.29}]}
|
headroom = {'headroom': [{'headroomCapacity': 20348.29}]}
|
||||||
|
|
||||||
|
@ -1274,7 +1274,7 @@ class PowerMaxData(object):
|
||||||
|
|
||||||
data_dict = {volume_id: volume_info_dict}
|
data_dict = {volume_id: volume_info_dict}
|
||||||
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.1.0.14'
|
unisphere_version = u'V9.2.0.0'
|
||||||
unisphere_version_90 = "V9.0.0.1"
|
unisphere_version_90 = "V9.0.0.1"
|
||||||
openstack_release = '12.0.0.0b3.dev401'
|
openstack_release = '12.0.0.0b3.dev401'
|
||||||
openstack_version = '12.0.0'
|
openstack_version = '12.0.0'
|
||||||
|
@ -1523,3 +1523,11 @@ class PowerMaxData(object):
|
||||||
'Emulation': 'FBA',
|
'Emulation': 'FBA',
|
||||||
'Configuration': 'TDEV',
|
'Configuration': 'TDEV',
|
||||||
'CompressionDisabled': False}})
|
'CompressionDisabled': False}})
|
||||||
|
|
||||||
|
vol_create_desc1 = 'Populating Storage Group(s) with volumes : [00001]'
|
||||||
|
vol_create_desc2 = ('Refresh [Storage Group [OS-SG] '
|
||||||
|
'on Symmetrix [000197800123]] ')
|
||||||
|
vol_create_task = [{'execution_order': 1,
|
||||||
|
'description': vol_create_desc1},
|
||||||
|
{'execution_order': 2,
|
||||||
|
'description': vol_create_desc2}]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -423,6 +423,17 @@ class PowerMaxRestTest(test.TestCase):
|
||||||
self.data.test_volume.size, self.data.extra_specs)
|
self.data.test_volume.size, self.data.extra_specs)
|
||||||
self.assertEqual(ref_dict, volume_dict)
|
self.assertEqual(ref_dict, volume_dict)
|
||||||
|
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, 'get_volume')
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, 'wait_for_job',
|
||||||
|
return_value=tpd.PowerMaxData.vol_create_task)
|
||||||
|
def test_create_volume_from_sg_existing_volume_success(
|
||||||
|
self, mock_task, mock_get):
|
||||||
|
volume_name = self.data.volume_details[0]['volume_identifier']
|
||||||
|
self.rest.create_volume_from_sg(
|
||||||
|
self.data.array, volume_name, self.data.defaultstoragegroup_name,
|
||||||
|
self.data.test_volume.size, self.data.extra_specs)
|
||||||
|
mock_get.assert_called_with(self.data.array, self.data.device_id)
|
||||||
|
|
||||||
def test_create_volume_from_sg_failed(self):
|
def test_create_volume_from_sg_failed(self):
|
||||||
volume_name = self.data.volume_details[0]['volume_identifier']
|
volume_name = self.data.volume_details[0]['volume_identifier']
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
|
@ -1695,7 +1706,7 @@ class PowerMaxRestTest(test.TestCase):
|
||||||
|
|
||||||
def test_get_iterator_list(self):
|
def test_get_iterator_list(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.rest, '_get_request', side_effect=[
|
self.rest, 'get_request', side_effect=[
|
||||||
self.data.rest_iterator_resonse_one,
|
self.data.rest_iterator_resonse_one,
|
||||||
self.data.rest_iterator_resonse_two]):
|
self.data.rest_iterator_resonse_two]):
|
||||||
expected_response = [
|
expected_response = [
|
||||||
|
@ -1744,7 +1755,7 @@ class PowerMaxRestTest(test.TestCase):
|
||||||
|
|
||||||
def test_get_vmax_model(self):
|
def test_get_vmax_model(self):
|
||||||
reference = 'PowerMax_2000'
|
reference = 'PowerMax_2000'
|
||||||
with mock.patch.object(self.rest, '_get_request',
|
with mock.patch.object(self.rest, 'get_request',
|
||||||
return_value=self.data.powermax_model_details):
|
return_value=self.data.powermax_model_details):
|
||||||
self.assertEqual(self.rest.get_vmax_model(self.data.array),
|
self.assertEqual(self.rest.get_vmax_model(self.data.array),
|
||||||
reference)
|
reference)
|
||||||
|
@ -2224,7 +2235,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.1.0.1054'
|
version = 'T9.2.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,
|
||||||
|
@ -2253,3 +2264,69 @@ class PowerMaxRestTest(test.TestCase):
|
||||||
self.assertTrue(valid_version)
|
self.assertTrue(valid_version)
|
||||||
request_count = mock_req.call_count
|
request_count = mock_req.call_count
|
||||||
self.assertEqual(1, request_count)
|
self.assertEqual(1, request_count)
|
||||||
|
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, '_build_uri_kwargs')
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, '_build_uri_legacy_args')
|
||||||
|
def test_build_uri_legacy(self, mck_build_legacy, mck_build_kwargs):
|
||||||
|
self.rest.build_uri('array', f_key='test')
|
||||||
|
mck_build_legacy.assert_called_once()
|
||||||
|
mck_build_kwargs.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, '_build_uri_kwargs')
|
||||||
|
@mock.patch.object(rest.PowerMaxRest, '_build_uri_legacy_args')
|
||||||
|
def test_build_uri_kwargs(self, mck_build_legacy, mck_build_kwargs):
|
||||||
|
self.rest.build_uri(array='test', f_key='test')
|
||||||
|
mck_build_legacy.assert_not_called()
|
||||||
|
mck_build_kwargs.assert_called_once()
|
||||||
|
|
||||||
|
def test_build_uri_legacy_args_private_no_version(self):
|
||||||
|
target_uri = self.rest._build_uri_legacy_args(
|
||||||
|
self.data.array, 'sloprovisioning', 'storagegroup',
|
||||||
|
resource_name='test-sg', private=True, no_version=True)
|
||||||
|
expected_uri = (
|
||||||
|
'/private/sloprovisioning/symmetrix/%(arr)s/storagegroup/test-sg' %
|
||||||
|
{'arr': self.data.array})
|
||||||
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
|
def test_build_uri_legacy_args_public_version(self):
|
||||||
|
target_uri = self.rest._build_uri_legacy_args(
|
||||||
|
self.data.array, 'sloprovisioning', 'storagegroup',
|
||||||
|
resource_name='test-sg')
|
||||||
|
expected_uri = (
|
||||||
|
'/%(ver)s/sloprovisioning/symmetrix/%(arr)s/storagegroup/test-sg' %
|
||||||
|
{'ver': rest.U4V_VERSION, 'arr': self.data.array})
|
||||||
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
|
def test_build_uri_kwargs_private_no_version(self):
|
||||||
|
target_uri = self.rest._build_uri_kwargs(
|
||||||
|
no_version=True, private=True, category='test')
|
||||||
|
expected_uri = '/private/test'
|
||||||
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
|
def test_build_uri_kwargs_public_version(self):
|
||||||
|
target_uri = self.rest._build_uri_kwargs(category='test')
|
||||||
|
expected_uri = '/%(ver)s/test' % {'ver': rest.U4V_VERSION}
|
||||||
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
|
def test_build_uri_kwargs_full_uri(self):
|
||||||
|
target_uri = self.rest._build_uri_kwargs(
|
||||||
|
category='test-cat',
|
||||||
|
resource_level='res-level', resource_level_id='id1',
|
||||||
|
resource_type='res-type', resource_type_id='id2',
|
||||||
|
resource='res', resource_id='id3',
|
||||||
|
object_type='obj', object_type_id='id4')
|
||||||
|
expected_uri = (
|
||||||
|
'/%(ver)s/test-cat/res-level/id1/res-type/id2/res/id3/obj/id4' % {
|
||||||
|
'ver': rest.U4V_VERSION})
|
||||||
|
self.assertEqual(target_uri, expected_uri)
|
||||||
|
|
||||||
|
@mock.patch.object(
|
||||||
|
rest.PowerMaxRest, 'request', return_value=(200, {'success': True}))
|
||||||
|
def test_post_request(self, mck_request):
|
||||||
|
test_uri = '/92/test/uri'
|
||||||
|
test_op = 'performance metrics'
|
||||||
|
test_filters = {'filters': False}
|
||||||
|
response_obj = self.rest.post_request(test_uri, test_op, test_filters)
|
||||||
|
mck_request.assert_called_once_with(
|
||||||
|
test_uri, rest.POST, request_object=test_filters)
|
||||||
|
self.assertEqual(response_obj, {'success': True})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -2824,17 +2824,17 @@ class PowerMaxCommon(object):
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
:param extra_specs: extra specifications
|
:param extra_specs: extra specifications
|
||||||
"""
|
"""
|
||||||
snap_name = session['snap_name']
|
snap_name = session.get('snap_name')
|
||||||
source = session['source_vol_id']
|
source = session.get('source_vol_id')
|
||||||
generation = session['generation']
|
generation = session.get('generation')
|
||||||
expired = session['expired']
|
expired = session.get('expired')
|
||||||
|
|
||||||
target, cm_enabled = None, False
|
target, cm_enabled = None, False
|
||||||
if session.get('target_vol_id'):
|
if session.get('target_vol_id'):
|
||||||
target = session['target_vol_id']
|
target = session.get('target_vol_id')
|
||||||
cm_enabled = session['copy_mode']
|
cm_enabled = session.get('copy_mode')
|
||||||
|
|
||||||
if target:
|
if target and snap_name:
|
||||||
loop = True if cm_enabled else False
|
loop = True if cm_enabled else False
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Unlinking source from target. Source: %(vol)s, Target: "
|
"Unlinking source from target. Source: %(vol)s, Target: "
|
||||||
|
@ -2848,7 +2848,7 @@ class PowerMaxCommon(object):
|
||||||
# 1. If legacy snapshot with 'EMC_SMI' in snapshot name
|
# 1. If legacy snapshot with 'EMC_SMI' in snapshot name
|
||||||
# 2. If snapVX snapshot with copy mode enabled
|
# 2. If snapVX snapshot with copy mode enabled
|
||||||
# 3. If snapVX snapshot with copy mode disabled and not expired
|
# 3. If snapVX snapshot with copy mode disabled and not expired
|
||||||
if ('EMC_SMI' in snap_name or cm_enabled or (
|
if (snap_name and 'EMC_SMI' in snap_name or cm_enabled or (
|
||||||
not cm_enabled and not expired)):
|
not cm_enabled and not expired)):
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Deleting temporary snapshot. Source: %(vol)s, snap name: "
|
"Deleting temporary snapshot. Source: %(vol)s, snap name: "
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -122,9 +122,10 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
||||||
- Switch to Unisphere REST API public replication endpoints
|
- Switch to Unisphere REST API public replication endpoints
|
||||||
- Support for multiple replication devices
|
- Support for multiple replication devices
|
||||||
- Pools bug fix allowing 'None' variants (bug #1873253)
|
- Pools bug fix allowing 'None' variants (bug #1873253)
|
||||||
|
4.3.0 - Changing from 91 to 92 REST endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.2.0"
|
VERSION = "4.3.0"
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
CI_WIKI_NAME = "EMC_VMAX_CI"
|
CI_WIKI_NAME = "EMC_VMAX_CI"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -127,9 +127,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
||||||
- Switch to Unisphere REST API public replication endpoints
|
- Switch to Unisphere REST API public replication endpoints
|
||||||
- Support for multiple replication devices
|
- Support for multiple replication devices
|
||||||
- Pools bug fix allowing 'None' variants (bug #1873253)
|
- Pools bug fix allowing 'None' variants (bug #1873253)
|
||||||
|
4.3.0 - Changing from 91 to 92 REST endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.2.0"
|
VERSION = "4.3.0"
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
CI_WIKI_NAME = "EMC_VMAX_CI"
|
CI_WIKI_NAME = "EMC_VMAX_CI"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
@ -35,8 +36,8 @@ LOG = logging.getLogger(__name__)
|
||||||
SLOPROVISIONING = 'sloprovisioning'
|
SLOPROVISIONING = 'sloprovisioning'
|
||||||
REPLICATION = 'replication'
|
REPLICATION = 'replication'
|
||||||
SYSTEM = 'system'
|
SYSTEM = 'system'
|
||||||
U4V_VERSION = '91'
|
U4V_VERSION = '92'
|
||||||
MIN_U4P_VERSION = '9.1.0.14'
|
MIN_U4P_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
|
||||||
|
@ -56,6 +57,7 @@ INCOMPLETE_LIST = ['created', 'unscheduled', 'scheduled', 'running',
|
||||||
CREATED = 'created'
|
CREATED = 'created'
|
||||||
SUCCEEDED = 'succeeded'
|
SUCCEEDED = 'succeeded'
|
||||||
CREATE_VOL_STRING = "Creating new Volumes"
|
CREATE_VOL_STRING = "Creating new Volumes"
|
||||||
|
POPULATE_SG_LIST = "Populating Storage Group(s) with volumes"
|
||||||
|
|
||||||
|
|
||||||
class PowerMaxRest(object):
|
class PowerMaxRest(object):
|
||||||
|
@ -367,7 +369,7 @@ class PowerMaxRest(object):
|
||||||
"""
|
"""
|
||||||
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" % (U4V_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']
|
||||||
try:
|
try:
|
||||||
|
@ -426,29 +428,137 @@ class PowerMaxRest(object):
|
||||||
message=exception_message)
|
message=exception_message)
|
||||||
return task
|
return task
|
||||||
|
|
||||||
@staticmethod
|
def build_uri(self, *args, **kwargs):
|
||||||
def _build_uri(array, category, resource_type,
|
|
||||||
resource_name=None, private='', version=U4V_VERSION):
|
|
||||||
"""Build the target url.
|
"""Build the target url.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param args: input args, see _build_uri_legacy_args() for input
|
||||||
:param category: the resource category e.g. sloprovisioning
|
breakdown
|
||||||
:param resource_type: the resource type e.g. maskingview
|
:param kwargs: input keyword args, see _build_uri_kwargs() for input
|
||||||
:param resource_name: the name of a specific resource
|
breakdown
|
||||||
:param private: empty string or '/private' if private url
|
:return: target uri -- str
|
||||||
:returns: target url, string
|
|
||||||
"""
|
"""
|
||||||
target_uri = ('%(private)s/%(version)s/%(category)s/symmetrix/'
|
if args:
|
||||||
'%(array)s/%(resource_type)s'
|
target_uri = self._build_uri_legacy_args(*args, **kwargs)
|
||||||
% {'private': private, 'version': version,
|
else:
|
||||||
'category': category, 'array': array,
|
target_uri = self._build_uri_kwargs(**kwargs)
|
||||||
'resource_type': resource_type})
|
|
||||||
if resource_name:
|
|
||||||
target_uri += '/%(resource_name)s' % {
|
|
||||||
'resource_name': resource_name}
|
|
||||||
return target_uri
|
return target_uri
|
||||||
|
|
||||||
def _get_request(self, target_uri, resource_type, params=None):
|
@staticmethod
|
||||||
|
def _build_uri_legacy_args(*args, **kwargs):
|
||||||
|
"""Build the target URI using legacy args & kwargs.
|
||||||
|
|
||||||
|
Expected format:
|
||||||
|
arg[0]: the array serial number: the array serial number -- str
|
||||||
|
arg[1]: the resource category e.g. 'sloprovisioning' -- str
|
||||||
|
arg[2]: the resource type e.g. 'maskingview' -- str
|
||||||
|
kwarg resource_name: the name of a specific resource -- str
|
||||||
|
kwarg private: if endpoint is private -- bool
|
||||||
|
kwarg version: U4V REST endpoint version -- int/str
|
||||||
|
kwarg no_version: if endpoint should be versionless -- bool
|
||||||
|
|
||||||
|
:param args: input args -- see above
|
||||||
|
:param kwargs: input keyword args -- see above
|
||||||
|
:return: target URI -- str
|
||||||
|
"""
|
||||||
|
# Extract args following legacy _build_uri() format
|
||||||
|
array_id, category, resource_type = args[0], args[1], args[2]
|
||||||
|
# Extract keyword args following legacy _build_uri() format
|
||||||
|
resource_name = kwargs.get('resource_name')
|
||||||
|
private = kwargs.get('private')
|
||||||
|
version = kwargs.get('version', U4V_VERSION)
|
||||||
|
if kwargs.get('no_version'):
|
||||||
|
version = None
|
||||||
|
|
||||||
|
# Build URI
|
||||||
|
target_uri = ''
|
||||||
|
if private:
|
||||||
|
target_uri += '/private'
|
||||||
|
if version:
|
||||||
|
target_uri += '/%(version)s' % {'version': version}
|
||||||
|
target_uri += (
|
||||||
|
'/{cat}/symmetrix/{array_id}/{res_type}'.format(
|
||||||
|
cat=category, array_id=array_id, res_type=resource_type))
|
||||||
|
if resource_name:
|
||||||
|
target_uri += '/{resource_name}'.format(
|
||||||
|
resource_name=kwargs.get('resource_name'))
|
||||||
|
|
||||||
|
return target_uri
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_uri_kwargs(**kwargs):
|
||||||
|
"""Build the target URI using kwargs.
|
||||||
|
|
||||||
|
Expected kwargs:
|
||||||
|
private: if endpoint is private (optional) -- bool
|
||||||
|
version: U4P REST endpoint version (optional) -- int/None
|
||||||
|
no_version: if endpoint should be versionless (optional) -- bool
|
||||||
|
|
||||||
|
category: U4P REST category eg. 'common', 'replication'-- str
|
||||||
|
|
||||||
|
resource_level: U4P REST resource level eg. 'symmetrix'
|
||||||
|
(optional) -- str
|
||||||
|
resource_level_id: U4P REST resource level id (optional) -- str
|
||||||
|
|
||||||
|
resource_type: U4P REST resource type eg. 'rdf_director', 'host'
|
||||||
|
(optional) -- str
|
||||||
|
resource_type_id: U4P REST resource type id (optional) -- str
|
||||||
|
|
||||||
|
resource: U4P REST resource eg. 'port' (optional) -- str
|
||||||
|
resource_id: U4P REST resource id (optional) -- str
|
||||||
|
|
||||||
|
object_type: U4P REST resource eg. 'rdf_group' (optional) -- str
|
||||||
|
object_type_id: U4P REST resource id (optional) -- str
|
||||||
|
|
||||||
|
:param kwargs: input keyword args -- see above
|
||||||
|
:return: target URI -- str
|
||||||
|
"""
|
||||||
|
version = kwargs.get('version', U4V_VERSION)
|
||||||
|
if kwargs.get('no_version'):
|
||||||
|
version = None
|
||||||
|
|
||||||
|
target_uri = ''
|
||||||
|
|
||||||
|
if kwargs.get('private'):
|
||||||
|
target_uri += '/private'
|
||||||
|
|
||||||
|
if version:
|
||||||
|
target_uri += '/%(ver)s' % {'ver': version}
|
||||||
|
|
||||||
|
target_uri += '/%(cat)s' % {'cat': kwargs.get('category')}
|
||||||
|
|
||||||
|
if kwargs.get('resource_level'):
|
||||||
|
target_uri += '/%(res_level)s' % {
|
||||||
|
'res_level': kwargs.get('resource_level')}
|
||||||
|
|
||||||
|
if kwargs.get('resource_level_id'):
|
||||||
|
target_uri += '/%(res_level_id)s' % {
|
||||||
|
'res_level_id': kwargs.get('resource_level_id')}
|
||||||
|
|
||||||
|
if kwargs.get('resource_type'):
|
||||||
|
target_uri += '/%(res_type)s' % {
|
||||||
|
'res_type': kwargs.get('resource_type')}
|
||||||
|
if kwargs.get('resource_type_id'):
|
||||||
|
target_uri += '/%(res_type_id)s' % {
|
||||||
|
'res_type_id': kwargs.get('resource_type_id')}
|
||||||
|
|
||||||
|
if kwargs.get('resource'):
|
||||||
|
target_uri += '/%(res)s' % {
|
||||||
|
'res': kwargs.get('resource')}
|
||||||
|
if kwargs.get('resource_id'):
|
||||||
|
target_uri += '/%(res_id)s' % {
|
||||||
|
'res_id': kwargs.get('resource_id')}
|
||||||
|
|
||||||
|
if kwargs.get('object_type'):
|
||||||
|
target_uri += '/%(object_type)s' % {
|
||||||
|
'object_type': kwargs.get('object_type')}
|
||||||
|
if kwargs.get('object_type_id'):
|
||||||
|
target_uri += '/%(object_type_id)s' % {
|
||||||
|
'object_type_id': kwargs.get('object_type_id')}
|
||||||
|
|
||||||
|
return target_uri
|
||||||
|
|
||||||
|
def get_request(self, target_uri, resource_type, params=None):
|
||||||
"""Send a GET request to the array.
|
"""Send a GET request to the array.
|
||||||
|
|
||||||
:param target_uri: the target uri
|
:param target_uri: the target uri
|
||||||
|
@ -469,8 +579,30 @@ class PowerMaxRest(object):
|
||||||
resource_object = self.list_pagination(resource_object)
|
resource_object = self.list_pagination(resource_object)
|
||||||
return resource_object
|
return resource_object
|
||||||
|
|
||||||
|
def post_request(self, target_uri, resource_type, request_body):
|
||||||
|
"""Send a POST request to the array.
|
||||||
|
|
||||||
|
:param target_uri: the target uri -- str
|
||||||
|
:param resource_type: the resource type -- str
|
||||||
|
:param request_body: the POST request body -- dict
|
||||||
|
:return: resource object -- dict or None
|
||||||
|
"""
|
||||||
|
resource_object = None
|
||||||
|
sc, msg = self.request(target_uri, POST, request_object=request_body)
|
||||||
|
|
||||||
|
operation = 'POST %(res)s' % {'res': resource_type}
|
||||||
|
try:
|
||||||
|
self.check_status_code_success(operation, sc, msg)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug("POST resource failed with %(e)s", {'e': e})
|
||||||
|
|
||||||
|
if sc == STATUS_200:
|
||||||
|
resource_object = msg
|
||||||
|
|
||||||
|
return resource_object
|
||||||
|
|
||||||
def get_resource(self, array, category, resource_type,
|
def get_resource(self, array, category, resource_type,
|
||||||
resource_name=None, params=None, private='',
|
resource_name=None, params=None, private=False,
|
||||||
version=U4V_VERSION):
|
version=U4V_VERSION):
|
||||||
"""Get resource details from array.
|
"""Get resource details from array.
|
||||||
|
|
||||||
|
@ -483,12 +615,13 @@ class PowerMaxRest(object):
|
||||||
:param version: None or specific version number if required
|
:param version: None or specific version number if required
|
||||||
:returns: resource object -- dict or None
|
:returns: resource object -- dict or None
|
||||||
"""
|
"""
|
||||||
target_uri = self._build_uri(array, category, resource_type,
|
target_uri = self.build_uri(
|
||||||
resource_name, private, version=version)
|
array, category, resource_type, resource_name=resource_name,
|
||||||
return self._get_request(target_uri, resource_type, params)
|
private=private, version=version)
|
||||||
|
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,
|
||||||
private=''):
|
private=False):
|
||||||
"""Create a provisioning resource.
|
"""Create a provisioning resource.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
|
@ -498,8 +631,8 @@ class PowerMaxRest(object):
|
||||||
:param private: empty string or '/private' if private url
|
:param private: empty string or '/private' if private url
|
||||||
:returns: status_code -- int, message -- string, server response
|
:returns: status_code -- int, message -- string, server response
|
||||||
"""
|
"""
|
||||||
target_uri = self._build_uri(array, category, resource_type,
|
target_uri = self.build_uri(
|
||||||
None, private)
|
array, category, resource_type, private=private)
|
||||||
status_code, message = self.request(target_uri, POST,
|
status_code, message = self.request(target_uri, POST,
|
||||||
request_object=payload)
|
request_object=payload)
|
||||||
operation = 'Create %(res)s resource' % {'res': resource_type}
|
operation = 'Create %(res)s resource' % {'res': resource_type}
|
||||||
|
@ -507,8 +640,9 @@ class PowerMaxRest(object):
|
||||||
operation, status_code, message)
|
operation, status_code, message)
|
||||||
return status_code, message
|
return status_code, message
|
||||||
|
|
||||||
def modify_resource(self, array, category, resource_type, payload,
|
def modify_resource(
|
||||||
version=U4V_VERSION, resource_name=None, private=''):
|
self, array, category, resource_type, payload, version=U4V_VERSION,
|
||||||
|
resource_name=None, private=False):
|
||||||
"""Modify a resource.
|
"""Modify a resource.
|
||||||
|
|
||||||
:param version: the uv4 version
|
:param version: the uv4 version
|
||||||
|
@ -520,8 +654,9 @@ class PowerMaxRest(object):
|
||||||
:param private: empty string or '/private' if private url
|
:param private: empty string or '/private' if private url
|
||||||
:returns: status_code -- int, message -- string (server response)
|
:returns: status_code -- int, message -- string (server response)
|
||||||
"""
|
"""
|
||||||
target_uri = self._build_uri(array, category, resource_type,
|
target_uri = self.build_uri(
|
||||||
resource_name, private, version)
|
array, category, resource_type, resource_name=resource_name,
|
||||||
|
private=private, version=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}
|
||||||
|
@ -531,7 +666,7 @@ class PowerMaxRest(object):
|
||||||
@retry(retry_exc_tuple, interval=2, retries=3)
|
@retry(retry_exc_tuple, interval=2, retries=3)
|
||||||
def delete_resource(
|
def delete_resource(
|
||||||
self, array, category, resource_type, resource_name,
|
self, array, category, resource_type, resource_name,
|
||||||
payload=None, private='', params=None):
|
payload=None, private=False, params=None):
|
||||||
"""Delete a provisioning resource.
|
"""Delete a provisioning resource.
|
||||||
|
|
||||||
:param array: the array serial number
|
:param array: the array serial number
|
||||||
|
@ -542,8 +677,9 @@ class PowerMaxRest(object):
|
||||||
:param private: empty string or '/private' if private url
|
:param private: empty string or '/private' if private url
|
||||||
:param params: dict of optional query params
|
:param params: dict of optional query params
|
||||||
"""
|
"""
|
||||||
target_uri = self._build_uri(array, category, resource_type,
|
target_uri = self.build_uri(
|
||||||
resource_name, private)
|
array, category, resource_type, resource_name=resource_name,
|
||||||
|
private=private)
|
||||||
status_code, message = self.request(target_uri, DELETE,
|
status_code, message = self.request(target_uri, DELETE,
|
||||||
request_object=payload,
|
request_object=payload,
|
||||||
params=params)
|
params=params)
|
||||||
|
@ -557,7 +693,7 @@ class PowerMaxRest(object):
|
||||||
: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' % (U4V_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.",
|
||||||
{'array': array})
|
{'array': array})
|
||||||
|
@ -570,7 +706,7 @@ class PowerMaxRest(object):
|
||||||
: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' % (U4V_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')
|
||||||
|
|
||||||
def is_next_gen_array(self, array):
|
def is_next_gen_array(self, array):
|
||||||
|
@ -967,6 +1103,12 @@ class PowerMaxRest(object):
|
||||||
device_id = t_list[(len(t_list) - 1)]
|
device_id = t_list[(len(t_list) - 1)]
|
||||||
device_id = device_id[1:-1]
|
device_id = device_id[1:-1]
|
||||||
break
|
break
|
||||||
|
elif POPULATE_SG_LIST in desc:
|
||||||
|
regex_str = (r'Populating Storage Group\(s\) ' +
|
||||||
|
r'with volumes : \[(.+)\]$')
|
||||||
|
full_str = re.compile(regex_str)
|
||||||
|
match = full_str.match(desc)
|
||||||
|
device_id = match.group(1) if match else None
|
||||||
if device_id:
|
if device_id:
|
||||||
self.get_volume(array, device_id)
|
self.get_volume(array, device_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1839,7 +1981,7 @@ class PowerMaxRest(object):
|
||||||
array_capabilities = None
|
array_capabilities = None
|
||||||
target_uri = ("/%s/replication/capabilities/symmetrix"
|
target_uri = ("/%s/replication/capabilities/symmetrix"
|
||||||
% U4V_VERSION)
|
% U4V_VERSION)
|
||||||
capabilities = self._get_request(
|
capabilities = self.get_request(
|
||||||
target_uri, 'replication capabilities')
|
target_uri, 'replication capabilities')
|
||||||
if capabilities:
|
if capabilities:
|
||||||
symm_list = capabilities['symmetrixCapability']
|
symm_list = capabilities['symmetrixCapability']
|
||||||
|
@ -2245,19 +2387,26 @@ class PowerMaxRest(object):
|
||||||
snap_src_dict_list.append(snap_src_dict)
|
snap_src_dict_list.append(snap_src_dict)
|
||||||
|
|
||||||
if snap_tgt:
|
if snap_tgt:
|
||||||
snap_tgt_dict['source_vol_id'] = snap_tgt['linkSourceName']
|
snap_tgt_dict['source_vol_id'] = snap_tgt.get('linkSourceName')
|
||||||
snap_tgt_dict['target_vol_id'] = device_id
|
snap_tgt_dict['target_vol_id'] = device_id
|
||||||
snap_tgt_dict['state'] = snap_tgt['state']
|
snap_tgt_dict['state'] = snap_tgt.get('state')
|
||||||
snap_tgt_dict['copy_mode'] = snap_tgt['copy']
|
snap_tgt_dict['copy_mode'] = snap_tgt.get('copy')
|
||||||
|
|
||||||
vol_info = self._get_private_volume(array, device_id)
|
vol_info = self._get_private_volume(array, device_id)
|
||||||
vol_tf_sessions = vol_info['timeFinderInfo']['snapVXSession']
|
if vol_info.get('timeFinderInfo'):
|
||||||
for session in vol_tf_sessions:
|
vol_tf_sessions = vol_info.get(
|
||||||
if session.get('tgtSrcSnapshotGenInfo'):
|
'timeFinderInfo').get('snapVXSession')
|
||||||
snap_tgt_link = session.get('tgtSrcSnapshotGenInfo')
|
if vol_tf_sessions:
|
||||||
snap_tgt_dict['snap_name'] = snap_tgt_link['snapshotName']
|
for session in vol_tf_sessions:
|
||||||
snap_tgt_dict['expired'] = snap_tgt_link['expired']
|
if session.get('tgtSrcSnapshotGenInfo'):
|
||||||
snap_tgt_dict['generation'] = snap_tgt_link['generation']
|
snap_tgt_link = session.get(
|
||||||
|
'tgtSrcSnapshotGenInfo')
|
||||||
|
snap_tgt_dict['snap_name'] = snap_tgt_link.get(
|
||||||
|
'snapshotName')
|
||||||
|
snap_tgt_dict['expired'] = snap_tgt_link.get(
|
||||||
|
'expired')
|
||||||
|
snap_tgt_dict['generation'] = snap_tgt_link.get(
|
||||||
|
'generation')
|
||||||
|
|
||||||
return snap_src_dict_list, snap_tgt_dict
|
return snap_src_dict_list, snap_tgt_dict
|
||||||
|
|
||||||
|
@ -3037,8 +3186,8 @@ class PowerMaxRest(object):
|
||||||
params = {'to': end_position, 'from': start_position}
|
params = {'to': end_position, 'from': start_position}
|
||||||
target_uri = ('/common/Iterator/%(iterator_id)s/page' % {
|
target_uri = ('/common/Iterator/%(iterator_id)s/page' % {
|
||||||
'iterator_id': iterator_id})
|
'iterator_id': iterator_id})
|
||||||
iterator_response = self._get_request(target_uri, 'iterator',
|
iterator_response = self.get_request(target_uri, 'iterator',
|
||||||
params)
|
params)
|
||||||
try:
|
try:
|
||||||
iterator_result += iterator_response['result']
|
iterator_result += iterator_response['result']
|
||||||
start_position += max_page_size
|
start_position += max_page_size
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
|
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
PowerMax driver - the minimum version of Unisphere for PowerMax required
|
||||||
|
for Victoria is 9.2, so all the latest 92 REST endpoints will be used.
|
Loading…
Reference in New Issue