Browse Source

Adds arguments to filter archives

These are: --sort-by, --first and --last

Includes a method to obtain a list of archive infos filtered by these

Archives.list:
- ensure reverse is always applied
- always return a list
Frank Sachsenheim 8 years ago
parent
commit
f6b9276de9
2 changed files with 56 additions and 4 deletions
  1. 39 1
      src/borg/archiver.py
  2. 17 3
      src/borg/helpers.py

+ 39 - 1
src/borg/archiver.py

@@ -16,6 +16,7 @@ import traceback
 from binascii import unhexlify
 from datetime import datetime
 from itertools import zip_longest
+from operator import attrgetter
 
 from .logger import create_logger, setup_logging
 logger = create_logger()
@@ -28,7 +29,8 @@ from .cache import Cache
 from .constants import *  # NOQA
 from .helpers import EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR
 from .helpers import Error, NoManifestError
-from .helpers import location_validator, archivename_validator, ChunkerParams, CompressionSpec, PrefixSpec
+from .helpers import location_validator, archivename_validator, ChunkerParams, CompressionSpec
+from .helpers import PrefixSpec, sort_by_spec, HUMAN_SORT_KEYS
 from .helpers import BaseFormatter, ItemFormatter, ArchiveFormatter, format_time, format_file_size, format_archive
 from .helpers import safe_encode, remove_surrogates, bin_to_hex
 from .helpers import prune_within, prune_split
@@ -2549,6 +2551,23 @@ class Archiver:
 
         return parser
 
+    @staticmethod
+    def add_archives_filters_args(subparser):
+        filters_group = subparser.add_argument_group('filters', 'Archive filters can be applied to repository targets.')
+        filters_group.add_argument('-P', '--prefix', dest='prefix', type=prefix_spec, default='',
+                                   help='only consider archive names starting with this prefix')
+
+        sort_by_default = 'timestamp'
+        filters_group.add_argument('--sort-by', dest='sort_by', type=sort_by_spec, default=sort_by_default,
+                               help='Comma-separated list of sorting keys; valid keys are: {}; default is: {}'
+                               .format(', '.join(HUMAN_SORT_KEYS), sort_by_default))
+
+        group = filters_group.add_mutually_exclusive_group()
+        group.add_argument('--first', dest='first', metavar='N', default=0, type=int,
+                           help='select first N archives')
+        group.add_argument('--last', dest='last', metavar='N', default=0, type=int,
+                           help='delete last N archives')
+
     def get_args(self, argv, cmd):
         """usually, just returns argv, except if we deal with a ssh forced command for borg serve."""
         result = self.parse_args(argv[1:])
@@ -2611,6 +2630,25 @@ class Archiver:
             logger.warning("Using a pure-python msgpack! This will result in lower performance.")
         return args.func(args)
 
+    def _get_filtered_archives(self, args, manifest):
+        if args.location.archive:
+            raise Error('The options --first, --last and --prefix can only be used on repository targets.')
+
+        archives = manifest.archives.list()
+        if not archives:
+            logger.critical('There are no archives.')
+            self.exit_code = self.exit_code or EXIT_WARNING
+            return []
+
+        for sortkey in reversed(args.sort_by.split(',')):
+            archives.sort(key=attrgetter(sortkey))
+        if args.last:
+            archives.reverse()
+
+        n = args.first or args.last
+
+        return archives[:n]
+
 
 def sig_info_handler(sig_no, stack):  # pragma: no cover
     """search the stack for infos about the currently processed file and print them"""

+ 17 - 3
src/borg/helpers.py

@@ -143,10 +143,14 @@ class Archives(abc.MutableMapping):
         del self._archives[name]
 
     def list(self, sort_by=None, reverse=False):
-        # inexpensive Archive.list_archives replacement if we just need .name, .id, .ts
-        archives = self.values()  # [self[name] for name in self]
+        """ Inexpensive Archive.list_archives replacement if we just need .name, .id, .ts
+            Returns list of borg.helpers.ArchiveInfo instances
+        """
+        archives = list(self.values())  # [self[name] for name in self]
         if sort_by is not None:
-            archives = sorted(archives, key=attrgetter(sort_by), reverse=reverse)
+            archives = sorted(archives, key=attrgetter(sort_by))
+        if reverse:
+            archives.reverse()
         return archives
 
     def set_raw_dict(self, d):
@@ -655,6 +659,16 @@ def replace_placeholders(text):
     return format_line(text, data)
 
 
+HUMAN_SORT_KEYS = ['timestamp'] + list(ArchiveInfo._fields)
+HUMAN_SORT_KEYS.remove('ts')
+
+def sort_by_spec(text):
+    for token in text.split(','):
+        if token not in HUMAN_SORT_KEYS:
+            raise ValueError('Invalid sort key: %s' % token)
+    return text.replace('timestamp', 'ts')
+
+
 def safe_timestamp(item_timestamp_ns):
     try:
         return datetime.fromtimestamp(bigint_to_int(item_timestamp_ns) / 1e9)