How to discover current role in Python Fabric

前端 未结 5 385
既然无缘
既然无缘 2021-02-02 10:24

This is a very Fabric specific question, but more experienced python hackers might be able to answer this, even if they don\'t know Fabric.

I am trying to specify differ

相关标签:
5条回答
  • 2021-02-02 11:03

    The env.roles will give you the roles specified by -R flag or hardcoded in the script itself. It won't contain the roles specified per task using the command line or using @roles decorator. There is currently no way of getting this kind of information.

    The next release of fabric (1.9 probably) will provide env.effective_roles attribute with exactly what you want - the roles being used for the currently executed task. The code for that has already been merged into master.

    Have a look at this issue.

    0 讨论(0)
  • 2021-02-02 11:05

    For everyone else ever with this question, here is my solution:

    The key was finding env.host_string.

    This is how I restart different types of servers with one command:

    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)
    

    enjoy!

    0 讨论(0)
  • 2021-02-02 11:08

    Using fabric 1.14.0 under Anaconda2 5.1.0... confirm the issue when using the @roles decorator... especially in the case that the @roles decorator is used with multiple arguments and then another task without the @roles decorator (or with different arguments) is called from within the first task. In my experience, this can have the effect of nonsensically mismatching the hosts, depending on how I discover the role (i.e. role = env.effective_roles[0]).

    Note that role = env.effective_roles[0] does work well in simple situations, e.g. (a) @roles only specifies one role, and (b) the original task does not call another task.

    Note also the situation where -R on command line does not override @roles and must use task:roles=role1 instead: How to run a @roles-decorated fabric task on only a single host ... also wondering how to pass multiple roles to an argument named roles... hmmm, but I digress.

    Perhaps there is a better way, but documentation on @roles leaves one wanting. Next step is to probably read through the source code at this point.

    In the meantime, I've hacked up the following workaround...

    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.
    

    Hope that helps.

    0 讨论(0)
  • 2021-02-02 11:12

    I didn't test it, but might work:

    def _get_current_role():
        for role in env.roledefs.keys():
            if env.host_string in env.roledefs[role]:
                return role
        return None
    
    0 讨论(0)
  • 2021-02-02 11:26

    Update: Just checked the source code and it seems that this was already available as early as 1.4.2!

    update 2: This seems not to work when using the @roles decorator (in 1.5.3)! It only works when specifying the roles using the -R command line flag.

    For fabric 1.5.3 the current roles are directly available in `fabric.api.env.roles'. For example:

    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
    

    Test output on the console:

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

    Or:

    › 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.
    

    With this, we can do a simple in test in a fabric task:

    @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!')
    
    0 讨论(0)
提交回复
热议问题