Pass connection timeout so that invoke_api will not wait forever

invoke_api does not throw any exception when interface goes down and
application loses connectivity with vCenter because default timeout
is set to None which is equivalent to infinity.

This patch set allows any application to pass connection timeout so that
it will wait for the given timeout and then throw an exception.

Closes-Bug: #1564839

Change-Id: I69b099b7caaa4fb0e0067cb83b9468d4aa901cc7
Co-Authored-By: Raghuveer Shenoy <rshenoy@hp.com>
This commit is contained in:
Aman Kumar 2016-06-16 22:27:14 -07:00
parent 7c893cab6a
commit b32f627b81
6 changed files with 54 additions and 19 deletions

View File

@ -139,7 +139,8 @@ class VMwareAPISession(object):
def __init__(self, host, server_username, server_password, def __init__(self, host, server_username, server_password,
api_retry_count, task_poll_interval, scheme='https', api_retry_count, task_poll_interval, scheme='https',
create_session=True, wsdl_loc=None, pbm_wsdl_loc=None, create_session=True, wsdl_loc=None, pbm_wsdl_loc=None,
port=443, cacert=None, insecure=True, pool_size=10): port=443, cacert=None, insecure=True, pool_size=10,
connection_timeout=None):
"""Initializes the API session with given parameters. """Initializes the API session with given parameters.
:param host: ESX/VC server IP address or host name :param host: ESX/VC server IP address or host name
@ -161,6 +162,8 @@ class VMwareAPISession(object):
used only if cacert is not specified used only if cacert is not specified
:param pool_size: Maximum number of connections in http :param pool_size: Maximum number of connections in http
connection pool connection pool
:param connection_timeout: Maximum time in seconds to wait for peer to
respond.
:raises: VimException, VimFaultException, VimAttributeException, :raises: VimException, VimFaultException, VimAttributeException,
VimSessionOverLoadException VimSessionOverLoadException
""" """
@ -180,6 +183,7 @@ class VMwareAPISession(object):
self._cacert = cacert self._cacert = cacert
self._insecure = insecure self._insecure = insecure
self._pool_size = pool_size self._pool_size = pool_size
self._connection_timeout = connection_timeout
if create_session: if create_session:
self._create_session() self._create_session()
@ -197,7 +201,8 @@ class VMwareAPISession(object):
wsdl_url=self._vim_wsdl_loc, wsdl_url=self._vim_wsdl_loc,
cacert=self._cacert, cacert=self._cacert,
insecure=self._insecure, insecure=self._insecure,
pool_maxsize=self._pool_size) pool_maxsize=self._pool_size,
connection_timeout=self._connection_timeout)
return self._vim return self._vim
@property @property
@ -209,7 +214,8 @@ class VMwareAPISession(object):
wsdl_url=self._pbm_wsdl_loc, wsdl_url=self._pbm_wsdl_loc,
cacert=self._cacert, cacert=self._cacert,
insecure=self._insecure, insecure=self._insecure,
pool_maxsize=self._pool_size) pool_maxsize=self._pool_size,
connection_timeout=self._connection_timeout)
if self._session_id: if self._session_id:
# To handle the case where pbm property is accessed after # To handle the case where pbm property is accessed after
# session creation. If pbm property is accessed before session # session creation. If pbm property is accessed before session

View File

@ -41,7 +41,8 @@ class Pbm(service.Service):
"""Service class that provides access to the Storage Policy API.""" """Service class that provides access to the Storage Policy API."""
def __init__(self, protocol='https', host='localhost', port=443, def __init__(self, protocol='https', host='localhost', port=443,
wsdl_url=None, cacert=None, insecure=True, pool_maxsize=10): wsdl_url=None, cacert=None, insecure=True, pool_maxsize=10,
connection_timeout=None):
"""Constructs a PBM service client object. """Constructs a PBM service client object.
:param protocol: http or https :param protocol: http or https
@ -54,11 +55,13 @@ class Pbm(service.Service):
used only if cacert is not specified used only if cacert is not specified
:param pool_maxsize: Maximum number of connections in http :param pool_maxsize: Maximum number of connections in http
connection pool connection pool
:param connection_timeout: Maximum time in seconds to wait for peer to
respond.
""" """
base_url = service.Service.build_base_url(protocol, host, port) base_url = service.Service.build_base_url(protocol, host, port)
soap_url = base_url + '/pbm' soap_url = base_url + '/pbm'
super(Pbm, self).__init__(wsdl_url, soap_url, cacert, insecure, super(Pbm, self).__init__(wsdl_url, soap_url, cacert, insecure,
pool_maxsize) pool_maxsize, connection_timeout)
def set_soap_cookie(self, cookie): def set_soap_cookie(self, cookie):
"""Set the specified vCenter session cookie in the SOAP header """Set the specified vCenter session cookie in the SOAP header

View File

