Build an internal Ansible automated management platform from scratch - Chapter 1 (Api)

Posted by s0me0ne on Sun, 05 Apr 2020 23:14:12 +0200

Build an internal Ansible automated management platform from scratch - Chapter 1


1. Background;

With the rapid technological iteration of lower and middle-level enterprise technologies and the popularity of micro-service distributed architecture, it is difficult for traditional operation and maintenance systems to initialize systems, deploy environments, and consistency of environments. As a result, ansible is a new automated operation and maintenance tool based on Python development.It combines the advantages of many operations and maintenance tools (puppet, cfengine, chef, func, fabric).

Functions such as batch system configuration, batch program deployment, rapid environment deployment iteration, and batch command running are implemented.

Introduction to Ansible's working principle and related modules:

Ansible is module based and does not have the capacity to deploy in bulk.What really has a bulk deployment is the modules that ansible runs, and ansible only provides a framework.Mainly include:

(1) connection plugins: responsible and monitored for communication;

(2), host inventory: The host for the specified operation is the host for which monitoring is defined in the configuration file;

(3), various module core modules, command modules, custom modules;

(4) Logging mail functions with plug-ins;

(5), playbook: When a script performs multiple tasks, it is not necessary for a node to run multiple tasks at once.


2. Overview of this chapter:

This paper mainly describes the ansible version 2.7.2 official Python version 3 Api encapsulation, (Content: Ansible data structure return, host inventory dynamic host, playbook execution return)


3. Native ansible-api encapsulation code section;

Official Reference Connection: https://docs.ansible.com/ansible/latest/dev_guide/developing_api.html

Step to githup for complete project code: https://github.com/breaklinux/devops-bmc-api/


4. The internal design of the project is as follows:

     



5. The project sample code is as follows:

1.encapsulation Ansible-api The actual code snippet is as follows: 

"""    
@author:lijx    
@contact: 360595252@qq.com    
@site: https://blog.51cto.com/breaklinux    
@version: 1.0 
@githup:https://github.com/breaklinux/devops-bmc-api/   
""" 
import json
from collections import namedtuple
from ansible.executor.task_queue_manager import TaskQueueManager
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.errors import AnsibleParserError

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_ok(self, result, **kwargs):
        self.host_ok[result._host.get_name()] = result
        host = result._host
        print(json.dumps({host.name: result._result}, indent=4))
    def v2_runner_on_unreachable(self, result):
        self.host_unreachable[result._host.get_name()] = result

    def v2_runner_on_failed(self, result, *args, **kwargs):
        self.host_failed[result._host.get_name()] = result
