2
0
Эх сурвалжийг харах

Merge pull request #1739 from ThomasWaldmann/borg-check-refactor

borg check --first / --last / --sort / --prefix, fixes #1663
TW 8 жил өмнө
parent
commit
1a272ddd48

+ 14 - 16
src/borg/archive.py

@@ -978,19 +978,19 @@ class ArchiveChecker:
         self.error_found = False
         self.error_found = False
         self.possibly_superseded = set()
         self.possibly_superseded = set()
 
 
-    def check(self, repository, repair=False, archive=None, last=None, prefix=None, verify_data=False,
-              save_space=False):
+    def check(self, repository, repair=False, archive=None, first=0, last=0, sort_by='', prefix='',
+              verify_data=False, save_space=False):
         """Perform a set of checks on 'repository'
         """Perform a set of checks on 'repository'
 
 
         :param repair: enable repair mode, write updated or corrected data into repository
         :param repair: enable repair mode, write updated or corrected data into repository
         :param archive: only check this archive
         :param archive: only check this archive
-        :param last: only check this number of recent archives
+        :param first/last/sort_by: only check this number of first/last archives ordered by sort_by
         :param prefix: only check archives with this prefix
         :param prefix: only check archives with this prefix
         :param verify_data: integrity verification of data referenced by archives
         :param verify_data: integrity verification of data referenced by archives
         :param save_space: Repository.commit(save_space)
         :param save_space: Repository.commit(save_space)
         """
         """
         logger.info('Starting archive consistency check...')
         logger.info('Starting archive consistency check...')
-        self.check_all = archive is None and last is None and prefix is None
+        self.check_all = archive is None and not any((first, last, prefix))
         self.repair = repair
         self.repair = repair
         self.repository = repository
         self.repository = repository
         self.init_chunks()
         self.init_chunks()
@@ -1003,7 +1003,7 @@ class ArchiveChecker:
             self.manifest = self.rebuild_manifest()
             self.manifest = self.rebuild_manifest()
         else:
         else:
             self.manifest, _ = Manifest.load(repository, key=self.key)
             self.manifest, _ = Manifest.load(repository, key=self.key)
-        self.rebuild_refcounts(archive=archive, last=last, prefix=prefix)
+        self.rebuild_refcounts(archive=archive, first=first, last=last, sort_by=sort_by, prefix=prefix)
         self.orphan_chunks_check()
         self.orphan_chunks_check()
         self.finish(save_space=save_space)
         self.finish(save_space=save_space)
         if self.error_found:
         if self.error_found:
@@ -1160,7 +1160,7 @@ class ArchiveChecker:
         logger.info('Manifest rebuild complete.')
         logger.info('Manifest rebuild complete.')
         return manifest
         return manifest
 
 
-    def rebuild_refcounts(self, archive=None, last=None, prefix=None):
+    def rebuild_refcounts(self, archive=None, first=0, last=0, sort_by='', prefix=''):
         """Rebuild object reference counts by walking the metadata
         """Rebuild object reference counts by walking the metadata
 
 
         Missing and/or incorrect data is repaired when detected
         Missing and/or incorrect data is repaired when detected
@@ -1294,12 +1294,11 @@ class ArchiveChecker:
                     i += 1
                     i += 1
 
 
         if archive is None:
         if archive is None:
-            # we need last N or all archives
-            archive_infos = self.manifest.archives.list(sort_by=['ts'], reverse=True)
-            if prefix is not None:
-                archive_infos = [info for info in archive_infos if info.name.startswith(prefix)]
-            num_archives = len(archive_infos)
-            end = None if last is None else min(num_archives, last)
+            sort_by = sort_by.split(',')
+            if any((first, last, prefix)):
+                archive_infos = self.manifest.archives.list(sort_by=sort_by, prefix=prefix, first=first, last=last)
+            else:
+                archive_infos = self.manifest.archives.list(sort_by=sort_by)
         else:
         else:
             # we only want one specific archive
             # we only want one specific archive
             info = self.manifest.archives.get(archive)
             info = self.manifest.archives.get(archive)
@@ -1308,12 +1307,11 @@ class ArchiveChecker:
                 archive_infos = []
                 archive_infos = []
             else:
             else:
                 archive_infos = [info]
                 archive_infos = [info]
-            num_archives = 1
-            end = 1
+        num_archives = len(archive_infos)
 
 
         with cache_if_remote(self.repository) as repository:
         with cache_if_remote(self.repository) as repository:
-            for i, info in enumerate(archive_infos[:end]):
-                logger.info('Analyzing archive {} ({}/{})'.format(info.name, num_archives - i, num_archives))
+            for i, info in enumerate(archive_infos):
+                logger.info('Analyzing archive {} ({}/{})'.format(info.name, i + 1, num_archives))
                 archive_id = info.id
                 archive_id = info.id
                 if archive_id not in self.chunks:
                 if archive_id not in self.chunks:
                     logger.error('Archive metadata block is missing!')
                     logger.error('Archive metadata block is missing!')

+ 5 - 9
src/borg/archiver.py

@@ -206,16 +206,16 @@ class Archiver:
                        truish=('YES', ), retry=False,
                        truish=('YES', ), retry=False,
                        env_var_override='BORG_CHECK_I_KNOW_WHAT_I_AM_DOING'):
                        env_var_override='BORG_CHECK_I_KNOW_WHAT_I_AM_DOING'):
                 return EXIT_ERROR
                 return EXIT_ERROR
-        if args.repo_only and args.verify_data:
-            self.print_error("--repository-only and --verify-data contradict each other. Please select one.")
+        if args.repo_only and any((args.verify_data, args.first, args.last, args.prefix)):
+            self.print_error("--repository-only contradicts --first, --last, --prefix and --verify-data arguments.")
             return EXIT_ERROR
             return EXIT_ERROR
         if not args.archives_only:
         if not args.archives_only:
             if not repository.check(repair=args.repair, save_space=args.save_space):
             if not repository.check(repair=args.repair, save_space=args.save_space):
                 return EXIT_WARNING
                 return EXIT_WARNING
         if not args.repo_only and not ArchiveChecker().check(
         if not args.repo_only and not ArchiveChecker().check(
                 repository, repair=args.repair, archive=args.location.archive,
                 repository, repair=args.repair, archive=args.location.archive,
-                last=args.last, prefix=args.prefix, verify_data=args.verify_data,
-                save_space=args.save_space):
+                first=args.first, last=args.last, sort_by=args.sort_by or 'ts', prefix=args.prefix,
+                verify_data=args.verify_data, save_space=args.save_space):
             return EXIT_WARNING
             return EXIT_WARNING
         return EXIT_SUCCESS
         return EXIT_SUCCESS
 
 
@@ -1660,14 +1660,10 @@ class Archiver:
         subparser.add_argument('--save-space', dest='save_space', action='store_true',
         subparser.add_argument('--save-space', dest='save_space', action='store_true',
                                default=False,
                                default=False,
                                help='work slower, but using less space')
                                help='work slower, but using less space')
-        subparser.add_argument('--last', dest='last',
-                               type=int, default=None, metavar='N',
-                               help='only check last N archives (Default: all)')
-        subparser.add_argument('-P', '--prefix', dest='prefix', type=PrefixSpec,
-                               help='only consider archive names starting with this prefix')
         subparser.add_argument('-p', '--progress', dest='progress',
         subparser.add_argument('-p', '--progress', dest='progress',
                                action='store_true', default=False,
                                action='store_true', default=False,
                                help="""show progress display while checking""")
                                help="""show progress display while checking""")
+        self.add_archives_filters_args(subparser)
 
 
         change_passphrase_epilog = textwrap.dedent("""
         change_passphrase_epilog = textwrap.dedent("""
         The key files used for repository encryption are optionally passphrase
         The key files used for repository encryption are optionally passphrase

+ 6 - 0
src/borg/testsuite/archiver.py

@@ -2014,6 +2014,12 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase):
         self.assert_in('Starting archive consistency check', output)
         self.assert_in('Starting archive consistency check', output)
         output = self.cmd('check', '-v', '--archives-only', '--prefix=archive2', self.repository_location, exit_code=0)
         output = self.cmd('check', '-v', '--archives-only', '--prefix=archive2', self.repository_location, exit_code=0)
         self.assert_not_in('archive1', output)
         self.assert_not_in('archive1', output)
+        output = self.cmd('check', '-v', '--archives-only', '--first=1', self.repository_location, exit_code=0)
+        self.assert_in('archive1', output)
+        self.assert_not_in('archive2', output)
+        output = self.cmd('check', '-v', '--archives-only', '--last=1', self.repository_location, exit_code=0)
+        self.assert_not_in('archive1', output)
+        self.assert_in('archive2', output)
 
 
     def test_missing_file_chunk(self):
     def test_missing_file_chunk(self):
         archive, repository = self.open_archive('archive1')
         archive, repository = self.open_archive('archive1')