@ -134,7 +134,8 @@ class LocalFileAdapter(requests.adapters.HTTPAdapter):
class RequestsTransport(transport.Transport): class RequestsTransport(transport.Transport):
def __init__(self, cacert=None, insecure=True, pool_maxsize=10): def __init__(self, cacert=None, insecure=True, pool_maxsize=10,
connection_timeout=None):
transport.Transport.__init__(self) transport.Transport.__init__(self)
# insecure flag is used only if cacert is not # insecure flag is used only if cacert is not
# specified. # specified.
@ -143,6 +144,7 @@ class RequestsTransport(transport.Transport):
self.session.mount('file:///', self.session.mount('file:///',
LocalFileAdapter(pool_maxsize=pool_maxsize)) LocalFileAdapter(pool_maxsize=pool_maxsize))
self.cookiejar = self.session.cookies self.cookiejar = self.session.cookies
self._connection_timeout = connection_timeout
def open(self, request): def open(self, request):
resp = self.session.get(request.url, verify=self.verify) resp = self.session.get(request.url, verify=self.verify)
@ -152,7 +154,8 @@ class RequestsTransport(transport.Transport):
resp = self.session.post(request.url, resp = self.session.post(request.url,
data=request.message, data=request.message,
headers=request.headers, headers=request.headers,
verify=self.verify) verify=self.verify,
timeout=self._connection_timeout)
return transport.Reply(resp.status_code, resp.headers, resp.content) return transport.Reply(resp.status_code, resp.headers, resp.content)
@ -188,12 +191,16 @@ class Service(object):
""" """
def __init__(self, wsdl_url=None, soap_url=None, def __init__(self, wsdl_url=None, soap_url=None,
cacert=None, insecure=True, pool_maxsize=10): cacert=None, insecure=True, pool_maxsize=10,
connection_timeout=None):
self.wsdl_url = wsdl_url self.wsdl_url = wsdl_url
self.soap_url = soap_url self.soap_url = soap_url
LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'", LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'",
self.soap_url, self.wsdl_url) self.soap_url, self.wsdl_url)
transport = RequestsTransport(cacert, insecure, pool_maxsize) transport = RequestsTransport(cacert=cacert,
insecure=insecure,
pool_maxsize=pool_maxsize,
connection_timeout=connection_timeout)
self.client = client.Client(self.wsdl_url, self.client = client.Client(self.wsdl_url,
transport=transport, transport=transport,
location=self.soap_url, location=self.soap_url,

View File

@ -129,14 +129,15 @@ class VMwareAPISessionTest(base.TestCase):
def test_vim(self): def test_vim(self):
api_session = self._create_api_session(False) api_session = self._create_api_session(False)
api_session.vim api_session.vim
self.VimMock.assert_called_with(protocol=api_session._scheme, self.VimMock.assert_called_with(
protocol=api_session._scheme,
host=VMwareAPISessionTest.SERVER_IP, host=VMwareAPISessionTest.SERVER_IP,
port=VMwareAPISessionTest.PORT, port=VMwareAPISessionTest.PORT,
wsdl_url=api_session._vim_wsdl_loc, wsdl_url=api_session._vim_wsdl_loc,
cacert=self.cert_mock, cacert=self.cert_mock,
insecure=False, insecure=False,
pool_maxsize=VMwareAPISessionTest. pool_maxsize=VMwareAPISessionTest.POOL_SIZE,
POOL_SIZE) connection_timeout=None)
@mock.patch.object(pbm, 'Pbm') @mock.patch.object(pbm, 'Pbm')
def test_pbm(self, pbm_mock): def test_pbm(self, pbm_mock):

View File

@ -483,6 +483,21 @@ class RequestsTransportTest(base.TestCase):
resp = transport.session.send(request) resp = transport.session.send(request)
self.assertEqual(data, resp.content) self.assertEqual(data, resp.content)
def test_send_with_connection_timeout(self):
transport = service.RequestsTransport(connection_timeout=120)
request = mock.Mock(url=mock.sentinel.url,
message=mock.sentinel.message,
headers=mock.sentinel.req_headers)
with mock.patch.object(transport.session, "post") as mock_post:
transport.send(request)
mock_post.assert_called_once_with(
mock.sentinel.url,
data=mock.sentinel.message,
headers=mock.sentinel.req_headers,
timeout=120,
verify=transport.verify)
class SudsLogFilterTest(base.TestCase): class SudsLogFilterTest(base.TestCase):
"""Tests for SudsLogFilter.""" """Tests for SudsLogFilter."""

View File

@ -20,7 +20,8 @@ class Vim(service.Service):
"""Service class that provides access to the VIM API.""" """Service class that provides access to the VIM API."""
def __init__(self, protocol='https', host='localhost', port=None, def __init__(self, protocol='https', host='localhost', port=None,
wsdl_url=None, cacert=None, insecure=True, pool_maxsize=10): wsdl_url=None, cacert=None, insecure=True, pool_maxsize=10,
connection_timeout=None):
"""Constructs a VIM service client object. """Constructs a VIM service client object.
:param protocol: http or https :param protocol: http or https
@ -33,6 +34,8 @@ class Vim(service.Service):
used only if cacert is not specified used only if cacert is not specified
:param pool_maxsize: Maximum number of connections in http :param pool_maxsize: Maximum number of connections in http
connection pool connection pool
:param connection_timeout: Maximum time in seconds to wait for peer to
respond.
:raises: VimException, VimFaultException, VimAttributeException, :raises: VimException, VimFaultException, VimAttributeException,
VimSessionOverLoadException, VimConnectionException VimSessionOverLoadException, VimConnectionException
""" """
@ -41,7 +44,7 @@ class Vim(service.Service):
if wsdl_url is None: if wsdl_url is None:
wsdl_url = soap_url + '/vimService.wsdl' wsdl_url = soap_url + '/vimService.wsdl'
super(Vim, self).__init__(wsdl_url, soap_url, cacert, insecure, super(Vim, self).__init__(wsdl_url, soap_url, cacert, insecure,
pool_maxsize) pool_maxsize, connection_timeout)
def retrieve_service_content(self): def retrieve_service_content(self):
return self.RetrieveServiceContent(service.SERVICE_INSTANCE) return self.RetrieveServiceContent(service.SERVICE_INSTANCE)