PowerMax Driver - Changing from 91 to 92 REST endpoints

Changing unisphere for powermax REST endpoint from 91 to 92.
Improvements have been made to the method by with URIs are
created to allow for full coverage of all possible Unisphere
REST API endpoints.

Change-Id: I6b3f826dee5c901b50159656a72ad2399818fcc5
This commit is contained in:
Helen Walsh 2020-06-24 16:10:28 +01:00
parent 70beaeb788
commit d592b2ad0d
20 changed files with 326 additions and 85 deletions

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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_3 = '72'
rdf_group_no_4 = '73'
u4v_version = '91'
u4v_version = '92'
storagegroup_name_source = 'Grp_source_sg'
storagegroup_name_target = 'Grp_target_sg'
group_snapshot_name = 'Grp_snapshot'
@ -988,7 +988,7 @@ class PowerMaxData(object):
{'symmetrixId': array_herc,
'model': 'PowerMax 2000',
'ucode': '5978.1091.1092'}]
version_details = {'version': 'V9.1.0.1054'}
version_details = {'version': 'V9.2.0.0'}
headroom = {'headroom': [{'headroomCapacity': 20348.29}]}
@ -1274,7 +1274,7 @@ class PowerMaxData(object):
data_dict = {volume_id: volume_info_dict}
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"
openstack_release = '12.0.0.0b3.dev401'
openstack_version = '12.0.0'
@ -1523,3 +1523,11 @@ class PowerMaxData(object):
'Emulation': 'FBA',
'Configuration': 'TDEV',
'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}]

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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.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):
volume_name = self.data.volume_details[0]['volume_identifier']
self.assertRaises(
@ -1695,7 +1706,7 @@ class PowerMaxRestTest(test.TestCase):
def test_get_iterator_list(self):
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_two]):
expected_response = [
@ -1744,7 +1755,7 @@ class PowerMaxRestTest(test.TestCase):
def test_get_vmax_model(self):
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):
self.assertEqual(self.rest.get_vmax_model(self.data.array),
reference)
@ -2224,7 +2235,7 @@ class PowerMaxRestTest(test.TestCase):
array_id, sg_name, rdf_group_no, rep_extra_specs)
def test_validate_unisphere_version_unofficial_success(self):
version = 'T9.1.0.1054'
version = 'T9.2.0.1054'
returned_version = {'version': version}
with mock.patch.object(self.rest, "request",
return_value=(200,
@ -2253,3 +2264,69 @@ class PowerMaxRestTest(test.TestCase):
self.assertTrue(valid_version)
request_count = mock_req.call_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})

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2019 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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 extra_specs: extra specifications
"""
snap_name = session['snap_name']
source = session['source_vol_id']
generation = session['generation']
expired = session['expired']
snap_name = session.get('snap_name')
source = session.get('source_vol_id')
generation = session.get('generation')
expired = session.get('expired')
target, cm_enabled = None, False
if session.get('target_vol_id'):
target = session['target_vol_id']
cm_enabled = session['copy_mode']
target = session.get('target_vol_id')
cm_enabled = session.get('copy_mode')
if target:
if target and snap_name:
loop = True if cm_enabled else False
LOG.debug(
"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
# 2. If snapVX snapshot with copy mode enabled
# 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)):
LOG.debug(
"Deleting temporary snapshot. Source: %(vol)s, snap name: "

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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
- Support for multiple replication devices
- 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
CI_WIKI_NAME = "EMC_VMAX_CI"

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# 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
- Support for multiple replication devices
- 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
CI_WIKI_NAME = "EMC_VMAX_CI"

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -14,6 +14,7 @@
# under the License.
import json
import re
import sys
import time
@ -35,8 +36,8 @@ LOG = logging.getLogger(__name__)
SLOPROVISIONING = 'sloprovisioning'
REPLICATION = 'replication'
SYSTEM = 'system'
U4V_VERSION = '91'
MIN_U4P_VERSION = '9.1.0.14'
U4V_VERSION = '92'
MIN_U4P_VERSION = '9.2.0.0'
UCODE_5978 = '5978'
retry_exc_tuple = (exception.VolumeBackendAPIException,)
u4p_failover_max_wait = 120
@ -56,6 +57,7 @@ INCOMPLETE_LIST = ['created', 'unscheduled', 'scheduled', 'running',
CREATED = 'created'
SUCCEEDED = 'succeeded'
CREATE_VOL_STRING = "Creating new Volumes"
POPULATE_SG_LIST = "Populating Storage Group(s) with volumes"
class PowerMaxRest(object):
@ -367,7 +369,7 @@ class PowerMaxRest(object):
"""
complete, rc, status, result, task = False, 0, None, None, None
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:
status = job['status']
try:
@ -426,29 +428,137 @@ class PowerMaxRest(object):
message=exception_message)
return task
@staticmethod
def _build_uri(array, category, resource_type,
resource_name=None, private='', version=U4V_VERSION):
def build_uri(self, *args, **kwargs):
"""Build the target url.
:param array: the array serial number
:param category: the resource category e.g. sloprovisioning
:param resource_type: the resource type e.g. maskingview
:param resource_name: the name of a specific resource
:param private: empty string or '/private' if private url
:returns: target url, string
:param args: input args, see _build_uri_legacy_args() for input
breakdown
:param kwargs: input keyword args, see _build_uri_kwargs() for input
breakdown
:return: target uri -- str
"""
target_uri = ('%(private)s/%(version)s/%(category)s/symmetrix/'
'%(array)s/%(resource_type)s'
% {'private': private, 'version': version,
'category': category, 'array': array,
'resource_type': resource_type})
if resource_name:
target_uri += '/%(resource_name)s' % {
'resource_name': resource_name}
if args:
target_uri = self._build_uri_legacy_args(*args, **kwargs)
else:
target_uri = self._build_uri_kwargs(**kwargs)
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.
:param target_uri: the target uri
@ -469,8 +579,30 @@ class PowerMaxRest(object):
resource_object = self.list_pagination(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,
resource_name=None, params=None, private='',
resource_name=None, params=None, private=False,
version=U4V_VERSION):
"""Get resource details from array.
@ -483,12 +615,13 @@ class PowerMaxRest(object):
:param version: None or specific version number if required
:returns: resource object -- dict or None
"""
target_uri = self._build_uri(array, category, resource_type,
resource_name, private, version=version)
return self._get_request(target_uri, resource_type, params)
target_uri = self.build_uri(
array, category, resource_type, resource_name=resource_name,
private=private, version=version)
return self.get_request(target_uri, resource_type, params)
def create_resource(self, array, category, resource_type, payload,
private=''):
private=False):
"""Create a provisioning resource.
:param array: the array serial number
@ -498,8 +631,8 @@ class PowerMaxRest(object):
:param private: empty string or '/private' if private url
:returns: status_code -- int, message -- string, server response
"""
target_uri = self._build_uri(array, category, resource_type,
None, private)
target_uri = self.build_uri(
array, category, resource_type, private=private)
status_code, message = self.request(target_uri, POST,
request_object=payload)
operation = 'Create %(res)s resource' % {'res': resource_type}
@ -507,8 +640,9 @@ class PowerMaxRest(object):
operation, status_code, message)
return status_code, message
def modify_resource(self, array, category, resource_type, payload,
version=U4V_VERSION, resource_name=None, private=''):
def modify_resource(
self, array, category, resource_type, payload, version=U4V_VERSION,
resource_name=None, private=False):
"""Modify a resource.
:param version: the uv4 version
@ -520,8 +654,9 @@ class PowerMaxRest(object):
:param private: empty string or '/private' if private url
:returns: status_code -- int, message -- string (server response)
"""
target_uri = self._build_uri(array, category, resource_type,
resource_name, private, version)
target_uri = self.build_uri(
array, category, resource_type, resource_name=resource_name,
private=private, version=version)
status_code, message = self.request(target_uri, PUT,
request_object=payload)
operation = 'modify %(res)s resource' % {'res': resource_type}
@ -531,7 +666,7 @@ class PowerMaxRest(object):
@retry(retry_exc_tuple, interval=2, retries=3)
def delete_resource(
self, array, category, resource_type, resource_name,
payload=None, private='', params=None):
payload=None, private=False, params=None):
"""Delete a provisioning resource.
:param array: the array serial number
@ -542,8 +677,9 @@ class PowerMaxRest(object):
:param private: empty string or '/private' if private url
:param params: dict of optional query params
"""
target_uri = self._build_uri(array, category, resource_type,
resource_name, private)
target_uri = self.build_uri(
array, category, resource_type, resource_name=resource_name,
private=private)
status_code, message = self.request(target_uri, DELETE,
request_object=payload,
params=params)
@ -557,7 +693,7 @@ class PowerMaxRest(object):
:returns: array_details -- dict or None
"""
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:
LOG.error("Cannot connect to array %(array)s.",
{'array': array})
@ -570,7 +706,7 @@ class PowerMaxRest(object):
:returns: tag list -- list or empty list
"""
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')
def is_next_gen_array(self, array):
@ -967,6 +1103,12 @@ class PowerMaxRest(object):
device_id = t_list[(len(t_list) - 1)]
device_id = device_id[1:-1]
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:
self.get_volume(array, device_id)
except Exception as e:
@ -1839,7 +1981,7 @@ class PowerMaxRest(object):
array_capabilities = None
target_uri = ("/%s/replication/capabilities/symmetrix"
% U4V_VERSION)
capabilities = self._get_request(
capabilities = self.get_request(
target_uri, 'replication capabilities')
if capabilities:
symm_list = capabilities['symmetrixCapability']
@ -2245,19 +2387,26 @@ class PowerMaxRest(object):
snap_src_dict_list.append(snap_src_dict)
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['state'] = snap_tgt['state']
snap_tgt_dict['copy_mode'] = snap_tgt['copy']
snap_tgt_dict['state'] = snap_tgt.get('state')
snap_tgt_dict['copy_mode'] = snap_tgt.get('copy')
vol_info = self._get_private_volume(array, device_id)
vol_tf_sessions = vol_info['timeFinderInfo']['snapVXSession']
for session in vol_tf_sessions:
if session.get('tgtSrcSnapshotGenInfo'):
snap_tgt_link = session.get('tgtSrcSnapshotGenInfo')
snap_tgt_dict['snap_name'] = snap_tgt_link['snapshotName']
snap_tgt_dict['expired'] = snap_tgt_link['expired']
snap_tgt_dict['generation'] = snap_tgt_link['generation']
if vol_info.get('timeFinderInfo'):
vol_tf_sessions = vol_info.get(
'timeFinderInfo').get('snapVXSession')
if vol_tf_sessions:
for session in vol_tf_sessions:
if session.get('tgtSrcSnapshotGenInfo'):
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
@ -3037,8 +3186,8 @@ class PowerMaxRest(object):
params = {'to': end_position, 'from': start_position}
target_uri = ('/common/Iterator/%(iterator_id)s/page' % {
'iterator_id': iterator_id})
iterator_response = self._get_request(target_uri, 'iterator',
params)
iterator_response = self.get_request(target_uri, 'iterator',
params)
try:
iterator_result += iterator_response['result']
start_position += max_page_size

View File

@ -1,4 +1,4 @@
# Copyright (c) 2017-2018 Dell Inc. or its subsidiaries.
# Copyright (c) 2020 Dell Inc. or its subsidiaries.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may

View File

@ -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.