diff --git a/oslo_vmware/api.py b/oslo_vmware/api.py index aaeb77b7..a9839c07 100644 --- a/oslo_vmware/api.py +++ b/oslo_vmware/api.py @@ -139,7 +139,8 @@ class VMwareAPISession(object): def __init__(self, host, server_username, server_password, api_retry_count, task_poll_interval, scheme='https', 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. :param host: ESX/VC server IP address or host name @@ -161,6 +162,8 @@ class VMwareAPISession(object): used only if cacert is not specified :param pool_size: Maximum number of connections in http connection pool + :param connection_timeout: Maximum time in seconds to wait for peer to + respond. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException """ @@ -180,6 +183,7 @@ class VMwareAPISession(object): self._cacert = cacert self._insecure = insecure self._pool_size = pool_size + self._connection_timeout = connection_timeout if create_session: self._create_session() @@ -197,7 +201,8 @@ class VMwareAPISession(object): wsdl_url=self._vim_wsdl_loc, cacert=self._cacert, insecure=self._insecure, - pool_maxsize=self._pool_size) + pool_maxsize=self._pool_size, + connection_timeout=self._connection_timeout) return self._vim @property @@ -209,7 +214,8 @@ class VMwareAPISession(object): wsdl_url=self._pbm_wsdl_loc, cacert=self._cacert, insecure=self._insecure, - pool_maxsize=self._pool_size) + pool_maxsize=self._pool_size, + connection_timeout=self._connection_timeout) if self._session_id: # To handle the case where pbm property is accessed after # session creation. If pbm property is accessed before session diff --git a/oslo_vmware/pbm.py b/oslo_vmware/pbm.py index 8a5fe545..b0c60af5 100644 --- a/oslo_vmware/pbm.py +++ b/oslo_vmware/pbm.py @@ -41,7 +41,8 @@ class Pbm(service.Service): """Service class that provides access to the Storage Policy API.""" 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. :param protocol: http or https @@ -54,11 +55,13 @@ class Pbm(service.Service): used only if cacert is not specified :param pool_maxsize: Maximum number of connections in http 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) soap_url = base_url + '/pbm' super(Pbm, self).__init__(wsdl_url, soap_url, cacert, insecure, - pool_maxsize) + pool_maxsize, connection_timeout) def set_soap_cookie(self, cookie): """Set the specified vCenter session cookie in the SOAP header diff --git a/oslo_vmware/service.py b/oslo_vmware/service.py index ac341b16..1f1cd235 100644 --- a/oslo_vmware/service.py +++ b/oslo_vmware/service.py @@ -134,7 +134,8 @@ class LocalFileAdapter(requests.adapters.HTTPAdapter): 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) # insecure flag is used only if cacert is not # specified. @@ -143,6 +144,7 @@ class RequestsTransport(transport.Transport): self.session.mount('file:///', LocalFileAdapter(pool_maxsize=pool_maxsize)) self.cookiejar = self.session.cookies + self._connection_timeout = connection_timeout def open(self, request): resp = self.session.get(request.url, verify=self.verify) @@ -152,7 +154,8 @@ class RequestsTransport(transport.Transport): resp = self.session.post(request.url, data=request.message, headers=request.headers, - verify=self.verify) + verify=self.verify, + timeout=self._connection_timeout) 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, - cacert=None, insecure=True, pool_maxsize=10): + cacert=None, insecure=True, pool_maxsize=10, + connection_timeout=None): self.wsdl_url = wsdl_url self.soap_url = soap_url LOG.debug("Creating suds client with soap_url='%s' and wsdl_url='%s'", 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, transport=transport, location=self.soap_url, diff --git a/oslo_vmware/tests/test_api.py b/oslo_vmware/tests/test_api.py index c402d47f..fbc4c8de 100644 --- a/oslo_vmware/tests/test_api.py +++ b/oslo_vmware/tests/test_api.py @@ -129,14 +129,15 @@ class VMwareAPISessionTest(base.TestCase): def test_vim(self): api_session = self._create_api_session(False) api_session.vim - self.VimMock.assert_called_with(protocol=api_session._scheme, - host=VMwareAPISessionTest.SERVER_IP, - port=VMwareAPISessionTest.PORT, - wsdl_url=api_session._vim_wsdl_loc, - cacert=self.cert_mock, - insecure=False, - pool_maxsize=VMwareAPISessionTest. - POOL_SIZE) + self.VimMock.assert_called_with( + protocol=api_session._scheme, + host=VMwareAPISessionTest.SERVER_IP, + port=VMwareAPISessionTest.PORT, + wsdl_url=api_session._vim_wsdl_loc, + cacert=self.cert_mock, + insecure=False, + pool_maxsize=VMwareAPISessionTest.POOL_SIZE, + connection_timeout=None) @mock.patch.object(pbm, 'Pbm') def test_pbm(self, pbm_mock): diff --git a/oslo_vmware/tests/test_service.py b/oslo_vmware/tests/test_service.py index ee4a8e06..14f6dc02 100644 --- a/oslo_vmware/tests/test_service.py +++ b/oslo_vmware/tests/test_service.py @@ -483,6 +483,21 @@ class RequestsTransportTest(base.TestCase): resp = transport.session.send(request) 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): """Tests for SudsLogFilter.""" diff --git a/oslo_vmware/vim.py b/oslo_vmware/vim.py index f9842366..e8492463 100644 --- a/oslo_vmware/vim.py +++ b/oslo_vmware/vim.py @@ -20,7 +20,8 @@ class Vim(service.Service): """Service class that provides access to the VIM API.""" 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. :param protocol: http or https @@ -33,6 +34,8 @@ class Vim(service.Service): used only if cacert is not specified :param pool_maxsize: Maximum number of connections in http connection pool + :param connection_timeout: Maximum time in seconds to wait for peer to + respond. :raises: VimException, VimFaultException, VimAttributeException, VimSessionOverLoadException, VimConnectionException """ @@ -41,7 +44,7 @@ class Vim(service.Service): if wsdl_url is None: wsdl_url = soap_url + '/vimService.wsdl' super(Vim, self).__init__(wsdl_url, soap_url, cacert, insecure, - pool_maxsize) + pool_maxsize, connection_timeout) def retrieve_service_content(self): return self.RetrieveServiceContent(service.SERVICE_INSTANCE)