create.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import glob
  2. import itertools
  3. import logging
  4. import os
  5. import subprocess
  6. import tempfile
  7. from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
  8. logger = logging.getLogger(__name__)
  9. def initialize_environment(storage_config):
  10. passphrase = storage_config.get('encryption_passphrase')
  11. if passphrase:
  12. os.environ['BORG_PASSPHRASE'] = passphrase
  13. ssh_command = storage_config.get('ssh_command')
  14. if ssh_command:
  15. os.environ['BORG_RSH'] = ssh_command
  16. def _expand_directory(directory):
  17. '''
  18. Given a directory path, expand any tilde (representing a user's home directory) and any globs
  19. therein. Return a list of one or more resulting paths.
  20. '''
  21. expanded_directory = os.path.expanduser(directory)
  22. return glob.glob(expanded_directory) or [expanded_directory]
  23. def _write_exclude_file(exclude_patterns=None):
  24. '''
  25. Given a sequence of exclude patterns, write them to a named temporary file and return it. Return
  26. None if no patterns are provided.
  27. '''
  28. if not exclude_patterns:
  29. return None
  30. exclude_file = tempfile.NamedTemporaryFile('w')
  31. exclude_file.write('\n'.join(exclude_patterns))
  32. exclude_file.flush()
  33. return exclude_file
  34. def _make_exclude_flags(location_config, exclude_patterns_filename=None):
  35. '''
  36. Given a location config dict with various exclude options, and a filename containing any exclude
  37. patterns, return the corresponding Borg flags as a tuple.
  38. '''
  39. exclude_filenames = tuple(location_config.get('exclude_from') or ()) + (
  40. (exclude_patterns_filename,) if exclude_patterns_filename else ()
  41. )
  42. exclude_from_flags = tuple(
  43. itertools.chain.from_iterable(
  44. ('--exclude-from', exclude_filename)
  45. for exclude_filename in exclude_filenames
  46. )
  47. )
  48. caches_flag = ('--exclude-caches',) if location_config.get('exclude_caches') else ()
  49. if_present = location_config.get('exclude_if_present')
  50. if_present_flags = ('--exclude-if-present', if_present) if if_present else ()
  51. return exclude_from_flags + caches_flag + if_present_flags
  52. def create_archive(
  53. verbosity, repository, location_config, storage_config,
  54. ):
  55. '''
  56. Given a vebosity flag, a local or remote repository path, a location config dict, and a storage
  57. config dict, create a Borg archive.
  58. '''
  59. sources = tuple(
  60. itertools.chain.from_iterable(
  61. _expand_directory(directory)
  62. for directory in location_config['source_directories']
  63. )
  64. )
  65. exclude_patterns_file = _write_exclude_file(location_config.get('exclude_patterns'))
  66. exclude_flags = _make_exclude_flags(
  67. location_config,
  68. exclude_patterns_file.name if exclude_patterns_file else None,
  69. )
  70. compression = storage_config.get('compression', None)
  71. compression_flags = ('--compression', compression) if compression else ()
  72. remote_rate_limit = storage_config.get('remote_rate_limit', None)
  73. remote_rate_limit_flags = ('--remote-ratelimit', str(remote_rate_limit)) if remote_rate_limit else ()
  74. umask = storage_config.get('umask', None)
  75. umask_flags = ('--umask', str(umask)) if umask else ()
  76. one_file_system_flags = ('--one-file-system',) if location_config.get('one_file_system') else ()
  77. files_cache = location_config.get('files_cache')
  78. files_cache_flags = ('--files-cache', files_cache) if files_cache else ()
  79. remote_path = location_config.get('remote_path')
  80. remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
  81. verbosity_flags = {
  82. VERBOSITY_SOME: ('--info', '--stats',),
  83. VERBOSITY_LOTS: ('--debug', '--list', '--stats'),
  84. }.get(verbosity, ())
  85. default_archive_name_format = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}'
  86. archive_name_format = storage_config.get('archive_name_format', default_archive_name_format)
  87. full_command = (
  88. 'borg', 'create',
  89. '{repository}::{archive_name_format}'.format(
  90. repository=repository,
  91. archive_name_format=archive_name_format,
  92. ),
  93. ) + sources + exclude_flags + compression_flags + remote_rate_limit_flags + \
  94. one_file_system_flags + files_cache_flags + remote_path_flags + umask_flags + \
  95. verbosity_flags
  96. logger.debug(' '.join(full_command))
  97. subprocess.check_call(full_command)