Bill Rosmus
Bill Rosmus

Reputation: 3011

python fabric no host found must manually set 'env.host_string'

Is there any way to get this to work with env.hosts? As opposed to having to loop manually whenever I have multiple hosts to run this on?

I am trying to use the fabric api, to not have to use the very inconvenient and kludgey fabric command line call. I set the env.hosts variable in one module/class and then call a another class instance method to run a fabric command. In the called class instance I can print out the env.hosts list. Yet when I try to run a command it tells me it can't find a host.

If I loop through the env.hosts array and manually set the env.host variable for each host in the env.hosts array, I can get the run command to work. What is odd is that I also set the env.user variable in the calling class and it is picked up.

e.g. this works:

    def upTest(self):
        print('env.hosts = ' + str(env.hosts))
        for host in env.hosts:
            env.host_string = host
            print('env.host_string = ' + env.host_string)
            run("uptime")

output from this:

env.hosts = ['ec2-....amazonaws.com']
env.host_string = ec2-....amazonaws.com
[ec2-....amazonaws.com] run: uptime
[ec2-....amazonaws.com] out:  18:21:15 up 2 days,  2:13,  1 user,  load average: 0.00, 0.01, 0.05
[ec2-....amazonaws.com] out:

This doesn't work... but it does work if you run it from a "fab" file... makes no sense to me.

    def upTest(self):
        print('env.hosts = ' + str(env.hosts))
        run("uptime")

This is the output:

No hosts found. Please specify (single) host string for connection: 

I did try putting an @task decorator on the method (and removing the 'self' reference since the decorator didn't like that). But to no help.

Is there any way to get this to work with env.hosts? As opposed to having to loop manually whenever I have multiple hosts to run this on?

Upvotes: 5

Views: 3351

Answers (2)

Ajay K. Jain
Ajay K. Jain

Reputation: 101

I've found that it's best not to set env.hosts in code but instead to define roles based on your config file and use the fab tool to specify a role. It worked for me

my_roles.json

{
    "web": [ "[email protected]", "[email protected]" ],
    "db": [ "[email protected]", "[email protected]" ]
}

fabfile.py

from fabric.api import env, run, task
import json

def load_roles():
    with open('my_roles.json') as f:
        env.roledefs = json.load(f)

load_roles()

@task
def my_task():
    run("hostname")

CLI

fab -R web my_task

output from running my_task for each of web1 and web2 is here

Upvotes: 0

mcsrainbow
mcsrainbow

Reputation: 370

Finally, I fixed this problem by using execute() and exec.

main.py

#!/usr/bin/env python

from demo import FabricSupport

hosts = ['localhost']

myfab = FabricSupport()
myfab.execute("df",hosts)

demo.py

#!/usr/bin/env python

from fabric.api import env, run, execute

class FabricSupport:
    def __init__(self):
        pass

    def hostname(self):
        run("hostname")

    def df(self):
        run("df -h")

    def execute(self,task,hosts):
        get_task = "task = self.%s" % task
        exec get_task
        execute(task,hosts=hosts)

python main.py

[localhost] Executing task 'hostname'
[localhost] run: hostname
[localhost] out: heydevops-workspace

Upvotes: 5

Related Questions