使用 ansible python api 调用 playbook (仅适用于 ansible 2.8)

2,477 阅读3分钟

一. 前言:

ansible 是一款自动化的运维工具,基于python语言,模块化工作;

作为一款运维工具,最重要的便是具备批量操作的能力,ansible能够进行批量的系统配置,程序部署,命令执行等;

ansible是基于ssh来和远程机器进行通信的,无需在远程机器上安装client或者agent;

ansible最强大的功能就是基于yaml语法的playbook(剧本),通过使用ansible的各个模块来定制化运维操作,是基于ansible开发运维操作系统的关键.

二. 使用 python api 调用 playbook

废话不多说,直接上代码,建议使用root账号密码来操作

ansible 版本: 2.8.5

python版本: 3.7.4

  1. 定义执行方法,用于执行剧本,参数含义就是字面意思

    # 定义方法用于执行剧本
    def cluster_node_start(target_host_list, name, callback_vars, tags, extra_vars=None):
        play = get_mine_playbook_executor(extra_vars, name, target_host_list, tags)
        # set callback
        results_callback = mine_playbook_callback(callback_vars)
        play._tqm._stdout_callback = results_callback
        play.run()
    
  2. 定义剧本的执行器 PlaybookExecutor (核心,不同的版本之间有差异,遇坑的举爪)

    # 定义剧本的执行器 PlaybookExecutor
    def get_mine_playbook_executor(extra_vars, name, target_host_list, tags=[]):
        # 非常神奇的一句话
        current_process()._config = {'semprefix': '/mp'}
        loader = DataLoader()
        passwords = dict()
        inventory = InventoryManager(loader=loader, sources=settings.WHOLE_HOSTS_PATH)
        variable_manager = VariableManager(loader=loader, inventory=inventory)
        inventory.add_group("now_group")
        for data in target_host_list:
            host_ip = data.get("host_ip")
            inventory.add_host(host_ip, 'now_group')
    
            hostname = host_ip
            hostip = data.get('host_ip')
            username = data.get("host_user")
            password = data.get("host_password")
            my_host = Host(name=hostname)
            variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_host", value=hostip)
            variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_user", value=username)
            variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_pass", value=password)
        if extra_vars is not None:
            variable_manager._extra_vars = extra_vars
    
        # 如果你希望能够动态的更改tags,请使用这种方式获取options,
        # 使用context._init_global_context() tags只会赋一次初值!!!
        context.CLIARGS = get_mine_default_options(tags)
        play = PlaybookExecutor(
            playbooks=[settings.PLAYBOOK_BASE_DIR + name],
            inventory=inventory,
            loader=loader,
            # options=get_default_options(tags),#ansible 2.8 没办法直接在这里添加options了
            passwords=passwords,
            variable_manager=variable_manager,
        )
    
        return play
    
  3. options的获取,定义方法get_mine_default_options(tags)

    # 获取options
    def get_mine_default_options(tags):
        from ansible.module_utils.common.collections import ImmutableDict
        return ImmutableDict(verbosity=0,
                             ask_pass=False,
                             private_key_file='/root/.ssh/id_rsa',
                             remote_user=None,
                             connection='smart',
                             timeout=60,
                             ssh_common_args='',
                             sftp_extra_args='',
                             scp_extra_args='',
                             ssh_extra_args='',
                             force_handlers=False,
                             flush_cache=None,
                             become=False, become_method='sudo', become_user=None, become_ask_pass=False,
                             tags=tags if len(tags) > 0 else [], skip_tags=[],
                             check=False,
                             syntax=None,
                             diff=False,
                             # inventory= '/Users/caishichao/Code/AnsibleCentrolManagement/inventory/hosts.uat',
                             listhosts=None, subset=None,
                             extra_vars=[],
                             ask_vault_pass=False,
                             vault_password_files=[], vault_ids=[], forks=5, module_path=None, listtasks=None,
                             listtags=None, step=None, start_at_task=None, args=['fake'])
    
  4. 剧本回调(callback)--如果有运维平台的需求,会需要使用回调来获取task的执行情况

    # 定义剧本执行完成后的回调策略,用来对任务的执行结果进行记录
    class mine_playbook_callback(CallbackBase):
        def __init__(self, vars):
            super().__init__()
            self.vars = vars
    
        def v2_runner_on_ok(self, result):
            if (result._task._role != None):
                # 传入的playbook参数
                extra_vars = result._task._variable_manager._extra_vars
                # 执行playbook的节点ip
                host_ip = result._host.addressc
                # 执行的playbook中的任务名称
                task_name = result.task_name
                # 执行shell时的输出
                stdout_lines = result._result.get('stdout_lines')
    
        def v2_runner_on_failed(self, result, ignore_errors=False):
            # 执行失败时的回调用
            pass
    
        def v2_runner_on_unreachable(self, result):
            # host 不可达时的回调
            pass
    

    以上就是ansible2.8 执行一个playbook的核心代码了!

三. 后记

ansible是个蛮不错的运维工具,结合playbook可以完成很多自动化的工作,比如新机器的环境check,应用安装等操作,都可以在定义好剧本后批量操作,比较方便.

当然我觉得目前的ansible文档较少,官网上说的不清不楚,对于python api 调用 playbook 基本没有什么说明,导致升级使用起来较为困难,在写上述的剧本代码调用也是被无情践踏了多词,疯狂的去各个社区搜索相关资料来逐渐完善自己的调用代码,非常感谢哪些愿意分享的优秀博主.

@author xigua