繁体   English   中英

使用 Python API 2.0.0.1 运行 Ansible Playbook

[英]Running an Ansible Playbook using Python API 2.0.0.1

版本:2.0.0.1

我现在已经四处寻找了很多,我发现的大多数文档要么不完整要么已弃用( 这篇文章适用于 1.8.4 版,即

我正在尝试通过 Python API 启动 Ansible 剧本。 Ansible 的文档似乎展示了如何生成和播放任务,但没有展示如何加载和运行剧本 yml 文件。 我一直在深入研究代码,试图了解如何启动它,我认为我已经取得了一些进展,但我真的碰壁了。 这是我到目前为止所拥有的:

def createcluster(region, environment, cluster):
    Options = namedtuple('Options', ['region','env', 'cluster'])

    # initialize needed objects
    variable_manager = VariableManager()
    loader = DataLoader()
    options = Options(region=region, env=environment, cluster=cluster)
    options.listhosts = False
    vault_password = getpass.getpass('Enter vault password :')
    passwords = dict(vault_pass=vault_password)

    #Getting hosts
    hostsread = open('provisioning/inventory/hosts','r')
    hosts =  hostsread.read()
    inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=hosts)
    variable_manager.set_inventory(inventory)
    #Create and load the playbook file
    playbook = Playbook(loader)
    playbook.load('provisioning/cluster.yml', variable_manager,loader)

    #Create an executor to launch the playbook ? 
    executor = None
    executor = PlaybookExecutor(playbook,inventory,variable_manager,loader,options,passwords)
    try:
        result = executor.run()
    finally:
        if executor is not None:
            executor.cleanup()

我完全不确定执行程序部分,当我尝试启动代码时,我不断收到“AttributeError:'Options'对象没有属性'listhosts'”错误(奇怪的是它应该忽略它的缺失,我认为(第 60 行)

我应该如何加载 YML 文件并通过 Python API 启动它? 我走在正确的道路上还是迷失了自我? 为什么没有更好地记录 Ansible? 为什么 42 是 7*7 的答案?

这是 Ansible 2 的示例:

#!/usr/bin/python2

from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager
from ansible.inventory import Inventory
from ansible.playbook import Playbook
from ansible.executor.playbook_executor import PlaybookExecutor

Options = namedtuple('Options', ['connection',  'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts', 'listtasks', 'listtags', 'syntax', 'module_path'])

variable_manager = VariableManager()
loader = DataLoader()
options = Options(connection='local', forks=100, become=None, become_method=None, become_user=None, check=False, listhosts=False, listtasks=False, listtags=False, syntax=False, module_path="")
passwords = dict(vault_pass='secret')

inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list='localhost')
variable_manager.set_inventory(inventory)
playbooks = ["./test.yaml"]

executor = PlaybookExecutor(
              playbooks=playbooks,
              inventory=inventory,
              variable_manager=variable_manager,
              loader=loader,
              options=options,
              passwords=passwords)

executor.run()

使用 Python 2.7.10 和 ansible 2.0.1.0 进行测试

免责声明

发帖待完成。
我在为 ansible 2.4 设置冗长时遇到了麻烦。 我主要讲一下。

长话短说

Ansible 在__main__文件(您启动的文件)中使用一个全局Display对象,如果它不存在,一些导入将创建它。
这被认为是一种不好的做法不符合 PEP8 (第二个要点)


说明部分

版本:(我正在使用 python virtualenv)

  • ansible = 2.4.2.0(也在 2.4.1.0 中测试过)
  • 蟒蛇= 2.7.13

ansible里面是怎么使用的

try:
    from __main__ import display
except ImportError:
    from ansible.utils.display import Display
    display = Display()

几乎每个文件 (108) 都会调用它。 就像你在入口点有一个新的显示,然后所有其他模块将检索这个第一个声明的显示。

以另一种冗长的方式运行

您只需像这样声明一个 Display 对象:

from ansible.utils.display import Display
display = Display(verbosity=5)

您也可以在之后使用它: display.verbosity = 1000


问题

我希望能够完全删除 ansible 输出(负值 = 无输出)

求解

我最终像这样创建了一个新类:

from ansible.utils.display import Display