class AnsibleApi(object):

    def __init__(self, resource, user, becomeuser, playvars={}, *args, **kwargs):
        self._resource = resource
        self._user = user
        self._becomeuser = becomeuser
        self.inventory = None
        self.playvars = playvars  # add
        self.variable_manager = None
        self.loader = None
        self.options = None
        self.passwords = None
        self.callback = None
        self.__initializeAnsibleData()
        self.results_raw = {}
        
    def __initializeAnsibleData(self):
        Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'timeout', 'remote_user',
                                         'ask_pass', 'private_key_file', 'ssh_common_args', 'ssh_extra_args',
                                         'sftp_extra_args',
                                         'scp_extra_args', 'become', 'become_method', 'become_user', 'ask_value_pass',
                                         'verbosity',
                                         'check', 'listhosts', 'listtasks', 'listtags', 'syntax', 'diff'])
        self.options = Options(connection='ssh', module_path=None, forks=100, timeout=5,
                               remote_user=self._user, ask_pass=False, private_key_file=None, ssh_common_args=None,
                               ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=True,
                               become_method='sudo',
                               become_user=self._becomeuser, ask_value_pass=False, verbosity=None, check=False,
                               listhosts=False,
                               listtasks=False, listtags=False, syntax=False, diff=False)
        self.loader = DataLoader()
        self.passwords = dict(sshpass=None, becomepass=None)
        self.inventory = InventoryManager(loader=self.loader, sources=self._resource)
        self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
        self.variable_manager.extra_vars = self.playvars
        
    def run(self, host_list, module_name, module_args, ):
        play_source = dict(
            name="Ansible Play For  At 20190104",
            hosts=host_list,
            gather_facts='no',
            tasks=[
                dict(action=dict(module=module_name, args=module_args))]

        )
        play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader)
        tqm = None
        self.callback = ResultCallback()
        try:
            tqm = TaskQueueManager(
                inventory=self.inventory,
                variable_manager=self.variable_manager,
                loader=self.loader,
                options=self.options,
                passwords=self.passwords,
                stdout_callback="default",
               
            )
            tqm._stdout_callback = self.callback
            result = tqm.run(play)  
        finally:
            if tqm is not None:
                tqm.cleanup()
           

    def playbookRun(self, playbook_path):
        from ansible.executor.playbook_executor import PlaybookExecutor

        playbook = PlaybookExecutor(playbooks=playbook_path,
                                    inventory=self.inventory,
                                    variable_manager=self.variable_manager,
                                    loader=self.loader,
                                    options=self.options,
                                    passwords=self.passwords)

        self.callback = ResultCallback()
        playbook._tqm._stdout_callback = self.callback
        try:
            result = playbook.run()
        except AnsibleParserError:
            code = 1001
            results = {'playbook': playbook_path, 'msg': playbook_path + 'playbook have syntax error', 'flag': False}
            return code, results

    def get_result(self):
        self.results_raw = {'success': {}, 'failed': {}, 'unreachable': {}}
        for host, result in self.callback.host_ok.items():
            self.results_raw['success'][host] = result._result

        for host, result in self.callback.host_failed.items():
            self.results_raw['failed'][host] = result._result

        for host, result in self.callback.host_unreachable.items():
            self.results_raw['unreachable'][host] = result._result['msg']

        return self.results_raw


    def get_result_v2(self):
        self.results_raw = {'success': list(), 'failed': list(), 'unreachable': list()}

        for host, result in self.callback.host_ok.items():
            self.results_raw['success'].append({"ip": host, "result": result._result})

        for host, result in self.callback.host_failed.items():
            self.results_raw['failed'].append({"ip": host, "result": result._result})

        for host, result in self.callback.host_unreachable.items():
            self.results_raw['unreachable'].append({"ip": host, "result": result._result['msg']})
        return self.results_raw


if __name__ == "__main__":
    print("Ansible Api By  20190104 Ansible Version: 2.7.5  Test Ok")


2. The Inventory Dynamic Host code is as follows;

#!/usr/bin/env python36
"""
@author:lijx
@contact: 360595252@qq.com
@site: https://blog.51cto.com/breaklinux
@version: 1.0
"""
from flask import request, Response
import requests
import os
HERE = os.path.abspath(__file__)
HOME_DIR = os.path.split(os.path.split(HERE)[0])[0]
script_path = os.path.join(HOME_DIR, "tools")
def getHostInventoryData(url):
    import json
    gethostdata = requests.get(url)
    getdata = gethostdata.json()["data"]
    data = dict()
    l=[]
    for i in getdata:
        l.append(i["group"])
    groups = set(l)
    gdata=str(groups)
    data["all"] = {"children": gdata}
    data["_meta"] = {"hostvars": {}}
    for group in groups:
        data[group] = dict()
        data[group]["hosts"] = list()
        for x in getdata:
            if x["group"] == group:
               data[group]["hosts"].append(x["instanceip"])
    return json.dumps(data, indent=5)

def HostApi():
    getInventoryUrl = "http://192.168.58.14:5000/ansible/host/v1"
    import json
    import configparser
    data = json.loads(getHostInventoryData(getInventoryUrl))

    config = configparser.ConfigParser(allow_no_value=True)
    for i in data:
        if i != "all" and i != "_meta":
            config.add_section(i)
            for h in data[i]["hosts"]:
                config.set(i, h)
            config.write(open("%s/static_hosts"%script_path, "w"))
    return True


if __name__ == "__main__":
    from optparse import OptionParser
    getInventoryUrl = "http://192.168.58.14:5000/ansible/host/v1 "###Get Dynamic Host Interface ##
    parse = OptionParser()
    parse.add_option("-l", "--list", action="store_true", dest="list", default=False)
    (option, arges) = parse.parse_args()
    if option.list:
        print(getHostInventoryData(getInventoryUrl))
    else:
        import json
        print(json.loads(getHostInventoryData(getInventoryUrl)))







Topics: Linux ansible JSON Python github