This commit is contained in:
Agatha Lovelace 2021-10-05 02:56:53 +03:00
commit 93a871efa7
No known key found for this signature in database
GPG Key ID: 2DB18BA2E0A80BC3
4 changed files with 120 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
__pycache__

31
config.py Normal file
View File

@ -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')

85
main.py Normal file
View File

@ -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"
))

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
rich==10.11.0
toml==0.10.2
tomli==1.2.1