【问题标题】:Running ansible-playbook using Python API使用 Python API 运行 ansible-playbook
【发布时间】:2015-02-19 19:20:07
【问题描述】:

如何在 python 脚本中运行剧本?在 python 中使用 ansible 模块相当于什么:

ansible -i hosts dbservers -m setup
ansible-playbook -i hosts -vvvv -k site.yml

我在http://docs.ansible.com/developing_api.html 中查看了他们的文档,但他们的示例非常有限。

【问题讨论】:

    标签: python ansible ansible-runner


    【解决方案1】:

    您正在查看不受官方支持或推荐的内容,因此需要的文档很少。

    也就是说,如果你真的想学习那门课程,我会先在 bin 中打开 ansible-playbook 脚本,然后对你想做的事情进行逆向工程。

    【讨论】:

    • 这是不准确的,已被 official documentation 覆盖。文档很少这一事实是另一回事。
    • tristan:我不确定你在看什么,因为目前关于通过 Python API 运行 playbook 的官方文档为零。
    • @Dolph 点击link in my comment -- ansible.runner 是运行 playbook 的入口点(参见评论“Python API 非常强大,并且是 ansible CLI 和 ansible-剧本已实施。”)。是的,对于已经在 Ansible 代码库中工作过的人来说,文档读​​起来更好,但这并不能使这个答案正确。
    • 现在有弃用警告 (20200616_2215)。
    【解决方案2】:

    弃用通知:这篇文章在 ansible 2 中不起作用。API 已更改。

    这在“Python API”下的Ansible documentation 中有介绍。

    例如,ansible -i hosts dbservers -m setup 是通过以下方式实现的:

    import ansible.runner
    
    runner = ansible.runner.Runner(
       module_name='setup',
       module_args='',
       pattern='dbservers',
    )
    dbservers_get_facts = runner.run()
    

    Runner的__init__方法中有一堆未记录的参数(来自ansible.runner)。有too many to list inline,但我在这篇文章中包含了一些参数,作为对你具体寻找的猜测。

    class Runner(object):
        ''' core API interface to ansible '''
    
        # see bin/ansible for how this is used...
    
        def __init__(self,
            host_list=C.DEFAULT_HOST_LIST,      # ex: /etc/ansible/hosts, legacy usage
            module_path=None,                   # ex: /usr/share/ansible
            module_name=C.DEFAULT_MODULE_NAME,  # ex: copy
            module_args=C.DEFAULT_MODULE_ARGS,  # ex: "src=/tmp/a dest=/tmp/b"
            ...
            pattern=C.DEFAULT_PATTERN,          # which hosts?  ex: 'all', 'acme.example.org'
            remote_user=C.DEFAULT_REMOTE_USER,  # ex: 'username'
            remote_pass=C.DEFAULT_REMOTE_PASS,  # ex: 'password123' or None if using key
            remote_port=None,                   # if SSH on different ports
            private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
            sudo_pass=C.DEFAULT_SUDO_PASS,      # ex: 'password123' or None
            ...
            sudo=False,                         # whether to run sudo or not
            sudo_user=C.DEFAULT_SUDO_USER,      # ex: 'root'
            module_vars=None,                   # a playbooks internals thing
            play_vars=None,                     #
            play_file_vars=None,                #
            role_vars=None,                     #
            role_params=None,                   #
            default_vars=None,                  #
            extra_vars=None,                    # extra vars specified with he playbook(s)
            is_playbook=False,                  # running from playbook or not?
            inventory=None,                     # reference to Inventory object
            ...
            su=False,                           # Are we running our command via su?
            su_user=None,                       # User to su to when running command, ex: 'root'
            su_pass=C.DEFAULT_SU_PASS,
            vault_pass=None,
            ...
            ):
    

    例如,上面指定 sudo 用户和密码的命令是:

    runner = ansible.runner.Runner(
       module_name='setup',
       module_args='',
       pattern='dbservers',
       remote_user='some_user'
       remote_pass='some_pass_or_python_expression_that_returns_a_string'
    )
    

    对于剧本,请查看playbook.PlayBook,它采用一组类似的初始化程序:

    class PlayBook(object):
        '''
        runs an ansible playbook, given as a datastructure or YAML filename.
        ...
        '''
    
        # *****************************************************
    
        def __init__(self,
            playbook         = None,
            host_list        = C.DEFAULT_HOST_LIST,
            module_path      = None,
            .... 
    

    并且可以使用.run() 方法执行。例如:

    from ansible.playbook import PlayBook
    pb = PlayBook(playbook='/path/to/book.yml, --other initializers--)
    pb.run()
    

    更强大的用法可以在ansible-playbook file 中找到。

    据我所知,将 playbook 转换为 Python 模块会涉及更多内容,但上面列出的文档应该可以让您了解,您可以重用 Ansible 中内置的 YAML 解析器将 playbook 转换为变量。

    【讨论】:

    • 这似乎没有回答如何运行剧本,而是一个单独的任务。有一个 ansible.playbook.Playbook 类也有一个 run 方法,我认为 OP 正在询问 - 我知道这是我想要找到的:D
    • ansible.playbook.Playbook 调用 runner 以执行(参见 ansible.runner 中的is_playbook),但很好。现在更新答案。
    • 非常感谢!还在(还有哪里?!)GitHub repo 中找到了一个很好的例子,他们的ansible-playbook command ---(编辑:哎呀,你已经指出了:D)
    • @fideloper 乐于提供帮助。我扩展 Ansible 的典型路径是直接跳到源代码中,或者直接启动 pdb 并遵循它——Ansible 的代码库非常简单。
    【解决方案3】:

    我已经回答了here的问题 在此处发布此内容是因为社区不鼓励发布链接。希望对您有所帮助。

    文档出奇地缺乏,你必须开始here

    话虽如此,这是我一起编写的一个快速脚本,可以运行剧本。

    #!/usr/bin/env python
    
    import os
    import sys
    from collections import namedtuple
    
    from ansible.parsing.dataloader import DataLoader
    from ansible.vars.manager import VariableManager
    from ansible.inventory.manager import Inventory
    from ansible.executor.playbook_executor import PlaybookExecutor
    
    loader = DataLoader()
    
    inventory = Inventory(loader=loader, sources='/home/slotlocker/hosts2')
    variable_manager = VariableManager(loader=loader, inventory=inventory)
    playbook_path = '/home/slotlocker/ls.yml'
    
    if not os.path.exists(playbook_path):
        print '[INFO] The playbook does not exist'
        sys.exit()
    
    Options = namedtuple('Options', ['listtags', 'listtasks', 'listhosts', 'syntax', 'connection','module_path', 'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args', 'scp_extra_args', 'become', 'become_method', 'become_user', 'verbosity', 'check','diff'])
    options = Options(listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh', module_path=None, forks=100, remote_user='slotlocker', private_key_file=None, ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=True, become_method='sudo', become_user='root', verbosity=None, check=False, diff=False)
    
    variable_manager.extra_vars = {'hosts': 'mywebserver'} # This can accomodate various other command line arguments.`
    
    passwords = {}
    
    pbex = PlaybookExecutor(playbooks=[playbook_path], inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords)
    
    results = pbex.run()
    

    【讨论】:

    • 我们如何获取剧本的所有相关组变量?
    • 您好,谢谢您的回答,对我帮助很大,对于使用 paramiko_ssh 的每个人请注意,您必须拥有 ** ssh_common_args=''ssh_extra_args='' 而不是 None **,否则您会得到一个例外:TypeError: sequence item: expected string, NoneType found 花费了我一些时间并浏览代码来弄清楚这一点。
    • 对于 Ansible 2.6 (2.6.8) 有 API 更改,您应该将 Inventory 更改为 InventoryManager 所以.... from ansible.inventory.manager import InventoryManager and inventory = InventoryManager(loader=loader .. .
    • variable_manager._extra_vars = {'hosts': 'mywebserver'} 根据github.com/grycap/im/commit/…,Ansible 版本大于 2.8@
    【解决方案4】:

    只是一个适用于 2.8.3 的快速代码更新,

    from ansible import context
    from ansible.cli import CLI
    from ansible.module_utils.common.collections import ImmutableDict
    from ansible.executor.playbook_executor import PlaybookExecutor
    from ansible.parsing.dataloader import DataLoader
    from ansible.inventory.manager import InventoryManager
    from ansible.vars.manager import VariableManager
    
    loader = DataLoader()
    
    context.CLIARGS = ImmutableDict(tags={}, listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh',
                        module_path=None, forks=100, remote_user='xxx', private_key_file=None,
                        ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=True,
                        become_method='sudo', become_user='root', verbosity=True, check=False, start_at_task=None)
    
    inventory = InventoryManager(loader=loader, sources=('/xxx/inventory_file',))
    
    variable_manager = VariableManager(loader=loader, inventory=inventory, version_info=CLI.version_info(gitinfo=False))
    
    pbex = PlaybookExecutor(playbooks=['/xxx/playbook.yml'], inventory=inventory, variable_manager=variable_manager, loader=loader, passwords={})
    
    results = pbex.run()
    

    【讨论】:

    • 很好的答案。 ansible 文档对此没有太大帮助,您的回答是我能找到的唯一实际工作的地方,谢谢!
    • 我们如何传递 group_vars 文件?
    • 没关系;文件夹不包含它们;我在 docker 容器中运行
    • 比官方文档中的任何内容都更有帮助。谢谢你!
    【解决方案5】:

    根据 vigilander 的回答,我冒昧地将 pkg 上传到 pypi,现在您可以使用这个简单的 ansible 运行器:

    只需 pip install ansible-playbook-runner 并运行:

    from ansible_playbook_runner import Runner
    
    Runner(['inventory_path'], 'playbook_path').run()
    

    【讨论】:

    • 项目上线了吗?可以分享一下代码吗?遗憾的是 Ansible 的 python 接口非常糟糕,尽管 API 已经 100% 准备好。
    • 我目前不维护它。我上传的是在可预见的未来会出现的东西
    • 介意发布源链接吗?只能在 PyPi 上找到,在 github 上找不到
    • 别以为是我上传的...你可以在安装后把它们从轮子上取下来
    【解决方案6】:

    Ansible 提供了ansible-runner Python 包(PyPIGitHub),可用于此目的。

    来自documentation的用法示例:

    import ansible_runner
    r = ansible_runner.run(private_data_dir='/tmp/demo', playbook='test.yml')
    print("{}: {}".format(r.status, r.rc))
    # successful: 0
    for each_host_event in r.events:
        print(each_host_event['event'])
    print("Final status:")
    print(r.stats)
    

    【讨论】:

    • ansible-runner 需要 ansible 可执行文件
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 2018-02-10
    相关资源
    最近更新 更多