84

I'm having trouble with Fabric not recognizing hosts that I have in ~/.ssh/config.

My fabfile.py is as follows:

from fabric.api import run, env

env.hosts = ['lulu']

def whoami():
    run('whoami')

Running $ fab whoami gives:

[lulu] run: whoami

Fatal error: Name lookup failed for lulu

The name lulu is in my ~/.ssh/config, like this:

Host lulu
     hostname 192.168.100.100
     port 2100
     IdentityFile ~/.ssh/lulu-key

My first thought to solving this is adding something like lulu.lulu to /etc/hosts (I'm on a Mac), but then I have to also pass in the identity file to Fabric - and I'd rather keep my authentication (i.e. ~/.ssh/config) separate from my deployment (i.e. fabfile.py).

As well, incidentally, if you try to connect to a host in the hosts file, fabric.contrib.projects.rsync_project doesn't seem to acknowledge 'ports' in the hosts.env (i.e. if you use hosts.env = [lulu:2100] a call to rsync_project seems to try connecting to lulu:21).

Is there a reason Fabric doesn't recognize this lulu name?

JuanPablo
  • 23,792
  • 39
  • 118
  • 164
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343

4 Answers4

146

Since version 1.4.0, Fabric uses your ssh config (partly). However, you need to explicitly enable it, with

env.use_ssh_config = True

somewhere near the top of your fabfile. Once you do this, Fabric should read your ssh config (from ~/.ssh/config by default, or from env.ssh_config_path).

One warning: if you use a version older than 1.5.4, an abort will occur if env.use_ssh_config is set but there is no config file present. In that case, you can use a workaround like:

if env.ssh_config_path and os.path.isfile(os.path.expanduser(env.ssh_config_path)):
    env.use_ssh_config = True
tshepang
  • 12,111
  • 21
  • 91
  • 136
rbp
  • 43,594
  • 3
  • 38
  • 31
  • The answer originally started with "The accepted answer is outdated" [since it documented pre-1.4.0 behaviour]. I've removed this preface, since my answer was accepted instead ;) Thanks! – rbp Mar 13 '12 at 14:32
9

Note that this also happens when the name is not in /etc/hosts. I had the same problem and had to add the host name to both that file and ~/.ssh/config.

tshepang
  • 12,111
  • 21
  • 91
  • 136
MrD
  • 2,405
  • 3
  • 22
  • 23
5

update: This Answer is now outdated.


Fabric doesn't have support currently for the .ssh/config file. You can set these up in a function to then call on the cli, eg: fab production task; where production sets the username, hostname, port, and ssh identity.

As for rsync project, that should now have port setting ability, if not, you can always run local("rsync ...") as that is essentially what that contributed function does.

Community
  • 1
  • 1
Morgan
  • 4,143
  • 27
  • 35
  • 1
    Set env.key_filename to the full path of the private key. Also if you run into trouble, see http://code.fabfile.org/issues/show/265 for a few issues around this. – tobych Aug 17 '11 at 22:34
  • 1
    Yup, it's got support now. (though it was pre 1.0 when I commented) Noting for later readers. – Morgan Mar 28 '12 at 04:46
4

One can use following code to read the config (original code taken from: http://markpasc.typepad.com/blog/2010/04/loading-ssh-config-settings-for-fabric.html):

from fabric.api import *
env.hosts = ['servername']

def _annotate_hosts_with_ssh_config_info():
    from os.path import expanduser
    from paramiko.config import SSHConfig

    def hostinfo(host, config):
        hive = config.lookup(host)
        if 'hostname' in hive:
            host = hive['hostname']
        if 'user' in hive:
            host = '%s@%s' % (hive['user'], host)
        if 'port' in hive:
            host = '%s:%s' % (host, hive['port'])
        return host

    try:
        config_file = file(expanduser('~/.ssh/config'))
    except IOError:
        pass
    else:
        config = SSHConfig()
        config.parse(config_file)
        keys = [config.lookup(host).get('identityfile', None)
            for host in env.hosts]
        env.key_filename = [expanduser(key) for key in keys if key is not None]
        env.hosts = [hostinfo(host, config) for host in env.hosts]

        for role, rolehosts in env.roledefs.items():
            env.roledefs[role] = [hostinfo(host, config) for host in rolehosts]

_annotate_hosts_with_ssh_config_info()
jmu
  • 3,619
  • 1
  • 23
  • 12
  • 1
    fabric 1.2+ uses `ssh` library (`paramiko` fork): `try: \n from ssh.config import SSHConfig \n except ImportError: \n from paramiko.config import SSHConfig` – jfs Dec 19 '11 at 12:55
  • SSH library has been merged back into Paramiko, e.g. `from paramiko.config import SSHConfig` – n8henrie Sep 28 '15 at 17:48