From 93a871efa7fbddb9dc9248f4bb31c270e79ea6d2 Mon Sep 17 00:00:00 2001 From: Agatha Rose Date: Tue, 5 Oct 2021 02:56:53 +0300 Subject: [PATCH] flop --- .gitignore | 1 + config.py | 31 ++++++++++++++++++ main.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 ++ 4 files changed, 120 insertions(+) create mode 100644 .gitignore create mode 100644 config.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..da1bc56 --- /dev/null +++ b/config.py @@ -0,0 +1,31 @@ +import os +import toml + +if 'XDG_CONFIG_HOME' in os.environ: + config_dir = os.environ['XDG_CONFIG_HOME'] +else: + config_dir = os.path.join(os.environ['HOME'], '.config') + +# if the config dir doesn't exist, create it +config_dir = os.path.join(config_dir, 'vacuum-tube') +os.makedirs(config_dir, exist_ok=True) + +# create an example config file if it doesn't already exist +if not os.path.exists(config_dir + '/config.toml'): + with open(config_dir + '/config.toml', 'a+') as f: + f.write('''[repo] +# Example: `~/backups/backup.borg` or `ssh://agatha@some.place:22/~/backups/backup.borg` +path = "" +# Leave empty if none +# If repo is encrypted and no passphrase is provided, the program will ask you for one. +passphrase = "" + +[disk] +# Example: `/dev/sda1` +partition = "" +# If the backup disk is remote, provide an ssh command. +# Otherwise, leave empty. +# Example: `ssh -p 22 agatha@some.place` +ssh = ""''') + +config = toml.load(config_dir + '/config.toml') diff --git a/main.py b/main.py new file mode 100644 index 0000000..b44852e --- /dev/null +++ b/main.py @@ -0,0 +1,85 @@ +from config import config +from datetime import datetime +from rich import print, box +from rich.console import Group +from rich.progress import Progress, BarColumn +from rich.panel import Panel +from rich.style import Style +import json +import os + + +# https://stackoverflow.com/questions/1094841/get-human-readable-version-of-file-size +# except, the units match the output of `df -h` and `borg info` +def readable_size(num, suffix=""): + for unit in ["B", "K", "M", "G", "T", "P", "E", "Z"]: + if abs(num) < 1000.0: + return f"{num:3.1f}{unit}{suffix}" + num /= 1000.0 + return f"{num:.1f}Y{suffix}" + + +# setup +# get the repo information + +if not config['repo']['path']: + raise Exception('No repo path provided!') +if not config['disk']['partition']: + raise Exception('No partition path provided!') + +# check if a passphrase has been provided +if config['repo']['passphrase']: + passphrase = 'BORG_PASSPHRASE=' + config['repo']['passphrase'] + ' ' +else: + passphrase = '' + + +borg_info = os.popen(passphrase + 'borg info --json ' + config['repo']['path']).read() +# parse repo info from json +borg_info = json.loads(borg_info) + +stats = borg_info['cache']['stats'] +csize = int(stats['unique_csize']) + +borg_list = os.popen(passphrase + 'borg list --json ' + config['repo']['path']).read() +# parse repo info from json +borg_list = json.loads(borg_list) + +last_archive = borg_list['archives'][-1] +# datetime format: https://borgbackup.readthedocs.io/en/stable/internals/frontends.html#standard-output +last_archive_time = datetime.strptime(last_archive['time'], '%Y-%m-%dT%H:%M:%S.%f') +last_archive_time = last_archive_time.strftime('%d/%m/%Y %H:%M') + +# get free disk space +df_avail = os.popen(config['disk']['ssh'] + ' "df --block-size=1000 --output=avail ' + config['disk']['partition'] + ' | tail -1"').read().rstrip() + + +# actually print the thing + +emphasis = Style(color="#d89961", bold=True) + +# it's hacky, but should work as expected +used = Progress( + '[progress.description]{task.description}', + # space used + f'[{emphasis}]{readable_size(csize)}[/{emphasis}]', + BarColumn(complete_style=emphasis, finished_style="#d34141"), + # space available + f'[{emphasis}]{readable_size((int(df_avail) * 1000))}[/{emphasis}]', + '[progress.percentage]{task.percentage:>3.0f}%' +) +used.add_task("Used:", completed=csize, total=(int(df_avail) * 1000)) + +disk_usage = Panel(used, box=box.DOUBLE, border_style=emphasis) + +avail_backups = Panel(f"Available: [{emphasis}]{len(borg_list['archives'])}[/{emphasis}] backups. Last backup from: [{emphasis}]{last_archive_time}[/{emphasis}]", box=box.DOUBLE, border_style=emphasis) + +output_group = Group( + disk_usage, + avail_backups +) + +print(Panel( + output_group, + box=box.SQUARE, border_style="#d89961" +)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..44a05e3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +rich==10.11.0 +toml==0.10.2 +tomli==1.2.1