|
@@ -21,6 +21,10 @@ class DependencyException(Exception):
|
|
|
pass
|
|
|
|
|
|
|
|
|
+class BackupException(Exception):
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
def deptree(sources, deps=None):
|
|
|
if deps is None:
|
|
|
deps = []
|
|
@@ -28,11 +32,11 @@ def deptree(sources, deps=None):
|
|
|
deferred = []
|
|
|
for name in sources.keys():
|
|
|
source = sources[name]
|
|
|
- if "depends" not in source:
|
|
|
- deps.append(name)
|
|
|
+ if "depends" in source and not source["depends"]:
|
|
|
+ deferred.append(name)
|
|
|
|
|
|
else:
|
|
|
- deferred.append(name)
|
|
|
+ deps.append(name)
|
|
|
|
|
|
while deferred:
|
|
|
name = deferred.pop()
|
|
@@ -55,30 +59,63 @@ def deptree(sources, deps=None):
|
|
|
return deps
|
|
|
|
|
|
|
|
|
+def status(name, value=None):
|
|
|
+ if not hasattr(status, "_handle"):
|
|
|
+ status._handle = {}
|
|
|
+
|
|
|
+ if value is None:
|
|
|
+ return status._handle[name] if name in status._handle else None
|
|
|
+
|
|
|
+ status._handle[name] = value
|
|
|
+
|
|
|
+
|
|
|
+def backup(name):
|
|
|
+ source = main.sources[name]
|
|
|
+ failed = [x for x in source["depends"] if not status(x)]
|
|
|
+ if failed:
|
|
|
+ raise BackupException(
|
|
|
+ "Unable to backup {0} due to ncomplete backups: {1}".format(
|
|
|
+ name, failed))
|
|
|
+
|
|
|
+ # TODO - handle explicit failures with false
|
|
|
+ # status(name, False)
|
|
|
+ status(name, True)
|
|
|
+
|
|
|
+
|
|
|
def main(args):
|
|
|
with pushd('etc/backup.d'):
|
|
|
with open("backup.yml") as f:
|
|
|
- config = yaml.load(f)
|
|
|
+ main.config = yaml.load(f)
|
|
|
|
|
|
sources = {}
|
|
|
- for source in config['sources']:
|
|
|
+ for source in main.config['sources']:
|
|
|
source = os.path.realpath(source)
|
|
|
for path in glob('{}/*.yml'.format(source)):
|
|
|
path = os.path.realpath(path)
|
|
|
with pushd(os.path.dirname(path)), open(path) as f:
|
|
|
data = yaml.load(f)
|
|
|
- if "depends" in data:
|
|
|
- for i in range(0, len(data["depends"])):
|
|
|
- data["depends"][i] = os.path.realpath(
|
|
|
- '{}.yml'.format(data["depends"][i]))
|
|
|
+ if "depends" not in data:
|
|
|
+ data["depends"] = []
|
|
|
+
|
|
|
+ for i in range(0, len(data["depends"])):
|
|
|
+ data["depends"][i] = os.path.realpath(
|
|
|
+ '{}.yml'.format(data["depends"][i]))
|
|
|
|
|
|
sources[path] = data
|
|
|
|
|
|
- config['sources'] = sources
|
|
|
- import json
|
|
|
- for name in deptree(config["sources"]):
|
|
|
- source = config["sources"][name]
|
|
|
- print(json.dumps(source, indent=2))
|
|
|
+ main.sources = sources
|
|
|
+ main.deptree = deptree(sources)
|
|
|
+ errors = []
|
|
|
+ for name in main.deptree:
|
|
|
+ try:
|
|
|
+ backup(name)
|
|
|
+
|
|
|
+ except BackupException as ex:
|
|
|
+ print(ex)
|
|
|
+ errors.append(ex)
|
|
|
+
|
|
|
+ if errors:
|
|
|
+ raise BackupException("At least one backup failed")
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
@@ -89,6 +126,10 @@ if __name__ == '__main__':
|
|
|
print(ex)
|
|
|
sys.exit(1)
|
|
|
|
|
|
+ except BackupException as ex:
|
|
|
+ print(ex)
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
except Exception:
|
|
|
from traceback import format_exc
|
|
|
msg = "Error encountered:\n" + format_exc().strip()
|