Python呼叫ansible API系列(三)帶有callback的執行adhoc和playbook

昀溪發表於2019-04-09

在第二篇文章中雖然可以執行adhoc和playbook但是執行結果的輸出並不是特別直觀,雖然沒有報錯但是到底什麼結果其實你是不知道的尤其是在執行adhoc的時候,這時候我們要利用callback來設定一下執行結果的輸出。

執行adhoc

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from collections import namedtuple
# 核心類
# 用於讀取YAML和JSON格式的檔案
import sys
from ansible.parsing.dataloader import DataLoader
# 用於儲存各類變數資訊
from ansible.vars.manager import VariableManager
# 用於匯入資產檔案
from ansible.inventory.manager import InventoryManager
# 儲存執行hosts的角色資訊
from ansible.playbook.play import Play
# ansible底層用到的任務佇列
from ansible.executor.task_queue_manager import TaskQueueManager
# 狀態回撥,各種成功失敗的狀態
from ansible.plugins.callback import CallbackBase


class MyCallbackBase(CallbackBase):
    """
    透過api呼叫ac-hoc的時候輸出結果很多時候不是很明確或者說不是我們想要的結果,主要它還是輸出到STDOUT,而且通常我們是在工程裡面執行
    這時候就需要後臺的結果前端可以解析,正常的API呼叫輸出前端很難解析。 對比之前的執行 adhoc()檢視區別。
    為了實現這個目的就需要重寫CallbackBase類,需要重寫下面三個方法
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # python3中過載父類構造方法的方式,在Python2中寫法會有區別。
        self.host_ok = {}
        self.host_unreachable = {}
        self.host_failed = {}

    def v2_runner_on_unreachable(self, result):
        """
        重寫 unreachable 狀態
        :param result:  這是父類裡面一個物件,這個物件可以獲取執行任務資訊
        """
        self.host_unreachable[result._host.get_name()] = result

    def v2_runner_on_ok(self, result, *args, **kwargs):
        """
        重寫 ok 狀態
        :param result:
        """
        self.host_ok[result._host.get_name()] = result

    def v2_runner_on_failed(self, result, *args, **kwargs):
        """
        重寫 failed 狀態
        :param result:
        """
        self.host_failed[result._host.get_name()] = result


def useMyCallbackBase():
    """
    這裡透過呼叫ad-hoc來使用自定義callback
    :return:
    """
    dl = DataLoader()
    im = InventoryManager(loader=dl, sources=["hosts"])
    vm = VariableManager(loader=dl, inventory=im)
    Options = namedtuple("Options", [
        "connection", "remote_user", "ask_sudo_pass", "verbosity", "ack_pass",
        "module_path", "forks", "become", "become_method", "become_user", "check",
        "listhosts", "listtasks", "listtags", "syntax", "sudo_user", "sudo", "diff"
    ])
    options = Options(connection='smart', remote_user=None, ack_pass=None, sudo_user=None, forks=5, sudo=None,
                      ask_sudo_pass=False,
                      verbosity=5, module_path=None, become=None, become_method=None, become_user=None, check=False,
                      diff=False,
                      listhosts=None, listtasks=None, listtags=None, syntax=None)
    play_source = dict(name="Ansible Play",  # 任務名稱
                       hosts="test",  # 目標主機,可以填寫具體主機也可以是主機組名稱
                       gather_facts="no",  # 是否收集配置資訊

                       # tasks是具體執行的任務,列表形式,每個具體任務都是一個字典
                       tasks=[
                           dict(action=dict(module="shell", args="touch /tmp/bbb.txt", warn=False))
                       ])
    play = Play().load(play_source, variable_manager=vm, loader=dl)

    passwords = dict()  # 這個可以為空,因為在hosts檔案中

    mycallback = MyCallbackBase()  # 例項化自定義callback

    tqm = TaskQueueManager(
        inventory=im,
        variable_manager=vm,
        loader=dl,
        options=options,
        passwords=passwords,
        stdout_callback=mycallback  # 配置使用自定義callback
    )
    tqm.run(play)
    # print(mycallback.host_ok.items())  # 它會返回2個東西,一個主機一個是執行結果物件
    # 定義資料結構
    result_raw = {"success": {}, "failed": {}, "unreachable": {}}
    # 如果成功那麼  mycallback.host_ok.items() 才可以遍歷,上面的任務肯定能成功所以我們就直接遍歷這個
    for host, result in mycallback.host_ok.items():
        result_raw["success"][host] = result._result

    for host, result in mycallback.host_failed.items():
        result_raw["failed"][host] = result._result

    for host, result in mycallback.host_unreachable.items():
        result_raw["unreachable"][host] = result._result

    print(result_raw)


def main():
    useMyCallbackBase()

if __name__ == "__main__":
    try:
        main()
    finally:
        sys.exit()
View Code

執行playbook

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from collections import namedtuple
# 核心類
# 用於讀取YAML和JSON格式的檔案
from ansible.parsing.dataloader import DataLoader
# 用於儲存各類變數資訊
from ansible.vars.manager import VariableManager
# 用於匯入資產檔案
from ansible.inventory.manager import InventoryManager
from ansible.executor.playbook_executor import PlaybookExecutor
# 狀態回撥,各種成功失敗的狀態
from ansible.plugins.callback import CallbackBase


class MyPlaybookCallbackBase(CallbackBase):
    """
    playbook的callback改寫,格式化輸出playbook執行結果
    """
    CALLBACK_VERSION = 2.0

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.task_ok = {}
        self.task_unreachable = {}
        self.task_failed = {}
        self.task_skipped = {}
        self.task_status = {}

    def v2_runner_on_unreachable(self, result):
        """
        重寫 unreachable 狀態
        :param result:  這是父類裡面一個物件,這個物件可以獲取執行任務資訊
        """
        self.task_unreachable[result._host.get_name()] = result

    def v2_runner_on_ok(self, result, *args, **kwargs):
        """
        重寫 ok 狀態
        :param result:
        """
        self.task_ok[result._host.get_name()] = result

    def v2_runner_on_failed(self, result, *args, **kwargs):
        """
        重寫 failed 狀態
        :param result:
        """
        self.task_failed[result._host.get_name()] = result

    def v2_runner_on_skipped(self, result):
        self.task_skipped[result._host.get_name()] = result

    def v2_playbook_on_stats(self, stats):
        hosts = sorted(stats.processed.keys())
        for h in hosts:
            t = stats.summarize(h)
            self.task_status[h] = {
                "ok": t["ok"],
                "changed": t["changed"],
                "unreachable": t["unreachable"],
                "skipped": t["skipped"],
                "failed": t["failed"]
            }


def usecallbackplaybook():
    """
    呼叫 playbook
    呼叫playboo大致和呼叫ad-hoc相同,只是真正呼叫的是使用PlaybookExecutor
    :return:
    """
    dl = DataLoader()
    im = InventoryManager(loader=dl, sources=["hosts"])
    vm = VariableManager(loader=dl, inventory=im)
    Options = namedtuple("Options", [
        "connection", "remote_user", "ask_sudo_pass", "verbosity", "ack_pass",
        "module_path", "forks", "become", "become_method", "become_user", "check",
        "listhosts", "listtasks", "listtags", "syntax", "sudo_user", "sudo", "diff"
    ])
    options = Options(connection='smart', remote_user=None, ack_pass=None, sudo_user=None, forks=5, sudo=None,
                      ask_sudo_pass=False,
                      verbosity=5, module_path=None, become=None, become_method=None, become_user=None, check=False,
                      diff=False,
                      listhosts=None, listtasks=None, listtags=None, syntax=None)
    passwords = dict()  # 這個可以為空,因為在hosts檔案中
    #
    try:
        playbook = PlaybookExecutor(playbooks=["f1.yml"], inventory=im, variable_manager=vm, loader=dl, options=options, passwords=passwords)
        callback = MyPlaybookCallbackBase()
        playbook._tqm._stdout_callback = callback  # 配置callback
        playbook.run()

        # print(callback.task_ok.items())
        result_raw = {"ok": {}, "failed": {}, "unreachable": {}, "skipped": {}, "status": {}}
        for host, result in callback.task_ok.items():
            result_raw["ok"][host] = result._result

        for host, result in callback.task_failed.items():
            result_raw["failed"][host] = result._result

        for host, result in callback.task_unreachable.items():
            result_raw["unreachable"][host] = result._result

        for host, result in callback.task_skipped.items():
            result_raw["skipped"][host] = result._result

        for host, result in callback.task_status.items():
            result_raw["status"][host] = result._result

        print(result_raw)
    except Exception as err:
        print(err)


def main():
    usecallbackplaybook()


if __name__ == "__main__":
    try:
        main()
    finally:
        sys.exit()
View Code

 

相關文章