Merge "PowerMax Driver - Changing from 91 to 92 REST endpoints"

This commit is contained in:
Zuul 2020-07-28 23:20:52 +00:00 committed by Gerrit Code Review
commit d68649855d
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. # 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}]

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

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

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

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

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

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

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

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

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. # 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})

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

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. # 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: "

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. # 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"

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. # 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"

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

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

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

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

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

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.