diff --git a/zfs_autobackup b/zfs_autobackup index 501a6c6..c0dba57 100755 --- a/zfs_autobackup +++ b/zfs_autobackup @@ -446,19 +446,19 @@ class ExecuteNode: break + if self.debug_output: + self.debug("EXIT > {}".format(p.returncode)) #handle piped process error output and exit codes if isinstance(input, subprocess.Popen): if self.debug_output: self.debug("EXIT |> {}".format(input.returncode)) - if input.returncode not in valid_exitcodes: + if valid_exitcodes and input.returncode not in valid_exitcodes: raise(subprocess.CalledProcessError(input.returncode, "(pipe)")) - if self.debug_output: - self.debug("EXIT > {}".format(p.returncode)) - if p.returncode not in valid_exitcodes: + if valid_exitcodes and p.returncode not in valid_exitcodes: raise(subprocess.CalledProcessError(p.returncode, encoded_cmd)) if not tab_split: @@ -802,7 +802,7 @@ class ZfsDataset(): return(self.zfs_node.run(cmd, pipe=True)) - def recv_pipe(self, pipe, resume=True, filter_properties=[], set_properties=[]): + def recv_pipe(self, pipe, resume=True, filter_properties=[], set_properties=[], ignore_exit_code=False): """starts a zfs recv for this snapshot and uses pipe as input note: you can also call both a snapshot and filesystem object. @@ -831,8 +831,13 @@ class ZfsDataset(): cmd.append(self.filesystem_name) + if ignore_exit_code: + valid_exitcodes=[] + else: + valid_exitcodes=[0] + self.zfs_node.reset_progress() - self.zfs_node.run(cmd, input=pipe) + self.zfs_node.run(cmd, input=pipe, valid_exitcodes=valid_exitcodes) #invalidate cache, but we at least know we exist now self.invalidate() @@ -849,7 +854,7 @@ class ZfsDataset(): # cmd.append("|mbuffer -m {}".format(args.buffer)) - def transfer_snapshot(self, target_snapshot, prev_snapshot=None, resume=True, show_progress=False, filter_properties=[], set_properties=[]): + def transfer_snapshot(self, target_snapshot, prev_snapshot=None, resume=True, show_progress=False, filter_properties=[], set_properties=[], ignore_recv_exit_code=False): """transfer this snapshot to target_snapshot. specify prev_snapshot for incremental transfer connects a send_pipe() to recv_pipe() @@ -867,10 +872,10 @@ class ZfsDataset(): #do it pipe=self.send_pipe(resume=resume, show_progress=show_progress, prev_snapshot=prev_snapshot) - target_snapshot.recv_pipe(pipe, resume=resume, filter_properties=filter_properties, set_properties=set_properties) + target_snapshot.recv_pipe(pipe, resume=resume, filter_properties=filter_properties, set_properties=set_properties, ignore_exit_code=ignore_recv_exit_code) - def resume_transfer(self, target_dataset, show_progress=False): + def resume_transfer(self, target_dataset, show_progress=False, filter_properties=[], set_properties=[], ignore_recv_exit_code=False): """resume an interrupted transfer, if there is one""" #resume is a kind of special case since we dont know which snapshot we are transferring. (its encoded in the resume token) @@ -878,7 +883,7 @@ class ZfsDataset(): target_dataset.verbose("resuming") #just send and recv on dataset instead of snapshot object. pipe=self.send_pipe(show_progress=show_progress, resume_token=target_dataset.properties['receive_resume_token']) - target_dataset.recv_pipe(pipe,resume=True) + target_dataset.recv_pipe(pipe,resume=True, filter_properties=filter_properties, set_properties=set_properties, ignore_exit_code=ignore_recv_exit_code) return(True) return(False) @@ -910,13 +915,14 @@ class ZfsDataset(): return(snapshot) - def sync_snapshots(self, target_dataset, show_progress=False, resume=True, filter_properties=[], set_properties=[]): + def sync_snapshots(self, target_dataset, show_progress=False, resume=True, filter_properties=[], set_properties=[], ignore_recv_exit_code=False): """sync our snapshots to target_dataset""" #resume something first? - if self.resume_transfer(target_dataset, show_progress): + if self.resume_transfer(target_dataset, show_progress=show_progress, filter_properties=filter_properties, set_properties=set_properties, ignore_recv_exit_code=ignore_recv_exit_code): + #running in readonly mode and no snapshots yet? assume initial snapshot (otherwise we cant find common snapshot in next step) if self.zfs_node.readonly and not target_dataset.our_snapshots: target_dataset.snapshots.append(ZfsDataset(target_dataset.zfs_node, target_dataset.name + "@" + self.our_snapshots[0].snapshot_name)) @@ -968,7 +974,7 @@ class ZfsDataset(): #does target actually want it? if target_snapshot in target_keeps: - source_snapshot.transfer_snapshot(target_snapshot, prev_snapshot=prev_source_snapshot, show_progress=show_progress, resume=resume, filter_properties=filter_properties, set_properties=set_properties) + source_snapshot.transfer_snapshot(target_snapshot, prev_snapshot=prev_source_snapshot, show_progress=show_progress, resume=resume, filter_properties=filter_properties, set_properties=set_properties, ignore_recv_exit_code=ignore_recv_exit_code) #we may destroy the previous snapshot now, if we dont want it anymore if prev_source_snapshot and (prev_source_snapshot not in source_keeps): @@ -1181,8 +1187,8 @@ class ZfsAutobackup: # parser.add_argument('--destroy-stale', action='store_true', help='Destroy stale backups that have no more snapshots. Be sure to verify the output before using this! ') parser.add_argument('--clear-refreservation', action='store_true', help='Set refreservation property to none for new filesystems. Usefull when backupping SmartOS volumes. (recommended. same as --set-properties refreservation=none)') parser.add_argument('--clear-mountpoint', action='store_true', help='Sets canmount=noauto property, to prevent the received filesystem from mounting over existing filesystems. (recommended. same as --set-properties canmount=noauto)') - parser.add_argument('--filter-properties', action='append', help='List of propererties to "filter" when receiving filesystems. (you can still restore them with zfs inherit -S)') - parser.add_argument('--set-properties', action='append', help='List of propererties to override when receiving filesystems. (you can still restore them with zfs inherit -S)') + parser.add_argument('--filter-properties', type=str, help='List of propererties to "filter" when receiving filesystems. (you can still restore them with zfs inherit -S)') + parser.add_argument('--set-properties', type=str, help='List of propererties to override when receiving filesystems. (you can still restore them with zfs inherit -S)') parser.add_argument('--rollback', action='store_true', help='Rollback changes on the target before starting a backup. (normally you can prevent changes by setting the readonly property on the target_path to on)') parser.add_argument('--ignore-transfer-errors', action='store_true', help='Ignore transfer errors (still checks if received filesystem exists. usefull for acltype errors)') @@ -1265,7 +1271,7 @@ class ZfsAutobackup: if not target_dataset.parent.exists: target_dataset.parent.create_filesystem(parents=True) - source_dataset.sync_snapshots(target_dataset, show_progress=self.args.progress, resume=self.args.resume, filter_properties=filter_properties, set_properties=set_properties) + source_dataset.sync_snapshots(target_dataset, show_progress=self.args.progress, resume=self.args.resume, filter_properties=filter_properties, set_properties=set_properties, ignore_recv_exit_code=self.args.ignore_transfer_errors) except Exception as e: source_dataset.error(str(e)) if self.args.debug: