From a37d5cfa4fa2073e3cc3816ca5623b7a1b14d02f Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 17 Apr 2018 20:35:36 -0400 Subject: [PATCH] untangle.py: use a configurable "timeout" for socket operations. If an untangle host holds a connection open but sends no data, it can hang the backup process because we'll simply wait forever on that one host. To work around that problem, a new configurable "timeout" value has been added and is used in all open() calls. When the timeout is reached, a socket.timeout error is raised, terminating the backup process for that host (and all others, at the moment). --- src/untangle/untangle.py | 41 ++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/untangle/untangle.py b/src/untangle/untangle.py index 82b29c4..681135a 100644 --- a/src/untangle/untangle.py +++ b/src/untangle/untangle.py @@ -18,10 +18,11 @@ class Untangle: self.host = s['host'] self.username = s.get('username', 'admin') self.password = s['password'] - self.version = s.get('version', '13.1') + self.timeout = s.get('timeout', 300) self.base_url = 'https://' + self.host + '/' # This never changes # Sanity check the numerical version. + self.version = s.get('version', '13.1') if self.version not in ['9', '10', '11', '12', '13', '13.1']: msg = 'Invalid version "' + self.version + '" ' msg += 'in section "' + s.name + '"' @@ -38,6 +39,25 @@ class Untangle: msg += 'in section "' + s.name + '"' raise configparser.ParsingError(msg) + # Sanity check the integer "timeout" parameter. We want to + # bail if either the given parameter is not an integer, or if + # it's negative. To handle both at the same time, we try to + # parse an integer... + timeout = s.get('timeout', 300) + try: + self.timeout = int(timeout) + except: + # ...and set self.timeout to a negative value if we can't... + self.timeout = -1 + + # Now we check to see if the timeout value is negative. That + # will happen if it was either negative to begin with, or + # non-integer (and we set it negative). + if self.timeout < 0: + msg = 'Invalid value "' + timeout + '" for timeout ' + msg += 'in section "' + s.name + '"' + raise configparser.ParsingError(msg) + # Finally, create a URL opener to make HTTPS requests. # # First, create a cookie jar that we'll attach to our URL @@ -59,6 +79,15 @@ class Untangle: self.opener = urllib.request.build_opener(https_handler, cookie_proc) + def open(self, url, data=None): + """ + A wrapper around ``self.opener.open()`` that uses our + configurable socket "timeout" value. Without the timeout + parameter, it seems we can wait forever in some corner cases. + """ + return self.opener.open(url, data, self.timeout) + + def login(self): """ Perform the HTTPS request to log in to the Untangle web admin @@ -68,7 +97,7 @@ class Untangle: url = self.base_url + login_path post_vars = {'username': self.username, 'password': self.password } post_data = urllib.parse.urlencode(post_vars).encode('ascii') - self.opener.open(url, post_data) + self.open(url, post_data) def get_backup(self): @@ -97,10 +126,10 @@ class Untangle: url = self.base_url + '/webui/backup' post_vars = {'action': 'requestBackup'} post_data = urllib.parse.urlencode(post_vars).encode('ascii') - self.opener.open(url, post_data) + self.open(url, post_data) url = self.base_url + 'webui/backup?action=initiateDownload' - with self.opener.open(url) as response: + with self.open(url) as response: return response.read() @@ -113,7 +142,7 @@ class Untangle: url = self.base_url + '/webui/download' post_vars = {'type': 'backup'} post_data = urllib.parse.urlencode(post_vars).encode('ascii') - with self.opener.open(url, post_data) as response: + with self.open(url, post_data) as response: return response.read() @@ -128,5 +157,5 @@ class Untangle: url = self.base_url + '/admin/download' post_vars = {'type': 'backup'} post_data = urllib.parse.urlencode(post_vars).encode('ascii') - with self.opener.open(url, post_data) as response: + with self.open(url, post_data) as response: return response.read() -- 2.44.2