zoukankan      html  css  js  c++  java
  • ansible笔记4:Python API

    4. Python API

    api文档地址:https://docs.ansible.com/ansible/latest/dev_guide/developing_api.html

    4.1 官方态度

    ansible api这几年变化好大,而且非常不易用。

    这回仔细看了一下文档提示部分,才发现”不用读系列“还是要读的...

    This API is intended for internal Ansible use. Ansible may make changes to this API at any time that could break backward compatibility with older versions of the API. Because of this, external use is not supported by Ansible.

    重点:

    • API是内部用的
    • 随时变化,不保证向后兼容性
    • 你们外面人用不保证质量

    我建议有项目做的话,还是调用 命令 解决吧。

    4.2 官方示例阅读

    官方示例的是如何通过访问Inventory并执行对应的modules

    把官方例子搬下来,然后看着文档做一些本地化修改

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Date: 2020-03-18
    # @File: example-execute.py
    # @Author: zhangwei
    # @Desc: 官方例子解析
    
    import json
    import shutil
    from ansible.module_utils.common.collections import ImmutableDict
    from ansible.parsing.dataloader import DataLoader
    from ansible.vars.manager import VariableManager
    from ansible.inventory.manager import InventoryManager
    from ansible.playbook.play import Play
    from ansible.executor.task_queue_manager import TaskQueueManager
    from ansible.plugins.callback import CallbackBase
    from ansible import context
    import ansible.constants as C
    
    class ResultCallback(CallbackBase):
        def __init__(self):
            super().__init__()
            self.hosts_ok = []
            self.hosts_failed = []
            self.hosts_unreachable = []
            self.hosts_all = {}
    
        def v2_runner_on_ok(self, result, **kwargs):
            self.hosts_ok.append({result._host.name: result._result})
            self.hosts_all[result._host.name] = result._result
    
        def v2_runner_on_failed(self, result, **kwargs):
            self.hosts_failed.append({result._host.name: result._result})
            self.hosts_all[result._host.name] = result._result
    
        def v2_runner_on_unreachable(self, result, **kwargs):
            self.hosts_unreachable.append({result._host.name: result._result})
            self.hosts_all[result._host.name] = result._result
    
    context.CLIARGS = ImmutableDict(
        connection='local', module_path=['/to/mymodules'], forks=10, become=None,
        become_method=None, become_user=None, check=False, diff=False)
    
    loader = DataLoader()
    passwords = dict(vault_pass='secret')
    results_callback = ResultCallback()
    inventory = InventoryManager(
        loader=loader, sources='/app/ansible/ansible-etc/inventory_dynamic_example.py'
    )
    variable_manager = VariableManager(loader=loader, inventory=inventory)
    
    play_source =  dict(
        name = "Ansible Play",
        hosts = 'test_01',
        gather_facts = 'no',
        tasks = [
            dict(
                action=dict(module='shell', args='hostname'),
                register='shell_out'
            )
        ]
    )
    
    play = Play().load(
        play_source, variable_manager=variable_manager, loader=loader
    )
    
    tqm = None
    try:
        tqm = TaskQueueManager(
            inventory=inventory,
            variable_manager=variable_manager,
            loader=loader,
            passwords=passwords,
            stdout_callback=results_callback,
        )
        result = tqm.run(play)
    
        print(f'result: {result}')
        for ip, ret in results_callback.hosts_all.items():
            output = [
                ip,
                ret['stdout'] if 'stdout' in ret else '',
                ret['exception'] if 'exception' in ret else '',
            ]
            print(','.join(output))
    
    finally:
        if tqm is not None:
            tqm.cleanup()
            
        shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
    

    然后执行,我test_01下有两台独立节点,inventory数据为

    inventory = {
        "all": {
            "hosts": [],
            "vars": {
                "ansible_ssh_user": "root",
                "ansible_ssh_port": 22
            },
            "children": []
        },
        "test_01": {
            "hosts": [
                "10.3.10.40"
            ],
            "vars": {
                "ansible_ssh_pass": "GIMDvZcgqhaYhWk9"
            },
            "children": ["test_01_01"]
        },
        "test_01_01": {
            "hosts": [
                "10.3.10.154"
            ],
            "vars": {},
            "children": []
        },
        "_meta": {}
    }
    

    执行我们的脚本,结果不对劲,两台节点主机名竟然一样?

    (py36) [root@VM_10_40_centos test-scripts]# ./example-execute.py 
    result: 0
    10.3.10.40,VM_10_40_centos,
    10.3.10.154,VM_10_40_centos,
    

    原来是 connection 参数设置为 local,只管理本地主机,修改一下代码片段为

    context.CLIARGS = ImmutableDict(connection='smart',)
    

    重新执行报错,错误提示很明显

    ansible/plugins/connection/ssh.py,line 584,self._play_context.verbosity 未定义

    (py36) [root@VM_10_40_centos test-scripts]# ./example-execute.py 
    result: 2
    10.3.10.40,,Traceback (most recent call last):
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/executor/task_executor.py", line 146, in run
        res = self._execute()
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/executor/task_executor.py", line 645, in _execute
        result = self._handler.run(task_vars=variables)
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/action/shell.py", line 27, in run
        result = command_action.run(task_vars=task_vars)
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/action/command.py", line 24, in run
        results = merge_hash(results, self._execute_module(task_vars=task_vars, wrap_async=wrap_async))
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/action/__init__.py", line 780, in _execute_module
        self._make_tmp_path()
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/action/__init__.py", line 348, in _make_tmp_path
        result = self._low_level_execute_command(cmd, sudoable=False)
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/action/__init__.py", line 1071, in _low_level_execute_command
        rc, stdout, stderr = self._connection.exec_command(cmd, in_data=in_data, sudoable=sudoable)
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/connection/ssh.py", line 1191, in exec_command
        cmd = self._build_command(*args)
      File "/app/ansible/py36/lib64/python3.6/site-packages/ansible/plugins/connection/ssh.py", line 584, in _build_command
        if self._play_context.verbosity > 3:
    TypeError: '>' not supported between instances of 'NoneType' and 'int'
    

    搜了一下这块的定义:https://docs.ansible.com/ansible/latest/modules/debug_module.html

    verbosity, default 0, 设置为3时加上 -vvv 参数做 ssh debug

    奇怪的是,文档说是有default参数,ansible命令帮助里也说时有默认值的,我们还是看一下

    最直接的 debug 当然是 print 一下,在 ssh.py 内加入两个调试片段

            print('zw', self._play_context)
            print('zw', type(self._play_context))
    

    重新执行脚本,拿到我们的信息

    (py36) [root@VM_10_40_centos test-scripts]# ./example-execute.py 
    zw <ansible.playbook.play_context.PlayContext object at 0x7f0ee4056d30>
    zw <class 'ansible.playbook.play_context.PlayContext'>
    

    重新追到 ansible/playbook/play_context.py,找到 class PlayContext

    PlayContext 就是存储执行参数的对象,发现了设置变量的代码

    class PlayContext(Base):
    	...
        def set_attributes_from_cli(self):
            self.verbosity = context.CLIARGS.get('verbosity')  # Else default
    

    这里也被注释了 Else default,但为什么是 NoneType呢。还是我call的方法不对,不管了,我先补上

    把自己的代码片段修改一下

    context.CLIARGS = ImmutableDict(connection='smart', verbosity=0)
    

    重新执行,结果正常

    (py36) [root@VM_10_40_centos test-scripts]# ./example-execute.py 
    result: 0
    10.3.10.154,VM_10_154_centos,
    10.3.10.40,VM_10_40_centos,
    

    好的,确认是为获取到默认值。我改一下 ansible 的原始代码再试试

    编辑 ansible/playbook/play_context.py,把self.verbosity赋值那一行改为

    self.verbosity = context.CLIARGS.get('verbosity', 0)  # Else default
    

    再把我们自己的代码改为

    context.CLIARGS = ImmutableDict(connection='smart')
    

    执行脚本测试,结果正常

    (py36) [root@VM_10_40_centos test-scripts]# ./example-execute.py 
    result: 0
    10.3.10.154,VM_10_154_centos,
    10.3.10.40,VM_10_40_centos,
    

    好了,提issue去...先搜一下有没有相关,发现一个应该也是国人老哥

    https://github.com/ansible/ansible/issues/64933

    我看这答复,在结合文档说明。嗯嗯,个人就别折腾了。

    The internal Python API is considered an unsupported aspect of Ansible and this
    is not considered a bug unless there is an issue with an Ansible binary
    (ansible, ansible-playbook, ansible-doc, etc) or an issue with an external
    API such as are provided for the development of plugins (modules, dynamic
    inventories, callbacks, strategies, etc).
    

    搜了一下互联网上并没有多少关于这个情况记录,那我写一下吧...

  • 相关阅读:
    internet连接共享被启用时 出现了一个错误 (null)
    mybatis01-1测试
    配置没有问题,虚拟机Ubuntu系统ifconfig没有网卡信息
    Ubuntu启动Apache
    VM虚拟机Linux系统eth0下面没有inet和inet6
    jQuery通过id和name获取值的区别
    1.4.3 ID遍历爬虫(每天一更)
    mysql中的SQL语句执行的顺序
    Mecanim动画系统丶
    html中常见的行内元素和块级元素,还有常见的行内块元素
  • 原文地址:https://www.cnblogs.com/tutuye/p/12533186.html
Copyright © 2011-2022 走看看