backup.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import contextlib
  2. import os
  3. import yaml
  4. import sys
  5. from glob import glob
  6. @contextlib.contextmanager
  7. def pushd(newDir):
  8. previousDir = os.getcwd()
  9. os.chdir(newDir)
  10. try:
  11. yield
  12. finally:
  13. os.chdir(previousDir)
  14. class DependencyException(Exception):
  15. pass
  16. def deptree(sources, deps=None):
  17. if deps is None:
  18. deps = []
  19. deferred = []
  20. for name in sources.keys():
  21. source = sources[name]
  22. if "depends" not in source:
  23. deps.append(name)
  24. else:
  25. deferred.append(name)
  26. while deferred:
  27. name = deferred.pop()
  28. depends = sources[name]["depends"]
  29. # todo - detect dependency loop
  30. if name in depends:
  31. raise DependencyException('Source {} depends upon itself'.format(name))
  32. elif set(depends).issubset(set(deps)):
  33. deps.append(name)
  34. elif not set(depends).issubset(set(deps)):
  35. missing = ', '.join(set(depends).difference(set(deps)))
  36. raise DependencyException(
  37. 'Source {0} has missing dependencies: {1}'.format(name, missing))
  38. else:
  39. deferred.append(name)
  40. return deps
  41. def main(args):
  42. with pushd('etc/backup.d'):
  43. with open("backup.yml") as f:
  44. config = yaml.load(f)
  45. sources = {}
  46. for source in config['sources']:
  47. source = os.path.realpath(source)
  48. for path in glob('{}/*.yml'.format(source)):
  49. path = os.path.realpath(path)
  50. with pushd(os.path.dirname(path)), open(path) as f:
  51. data = yaml.load(f)
  52. if "depends" in data:
  53. for i in range(0, len(data["depends"])):
  54. data["depends"][i] = os.path.realpath('{}.yml'.format(data["depends"][i]))
  55. sources[path] = data
  56. config['sources'] = sources
  57. import json
  58. for name in deptree(config["sources"]):
  59. source = config["sources"][name]
  60. print(json.dumps(source, indent=2))
  61. if __name__ == '__main__':
  62. try:
  63. main(sys.argv[1:])
  64. except DependencyException as ex:
  65. print(ex)
  66. sys.exit(1)
  67. except Exception:
  68. from traceback import format_exc
  69. msg = "Error encountered:\n" + format_exc().strip()
  70. print(msg)
  71. sys.exit(1)