diff options
-rw-r--r-- | modules/zfssnap/zfssnap.py | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/modules/zfssnap/zfssnap.py b/modules/zfssnap/zfssnap.py index b72174fc..e77f11f3 100644 --- a/modules/zfssnap/zfssnap.py +++ b/modules/zfssnap/zfssnap.py | |||
@@ -32,7 +32,7 @@ def _now(): | |||
32 | 32 | ||
33 | def _snap_name(item, time=_now()): | 33 | def _snap_name(item, time=_now()): |
34 | suffix = re.sub(r'\+00:00$', r'Z', time.isoformat(timespec='seconds')) | 34 | suffix = re.sub(r'\+00:00$', r'Z', time.isoformat(timespec='seconds')) |
35 | return f'{item}@auto_{suffix}' | 35 | return f'{item}@{suffix}' |
36 | 36 | ||
37 | def _log_cmd(*args): | 37 | def _log_cmd(*args): |
38 | fmt_args = ' '.join(map(shlex.quote, args)) | 38 | fmt_args = ' '.join(map(shlex.quote, args)) |
@@ -53,8 +53,18 @@ def _get_items(): | |||
53 | return items | 53 | return items |
54 | 54 | ||
55 | def prune(config, dry_run, keep_newest): | 55 | def prune(config, dry_run, keep_newest): |
56 | items = defaultdict(list) | 56 | prunable_snapshots = set() |
57 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', '-s', 'local' 'li.yggdrasil:is-auto-snapshot'] | ||
58 | _log_cmd(*args) | ||
59 | with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: | ||
60 | text_stdout = io.TextIOWrapper(proc.stdout) | ||
61 | reader = csv.reader(text_stdout, delimiter='\t', quoting=csv.QUOTE_NONE) | ||
62 | Row = namedtuple('Row', ['name', 'is_auto_snapshot']) | ||
63 | for row in map(Row._make, reader): | ||
64 | if bool(strtobool(row.is_auto_snapshot)): | ||
65 | prunable_snapshots.add(row.name) | ||
57 | 66 | ||
67 | items = defaultdict(list) | ||
58 | Snap = namedtuple('Snap', ['name', 'creation']) | 68 | Snap = namedtuple('Snap', ['name', 'creation']) |
59 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] | 69 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', '-t', 'snapshot', 'creation'] |
60 | _log_cmd(*args) | 70 | _log_cmd(*args) |
@@ -63,12 +73,10 @@ def prune(config, dry_run, keep_newest): | |||
63 | reader = csv.reader(text_stdout, delimiter='\t', quoting=csv.QUOTE_NONE) | 73 | reader = csv.reader(text_stdout, delimiter='\t', quoting=csv.QUOTE_NONE) |
64 | Row = namedtuple('Row', ['name', 'timestamp']) | 74 | Row = namedtuple('Row', ['name', 'timestamp']) |
65 | for row in map(Row._make, reader): | 75 | for row in map(Row._make, reader): |
66 | creation = datetime.fromtimestamp(int(row.timestamp), timezone.utc) | 76 | if row.name is not in prunable_snapshots: |
67 | base_name, _, _ = row.name.rpartition('@') | ||
68 | expected_name = _snap_name(base_name, time=creation) | ||
69 | if expected_name != row.name: | ||
70 | # logger.debug(f'Skipping ‘{row.name}’ since it does not conform to naming scheme') | ||
71 | continue | 77 | continue |
78 | |||
79 | creation = datetime.fromtimestamp(int(row.timestamp), timezone.utc) | ||
72 | items[base_name].append(Snap(name=row.name, creation=creation)) | 80 | items[base_name].append(Snap(name=row.name, creation=creation)) |
73 | 81 | ||
74 | kept_count = defaultdict(lambda: defaultdict(lambda: 0)) | 82 | kept_count = defaultdict(lambda: defaultdict(lambda: 0)) |