]> gitweb.michael.orlitzky.com - untangle-https-backup.git/commitdiff
untangle.py: use a configurable "timeout" for socket operations.
authorMichael Orlitzky <michael@orlitzky.com>
Wed, 18 Apr 2018 00:35:36 +0000 (20:35 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Wed, 18 Apr 2018 00:44:51 +0000 (20:44 -0400)
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

index 82b29c445da89dda7decf2ccd2a5825223535c67..681135ab5ff302ce0df7aac1a9f669ea8742bb96 100644 (file)
@@ -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()