Example #1
0
 def test_empty_everything(self):
     """
     No env.key_filename and no ssh_config = empty list
     """
     with settings(use_ssh_config=False):
         with settings(key_filename=""):
             eq_(key_filenames(), [])
         with settings(key_filename=[]):
             eq_(key_filenames(), [])
Example #2
0
 def test_empty_everything(self):
     """
     No env.key_filename and no ssh_config = empty list
     """
     with settings(use_ssh_config=False):
         with settings(key_filename=""):
             eq_(key_filenames(), [])
         with settings(key_filename=[]):
             eq_(key_filenames(), [])
Example #3
0
 def test_just_env(self):
     """
     Valid env.key_filename and no ssh_config = just env
     """
     with settings(use_ssh_config=False):
         with settings(key_filename="mykey"):
             eq_(key_filenames(), ["mykey"])
         with settings(key_filename=["foo", "bar"]):
             eq_(key_filenames(), ["foo", "bar"])
Example #4
0
 def test_just_env(self):
     """
     Valid env.key_filename and no ssh_config = just env
     """
     with settings(use_ssh_config=False):
         with settings(key_filename="mykey"):
             eq_(key_filenames(), ["mykey"])
         with settings(key_filename=["foo", "bar"]):
             eq_(key_filenames(), ["foo", "bar"])
Example #5
0
 def test_both(self):
     """
     Both env.key_filename + valid ssh_config = both show up w/ env var first
     """
     with settings(use_ssh_config=True, ssh_config_path=support("ssh_config")):
         with settings(key_filename="bizbaz.pub"):
             eq_(key_filenames(), ["bizbaz.pub", "foobar.pub"])
         with settings(key_filename=["bizbaz.pub", "whatever.pub"]):
             expected = ["bizbaz.pub", "whatever.pub", "foobar.pub"]
             eq_(key_filenames(), expected)
Example #6
0
 def test_both(self):
     """
     Both env.key_filename + valid ssh_config = both show up w/ env var first
     """
     with settings(use_ssh_config=True, ssh_config_path=support("ssh_config")):
         with settings(key_filename="bizbaz.pub"):
             eq_(key_filenames(), ["bizbaz.pub", "foobar.pub"])
         with settings(key_filename=["bizbaz.pub", "whatever.pub"]):
             expected = ["bizbaz.pub", "whatever.pub", "foobar.pub"]
             eq_(key_filenames(), expected)
Example #7
0
def ssh(ssh_opts=''):
    # Keys
    key_string = ''
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)

    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port

    # Proxy
    proxy_string = ''
    if env.gateway:
        gw_user, gw_host, gw_port = normalize(env.gateway)
        gw_port_string = "-p %s" % gw_port
        if '@' in env.gateway:
            gw_user_host_string = env.gateway
        else:
            gw_user_host_string = ssh_host_string(user, env.gateway)
        proxy_string = '-o "ProxyCommand ssh {key_string} {ssh_opts} {gw_port_string} {gw_user_host_string} nc %h %p"'.format(
            key_string=key_string,
            ssh_opts=ssh_opts,
            gw_port_string=gw_port_string,
            gw_user_host_string=gw_user_host_string)

    cmd = "ssh -A -o 'ServerAliveInterval 30' {key_string} {ssh_opts} {port_string} {user_host_string} {proxy_string}".format(
        key_string=key_string,
        ssh_opts=ssh_opts,
        port_string=port_string,
        user_host_string=ssh_host_string(user, host),
        proxy_string=proxy_string)
    return local(cmd)
Example #8
0
def ssh(ssh_opts=''):
    # Keys
    key_string = ''
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)

    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port

    # Proxy
    proxy_string = ''
    if env.gateway:
        gw_user, gw_host, gw_port = normalize(env.gateway)
        gw_port_string = "-p %s" % gw_port
        if '@' in env.gateway:
            gw_user_host_string = env.gateway
        else:
            gw_user_host_string = ssh_host_string(user, env.gateway)
        proxy_string = '-o "ProxyCommand ssh {key_string} {ssh_opts} {gw_port_string} {gw_user_host_string} nc %h %p"'.format(
            key_string=key_string,
            ssh_opts=ssh_opts,
            gw_port_string=gw_port_string,
            gw_user_host_string=gw_user_host_string)

    cmd = "ssh -A -o 'ServerAliveInterval 30' {key_string} {ssh_opts} {port_string} {user_host_string} {proxy_string}".format(
        key_string=key_string,
        ssh_opts=ssh_opts,
        port_string=port_string,
        user_host_string=ssh_host_string(user, host),
        proxy_string=proxy_string)
    return local(cmd)
