Updated backup tool
parent
e5c0122007
commit
17b8df091d
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Reference in New Issue