diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2022-02-19 17:37:29 +0100 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2022-02-19 17:37:29 +0100 |
commit | 9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a (patch) | |
tree | 53871a18600eed73179b18ffc462c449a0cc1c77 /modules/zfssnap/zfssnap.py | |
parent | e20069b8d44eba99127e3af714dc99dbe76d8146 (diff) | |
download | nixos-9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a.tar nixos-9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a.tar.gz nixos-9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a.tar.bz2 nixos-9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a.tar.xz nixos-9a77d1dfd0cf1f9fa0ee401598bae2b36de1196a.zip |
zfssnap: keep-oldest
Diffstat (limited to 'modules/zfssnap/zfssnap.py')
-rw-r--r-- | modules/zfssnap/zfssnap.py | 37 |
1 files changed, 22 insertions, 15 deletions
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(): | |||
53 | 53 | ||
54 | return items | 54 | return items |
55 | 55 | ||
56 | def prune(config, dry_run): | 56 | def prune(config, dry_run, keep_newest): |
57 | |||
58 | items = defaultdict(list) | 57 | items = defaultdict(list) |
59 | 58 | ||
60 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] | 59 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] |
@@ -116,21 +115,25 @@ def prune(config, dry_run): | |||
116 | desired_count = config.getint('KEEP', rule, fallback=0) | 115 | desired_count = config.getint('KEEP', rule, fallback=0) |
117 | 116 | ||
118 | for base, snaps in items.items(): | 117 | for base, snaps in items.items(): |
119 | last_period = None | 118 | periods = OrderedDict() |
120 | to_keep = desired_count | ||
121 | |||
122 | if to_keep == 0: | ||
123 | continue | ||
124 | 119 | ||
125 | for snap in sorted(snaps, key=lambda snap: snap['creation'], reverse=True): | 120 | for snap in sorted(snaps, key=lambda snap: snap['creation'], reverse=keep_newest): |
121 | period = pattern(snap['creation']) | ||
122 | if period not in periods: | ||
123 | periods[period] = deque() | ||
124 | periods[period].append(snap) | ||
125 | |||
126 | to_keep = desired_count | ||
127 | ordered_periods = reversed(periods.items()) if keep_newest else periods.items() | ||
128 | for period, period_snaps in ordered_periods: | ||
126 | if to_keep == 0: | 129 | if to_keep == 0: |
127 | break | 130 | break |
128 | 131 | ||
129 | period = pattern(snap['creation']) | 132 | for snap in period_snaps: |
130 | if period != last_period: | 133 | if snap['name'] not in keep: |
131 | last_period = period | 134 | keep_because(base, snap['name'], rule, period=period) |
132 | keep_because(base, snap['name'], rule, period=period) | 135 | to_keep -= 1 |
133 | to_keep -= 1 | 136 | break |
134 | 137 | ||
135 | if to_keep > 0: | 138 | if to_keep > 0: |
136 | logger.debug(f'Missing {to_keep} to fulfill {rule}={desired_count} for ‘{base}’') | 139 | logger.debug(f'Missing {to_keep} to fulfill {rule}={desired_count} for ‘{base}’') |
@@ -147,7 +150,10 @@ def prune(config, dry_run): | |||
147 | args += [snap] | 150 | args += [snap] |
148 | _log_cmd(*args) | 151 | _log_cmd(*args) |
149 | subprocess.run(args, check=True) | 152 | subprocess.run(args, check=True) |
150 | logger.info(f'Pruned ‘{snap}’') | 153 | if dry_run: |
154 | logger.info(f'Would have pruned ‘{snap}’') | ||
155 | else: | ||
156 | logger.info(f'Pruned ‘{snap}’') | ||
151 | 157 | ||
152 | def rename(snapshots, destroy=False): | 158 | def rename(snapshots, destroy=False): |
153 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', 'creation', *snapshots] | 159 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', 'creation', *snapshots] |
@@ -269,6 +275,7 @@ def main(): | |||
269 | prune_parser = subparsers.add_parser('prune') | 275 | prune_parser = subparsers.add_parser('prune') |
270 | prune_parser.add_argument('--config', '-c', dest='config_files', nargs='*', default=list()) | 276 | prune_parser.add_argument('--config', '-c', dest='config_files', nargs='*', default=list()) |
271 | prune_parser.add_argument('--dry-run', '-n', action='store_true', default=False) | 277 | prune_parser.add_argument('--dry-run', '-n', action='store_true', default=False) |
278 | prune_parser.add_argument('--keep-newest', action='store_true', default=False) | ||
272 | prune_parser.set_defaults(cmd=prune) | 279 | prune_parser.set_defaults(cmd=prune) |
273 | args = parser.parse_args() | 280 | args = parser.parse_args() |
274 | 281 | ||
@@ -280,7 +287,7 @@ def main(): | |||
280 | logger.setLevel(logging.DEBUG) | 287 | logger.setLevel(logging.DEBUG) |
281 | 288 | ||
282 | cmdArgs = {} | 289 | cmdArgs = {} |
283 | for copy in {'snapshots', 'dry_run', 'destroy'}: | 290 | for copy in {'snapshots', 'dry_run', 'destroy', 'keep_newest'}: |
284 | if copy in vars(args): | 291 | if copy in vars(args): |
285 | cmdArgs[copy] = vars(args)[copy] | 292 | cmdArgs[copy] = vars(args)[copy] |
286 | if 'config_files' in vars(args): | 293 | if 'config_files' in vars(args): |