Run Ansible from Python using API

at first

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.

Source code

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
}

At the end

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.

reference

-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

Run Ansible from Python using API
I tried using UnityCloudBuild API from Python
Run python from excel
Run a Python file from html using Django
Run a python script from excel (using xlwings)
Flatten using Python yield from
Run illustrator script from python
Use e-Stat API from Python
Push notifications from Python to Android using Google's API
A little bit from Python using the Jenkins API
Data acquisition using python googlemap api
Using Rstan from Python with PypeR
Install Python from source with Ansible
[Python3] Google translate google translate without using api
Notes on using MeCab from Python
Try using Pleasant's API (python / FastAPI)
Using Cloud Storage from Python3 (Introduction)
Run Aprili from Python with Orange
Use kabu StationĀ® API from Python
Use the Flickr API from Python
Python error detection run from Powershell
Get upcoming weather from python weather api
Try using Python argparse's action API
Run Python scripts synchronously from C #
Predict gender from name using Gender API and Pykakasi in Python
Precautions when using phantomjs from python
Access spreadsheets using OAuth 2.0 from Python
Use Google Analytics API from Python
Handle SOAP API from Python (Zeep)
Run Python Scripts from Cisco Memorandum_EEM
Try using Amazon DynamoDB from Python
How to get followers and followers from python using the Mastodon API
Collecting information from Twitter with Python (Twitter API)
Mouse operation using Windows API in Python
[Python] Web application from 0! Hands-on (3) -API implementation-
Run Cloud Dataflow (Python) from App Engine
Try using the Wunderlist API in Python
From Python to using MeCab (and CaboCha)
Try using the Kraken API in Python
Tweet using the Twitter API in Python
Get Youtube data in Python using Youtube Data API
Run servomotor on Raspberry Pi 3 using python
Creating Google Spreadsheet using Python / Google Data API
API explanation to touch mastodon from python
Connect to coincheck's Websocket API from Python
Start using Python
sql from python
Detect Japanese characters from images using Google's Cloud Vision API in Python
Scraping using Python
Run a Python script from a C # GUI application
Procedure to use TeamGant's WEB API (using python)
I want to email from Gmail using Python.
Run Google Analytics API (core v3) in python
Get image URL using Flickr API in Python
Let's judge emotions using Emotion API in Python
Operate the schedule app using python from iphone
Recent ranking creation using Qiita API with Python
Manipulate objects using Blender 2.8's low-level Python API
Run VMware vSphere 6 vSphere API with Python script (pyvmomi)
Load images from URLs using Pillow in Python 3
Anonymous upload of images using Imgur API (using Python)