Dell EMC SC: Added timeout options for SC driver

Added dell_api_async_rest_timeout and dell_api_sync_rest_timeout to allow
setting of async and sync timeouts for the Dell EMC SC REST API.

The user should generally not set these. They should be set only when
instructed by support.

Fixed a couple of comments.

Updated documentation.

Change-Id: Id8fd27d83e2f97070f67523c9c2d8c59f66e6caa
This commit is contained in:
Tom Swanson 2018-02-26 13:50:22 -06:00
parent 015b105399
commit 1d6ad6ef17
7 changed files with 130 additions and 32 deletions

View File

@ -14,6 +14,7 @@
import ddt import ddt
import eventlet import eventlet
import json
import mock import mock
import requests import requests
from requests import models from requests import models
@ -1683,6 +1684,8 @@ class DellSCSanAPITestCase(test.TestCase):
self.configuration.target_port = 3260 self.configuration.target_port = 3260
self._context = context.get_admin_context() self._context = context.get_admin_context()
self.apiversion = '2.0' self.apiversion = '2.0'
self.asynctimeout = 15
self.synctimeout = 30
# Set up the SCApi # Set up the SCApi
self.scapi = storagecenter_api.SCApi( self.scapi = storagecenter_api.SCApi(
@ -1691,6 +1694,8 @@ class DellSCSanAPITestCase(test.TestCase):
self.configuration.san_login, self.configuration.san_login,
self.configuration.san_password, self.configuration.san_password,
self.configuration.dell_sc_verify_cert, self.configuration.dell_sc_verify_cert,
self.asynctimeout,
self.synctimeout,
self.apiversion) self.apiversion)
# Set up the scapi configuration vars # Set up the scapi configuration vars
@ -8635,6 +8640,8 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
self.configuration.target_ip_address = '192.168.1.1' self.configuration.target_ip_address = '192.168.1.1'
self.configuration.target_port = 3260 self.configuration.target_port = 3260
self._context = context.get_admin_context() self._context = context.get_admin_context()
self.asynctimeout = 15
self.synctimeout = 30
self.apiversion = '2.0' self.apiversion = '2.0'
# Set up the SCApi # Set up the SCApi
@ -8644,6 +8651,8 @@ class DellSCSanAPIConnectionTestCase(test.TestCase):
self.configuration.san_login, self.configuration.san_login,
self.configuration.san_password, self.configuration.san_password,
self.configuration.dell_sc_verify_cert, self.configuration.dell_sc_verify_cert,
self.asynctimeout,
self.synctimeout,
self.apiversion) self.apiversion)
# Set up the scapi configuration vars # Set up the scapi configuration vars
@ -8772,10 +8781,12 @@ class DellHttpClientTestCase(test.TestCase):
self.user = 'johnnyuser' self.user = 'johnnyuser'
self.password = 'password' self.password = 'password'
self.verify = False self.verify = False
self.asynctimeout = 15
self.synctimeout = 30
self.apiversion = '3.1' self.apiversion = '3.1'
self.httpclient = storagecenter_api.HttpClient( self.httpclient = storagecenter_api.HttpClient(
self.host, self.port, self.user, self.password, self.host, self.port, self.user, self.password, self.verify,
self.verify, self.apiversion) self.asynctimeout, self.synctimeout, self.apiversion)
def test_get_async_url(self): def test_get_async_url(self):
url = self.httpclient._get_async_url(self.ASYNCTASK) url = self.httpclient._get_async_url(self.ASYNCTASK)
@ -8904,8 +8915,36 @@ class DellHttpClientTestCase(test.TestCase):
expected_headers = self.httpclient.header.copy() expected_headers = self.httpclient.header.copy()
mock_get.assert_called_once_with('https://localhost:3033/api/rest/url', mock_get.assert_called_once_with('https://localhost:3033/api/rest/url',
headers=expected_headers, headers=expected_headers,
timeout=30,
verify=False) verify=False)
@mock.patch.object(requests.Session, 'post', return_value=RESPONSE_200)
@mock.patch.object(storagecenter_api.HttpClient, '_rest_ret')
def test_post(self, mock_rest_ret, mock_post):
payload = {'payload': 'payload'}
self.httpclient.post('url', payload, True)
expected_headers = self.httpclient.header.copy()
expected_headers['async'] = 'True'
mock_post.assert_called_once_with(
'https://localhost:3033/api/rest/url',
data=json.dumps(payload, ensure_ascii=False).encode('utf-8'),
headers=expected_headers,
timeout=15,
verify=False)
@mock.patch.object(requests.Session, 'post', return_value=RESPONSE_200)
@mock.patch.object(storagecenter_api.HttpClient, '_rest_ret')
def test_post_sync(self, mock_rest_ret, mock_post):
payload = {'payload': 'payload'}
self.httpclient.post('url', payload, False)
expected_headers = self.httpclient.header.copy()
mock_post.assert_called_once_with(
'https://localhost:3033/api/rest/url',
data=json.dumps(payload, ensure_ascii=False).encode('utf-8'),
headers=expected_headers,
timeout=30,
verify=False)
class DellStorageCenterApiHelperTestCase(test.TestCase): class DellStorageCenterApiHelperTestCase(test.TestCase):

