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.
精彩评论