From 68bb94d1d817ace50c2af7212980fc9c642398cd Mon Sep 17 00:00:00 2001 From: David Huss <dh@atoav.com> Date: Wed, 10 Jun 2020 17:00:50 +0200 Subject: [PATCH] Add --read-load-from option to meetings --- bbbmon/__init__.py | 2 +- bbbmon/bbbmon.py | 18 ++++++++++------ bbbmon/load.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++ bbbmon/meetings.py | 16 +++++++++++--- pyproject.toml | 2 +- 5 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 bbbmon/load.py diff --git a/bbbmon/__init__.py b/bbbmon/__init__.py index a64f0d9..6903c24 100644 --- a/bbbmon/__init__.py +++ b/bbbmon/__init__.py @@ -1 +1 @@ -__version__ = '0.1.31' +__version__ = '0.1.32' diff --git a/bbbmon/bbbmon.py b/bbbmon/bbbmon.py index da0451b..f4c6af0 100755 --- a/bbbmon/bbbmon.py +++ b/bbbmon/bbbmon.py @@ -13,6 +13,7 @@ from bbbmon.xmldict import XmlListConfig, XmlDictConfig from bbbmon.configuration import Config, Endpoint, SERVER_PROPERTIES_FILE, Url, Secret, get_user_config_path, init_config, new_config from bbbmon.meetings import * from bbbmon.printing import * +from bbbmon.load import * @@ -53,7 +54,7 @@ def main(userconfig, watch, version): Internally bbbmon relies on the offical bbb-API, which means you need to have the server's secret in order to create a valid request. Create a new configuration with: bbbmon config --new """ - __version__ = "0.1.31" + __version__ = "0.1.32" if version: print(__version__) @@ -68,6 +69,7 @@ def main(userconfig, watch, version): @click.option('--endpoint', '-e', multiple=True, help="Filter by one or more endpoints as named in the user configuration (e.g. [servername]). Order is respected.") @click.option('--watch', '-w', help="Run repeatedly with the given interval in seconds", type=click.IntRange(2, 2147483647, clamp=True)) @click.option('-n', help="The number of meetings to show in the leaderboards (Default: 5)", type=int) +@click.option('--read-load-from', help="Read the load from file/https (if starts with http(s) request, else read from path). Looks for floats and calculates the average.", type=str) @click.option('--leaderboards/--no-leaderboards', default=True, show_default=True, help="Hide or show the meeting leaderboards") @click.option('--participants/--no-participants', default=True, show_default=True, help="Hide or show the participants") @click.option('--meetings/--no-meetings', default=True, show_default=True, help="Hide or show the meetings") @@ -79,7 +81,7 @@ def main(userconfig, watch, version): @click.option('--all', '-a', 'all_', is_flag=True, help="Print all") @click.option('--fancy/--no-fancy', default=False, show_default=True, help="Use fancy headers") @click.option('--sum','sum_', is_flag=True, help="Print all") -def meetings(ctx, userconfig, watch, short, compact, n, all_, twolines, leaderboards, participants, presenter, presenter_id, meetings, endpoint, fancy, sum_): +def meetings(ctx, userconfig, watch, short, compact, n, all_, twolines, read_load_from, leaderboards, participants, presenter, presenter_id, meetings, endpoint, fancy, sum_): """View currently active meetings""" if short: leaderboards = False @@ -99,21 +101,25 @@ def meetings(ctx, userconfig, watch, short, compact, n, all_, twolines, leaderbo config = init_config(userconfig) config.filter_endpoints(endpoint) + + # Load is none if it doesn't work + load = read_load(read_load_from) + if watch is not None: while watch is not None: try: if twolines: - meetings_twolines(config, watch, fancy, sum_) + meetings_twolines(config, watch, fancy, sum_, load) else: - list_meetings(config, leaderboards, n, participants, presenter, presenter_id, meetings, watch, fancy, compact, sum_) + list_meetings(config, leaderboards, n, participants, presenter, presenter_id, meetings, watch, fancy, compact, sum_, load) time.sleep(watch) except KeyboardInterrupt: sys.exit() else: if twolines: - meetings_twolines(config, watch, fancy, sum_) + meetings_twolines(config, watch, fancy, sum_, load) else: - list_meetings(config, leaderboards, n, participants, presenter, presenter_id, meetings, watch, fancy, compact, sum_) + list_meetings(config, leaderboards, n, participants, presenter, presenter_id, meetings, watch, fancy, compact, sum_, load) diff --git a/bbbmon/load.py b/bbbmon/load.py new file mode 100644 index 0000000..ac1d483 --- /dev/null +++ b/bbbmon/load.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys +import re +import requests +from typing import Optional +from bbbmon.printing import eprint + + +LOAD_PATTERN = re.compile(r"(?!\d+\.\d+[\./])\d+\.\d+") + +def read_load(read_load_from: Optional[str]) -> Optional[float]: + """ + Read the load either from a file or a url depending on the input. Return None if anything fails. + This matches all floats in the given file/url and returns the average + """ + if read_load_from is None: + return None + + text = "" + if read_load_from.lower().startswith(("http")): + try: + r = requests.get(read_load_from, timeout=3) + text = r.text + except: + eprint("Error: Ignored load - Couldn't read load from {}: Response was: {}".format(read_load_from, r)) + return None + else: + if os.path.isfile(read_load_from): + with open(read_load_from, "r", encoding="utf-8") as f: + text = f.read() + else: + eprint("Error: Ignored load - Couldn't read load from {}: Not a file or not readable".format(read_load_from)) + return None + + text = text.strip() + + if text == "": + eprint("Error: Ignored load - Couldn't read load from {}: The file was empty".format(read_load_from)) + return None + + matches = re.findall(LOAD_PATTERN, text) + + if matches is None: + eprint("Error: Ignored load - Couldn't read load from {}: No float values found".format(read_load_from)) + return None + elif len(matches) == 0: + eprint("Error: Ignored load - Couldn't read load from {}: No float values found".format(read_load_from)) + return None + else: + return sum([float(m) for m in matches]) / float(len(matches)) \ No newline at end of file diff --git a/bbbmon/meetings.py b/bbbmon/meetings.py index 553e6ed..84b0c8c 100644 --- a/bbbmon/meetings.py +++ b/bbbmon/meetings.py @@ -143,7 +143,7 @@ def get_duration(meeting: XmlDictConfig) -> timedelta: return duration -def list_meetings(config: Config, leaderboards: bool, n: int, participants: bool, presenter: bool, presenter_id: bool, show_meetings: bool, watch: int, fancy: bool, compact: bool, sum_:bool): +def list_meetings(config: Config, leaderboards: bool, n: int, participants: bool, presenter: bool, presenter_id: bool, show_meetings: bool, watch: int, fancy: bool, compact: bool, sum_:bool, load: Optional[float]): """ For each endpoint in the configuration get the active meetings and print out an overview of the current bbb-usage @@ -163,6 +163,10 @@ def list_meetings(config: Config, leaderboards: bool, n: int, participants: bool if watch is not None: click.clear() + # Average load + if load is not None: + print("Average load is: {:04.2f}".format(load)) + for i, endpoint in enumerate(config_override.endpoints): # Print divider if there is more than one endpoint @@ -270,7 +274,7 @@ def sum_meetings(config: Config, meetings: Optional[XmlDictConfig]) -> Optiona -def meetings_twolines(config: Config, watch: int, fancy: bool, sum_:bool): +def meetings_twolines(config: Config, watch: int, fancy: bool, sum_:bool, load: Optional[float]): """ For each endpoint in the configuration get the active meetings and print out an overview of the current bbb-usage. This is guaranteed to fit within @@ -349,7 +353,7 @@ def meetings_twolines(config: Config, watch: int, fancy: bool, sum_:bool): avg_s = avg_s/60./60. if not fancy: - if config_override.on_server: + if config_override.on_server and load is None: # If on server get load w = subprocess.run(["w | head -1"], shell=True, stdout=subprocess.PIPE) w = w.stdout.decode('utf-8').strip().split("load average:") @@ -366,6 +370,12 @@ def meetings_twolines(config: Config, watch: int, fancy: bool, sum_:bool): "stats {:>2} / {:<2} {:>3} {:>3} {:>3} {:>3} {:>3} {:.1f}"\ .format(n_recording, n_running, n_participants, n_moderator, n_video, n_voice, n_listeners, w) ] + elif load is not None: + lines = [ + "{} rec / ses ppl mod vid mic ear lod".format(endpoint.name[:3]), + "stats {:>2} / {:<2} {:>3} {:>3} {:>3} {:>3} {:>3} {:.1f}"\ + .format(n_recording, n_running, n_participants, n_moderator, n_video, n_voice, n_listeners, load) + ] else: lines = [ "{} rec / ses ppl mod vid mic ear".format(endpoint.name[:3]), diff --git a/pyproject.toml b/pyproject.toml index fa89388..d6295e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "bbbmon" -version = "0.1.31" +version = "0.1.32" description = "A small CLI utility to monitor bbb usage" authors = ["David Huss <david.huss@hfbk-hamburg.de>"] maintainers = ["David Huss <david.huss@hfbk-hamburg.de>"] -- GitLab