Question

I've some fabric tasks in my fabfile and I need to initialize, the env variable before their execution. I'm trying to use a decorator, it works but fabric always says "no host found Please specify (single)" however if I print the content of my variable "env" all seems good. Also I call my tasks from another python script.

from fabric.api import *
from instances import find_instances

def init_env(func):
    def wrapper(*args, **kwargs):
        keysfolder = 'keys/'
        env.user = 'admin'
        env.key_filename = '%skey_%s_prod.pem'%(keysfolder, args[0])
        env.hosts = find_instances(args[1])
        return func(args[0], args[1])
    return wrapper


@init_env
def restart_apache2(region, groupe):
    print(env.hosts)
    run('/etc/init.d/apache2 restart')
    return True

My script which call the fabfile:

from fabfile import init_env, restart_apache2

restart_apache2('eu-west-1', 'apache2')

Output of print in restart apache2:

[u'10.10.0.1', u'10.10.0.2']

Any idea why my task restart_apache2 doesn't use the env variable?

Thanks

EDIT:

Which is interesting it's if in my script which calls the fabfile, I use settings from fabric.api and set a host ip, it works. This show that my decorator has well initialized the env variable because the key and user are send to fabric. It's only the env.hosts that's not read by fabric...

EDIT2:

I can reach my goal with using settings from fabric.api, like that:

@init_env
def restart_apache2(region, groupe):
    for i in env.hosts:
        with settings(host_string = '%s@%s' % (env.user, i)):
            run('/etc/init.d/apache2 restart')
    return True

Bonus question, has there a solution to use directly the env.hosts without settings?

Was it helpful?

Solution

I'm guessing here a little, but I'm assuming you've got into trouble because you're trying to solve two problems at once.

The first issue relates to the issue of multiple hosts. Fabric includes the concepts of roles, which are just groups of machines that you can issue commands to in one go. The information in the find_instances function could be used to populate this data.

from fabric import *
from something import find_instances

env.roledefs = {
    'eu-west-1' : find_instances('eu-west-1'),
    'eu-west-2' : find_instances('eu-west-2'),
}

@task
def restart_apache2():
    run('/etc/init.d/apache2 restart')

The second issue is that you have different keys for different groups of servers. One way to resolve this problem is to use an SSH config file to prevent you from having to mix the details of the keys / users accounts with your fabric code. You can either add an entry per instance into your ~/.ssh/config, or you can use local SSH config (env.use_ssh_config and env.ssh_config_path)

Host instance00
  User admin
  IdentityFile keys/key_instance00_prod.pem

Host instance01
  User admin
  IdentityFile keys/key_instance01_prod.pem

# ...

On the command line, you should then be able to issue the commands like:

fab restart_apache2 -R eu-west-1

Or, you can still do single hosts:

fab restart_apache2 -H apache2

In your script, these two are equivalent to the execute function:

from fabric.api import execute
from fabfile import restart_apache2

execute(restart_apache2, roles = ['eu-west-1'])
execute(restart_apache2, hosts = ['apache2'])
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top