开发者

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 an开发者_开发技巧d paths etc.

Why doesn't this work:

from fabric.api import run

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

if __name__ == '__main__':
    host_type()    


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...


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.


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'])


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()


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()


# 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()


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


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()


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__])


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