added --...-format options. closes #87
This commit is contained in:
@ -135,6 +135,13 @@ class ZfsAutobackup:
|
||||
parser.add_argument('--buffer', metavar='SIZE', default=None,
|
||||
help='Add zfs send and recv buffers to smooth out IO bursts. (e.g. 128M. requires mbuffer)')
|
||||
|
||||
parser.add_argument('--snapshot-format', metavar='FORMAT', default="{}-%Y%m%d%H%M%S",
|
||||
help='Snapshot naming format. Default: %(default)s')
|
||||
parser.add_argument('--property-format', metavar='FORMAT', default="autobackup:{}",
|
||||
help='Select property naming format. Default: %(default)s')
|
||||
parser.add_argument('--hold-format', metavar='FORMAT', default="zfs_autobackup:{}",
|
||||
help='Hold naming format. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--version', action='store_true',
|
||||
help='Show version.')
|
||||
|
||||
@ -464,6 +471,19 @@ class ZfsAutobackup:
|
||||
if self.args.test:
|
||||
self.warning("TEST MODE - SIMULATING WITHOUT MAKING ANY CHANGES")
|
||||
|
||||
#format all the names
|
||||
property_name = self.args.property_format.format(self.args.backup_name)
|
||||
snapshot_time_format = self.args.snapshot_format.format(self.args.backup_name)
|
||||
hold_name = self.args.hold_format.format(self.args.backup_name)
|
||||
|
||||
self.verbose("")
|
||||
self.verbose("Selecting dataset property : {}".format(property_name))
|
||||
self.verbose("Snapshot format : {}".format(snapshot_time_format))
|
||||
|
||||
if not self.args.no_holds:
|
||||
self.verbose("Hold name : {}".format(hold_name))
|
||||
|
||||
|
||||
################ create source zfsNode
|
||||
self.set_title("Source settings")
|
||||
|
||||
@ -472,17 +492,9 @@ class ZfsAutobackup:
|
||||
source_thinner = None
|
||||
else:
|
||||
source_thinner = Thinner(self.args.keep_source)
|
||||
source_node = ZfsNode(self.args.backup_name, self, ssh_config=self.args.ssh_config,
|
||||
source_node = ZfsNode(snapshot_time_format=snapshot_time_format, hold_name=hold_name, logger=self, ssh_config=self.args.ssh_config,
|
||||
ssh_to=self.args.ssh_source, readonly=self.args.test,
|
||||
debug_output=self.args.debug_output, description=description, thinner=source_thinner)
|
||||
source_node.verbose(
|
||||
"Selecting dataset property: 'autobackup:{}'".format(
|
||||
self.args.backup_name, self.args.backup_name))
|
||||
|
||||
source_node.verbose("Snapshot prefix : '{}-'".format(self.args.backup_name))
|
||||
|
||||
if not self.args.no_holds:
|
||||
source_node.verbose("Hold name : '{}'".format("zfs_autobackup:" + self.args.backup_name))
|
||||
|
||||
|
||||
################# select source datasets
|
||||
@ -503,7 +515,7 @@ class ZfsAutobackup:
|
||||
self.warning("Source and target are on the same host, excluding received datasets from selection.")
|
||||
exclude_received = True
|
||||
|
||||
source_datasets = source_node.selected_datasets(exclude_received=exclude_received,
|
||||
source_datasets = source_node.selected_datasets(property_name=property_name,exclude_received=exclude_received,
|
||||
exclude_paths=exclude_paths,
|
||||
exclude_unchanged=self.args.exclude_unchanged,
|
||||
min_change=self.args.min_change)
|
||||
@ -517,7 +529,8 @@ class ZfsAutobackup:
|
||||
################# snapshotting
|
||||
if not self.args.no_snapshot:
|
||||
self.set_title("Snapshotting")
|
||||
source_node.consistent_snapshot(source_datasets, source_node.new_snapshotname(),
|
||||
snapshot_name=time.strftime(snapshot_time_format)
|
||||
source_node.consistent_snapshot(source_datasets, snapshot_name,
|
||||
min_changed_bytes=self.args.min_change,
|
||||
pre_snapshot_cmds=self.args.pre_snapshot_cmd,
|
||||
post_snapshot_cmds=self.args.post_snapshot_cmd)
|
||||
@ -532,7 +545,7 @@ class ZfsAutobackup:
|
||||
target_thinner = None
|
||||
else:
|
||||
target_thinner = Thinner(self.args.keep_target)
|
||||
target_node = ZfsNode(self.args.backup_name, self, ssh_config=self.args.ssh_config,
|
||||
target_node = ZfsNode(snapshot_time_format=snapshot_time_format, hold_name=hold_name, logger=self, ssh_config=self.args.ssh_config,
|
||||
ssh_to=self.args.ssh_target,
|
||||
readonly=self.args.test, debug_output=self.args.debug_output,
|
||||
description="[Target]",
|
||||
|
||||
@ -321,15 +321,14 @@ class ZfsDataset:
|
||||
return True
|
||||
|
||||
def is_ours(self):
|
||||
"""return true if this snapshot is created by this backup_name"""
|
||||
if re.match("^" + self.zfs_node.backup_name + "-[0-9]*$", self.snapshot_name):
|
||||
return True
|
||||
else:
|
||||
"""return true if this snapshot name has format"""
|
||||
try:
|
||||
test = self.timestamp
|
||||
except ValueError as e:
|
||||
self.error(str(e))
|
||||
return False
|
||||
|
||||
@property
|
||||
def _hold_name(self):
|
||||
return "zfs_autobackup:" + self.zfs_node.backup_name
|
||||
return True
|
||||
|
||||
@property
|
||||
def holds(self):
|
||||
@ -341,30 +340,26 @@ class ZfsDataset:
|
||||
|
||||
def is_hold(self):
|
||||
"""did we hold this snapshot?"""
|
||||
return self._hold_name in self.holds
|
||||
return self.zfs_node.hold_name in self.holds
|
||||
|
||||
def hold(self):
|
||||
"""hold dataset"""
|
||||
self.debug("holding")
|
||||
self.zfs_node.run(["zfs", "hold", self._hold_name, self.name], valid_exitcodes=[0, 1])
|
||||
self.zfs_node.run(["zfs", "hold", self.zfs_node.hold_name, self.name], valid_exitcodes=[0, 1])
|
||||
|
||||
def release(self):
|
||||
"""release dataset"""
|
||||
if self.zfs_node.readonly or self.is_hold():
|
||||
self.debug("releasing")
|
||||
self.zfs_node.run(["zfs", "release", self._hold_name, self.name], valid_exitcodes=[0, 1])
|
||||
self.zfs_node.run(["zfs", "release", self.zfs_node.hold_name, self.name], valid_exitcodes=[0, 1])
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
"""get timestamp from snapshot name. Only works for our own snapshots
|
||||
with the correct format.
|
||||
"""
|
||||
time_str = re.findall("^.*-([0-9]*)$", self.snapshot_name)[0]
|
||||
if len(time_str) != 14:
|
||||
raise (Exception("Snapshot has invalid timestamp in name: {}".format(self.snapshot_name)))
|
||||
|
||||
# new format:
|
||||
time_secs = time.mktime(time.strptime(time_str, "%Y%m%d%H%M%S"))
|
||||
time_secs = time.mktime(time.strptime(self.snapshot_name, self.zfs_node.snapshot_time_format))
|
||||
return time_secs
|
||||
|
||||
def from_names(self, names):
|
||||
|
||||
@ -17,9 +17,13 @@ from .ExecuteNode import ExecuteError
|
||||
class ZfsNode(ExecuteNode):
|
||||
"""a node that contains zfs datasets. implements global (systemwide/pool wide) zfs commands"""
|
||||
|
||||
def __init__(self, backup_name, logger, ssh_config=None, ssh_to=None, readonly=False, description="",
|
||||
def __init__(self, snapshot_time_format, hold_name, logger, ssh_config=None, ssh_to=None, readonly=False,
|
||||
description="",
|
||||
debug_output=False, thinner=None):
|
||||
self.backup_name = backup_name
|
||||
|
||||
self.snapshot_time_format = snapshot_time_format
|
||||
self.hold_name = hold_name
|
||||
|
||||
self.description = description
|
||||
|
||||
self.logger = logger
|
||||
@ -54,7 +58,7 @@ class ZfsNode(ExecuteNode):
|
||||
if self.__thinner is not None:
|
||||
return self.__thinner.thin(objects, keep_objects)
|
||||
else:
|
||||
return ( keep_objects, [] )
|
||||
return (keep_objects, [])
|
||||
|
||||
@CachedProperty
|
||||
def supported_send_options(self):
|
||||
@ -129,8 +133,9 @@ class ZfsNode(ExecuteNode):
|
||||
bytes_left = self._progress_total_bytes - bytes_
|
||||
minutes_left = int((bytes_left / (bytes_ / (time.time() - self._progress_start_time))) / 60)
|
||||
|
||||
self.logger.progress("Transfer {}% {}MB/s (total {}MB, {} minutes left)".format(percentage, speed, int(
|
||||
self._progress_total_bytes / (1024 * 1024)), minutes_left))
|
||||
self.logger.progress(
|
||||
"Transfer {}% {}MB/s (total {}MB, {} minutes left)".format(percentage, speed, int(
|
||||
self._progress_total_bytes / (1024 * 1024)), minutes_left))
|
||||
|
||||
return
|
||||
|
||||
@ -158,11 +163,8 @@ class ZfsNode(ExecuteNode):
|
||||
def debug(self, txt):
|
||||
self.logger.debug("{} {}".format(self.description, txt))
|
||||
|
||||
def new_snapshotname(self):
|
||||
"""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, pre_snapshot_cmds=[], post_snapshot_cmds=[]):
|
||||
def consistent_snapshot(self, datasets, snapshot_name, min_changed_bytes, pre_snapshot_cmds=[],
|
||||
post_snapshot_cmds=[]):
|
||||
"""create a consistent (atomic) snapshot of specified datasets, per pool.
|
||||
"""
|
||||
|
||||
@ -214,7 +216,7 @@ class ZfsNode(ExecuteNode):
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def selected_datasets(self, exclude_received, exclude_paths, exclude_unchanged, min_change):
|
||||
def selected_datasets(self, property_name, exclude_received, exclude_paths, exclude_unchanged, min_change):
|
||||
"""determine filesystems that should be backed up by looking at the special autobackup-property, systemwide
|
||||
|
||||
returns: list of ZfsDataset
|
||||
@ -225,7 +227,7 @@ class ZfsNode(ExecuteNode):
|
||||
# get all source filesystems that have the backup property
|
||||
lines = self.run(tab_split=True, readonly=True, cmd=[
|
||||
"zfs", "get", "-t", "volume,filesystem", "-o", "name,value,source", "-H",
|
||||
"autobackup:" + self.backup_name
|
||||
property_name
|
||||
])
|
||||
|
||||
# The returnlist of selected ZfsDataset's:
|
||||
@ -249,7 +251,9 @@ class ZfsNode(ExecuteNode):
|
||||
source = raw_source
|
||||
|
||||
# determine it
|
||||
if dataset.is_selected(value=value, source=source, inherited=inherited, exclude_received=exclude_received, exclude_paths=exclude_paths, exclude_unchanged=exclude_unchanged, min_change=min_change):
|
||||
if dataset.is_selected(value=value, source=source, inherited=inherited, exclude_received=exclude_received,
|
||||
exclude_paths=exclude_paths, exclude_unchanged=exclude_unchanged,
|
||||
min_change=min_change):
|
||||
selected_filesystems.append(dataset)
|
||||
|
||||
return selected_filesystems
|
||||
|
||||
Reference in New Issue
Block a user