Added working backup system

recovery-function
Jef Roosens 2021-01-15 21:08:56 +01:00
parent bb33f7cbbc
commit 44764d30d9
7 changed files with 72 additions and 28 deletions

View File

@ -8,7 +8,7 @@ def except_hook(ext_type, value, traceback):
sys.stderr.write("{}: {}\n".format(ext_type.__name__, value)) sys.stderr.write("{}: {}\n".format(ext_type.__name__, value))
sys.excepthook = except_hook # sys.excepthook = except_hook
# Define parser # Define parser

View File

@ -0,0 +1,4 @@
class Notifier:
# Placeholder
def __init__(*args, **kwargs):
pass

View File

@ -3,32 +3,33 @@ import yaml
from pathlib import Path from pathlib import Path
from typing import List, Union from typing import List, Union
from specs import Spec, DirectorySpec from specs import Spec, DirectorySpec
import skeleton
def read_specs_file(path: Union[str, Path]) -> List[Spec]: def read_specs_file(path: Union[str, Path]) -> List[Spec]:
with open(path, "r") as yaml_file: with open(path, "r") as yaml_file:
data = yaml.safe_load(yaml_file, Loader=yaml.FullLoader) data = yaml.safe_load(yaml_file)
categories = [("directories", DirectorySpec)] categories = [("directories", DirectorySpec)]
specs = [] specs = []
for key, class_name in categories: for key, class_name in categories:
if key not in data["specs"]: if not data["specs"].get(key):
continue continue
# Check what defaults are present # Check what defaults are present
defaults = [] defaults = {}
if data.get("defaults"): if data.get("defaults"):
if data["defaults"].get("all"): if data["defaults"].get("all"):
defaults.append(data["defaults"]["all"]) defaults = skeleton.merge(defaults, data["defaults"]["all"])
if data["defaults"].get(key): if data["defaults"].get(key):
defaults.append(data["defaults"][key]) defaults = skeleton.merge(defaults, data["defaults"][key])
specs.extend( specs.extend(
[ [
class_name.from_dict(name, spec, *defaults) class_name.from_dict(name, spec, defaults)
for name, spec in data["specs"][key].items() for name, spec in data["specs"][key].items()
] ]
) )

View File

@ -15,7 +15,35 @@ class MissingKeyError(Exception):
super().__init__(key) super().__init__(key)
def combine(data: Dict, skel: Dict) -> Dict: def merge(*dicts: [Dict]) -> Dict:
# Base cases
if len(dicts) == 0:
return {}
if len(dicts) == 1:
return dicts[0]
# We merge the first two dicts
d1, d2 = dicts[0], dicts[1]
output = d1.copy()
for key, value in d2.items():
if type(value) == dict:
# Merge the two sub-dictionaries
output[key] = (
merge(output[key], value)
if type(output.get(key)) == dict
else value
)
else:
output[key] = value
return merge(output, *dicts[2:])
def merge_with_skeleton(data: Dict, skel: Dict) -> Dict:
""" """
Compare a dict with a given skeleton dict, and fill in default values where Compare a dict with a given skeleton dict, and fill in default values where
needed. needed.
@ -42,6 +70,6 @@ def combine(data: Dict, skel: Dict) -> Dict:
# Recurse into dicts # Recurse into dicts
elif type(value) == dict: elif type(value) == dict:
data[key] = combine_with_skeleton(data[key], value) data[key] = merge_with_skeleton(data[key], value)
return data return data

View File

@ -10,14 +10,9 @@ class DirectorySpec(Spec):
A spec for backing up a local directory. A spec for backing up a local directory.
""" """
__SKEL = { _SKEL = {
"name": None,
"source": None, "source": None,
"destination": None,
"limit": None,
"notifier": None,
"command": "tar -czf '{destination}/{filename}' .", "command": "tar -czf '{destination}/{filename}' .",
"extension": "tar.gz",
} }
def __init__( def __init__(
@ -28,9 +23,9 @@ class DirectorySpec(Spec):
limit: int, limit: int,
command: str, command: str,
extension: str, extension: str,
notifier=None, notify=None,
): ):
super().__init__(name, destination, limit, extension, notifier) super().__init__(name, destination, limit, extension, notify)
self.source = source if type(source) == Path else Path(source) self.source = source if type(source) == Path else Path(source)
@ -58,6 +53,7 @@ class DirectorySpec(Spec):
filename=filename, filename=filename,
), ),
cwd=self.source, cwd=self.source,
shell=True,
) )
if self.notifier: if self.notifier:

View File

@ -3,6 +3,8 @@ from pathlib import Path
from typing import Union, Dict from typing import Union, Dict
import skeleton import skeleton
import os import os
from notifier import Notifier
import inspect
class Spec: class Spec:
@ -10,11 +12,13 @@ class Spec:
Base class for all other spec types. Base class for all other spec types.
""" """
__SKEL = { _SKEL = {
"name": None,
"destination": None, "destination": None,
"limit": None, "limit": None,
"notifier": None, "notify": {
"title": "Backup Notification",
"events": ["backup_sucess"],
},
"extension": "tar.gz", "extension": "tar.gz",
} }
@ -24,7 +28,7 @@ class Spec:
destination: Union[Path, str], destination: Union[Path, str],
limit: int, limit: int,
extension: str, extension: str,
notifier=None, notify=None,
): ):
""" """
Args: Args:
@ -48,9 +52,15 @@ class Spec:
) )
self.limit = limit self.limit = limit
self.notifier = notifier self.notifier = Notifier(*notify) if notify else None
self.extension = extension self.extension = extension
@classmethod
def skeleton(cls):
return skeleton.merge(
*[val._SKEL for val in reversed(inspect.getmro(cls)[:-1])]
)
def remove_backups(self): def remove_backups(self):
""" """
Remove all backups exceeding the limit Remove all backups exceeding the limit
@ -73,14 +83,15 @@ class Spec:
raise NotImplementedError() raise NotImplementedError()
@classmethod @classmethod
def from_dict(cls, name, obj: Dict, *defaults: Dict) -> Spec: def from_dict(cls, name, obj: Dict, defaults: Dict) -> Spec:
# Combine defaults with skeleton, creating new skeleton # Combine defaults with skeleton, creating new skeleton
skel = cls.__SKEL skel = skeleton.merge(cls.skeleton(), defaults)
print(skel)
for default in defaults:
skel = skeleton.combine(defaults, skel)
# Then, combine actual values with new skeleton # Then, combine actual values with new skeleton
obj = skeleton.combine(obj, skel) obj = skeleton.merge_with_skeleton(obj, skel)
return cls(name, **obj) return cls(name, **obj)
def to_dict(self):
raise NotImplementedError()

View File

@ -36,3 +36,7 @@ specs:
cmd: '' cmd: ''
extension: 'tar.gz' extension: 'tar.gz'
directories: directories:
test:
destination: "/home/jjr/test"
limit: 5
source: "/home/jjr"