|
2 | 2 |
|
3 | 3 | import os
|
4 | 4 | import re
|
5 |
| -import sys |
6 | 5 | import json
|
| 6 | +import time |
7 | 7 | import urllib.error
|
8 | 8 | import urllib.request
|
9 | 9 | from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES, SAFE_SYNC
|
|
26 | 26 | TEST_UNIX_CLUSTER_ID = 10
|
27 | 27 | TEST_LDAP_TARGET_ID = 9
|
28 | 28 |
|
29 |
| - |
30 |
| -MIN_TIMEOUT = 5 |
31 |
| -MAX_TIMEOUT = 625 |
32 |
| -TIMEOUTMULTIPLE = 5 |
| 29 | +# Value for the base of the exponential backoff |
| 30 | +TIMEOUT_BASE = 5 |
| 31 | +MAX_ATTEMPTS = 5 |
33 | 32 |
|
34 | 33 |
|
35 | 34 | GET = "GET"
|
36 | 35 | PUT = "PUT"
|
37 | 36 | POST = "POST"
|
38 | 37 | DELETE = "DELETE"
|
39 | 38 |
|
| 39 | +#Exceptions |
| 40 | +class Error(Exception): |
| 41 | + """Base exception class for all exceptions defined""" |
| 42 | + pass |
| 43 | + |
| 44 | + |
| 45 | +class URLRequestError(Error): |
| 46 | + """Class for exceptions due to not being able to fulfill a URLRequest""" |
| 47 | + pass |
| 48 | + |
40 | 49 |
|
41 | 50 | def getpw(user, passfd, passfile):
|
42 | 51 | if ":" in user:
|
@@ -81,21 +90,28 @@ def call_api2(method, target, endpoint, authstr, **kw):
|
81 | 90 |
|
82 | 91 | def call_api3(method, target, data, endpoint, authstr, **kw):
|
83 | 92 | req = mkrequest(method, target, data, endpoint, authstr, **kw)
|
84 |
| - trying = True |
85 |
| - currentTimeout = MIN_TIMEOUT |
86 |
| - while trying: |
| 93 | + req_attempts = 0 |
| 94 | + current_timeout = TIMEOUT_BASE |
| 95 | + total_timeout = 0 |
| 96 | + payload = None |
| 97 | + while req_attempts < MAX_ATTEMPTS: |
87 | 98 | try:
|
88 |
| - resp = urllib.request.urlopen(req, timeout=currentTimeout) |
89 |
| - payload = resp.read() |
90 |
| - trying = False |
| 99 | + resp = urllib.request.urlopen(req, timeout=current_timeout) |
| 100 | + # exception catching, mainly for request timeouts, "Service Temporarily Unavailable" (Rate limiting), and DNS failures. |
91 | 101 | except urllib.error.URLError as exception:
|
92 |
| - if currentTimeout < MAX_TIMEOUT: |
93 |
| - currentTimeout *= TIMEOUTMULTIPLE |
94 |
| - else: |
95 |
| - sys.exit( |
96 |
| - f"Exception raised after maximum number of retries and/or timeout {MAX_TIMEOUT} seconds reached. " |
97 |
| - + f"Exception reason: {exception.reason}.\n Request: {req.full_url}" |
| 102 | + req_attempts += 1 |
| 103 | + if req_attempts >= MAX_ATTEMPTS: |
| 104 | + raise URLRequestError( |
| 105 | + "Exception raised after maximum number of retries reached after total backoff of " + |
| 106 | + f"{total_timeout} seconds. Retries: {req_attempts}. " |
| 107 | + + f"Exception reason: {exception}.\n Request: {req.full_url}" |
98 | 108 | )
|
| 109 | + time.sleep(current_timeout) |
| 110 | + total_timeout += current_timeout |
| 111 | + current_timeout *= TIMEOUT_BASE |
| 112 | + else: |
| 113 | + payload = resp.read() |
| 114 | + break |
99 | 115 |
|
100 | 116 | return json.loads(payload) if payload else None
|
101 | 117 |
|
|
0 commit comments