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:
parent
70beaeb788
commit
d592b2ad0d
@ -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'):
|
||||||
|
vol_tf_sessions = vol_info.get(
|
||||||
|
'timeFinderInfo').get('snapVXSession')
|
||||||
|
if vol_tf_sessions:
|
||||||
for session in vol_tf_sessions:
|
for session in vol_tf_sessions:
|
||||||
if session.get('tgtSrcSnapshotGenInfo'):
|
if session.get('tgtSrcSnapshotGenInfo'):
|
||||||
snap_tgt_link = session.get('tgtSrcSnapshotGenInfo')
|
snap_tgt_link = session.get(
|
||||||
snap_tgt_dict['snap_name'] = snap_tgt_link['snapshotName']
|
'tgtSrcSnapshotGenInfo')
|
||||||
snap_tgt_dict['expired'] = snap_tgt_link['expired']
|
snap_tgt_dict['snap_name'] = snap_tgt_link.get(
|
||||||
snap_tgt_dict['generation'] = snap_tgt_link['generation']
|
'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,7 +3186,7 @@ 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']
|
||||||
|
@ -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
Block a user