From 9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Sat, 19 Feb 2022 17:37:29 +0100 Subject: zfssnap: keep-oldest --- modules/zfssnap/default.nix | 2 +- modules/zfssnap/zfssnap.py | 37 ++++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) (limited to 'modules') diff --git a/modules/zfssnap/default.nix b/modules/zfssnap/default.nix index 86e7fdc0..27e5198d 100644 --- a/modules/zfssnap/default.nix +++ b/modules/zfssnap/default.nix @@ -77,7 +77,7 @@ in { ExecStart = let mkSectionName = name: strings.escape [ "[" "]" ] (strings.toUpper name); zfssnapConfig = generators.toINI { inherit mkSectionName; } cfg.config; - in "${zfssnap}/bin/zfssnap -v prune --config=${pkgs.writeText "zfssnap.ini" zfssnapConfig}"; + in "${zfssnap}/bin/zfssnap -v prune --dry-run --config=${pkgs.writeText "zfssnap.ini" zfssnapConfig}"; }; }; diff --git a/modules/zfssnap/zfssnap.py b/modules/zfssnap/zfssnap.py index 1c3e1f9a..cf6ab28c 100644 --- a/modules/zfssnap/zfssnap.py +++ b/modules/zfssnap/zfssnap.py @@ -53,8 +53,7 @@ def _get_items(): return items -def prune(config, dry_run): - +def prune(config, dry_run, keep_newest): items = defaultdict(list) args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] @@ -116,21 +115,25 @@ def prune(config, dry_run): desired_count = config.getint('KEEP', rule, fallback=0) for base, snaps in items.items(): - last_period = None - to_keep = desired_count - - if to_keep == 0: - continue + periods = OrderedDict() - for snap in sorted(snaps, key=lambda snap: snap['creation'], reverse=True): + for snap in sorted(snaps, key=lambda snap: snap['creation'], reverse=keep_newest): + period = pattern(snap['creation']) + if period not in periods: + periods[period] = deque() + periods[period].append(snap) + + to_keep = desired_count + ordered_periods = reversed(periods.items()) if keep_newest else periods.items() + for period, period_snaps in ordered_periods: if to_keep == 0: break - period = pattern(snap['creation']) - if period != last_period: - last_period = period - keep_because(base, snap['name'], rule, period=period) - to_keep -= 1 + for snap in period_snaps: + if snap['name'] not in keep: + keep_because(base, snap['name'], rule, period=period) + to_keep -= 1 + break if to_keep > 0: logger.debug(f'Missing {to_keep} to fulfill {rule}={desired_count} for ‘{base}’') @@ -147,7 +150,10 @@ def prune(config, dry_run): args += [snap] _log_cmd(*args) subprocess.run(args, check=True) - logger.info(f'Pruned ‘{snap}’') + if dry_run: + logger.info(f'Would have pruned ‘{snap}’') + else: + logger.info(f'Pruned ‘{snap}’') def rename(snapshots, destroy=False): args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', 'creation', *snapshots] @@ -269,6 +275,7 @@ def main(): prune_parser = subparsers.add_parser('prune') prune_parser.add_argument('--config', '-c', dest='config_files', nargs='*', default=list()) prune_parser.add_argument('--dry-run', '-n', action='store_true', default=False) + prune_parser.add_argument('--keep-newest', action='store_true', default=False) prune_parser.set_defaults(cmd=prune) args = parser.parse_args() @@ -280,7 +287,7 @@ def main(): logger.setLevel(logging.DEBUG) cmdArgs = {} - for copy in {'snapshots', 'dry_run', 'destroy'}: + for copy in {'snapshots', 'dry_run', 'destroy', 'keep_newest'}: if copy in vars(args): cmdArgs[copy] = vars(args)[copy] if 'config_files' in vars(args): -- cgit v1.2.3