View File

@ -79,7 +79,8 @@ class HttpClient(object):
Helper for making the REST calls. Helper for making the REST calls.
""" """
def __init__(self, host, port, user, password, verify, apiversion): def __init__(self, host, port, user, password,
verify, asynctimeout, synctimeout, apiversion):
"""HttpClient handles the REST requests. """HttpClient handles the REST requests.
:param host: IP address of the Dell Data Collector. :param host: IP address of the Dell Data Collector.
@ -88,6 +89,8 @@ class HttpClient(object):
:param password: Password. :param password: Password.
:param verify: Boolean indicating whether certificate verification :param verify: Boolean indicating whether certificate verification
should be turned on or not. should be turned on or not.
:param asynctimeout: async REST call time out.
:param synctimeout: sync REST call time out.
:param apiversion: Dell API version. :param apiversion: Dell API version.
""" """
self.baseUrl = 'https://%s:%s/' % (host, port) self.baseUrl = 'https://%s:%s/' % (host, port)
@ -100,6 +103,8 @@ class HttpClient(object):
self.header['Accept'] = 'application/json' self.header['Accept'] = 'application/json'
self.header['x-dell-api-version'] = apiversion self.header['x-dell-api-version'] = apiversion
self.verify = verify self.verify = verify
self.asynctimeout = asynctimeout
self.synctimeout = synctimeout
# Verify is a configurable option. So if this is false do not # Verify is a configurable option. So if this is false do not
# spam the c-vol log. # spam the c-vol log.
@ -230,7 +235,8 @@ class HttpClient(object):
LOG.debug('get: %(url)s', {'url': url}) LOG.debug('get: %(url)s', {'url': url})
rest_response = self.session.get(self.__formatUrl(url), rest_response = self.session.get(self.__formatUrl(url),
headers=self.header, headers=self.header,
verify=self.verify) verify=self.verify,
timeout=self.synctimeout)
if (rest_response and rest_response.status_code == ( if (rest_response and rest_response.status_code == (
http_client.BAD_REQUEST)) and ( http_client.BAD_REQUEST)) and (
@ -248,7 +254,9 @@ class HttpClient(object):
data=json.dumps(payload, data=json.dumps(payload,
ensure_ascii=False).encode('utf-8'), ensure_ascii=False).encode('utf-8'),
headers=self._get_header(async_call), headers=self._get_header(async_call),
verify=self.verify), async_call) verify=self.verify, timeout=(
self.asynctimeout if async_call else self.synctimeout)),
async_call)
@utils.retry(exceptions=(requests.ConnectionError,)) @utils.retry(exceptions=(requests.ConnectionError,))
def put(self, url, payload, async_call=False): def put(self, url, payload, async_call=False):
@ -260,14 +268,18 @@ class HttpClient(object):
data=json.dumps(payload, data=json.dumps(payload,
ensure_ascii=False).encode('utf-8'), ensure_ascii=False).encode('utf-8'),
headers=self._get_header(async_call), headers=self._get_header(async_call),
verify=self.verify), async_call) verify=self.verify, timeout=(
self.asynctimeout if async_call else self.synctimeout)),
async_call)
@utils.retry(exceptions=(requests.ConnectionError,)) @utils.retry(exceptions=(requests.ConnectionError,))
def delete(self, url, payload=None, async_call=False): def delete(self, url, payload=None, async_call=False):
LOG.debug('delete: %(url)s data: %(payload)s', LOG.debug('delete: %(url)s data: %(payload)s',
{'url': url, 'payload': payload}) {'url': url, 'payload': payload})
named = {'headers': self._get_header(async_call), named = {'headers': self._get_header(async_call),
'verify': self.verify} 'verify': self.verify,
'timeout': (
self.asynctimeout if async_call else self.synctimeout)}
if payload: if payload:
named['data'] = json.dumps( named['data'] = json.dumps(
payload, ensure_ascii=False).encode('utf-8') payload, ensure_ascii=False).encode('utf-8')
@ -336,6 +348,8 @@ class SCApiHelper(object):
self.san_login, self.san_login,
self.san_password, self.san_password,
self.config.dell_sc_verify_cert, self.config.dell_sc_verify_cert,
self.config.dell_api_async_rest_timeout,
self.config.dell_api_sync_rest_timeout,
self.apiversion) self.apiversion)
# This instance is for a single backend. That backend has a # This instance is for a single backend. That backend has a
# few items of information we should save rather than passing them # few items of information we should save rather than passing them
@ -368,7 +382,7 @@ class SCApiHelper(object):
connection = None connection = None
LOG.info('open_connection to %(ssn)s at %(ip)s', LOG.info('open_connection to %(ssn)s at %(ip)s',
{'ssn': self.primaryssn, {'ssn': self.primaryssn,
'ip': self.config.san_ip}) 'ip': self.san_ip})
if self.primaryssn: if self.primaryssn:
try: try:
"""Open connection to REST API.""" """Open connection to REST API."""
@ -420,12 +434,14 @@ class SCApi(object):
3.6.0 - Server type support. 3.6.0 - Server type support.
3.7.0 - Support for Data Reduction, Group QOS and Volume QOS. 3.7.0 - Support for Data Reduction, Group QOS and Volume QOS.
4.0.0 - Driver moved to dell_emc. 4.0.0 - Driver moved to dell_emc.
4.1.0 - Timeouts added to rest calls.
""" """
APIDRIVERVERSION = '4.0.0' APIDRIVERVERSION = '4.1.0'
def __init__(self, host, port, user, password, verify, apiversion): def __init__(self, host, port, user, password, verify,
asynctimeout, synctimeout, apiversion):
"""This creates a connection to Dell SC or EM. """This creates a connection to Dell SC or EM.
:param host: IP address of the REST interface.. :param host: IP address of the REST interface..
@ -434,6 +450,8 @@ class SCApi(object):
:param password: Password. :param password: Password.
:param verify: Boolean indicating whether certificate verification :param verify: Boolean indicating whether certificate verification
should be turned on or not. should be turned on or not.
:param asynctimeout: async REST call time out.
:param synctimeout: sync REST call time out.
:param apiversion: Version used on login. :param apiversion: Version used on login.
""" """
self.notes = 'Created by Dell EMC Cinder Driver' self.notes = 'Created by Dell EMC Cinder Driver'
@ -454,8 +472,8 @@ class SCApi(object):
# Nothing other than Replication should care if we are direct connect # Nothing other than Replication should care if we are direct connect
# or not. # or not.
self.is_direct_connect = False self.is_direct_connect = False
self.client = HttpClient(host, port, user, password, self.client = HttpClient(host, port, user, password, verify,
verify, apiversion) asynctimeout, synctimeout, apiversion)
def __enter__(self): def __enter__(self):
return self return self