Example #9
0
def rsync(local_path, remote_path, extra_rsync_options=""):  # pylint: disable=too-many-locals
    """Rsync files/directories from local path to remote_path.

    .. note::
            Using absolute ``local_path`` supported but not recommended.

    Args:
        local_path: Local path on local host, copy files/directories from it. Should be relative.
        remote_path: Remote path on remote host, copy files/directories to it. Must be absolute.
        extra_rsync_options: Additional rsync options added after default '-aH --stats --force --timeout=600'

    Returns:
        True if some of remote files/directories are changed, False otherwise.
    """
    files_dir = os.path.join(os.path.dirname(env.real_fabfile), 'files')
    if not os.path.isdir(files_dir):
        fname = str(inspect.stack()[1][1])
        nline = str(inspect.stack()[1][2])
        abort('rsync: files dir \'%s\' not exists in file %s line %s' % (files_dir, fname, nline))
    local_abs_path = os.path.join(files_dir, local_path)
    if not os.path.exists(local_abs_path):
        fname = str(inspect.stack()[1][1])
        nline = str(inspect.stack()[1][2])
        abort('rsync: local path \'%s\' not exists in file %s line %s' % (local_abs_path, fname, nline))
    if not os.path.isabs(remote_path):
        fname = str(inspect.stack()[1][1])
        nline = str(inspect.stack()[1][2])
        abort('rsync: remote path \'%s\' must be absolute in file %s line %s' % (remote_path, fname, nline))
    # ssh keys
    ssh_keys = ""
    keys = key_filenames()
    if keys:
        ssh_keys = " -i " + " -i ".join(keys)
    # ssh port
    user, host, port = normalize(env.host_string)
    ssh_port = "-p %s" % port
    # ssh options
    ssh_options = "-e 'ssh %s%s'" % (ssh_port, ssh_keys)
    # rsync options
    rsync_options = '-aH --stats --force --timeout=600 %s %s --' % (ssh_options, extra_rsync_options)
    # remote_prefix
    if host.count(':') > 1:
        # Square brackets are mandatory for IPv6 rsync address,
        # even if port number is not specified
        remote_prefix = "%s@[%s]" % (user, host)
    else:
        remote_prefix = "%s@%s" % (user, host)
    # execute command
    command = "rsync %s %s %s:%s" % (rsync_options, local_abs_path, remote_prefix, remote_path)
    with settings(fabric.api.hide('everything')):
        stdout = local(command, capture=True)
    zero_transfer_regexp = re.compile(r'^Total transferred file size: 0 bytes$')
    changed = True
    for line in stdout.split('\n'):
        line = line.strip()
        if zero_transfer_regexp.match(line):
            changed = False
            break
    return changed
Example #10
0
 def test_just_ssh_config(self):
     """
     No env.key_filename + valid ssh_config = ssh value
     """
     with settings(use_ssh_config=True, ssh_config_path=support("ssh_config")):
         for val in ["", []]:
             with settings(key_filename=val):
                 eq_(key_filenames(), ["foobar.pub"])
Example #11
0
 def test_just_ssh_config(self):
     """
     No env.key_filename + valid ssh_config = ssh value
     """
     with settings(use_ssh_config=True, ssh_config_path=support("ssh_config")):
         for val in ["", []]:
             with settings(key_filename=val):
                 eq_(key_filenames(), ["foobar.pub"])
Example #12
0
 def test_specific_host(self):
     """
     SSH lookup aspect should correctly select per-host value
     """
     with settings(use_ssh_config=True,
                   ssh_config_path=support("ssh_config"),
                   host_string="myhost"):
         eq_(key_filenames(), ["neighbor.pub"])
Example #13
0
 def test_specific_host(self):
     """
     SSH lookup aspect should correctly select per-host value
     """
     with settings(
         use_ssh_config=True,
         ssh_config_path=support("ssh_config"),
         host_string="myhost"
     ):
         eq_(key_filenames(), ["neighbor.pub"])
