Beispiel #1
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 : {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
Beispiel #2
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
Beispiel #3
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