diff --git a/borg-client/__init__.py b/borg-client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/borg-client/borgclient.py b/borg-client/borgclient.py new file mode 100644 index 0000000..a4aecef --- /dev/null +++ b/borg-client/borgclient.py @@ -0,0 +1,50 @@ +import requests + + +class BorgClient(object): + def __init__(self, url: str, username: str, password: str): + self.url = url + self.client = requests.Session() + self.referer = self.url + self.username = username + self.password = password + + self.__login() + + def __login(self): + url = f"{self.url}/accounts/login/" + + post_data = { + "username": self.username, + "password": self.password, + } + return self.__post(url, post_data).text + + def __post(self, url, post_data): + self.client.get(url=url, verify=False) + csrf_token = self.client.cookies['csrftoken'] + + post_data['csrfmiddlewaretoken'] = csrf_token + + headers = dict(self.client.headers) + headers['X-CSRFToken'] = csrf_token + headers['Referer'] = self.referer + + post_responce = self.client.post(url=url, data=post_data, headers=headers, + verify=False) + return post_responce + + def post_error(self, post_data): + url = f"{self.url}/error" + + return self.__post(url, post_data).text + + def post_repo(self, post_data): + url = f"{self.url}/repo" + + return self.__post(url, post_data).text + + def post_archive_and_cache(self, post_data): + url = f"{self.url}/archive" + + return self.__post(url, post_data).text diff --git a/borg-client/main.py b/borg-client/main.py new file mode 100644 index 0000000..47c7612 --- /dev/null +++ b/borg-client/main.py @@ -0,0 +1,40 @@ +from borgclient import BorgClient +from sys import stdin +import argparse +from tables import Error, Repo, Archive, Cache +import json +from datetime import datetime + + +def main(args): + borg_output = " ".join(stdin.readlines()) + if not (args.label and args.username and args.password and args.url): + raise Exception("Supply label, username, password and url") + else: + client = BorgClient(url=args.url, username=args.username, password=args.password) + try: + borg_json = json.loads(borg_output) + repo = Repo.from_json(borg_json['repository']) + archive = Archive.from_json(borg_json['archive']) + cache = Cache.from_json(borg_json['cache']['stats']) + + client.post_repo(repo.get_dict(args.label)) + archive_cache = archive.get_dict(args.label) + archive_cache.update(cache.get_dict(args.label)) + client.post_archive_and_cache(archive_cache) + except json.JSONDecodeError: + error = Error(borg_output, datetime.utcnow()) + client.post_error(error.get_dict(args.label)) + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("-l", "--label", help="Repo Label", type=str, required=True) + parser.add_argument("-u", "--username", help="Username", type=str, required=True) + parser.add_argument("-p", "--password", help="Password", type=str, required=True) + parser.add_argument("-w", "--url", help="Server Url", type=str, required=True) + return parser.parse_args() + + +if __name__ == "__main__": + main(get_args()) diff --git a/borg-client/tables/__init__.py b/borg-client/tables/__init__.py new file mode 100644 index 0000000..55609cb --- /dev/null +++ b/borg-client/tables/__init__.py @@ -0,0 +1,4 @@ +from .error import Error +from .repo import Repo +from .archive import Archive +from .cache import Cache diff --git a/borg-client/tables/archive.py b/borg-client/tables/archive.py new file mode 100644 index 0000000..88360a3 --- /dev/null +++ b/borg-client/tables/archive.py @@ -0,0 +1,44 @@ +from datetime import datetime, timezone + + +class Archive(object): + def __init__(self, fingerprint: str, name: str, start: datetime, end: datetime, file_count: int, original_size: int, + compressed_size: int, deduplicated_size: int): + self.fingerprint = fingerprint + self.name = name + self.start = start + self.end = end + self.file_count = file_count + self.original_size = original_size + self.compressed_size = compressed_size + self.deduplicated_size = deduplicated_size + + @classmethod + def from_json(cls, json: dict): + fingerprint = json['id'] + name = json['name'] + start = datetime.fromisoformat(json['start']).astimezone(tz=timezone.utc).replace(tzinfo=None) + end = datetime.fromisoformat(json['end']).astimezone(tz=timezone.utc).replace(tzinfo=None) + + stats_json = json['stats'] + file_count = stats_json['nfiles'] + original_size = stats_json['original_size'] + compressed_size = stats_json['compressed_size'] + deduplicated_size = stats_json['deduplicated_size'] + + return cls(fingerprint, name, start, end, file_count, original_size, compressed_size, deduplicated_size) + + def get_dict(self, label): + if not label.strip(): + raise Exception("No label supplied") + return { + "label": label, + "fingerprint": self.fingerprint, + "name": self.name, + "start": self.start.isoformat(), + "end": self.end.isoformat(), + "file_count": self.file_count, + "original_size": self.original_size, + "compressed_size": self.compressed_size, + "deduplicated_size": self.deduplicated_size + } diff --git a/borg-client/tables/cache.py b/borg-client/tables/cache.py new file mode 100644 index 0000000..1bf0d55 --- /dev/null +++ b/borg-client/tables/cache.py @@ -0,0 +1,36 @@ +from datetime import datetime, timezone +from pathlib import Path + + +class Cache(object): + def __init__(self, total_chunks: int, total_csize: int, total_size: int, total_unique_chunks: int, + unique_csize: int, unique_size: int): + self.total_chunks = total_chunks + self.total_csize = total_csize + self.total_size = total_size + self.total_unique_chunks = total_unique_chunks + self.unique_csize = unique_csize + self.unique_size = unique_size + + @classmethod + def from_json(cls, json: dict): + total_chunks = json['total_chunks'] + total_csize = json['total_csize'] + total_size = json['total_size'] + total_unique_chunks = json['total_unique_chunks'] + unique_csize = json['unique_csize'] + unique_size = json['unique_size'] + return cls(total_chunks, total_csize, total_size, total_unique_chunks, unique_csize, unique_size) + + def get_dict(self, label): + if not label.strip(): + raise Exception("No label supplied") + return { + "label": label, + "total_chunks": self.total_chunks, + "total_csize": self.total_csize, + "total_size": self.total_size, + "total_unique_chunks": self.total_unique_chunks, + "unique_csize": self.unique_csize, + "unique_size": self.unique_size + } diff --git a/borg-client/tables/error.py b/borg-client/tables/error.py new file mode 100644 index 0000000..d55052c --- /dev/null +++ b/borg-client/tables/error.py @@ -0,0 +1,18 @@ +from datetime import datetime + + +class Error(object): + def __init__(self, error: str, time: datetime): + self.error = error.strip() + if not self.error: + self.error = "No error information supplied" + self.time = time + + def get_dict(self, label): + if not label.strip(): + raise Exception("No label supplied") + return { + "label": label, + "error": self.error.strip(), + "time": self.time.isoformat() + } diff --git a/borg-client/tables/repo.py b/borg-client/tables/repo.py new file mode 100644 index 0000000..1d90384 --- /dev/null +++ b/borg-client/tables/repo.py @@ -0,0 +1,28 @@ +from datetime import datetime, timezone +from pathlib import Path + + +class Repo(object): + def __init__(self, fingerprint: str, location, last_modified: datetime): + self.fingerprint = fingerprint + self.location = location + self.last_modified = last_modified + + @classmethod + def from_json(cls, json: dict): + uuid = json['id'] + location = Path(json['location']) + last_modified = datetime.fromisoformat(json['last_modified']) \ + .astimezone(tz=timezone.utc) \ + .replace(tzinfo=None) + return cls(uuid, location, last_modified) + + def get_dict(self, label): + if not label.strip(): + raise Exception("No label supplied") + return { + "label": label, + "fingerprint": self.fingerprint, + "location": self.location, + "last_modified": self.last_modified.isoformat() + }