class AnsibleDisplay(Display):
    ''' 
    This  class override the display.display() function
    '''
    def display(self, *args, **kwargs):
        if self.verbosity >= 0:
            super(AnsibleDisplay, self).display(*args, **kwargs)

然后将其导入我的__main__文件

# Ansible call this global object for display
sys.path.append(ROOT_DIR + os.sep + 'lib')
from ansible_display import AnsibleDisplay
display = AnsibleDisplay(verbosity=0)

并且只有在导入所有其他模块之后

例子

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.dirname(SCRIPT_DIR)

## For ansible
import json
from collections import namedtuple

# Ansible call this global object for display
sys.path.append(ROOT_DIR + os.sep + 'lib')
from ansible_display import AnsibleDisplay
display = AnsibleDisplay(verbosity=0)

# Load other libs after to make sure they all use the above 'display'
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.playbook_executor import PlaybookExecutor

def apply_verbosity(args):
    global display
    verb = -1 if args.verbosity is None else args.verbosity
    display.verbosity = verb

def ansible_part():
    playbook_path = "%s/ansible/main_playbook.yml" % (ROOT_DIR)
    inventory_path = "%s/watev/my_inventory.ini"

    Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff', 'listhosts', 'listtasks', 'listtags', 'syntax'])
    # initialize needed objects
    loader = DataLoader()
    options = Options(connection='local', module_path='%s/' % (ROOT_DIR), forks=100, become=None, become_method=None, become_user=None, check=False,
                    diff=False, listhosts=True, listtasks=False, listtags=False, syntax=False)
    passwords = dict(vault_pass='secret')

    # create inventory and pass to var manager
    inventory = InventoryManager(loader=loader, sources=[inventory_path])
    variable_manager = VariableManager(loader=loader, inventory=inventory)

    pbex = PlaybookExecutor(playbooks=[playbook_path], inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords)
    results = pbex.run()

def main():
    ansible_part()

笔记

  1. 我需要将 4 个选项添加到 namedtuple:
    listhosts=True, listtasks=False, listtags=False, syntax=False
  2. import __main__使调试变得不切实际,因为在使用调试器时(在我的例子中是 pudb),__ __main__文件是调试器文件,因此from __main__ import display永远不会工作

hth

[Edit1]:添加注释

感谢此线程中的建议,我设法找到了一种更改冗长的简单方法:

import ansible
class AnsibleDisplay(ansible.utils.display.Display):
    def display(self, *args, **kwargs):
        self.verbosity = int(os.getenv('ANSIBLE_VERBOSITY', 0))
        super(AnsibleDisplay, self).display(*args, **kwargs)

ansible.utils.display.Display = AnsibleDisplay

# Then import ansible packages after the patch

import ansible.constants as C
from ansible import context
from ansible.cli import CLI
from ansible.executor.playbook_executor import PlaybookExecutor

我在没有看到您想要版本 2 的情况下写了这篇文章。离开它,尽管它不是正确的答案。

这将在 1.9 中工作。 您可以修改您的 createcluster() 命令来调用它。

def run_ansible():
  vaultpass = "password"
  inventory = ansible.inventory.Inventory("provisioning/inventory/hosts", vault_password=vaultpass)

  stats = callbacks.AggregateStats()
  playbook_cb = callbacks.PlaybookCallbacks(verbose=3)

  pb = ansible.playbook.PlayBook(
            playbook=playbook,
            inventory=inventory,
            extra_vars=parsed_extra_vars,
            #private_key_file="/path/to/key.pem",
            vault_password=vaultpass,
            stats=stats,
            callbacks=playbook_cb,
            runner_callbacks=callbacks.PlaybookRunnerCallbacks(stats, verbose=3)
        )
  pb.run()

  hosts = sorted(pb.stats.processed.keys())

  failed_hosts = []
  unreachable_hosts = []
  for h in hosts:
    t = pb.stats.summarize(h)
    if t['failures'] > 0:
      failed_hosts.append(h)
    if t['unreachable'] > 0:
      unreachable_hosts.append(h)

  print("failed hosts: ", failed_hosts)
  print("unreachable hosts: ", unreachable_hosts)

  retries = failed_hosts + unreachable_hosts
  print("retries:", retries)
  if len(retries) > 0:
    return 1
  return 0

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM