diff options
Diffstat (limited to 'modules/zfssnap')
| -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') |
