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)))