Ansible的python接口
来自CloudWiki
原始版
代码
#!/usr/bin/env python from __future__ import (absolute_import, division, print_function) __metaclass__ = type import json import shutil import ansible.constants as C from ansible.executor.task_queue_manager import TaskQueueManager from ansible.module_utils.common.collections import ImmutableDict from ansible.inventory.manager import InventoryManager from ansible.parsing.dataloader import DataLoader from ansible.playbook.play import Play from ansible.plugins.callback import CallbackBase from ansible.vars.manager import VariableManager from ansible import context # Create a callback plugin so we can capture the output class ResultsCollectorJSONCallback(CallbackBase): """A sample callback plugin used for performing an action as results come in. If you want to collect all results into a single object for processing at the end of the execution, look into utilizing the ``json`` callback plugin or writing your own custom callback plugin. """ def __init__(self, *args, **kwargs): super(ResultsCollectorJSONCallback, self).__init__(*args, **kwargs) self.host_ok = {} self.host_unreachable = {} self.host_failed = {} def v2_runner_on_unreachable(self, result): host = result._host self.host_unreachable[host.get_name()] = result def v2_runner_on_ok(self, result, *args, **kwargs): """Print a json representation of the result. Also, store the result in an instance attribute for retrieval later """ host = result._host self.host_ok[host.get_name()] = result print(json.dumps({host.name: result._result}, indent=4)) def v2_runner_on_failed(self, result, *args, **kwargs): host = result._host self.host_failed[host.get_name()] = result def main(): host_list = ['localhost', '192.168.100.10', '192.168.100.20'] # since the API is constructed for CLI it expects certain options to always be set in the context object context.CLIARGS = ImmutableDict(connection='smart', module_path=['/to/mymodules', '/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, verbosity=0) # required for # https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/manager.py#L204 sources = ','.join(host_list) if len(host_list) == 1: sources += ',' # initialize needed objects loader = DataLoader() # Takes care of finding and reading yaml, json and ini files passwords = dict(vault_pass='secret') # Instantiate our ResultsCollectorJSONCallback for handling results as they come in. Ansible expects this to be one of its main display outlets results_callback = ResultsCollectorJSONCallback() # create inventory, use path to host config file as source or hosts in a comma separated string inventory = InventoryManager(loader=loader, sources=sources) # variable manager takes care of merging all the different sources to give you a unified view of variables available in each context variable_manager = VariableManager(loader=loader, inventory=inventory) # instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks # IMPORTANT: This also adds library dirs paths to the module loader # IMPORTANT: and so it must be initialized before calling `Play.load()`. tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, passwords=passwords, stdout_callback=results_callback, # Use our custom callback instead of the ``default`` callback plugin, which prints to stdout ) # create data structure that represents our play, including tasks, this is basically what our YAML loader does internally. play_source = dict( name="Ansible Play", hosts=host_list, gather_facts='no', tasks=[ dict(action=dict(module='shell', args='ls'), register='shell_out'), dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))), dict(action=dict(module='command', args=dict(cmd='/usr/bin/uptime'))), ] ) # Create play object, playbook objects use .load instead of init or new methods, # this will also automatically create the task objects from the info provided in play_source play = Play().load(play_source, variable_manager=variable_manager, loader=loader) # Actually run it try: result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methods finally: # we always need to cleanup child procs and the structures we use to communicate with them tqm.cleanup() if loader: loader.cleanup_all_tmp_files() # Remove ansible tmpdir shutil.rmtree(C.DEFAULT_LOCAL_TMP, True) print("UP ***********") for host, result in results_callback.host_ok.items(): print('{0} >>> {1}'.format(host, result._result['stdout'])) print("FAILED *******") for host, result in results_callback.host_failed.items(): print('{0} >>> {1}'.format(host, result._result['msg'])) print("DOWN *********") for host, result in results_callback.host_unreachable.items(): print('{0} >>> {1}'.format(host, result._result['msg'])) if __name__ == '__main__': main()
输出
{ "192.168.100.20": { "changed": true, "end": "2022-05-17 12:39:28.471664", "stdout": "anaconda-ks.cfg\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz", "cmd": "ls", "start": "2022-05-17 12:39:28.436026", "delta": "0:00:00.035638", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "ls", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ "anaconda-ks.cfg", "flask-test", "hello.txt", "libdb4-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm.1", "list.txt", "Python-3.7.5", "Python-3.7.5.tgz" ], "stderr_lines": [], "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "_ansible_no_log": false } } { "localhost": { "changed": true, "end": "2022-05-17 12:39:22.319828", "stdout": "anaconda-ks.cfg\nansible_test\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz\ntest3.py\ntime.txt", "cmd": "ls", "start": "2022-05-17 12:39:22.278211", "delta": "0:00:00.041617", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "ls", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ "anaconda-ks.cfg", "ansible_test", "flask-test", "hello.txt", "libdb4-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm.1", "list.txt", "Python-3.7.5", "Python-3.7.5.tgz", "test3.py", "time.txt" ], "stderr_lines": [], "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "_ansible_no_log": false } } { "192.168.100.10": { "changed": true, "end": "2022-05-17 12:39:22.381183", "stdout": "anaconda-ks.cfg\nansible_test\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz\ntest3.py\ntime.txt", "cmd": "ls", "start": "2022-05-17 12:39:22.331618", "delta": "0:00:00.049565", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "ls", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ "anaconda-ks.cfg", "ansible_test", "flask-test", "hello.txt", "libdb4-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm", "libdb4-devel-4.8.30-13.el7.x86_64.rpm.1", "list.txt", "Python-3.7.5", "Python-3.7.5.tgz", "test3.py", "time.txt" ], "stderr_lines": [], "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python" }, "_ansible_no_log": false } } { "localhost": { "msg": "anaconda-ks.cfg\nansible_test\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz\ntest3.py\ntime.txt", "_ansible_verbose_always": true, "_ansible_no_log": false, "changed": false } } { "192.168.100.10": { "msg": "anaconda-ks.cfg\nansible_test\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz\ntest3.py\ntime.txt", "_ansible_verbose_always": true, "_ansible_no_log": false, "changed": false } } { "192.168.100.20": { "msg": "anaconda-ks.cfg\nflask-test\nhello.txt\nlibdb4-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm\nlibdb4-devel-4.8.30-13.el7.x86_64.rpm.1\nlist.txt\nPython-3.7.5\nPython-3.7.5.tgz", "_ansible_verbose_always": true, "_ansible_no_log": false, "changed": false } } { "192.168.100.10": { "changed": true, "end": "2022-05-17 12:39:25.567167", "stdout": " 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11", "cmd": [ "/usr/bin/uptime" ], "start": "2022-05-17 12:39:25.550868", "delta": "0:00:00.016299", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": false, "strip_empty_ends": true, "_raw_params": "/usr/bin/uptime", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ " 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11" ], "stderr_lines": [], "_ansible_no_log": false } } { "localhost": { "changed": true, "end": "2022-05-17 12:39:25.590118", "stdout": " 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11", "cmd": [ "/usr/bin/uptime" ], "start": "2022-05-17 12:39:25.572084", "delta": "0:00:00.018034", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": false, "strip_empty_ends": true, "_raw_params": "/usr/bin/uptime", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ " 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11" ], "stderr_lines": [], "_ansible_no_log": false } } { "192.168.100.20": { "changed": true, "end": "2022-05-17 12:39:32.114154", "stdout": " 12:39:32 up 1:26, 1 user, load average: 0.40, 0.11, 0.07", "cmd": [ "/usr/bin/uptime" ], "start": "2022-05-17 12:39:32.084176", "delta": "0:00:00.029978", "stderr": "", "rc": 0, "invocation": { "module_args": { "creates": null, "executable": null, "_uses_shell": false, "strip_empty_ends": true, "_raw_params": "/usr/bin/uptime", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null } }, "msg": "", "stdout_lines": [ " 12:39:32 up 1:26, 1 user, load average: 0.40, 0.11, 0.07" ], "stderr_lines": [], "_ansible_no_log": false } } UP *********** 192.168.100.20 >>> 12:39:32 up 1:26, 1 user, load average: 0.40, 0.11, 0.07 localhost >>> 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11 192.168.100.10 >>> 12:39:25 up 1:26, 4 users, load average: 0.99, 0.23, 0.11 FAILED *******
参考文档
https://docs.ansible.com/ansible/latest/dev_guide/developing_api.html#python-api-2-0
改进版
代码
#!/usr/bin/env python from __future__ import (absolute_import, division, print_function) __metaclass__ = type import json import shutil import ansible.constants as C from ansible.executor.task_queue_manager import TaskQueueManager from ansible.module_utils.common.collections import ImmutableDict from ansible.inventory.manager import InventoryManager from ansible.parsing.dataloader import DataLoader from ansible.playbook.play import Play from ansible.plugins.callback import CallbackBase from ansible.vars.manager import VariableManager from ansible import context # Create a callback plugin so we can capture the output class ResultsCollectorJSONCallback(CallbackBase): """A sample callback plugin used for performing an action as results come in. If you want to collect all results into a single object for processing at the end of the execution, look into utilizing the ``json`` callback plugin or writing your own custom callback plugin. """ def __init__(self, *args, **kwargs): super(ResultsCollectorJSONCallback, self).__init__(*args, **kwargs) self.host_ok = {} self.host_unreachable = {} self.host_failed = {} def v2_runner_on_unreachable(self, result): host = result._host self.host_unreachable[host.get_name()] = result def v2_runner_on_ok(self, result, *args, **kwargs): """Print a json representation of the result. Also, store the result in an instance attribute for retrieval later """ host = result._host self.host_ok[host.get_name()] = result print(json.dumps({host.name: result._result}, indent=4)) def v2_runner_on_failed(self, result, *args, **kwargs): host = result._host self.host_failed[host.get_name()] = result def do_ansible(host_list,tasks): # since the API is constructed for CLI it expects certain options to always be set in the context object context.CLIARGS = ImmutableDict(connection='smart', module_path=['/to/mymodules', '/usr/share/ansible'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False, verbosity=0) # required for # https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/manager.py#L204 sources = ','.join(host_list) if len(host_list) == 1: sources += ',' # initialize needed objects loader = DataLoader() # Takes care of finding and reading yaml, json and ini files passwords = dict(vault_pass='secret') # Instantiate our ResultsCollectorJSONCallback for handling results as they come in. Ansible expects this to be one of its main display outlets results_callback = ResultsCollectorJSONCallback() # create inventory, use path to host config file as source or hosts in a comma separated string inventory = InventoryManager(loader=loader, sources=sources) # variable manager takes care of merging all the different sources to give you a unified view of variables available in each context variable_manager = VariableManager(loader=loader, inventory=inventory) # instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks # IMPORTANT: This also adds library dirs paths to the module loader # IMPORTANT: and so it must be initialized before calling `Play.load()`. tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, passwords=passwords, stdout_callback=results_callback, # Use our custom callback instead of the ``default`` callback plugin, which prints to stdout ) # create data structure that represents our play, including tasks, this is basically what our YAML loader does internally. play_source = dict( name="Ansible Play", hosts=host_list, gather_facts='no', tasks=tasks ) # Create play object, playbook objects use .load instead of init or new methods, # this will also automatically create the task objects from the info provided in play_source play = Play().load(play_source, variable_manager=variable_manager, loader=loader) # Actually run it try: result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methods finally: # we always need to cleanup child procs and the structures we use to communicate with them tqm.cleanup() if loader: loader.cleanup_all_tmp_files() # Remove ansible tmpdir shutil.rmtree(C.DEFAULT_LOCAL_TMP, True) print("UP ***********") for host, result in results_callback.host_ok.items(): print('{0} >>> {1}'.format(host, result._result['stdout'])) print("FAILED *******") for host, result in results_callback.host_failed.items(): print('{0} >>> {1}'.format(host, result._result['msg'])) print("DOWN *********") for host, result in results_callback.host_unreachable.items(): print('{0} >>> {1}'.format(host, result._result['msg'])) if __name__ == '__main__': host_list = ['localhost', '119.3.251.91', '119.3.212.154']#全部机器列表 room306 = ['119.3.212.154']#306机房机器列表 #管理命令列表 tasks=[ dict(action=dict(module='shell', args='ls'), register='shell_out'), #dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))), #dict(action=dict(module='command', args=dict(cmd='/usr/bin/uptime'))), ] #do_ansible(host_list,tasks) do_ansible(room306,tasks)
输出
{ "119.3.212.154": { "changed": true, "stdout": "3.py\nansible.cfg\nansible_hosts\nelasticsearch-7.17.1-linux-x86_64.tar.gz\nes_test2.py\nes_test.py\nmail-py\nwmtools", "stderr": "", "rc": 0, "cmd": "ls", "start": "2022-07-24 08:11:16.465014", "end": "2022-07-24 08:11:16.469472", "delta": "0:00:00.004458", "msg": "", "invocation": { "module_args": { "_raw_params": "ls", "_uses_shell": true, "warn": false, "stdin_add_newline": true, "strip_empty_ends": true, "argv": null, "chdir": null, "executable": null, "creates": null, "removes": null, "stdin": null } }, "stdout_lines": [ "3.py", "ansible.cfg", "ansible_hosts", "elasticsearch-7.17.1-linux-x86_64.tar.gz", "es_test2.py", "es_test.py", "mail-py", "wmtools" ], "stderr_lines": [], "ansible_facts": { "discovered_interpreter_python": "/usr/libexec/platform-python" }, "_ansible_no_log": false } } UP *********** 119.3.212.154 >>> 3.py ansible.cfg ansible_hosts elasticsearch-7.17.1-linux-x86_64.tar.gz es_test2.py es_test.py mail-py wmtools FAILED ******* DOWN *********