def ssh(ssh_opts='', remote_cmd=None):
    """
    This is the default ssh function for accessing various environments.
    It handles connections that require a gateway like Skyscape.
    You can optionally pass ssh options `ssh_opts` e.g
    `ssh_opts =' -o StrictHostKeyChecking=no'`
    The `remote_cmd` parameter allows you to run an interactive command against
    a remote host, for example, you can use nsenter to run a shell inside
    a docker container as follows:
    `remote_cmd = 'nsenter --target $PID --mount --uts --ipc --net --pid'`

    """
    # Keys
    key_string = ''
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)

    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port

    # Proxy
    proxy_string = ''
    if env.gateway:
        gw_user, gw_host, gw_port = normalize(env.gateway)
        gw_port_string = "-p %s" % gw_port
        if '@' in env.gateway:
            gw_user_host_string = env.gateway
        elif "gateway_user" in env and env.gateway_user:
            gw_user_host_string = ssh_host_string(env.gateway_user, env.gateway)
        else:
            gw_user_host_string = ssh_host_string(user, env.gateway)
        proxy_string = '-o "ProxyCommand ssh {key_string} {ssh_opts} {gw_port_string} {gw_user_host_string} nc %h %p"'.format(
            key_string=key_string,
            ssh_opts=ssh_opts,
            gw_port_string=gw_port_string,
            gw_user_host_string=gw_user_host_string)

    cmd = "ssh -A -o 'ServerAliveInterval 30' {key_string} {ssh_opts} {port_string} {user_host_string} {proxy_string}".format(
        key_string=key_string,
        ssh_opts=ssh_opts,
        port_string=port_string,
        user_host_string=ssh_host_string(user, host),
        proxy_string=proxy_string)

    # Sometimes we want to run an interactive command against a remote host, for
    # example nsenter to run a shell inside a running docker container
    if remote_cmd is not None:
        cmd = "{} '{}'".format(cmd, remote_cmd)

    return local(cmd)
Example #15
0
def ssh(ssh_opts='', remote_cmd=None):
    """
    This is the default ssh function for accessing various environments.
    It handles connections that require a gateway like Skyscape.
    You can optionally pass ssh options `ssh_opts` e.g
    `ssh_opts =' -o StrictHostKeyChecking=no'`
    The `remote_cmd` parameter allows you to run an interactive command against
    a remote host, for example, you can use nsenter to run a shell inside
    a docker container as follows:
    `remote_cmd = 'nsenter --target $PID --mount --uts --ipc --net --pid'`

    """
    # Keys
    key_string = ''
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)

    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port

    # Proxy
    proxy_string = ''
    if env.gateway:
        gw_user, gw_host, gw_port = normalize(env.gateway)
        gw_port_string = "-p %s" % gw_port
        if '@' in env.gateway:
            gw_user_host_string = env.gateway
        elif "gateway_user" in env and env.gateway_user:
            gw_user_host_string = ssh_host_string(env.gateway_user, env.gateway)
        else:
            gw_user_host_string = ssh_host_string(user, env.gateway)
        proxy_string = '-o "ProxyCommand ssh {key_string} {ssh_opts} {gw_port_string} {gw_user_host_string} nc %h %p"'.format(
            key_string=key_string,
            ssh_opts=ssh_opts,
            gw_port_string=gw_port_string,
            gw_user_host_string=gw_user_host_string)

    cmd = "ssh -A -o 'ServerAliveInterval 30' {key_string} {ssh_opts} {port_string} {user_host_string} {proxy_string}".format(
        key_string=key_string,
        ssh_opts=ssh_opts,
        port_string=port_string,
        user_host_string=ssh_host_string(user, host),
        proxy_string=proxy_string)

    # Sometimes we want to run an interactive command against a remote host, for
    # example nsenter to run a shell inside a running docker container
    if remote_cmd is not None:
        cmd = "{} '{}'".format(cmd, remote_cmd)

    return local(cmd)
