added support to create remote snapshots
This commit is contained in:
parent
8adf8f14f8
commit
eee2f12004
65
bin/zsnapper
65
bin/zsnapper
@ -75,13 +75,14 @@ def str_to_timedelta(deltastr):
|
|||||||
delta += datetime.timedelta(minutes=int(match.group(1)))
|
delta += datetime.timedelta(minutes=int(match.group(1)))
|
||||||
return delta
|
return delta
|
||||||
|
|
||||||
def get_config_for_fs(fs, config):
|
def get_config_for_fs(fs, config, remote=''):
|
||||||
fs_config = DEFAULT_CONFIG
|
fs_config = DEFAULT_CONFIG.copy()
|
||||||
fs_build = ''
|
fs_build = ''
|
||||||
for fs_part in fs.split('/'):
|
for fs_part in fs.split('/'):
|
||||||
fs_build += fs_part
|
fs_build += fs_part
|
||||||
if fs_build in config:
|
section = "{}@{}".format(fs_build, remote)
|
||||||
fs_config.update(config[fs_build])
|
if section in config:
|
||||||
|
fs_config.update(config[section])
|
||||||
if fs_build == fs:
|
if fs_build == fs:
|
||||||
break
|
break
|
||||||
fs_build += '/'
|
fs_build += '/'
|
||||||
@ -89,13 +90,15 @@ def get_config_for_fs(fs, config):
|
|||||||
return fs_config
|
return fs_config
|
||||||
|
|
||||||
|
|
||||||
def do_snapshots(fslist, snapshots, config, sudo):
|
def do_snapshots(fslist, snapshots, config, sudo, remote=None, zfs_cmd=None):
|
||||||
failed_snapshots = set()
|
failed_snapshots = set()
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
log = logging.getLogger(LOGGER)
|
log = logging.getLogger(LOGGER)
|
||||||
|
if not remote:
|
||||||
|
remote = ''
|
||||||
|
|
||||||
for fs in fslist:
|
for fs in fslist:
|
||||||
conf = get_config_for_fs(fs, config)
|
conf = get_config_for_fs(fs, config, remote=remote)
|
||||||
if not conf['snapshot_interval']:
|
if not conf['snapshot_interval']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -106,6 +109,10 @@ def do_snapshots(fslist, snapshots, config, sudo):
|
|||||||
last_snap = datetime.datetime.min
|
last_snap = datetime.datetime.min
|
||||||
if interval > datetime.timedelta() and last_snap+interval < now:
|
if interval > datetime.timedelta() and last_snap+interval < now:
|
||||||
try:
|
try:
|
||||||
|
if zfs_cmd:
|
||||||
|
zsnaplib.create_snapshot(fs, sudo, zfs_cmd=zfs_cmd)
|
||||||
|
log.info('{} snapshot created on {}'.format(fs, remote))
|
||||||
|
else:
|
||||||
zsnaplib.create_snapshot(fs, sudo)
|
zsnaplib.create_snapshot(fs, sudo)
|
||||||
log.info('{} snapshot created'.format(fs))
|
log.info('{} snapshot created'.format(fs))
|
||||||
except zsnaplib.ZFSSnapshotError as e:
|
except zsnaplib.ZFSSnapshotError as e:
|
||||||
@ -113,6 +120,17 @@ def do_snapshots(fslist, snapshots, config, sudo):
|
|||||||
failed_snapshots.add(fs)
|
failed_snapshots.add(fs)
|
||||||
return failed_snapshots
|
return failed_snapshots
|
||||||
|
|
||||||
|
def get_remote_hosts(config):
|
||||||
|
ret = {}
|
||||||
|
for section in config.sections():
|
||||||
|
if '@' in section and 'remote_zfs_cmd' in config[section]:
|
||||||
|
fs, remote = section.split('@', 1)
|
||||||
|
remote_zfs_cmd = Template(config[section]['remote_zfs_cmd']).safe_substitute(config[section])
|
||||||
|
remote_zfs_cmd = remote_zfs_cmd.split()
|
||||||
|
ret[remote] = remote_zfs_cmd
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def send_snapshots(fslist, snapshots, config, sudo):
|
def send_snapshots(fslist, snapshots, config, sudo):
|
||||||
failed_snapshots = set()
|
failed_snapshots = set()
|
||||||
remote_hosts = {}
|
remote_hosts = {}
|
||||||
@ -290,8 +308,43 @@ def main():
|
|||||||
log.error('lockfile {} exists but does not seem to contain a pid. Will not continue'.format(lockfile))
|
log.error('lockfile {} exists but does not seem to contain a pid. Will not continue'.format(lockfile))
|
||||||
return RET_CODES['FAILED']
|
return RET_CODES['FAILED']
|
||||||
|
|
||||||
|
# create any remote snapshots
|
||||||
|
remotes = get_remote_hosts(config)
|
||||||
|
remote_fs = {}
|
||||||
|
remote_snapshots = {}
|
||||||
|
failed_remote_snapshots = {}
|
||||||
|
for remote, zfs_cmd in remotes.items():
|
||||||
|
try:
|
||||||
|
remote_fs[remote] = sorted(zsnaplib.get_filesystems(zfs_cmd=zfs_cmd))
|
||||||
|
remote_snapshots[remote] = zsnaplib.get_snapshots(zfs_cmd=zfs_cmd)
|
||||||
|
failed_remote_snapshots[remote] = do_snapshots(
|
||||||
|
remote_fs[remote],
|
||||||
|
remote_snapshots[remote],
|
||||||
|
config,
|
||||||
|
False, # sudo should be configured in zfs_cmd already
|
||||||
|
remote=remote,
|
||||||
|
zfs_cmd=zfs_cmd)
|
||||||
|
except zsnaplib.ZFSSnapshotError:
|
||||||
|
if remote in remote_fs:
|
||||||
|
del remote_fs[remote]
|
||||||
|
if remote in remote_snapshots:
|
||||||
|
del remote_snapshots[remote]
|
||||||
|
log.warning("Failed to snapshot on {}".format(remote))
|
||||||
|
ret = RET_CODES['ERROR']
|
||||||
|
|
||||||
|
for remote, filesystems in failed_remote_snapshots.items():
|
||||||
|
for fs in filesystems:
|
||||||
|
log.warning("Failed to snapshot {} on {}".format(fs, remote))
|
||||||
|
|
||||||
# reload all snapshots so we get our new snapshots here
|
# reload all snapshots so we get our new snapshots here
|
||||||
|
for remote, zfs_cmd in remotes.items():
|
||||||
|
try:
|
||||||
|
if remote in remote_snapshots:
|
||||||
|
remote_snapshots[remote] = zsnaplib.get_snapshots(zfs_cmd=zfs_cmd)
|
||||||
|
except zsnaplib.ZFSSnapshotError:
|
||||||
|
del remote_snapshots[remote]
|
||||||
|
log.warning("Could not refresh snapshots on {}".format(remote))
|
||||||
|
|
||||||
snapshots = zsnaplib.get_snapshots(sudo)
|
snapshots = zsnaplib.get_snapshots(sudo)
|
||||||
failed_send = send_snapshots(fslist, snapshots, config, sudo)
|
failed_send = send_snapshots(fslist, snapshots, config, sudo)
|
||||||
if failed_send:
|
if failed_send:
|
||||||
|
2
setup.py
2
setup.py
@ -6,7 +6,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils import setup
|
from distutils import setup
|
||||||
|
|
||||||
version = '0.2'
|
version = '0.3'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='zsnapper',
|
name='zsnapper',
|
||||||
|
@ -13,7 +13,7 @@ logger = 'zsnapper'
|
|||||||
class ZFSSnapshotError(Exception):
|
class ZFSSnapshotError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def do_zfs_command(args, sudo, pipecmd=None, zfs_cmd=[zfs_bin]):
|
def do_zfs_command(args, sudo, zfs_cmd, pipecmd=None):
|
||||||
cmd = []
|
cmd = []
|
||||||
sudopw = None
|
sudopw = None
|
||||||
if sudo:
|
if sudo:
|
||||||
@ -55,6 +55,7 @@ def send_snapshot(
|
|||||||
snap,
|
snap,
|
||||||
remote_zfs_cmd,
|
remote_zfs_cmd,
|
||||||
remote_target,
|
remote_target,
|
||||||
|
zfs_cmd=[zfs_bin],
|
||||||
sudo=False,
|
sudo=False,
|
||||||
send_opts=[],
|
send_opts=[],
|
||||||
recv_opts=[],
|
recv_opts=[],
|
||||||
@ -74,18 +75,18 @@ def send_snapshot(
|
|||||||
|
|
||||||
pipecmd = remote_zfs_cmd + [ 'receive' ] + recv_opts + [ remote_target ]
|
pipecmd = remote_zfs_cmd + [ 'receive' ] + recv_opts + [ remote_target ]
|
||||||
|
|
||||||
do_zfs_command(args, sudo, pipecmd=pipecmd)
|
do_zfs_command(args, sudo, zfs_cmd, pipecmd=pipecmd)
|
||||||
|
|
||||||
|
|
||||||
def create_snapshot(fs, sudo=False):
|
def create_snapshot(fs, sudo=False, zfs_cmd=[zfs_bin]):
|
||||||
|
|
||||||
d = datetime.datetime.now().strftime(time_format)
|
d = datetime.datetime.now().strftime(time_format)
|
||||||
args = ['snapshot', '{}@{}'.format(fs, d)]
|
args = ['snapshot', '{}@{}'.format(fs, d)]
|
||||||
do_zfs_command(args, sudo)
|
do_zfs_command(args, sudo, zfs_cmd)
|
||||||
|
|
||||||
def get_filesystems(sudo=False, zfs_cmd=[zfs_bin]):
|
def get_filesystems(sudo=False, zfs_cmd=[zfs_bin]):
|
||||||
args = ['list', '-H']
|
args = ['list', '-H']
|
||||||
out = do_zfs_command(args, sudo, zfs_cmd=zfs_cmd)
|
out = do_zfs_command(args, sudo, zfs_cmd)
|
||||||
ret = set()
|
ret = set()
|
||||||
|
|
||||||
for row in out.splitlines():
|
for row in out.splitlines():
|
||||||
@ -96,7 +97,7 @@ def get_filesystems(sudo=False, zfs_cmd=[zfs_bin]):
|
|||||||
|
|
||||||
def get_snapshots(sudo=False, zfs_cmd=[zfs_bin]):
|
def get_snapshots(sudo=False, zfs_cmd=[zfs_bin]):
|
||||||
args = [ 'list', '-H', '-t', 'snapshot' ]
|
args = [ 'list', '-H', '-t', 'snapshot' ]
|
||||||
out = do_zfs_command(args, sudo, zfs_cmd=zfs_cmd)
|
out = do_zfs_command(args, sudo, zfs_cmd)
|
||||||
snapshots = {}
|
snapshots = {}
|
||||||
|
|
||||||
for row in out.splitlines():
|
for row in out.splitlines():
|
||||||
@ -115,10 +116,10 @@ def get_snapshots(sudo=False, zfs_cmd=[zfs_bin]):
|
|||||||
return snapshots
|
return snapshots
|
||||||
|
|
||||||
|
|
||||||
def remove_snapshot(fs, date, sudo=False):
|
def remove_snapshot(fs, date, sudo=False, zfs_cmd=[zfs_bin]):
|
||||||
date = date.strftime(time_format)
|
date = date.strftime(time_format)
|
||||||
args = [ 'destroy', '{}@{}'.format(fs, date) ]
|
args = [ 'destroy', '{}@{}'.format(fs, date) ]
|
||||||
do_zfs_command(args, sudo)
|
do_zfs_command(args, sudo, zfs_cmd)
|
||||||
|
|
||||||
|
|
||||||
def weed_snapshots(
|
def weed_snapshots(
|
||||||
|
Loading…
Reference in New Issue
Block a user