如何把動態生成資產信息、執行playbook以及自定義結果結合起來用呢?python
#!/usr/bin/env python # -*- coding: utf-8 -*- """ 經過ansible API動態生成ansible資產信息但不產生實際的hosts文件 主機信息均可以經過數據庫得到,而後生成指定格式,最後調用這個類來 生成主機信息。 """ import sys # 用於讀取YAML和JSON格式的文件 from ansible.executor.playbook_executor import PlaybookExecutor from ansible.parsing.dataloader import DataLoader # 用於存儲各種變量信息 from ansible.vars.manager import VariableManager # 用於導入資產文件 from ansible.inventory.manager import InventoryManager # 操做單個主機信息 from ansible.inventory.host import Host # 操做單個主機組信息 from ansible.inventory.group import Group # 狀態回調,各類成功失敗的狀態 from ansible.plugins.callback import CallbackBase from collections import namedtuple class PlaybookCallResultCollector(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"] # } class MyInventory: def __init__(self, hostsresource): """ 初始化函數 :param hostsresource: 主機資源能夠有2種形式 列表形式: [{"ip": "172.16.48.171", "port": "22", "username": "root", "password": "123456"}] 字典形式: { "Group1": { "hosts": [{"ip": "192.168.200.10", "port": "1314", "username": "root", "password": None}], "vars": {"var1": "ansible"} }, "Group2": {} } """ self._hostsresource = hostsresource self._loader = DataLoader() self._hostsfilelist = ["temphosts"] """ sources這個咱們知道這裏是設置hosts文件的地方,它能夠是一個列表裏面包含多個文件路徑且文件真實存在,在單純的執行ad-hoc的時候這裏的 文件裏面必須具備有效的hosts配置,可是當經過動態生成的資產信息的時候這個文件必須存在可是它裏面能夠是空的,若是這裏配置成None那麼 它不影響資產信息動態生成可是會有一個警告,因此仍是要配置一個真實文件。 """ self._inventory = InventoryManager(loader=self._loader, sources=self._hostsfilelist) self._variable_manager = VariableManager(loader=self._loader, inventory=self._inventory) self._dynamic_inventory() def _add_dynamic_group(self, hosts_list, groupname, groupvars=None): """ 動態添加主機到指定的主機組 完整的HOSTS文件格式 [test1] hostname ansible_ssh_host=192.168.1.111 ansible_ssh_user="root" ansible_ssh_pass="123456" 但一般咱們都省略hostname,端口也省略由於默認是22,這個在ansible配置文件中有,除非有非22端口的纔會配置 [test1] 192.168.100.10 ansible_ssh_user="root" ansible_ssh_pass="123456" ansible_python_interpreter="/PATH/python3/bin/python3" :param hosts_list: 主機列表 [{"ip": "192.168.100.10", "port": "22", "username": "root", "password": None}, {}] :param groupname: 組名稱 :param groupvars: 組變量,格式爲字典 :return: """ # 添加組 my_group = Group(name=groupname) self._inventory.add_group(groupname) # 添加組變量 if groupvars: for key, value in groupvars.items(): my_group.set_variable(key, value) # 添加一個主機 for host in hosts_list: hostname = host.get("hostname", None) hostip = host.get("ip", None) if hostip is None: print("IP地址爲空,跳過該元素。") continue hostport = host.get("port", "22") username = host.get("username", "root") password = host.get("password", None) ssh_key = host.get("ssh_key", None) python_interpreter = host.get("python_interpreter", None) try: # hostname能夠不寫,若是爲空默認就是IP地址 if hostname is None: hostname = hostip # 生成一個host對象 my_host = Host(name=hostname, port=hostport) # 添加主機變量 self._variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_host", value=hostip) self._variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_port", value=hostport) if password: self._variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_pass", value=password) self._variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_user", value=username) if ssh_key: self._variable_manager.set_host_variable(host=my_host, varname="ansible_ssh_private_key_file", value=ssh_key) if python_interpreter: self._variable_manager.set_host_variable(host=my_host, varname="ansible_python_interpreter", value=python_interpreter) # 添加其餘變量 for key, value in host.items(): if key not in ["ip", "hostname", "port", "username", "password", "ssh_key", "python_interpreter"]: self._variable_manager.set_host_variable(host=my_host, varname=key, value=value) # 添加主機到組 self._inventory.add_host(host=hostname, group=groupname, port=hostport) except Exception as err: print(err) def _dynamic_inventory(self): """ 添加 hosts 到inventory :return: """ if isinstance(self._hostsresource, list): self._add_dynamic_group(self._hostsresource, "default_group") elif isinstance(self._hostsresource, dict): for groupname, hosts_and_vars in self._hostsresource.items(): self._add_dynamic_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) @property def INVENTORY(self): """ 返回資產實例 :return: """ return self._inventory @property def VARIABLE_MANAGER(self): """ 返回變量管理器實例 :return: """ return self._variable_manager class AnsibleRunner(object): def __init__(self, hostsresource): 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" ]) self._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) self._passwords = dict(sshpass=None, becomepass=None) # 這個能夠爲空,由於在hosts文件中 self._loader = DataLoader() myinven = MyInventory(hostsresource=hostsresource) self._inventory = myinven.INVENTORY self._variable_manager = myinven.VARIABLE_MANAGER def run_playbook(self, playbook_path, extra_vars=None): """ 執行playbook :param playbook_path: playbook的yaml文件路徑 :param extra_vars: 額外變量 :return: 無返回值 """ try: if extra_vars: self._variable_manager.extra_vars = extra_vars playbook = PlaybookExecutor(playbooks=[playbook_path], inventory=self._inventory, variable_manager=self._variable_manager, loader=self._loader, options=self._options, passwords=self._passwords) # 配置使用自定義callback self._callback = PlaybookCallResultCollector() playbook._tqm._stdout_callback = self._callback # 執行playbook playbook.run() except Exception as err: print(err) def get_playbook_result(self): """ 獲取playbook執行結果 :return: """ result_raw = {"ok": {}, "failed": {}, "unreachable": {}, "skipped": {}, "status": {}} for host, result in self._callback.task_ok.items(): result_raw["ok"][host] = result._result for host, result in self._callback.task_failed.items(): result_raw["failed"][host] = result._result for host, result in self._callback.task_unreachable.items(): result_raw["unreachable"][host] = result._result for host, result in self._callback.task_skipped.items(): result_raw["skipped"][host] = result._result for host, result in self._callback.task_status.items(): result_raw["status"][host] = result._result return result_raw def main(): temphosts_list = [{"ip": "172.16.48.250", "port": "22", "username": "root", "password": "12qwaszx!@QW"}] temphosts_dict = { "Group1": { "hosts": [{"ip": "192.168.200.10", "port": "1314", "username": "root", "password": None}], "vars": {"var1": "ansible"} }, # "Group2": {} } # mi = MyInventory(temphosts_list) # for group, hosts in mi.INVENTORY.get_groups_dict().items(): # print(group, hosts) # host = mi.INVENTORY.get_host("192.168.200.10") # print(mi.VARIABLE_MANAGER.get_vars(host=host)) ar = AnsibleRunner(temphosts_list) ar.run_playbook("/Users/rex.chen/PycharmProjects/IDCMigration/AnsibleStudy/f1.yml") print(ar.get_playbook_result()) if __name__ == "__main__": try: main() finally: sys.exit()