View File

@ -58,6 +58,12 @@ common_opts = [
cfg.PortOpt('secondary_sc_api_port', cfg.PortOpt('secondary_sc_api_port',
default=3033, default=3033,
help='Secondary Dell API port'), help='Secondary Dell API port'),
cfg.IntOpt('dell_api_async_rest_timeout',
default=15,
help='Dell SC API async call default timeout in seconds.'),
cfg.IntOpt('dell_api_sync_rest_timeout',
default=30,
help='Dell SC API sync call default timeout in seconds.'),
cfg.MultiOpt('excluded_domain_ip', cfg.MultiOpt('excluded_domain_ip',
item_type=types.IPAddress(), item_type=types.IPAddress(),
default=None, default=None,

View File

@ -34,7 +34,7 @@ class SCFCDriver(storagecenter_common.SCCommonDriver,
"""Implements commands for Dell Storage Center FC management. """Implements commands for Dell Storage Center FC management.
To enable the driver add the following line to the cinder configuration: To enable the driver add the following line to the cinder configuration:
volume_driver=cinder.volume.drivers.dell_emc.sc.dell_storagecenter_fc.\ volume_driver=cinder.volume.drivers.dell_emc.sc.storagecenter_fc.\
SCFCDriver SCFCDriver
Version history: Version history:
@ -62,10 +62,11 @@ class SCFCDriver(storagecenter_common.SCCommonDriver,
3.6.0 - Server type support. 3.6.0 - Server type support.
3.7.0 - Support for Data Reduction, Group QOS and Volume QOS. 3.7.0 - Support for Data Reduction, Group QOS and Volume QOS.
4.0.0 - Driver moved to dell_emc. 4.0.0 - Driver moved to dell_emc.
4.1.0 - Timeouts added to rest calls.
""" """
VERSION = '4.0.0' VERSION = '4.1.0'
CI_WIKI_NAME = "Dell_EMC_SC_Series_CI" CI_WIKI_NAME = "Dell_EMC_SC_Series_CI"

View File

@ -34,7 +34,7 @@ class SCISCSIDriver(storagecenter_common.SCCommonDriver,
To enable the driver add the following line to the cinder configuration: To enable the driver add the following line to the cinder configuration:
volume_driver=cinder.volume.drivers.dell_emc.sc.\ volume_driver=cinder.volume.drivers.dell_emc.sc.\
dell_storagecenter_iscsi.SCISCSIDriver storagecenter_iscsi.SCISCSIDriver
Version history: Version history:
@ -62,10 +62,11 @@ class SCISCSIDriver(storagecenter_common.SCCommonDriver,
3.6.0 - Server type support. 3.6.0 - Server type support.
3.7.0 - Support for Data Reduction, Group QOS and Volume QOS. 3.7.0 - Support for Data Reduction, Group QOS and Volume QOS.
4.0.0 - Driver moved to dell_emc. 4.0.0 - Driver moved to dell_emc.
4.1.0 - Timeouts added to rest calls.
""" """
VERSION = '4.0.0' VERSION = '4.1.0'
CI_WIKI_NAME = "Dell_EMC_SC_Series_CI" CI_WIKI_NAME = "Dell_EMC_SC_Series_CI"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -2,19 +2,19 @@
Dell EMC SC Series Fibre Channel and iSCSI drivers Dell EMC SC Series Fibre Channel and iSCSI drivers
================================================== ==================================================
The Dell Storage Center volume driver interacts with configured Storage The Dell EMC Storage Center volume driver interacts with configured Storage
Center arrays. Center arrays.
The Dell Storage Center driver manages Storage Center arrays through The Dell EMC Storage Center driver manages Storage Center arrays through
the Dell Storage Manager (DSM). DSM connection settings and Storage the Dell EMC Storage Manager (DSM). DSM connection settings and Storage
Center options are defined in the ``cinder.conf`` file. Center options are defined in the ``cinder.conf`` file.
Prerequisite: Dell Storage Manager 2015 R1 or later must be used. Prerequisite: Dell EMC Storage Manager 2015 R1 or later must be used.
Supported operations Supported operations
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
The Dell Storage Center volume driver provides the following Cinder The Dell EMC Storage Center volume driver provides the following Cinder
volume operations: volume operations:
- Create, delete, attach (map), and detach (unmap) volumes. - Create, delete, attach (map), and detach (unmap) volumes.
@ -33,7 +33,7 @@ volume operations:
Extra spec options Extra spec options
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
Volume type extra specs can be used to enable a variety of Dell Storage Volume type extra specs can be used to enable a variety of Dell EMC Storage
Center options. Selecting Storage Profiles, Replay Profiles, enabling Center options. Selecting Storage Profiles, Replay Profiles, enabling
replication, replication options including Live Volume and Active Replay replication, replication options including Live Volume and Active Replay
replication. replication.
@ -170,7 +170,7 @@ Use the following instructions to update the configuration file for iSCSI:
# Name to give this storage back-end # Name to give this storage back-end
volume_backend_name = delliscsi volume_backend_name = delliscsi
# The iSCSI driver to load # The iSCSI driver to load
volume_driver = cinder.volume.drivers.dell.dell_storagecenter_iscsi.DellStorageCenterISCSIDriver volume_driver = cinder.volume.drivers.dell_emc.sc.storagecenter_iscsi.SCISCSIDriver
# IP address of DSM # IP address of DSM
san_ip = 172.23.8.101 san_ip = 172.23.8.101
# DSM user name # DSM user name
@ -204,7 +204,8 @@ channel:
# Name to give this storage back-end # Name to give this storage back-end
volume_backend_name = dellfc volume_backend_name = dellfc
# The FC driver to load # The FC driver to load
volume_driver = cinder.volume.drivers.dell.dell_storagecenter_fc.DellStorageCenterFCDriver volume_driver = cinder.volume.drivers.dell_emc.sc.storagecenter_fc.SCFCDriver
# IP address of the DSM # IP address of the DSM
san_ip = 172.23.8.101 san_ip = 172.23.8.101
# DSM user name # DSM user name
@ -298,10 +299,10 @@ Simply specify default as the backend_id.
$ cinder failover-host cinder@delliscsi --backend_id default $ cinder failover-host cinder@delliscsi --backend_id default
Non trivial heavy lifting is done by this command. It attempts to recover best Non trivial heavy lifting is done by this command. It attempts to recover as
it can but if things have diverged to far it can only do so much. It is also a best it can but if things have diverged too far it can only do so much. It is
one time only command so do not reboot or restart the service in the middle of also a one time only command so do not reboot or restart the service in the
it. middle of it.
Failover and failback are significant operations under OpenStack Cinder. Be Failover and failback are significant operations under OpenStack Cinder. Be
sure to consult with support before attempting. sure to consult with support before attempting.
@ -310,11 +311,11 @@ Server type configuration
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
This option allows one to set a default Server OS type to use when creating This option allows one to set a default Server OS type to use when creating
a server definition on the Dell Storage Center. a server definition on the Dell EMC Storage Center.
When attaching a volume to a node the Dell Storage Center driver creates a When attaching a volume to a node the Dell EMC Storage Center driver creates a
server definition on the storage array. This defition includes a Server OS server definition on the storage array. This defition includes a Server OS
type. The type used by the Dell Storage Center cinder driver is type. The type used by the Dell EMC Storage Center cinder driver is
"Red Hat Linux 6.x". This is a modern operating system definition that supports "Red Hat Linux 6.x". This is a modern operating system definition that supports
all the features of an OpenStack node. all the features of an OpenStack node.
@ -352,10 +353,31 @@ Add the following to the back-end specification to exclude the domains at
excluded_domain_ip=172.20.25.15 excluded_domain_ip=172.20.25.15
excluded_domain_ip=172.20.26.15 excluded_domain_ip=172.20.26.15
Setting Dell EMC SC REST API timeouts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The user can specify timeouts for Dell EMC SC REST API calls.
To set the timeout for ASYNC REST API calls in seconds.
.. code-block:: ini
[dell]
dell_api_async_rest_timeout=15
To set the timeout for SYNC REST API calls in seconds.
.. code-block:: ini
[dell]
dell_api_sync_rest_timeout=30
Generally these should not be set without guidance from Dell EMC support.
Driver options Driver options
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
The following table contains the configuration options specific to the The following table contains the configuration options specific to the
Dell Storage Center volume driver. Dell EMC Storage Center volume driver.
.. include:: ../../tables/cinder-dellsc.inc .. include:: ../../tables/cinder-dellsc.inc

View File

@ -0,0 +1,11 @@
---
features:
- Added dell_api_async_rest_timeout option to the
Dell EMC SC driver. This is the timeout used for
asynchronous REST calls to the Dell EMC SC REST
API. Default is 15 seconds.
- Added dell_api_sync_rest_timeout option to the
Dell EMC SC driver. This is the timeout used for
synchronous REST calls to the Dell EMC SC REST
API. Default is 30 seconds.