diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2022-02-19 18:26:34 +0100 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2022-02-19 18:26:34 +0100 |
commit | ea31ce7efbebfab8323a625f6ab72688ff2b0315 (patch) | |
tree | f49e7b94b2e407c934a2bbbaf19c96520040fbb0 /modules/zfssnap/zfssnap.py | |
parent | 286601005ceff1b61b7a7493da40ec209a779542 (diff) | |
download | nixos-ea31ce7efbebfab8323a625f6ab72688ff2b0315.tar nixos-ea31ce7efbebfab8323a625f6ab72688ff2b0315.tar.gz nixos-ea31ce7efbebfab8323a625f6ab72688ff2b0315.tar.bz2 nixos-ea31ce7efbebfab8323a625f6ab72688ff2b0315.tar.xz nixos-ea31ce7efbebfab8323a625f6ab72688ff2b0315.zip |
zfssnap: ...
Diffstat (limited to 'modules/zfssnap/zfssnap.py')
-rw-r--r-- | modules/zfssnap/zfssnap.py | 21 |
1 files changed, 11 insertions, 10 deletions
diff --git a/modules/zfssnap/zfssnap.py b/modules/zfssnap/zfssnap.py index 83d30a6b..4e7188f9 100644 --- a/modules/zfssnap/zfssnap.py +++ b/modules/zfssnap/zfssnap.py | |||
@@ -16,7 +16,7 @@ import logging | |||
16 | 16 | ||
17 | import shlex | 17 | import shlex |
18 | 18 | ||
19 | from collections import defaultdict, OrderedDict, deque | 19 | from collections import defaultdict, OrderedDict, deque, namedtuple |
20 | 20 | ||
21 | import configparser | 21 | import configparser |
22 | from xdg import BaseDirectory | 22 | from xdg import BaseDirectory |
@@ -56,6 +56,7 @@ def _get_items(): | |||
56 | def prune(config, dry_run, keep_newest): | 56 | def prune(config, dry_run, keep_newest): |
57 | items = defaultdict(list) | 57 | items = defaultdict(list) |
58 | 58 | ||
59 | Snap = namedtuple(Snap, ['name', 'creation']) | ||
59 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] | 60 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] |
60 | _log_cmd(*args) | 61 | _log_cmd(*args) |
61 | with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: | 62 | with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: |
@@ -70,7 +71,7 @@ def prune(config, dry_run, keep_newest): | |||
70 | if expected_name != name: | 71 | if expected_name != name: |
71 | # logger.debug(f'Skipping ‘{name}’ since it does not conform to naming scheme') | 72 | # logger.debug(f'Skipping ‘{name}’ since it does not conform to naming scheme') |
72 | continue | 73 | continue |
73 | items[base_name].append({'name': name, 'creation': creation}) | 74 | items[base_name].append(Snap(name=name, creation=creation)) |
74 | 75 | ||
75 | keep = set() | 76 | keep = set() |
76 | kept_count = defaultdict(lambda: defaultdict(lambda: 0)) | 77 | kept_count = defaultdict(lambda: defaultdict(lambda: 0)) |
@@ -84,17 +85,17 @@ def prune(config, dry_run, keep_newest): | |||
84 | within = config.gettimedelta('KEEP', 'within') | 85 | within = config.gettimedelta('KEEP', 'within') |
85 | if within > timedelta(seconds=0): | 86 | if within > timedelta(seconds=0): |
86 | for base, snaps in items.items(): | 87 | for base, snaps in items.items(): |
87 | time_ref = max(snaps, key=lambda snap: snap['creation'], default=None) | 88 | time_ref = max(snaps, key=lambda snap: snap.creation, default=None) |
88 | if not time_ref: | 89 | if not time_ref: |
89 | logger.warn(f'Nothing to keep for ‘{base}’') | 90 | logger.warn(f'Nothing to keep for ‘{base}’') |
90 | continue | 91 | continue |
91 | 92 | ||
92 | logger.info(f'Using ‘{time_ref["name"]}’ as time reference for ‘{base}’') | 93 | logger.info(f'Using ‘{time_ref["name"]}’ as time reference for ‘{base}’') |
93 | within_cutoff = time_ref['creation'] - within | 94 | within_cutoff = time_ref.creation - within |
94 | 95 | ||
95 | for snap in snaps: | 96 | for snap in snaps: |
96 | if snap['creation'] >= within_cutoff: | 97 | if snap.creation >= within_cutoff: |
97 | keep_because(base, snap['name'], 'within') | 98 | keep_because(base, snap.name, 'within') |
98 | else: | 99 | else: |
99 | logger.warn('Skipping rule ‘within’ since retention period is zero') | 100 | logger.warn('Skipping rule ‘within’ since retention period is zero') |
100 | 101 | ||
@@ -117,8 +118,8 @@ def prune(config, dry_run, keep_newest): | |||
117 | for base, snaps in items.items(): | 118 | for base, snaps in items.items(): |
118 | periods = OrderedDict() | 119 | periods = OrderedDict() |
119 | 120 | ||
120 | for snap in sorted(snaps, key=lambda snap: snap['creation'], reverse=keep_newest): | 121 | for snap in sorted(snaps, key=lambda snap: snap.creation, reverse=keep_newest): |
121 | period = pattern(snap['creation']) | 122 | period = pattern(snap.creation) |
122 | if period not in periods: | 123 | if period not in periods: |
123 | periods[period] = deque() | 124 | periods[period] = deque() |
124 | periods[period].append(snap) | 125 | periods[period].append(snap) |
@@ -130,14 +131,14 @@ def prune(config, dry_run, keep_newest): | |||
130 | break | 131 | break |
131 | 132 | ||
132 | for snap in period_snaps: | 133 | for snap in period_snaps: |
133 | keep_because(base, snap['name'], rule, period=period) | 134 | keep_because(base, snap.name, rule, period=period) |
134 | to_keep -= 1 | 135 | to_keep -= 1 |
135 | break | 136 | break |
136 | 137 | ||
137 | if to_keep > 0: | 138 | if to_keep > 0: |
138 | 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}’') |
139 | 140 | ||
140 | all_snaps = {snap['name'] for _, snaps in items.items() for snap in snaps} | 141 | all_snaps = {snap.name for _, snaps in items.items() for snap in snaps} |
141 | to_destroy = all_snaps - keep | 142 | to_destroy = all_snaps - keep |
142 | if not to_destroy: | 143 | if not to_destroy: |
143 | logger.info('Nothing to prune') | 144 | logger.info('Nothing to prune') |