Updated backup tool

pull/3/head
Jef Roosens 2021-01-14 16:29:58 +01:00
parent e5c0122007
commit 17b8df091d
4 changed files with 49 additions and 22 deletions

View File

@ -13,22 +13,21 @@ sys.excepthook = except_hook
# Define parser # Define parser
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Backup directories and Docker volumes.') description='Backup directories and Docker volumes.')
parser.add_argument('-s', '--spec', action='append', dest='specs', parser.add_argument('-f', '--file', action='append', dest='file',
help='Spec to back up. If not specified, all specs in ' help='File containing spec definitions.')
'in provided files are processed.')
parser.add_argument('-j', '--json', action='store_const', const=True, parser.add_argument('-j', '--json', action='store_const', const=True,
default=False, help='Print out the parsed specs as JSON ' default=False, help='Print out the parsed specs as JSON '
'and exit') 'and exit')
parser.add_argument('file', nargs='+', parser.add_argument('spec', nargs='*',
help='A YAML file containg specs.') help='The specs to process. Defaults to all.')
# Parse arguments # Parse arguments
args = parser.parse_args() args = parser.parse_args()
specs = sum([parse_specs_file(path) for path in args.file], []) specs = sum([parse_specs_file(path) for path in args.file], [])
# Filter specs if needed # Filter specs if needed
if args.specs: if args.spec:
specs = filter(lambda s: s.name in args.specs, specs) specs = filter(lambda s: s.name in args.spec, specs)
# Dump parsed data as json # Dump parsed data as json
if args.json: if args.json:

View File

@ -39,7 +39,7 @@ def parse_specs_file(path: Path) -> List[Spec]:
"volume": False, "volume": False,
"notify": { "notify": {
"title": "Backup Notification", "title": "Backup Notification",
"events": ["success"] "events": ["failure"]
} }
} }
@ -51,7 +51,9 @@ def parse_specs_file(path: Path) -> List[Spec]:
if "specs" not in data: if "specs" not in data:
raise MissingKeyError("specs") raise MissingKeyError("specs")
# TODO check if only specs section exists # Allow for default notify settings
if "notify" in data:
spec_skel["notify"] = data["notify"]
specs = [] specs = []
# Check format for each spec # Check format for each spec

View File

@ -8,7 +8,7 @@ class Spec:
self.destination = Path(destination) self.destination = Path(destination)
self.limit = limit self.limit = limit
self.title = title self.title = title
self.events = ["success"] if events is None else events self.events = [] if events is None else events
def to_dict(self): def to_dict(self):
return { return {
@ -24,6 +24,13 @@ class Spec:
def backup(self): def backup(self):
raise NotImplementedError() raise NotImplementedError()
def remove_redundant(self):
tarballs = self.destination.glob('*.tar.gz')
if len(tarballs) >= self.limit:
for path in tarballs[self.limit - 1:]:
path.unlink()
def notify(status_code): def notify(status_code):
if status_code: if status_code:
if "failure" not in self.events: if "failure" not in self.events:
@ -79,7 +86,6 @@ class Spec:
for name, info in data["specs"].items()] for name, info in data["specs"].items()]
class DirSpec(Spec): class DirSpec(Spec):
def __init__(self, name, source, destination, limit, title, events=None): def __init__(self, name, source, destination, limit, title, events=None):
super().__init__(name, destination, limit, title, events) super().__init__(name, destination, limit, title, events)
@ -87,14 +93,8 @@ class DirSpec(Spec):
self.source = Path(source) self.source = Path(source)
def backup(self): def backup(self):
tarballs = self.destination.glob('*.tar.gz') self.remove_redundant()
# Remove redundant tarballs
if len(tarballs) >= self.limit:
for path in tarballs[self.limit - 1:]:
path.unlink()
# Create new tarball
status_code = os.system( status_code = os.system(
"tar -C '{}' -czf '{}' -- .".format( "tar -C '{}' -czf '{}' -- .".format(
self.source, self.source,
@ -116,4 +116,28 @@ class DirSpec(Spec):
) )
class VolumeSpec(Spec): class VolumeSpec(Spec):
pass def __init__(self, name, volume, destination, limit, title, events=None):
super().__init__(name, destination, limit, title, events)
self.volume = volume
def backup(self):
status_code = os.system(
"docker run --rm -v '{}:/from' -v '{}:/to' alpine:latest "
"tar -C /from -czf '/to/{}' -- .".format(
self.volume,
self.destination,
self.get_filename()
)
)
@staticmethod
def from_dict(name, data):
return VolumeSpec(
name,
data["source"],
data["destination"],
data["limit"],
data["notify"]["title"],
data["notify"]["events"]
)

View File

@ -1,11 +1,13 @@
notify:
title: "title"
events:
- 'random'
specs: specs:
test-spec: test-spec:
source: '/some/path' source: '/some/path'
destination: '/some/other/path' destination: '/some/other/path'
limit: 7 limit: 7
notify:
events:
- 'failure'
test-2: test-2:
source: '/path/to' source: '/path/to'