diff --git a/cterasdk/clients/async_requests.py b/cterasdk/clients/async_requests.py index e9321e92..d52a2cd2 100644 --- a/cterasdk/clients/async_requests.py +++ b/cterasdk/clients/async_requests.py @@ -103,7 +103,15 @@ class BaseRequest: def __init__(self, method, url, **kwargs): self.method = method self.url = url - self.kwargs = kwargs + self.kwargs = BaseRequest.accept(**kwargs) + + @staticmethod + def accept(**kwargs): + timeout = kwargs.get('timeout', None) + if timeout: + logger.debug('Setting request timeout. %s', timeout) + kwargs['timeout'] = aiohttp.ClientTimeout(**timeout) + return kwargs class GetRequest(BaseRequest): diff --git a/cterasdk/clients/clients.py b/cterasdk/clients/clients.py index 0393d4e1..20ac3602 100644 --- a/cterasdk/clients/clients.py +++ b/cterasdk/clients/clients.py @@ -59,31 +59,31 @@ async def download(self, path, **kwargs): return await super().get(path, on_error=XMLHandler(), **kwargs) @decorators.authenticated - async def propfind(self, path, depth): - request = async_requests.PropfindRequest(self._builder(path), headers={'depth': str(depth)}) + async def propfind(self, path, depth, **kwargs): + request = async_requests.PropfindRequest(self._builder(path), headers={'depth': str(depth)}, **kwargs) response = await self.a_request(request, on_error=XMLHandler()) return await response.dav() @decorators.authenticated - async def mkcol(self, path): - request = async_requests.MkcolRequest(self._builder(path)) + async def mkcol(self, path, **kwargs): + request = async_requests.MkcolRequest(self._builder(path), **kwargs) response = await self.a_request(request, on_error=XMLHandler()) return await response.text() @decorators.authenticated - async def copy(self, source, destination, *, overwrite=False): - request = async_requests.CopyRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite)) + async def copy(self, source, destination, *, overwrite=False, **kwargs): + request = async_requests.CopyRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite), **kwargs) response = await self.a_request(request, on_error=XMLHandler()) return await response.xml() @decorators.authenticated - async def move(self, source, destination, *, overwrite=False): - request = async_requests.MoveRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite)) + async def move(self, source, destination, *, overwrite=False, **kwargs): + request = async_requests.MoveRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite), **kwargs) response = await self.a_request(request, on_error=XMLHandler()) return await response.xml() - async def delete(self, path): # pylint: disable=arguments-differ - response = await super().delete(path, on_error=XMLHandler()) + async def delete(self, path, **kwargs): # pylint: disable=arguments-differ + response = await super().delete(path, on_error=XMLHandler(), **kwargs) return await response.text() def _webdav_headers(self, destination, overwrite): @@ -138,21 +138,21 @@ async def delete(self, path, **kwargs): class AsyncExtended(AsyncXML): """CTERA Schema""" - async def get_multi(self, path, paths): - return await self.database(path, 'get-multi', paths) + async def get_multi(self, path, paths, **kwargs): + return await self.database(path, 'get-multi', paths, **kwargs) - async def execute(self, path, name, param=None): # schema method - return await self._execute(path, 'user-defined', name, param) + async def execute(self, path, name, param=None, **kwargs): # schema method + return await self._execute(path, 'user-defined', name, param, **kwargs) - async def database(self, path, name, param=None): # schema method - return await self._execute(path, 'db', name, param) + async def database(self, path, name, param=None, **kwargs): # schema method + return await self._execute(path, 'db', name, param, **kwargs) - async def _execute(self, path, _type, name, param): + async def _execute(self, path, _type, name, param, **kwargs): data = Object() data.type = _type data.name = name data.param = param - return await super().post(path, data) + return await super().post(path, data, **kwargs) class AsyncAPI(AsyncExtended): @@ -262,31 +262,31 @@ def download(self, path, **kwargs): return super().handle(path, on_error=XMLHandler(), **kwargs) @decorators.authenticated - def propfind(self, path, depth): - request = async_requests.PropfindRequest(self._builder(path), headers={'depth': str(depth)}) + def propfind(self, path, depth, **kwargs): + request = async_requests.PropfindRequest(self._builder(path), headers={'depth': str(depth)}, **kwargs) response = self.request(request, on_error=XMLHandler()) return response.dav() @decorators.authenticated - def mkcol(self, path): - request = async_requests.MkcolRequest(self._builder(path)) + def mkcol(self, path, **kwargs): + request = async_requests.MkcolRequest(self._builder(path), **kwargs) response = self.request(request, on_error=XMLHandler()) return response.text() @decorators.authenticated - def copy(self, source, destination, *, overwrite=False): - request = async_requests.CopyRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite)) + def copy(self, source, destination, *, overwrite=False, **kwargs): + request = async_requests.CopyRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite), **kwargs) response = self.request(request, on_error=XMLHandler()) return response.xml() @decorators.authenticated - def move(self, source, destination, *, overwrite=False): - request = async_requests.MoveRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite)) + def move(self, source, destination, *, overwrite=False, **kwargs): + request = async_requests.MoveRequest(self._builder(source), headers=self._webdav_headers(destination, overwrite), **kwargs) response = self.request(request, on_error=XMLHandler()) return response.xml() - def delete(self, path): # pylint: disable=arguments-differ - response = super().delete(path, on_error=XMLHandler()) + def delete(self, path, **kwargs): # pylint: disable=arguments-differ + response = super().delete(path, on_error=XMLHandler(), **kwargs) return response.text() def _webdav_headers(self, destination, overwrite): @@ -347,27 +347,27 @@ def delete(self, path, **kwargs): class Extended(XML): """CTERA Schema""" - def get_multi(self, path, paths): - return self.database(path, 'get-multi', paths) + def get_multi(self, path, paths, **kwargs): + return self.database(path, 'get-multi', paths, **kwargs) - def show_multi(self, path, paths): - print(Serializers.JSON(self.get_multi(path, paths), no_log=False)) + def show_multi(self, path, paths, **kwargs): + print(Serializers.JSON(self.get_multi(path, paths, **kwargs), no_log=False)) - def execute(self, path, name, param=None): # schema method - return self._execute(path, 'user-defined', name, param) + def execute(self, path, name, param=None, **kwargs): # schema method + return self._execute(path, 'user-defined', name, param, **kwargs) - def database(self, path, name, param=None): # schema method - return self._execute(path, 'db', name, param) + def database(self, path, name, param=None, **kwargs): # schema method + return self._execute(path, 'db', name, param, **kwargs) - def add(self, path, param=None): - return self.database(path, 'add', param) + def add(self, path, param=None, **kwargs): + return self.database(path, 'add', param, **kwargs) - def _execute(self, path, _type, name, param): + def _execute(self, path, _type, name, param, **kwargs): data = Object() data.type = _type data.name = name data.param = param - return super().post(path, data) + return super().post(path, data, **kwargs) class API(Extended): diff --git a/cterasdk/edge/network.py b/cterasdk/edge/network.py index 9ffe84e2..fb273815 100644 --- a/cterasdk/edge/network.py +++ b/cterasdk/edge/network.py @@ -126,7 +126,11 @@ def tcp_connect(self, service): logger.info("Testing connection. %s", {'host': service.host, 'port': service.port}) - task = self._edge.api.execute("/status/network", "tcpconnect", param) + task = self._edge.api.execute("/status/network", "tcpconnect", param, { + 'timeout': { + 'sock_read': 120 + } + }) try: task = self._edge.tasks.wait(task) logger.debug("Obtained connection status. %s", {'status': task.result.rc}) diff --git a/docs/source/UserGuides/Miscellaneous/Changelog.rst b/docs/source/UserGuides/Miscellaneous/Changelog.rst index f0309bfd..69a37cfd 100644 --- a/docs/source/UserGuides/Miscellaneous/Changelog.rst +++ b/docs/source/UserGuides/Miscellaneous/Changelog.rst @@ -1,6 +1,18 @@ Changelog ========= +2.20.12 +------- + +Improvements +^^^^^^^^^^^^ + +* Support for overriding timeout settings on a per-request basis. +* Increased the ``sock_read`` timeout to 2 minutes when invoking :py:func:`cterasdk.edge.network.Network.tcp_connect`. + +Related issues and pull requests on GitHub: `#302 `_ + + 2.20.11 ------- diff --git a/tests/ut/edge/test_network.py b/tests/ut/edge/test_network.py index f04f5e0b..a78e86d3 100644 --- a/tests/ut/edge/test_network.py +++ b/tests/ut/edge/test_network.py @@ -49,6 +49,12 @@ def setUp(self): self._proxy_user = 'admin' self._proxy_pass = 'password' + self._timeout = { + 'timeout': { + 'sock_read': 120 + } + } + def test_network_status(self): get_response = 'Success' self._init_filer(get_response=get_response) @@ -137,7 +143,7 @@ def test_tcp_connect_success(self): ret = network.Network(self._filer).tcp_connect(TCPService(self._tcp_connect_address, self._tcp_connect_port)) - self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY) + self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY, self._timeout) self._filer.tasks.wait.assert_called_once_with(self._task_id) expected_param = self._get_tcp_connect_object() @@ -157,7 +163,7 @@ def test_tcp_connect_failure(self): ret = network.Network(self._filer).tcp_connect(TCPService(self._tcp_connect_address, self._tcp_connect_port)) - self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY) + self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY, self._timeout) self._filer.tasks.wait.assert_called_once_with(self._task_id) expected_param = self._get_tcp_connect_object() @@ -200,7 +206,7 @@ def test_tcp_connect_task_error(self): ret = network.Network(self._filer).tcp_connect(TCPService(self._tcp_connect_address, self._tcp_connect_port)) - self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY) + self._filer.api.execute.assert_called_once_with('/status/network', 'tcpconnect', mock.ANY, self._timeout) self._filer.tasks.wait.assert_called_once_with(self._task_id) expected_param = self._get_tcp_connect_object()