Example #16
0
def rsync_project(remote_dir, local_dir=None, exclude=(), delete=False, extra_opts="", ssh_opts="", capture=False):
    """
    Synchronize a remote directory with the current project directory via rsync.

    Where ``upload_project()`` makes use of ``scp`` to copy one's entire
    project every time it is invoked, ``rsync_project()`` uses the ``rsync``
    command-line utility, which only transfers files newer than those on the
    remote end.

    ``rsync_project()`` is thus a simple wrapper around ``rsync``; for
    details on how ``rsync`` works, please see its manpage. ``rsync`` must be
    installed on both your local and remote systems in order for this operation
    to work correctly.

    This function makes use of Fabric's ``local()`` operation, and returns the
    output of that function call; thus it will return the stdout, if any, of
    the resultant ``rsync`` call.

    ``rsync_project()`` takes the following parameters:

    * ``remote_dir``: the only required parameter, this is the path to the
      directory on the remote server. Due to how ``rsync`` is implemented, the
      exact behavior depends on the value of ``local_dir``:

        * If ``local_dir`` ends with a trailing slash, the files will be
          dropped inside of ``remote_dir``. E.g.
          ``rsync_project("/home/username/project", "foldername/")`` will drop
          the contents of ``foldername`` inside of ``/home/username/project``.
        * If ``local_dir`` does **not** end with a trailing slash (and this
          includes the default scenario, when ``local_dir`` is not specified),
          ``remote_dir`` is effectively the "parent" directory, and a new
          directory named after ``local_dir`` will be created inside of it. So
          ``rsync_project("/home/username", "foldername")`` would create a new
          directory ``/home/username/foldername`` (if needed) and place the
          files there.

    * ``local_dir``: by default, ``rsync_project`` uses your current working
      directory as the source directory. This may be overridden by specifying
      ``local_dir``, which is a string passed verbatim to ``rsync``, and thus
      may be a single directory (``"my_directory"``) or multiple directories
      (``"dir1 dir2"``). See the ``rsync`` documentation for details.
    * ``exclude``: optional, may be a single string, or an iterable of strings,
      and is used to pass one or more ``--exclude`` options to ``rsync``.
    * ``delete``: a boolean controlling whether ``rsync``'s ``--delete`` option
      is used. If True, instructs ``rsync`` to remove remote files that no
      longer exist locally. Defaults to False.
    * ``extra_opts``: an optional, arbitrary string which you may use to pass
      custom arguments or options to ``rsync``.
    * ``ssh_opts``: Like ``extra_opts`` but specifically for the SSH options
      string (rsync's ``--rsh`` flag.)
    * ``capture``: Sent directly into an inner `~fabric.operations.local` call.

    Furthermore, this function transparently honors Fabric's port and SSH key
    settings. Calling this function when the current host string contains a
    nonstandard port, or when ``env.key_filename`` is non-empty, will use the
    specified port and/or SSH key filename(s).

    For reference, the approximate ``rsync`` command-line call that is
    constructed by this function is the following::

        rsync [--delete] [--exclude exclude[0][, --exclude[1][, ...]]] \\
            -pthrvz [extra_opts] <local_dir> <host_string>:<remote_dir>

    .. versionadded:: 1.4.0
        The ``ssh_opts`` keyword argument.
    .. versionadded:: 1.4.1
        The ``capture`` keyword argument.
    """
    # Turn single-string exclude into a one-item list for consistency
    if not hasattr(exclude, "__iter__"):
        exclude = (exclude,)
    # Create --exclude options from exclude list
    exclude_opts = ' --exclude "%s"' * len(exclude)
    # Double-backslash-escape
    exclusions = tuple([str(s).replace('"', '\\\\"') for s in exclude])
    # Honor SSH key(s)
    key_string = ""
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)
    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port
    # RSH
    rsh_string = ""
    rsh_parts = [key_string, port_string, ssh_opts]
    if any(rsh_parts):
        rsh_string = "--rsh='ssh %s'" % " ".join(rsh_parts)
    # Set up options part of string
    options_map = {
        "delete": "--delete" if delete else "",
        "exclude": exclude_opts % exclusions,
        "rsh": rsh_string,
        "extra": extra_opts,
    }
    options = "%(delete)s%(exclude)s -pthrvz %(extra)s %(rsh)s" % options_map
    # Get local directory
    if local_dir is None:
        local_dir = "../" + getcwd().split(sep)[-1]
    # Create and run final command string
    cmd = "rsync %s %s %s@%s:%s" % (options, local_dir, user, host, remote_dir)
    if output.running:
        print("[%s] rsync_project: %s" % (env.host_string, cmd))
    return local(cmd, capture=capture)
