extract.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import logging
  2. import os
  3. import subprocess
  4. from borgmatic.borg import environment, feature, flags
  5. from borgmatic.execute import DO_NOT_CAPTURE, execute_command
  6. logger = logging.getLogger(__name__)
  7. def extract_last_archive_dry_run(
  8. storage_config, repository, lock_wait=None, local_path='borg', remote_path=None
  9. ):
  10. '''
  11. Perform an extraction dry-run of the most recent archive. If there are no archives, skip the
  12. dry-run.
  13. '''
  14. remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
  15. lock_wait_flags = ('--lock-wait', str(lock_wait)) if lock_wait else ()
  16. verbosity_flags = ()
  17. if logger.isEnabledFor(logging.DEBUG):
  18. verbosity_flags = ('--debug', '--show-rc')
  19. elif logger.isEnabledFor(logging.INFO):
  20. verbosity_flags = ('--info',)
  21. full_list_command = (
  22. (local_path, 'list', '--short')
  23. + remote_path_flags
  24. + lock_wait_flags
  25. + verbosity_flags
  26. + (repository,)
  27. )
  28. borg_environment = environment.make_environment(storage_config)
  29. list_output = execute_command(
  30. full_list_command,
  31. output_log_level=None,
  32. borg_local_path=local_path,
  33. extra_environment=borg_environment,
  34. )
  35. try:
  36. last_archive_name = list_output.strip().splitlines()[-1]
  37. except IndexError:
  38. return
  39. list_flag = ('--list',) if logger.isEnabledFor(logging.DEBUG) else ()
  40. full_extract_command = (
  41. (local_path, 'extract', '--dry-run')
  42. + remote_path_flags
  43. + lock_wait_flags
  44. + verbosity_flags
  45. + list_flag
  46. + (
  47. '{repository}::{last_archive_name}'.format(
  48. repository=repository, last_archive_name=last_archive_name
  49. ),
  50. )
  51. )
  52. execute_command(
  53. full_extract_command, working_directory=None, extra_environment=borg_environment
  54. )
  55. def extract_archive(
  56. dry_run,
  57. repository,
  58. archive,
  59. paths,
  60. location_config,
  61. storage_config,
  62. local_borg_version,
  63. local_path='borg',
  64. remote_path=None,
  65. destination_path=None,
  66. strip_components=None,
  67. progress=False,
  68. extract_to_stdout=False,
  69. ):
  70. '''
  71. Given a dry-run flag, a local or remote repository path, an archive name, zero or more paths to
  72. restore from the archive, the local Borg version string, location/storage configuration dicts,
  73. optional local and remote Borg paths, and an optional destination path to extract to, extract
  74. the archive into the current directory.
  75. If extract to stdout is True, then start the extraction streaming to stdout, and return that
  76. extract process as an instance of subprocess.Popen.
  77. '''
  78. umask = storage_config.get('umask', None)
  79. lock_wait = storage_config.get('lock_wait', None)
  80. if progress and extract_to_stdout:
  81. raise ValueError('progress and extract_to_stdout cannot both be set')
  82. if feature.available(feature.Feature.NUMERIC_IDS, local_borg_version):
  83. numeric_ids_flags = ('--numeric-ids',) if location_config.get('numeric_owner') else ()
  84. else:
  85. numeric_ids_flags = ('--numeric-owner',) if location_config.get('numeric_owner') else ()
  86. full_command = (
  87. (local_path, 'extract')
  88. + (('--remote-path', remote_path) if remote_path else ())
  89. + numeric_ids_flags
  90. + (('--umask', str(umask)) if umask else ())
  91. + (('--lock-wait', str(lock_wait)) if lock_wait else ())
  92. + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
  93. + (('--debug', '--list', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
  94. + (('--dry-run',) if dry_run else ())
  95. + (('--strip-components', str(strip_components)) if strip_components else ())
  96. + (('--progress',) if progress else ())
  97. + (('--stdout',) if extract_to_stdout else ())
  98. + flags.make_repository_archive_flags(
  99. repository if ':' in repository else os.path.abspath(repository),
  100. archive,
  101. local_borg_version,
  102. )
  103. + (tuple(paths) if paths else ())
  104. )
  105. borg_environment = environment.make_environment(storage_config)
  106. # The progress output isn't compatible with captured and logged output, as progress messes with
  107. # the terminal directly.
  108. if progress:
  109. return execute_command(
  110. full_command,
  111. output_file=DO_NOT_CAPTURE,
  112. working_directory=destination_path,
  113. extra_environment=borg_environment,
  114. )
  115. return None
  116. if extract_to_stdout:
  117. return execute_command(
  118. full_command,
  119. output_file=subprocess.PIPE,
  120. working_directory=destination_path,
  121. run_to_completion=False,
  122. extra_environment=borg_environment,
  123. )
  124. # Don't give Borg local path so as to error on warnings, as "borg extract" only gives a warning
  125. # if the restore paths don't exist in the archive.
  126. execute_command(
  127. full_command, working_directory=destination_path, extra_environment=borg_environment
  128. )