Merge pull request from tuffnatty/pre-post-snapshot-cmd

Add support for pre- and post-snapshot scripts ()
This commit is contained in:
DatuX 2021-06-17 11:48:09 +02:00 committed by GitHub
commit 1286bfafd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 9 deletions

@ -279,6 +279,19 @@ root@ws1:~# zfs-autobackup test --verbose
This also allows you to make several snapshots during the day, but only backup the data at night when the server is not busy.
## Running custom commands before and after snapshotting
If you need, e.g. to quiesce a couple of mysql databases to make on-disk data consistent before snapshotting, try:
```sh
zfs-autobackup \
--pre-snapshot-cmd 'daemon -f jexec mysqljail1 mysql -s -e "set autocommit=0;flush logs;flush tables with read lock;\\! echo \$\$ > /tmp/mysql_lock.pid && sleep 60"' \
--pre-snapshot-cmd 'daemon -f jexec mysqljail2 mysql -s -e "set autocommit=0;flush logs;flush tables with read lock;\\! echo \$\$ > /tmp/mysql_lock.pid && sleep 60"' \
--post-snapshot-cmd 'pkill -F /jails/mysqljail1/tmp/mysql_lock.pid' \
--post-snapshot-cmd 'pkill -F /jails/mysqljail2/tmp/mysql_lock.pid' \
test
```
## Thinning out obsolete snapshots
The thinner is the thing that destroys old snapshots on the source and target.
@ -493,7 +506,8 @@ Look in man ssh_config for many more options.
```console
usage: zfs-autobackup [-h] [--ssh-config CONFIG-FILE] [--ssh-source USER@HOST]
[--ssh-target USER@HOST] [--keep-source SCHEDULE]
[--keep-target SCHEDULE] [--other-snapshots]
[--keep-target SCHEDULE] [--pre-snapshot-cmd COMMAND]
[--post-snapshot-cmd COMMAND] [--other-snapshots]
[--no-snapshot] [--no-send] [--no-thinning] [--no-holds]
[--min-change BYTES] [--allow-empty]
[--ignore-replicated] [--strip-path N]
@ -531,6 +545,12 @@ optional arguments:
--keep-target SCHEDULE
Thinning schedule for old target snapshots. Default:
10,1d1w,1w1m,1m1y
--pre-snapshot-cmd COMMAND
Run COMMAND before snapshotting (can be used multiple
times.
--post-snapshot-cmd COMMAND
Run COMMAND after snapshotting (can be used multiple
times.
--other-snapshots Send over other snapshots as well, not just the ones
created by this tool.
--no-snapshot Don't create new snapshots (useful for finishing

@ -45,6 +45,10 @@ class ZfsAutobackup:
help='Target ZFS filesystem (optional: if not specified, zfs-autobackup will only operate '
'as snapshot-tool on source)')
parser.add_argument('--pre-snapshot-cmd', metavar="COMMAND", default=[], action='append',
help='Run COMMAND before snapshotting (can be used multiple times.')
parser.add_argument('--post-snapshot-cmd', metavar="COMMAND", default=[], action='append',
help='Run COMMAND after snapshotting (can be used multiple times.')
parser.add_argument('--other-snapshots', action='store_true',
help='Send over other snapshots as well, not just the ones created by this tool.')
parser.add_argument('--no-snapshot', action='store_true',
@ -506,7 +510,9 @@ class ZfsAutobackup:
if not self.args.no_snapshot:
self.set_title("Snapshotting")
source_node.consistent_snapshot(source_datasets, source_node.new_snapshotname(),
min_changed_bytes=self.args.min_change)
min_changed_bytes=self.args.min_change,
pre_snapshot_cmd=self.args.pre_snapshot_cmd,
post_snapshot_cmd=self.args.post_snapshot_cmd)
################# sync
# if target is specified, we sync the datasets, otherwise we just thin the source. (e.g. snapshot mode)

@ -1,6 +1,7 @@
# python 2 compatibility
from __future__ import print_function
import re
import shlex
import subprocess
import sys
import time
@ -161,7 +162,7 @@ class ZfsNode(ExecuteNode):
"""determine uniq new snapshotname"""
return self.backup_name + "-" + time.strftime("%Y%m%d%H%M%S")
def consistent_snapshot(self, datasets, snapshot_name, min_changed_bytes):
def consistent_snapshot(self, datasets, snapshot_name, min_changed_bytes, pre_snapshot_cmd=[], post_snapshot_cmd=[]):
"""create a consistent (atomic) snapshot of specified datasets, per pool.
"""
@ -191,14 +192,24 @@ class ZfsNode(ExecuteNode):
self.verbose("No changes anywhere: not creating snapshots.")
return
# create consistent snapshot per pool
for (pool_name, snapshots) in pools.items():
cmd = ["zfs", "snapshot"]
try:
for cmd in pre_snapshot_cmd:
self.verbose("Running pre-snapshot-cmd")
self.run(cmd=shlex.split(cmd), readonly=False)
cmd.extend(map(lambda snapshot_: str(snapshot_), snapshots))
# create consistent snapshot per pool
for (pool_name, snapshots) in pools.items():
cmd = ["zfs", "snapshot"]
self.verbose("Creating snapshots {} in pool {}".format(snapshot_name, pool_name))
self.run(cmd, readonly=False)
cmd.extend(map(lambda snapshot_: str(snapshot_), snapshots))
self.verbose("Creating snapshots {} in pool {}".format(snapshot_name, pool_name))
self.run(cmd, readonly=False)
finally:
for cmd in post_snapshot_cmd:
self.verbose("Running post-snapshot-cmd")
self.run(cmd=shlex.split(cmd), readonly=False, valid_exitcodes=[])
def selected_datasets(self, exclude_received, exclude_paths):
"""determine filesystems that should be backed up by looking at the special autobackup-property, systemwide