Example #17
0
def _get_rsync(files, source='/', target='/', exclude=None, arguments=None):
    with NamedTemporaryFile() as files_file:
        with NamedTemporaryFile() as exclude_file:
            cmd = ['rsync']

            ##
            # Fix or set default files
            if isinstance(files, str) or isinstance(files, unicode):
                files = [files]
            elif files is None:
                files = []

            ##
            # Fix or set default exclude
            if isinstance(exclude, str) or isinstance(exclude, unicode):
                exclude = [exclude]
            elif exclude is None:
                exclude = []

            ##
            # Set common prefix to avoid rsync with root ('/') when possible
            prefix = os.path.commonprefix(files + exclude)
            if not os.path.exists(prefix):
                prefix = os.path.dirname(prefix)
            if os.path.isdir(prefix) and not prefix.endswith(os.path.sep):
                prefix += os.path.sep
            if (prefix.startswith(target.split(':')[-1])
                    and prefix.startswith(target.split(':')[-1])):
                source = ':'.join(source.split(':')[:-1] + [prefix])
                target = ':'.join(target.split(':')[:-1] + [prefix])
            else:
                prefix = ''

            ##
            # Build files-from
            for line in (files or []):
                if line[len(prefix):]:
                    files_file.write(line[len(prefix):] + '\n')
                files_file.flush()
            files_file.seek(0)
            if files and files_file.read():
                cmd.append('--files-from={0:s}'.format(files_file.name))

            ##
            # Build exclude-from
            for line in (exclude or []):
                if line[len(prefix):]:
                    exclude_file.write(line[len(prefix):] + '\n')
                exclude_file.flush()
            exclude_file.seek(0)
            if exclude and exclude_file.read():
                cmd.append('--exclude-from={0:s}'.format(exclude_file.name))

            ##
            # -p preserve permissions
            # -t preserve modification times
            # -h output numbers in a human-readable format
            # -l copy symlinks as symlinks
            # -r recurse into directories
            # -z compress file data during the transfer
            cmd.append('-pthlrz')

            if arguments:
                cmd.append(arguments)

            ##
            # Build rsh
            if any([api.env.port, key_filenames()]):
                cmd.append('--rsh="ssh {0:s}"'.format(' '.join(filter(bool, [
                    # api.env.port
                    api.env.port and '-p {0:s}'.format(api.env.port) or None,
                    # key_filenames
                    ' '.join(['-i {0:s}'.format(key)
                              for key in key_filenames()])
                ]))))

            cmd.append(source)
            cmd.append(target)

            yield ' '.join(cmd)
Example #18
0
def rsync_project(remote_dir, local_dir=None, exclude=(), delete=False,
    extra_opts='', ssh_opts='', capture=False):
    """
    Synchronize a remote directory with the current project directory via rsync.

    Where ``upload_project()`` makes use of ``scp`` to copy one's entire
    project every time it is invoked, ``rsync_project()`` uses the ``rsync``
    command-line utility, which only transfers files newer than those on the
    remote end.

    ``rsync_project()`` is thus a simple wrapper around ``rsync``; for
    details on how ``rsync`` works, please see its manpage. ``rsync`` must be
    installed on both your local and remote systems in order for this operation
    to work correctly.

    This function makes use of Fabric's ``local()`` operation, and returns the
    output of that function call; thus it will return the stdout, if any, of
    the resultant ``rsync`` call.

    ``rsync_project()`` takes the following parameters:

    * ``remote_dir``: the only required parameter, this is the path to the
      directory on the remote server. Due to how ``rsync`` is implemented, the
      exact behavior depends on the value of ``local_dir``:

        * If ``local_dir`` ends with a trailing slash, the files will be
          dropped inside of ``remote_dir``. E.g.
          ``rsync_project("/home/username/project", "foldername/")`` will drop
          the contents of ``foldername`` inside of ``/home/username/project``.
        * If ``local_dir`` does **not** end with a trailing slash (and this
          includes the default scenario, when ``local_dir`` is not specified),
          ``remote_dir`` is effectively the "parent" directory, and a new
          directory named after ``local_dir`` will be created inside of it. So
          ``rsync_project("/home/username", "foldername")`` would create a new
          directory ``/home/username/foldername`` (if needed) and place the
          files there.

    * ``local_dir``: by default, ``rsync_project`` uses your current working
      directory as the source directory. This may be overridden by specifying
      ``local_dir``, which is a string passed verbatim to ``rsync``, and thus
      may be a single directory (``"my_directory"``) or multiple directories
      (``"dir1 dir2"``). See the ``rsync`` documentation for details.
    * ``exclude``: optional, may be a single string, or an iterable of strings,
      and is used to pass one or more ``--exclude`` options to ``rsync``.
    * ``delete``: a boolean controlling whether ``rsync``'s ``--delete`` option
      is used. If True, instructs ``rsync`` to remove remote files that no
      longer exist locally. Defaults to False.
    * ``extra_opts``: an optional, arbitrary string which you may use to pass
      custom arguments or options to ``rsync``.
    * ``ssh_opts``: Like ``extra_opts`` but specifically for the SSH options
      string (rsync's ``--rsh`` flag.)
    * ``capture``: Sent directly into an inner `~fabric.operations.local` call.

    Furthermore, this function transparently honors Fabric's port and SSH key
    settings. Calling this function when the current host string contains a
    nonstandard port, or when ``env.key_filename`` is non-empty, will use the
    specified port and/or SSH key filename(s).

    For reference, the approximate ``rsync`` command-line call that is
    constructed by this function is the following::

        rsync [--delete] [--exclude exclude[0][, --exclude[1][, ...]]] \\
            -pthrvz [extra_opts] <local_dir> <host_string>:<remote_dir>

    .. versionadded:: 1.4.0
        The ``ssh_opts`` keyword argument.
    .. versionadded:: 1.4.1
        The ``capture`` keyword argument.
    """
    # Turn single-string exclude into a one-item list for consistency
    if not hasattr(exclude, '__iter__'):
        exclude = (exclude,)
    # Create --exclude options from exclude list
    exclude_opts = ' --exclude "%s"' * len(exclude)
    # Double-backslash-escape
    exclusions = tuple([str(s).replace('"', '\\\\"') for s in exclude])
    # Honor SSH key(s)
    key_string = ""
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)
    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port
    # RSH
    rsh_string = ""
    rsh_parts = [key_string, port_string, ssh_opts]
    if any(rsh_parts):
        rsh_string = "--rsh='ssh %s'" % " ".join(rsh_parts)
    # Set up options part of string
    options_map = {
        'delete': '--delete' if delete else '',
        'exclude': exclude_opts % exclusions,
        'rsh': rsh_string,
        'extra': extra_opts
    }
    options = "%(delete)s%(exclude)s -pthrvz %(extra)s %(rsh)s" % options_map
    # Get local directory
    if local_dir is None:
        local_dir = '../' + getcwd().split(sep)[-1]
    # Create and run final command string
    cmd = "rsync %s %s %s@%s:%s" % (options, local_dir, user, host, remote_dir)
    if output.running:
        print("[%s] rsync_project: %s" % (env.host_string, cmd))
    return local(cmd, capture=capture)
