diff --git a/.idea/bbb-status.iml b/.idea/bbb-status.iml index e0afde7..3c77fb3 100644 --- a/.idea/bbb-status.iml +++ b/.idea/bbb-status.iml @@ -3,8 +3,9 @@ + - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 9727bbc..d4e4db6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3935217..7e9686c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.7' services: bbb-status: build: . - command: ["log"] # see main.py for available commandline arguments + command: ["log-verbose"] # see main.py for available commandline arguments environment: - PYTHONPATH=/usr/src/ # pythonpath is required to import from self created modules ("from langfingaz ...") - PYTHONUNBUFFERED=1 diff --git a/src/langfingaz/bbbRequest.py b/src/langfingaz/bbbRequest.py index 1b22c9e..a51bb77 100644 --- a/src/langfingaz/bbbRequest.py +++ b/src/langfingaz/bbbRequest.py @@ -1,17 +1,27 @@ import hashlib +import logging + import requests +from requests.adapters import HTTPAdapter +import requests.packages.urllib3.exceptions +from requests.packages.urllib3.util.retry import Retry + from xml.etree import ElementTree import langfingaz.util.fileUtil as fileUtil -import langfingaz.util.util as util -def requestMeetingData() -> str: +def requestMeetingData(maxRetry=0, gracePeriod: int = 1) -> str: + """ + :arg maxRetry: maximum connection retries; if maxRetry=None, then unlimited retires + :arg gracePeriod: seconds to wait before retry + :return: XML file containing data about currently active BBB meetings + See also: https://docs.bigbluebutton.org/dev/api.html#monitoring + as well as: https://docs.bigbluebutton.org/dev/api.html#getmeetings + """ + requestUrl = getRequestUrl() - util.debug("starting request for " + requestUrl) - response = requests.get(requestUrl) - if not response.ok: - raise ValueError("error during request, got status code {}".format(response.status_code)) + response: requests.Response = doGetRequest(requestUrl) tree = ElementTree.fromstring(response.content) if tree.find('returncode').text != 'SUCCESS': @@ -20,6 +30,37 @@ def requestMeetingData() -> str: return str(response.content, encoding=response.encoding) +def doGetRequest(requestUrl: str, maxRetries=6) -> requests.Response: + """ + Performs a HTTPS GET request for the given url and does at most maxRetries retries + on connection errors. + + :raises requests.exceptions.HTTPError: If status code not 200 (will retry if 502, 503, 504) + :raises requests.packages.urllib3.exceptions.MaxRetryError: + """ + + logLevel = logging.root.level # https://stackoverflow.com/a/53951759/6334421 + logging.basicConfig(level=logging.DEBUG) + + # https://stackoverflow.com/a/35636367/6334421 + # Retry on basic connectivity issues (including DNS lookup failures), + # and HTTP status codes of 502, 503 and 504 + # Grace period increases after each retry. + # See also: + # - Session: https://requests.readthedocs.io/en/master/user/advanced/ + s: requests.sessions.Session = requests.Session() + retries: Retry = Retry(total=maxRetries, backoff_factor=5, status_forcelist=[502, 503, 504]) + s.mount('https://', HTTPAdapter(max_retries=retries)) + + response: requests.Response = s.get(requestUrl) + logging.basicConfig(level=logLevel) + + # raise exception if status conde NOT 200 + response.raise_for_status() + + return response + + def getRequestUrl(api_method: str = 'getMeetings', query_string: str = '') -> str: url = getUrl() api_url = url + "api/" @@ -48,4 +89,3 @@ def getSecret() -> str: if len(secret) <= min_length: raise ValueError("secret should be longer than {} characters!".format(min_length)) return secret - diff --git a/src/langfingaz/logMeetingData.py b/src/langfingaz/logMeetingData.py index 0415dc3..bca3803 100755 --- a/src/langfingaz/logMeetingData.py +++ b/src/langfingaz/logMeetingData.py @@ -14,9 +14,7 @@ def sleepFiveMin(verbose=False): if verbose: print(">> Sleeping for five minutes <<") - - sys.stdout.flush() - time.sleep(fiveMinutes) + util.sleep(fiveMinutes) def v2(folder: Path = saveData.getDefaultFolder()): diff --git a/src/langfingaz/main.py b/src/langfingaz/main.py index cc8b40f..67b0129 100755 --- a/src/langfingaz/main.py +++ b/src/langfingaz/main.py @@ -1,14 +1,14 @@ #!/usr/bin/python3 from sys import argv +import logging from langfingaz import plotMeetings from langfingaz import logMeetingData -from langfingaz.util import util def main(): print("=== bbb-status ===") - util.debug(str(argv)) + logging.debug(str(argv)) print() usageStr = 'Usage:\n' + argv[0] + ' [log|log-verbose|plot]\n' + \ diff --git a/src/langfingaz/util/util.py b/src/langfingaz/util/util.py index 9afcefe..b7c9b6c 100644 --- a/src/langfingaz/util/util.py +++ b/src/langfingaz/util/util.py @@ -1,9 +1,9 @@ -BBB_STATUS_DEBUG = True +import time -def debug(message: str): - if BBB_STATUS_DEBUG: - print(">> " + message) +def sleep(seconds: float): + print(flush=True) # print newline -> so that all characters of current line get flushed as well + time.sleep(seconds) def indentMultilineStr(s: str, indentWith='\t'):