From ff76f43e1414b10c11298dee57e7ba96d9a4514c Mon Sep 17 00:00:00 2001 From: Alban Bronisz Date: Sat, 13 Feb 2021 20:15:17 +0100 Subject: [PATCH] Add render --- .gitignore | 2 ++ README.md | 24 +++++++++++++- display/render.py | 72 ++++++++++++++++++++++++++++++++++++++++ display/render_stats.sh | 51 ++++++++++++++++++++++++++++ display/requirements.txt | 10 ++++++ 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 display/render.py create mode 100755 display/render_stats.sh create mode 100644 display/requirements.txt diff --git a/.gitignore b/.gitignore index 497d582..56ee44a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ stats.json +venv +mem_usage.png diff --git a/README.md b/README.md index 07a8836..ece6789 100644 --- a/README.md +++ b/README.md @@ -31,4 +31,26 @@ the crontab line redirect the outputs into `/dev/null`. Can also be a path to a Display stats ------------- -To be done... +This steps permit to generate a render from a stats file (generated by the script +called by the crontab, see stats export section). + +### Install + +Need python3 with venv module + + sudo apt install python3 python3-venv + +### Run + +Go in [display](display) folder + + cd display + +Then to generate a png, just run (install is automatically done if needed) + ./render_stats.sh stats.json + +It is also possible to see a web version fo the render : + ./render_stats.sh stats.json -w + +**Note:** if the stats file is in a remote server, one can use `scp` to get it: + scp alban@eunuque.caracals.org:/path/to/docker-stats-histo/stats.json . diff --git a/display/render.py b/display/render.py new file mode 100644 index 0000000..e391530 --- /dev/null +++ b/display/render.py @@ -0,0 +1,72 @@ + +import argparse +import json +from datetime import datetime + +import pygal + + +PNG_OUTPUT = 'mem_usage.png' + + +def parse_args(): + parser = argparse.ArgumentParser( + description=("Render for docker stats memory usage") + ) + + parser.add_argument('stats', type=str, help="Path to stats file") + parser.add_argument('--web', '-w', action='store_true', + help="Render in web browser instead of svg") + args = parser.parse_args() + return args.stats, args.web + + +def name_from_field(field: str) -> str : + if field.startswith("onlyoffice-"): + return "onlyoffice" + return field.split("_")[0] + +def main(): + stats_fn, web_render = parse_args() + data = load_data(stats_fn) + + render(data, web_render) + + +def load_data(stats_fn: str): + with open(stats_fn) as stats_f: + data = json.load(stats_f) + + data_dict = {name_from_field(field): [0]*len(data) for field in data[0]} + print("Found", len(data), "points") + + + for t_i, stat in enumerate(data): + for field in stat: + if field == "date": # date + data_dict[field][t_i] = datetime.strptime(stat[field], "%Y-%m-%dT%H:%M:%S%z") + else: # float + value = stat[field].split(" ")[0] + value = value.replace("MiB", "e3") + data_dict[name_from_field(field)][t_i] += float(value) / 1000 # values are in MiB + return data_dict + + +def render(data: dict, web_render:bool=False): + bar_chart = pygal.StackedBar(height=400) + bar_chart.x_labels = data["date"] + for k in data: + if k == "date": + continue + #if "db" in k or "database" in k or "mysql" in k or "mongo" in k or "postgre" in k: + # continue + bar_chart.add(k, data[k]) + + if web_render: + bar_chart.render_in_browser() + else: + bar_chart.render_to_png(PNG_OUTPUT) + print("Image generated in:", PNG_OUTPUT) + +if __name__ == "__main__": + main() diff --git a/display/render_stats.sh b/display/render_stats.sh new file mode 100755 index 0000000..64a4f85 --- /dev/null +++ b/display/render_stats.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -eu + + +# Need to replace last line of file from "}," to "}]" +# To avoid modify original file, use temporary file +TMP_FILE=stats_tmp.json +VENV=venv + +if [[ $# -ne 1 ]] && [[ $# -ne 2 ]]; then + echo "Usage:" + echo " $0 STATS_FILE [-w] " + echo "" + echo "Params:" + echo " - stats_file: path to stats file" + echo "" + echo "Options:" + echo " - w: do rende1r using web browser" + exit 1 +fi + +STATS_FILE=$1 +OPT="" +if [[ $# -eq 2 ]]; then + OPT=${OPT}" -w" +fi + +# Pip install ----------------------------------------------------------------- +if [ ! -d ${VENV} ]; then + echo "Installing python environment in ${VENV}..." + python3 -m venv ${VENV} + . ${VENV}/bin/activate + pip install --upgrade pip + pip install -r requirements.txt + echo "Python environment installed..." + echo "" +else + . ${VENV}/bin/activate +fi + + +# Render ---------------------------------------------------------------------- +sed -e '$s/},/}]/' ${STATS_FILE} > ${TMP_FILE} + +# Update last line +# Generate render +python render.py ${TMP_FILE} ${OPT} + +rm ${TMP_FILE} + +deactivate diff --git a/display/requirements.txt b/display/requirements.txt new file mode 100644 index 0000000..d11ec07 --- /dev/null +++ b/display/requirements.txt @@ -0,0 +1,10 @@ + + +pygal + + +# For pygal web render +lxml +tinycss +cssselect +cairosvg