Example #19
0
def rsync(source, target, exclude=(), delete=False, strict_host_keys=True,
    rsync_opts='', ssh_opts=''):
    """
    Convenient wrapper around your friendly local ``rsync``.

    Specifically, it calls your local ``rsync`` program via a subprocess, and
    fills in its arguments with Fabric's current target host/user/port. It
    provides Python level keyword arguments for some common rsync options, and
    allows you to specify custom options via a string if required (see below.)

    For details on how ``rsync`` works, please see its manpage. ``rsync`` must
    be installed on both your local and remote systems in order for this
    function to work correctly.

    This function makes use of Fabric's ``local()`` function and returns its
    output; thus it will exhibit ``failed``/``succeeded``/``stdout``/``stderr``
    attributes, behaves like a string consisting of ``stdout``, and so forth.

    ``rsync()`` takes the following parameters:

    * ``source``: The local location to copy from. Actually a string passed
      verbatim to ``rsync``, and thus may be a single directory
      (``"my_directory"``) or multiple directories (``"dir1 dir2"``). See the
      ``rsync`` documentation for details.
    * ``target``: The path to sync with on the remote server. Due to how
      ``rsync`` is implemented, the exact behavior depends on the value of
      ``source``:

        * If ``source`` ends with a trailing slash, the files will be
          dropped inside of ``target``. E.g.
          ``rsync("foldername/", "/home/username/project")`` will drop
          the contents of ``foldername`` inside of ``/home/username/project``.
        * If ``source`` does **not** end with a trailing slash,
          ``target`` is effectively the "parent" directory, and a new
          directory named after ``source`` will be created inside of it. So
          ``rsync("foldername", "/home/username")`` would create a new
          directory ``/home/username/foldername`` (if needed) and place the
          files there.

    * ``exclude``: optional, may be a single string, or an iterable of strings,
      and is used to pass one or more ``--exclude`` options to ``rsync``.
    * ``delete``: a boolean controlling whether ``rsync``'s ``--delete`` option
      is used. If True, instructs ``rsync`` to remove remote files that no
      longer exist locally. Defaults to False.
    * ``strict_host_keys``: Boolean determining whether to enable/disable the
      SSH-level option ``StrictHostKeyChecking`` (useful for
      frequently-changing hosts such as virtual machines or cloud instances.)
      Defaults to True.
    * ``rsync_opts``: an optional, arbitrary string which you may use to pass
      custom arguments or options to ``rsync``.
    * ``ssh_opts``: Like ``rsync_opts`` but specifically for the SSH options
      string (rsync's ``--rsh`` flag.)

    This function transparently honors Fabric's port and SSH key
    settings. Calling this function when the current host string contains a
    nonstandard port, or when ``env.key_filename`` is non-empty, will use the
    specified port and/or SSH key filename(s).

    For reference, the approximate ``rsync`` command-line call that is
    constructed by this function is the following::

        rsync [--delete] [--exclude exclude[0][, --exclude[1][, ...]]] \\
            -pthrvz [rsync_opts] <source> <host_string>:<target>

    """
    # Turn single-string exclude into a one-item list for consistency
    if not hasattr(exclude, '__iter__'):
        exclude = (exclude,)
    # Create --exclude options from exclude list
    exclude_opts = ' --exclude "%s"' * len(exclude)
    # Double-backslash-escape
    exclusions = tuple([str(s).replace('"', '\\\\"') for s in exclude])
    # Honor SSH key(s)
    key_string = ""
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)
    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port
    # Remote shell (SSH) options
    rsh_string = ""
    # Strict host key checking
    disable_keys = '-o StrictHostKeyChecking=no'
    if not strict_host_keys and disable_keys not in ssh_opts:
        ssh_opts += ' %s' % disable_keys
    rsh_parts = [key_string, port_string, ssh_opts]
    if any(rsh_parts):
        rsh_string = "--rsh='ssh %s'" % " ".join(rsh_parts)
    # Set up options part of string
    options_map = {
        'delete': '--delete' if delete else '',
        'exclude': exclude_opts % exclusions,
        'rsh': rsh_string,
        'extra': rsync_opts
    }
    options = "%(delete)s%(exclude)s -pthrvz %(extra)s %(rsh)s" % options_map
    # Create and run final command string
    if env.host.count(':') > 1:
        # Square brackets are mandatory for IPv6 rsync address,
        # even if port number is not specified
        cmd = "rsync %s %s [%s@%s]:%s" % (options, source, user, host, target)
    else:
        cmd = "rsync %s %s %s@%s:%s" % (options, source, user, host, target)
    return local(cmd)
