diff options
Diffstat (limited to 'modules/zfssnap')
-rw-r--r-- | modules/zfssnap/zfssnap.py | 32 |
1 files changed, 23 insertions, 9 deletions
diff --git a/modules/zfssnap/zfssnap.py b/modules/zfssnap/zfssnap.py index 7daa2935..57b0e95f 100644 --- a/modules/zfssnap/zfssnap.py +++ b/modules/zfssnap/zfssnap.py | |||
@@ -126,13 +126,13 @@ def prune(config, dry_run): | |||
126 | logger.debug(f'Missing {to_keep} to fulfill {rule}={desired_count} for ‘{base}’') | 126 | logger.debug(f'Missing {to_keep} to fulfill {rule}={desired_count} for ‘{base}’') |
127 | 127 | ||
128 | all_snaps = {snap['name'] for _, snaps in items.items() for snap in snaps} | 128 | all_snaps = {snap['name'] for _, snaps in items.items() for snap in snaps} |
129 | to_delete = all_snaps - keep | 129 | to_destroy = all_snaps - keep |
130 | if to_delete: | 130 | if to_destroy: |
131 | logger.info(f'Will prune: %s', ', '.join(map(lambda snap: f'‘{snap}’', to_delete))) | 131 | logger.info(f'Will prune: %s', ', '.join(map(lambda snap: f'‘{snap}’', to_destroy))) |
132 | else: | 132 | else: |
133 | logger.info('Nothing to prune') | 133 | logger.info('Nothing to prune') |
134 | 134 | ||
135 | for snap in to_delete: | 135 | for snap in to_destroy: |
136 | args = ['zfs', 'destroy'] | 136 | args = ['zfs', 'destroy'] |
137 | if dry_run: | 137 | if dry_run: |
138 | args += ['-n'] | 138 | args += ['-n'] |
@@ -140,9 +140,10 @@ def prune(config, dry_run): | |||
140 | _log_cmd(*args) | 140 | _log_cmd(*args) |
141 | subprocess.run(args, check=True) | 141 | subprocess.run(args, check=True) |
142 | 142 | ||
143 | def rename(snapshots, check=False): | 143 | def rename(snapshots, destroy=False): |
144 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', 'creation', *snapshots] | 144 | args = ['zfs', 'get', '-H', '-p', '-o', 'name,value', 'creation', *snapshots] |
145 | _log_cmd(*args) | 145 | _log_cmd(*args) |
146 | renamed_to = set() | ||
146 | with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: | 147 | with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: |
147 | text_stdout = io.TextIOWrapper(proc.stdout) | 148 | text_stdout = io.TextIOWrapper(proc.stdout) |
148 | reader = csv.reader(text_stdout, delimiter='\t', quoting=csv.QUOTE_NONE) | 149 | reader = csv.reader(text_stdout, delimiter='\t', quoting=csv.QUOTE_NONE) |
@@ -155,11 +156,23 @@ def rename(snapshots, check=False): | |||
155 | if new_name == name: | 156 | if new_name == name: |
156 | logger.debug(f'Not renaming ‘{name}’ since name is already correct') | 157 | logger.debug(f'Not renaming ‘{name}’ since name is already correct') |
157 | continue | 158 | continue |
158 | logger.info(f'Renaming ‘{name}’ to ‘{new_name}’') | ||
159 | 159 | ||
160 | if new_name in renamed_to: | ||
161 | if destroy: | ||
162 | logger.warning(f'Destroying ‘{name}’ since ‘{new_name}’ was already renamed to') | ||
163 | args = ['zfs', 'destroy', name] | ||
164 | _log_cmd(*args) | ||
165 | subprocess.run(args, check=True) | ||
166 | else: | ||
167 | logger.info(f'Skipping ‘{name}’ since ‘{new_name}’ was already renamed to') | ||
168 | |||
169 | continue | ||
170 | |||
171 | logger.info(f'Renaming ‘{name}’ to ‘{new_name}’') | ||
160 | args = ['zfs', 'rename', name, new_name] | 172 | args = ['zfs', 'rename', name, new_name] |
161 | _log_cmd(*args) | 173 | _log_cmd(*args) |
162 | subprocess.run(args, check=check) | 174 | subprocess.run(args, check=True) |
175 | renamed_to.add(new_name) | ||
163 | 176 | ||
164 | def autosnap(): | 177 | def autosnap(): |
165 | items = _get_items() | 178 | items = _get_items() |
@@ -207,7 +220,7 @@ def autosnap(): | |||
207 | 220 | ||
208 | _log_cmd(*args) | 221 | _log_cmd(*args) |
209 | subprocess.run(args, check=True) | 222 | subprocess.run(args, check=True) |
210 | rename(snapshots=all_snap_names, check=True) | 223 | rename(snapshots=all_snap_names) |
211 | 224 | ||
212 | do_snapshot(*single) | 225 | do_snapshot(*single) |
213 | do_snapshot(*recursive, recursive=True) | 226 | do_snapshot(*recursive, recursive=True) |
@@ -236,6 +249,7 @@ def main(): | |||
236 | parser.set_defaults(cmd=autosnap) | 249 | parser.set_defaults(cmd=autosnap) |
237 | rename_parser = subparsers.add_parser('rename') | 250 | rename_parser = subparsers.add_parser('rename') |
238 | rename_parser.add_argument('snapshots', nargs='+') | 251 | rename_parser.add_argument('snapshots', nargs='+') |
252 | rename_parser.add_argument('--destroy', action='store_true', default=False) | ||
239 | rename_parser.set_defaults(cmd=rename) | 253 | rename_parser.set_defaults(cmd=rename) |
240 | prune_parser = subparsers.add_parser('prune') | 254 | prune_parser = subparsers.add_parser('prune') |
241 | prune_parser.add_argument('--config', '-c', dest='config_files', nargs='*', default=list()) | 255 | prune_parser.add_argument('--config', '-c', dest='config_files', nargs='*', default=list()) |
@@ -251,7 +265,7 @@ def main(): | |||
251 | logger.setLevel(logging.DEBUG) | 265 | logger.setLevel(logging.DEBUG) |
252 | 266 | ||
253 | cmdArgs = {} | 267 | cmdArgs = {} |
254 | for copy in {'snapshots', 'dry_run'}: | 268 | for copy in {'snapshots', 'dry_run', 'destroy'}: |
255 | if copy in vars(args): | 269 | if copy in vars(args): |
256 | cmdArgs[copy] = vars(args)[copy] | 270 | cmdArgs[copy] = vars(args)[copy] |
257 | if 'config_files' in vars(args): | 271 | if 'config_files' in vars(args): |