Merge "Dell PowerMax: Added timeout into rest API call."
This commit is contained in:
commit
b2a96e82fc
@ -54,7 +54,7 @@ class FakeRequestsSession(object):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.data = tpd.PowerMaxData()
|
self.data = tpd.PowerMaxData()
|
||||||
|
|
||||||
def request(self, method, url, params=None, data=None):
|
def request(self, method, url, params=None, data=None, timeout=None):
|
||||||
return_object = ''
|
return_object = ''
|
||||||
status_code = 200
|
status_code = 200
|
||||||
if method == 'GET':
|
if method == 'GET':
|
||||||
@ -69,6 +69,12 @@ class FakeRequestsSession(object):
|
|||||||
elif method == 'TIMEOUT':
|
elif method == 'TIMEOUT':
|
||||||
raise requests.Timeout
|
raise requests.Timeout
|
||||||
|
|
||||||
|
elif method == 'READTIMEOUT' and timeout is not None:
|
||||||
|
raise requests.ReadTimeout
|
||||||
|
|
||||||
|
elif method == 'CONNECTTIMEOUT' and timeout is not None:
|
||||||
|
raise requests.ConnectTimeout
|
||||||
|
|
||||||
elif method == 'EXCEPTION':
|
elif method == 'EXCEPTION':
|
||||||
raise Exception
|
raise Exception
|
||||||
|
|
||||||
@ -326,6 +332,8 @@ class FakeConfiguration(object):
|
|||||||
self.initiator_check = False
|
self.initiator_check = False
|
||||||
self.powermax_service_level = None
|
self.powermax_service_level = None
|
||||||
self.vmax_workload = None
|
self.vmax_workload = None
|
||||||
|
self.rest_api_connect_timeout = 30
|
||||||
|
self.rest_api_read_timeout = 30
|
||||||
if replication_device:
|
if replication_device:
|
||||||
self.replication_device = replication_device
|
self.replication_device = replication_device
|
||||||
for key, value in kwargs.items():
|
for key, value in kwargs.items():
|
||||||
@ -422,3 +430,9 @@ class FakeConfiguration(object):
|
|||||||
|
|
||||||
def append_config_values(self, values):
|
def append_config_values(self, values):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def set_rest_api_connect_timeout(self, value):
|
||||||
|
self.rest_api_connect_timeout = value
|
||||||
|
|
||||||
|
def set_rest_api_read_timeout(self, value):
|
||||||
|
self.rest_api_read_timeout = value
|
||||||
|
@ -3217,7 +3217,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
{'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
||||||
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
'PortGroup': [self.data.port_group_name_i]})
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 30})
|
||||||
old_conf = tpfo.FakeConfiguration(None, 'CommonTests', 1, 1)
|
old_conf = tpfo.FakeConfiguration(None, 'CommonTests', 1, 1)
|
||||||
configuration = tpfo.FakeConfiguration(
|
configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
@ -3236,7 +3237,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
{'RestServerIp': '1.1.1.1', 'RestServerPort': 3448,
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 3448,
|
||||||
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
'PortGroup': [self.data.port_group_name_i]})
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 30})
|
||||||
configuration = tpfo.FakeConfiguration(
|
configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
powermax_array=self.data.array, powermax_srp='SRP_1',
|
powermax_array=self.data.array, powermax_srp='SRP_1',
|
||||||
@ -3251,7 +3253,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
{'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
||||||
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
'PortGroup': [self.data.port_group_name_i]})
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 30})
|
||||||
configuration = tpfo.FakeConfiguration(
|
configuration = tpfo.FakeConfiguration(
|
||||||
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
None, 'CommonTests', 1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
powermax_array=self.data.array, powermax_srp='SRP_1',
|
powermax_array=self.data.array, powermax_srp='SRP_1',
|
||||||
@ -3606,7 +3609,8 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
||||||
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
'SerialNumber': '000197800123', 'srpName': 'SRP_1',
|
'SerialNumber': '000197800123', 'srpName': 'SRP_1',
|
||||||
'PortGroup': ['OS-fibre-PG']}
|
'PortGroup': ['OS-fibre-PG'],
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 30}
|
||||||
|
|
||||||
self.mock_object(self.common.configuration, 'powermax_service_level',
|
self.mock_object(self.common.configuration, 'powermax_service_level',
|
||||||
None)
|
None)
|
||||||
@ -3662,7 +3666,9 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
'RestServerIp': '1.1.1.1', 'RestServerPort': 8443,
|
||||||
'RestUserName': 'test', 'RestPassword': 'test',
|
'RestUserName': 'test', 'RestPassword': 'test',
|
||||||
'SerialNumber': '000197800123', 'srpName': 'SRP_1',
|
'SerialNumber': '000197800123', 'srpName': 'SRP_1',
|
||||||
'PortGroup': None, 'SSLVerify': True}}
|
'PortGroup': None,
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 30,
|
||||||
|
'SSLVerify': True}}
|
||||||
self.mock_object(self.common, 'configuration', configuration)
|
self.mock_object(self.common, 'configuration', configuration)
|
||||||
self.common._get_u4p_failover_info()
|
self.common._get_u4p_failover_info()
|
||||||
self.assertIsNotNone(self.rest.u4p_failover_targets)
|
self.assertIsNotNone(self.rest.u4p_failover_targets)
|
||||||
@ -4839,3 +4845,58 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
self.data.remote_array, self.data.srp, 'Diamond', 'DSS',
|
self.data.remote_array, self.data.srp, 'Diamond', 'DSS',
|
||||||
self.data.rep_extra_specs_rep_config, False, is_re=True,
|
self.data.rep_extra_specs_rep_config, False, is_re=True,
|
||||||
rep_mode='Synchronous')
|
rep_mode='Synchronous')
|
||||||
|
|
||||||
|
def test_get_connect_timeout_from_cinder_config(self):
|
||||||
|
kwargs_expected = (
|
||||||
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 3448,
|
||||||
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 120, 'RestAPIReadTimeout': 30})
|
||||||
|
configuration = tpfo.FakeConfiguration(
|
||||||
|
None, 'CommonTests',
|
||||||
|
1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
|
powermax_array=self.data.array, powermax_srp='SRP_1',
|
||||||
|
san_password='smc', san_api_port=3448,
|
||||||
|
powermax_port_groups=[self.data.port_group_name_i])
|
||||||
|
configuration.set_rest_api_connect_timeout(120)
|
||||||
|
self.mock_object(self.common, 'configuration', configuration)
|
||||||
|
kwargs_returned = self.common.get_attributes_from_cinder_config()
|
||||||
|
self.assertEqual(kwargs_expected, kwargs_returned)
|
||||||
|
|
||||||
|
def test_get_read_timeout_from_cinder_config(self):
|
||||||
|
kwargs_expected = (
|
||||||
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 3448,
|
||||||
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 30, 'RestAPIReadTimeout': 120})
|
||||||
|
configuration = tpfo.FakeConfiguration(
|
||||||
|
None, 'CommonTests',
|
||||||
|
1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
|
powermax_array=self.data.array, powermax_srp='SRP_1',
|
||||||
|
san_password='smc', san_api_port=3448,
|
||||||
|
powermax_port_groups=[self.data.port_group_name_i])
|
||||||
|
configuration.set_rest_api_read_timeout(120)
|
||||||
|
self.mock_object(self.common, 'configuration', configuration)
|
||||||
|
kwargs_returned = self.common.get_attributes_from_cinder_config()
|
||||||
|
self.assertEqual(kwargs_expected, kwargs_returned)
|
||||||
|
|
||||||
|
def test_get_connect_and_read_timeout_from_cinder_config(self):
|
||||||
|
kwargs_expected = (
|
||||||
|
{'RestServerIp': '1.1.1.1', 'RestServerPort': 3448,
|
||||||
|
'RestUserName': 'smc', 'RestPassword': 'smc', 'SSLVerify': False,
|
||||||
|
'SerialNumber': self.data.array, 'srpName': 'SRP_1',
|
||||||
|
'PortGroup': [self.data.port_group_name_i],
|
||||||
|
'RestAPIConnectTimeout': 90, 'RestAPIReadTimeout': 90})
|
||||||
|
configuration = tpfo.FakeConfiguration(
|
||||||
|
None, 'CommonTests',
|
||||||
|
1, 1, san_ip='1.1.1.1', san_login='smc',
|
||||||
|
powermax_array=self.data.array, powermax_srp='SRP_1',
|
||||||
|
san_password='smc', san_api_port=3448,
|
||||||
|
powermax_port_groups=[self.data.port_group_name_i])
|
||||||
|
configuration.set_rest_api_connect_timeout(90)
|
||||||
|
configuration.set_rest_api_read_timeout(90)
|
||||||
|
self.mock_object(self.common, 'configuration', configuration)
|
||||||
|
kwargs_returned = self.common.get_attributes_from_cinder_config()
|
||||||
|
self.assertEqual(kwargs_expected, kwargs_returned)
|
||||||
|
@ -63,6 +63,14 @@ class PowerMaxRestTest(test.TestCase):
|
|||||||
self.assertRaises(requests.exceptions.Timeout,
|
self.assertRaises(requests.exceptions.Timeout,
|
||||||
self.rest.request, '', 'TIMEOUT')
|
self.rest.request, '', 'TIMEOUT')
|
||||||
|
|
||||||
|
def test_rest_request_read_timeout_exception(self):
|
||||||
|
self.assertRaises(requests.exceptions.ReadTimeout,
|
||||||
|
self.rest.request, '', 'READTIMEOUT', (60, 60))
|
||||||
|
|
||||||
|
def test_rest_request_connect_timeout_exception(self):
|
||||||
|
self.assertRaises(requests.exceptions.ConnectTimeout,
|
||||||
|
self.rest.request, '', 'CONNECTTIMEOUT', (60, 60))
|
||||||
|
|
||||||
def test_rest_request_connection_exception(self):
|
def test_rest_request_connection_exception(self):
|
||||||
self.assertRaises(requests.exceptions.ConnectionError,
|
self.assertRaises(requests.exceptions.ConnectionError,
|
||||||
self.rest.request, '', 'CONNECTION')
|
self.rest.request, '', 'CONNECTION')
|
||||||
|
@ -146,7 +146,15 @@ powermax_opts = [
|
|||||||
help='Metric used for port group load calculation.'),
|
help='Metric used for port group load calculation.'),
|
||||||
cfg.StrOpt(utils.PORT_LOAD_METRIC,
|
cfg.StrOpt(utils.PORT_LOAD_METRIC,
|
||||||
default='PercentBusy',
|
default='PercentBusy',
|
||||||
help='Metric used for port load calculation.')]
|
help='Metric used for port load calculation.'),
|
||||||
|
cfg.IntOpt(utils.REST_API_CONNECT_TIMEOUT,
|
||||||
|
default=30, min=1,
|
||||||
|
help='Use this value to specify connect '
|
||||||
|
'timeout value (in seconds) for rest call.'),
|
||||||
|
cfg.IntOpt(utils.REST_API_READ_TIMEOUT,
|
||||||
|
default=30, min=1,
|
||||||
|
help='Use this value to specify read '
|
||||||
|
'timeout value (in seconds) for rest call.')]
|
||||||
|
|
||||||
|
|
||||||
CONF.register_opts(powermax_opts, group=configuration.SHARED_CONF_GROUP)
|
CONF.register_opts(powermax_opts, group=configuration.SHARED_CONF_GROUP)
|
||||||
@ -7122,6 +7130,10 @@ class PowerMaxCommon(object):
|
|||||||
workload = self.configuration.safe_get(utils.VMAX_WORKLOAD)
|
workload = self.configuration.safe_get(utils.VMAX_WORKLOAD)
|
||||||
port_groups = self.configuration.safe_get(
|
port_groups = self.configuration.safe_get(
|
||||||
utils.POWERMAX_PORT_GROUPS)
|
utils.POWERMAX_PORT_GROUPS)
|
||||||
|
rest_api_connect_timeout = (
|
||||||
|
self.configuration.safe_get(utils.REST_API_CONNECT_TIMEOUT))
|
||||||
|
rest_api_read_timeout = (
|
||||||
|
self.configuration.safe_get(utils.REST_API_READ_TIMEOUT))
|
||||||
|
|
||||||
kwargs = (
|
kwargs = (
|
||||||
{'RestServerIp': self.configuration.safe_get(
|
{'RestServerIp': self.configuration.safe_get(
|
||||||
@ -7131,7 +7143,9 @@ class PowerMaxCommon(object):
|
|||||||
'RestPassword': password,
|
'RestPassword': password,
|
||||||
'SerialNumber': serial_number,
|
'SerialNumber': serial_number,
|
||||||
'srpName': srp_name,
|
'srpName': srp_name,
|
||||||
'PortGroup': port_groups})
|
'PortGroup': port_groups,
|
||||||
|
utils.REST_API_CONNECT_TIMEOUT_KEY: rest_api_connect_timeout,
|
||||||
|
utils.REST_API_READ_TIMEOUT_KEY: rest_api_read_timeout})
|
||||||
|
|
||||||
if self.configuration.safe_get('driver_ssl_cert_verify'):
|
if self.configuration.safe_get('driver_ssl_cert_verify'):
|
||||||
if self.configuration.safe_get('driver_ssl_cert_path'):
|
if self.configuration.safe_get('driver_ssl_cert_path'):
|
||||||
|
@ -93,6 +93,8 @@ class PowerMaxRest(object):
|
|||||||
self.ucode_minor_level = None
|
self.ucode_minor_level = None
|
||||||
self.is_snap_id = False
|
self.is_snap_id = False
|
||||||
self.u4p_version = None
|
self.u4p_version = None
|
||||||
|
self.rest_api_connect_timeout = 30
|
||||||
|
self.rest_api_read_timeout = 30
|
||||||
|
|
||||||
def set_rest_credentials(self, array_info):
|
def set_rest_credentials(self, array_info):
|
||||||
"""Given the array record set the rest server credentials.
|
"""Given the array record set the rest server credentials.
|
||||||
@ -108,6 +110,14 @@ class PowerMaxRest(object):
|
|||||||
self.base_uri = ("https://%(ip_port)s/univmax/restapi" % {
|
self.base_uri = ("https://%(ip_port)s/univmax/restapi" % {
|
||||||
'ip_port': ip_port})
|
'ip_port': ip_port})
|
||||||
self.session = self._establish_rest_session()
|
self.session = self._establish_rest_session()
|
||||||
|
new_connect_timeout = (
|
||||||
|
int(array_info.get(utils.REST_API_CONNECT_TIMEOUT_KEY, 0)))
|
||||||
|
if new_connect_timeout > 0:
|
||||||
|
self.rest_api_connect_timeout = new_connect_timeout
|
||||||
|
new_read_timeout = (
|
||||||
|
int(array_info.get(utils.REST_API_READ_TIMEOUT_KEY, 0)))
|
||||||
|
if new_read_timeout > 0:
|
||||||
|
self.rest_api_read_timeout = new_read_timeout
|
||||||
|
|
||||||
def set_residuals(self, serial_number):
|
def set_residuals(self, serial_number):
|
||||||
"""Set ucode and snapid information.
|
"""Set ucode and snapid information.
|
||||||
@ -243,18 +253,19 @@ class PowerMaxRest(object):
|
|||||||
url = ("%(self.base_uri)s%(target_uri)s" % {
|
url = ("%(self.base_uri)s%(target_uri)s" % {
|
||||||
'self.base_uri': self.base_uri,
|
'self.base_uri': self.base_uri,
|
||||||
'target_uri': target_uri})
|
'target_uri': target_uri})
|
||||||
|
timeout = (self.rest_api_connect_timeout,
|
||||||
|
self.rest_api_read_timeout)
|
||||||
if request_object:
|
if request_object:
|
||||||
response = self.session.request(
|
response = self.session.request(
|
||||||
method=method, url=url,
|
method=method, url=url,
|
||||||
data=json.dumps(request_object, sort_keys=True,
|
data=json.dumps(request_object, sort_keys=True,
|
||||||
indent=4))
|
indent=4), timeout=timeout)
|
||||||
elif params:
|
elif params:
|
||||||
response = self.session.request(
|
response = self.session.request(
|
||||||
method=method, url=url, params=params)
|
method=method, url=url, params=params, timeout=timeout)
|
||||||
else:
|
else:
|
||||||
response = self.session.request(
|
response = self.session.request(
|
||||||
method=method, url=url)
|
method=method, url=url, timeout=timeout)
|
||||||
|
|
||||||
status_code = response.status_code
|
status_code = response.status_code
|
||||||
if retry and status_code and status_code in [STATUS_200,
|
if retry and status_code and status_code in [STATUS_200,
|
||||||
@ -289,6 +300,10 @@ class PowerMaxRest(object):
|
|||||||
|
|
||||||
except (r_exc.Timeout, r_exc.ConnectionError,
|
except (r_exc.Timeout, r_exc.ConnectionError,
|
||||||
r_exc.HTTPError) as e:
|
r_exc.HTTPError) as e:
|
||||||
|
if isinstance(e, r_exc.Timeout):
|
||||||
|
msg = _("The %s request to URL %s failed with timeout "
|
||||||
|
"exception %s" % (method, url, str(e)))
|
||||||
|
LOG.error(msg)
|
||||||
if self.u4p_failover_enabled or u4p_check:
|
if self.u4p_failover_enabled or u4p_check:
|
||||||
if not u4p_check:
|
if not u4p_check:
|
||||||
# Failover process
|
# Failover process
|
||||||
|
@ -136,6 +136,10 @@ POWERMAX_ARRAY_TAG_LIST = 'powermax_array_tag_list'
|
|||||||
POWERMAX_SHORT_HOST_NAME_TEMPLATE = 'powermax_short_host_name_template'
|
POWERMAX_SHORT_HOST_NAME_TEMPLATE = 'powermax_short_host_name_template'
|
||||||
POWERMAX_PORT_GROUP_NAME_TEMPLATE = 'powermax_port_group_name_template'
|
POWERMAX_PORT_GROUP_NAME_TEMPLATE = 'powermax_port_group_name_template'
|
||||||
PORT_GROUP_LABEL = 'port_group_label'
|
PORT_GROUP_LABEL = 'port_group_label'
|
||||||
|
REST_API_CONNECT_TIMEOUT = 'rest_api_connect_timeout'
|
||||||
|
REST_API_READ_TIMEOUT = 'rest_api_read_timeout'
|
||||||
|
REST_API_CONNECT_TIMEOUT_KEY = 'RestAPIConnectTimeout'
|
||||||
|
REST_API_READ_TIMEOUT_KEY = 'RestAPIReadTimeout'
|
||||||
|
|
||||||
# Array Models, Service Levels & Workloads
|
# Array Models, Service Levels & Workloads
|
||||||
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
PowerMax Driver `bug #2051830
|
||||||
|
<https://bugs.launchpad.net/cinder/+bug/2051830>`_: REST
|
||||||
|
API calls to the PowerMax backend did not have a timeout
|
||||||
|
set, which could result in cinder waiting forever.
|
||||||
|
This fix introduces two configuration options,
|
||||||
|
``rest_api_connect_timeout`` and ``rest_api_read_timeout``,
|
||||||
|
to control timeouts when connecting to the backend.
|
||||||
|
The default value of each is 30 seconds.
|
Loading…
Reference in New Issue
Block a user