Example #20
0
def rsync_project(
    remote_dir,
    local_dir=None,
    exclude=(),
    delete=False,
    extra_opts='',
    ssh_opts='',
    capture=False,
    upload=True,
    default_opts='-pthrvz',
    sshpass=False
):
    """
    Synchronize a remote directory with the current project directory via rsync.

    Where ``upload_project()`` makes use of ``scp`` to copy one's entire
    project every time it is invoked, ``rsync_project()`` uses the ``rsync``
    command-line utility, which only transfers files newer than those on the
    remote end.

    ``rsync_project()`` is thus a simple wrapper around ``rsync``; for
    details on how ``rsync`` works, please see its manpage. ``rsync`` must be
    installed on both your local and remote systems in order for this operation
    to work correctly.

    This function makes use of Fabric's ``local()`` operation, and returns the
    output of that function call; thus it will return the stdout, if any, of
    the resultant ``rsync`` call.

    ``rsync_project()`` takes the following parameters:

    * ``remote_dir``: the only required parameter, this is the path to the
      directory on the remote server. Due to how ``rsync`` is implemented, the
      exact behavior depends on the value of ``local_dir``:

        * If ``local_dir`` ends with a trailing slash, the files will be
          dropped inside of ``remote_dir``. E.g.
          ``rsync_project("/home/username/project", "foldername/")`` will drop
          the contents of ``foldername`` inside of ``/home/username/project``.
        * If ``local_dir`` does **not** end with a trailing slash (and this
          includes the default scenario, when ``local_dir`` is not specified),
          ``remote_dir`` is effectively the "parent" directory, and a new
          directory named after ``local_dir`` will be created inside of it. So
          ``rsync_project("/home/username", "foldername")`` would create a new
          directory ``/home/username/foldername`` (if needed) and place the
          files there.

    * ``local_dir``: by default, ``rsync_project`` uses your current working
      directory as the source directory. This may be overridden by specifying
      ``local_dir``, which is a string passed verbatim to ``rsync``, and thus
      may be a single directory (``"my_directory"``) or multiple directories
      (``"dir1 dir2"``). See the ``rsync`` documentation for details.
    * ``exclude``: optional, may be a single string, or an iterable of strings,
      and is used to pass one or more ``--exclude`` options to ``rsync``.
    * ``delete``: a boolean controlling whether ``rsync``'s ``--delete`` option
      is used. If True, instructs ``rsync`` to remove remote files that no
      longer exist locally. Defaults to False.
    * ``extra_opts``: an optional, arbitrary string which you may use to pass
      custom arguments or options to ``rsync``.
    * ``ssh_opts``: Like ``extra_opts`` but specifically for the SSH options
      string (rsync's ``--rsh`` flag.)
    * ``capture``: Sent directly into an inner `~fabric.operations.local` call.
    * ``upload``: a boolean controlling whether file synchronization is
      performed up or downstream. Upstream by default.
    * ``default_opts``: the default rsync options ``-pthrvz``, override if
      desired (e.g. to remove verbosity, etc).
    * ``sshpass``: a boolean controlling whether use ``sshpass``, If True, you
      can support ``env.password`` to ``rsync``, no need input password. But
      you must install ``sshpass``.
        * If you use ubuntu, Type the following command:
             ``sudo apt-get install sshpass``
        * Other platform, Go ``http://sourceforge.net/projects/sshpass/``
          donwload the lastest verison, then type the following command:
          ``tar zxvf sshpass-1.05.tar.gz && cd sshpass-1.05 && ./configure &&
            make && sudo make install``


    Furthermore, this function transparently honors Fabric's port and SSH key
    settings. Calling this function when the current host string contains a
    nonstandard port, or when ``env.key_filename`` is non-empty, will use the
    specified port and/or SSH key filename(s).

    For reference, the approximate ``rsync`` command-line call that is
    constructed by this function is the following::

        rsync [--delete] [--exclude exclude[0][, --exclude[1][, ...]]] \\
            [default_opts] [extra_opts] <local_dir> <host_string>:<remote_dir>

    .. versionadded:: 1.4.0
        The ``ssh_opts`` keyword argument.
    .. versionadded:: 1.4.1
        The ``capture`` keyword argument.
    .. versionadded:: 1.8.0
        The ``default_opts`` keyword argument.
    """
    # Turn single-string exclude into a one-item list for consistency
    if not hasattr(exclude, '__iter__'):
        exclude = (exclude,)
    # Create --exclude options from exclude list
    exclude_opts = ' --exclude "%s"' * len(exclude)
    # Double-backslash-escape
    exclusions = tuple([str(s).replace('"', '\\\\"') for s in exclude])
    # Honor SSH key(s)
    key_string = ""
    keys = key_filenames()
    if keys:
        key_string = "-i " + " -i ".join(keys)
    # Port
    user, host, port = normalize(env.host_string)
    port_string = "-p %s" % port
    # RSH
    rsh_string = ""
    rsh_parts = [key_string, port_string, ssh_opts]
    gate_pass = None
    if any(rsh_parts):
        # when have gateway
        if env.gateway:
            guser, ghost, gport = normalize(env.gateway)
            gate_pass = env.passwords[env.gateway]
            rsh_parts.append(
                "%s sshpass -p %s ssh -p %s" % (ghost, env.password, gport))
        rsh_string = "--rsh='ssh %s'" % " ".join(rsh_parts)

    # Set up options part of string
    options_map = {
        'delete': '--delete' if delete else '',
        'exclude': exclude_opts % exclusions,
        'rsh': rsh_string,
        'default': default_opts,
        'extra': extra_opts,
    }
    options = "%(delete)s%(exclude)s %(default)s %(extra)s %(rsh)s" % options_map
    # Get local directory
    if local_dir is None:
        local_dir = '../' + getcwd().split(sep)[-1]
    # Create and run final command string
    if host.count(':') > 1:
        # Square brackets are mandatory for IPv6 rsync address,
        # even if port number is not specified
        remote_prefix = "[%s@%s]" % (user, host)
    else:
        remote_prefix = "%s@%s" % (user, host)
    if upload:
        cmd = "rsync %s %s %s:%s" % (options, local_dir, remote_prefix, remote_dir)
    else:
        cmd = "rsync %s %s:%s %s" % (options, remote_prefix, remote_dir, local_dir)
    if sshpass:
        cmd = "sshpass -p %s %s" % (gate_pass if gate_pass else env.password, cmd)
    if output.running:
        print("[%s] rsync_project: %s" % (env.host_string, cmd))
    return local(cmd, capture=capture)