summaryrefslogtreecommitdiff
path: root/modules/zfssnap/zfssnap.py
diff options
context:
space:
mode:
Diffstat (limited to 'modules/zfssnap/zfssnap.py')
-rw-r--r--modules/zfssnap/zfssnap.py21
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
17import shlex 17import shlex
18 18
19from collections import defaultdict, OrderedDict, deque 19from collections import defaultdict, OrderedDict, deque, namedtuple
20 20
21import configparser 21import configparser
22from xdg import BaseDirectory 22from xdg import BaseDirectory
@@ -56,6 +56,7 @@ def _get_items():
56def prune(config, dry_run, keep_newest): 56def 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')