如何发现Python Fabric中的当前角色

时间:2010-06-23 04:20:12

标签: python fabric

这是一个非常Fabric特定问题,但更有经验的python黑客可能能够回答这个问题,即使他们不了解Fabric。

我试图在命令中指定不同的行为,具体取决于它运行的角色,即:

def restart():
    if (SERVERTYPE == "APACHE"):
        sudo("apache2ctl graceful",pty=True)
    elif (SERVERTYPE == "APE"):
        sudo("supervisorctl reload",pty=True)

我用这样的函数来攻击它:

def apache():
    global SERVERTYPE
    SERVERTYPE = "APACHE"
    env.hosts = ['xxx.xxx.com']

但这显然不是很优雅,我只是发现了角色,所以我的问题是:

如何确定当前实例属于哪个角色?

env.roledefs = {
    'apache': ['xxx.xxx.com'],
    'APE': ['yyy.xxx.com'],
}

谢谢!

5 个答案:

答案 0 :(得分:18)

对于其他所有有过这个问题的人来说,这是我的解决方案:

关键是找到env.host_string。

这是我用一个命令重启不同类型服务器的方法:

env.roledefs = {
    'apache': ['xxx.xxx.com'],
    'APE': ['yyy.xxx.com']
}

def apache():
    env.roles = ['apache']

...

def restart():
    if env.host_string in env.roledefs['apache']:
        sudo("apache2ctl graceful", pty=True)
    elif env.host_string in env.roledefs['APE']:
        sudo ("supervisorctl reload", pty=True)

享受!

答案 1 :(得分:12)

我没有测试它,但可能有效:

def _get_current_role():
    for role in env.roledefs.keys():
        if env.host_string in env.roledefs[role]:
            return role
    return None

答案 2 :(得分:6)

env.roles将为您提供-R标志指定的角色或脚本本身的硬编​​码。它不包含使用命令行或使用@roles装饰器为每个任务指定的角色。目前无法获得此类信息。

下一个结构版本(可能是1.9)将为env.effective_roles属性提供您想要的内容 - 用于当前执行任务的角色。该代码已经合并为master。

查看this issue

答案 3 :(得分:5)

更新:刚刚检查了the source code似乎早在1.4.2就已经可以使用了它!

update 2 :使用@roles装饰器时(1.5.3),这似乎!它仅在使用-R命令行标志指定角色时才有效。

对于fabric 1.5.3,当前角色可直接在`fabric.api.env.roles'中使用。例如:

import fabric.api as fab

fab.env.roledefs['staging'] = ['bbs-evolution.ipsw.dt.ept.lu']
fab.env.roledefs['prod'] = ['bbs-arbiter.ipsw.dt.ept.lu']


@fab.task
def testrole():
    print fab.env.roles

在控制台上测试输出:

› fab -R staging testrole
[bbs-evolution.ipsw.dt.ept.lu] Executing task 'testrole'
['staging']

Done.

或者:

› fab -R staging,prod testrole
[bbs-evolution.ipsw.dt.ept.lu] Executing task 'testrole'
['staging', 'prod']
[bbs-arbiter.ipsw.dt.ept.lu] Executing task 'testrole'
['staging', 'prod']

Done.

有了这个,我们可以在结构任务中进行简单的in测试:

@fab.task
def testrole():
    if 'prod' in fab.env.roles:
        do_production_stuff()
    elif 'staging' in fab.env.roles:
        do_staging_stuff()
    else:
        raise ValueError('No valid role specified!')

答案 4 :(得分:0)

在Anaconda2 5.1.0下使用fabric 1.14.0 ...在使用@roles装饰器时确认问题...特别是在@roles装饰器与多个​​参数一起使用的情况下在第一个任务中调用没有@roles装饰器(或具有不同参数)的另一个任务。根据我的经验,这可能会产生非理论上不匹配主机的影响,具体取决于我发现角色的方式(即role = env.effective_roles[0])。

请注意role = env.effective_roles[0]在简单的情况下效果很好,例如(a)@roles仅指定一个角色,(b)原始任务不会调用另一个任务。

还请注意命令行上的-R未覆盖@roles并且必须使用task:roles=role1的情况:How to run a @roles-decorated fabric task on only a single host ...也想知道如何将多个角色传递给一个名为roles的争论...嗯,但我离题了。

也许有更好的方法,但@roles上的文档让人想要。下一步是在这一点上阅读源代码。

与此同时,我已经破解了以下解决方法......

from fabric.api import env
from fabric.decorators import roles
from fabric.decorators import task


def get_host_roles(env, of=None, die=False):
    """
    Get the role(s) for a host at run time
    :param env: Fabric env
    :param of: tuple/set/list
    :param die: boolean
    :return: tuple(host, roles) or tuple(host, role)
    """
    host = env.host
    def valid(role):
        return host in env.roledefs[role]:
    roles = set(filter(valid, env.roledefs.keys()))
    if of:
        roles = tuple(roles & set(of)) # set intersection
        if len(roles) == 1:
            return host, roles[0]
        elif die:
            e = 'Host "%s" is not in just one of the provided roles: %s!' \
                % (host, repr(roles))
            raise Exception(e)
    return host, roles


_roles = ('role1', 'role2')


@task
@roles(*_roles)
def do_something_with_roles():
    host, roles = get_host_roles(env)
    # roles is a tuple with all of the roles the host is in.


@task
@roles(*_roles)
def do_something_with_roles_diy():
    host, roles = get_host_roles(env, _roles)
    # `roles` is a tuple with the set intersection of `_roles` and the
    # host's actual roles... so you handle the situation!
    if 'role1' in roles:
        # do whatever
        pass


@task
@roles(*_roles)
def force_single_role():
    host, role = get_host_roles(env, _roles, True)
    # this usage raises an exception in the instance that the host is not
    # exclusively in either 'role1' or 'role2'.
    # roles is a string with the role for that host.

希望有所帮助。