__init__.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import datetime
  2. import re
  3. import subprocess
  4. import sys
  5. import time
  6. def _duplicity(params):
  7. stdout = subprocess.check_output(
  8. ['duplicity'] + params,
  9. )
  10. return stdout.decode(sys.stdout.encoding)
  11. def _parse_duplicity_timestamp(timestamp):
  12. return datetime.datetime.fromtimestamp(
  13. time.mktime(time.strptime(timestamp))
  14. )
  15. class Collection(object):
  16. def __init__(self, url):
  17. self.url = url
  18. def request_status(self):
  19. return _CollectionStatus._parse(
  20. text=_duplicity(['collection-status', self.url])
  21. )
  22. class _Status(object):
  23. def __eq__(self, other):
  24. return isinstance(self, type(other)) and vars(self) == vars(other)
  25. def __neq__(self, other):
  26. return not (self == other)
  27. class _CollectionStatus(_Status):
  28. chain_separator_regex = r'-{25}\s'
  29. def __init__(self, primary_chain):
  30. self.primary_chain = primary_chain
  31. @classmethod
  32. def _parse(cls, text):
  33. primary_chain_match = re.search(
  34. '^Found primary backup chain.*\s{sep}([\w\W]*?)\s{sep}'.format(
  35. sep=_CollectionStatus.chain_separator_regex,
  36. ),
  37. text,
  38. re.MULTILINE,
  39. )
  40. return cls(
  41. primary_chain=_ChainStatus._parse(
  42. text=primary_chain_match.group(1),
  43. ),
  44. )
  45. class _ChainStatus(_Status):
  46. def __init__(self, sets):
  47. self.sets = sets
  48. def __eq__(self, other):
  49. return isinstance(self, type(other)) and vars(self) == vars(other)
  50. def __neq__(self, other):
  51. return not (self == other)
  52. @classmethod
  53. def _parse(cls, text):
  54. sets = []
  55. set_lines = re.split(r'Num volumes: *\r?\n', text)[1]
  56. for set_line in re.split(r'\r?\n', set_lines):
  57. set_attr = re.match(
  58. r'\s*(?P<mode>\w+) {2,}(?P<ts>.+?) {2,} (?P<vol>\d+)',
  59. set_line,
  60. ).groupdict()
  61. # duplicity uses time.asctime().
  62. # time.strptime() without format inverts time.asctime().
  63. sets.append(_SetStatus(
  64. backup_time=_parse_duplicity_timestamp(set_attr['ts']),
  65. ))
  66. return cls(sets=sets)
  67. class _SetStatus(_Status):
  68. def __init__(self, backup_time):
  69. self.backup_time = backup_time
  70. def __eq__(self, other):
  71. return isinstance(self, type(other)) and vars(self) == vars(other)
  72. def __neq__(self, other):
  73. return not (self == other)