How can I properly set the `env.hosts` in a function in my Python Fabric `fabfile.py`?
Asked Answered
B

5

9

When I run this fabfile.py...

from fabric.api import env, run, local, cd

def setenv(foo):
  env.hosts = ['myhost']

def mycmd(foo):
  setenv(foo)
  print(env.hosts)
  run('ls')

with this command fab mycmd:bar. I get this output...

['myhost']
No hosts found. Please specify (single) host string for connection:

What, what?! I don't get it? I've set the env.hosts and it seems to be valid "inside" the mycmd function, but for some reason that run command doesn't know about the hosts I've specified.

Color me confused. Any help would be appreciated!

Briant answered 31/1, 2012 at 6:30 Comment(1)
This looks to be the same question, but with more answers.Map
V
7

@Chris, the reason you're seeing this behavior is because the host list is constructed before the task function is called. So, even though you're changing env.hosts inside the function, it is too late for it to have any effect.

Whereas the command fab setenv:foo mycmd:bar, would have resulted in something you would have expected:

$ fab setenv:foo mycmd:bar
[myhost] Executing task 'mycmd'
['myhost']
[myhost] run: ls

This is the same as the accepted answer, but because of the way setenv is defined, an argument is needed.

Another example:

from fabric.api import env, run, local, cd

env.hosts = ['other_host']

def setenv(foo):
    env.hosts = ['myhost']

def mycmd(foo):
    setenv(foo)
    print('env.hosts inside mycmd: %s' % env.hosts)
    run('ls')

The output of this is:

$ fab mycmd:bar
[other_host] Executing task 'mycmd'
env.hosts inside mycmd: ['myhost']
[other_host] run: ls

Fatal error: Name lookup failed for other_host

Underlying exception:
    (8, 'nodename nor servname provided, or not known')
Aborting.

As you can see, the host-list is already set to ['other_host', ] when fabric starts to execute mycmd.

Vaudeville answered 18/9, 2012 at 19:47 Comment(0)
K
5

The way you are doing it is not normally how I would use Fabric.

from fabric.api import *

def hostname():

    env.hosts = ['myhosts']

def mycmd():
    print env.hosts
    run('ls -l')

To run this I would then do

fab hostname mycmd

this allows you to seperate which host/hosts you want to perform the command on.

hope it helps.

Kantian answered 31/1, 2012 at 8:56 Comment(1)
I'm marking this as correct because I think this method is the 'proper' way to do it, but I still don't understand what the hell is going on with the code in my initial example.Briant
F
3

Have you tried to used the hosts decorator?

from fabric.api import env, run, hosts

@hosts('myhost')
def mycmd(foo):
    print(env.hosts)
    run('ls')
Faroff answered 31/1, 2012 at 7:19 Comment(0)
V
1

I know this question is super old, but just in case someone stumbles across this, I have found that you don't need to call this as a fab file per se (your file doesn't need to be called "fabfile.py" and command doesn't need to be fab setenv(foo) mycmd(bar). Since you are importing the needed fab elements, you can call the file anything you want (let's call it "testfile.py") and simply use the execute function in the file. That would make your command python testfile.py.

Inside the testfile, set everything up like you would normally, but start the function using the execute keyword. Your file would look something like this:

from fabric.api import env, run

def setenv(foo):
    env.hosts = ['myhost']
    execute(mycmd, bar)

def mycmd(bar):
    run('ls')

setenv(foo)

** It's important to note that the execute command does look like a regular function call. It will call the function and send the arguments in a single comma separated line. You can find more information here

So you'd start your program which would first call setenv, then setenv would execute the mycmd function. With this, you can also set multiple hosts within the same array. Something like:

env.hosts=['myhost1','myhost2','myhost3']
Vazquez answered 6/2, 2020 at 21:21 Comment(0)
W
0

I have figured out how to make it work:

from fabric.api import env, run, local, cd

def setenv(foo):
  env.hosts = ['myhost']
  return env

def mycmd(foo):
  env = setenv(foo)
  print(env.hosts)
  run('ls')
Wisecrack answered 4/5, 2015 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.