mirror of
https://github.com/borgbackup/borg.git
synced 2026-05-28 04:03:21 -04:00
parent
678fb2c845
commit
3982c34e6c
6 changed files with 43 additions and 37 deletions
|
|
@ -451,17 +451,15 @@ class ArchiveChecker:
|
|||
|
||||
def __init__(self):
|
||||
self.error_found = False
|
||||
self.progress = True
|
||||
self.possibly_superseded = set()
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
|
||||
def __del__(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
|
||||
def check(self, repository, progress=True, repair=False):
|
||||
def check(self, repository, repair=False):
|
||||
self.report_progress('Starting archive consistency check...')
|
||||
self.repair = repair
|
||||
self.progress = progress
|
||||
self.repository = repository
|
||||
self.init_chunks()
|
||||
self.key = self.identify_key(repository)
|
||||
|
|
@ -494,9 +492,8 @@ class ArchiveChecker:
|
|||
def report_progress(self, msg, error=False):
|
||||
if error:
|
||||
self.error_found = True
|
||||
if error or self.progress:
|
||||
print(msg, file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
print(msg, file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
|
||||
def identify_key(self, repository):
|
||||
cdata = repository.get(next(self.chunks.iteritems())[0])
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import io
|
|||
import os
|
||||
import stat
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
from attic import __version__
|
||||
from attic.archive import Archive, ArchiveChecker
|
||||
|
|
@ -72,13 +73,10 @@ in data loss.
|
|||
Type "Yes I am sure" if you understand this and want to continue.\n""")
|
||||
if input('Do you want to continue? ') == 'Yes I am sure':
|
||||
break
|
||||
if args.progress is None:
|
||||
args.progress = sys.stdout.isatty() or args.verbose
|
||||
if not repository.check(progress=args.progress, repair=args.repair):
|
||||
return 1
|
||||
|
||||
if not ArchiveChecker().check(repository, progress=args.progress, repair=args.repair):
|
||||
return 1
|
||||
if args.phase in ('all', 'repository') and not repository.check(repair=args.repair):
|
||||
return 1
|
||||
if args.phase in ('all', 'archive') and not ArchiveChecker().check(repository, repair=args.repair):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def do_change_passphrase(self, args):
|
||||
|
|
@ -429,26 +427,31 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
|
|||
choices=('none', 'passphrase', 'keyfile'), default='none',
|
||||
help='select encryption method')
|
||||
|
||||
check_epilog = """
|
||||
Progress status will be reported on the standard error stream by default when
|
||||
it is attached to a terminal. Any problems found are printed to the standard error
|
||||
stream and the command will have a non zero exit code.
|
||||
"""
|
||||
check_epilog = textwrap.dedent("""
|
||||
The check command verifies the consistency of a repository and corresponding
|
||||
archives. The check is performed in two phases. In the first phase the
|
||||
checksums of the underlying repository segment files are verified to detect
|
||||
bit rot and other types of damage. In the second phase the consistency and
|
||||
correctness of the archive metadata is verified.
|
||||
|
||||
A specific check phase can be selected using the --phase=repository|archive
|
||||
option. This can be useful since the "archive" phase can be time consuming
|
||||
and requires access to the key file and/or passphrase if encryption is enabled.
|
||||
""")
|
||||
subparser = subparsers.add_parser('check', parents=[common_parser],
|
||||
description=self.do_check.__doc__,
|
||||
epilog=check_epilog)
|
||||
epilog=check_epilog,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
subparser.set_defaults(func=self.do_check)
|
||||
subparser.add_argument('repository', metavar='REPOSITORY',
|
||||
type=location_validator(archive=False),
|
||||
help='repository to check consistency of')
|
||||
subparser.add_argument('--progress', dest='progress', action='store_true',
|
||||
default=None,
|
||||
help='Report progress status to standard output stream')
|
||||
subparser.add_argument('--no-progress', dest='progress', action='store_false',
|
||||
help='Disable progress reporting')
|
||||
subparser.add_argument('--phase', dest='phase', choices=['repository', 'archive', 'all'],
|
||||
default='all',
|
||||
help='which checks to perform (default: all)')
|
||||
subparser.add_argument('--repair', dest='repair', action='store_true',
|
||||
default=False,
|
||||
help='Attempt to repair any inconsistencies found')
|
||||
help='attempt to repair any inconsistencies found')
|
||||
|
||||
subparser = subparsers.add_parser('change-passphrase', parents=[common_parser],
|
||||
description=self.do_change_passphrase.__doc__)
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ class RemoteRepository(object):
|
|||
w_fds = []
|
||||
self.ignore_responses |= set(waiting_for)
|
||||
|
||||
def check(self, progress=False, repair=False):
|
||||
return self.call('check', progress, repair)
|
||||
def check(self, repair=False):
|
||||
return self.call('check', repair)
|
||||
|
||||
def commit(self, *args):
|
||||
return self.call('commit')
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ class Repository(object):
|
|||
self.write_index()
|
||||
self.rollback()
|
||||
|
||||
def check(self, progress=False, repair=False):
|
||||
def check(self, repair=False):
|
||||
"""Check repository consistency
|
||||
|
||||
This method verifies all segment checksums and makes sure
|
||||
|
|
@ -244,9 +244,8 @@ class Repository(object):
|
|||
nonlocal error_found
|
||||
if error:
|
||||
error_found = True
|
||||
if error or progress:
|
||||
print(msg, file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
print(msg, file=sys.stderr)
|
||||
sys.stderr.flush()
|
||||
|
||||
assert not self._active_txn
|
||||
report_progress('Starting repository check...')
|
||||
|
|
|
|||
|
|
@ -340,6 +340,17 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
|
|||
archive = Archive(repository, key, manifest, name)
|
||||
return archive, repository
|
||||
|
||||
def test_check_usage(self):
|
||||
output = self.attic('check', self.repository_location, exit_code=0)
|
||||
self.assert_in('Starting repository check', output)
|
||||
self.assert_in('Starting archive consistency check', output)
|
||||
output = self.attic('check', '--phase', 'repository', self.repository_location, exit_code=0)
|
||||
self.assert_in('Starting repository check', output)
|
||||
self.assert_not_in('Starting archive consistency check', output)
|
||||
output = self.attic('check', '--phase', 'archive', self.repository_location, exit_code=0)
|
||||
self.assert_not_in('Starting repository check', output)
|
||||
self.assert_in('Starting archive consistency check', output)
|
||||
|
||||
def test_missing_file_chunk(self):
|
||||
archive, repository = self.open_archive('archive1')
|
||||
for item in archive.iter_items():
|
||||
|
|
@ -372,8 +383,8 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
|
|||
repository.delete(Manifest.MANIFEST_ID)
|
||||
repository.commit()
|
||||
self.attic('check', self.repository_location, exit_code=1)
|
||||
self.attic('check', '--repair', '--progress', self.repository_location, exit_code=0)
|
||||
self.attic('check', '--progress', self.repository_location, exit_code=0)
|
||||
self.attic('check', '--repair', self.repository_location, exit_code=0)
|
||||
self.attic('check', self.repository_location, exit_code=0)
|
||||
|
||||
def test_extra_chunks(self):
|
||||
self.attic('check', self.repository_location, exit_code=0)
|
||||
|
|
|
|||
|
|
@ -86,10 +86,6 @@ Examples
|
|||
|
||||
.. include:: usage/check.rst.inc
|
||||
|
||||
The check command verifies the consistency of a repository. Any inconsistencies
|
||||
found are reported to the standard error stream and the command will have a
|
||||
non zero exit code.
|
||||
|
||||
.. include:: usage/delete.rst.inc
|
||||
|
||||
This command deletes an archive from the repository. Any disk space not
|
||||
|
|
|
|||
Loading…
Reference in a new issue