Ansible is convenient, isn't it? I think it's God when you do the same work on many servers.
It feels like it's fully automated just by hitting the playbook, but I also wanted to automate the execution of the playbook in a nice way. You can write a shell script, but it's a hassle to parse the results.
So, this time, I would like to hit Ansible from Python in a good way. I couldn't find the article unexpectedly.
It is easy to understand if you read from __main__
.
A playbook that runs ls -la /
on 192.168.0.1
.
If you have used Ansible in commands, you should be able to read it easily.
Please refer to the simple comments here and there.
pip3 install ansible
import json
import shutil
from ansible.module_utils.common.collections import ImmutableDict
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.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
from ansible import context
import ansible.constants as C
class ResultCallback(CallbackBase):
def __init__(self, *args, **kwargs):
super(ResultCallback, 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):
host = result._host
self.host_ok[host.get_name()] = result
def v2_runner_on_failed(self, result, *args, **kwargs):
host = result._host
self.host_failed[host.get_name()] = result
def ansible_run(play_source, host_list):
# ansible-Same as the argument that can be specified in playbook
context.CLIARGS = ImmutableDict(
tags={},
listtags=False,
listtasks=False,
listhosts=False,
syntax=False,
connection='ssh',
module_path=None,
forks=100,
private_key_file=None,
ssh_common_args=None,
ssh_extra_args=None,
sftp_extra_args=None,
scp_extra_args=None,
become=False,
become_method='Sudo',
become_user='root',
verbosity=True,
check=False,
start_at_task=None
)
#Priority is given to key authentication, and it is used only when the password is asked. If you don't want to write, it's OK
passwords = dict(vault_pass='secret')
#Callback instantiation
results_callback = ResultCallback()
#Convert inventory to 1-liner format
sources = ','.join(host_list)
if len(host_list) == 1:
sources += ','
loader = DataLoader()
inventory = InventoryManager(loader=loader, sources=sources)
#Set value
variable_manager = VariableManager(loader=loader, inventory=inventory)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
#Run
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
passwords=passwords,
stdout_callback=results_callback,
)
result = tqm.run(play)
finally:
#Deleting temporary files after exiting
if tqm is not None:
tqm.cleanup()
# Remove ansible tmpdir
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
return results_callback
if __name__ == "__main__":
#Specify the execution host (also specified in the inventory)
host_list = [ "[email protected]" ]
#Define playbook
play_source = dict(
name = "Ansible Play",
hosts = host_list,
gather_facts = 'no',
tasks = [
dict(action=dict(module='shell', args='ls -l /'), register='shell_out')
]
)
results = ansible_run(play_source=play_source, host_list=host_list)
for host, result in results.host_ok.items():
print(host)
print(json.dumps(result._result, indent=4))
for host, result in results.host_failed.items():
print(host)
print(json.dumps(result._result, indent=4))
for host, result in results.host_unreachable.items():
print(host)
print(json.dumps(result._result, indent=4))
You can get the dictionary of execution results like this. The following is an excerpt of the output in Json.
{
"cmd": "ls -l /",
"stdout": "total 28\nlrwxrwxrwx. 1 root root 7 May 11 2019 bin -> usr/bin\ndr-xr-xr-x. 6 root root 4096 Nov 12 18:17 boot\ndrwxr-xr-x. 7 root root 65 Nov 17 00:41 data\ndrwxr-xr-x. 21 root root 3580 Nov 23 12:10 dev\ndrwxr-xr-x. 104 root root 8192 Nov 22 14:11 etc\ndrwxr-xr-x. 6 root root 4096 Nov 20 13:06 gvolume0\ndrwxr-xr-x. 3 root root 4096 Nov 17 00:47 gvolume1\ndrwxr-xr-x. 3 root root 19 Nov 10 01:24 home\nlrwxrwxrwx. 1 root root 7 May 11 2019 lib -> usr/lib\nlrwxrwxrwx. 1 root root 9 May 11 2019 lib64 -> usr/lib64\ndrwxr-xr-x. 2 root root 6 May 11 2019 media\ndrwxr-xr-x. 2 root root 6 May 11 2019 mnt\ndrwxr-xr-x. 2 root root 6 May 11 2019 opt\ndr-xr-xr-x. 1056 root root 0 Nov 22 14:04 proc\ndr-xr-x---. 4 root root 192 Nov 23 11:27 root\ndrwxr-xr-x. 32 root root 1100 Nov 22 14:46 run\nlrwxrwxrwx. 1 root root 8 May 11 2019 sbin -> usr/sbin\ndrwxr-xr-x. 2 root root 6 May 11 2019 srv\ndr-xr-xr-x. 13 root root 0 Nov 22 14:04 sys\ndrwxrwxrwt. 9 root root 212 Nov 23 22:51 tmp\ndrwxr-xr-x. 12 root root 144 Nov 10 01:22 usr\ndrwxr-xr-x. 21 root root 4096 Nov 10 01:28 var",
"stderr": "",
"rc": 0,
"start": "2020-11-23 22:51:11.787866",
"end": "2020-11-23 22:51:11.793951",
"delta": "0:00:00.006085",
"changed": true,
"invocation": {
"module_args": {
"_raw_params": "ls -l /",
"_uses_shell": true,
"warn": true,
"stdin_add_newline": true,
"strip_empty_ends": true,
"argv": null,
"chdir": null,
"executable": null,
"creates": null,
"removes": null,
"stdin": null
}
},
"stdout_lines": [
"total 28",
"lrwxrwxrwx. 1 root root 7 May 11 2019 bin -> usr/bin",
"dr-xr-xr-x. 6 root root 4096 Nov 12 18:17 boot",
"drwxr-xr-x. 7 root root 65 Nov 17 00:41 data",
"drwxr-xr-x. 21 root root 3580 Nov 23 12:10 dev",
"drwxr-xr-x. 104 root root 8192 Nov 22 14:11 etc",
"drwxr-xr-x. 6 root root 4096 Nov 20 13:06 gvolume0",
"drwxr-xr-x. 3 root root 4096 Nov 17 00:47 gvolume1",
"drwxr-xr-x. 3 root root 19 Nov 10 01:24 home",
"lrwxrwxrwx. 1 root root 7 May 11 2019 lib -> usr/lib",
"lrwxrwxrwx. 1 root root 9 May 11 2019 lib64 -> usr/lib64",
"drwxr-xr-x. 2 root root 6 May 11 2019 media",
"drwxr-xr-x. 2 root root 6 May 11 2019 mnt",
"drwxr-xr-x. 2 root root 6 May 11 2019 opt",
"dr-xr-xr-x. 1056 root root 0 Nov 22 14:04 proc",
"dr-xr-x---. 4 root root 192 Nov 23 11:27 root",
"drwxr-xr-x. 32 root root 1100 Nov 22 14:46 run",
"lrwxrwxrwx. 1 root root 8 May 11 2019 sbin -> usr/sbin",
"drwxr-xr-x. 2 root root 6 May 11 2019 srv",
"dr-xr-xr-x. 13 root root 0 Nov 22 14:04 sys",
"drwxrwxrwt. 9 root root 212 Nov 23 22:51 tmp",
"drwxr-xr-x. 12 root root 144 Nov 10 01:22 usr",
"drwxr-xr-x. 21 root root 4096 Nov 10 01:28 var"
],
"stderr_lines": [],
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"_ansible_no_log": false
}
The results are also easy to use in Python, so it's easy to combine with other systems. I was looking for this method because I wanted to hit Ansible from Python system written as a hobby. It would be great if it could be useful to other people.
-Red Hat, Inc. --Ansible Official API Example 1 -Red Hat, Inc. --Ansible Official API Example 2 -[it-swarm-ja.tech --Run ansible-playbook using Python API](https://www.it-swarm-ja.tech/ja/python/python-api%E3%82%92 % E4% BD% BF% E7% 94% A8% E3% 81% 97% E3% 81% A6ansibleplaybook% E3% 82% 92% E5% AE% 9F% E8% A1% 8C / 1050193047 /
Recommended Posts