def send_snap(snapshot, dest_name, base=None, ssh=None): """Sends snapshot to destination, incrementally and over ssh if specified. Parameters: ---------- snapshot : {ZFSSnapshot} Snapshot to send dest_name : {str} Name of the location to send snapshot base : {ZFSSnapshot}, optional Base snapshot for incremental stream (the default is None, meaning a full stream) ssh : {paramiko.SSHClient}, optional Open ssh connection for remote backup (the default is None, meaning local backup) Returns ------- int 0 if success, 1 if not """ logger = logging.getLogger(__name__) dest_name_log = '{:s}@{:s}:{:s}'.format(ssh.user, ssh.host, dest_name) if ssh else dest_name stream_size = snapshot.stream_size(base=base) try: with snapshot.send(base=base, intermediates=True) as send: with Popen(MBUFFER, stdin=send.stdout, stdout=PIPE) as mbuffer: with Popen(PV(stream_size), stdin=mbuffer.stdout, stdout=PIPE) as pv: zfs.receive(name=dest_name, stdin=pv.stdout, ssh=ssh, force=True, nomount=True) except (DatasetNotFoundError, DatasetExistsError, DatasetBusyError, OSError, EOFError) as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err)) return 1 except CalledProcessError as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err.stderr.rstrip())) return 1 except KeyboardInterrupt: logger.error( 'KeyboardInterrupt while sending to {:s}...'.format(dest_name_log)) raise else: return 0
def send_snap(snapshot, dest_name, base=None, ssh_dest=None, raw=False, resume=False, resume_token=None): """Sends snapshot to destination, incrementally and over ssh if specified. Parameters: ---------- snapshot : {ZFSSnapshot} Snapshot to send dest_name : {str} Name of the location to send snapshot base : {ZFSSnapshot}, optional Base snapshot for incremental stream (the default is None, meaning a full stream) ssh_dest : {ssh.SSH}, optional Open ssh connection for remote backup (the default is None, meaning local backup) Returns ------- int 0 if success, 1 if not, 2 if CalledProcessError """ logger = logging.getLogger(__name__) dest_name_log = '{:s}@{:s}:{:s}'.format( ssh_dest.user, ssh_dest.host, dest_name) if ssh_dest else dest_name try: ssh_source = snapshot.ssh stream_size = snapshot.stream_size(base=base, raw=raw, resume_token=resume_token) zfs.STATS.add('zfs_send_snap_count') if get_dry_run(): zfs.STATS.add('send_size', stream_size) logger.warning( 'DRY_RUN: send_snapshot {} --> {} base:{} size:{} resume_token:{} compress:{}/{}' .format(snapshot.name, dest_name_log, base, stream_size, resume_token, ssh_source.compress if ssh_source else None, ssh_dest.decompress if ssh_dest else None)) return 0 send = snapshot.send(ssh_dest=ssh_dest, base=base, intermediates=True, raw=raw, resume_token=resume_token) recv = zfs.receive(name=dest_name, stdin=send.stdout, ssh=ssh_dest, ssh_source=ssh_source, force=True, nomount=True, stream_size=stream_size, raw=raw, resume=resume) send.stdout.close() # write pv output to stderr / stdout for line in TextIOWrapper(send.stderr, newline='\r'): if sys.stdout.isatty(): sys.stderr.write(' ' + line) sys.stderr.flush() elif line.rstrip(): # is stdout is redirected, write pv to stdout sys.stdout.write(' ' + line.rstrip() + '\n') sys.stdout.flush() send.stderr.close() stdout, stderr = recv.communicate() # raise any error that occurred if recv.returncode: raise CalledProcessError(returncode=recv.returncode, cmd=recv.args, output=stdout, stderr=stderr) except (DatasetNotFoundError, DatasetExistsError, DatasetBusyError, OSError, EOFError) as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err)) return 1 except CalledProcessError as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err.stderr.rstrip().decode().replace('\n', ' - '))) # returncode 2 means we will retry send if requested return 2 except KeyboardInterrupt: logger.error( 'KeyboardInterrupt while sending to {:s}...'.format(dest_name_log)) raise else: return 0
def send_snap(snapshot, dest_name, base=None, ssh=None): """Sends snapshot to destination, incrementally and over ssh if specified. Parameters: ---------- snapshot : {ZFSSnapshot} Snapshot to send dest_name : {str} Name of the location to send snapshot base : {ZFSSnapshot}, optional Base snapshot for incremental stream (the default is None, meaning a full stream) ssh : {ssh.SSH}, optional Open ssh connection for remote backup (the default is None, meaning local backup) Returns ------- int 0 if success, 1 if not """ logger = logging.getLogger(__name__) dest_name_log = '{:s}@{:s}:{:s}'.format(ssh.user, ssh.host, dest_name) if ssh else dest_name stream_size = snapshot.stream_size(base=base) try: # create list of all processes connected with a pipe processes = [snapshot.send(base=base, intermediates=True)] if MBUFFER: logger.debug("Using mbuffer: '{:s}'...".format(' '.join(MBUFFER))) processes.append( Popen(MBUFFER, stdin=processes[-1].stdout, stdout=PIPE)) if PV: logger.debug("Using pv: '{:s}'...".format(' '.join( PV(stream_size)))) processes.append( Popen(PV(stream_size), stdin=processes[-1].stdout, stdout=PIPE)) if ssh and ssh.compress: logger.debug("Using compression: '{:s}'...".format(' '.join( ssh.compress))) processes.append( Popen(ssh.compress, stdin=processes[-1].stdout, stdout=PIPE)) processes.append( zfs.receive(name=dest_name, stdin=processes[-1].stdout, ssh=ssh, force=True, nomount=True)) processes[-1].communicate() except (DatasetNotFoundError, DatasetExistsError, DatasetBusyError, OSError, EOFError) as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err)) return 1 except CalledProcessError as err: logger.error('Error while sending to {:s}: {}...'.format( dest_name_log, err.stderr.rstrip().decode())) return 1 except KeyboardInterrupt: logger.error( 'KeyboardInterrupt while sending to {:s}...'.format(dest_name_log)) raise else: return 0