Browse Source

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

changes/61/742861/2
Zuul 6 days ago
committed by Gerrit Code Review
parent
commit
d68649855d
20 changed files with 325 additions and 84 deletions
  1. +12
    -4
      cinder/tests/unit/volume/drivers/dell_emc/powermax/powermax_data.py
  2. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/powermax_fake_objects.py
  3. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py
  4. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_fc.py
  5. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_iscsi.py
  6. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_masking.py
  7. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_metadata.py
  8. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_provision.py
  9. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py
  10. +81
    -4
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py
  11. +1
    -1
      cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_utils.py
  12. +9
    -9
      cinder/volume/drivers/dell_emc/powermax/common.py
  13. +3
    -2
      cinder/volume/drivers/dell_emc/powermax/fc.py
  14. +3
    -2
      cinder/volume/drivers/dell_emc/powermax/iscsi.py
  15. +1
    -1
      cinder/volume/drivers/dell_emc/powermax/masking.py
  16. +1
    -1
      cinder/volume/drivers/dell_emc/powermax/metadata.py
  17. +1
    -1
      cinder/volume/drivers/dell_emc/powermax/provision.py
  18. +199
    -50
      cinder/volume/drivers/dell_emc/powermax/rest.py
  19. +1
    -1
      cinder/volume/drivers/dell_emc/powermax/utils.py
  20. +5
    -0
      releasenotes/notes/powermax-91-to-92-endpoints-bb467c8aca0165dd.yaml

+ 12
- 4
cinder/tests/unit/volume/drivers/dell_emc/powermax/powermax_data.py 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}]

+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/powermax_fake_objects.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_fc.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_iscsi.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_masking.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_metadata.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_provision.py 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


+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py 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


+ 81
- 4
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_rest.py 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})

+ 1
- 1
cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_utils.py 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


+ 9
- 9
cinder/volume/drivers/dell_emc/powermax/common.py 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: "


+ 3
- 2
cinder/volume/drivers/dell_emc/powermax/fc.py 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"


+ 3
- 2
cinder/volume/drivers/dell_emc/powermax/iscsi.py 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"


+ 1
- 1
cinder/volume/drivers/dell_emc/powermax/masking.py 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


+ 1
- 1
cinder/volume/drivers/dell_emc/powermax/metadata.py 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


+ 1
- 1
cinder/volume/drivers/dell_emc/powermax/provision.py 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


+ 199
- 50
cinder/volume/drivers/dell_emc/powermax/rest.py 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 args:
target_uri = self._build_uri_legacy_args(*args, **kwargs)
else:
target_uri = self._build_uri_kwargs(**kwargs)

return target_uri

@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)s' % {
'resource_name': 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):
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


+ 1
- 1
cinder/volume/drivers/dell_emc/powermax/utils.py 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


+ 5
- 0
releasenotes/notes/powermax-91-to-92-endpoints-bb467c8aca0165dd.yaml 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.

Loading…
Cancel
Save