Tal Weiss
Tal Weiss

Reputation: 8999

Stand-alone fabfile for fabric?

Is it possible to make the fabfile stand-alone?
I'm not very fond of running the external tool 'fab'. If I manage to get the fabfile standalone I can run the file from within the (Eclipse / Pydev) IDE, easily debug it, use project configurations and paths etc.
Why doesn't this work:

from fabric.api import run

def host_type():
    run('uname -s')

if __name__ == '__main__':
    host_type()    

Upvotes: 9

Views: 4635

Answers (10)

Wernight
Wernight

Reputation: 37658

I found the solution indirectly on run fabric file renamed other than fabfile.py and password-less ssh

def deploy():
    ...

if __name__ == '__main__':
    from fabric import execute
    execute(deploy)

This way also work if your file doesn't have .py extension.

Upvotes: 0

anatoly techtonik
anatoly techtonik

Reputation: 20539

This is my modified version of Greg's answer that changes default behavior to show available commands instead of listing tons of fabric options.

if __name__ == '__main__':
    # imports for standalone mode only
    import sys, fabric.main

    # show available commands by default
    if not sys.argv[1:]:
        sys.argv.append('--list')

    fabric.main.main(fabfile_locations=[__file__])

Upvotes: 0

Greg
Greg

Reputation: 5588

Since 1.5.0 there is a way better way to do this than messing around with argv.

import fabric.main

def main():
    fabric.main.main(fabfile_locations=[__file__])

if __name__ == "__main__":
    main()

This can also be utilized as a console script in setup.py

Upvotes: 2

user1971573
user1971573

Reputation: 21

Add this to the bottom of your fab file.

if __name__ == '__main__':
  from fabric.main import main
  import sys

  sys.argv = ['fab', '-f', __file__] + sys.argv[1:]

  main()

Upvotes: 1

Jon
Jon

Reputation: 21

# thanks to aaron, extending his code a little more
# here is a little bit more of a graceful solution than running subprocess.call, and specifying multiple hosts

from fabric.api import env, run

def main():
    run("uname -a")

def setup():
    env.hosts = ['host0','host1']

if __name__ == '__main__':
    setup()
    for host in env.hosts:
        env.host_string = host
        main()

Upvotes: 2

Aaron Robinson
Aaron Robinson

Reputation: 416

docs.fabfile.org/en/1.4.0/usage/library.html

"As that section mentions, the key is simply that run, sudo and the other operations only look in one place when connecting: env.host_string . All of the other mechanisms for setting hosts are interpreted by the fab tool when it runs, and don’t matter when running as a library."

I was looking at this same problem when I found this. Also, while looking I recall mention that when used in a fabfile, env changes should not be in the same def as run, sudo. Who knows if this still applies when used in "library" mode.

EDIT: Here is an example of said implementation

from fabric.api import env, run

def main():
    run("uname -a")

def setup():
    env.host_string = "me@remoteHost"

if __name__ == '__main__':
    setup()
    main()

Upvotes: 2

bfschott
bfschott

Reputation: 176

I fine tuned the above example to past through argv arguments you might want to pass to local commands and specify an optional default_commands list instead of a hard coded command name. Note, the filename must have a .py extension or fab will not detect it as a fab file!

#!/usr/bin/env python
from fabric.api import local

default_commands = ['hello', ]

def hello():
    print ('hello world')

def hostname():
    local('hostname')

if __name__ == '__main__':
   import sys
   from fabric.main import main
   sys.argv = ['fab', '-f', __file__,] +  default_commands + sys.argv[1:]
   main()

Upvotes: 2

Tal Weiss
Tal Weiss

Reputation: 8999

I eventually found the solution (and it is really simple!).
In my fabfile, I added:

from fabric.main import main

if __name__ == '__main__':
    import sys
    sys.argv = ['fab', '-f', __file__, 'update_server']
    main()

I hope this helps people...

Upvotes: 14

AdmiralNemo
AdmiralNemo

Reputation: 1321

If I recall correctly, I couldn't get the Fabric API to do what I wanted either. I decided to abandon the extra layer entirely and use Paramiko (the underlying SSH library used by Fabric) directly:

import os
import paramiko

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('hostname.example.com', 22, 'username', 'password')
ssh.save_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
stdin, stdout, stderr = ssh.exec_command('uname -s')
print stdout.read()

While there are a few more steps involved, doing it this way allows you to leverage the SSH layer directly, as opposed to using subprocess to spwan another Python instance, or figuring out the Fabric API. I have several projects, both web- and console- using Paramiko in this manner and I haven't had too much trouble.

Paramiko is extensively documented.

Upvotes: 3

muksie
muksie

Reputation: 13053

This isn't a really nice solution, but will work:

import subprocess

def hello():
    print 'Hello'

if __name__ == '__main__':
    subprocess.call(['fab', '-f', __file__, 'hello'])

Upvotes: 2

Related Questions