Ткань env.roledefs действует не так, как ожидалось

На веб-сайте веб-сайт приведен пример:

from fabric.api import env

env.roledefs = {
    'web': {
        'hosts': ['www1', 'www2', 'www3'],
        'foo': 'bar'
    },
    'dns': {
        'hosts': ['ns1', 'ns2'],
        'foo': 'baz'
    }
}

Насколько я могу судить по документации, эта настройка должна дать команде env dict "foo" значение "bar" при выполнении на хостах "www1", "www2", "www3". Я не могу получить это поведение, хотя ткань правильно определяет хосты. Пример fabfile:

env.foo = 'WRONG'
@task()
def set_role():
    env.roles.append('web')

@task()
def print_foo():
    print env.foo

Пример команды:

fab set_role print_foo

Неожиданный вывод:

[www1] Executing task 'print_foo'
WRONG
[www2] Executing task 'print_foo'
WRONG
[www3] Executing task 'print_foo'
WRONG

Done.

Неужели я не понимаю цели этого? Как я могу сделать это так, чтобы один сервер видел другое значение для ключа, а затем другое без особых проблем?

Я использую ткань 1.10.0

Ответ 2

Две проблемы: сначала версия используемой ткани старше, чем словарные определения, поэтому она интерпретирует ключи как имена хостов. Это можно исправить, обновив до >= ткань 1.10.0. Во-вторых, установленные здесь настройки не заполняют env автоматически, но если вы используете задачу для установки ролей, возможно, это можно установить так:

@task
def set_role(role_name):
    env.roles.append(role_name)
    for k, v in env.roledefs[role_name].iteritems():
        if k == 'hosts':
            continue
        env[k] = v

Вызывается с помощью:

fab set_role:dns do_my_task

Ответ 3

Просто ответ на этот вопрос:

env.roledefs = {
    'prod': {
        'hosts':['server1','server2'],
        'path':'/opt/prod'
        },
    'stag': {
        'hosts':['server3','server4'],
        'path':'/opt/stag'
        }
}

@roles('prod')
def runa():
    role = env.effective_roles[0]
    print env.roledefs[role]['path']

Ответ 4

Если вы хотите, чтобы дополнительные ключи roledef автоматически устанавливались в env для данной задачи, вы можете использовать декоратор. Вот тот, который я использую:

from functools import wraps

def apply_role(f):
    "Decorator to apply keys in effective roledef to current env."
    @wraps(f)
    def wrapper(*args, **kwargs):
        if env.effective_roles:
            for k, v in env.roledefs[env.effective_roles[0]].items():
                if k in env and isinstance(env[k], list) and isinstance(v, list):
                    env[k].extend(v)
                elif k in env and isinstance(env[k], list):
                    env[k].append(v)
                else:
                    env[k] = v
        return f(*args, **kwargs)
    return wrapper

Затем украсьте любую задачу, для которой вы хотите применить свои ключи roledef:

@apply_role
def mytask():
    ...