This commit is contained in:
Edwin Eefting
2017-07-30 00:52:31 +02:00
parent 441a323fb2
commit cae8ec3e70

View File

@ -23,13 +23,19 @@ def debug(txt):
print(txt)
class Node(Object):
"""an endpoint that contains zfs filesystems. can be local or remote"""
class ZfsNode:
"""an endpoint that contains zfs filesystems.
def __init__(self, ssh_to='local'):
self.backup_name=backup_name
contains lowlevel zfs wrappers for actual zfs commands on remote nodes via ssh (or local)
methods only accept and return simple dataset names, just like the real commands
"""
def __init__(self, ssh_to):
"""ssh_to: server you want to ssh to. specify 'local' to just use local commands without ssh"""
self.ssh_to=ssh_to
def run(cmd, input=None, tab_split=False, valid_exitcodes=[ 0 ], test=False):
"""run a command on the node"""
@ -87,7 +93,10 @@ class Node(Object):
def zfs_get_selected_filesystems():
"""determine filesystems that should be backupped by looking at the special autobackup-property"""
"""determine filesystems that should be backupped by looking at the special autobackup-property
return: list with dataset names
"""
#get all source filesystems that have the backup property
source_filesystems=self.run(tab_split=True, cmd=[
@ -171,20 +180,18 @@ class Node(Object):
#in testmode we dont actually make changes, so keep them in a list to simulate
if args.test:
if not ssh_to in test_snapshots:
test_snapshots[ssh_to]={}
if not filesystem in test_snapshots[ssh_to]:
test_snapshots[ssh_to][filesystem]=[]
test_snapshots[ssh_to][filesystem].append(snapshot)
if not filesystem in test_snapshots:
test_snapshots[filesystem]=[]
test_snapshots[filesystem].append(snapshot)
run(ssh_to=ssh_to, tab_split=False, cmd=cmd, test=args.test)
"""get names of all snapshots for specified filesystems belonging to backup_name
def zfs_get_snapshots(filesystems):
"""get names of all snapshots for specified filesystems belonging to backup_name
return[filesystem_name]=[ "snashot1", "snapshot2", ... ]
"""
def zfs_get_snapshots(ssh_to, filesystems, backup_name):
return[filesystem_name]=[ "snashot1", "snapshot2", ... ]
"""
ret={}
@ -218,29 +225,29 @@ def zfs_get_snapshots(ssh_to, filesystems, backup_name):
"""transfer a zfs snapshot from source to target. both can be either local or via ssh.
"""transfer a zfs snapshot from source to target. both can be either local or via ssh.
TODO:
TODO:
buffering: specify buffer_size to use mbuffer (or alike) to apply buffering where neccesary
buffering: specify buffer_size to use mbuffer (or alike) to apply buffering where neccesary
local to local:
local send -> local buffer -> local receive
local to local:
local send -> local buffer -> local receive
local to remote and remote to local:
local send -> local buffer -> ssh -> remote buffer -> remote receive
remote send -> remote buffer -> ssh -> local buffer -> local receive
local to remote and remote to local:
local send -> local buffer -> ssh -> remote buffer -> remote receive
remote send -> remote buffer -> ssh -> local buffer -> local receive
remote to remote:
remote send -> remote buffer -> ssh -> local buffer -> ssh -> remote buffer -> remote receive
remote to remote:
remote send -> remote buffer -> ssh -> local buffer -> ssh -> remote buffer -> remote receive
TODO: can we string together all the zfs sends and recvs, so that we only need to use 1 ssh connection? should be faster if there are many small snaphots
TODO: can we string together all the zfs sends and recvs, so that we only need to use 1 ssh connection? should be faster if there are many small snaphots
"""
def zfs_transfer(ssh_source, source_filesystem, first_snapshot, second_snapshot,
"""
def zfs_transfer(ssh_source, source_filesystem, first_snapshot, second_snapshot,
ssh_target, target_filesystem, resume_token=None, buffer_size=None):
#### build source command
@ -339,8 +346,8 @@ def zfs_transfer(ssh_source, source_filesystem, first_snapshot, second_snapshot,
"""get filesystems that where already backupped to a target. """
def zfs_get_backupped_filesystems(ssh_to, backup_name, target_fs):
"""get filesystems that where already backupped to a target. """
def zfs_get_backupped_filesystems(ssh_to, backup_name, target_fs):
#get all target filesystems that have received or inherited the backup propert, under the target_fs tree
ret=run(ssh_to=ssh_to, tab_split=False, cmd=[
"zfs", "get", "-r", "-t", "volume,filesystem", "-o", "name", "-s", "received,inherited", "-H", "autobackup:"+backup_name, target_fs
@ -350,13 +357,13 @@ def zfs_get_backupped_filesystems(ssh_to, backup_name, target_fs):
"""get filesystems that where once backupped to target but are no longer selected on source
"""get filesystems that where once backupped to target but are no longer selected on source
these are filesystems that are not in the list in target_filesystems.
these are filesystems that are not in the list in target_filesystems.
this happens when filesystems are destroyed or unselected on the source.
"""
def get_stale_backupped_filesystems(ssh_to, backup_name, target_fs, target_filesystems):
this happens when filesystems are destroyed or unselected on the source.
"""
def get_stale_backupped_filesystems(ssh_to, backup_name, target_fs, target_filesystems):
backupped_filesystems=zfs_get_backupped_filesystems(ssh_to=ssh_to, backup_name=backup_name, target_fs=target_fs)
@ -369,9 +376,9 @@ def get_stale_backupped_filesystems(ssh_to, backup_name, target_fs, target_files
return(stale_backupped_filesystems)
now=time.time()
"""determine list of snapshot (in @format) to destroy, according to age"""
def determine_destroy_list(snapshots, days):
now=time.time()
"""determine list of snapshot (in @format) to destroy, according to age"""
def determine_destroy_list(snapshots, days):
ret=[]
for filesystem in snapshots:
for snapshot in snapshots[filesystem]:
@ -388,10 +395,57 @@ def determine_destroy_list(snapshots, days):
return(ret)
def lstrip_path(path, count):
def lstrip_path(path, count):
return("/".join(path.split("/")[count:]))
class ZfsDataset:
"""a generic zfs dataset"""
def __init__(name, parent, backup=false, created=false):
""" backup: should be backupped by zfs_autobackup
created: is created by zfs_autobackup (and may be destroyed by it as well)
parent: parent dataset this belongs to (none is "root")
"""
self.name=name
self.parent=parent
self.created=created
self.backup=backup
self.childs={}
class ZfsSnapshot(Dataset):
"""A zfs snapshot"""
def __init__(previous_snapshot=false, next_snapshot=fase, keep_time=false, timestamp=false, **kwargs, *args):
super.__init__(**kargs, *args)
self.timestamp
self.keep_time
self.previous_snapshot
self.next_snapshot
class ZfsBackupSource():
"""backup source.
contains high level backup source functions.
these work with ZfsDataset and ZfsSnapshot objects.
"""
def __init__(self):
self.node=ZfsNode(ssh_to=args.ssh_to)
self.datasets={}
self.snapshots={}
def refresh():
"""refresh all data by calling various zfs commands"""
selected_filesystems=self.node.zfs_get_selected_filesystems()
def zfs_autobackup():