In fabric2's task definition, how do I get the command line argument "-H --hosts"? Since I need to create connection myself.
eg.
fab -H web1,db1 task1
#task
def task1(c):
# HOW TO GET 'web1,db1' HERE???
with Connection(host=???, connect_kwargs={}) as conn:
conn.put('a','b')
do_my_stuff()
After a short while I figured it out.
#task
def task1(ctx):
with Connection(host=ctx.host, connect_kwargs={}) as c:
c.put('a', '/tmp/a')
Related
Python 3.8.2, Fabric 2.5.0, Paramiko 2.7.2, Invoke 1.4.1
Hello,
I have a fabfile which needs to handle hosts passed at the command-line (using -H) and hosts defined in the fabfile if -H was not passed. Here's an example of the issue I'm facing:
target_group = None
#task
def prod(c):
_env_handler(c, "prod")
def _env_handler(c, env_name):
global target_group
if not hasattr(c, 'host'):
target_group = Group("somehost1.tld", "somehost2.tld")
#task(hosts=target_group)
def test(c):
print(c)
If I run fab prod test:
<Context: <Config: {'run': {'asynch ...
If I run fab -H 1,2 test:
<Connection host=1>
<Connection host=2>
So, passing hosts using the #task(hosts=[...] decorator produces a c Context object, and using -H produces a c Connection object.
I know using a task (prod(c)) to wrap environment logic may be questionable...but is there a way to ensure the task (test(c)) always receives a Connection object...or am I fundamentally misunderstanding something?
Thanks.
Edit: I've also tried directly passing a hosts list (e.g. #task(hosts=["somehost1.tld", "somehost2.tld"])) with the same result.
Edit: Here's the current workaround, but it's obviously not ideal if you have a lot of tasks:
#task
def test(c):
if not hasattr(c, 'host'):
for c in target_group:
test(c)
else:
logging.info(f"Targeting {c.host}")
Workaround using a custom task decorator:
def _task_handler(func):
#task
#functools.wraps(func)
def wrapper(c):
if not hasattr(c, 'host'):
for c in target_group:
func(c)
else:
func(c)
return wrapper
Im trying to execute main task that needs to execute tasks differently for each host. In the following setup, task 'sometask' will get execute twice for each host. What is the best way to prevent that?
#task
#hosts('host1', 'host2')
def test():
execute(do_something_everywhere) # execute on both hosts
execute(sometask, 'arg1', host='host1') # execute on host1 only
execute(sometask, 'arg2', host='host2') # execute on host2 only
You can user the #runs_once decorator to remedy this but I find that can cause extra work making wrapper functions to get the execution order you want, so here's a quick fix using the env.host_string value to evaluate which server you are deploying to and adjust your script accordingly:
#hosts('host1', 'host2')
#task
def checkout(branch='master'):
execute(_test_task_w_arg, 'all-servers')
execute(_test_task_w_arg, 'arg1' if env.host_string == 'host1' else 'arg2')
def _test_task_w_arg(arg1):
local("touch test-file-" + arg1)
Results in this output which seems to achieve what you want:
[host1] Executing task 'checkout'
[localhost] local: touch test-file-all-servers
[localhost] local: touch test-file-arg1
[host2] Executing task 'checkout'
[localhost] local: touch test-file-all-servers
[localhost] local: touch test-file-arg2
I'm attempting to announce deployment start and end in my fabric script. Feels like this should be easy, but for the life of me I can't figure out how to do it.
env.hosts = ['www1', 'www2', 'www3', 'www4']
def announce_start():
# code to connect to irc server and announce deployment begins
pass
def announce_finish():
# code to connect to irc server and announce deployment finishes
pass
def deploy():
# actual deployment code here
pass
Here's what I've tried:
If I make my deploy task contain 'announce_start' and 'announce_finish'. It will attempt to run all those tasks on each server.
def deploy():
announce_start()
# actual deployment code here
announce_finish()
If I decorate announce_start() and announce_end() with #hosts('localhost'), it runs it on localhost, but still four times. One for each host.
As I was typing this, I finally got it to work by using the decorator #hosts('localhost') on announce_start/end and the fab command:
fab announce_start deploy announce_end
But this seems a bit hacky. I'd like it all wrapped in a single deploy command. Is there a way to do this?
You can use fabric.api.execute, e.g.
def announce_start():
# code to connect to irc server and announce deployment begins
pass
def announce_finish():
# code to connect to irc server and announce deployment finishes
pass
#hosts(...)
def deploy_machine1():
pass
#hosts(...)
def deploy_machine2():
pass
def deploy():
announce_start()
execute(deploy_machine1)
execute(deploy_machine2)
announce_finish()
and then just invoke fab deploy
How do I NOT parallelize local commands inside a task with #parallel decorator:
#parallel
def myTask():
local('initialize localhost')
run('command on remote host')
local('clean up localhost')
I want commands on local host only to execute once, and commands for remote hosts run in parallel. Right now I'm seeing local host commands running for each remote host instance. What is the cleanest way to do this?
Thanks
Group your parallel commands into a decorated function. Then use execute() to call it inbetween the local calls.
Does the following work for you?
def local_init():
local('initialize')
#parallel
def myTask():
run('remote command')
def local_cleanup():
local('clean up')
And then:
fab local_init myTask local_cleanup
when I run my python code it is asking for host.
No hosts found. Please specify (single) host string for connection:
I have the following code:
from fabric.api import *
from fabric.contrib.console import confirm
env.hosts = [ 'ipaddress' ]
def remoteRun():
print "ENV %s" %(env.hosts)
out = run('uname -r')
print "Output %s"%(out)
remoteRun();
I even tried running fab with -H option and I am getting the same message. I'm using Ubuntu 10.10 any help is appreciated. Btw I am a newbie in Python.
In order to get hosts to work in a script outside of the fab command-line tool and fabfile.py, you'll have to use execute():
from fabric.api import run
from fabric.tasks import execute
def mytask():
run('uname -a')
results = execute(mytask)
If it's only one host, you can use env.host_string = 'somehost or ipaddress'.
You also don’t need the ; at the end of your remoteRun.
from __future__ import with_statement
from fabric.api import *
from fabric.contrib.console import confirm
from fabric.api import env, run
env.host_string = 'ipaddress'
def remoteRun():
print "ENV %s" %(env.hosts)
out = run('uname -r')
print "Output %s"%(out)
remoteRun()
I am not exactly sure what remoteRun(); is supposed to do in your example.
Is it part of your fabfile or is this your terminal command to invoke the script?
The correct way would be a command like this in your shell:
fab remoteRun
Generally it's better to specify the concrete hosts your command is supposed to run on like this:
def localhost():
env.hosts = [ '127.0.0.1']
def remoteRun():
print "ENV %s" %(env.hosts)
out = run('uname -r')
print "Output %s"%(out)
You can run it like this from a terminal (assuming you are in the directory that contains your fabfile):
fab localhost remoteRun
As an alternative you could specify the host with the -H parameter:
fab -H 127.0.0.1 remoteRun
If you have a list of hosts you want to invoke the command for, do it like this:
http://readthedocs.org/docs/fabric/latest/usage/execution.html
Adjusted to your example:
env.hosts = [ 'localhost', '127.0.0.1']
def remoteRun():
print "ENV %s" %(env.hosts)
out = run('uname -r')
print "Output %s"%(out)
And called via: fab remoteRun
This way the remoteRun is performed on all hosts in env.hosts.
#Nerdatastic is right, for simple: don't use env.hosts, use env.host_string instead. e.g.
def setup_db_server
env.host_string = 'db01.yoursite.com' # or the ip address
run("mysqladmin ...")
end
and running $ fab setup_db_server will execute the script on the target server.
Nerdatastic is right, you need to specify the env.host_string varaible for fabric to know what host string to use. I came across this problem trying to use a subclass of Task and call the run() method. It seemed to ignore env.hosts except when using execute from fabric.tasks in version 1.3.
i have same issue.
I think this is a bug. Because all work before today.
I store my env in .fabricrc.
Now i have same message as yours. Don't know why.