从零打造企业内部Ansible自动化管理平台-第一章


一. 背景;

      随着中下企业技术的快速技术迭代,以及微服务分布式架构的普及,传统运维系统运维环境治理方面,系统初始化,环境部署,环境一致性困难较大. 由此      ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,

      实现了批量系统配置、批量程序部署、环境快速部署迭代,批量运行命令等功能。

       Ansible 工作原理和关联模块介绍:

        ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。主要包括:

        (1)、连接插件connection plugins:负责和被监控端实现通信;

        (2)、host inventory:指定操作的主机,是一个配置文件里面定义监控的主机;

        (3)、各种模块核心模块、command模块、自定义模块;

        (4)、借助于插件完成记录日志邮件等功能;

        (5)、playbook:剧本执行多个任务时,非必需可以让节点一次性运行多个任务。


二. 本章概述:

       本文主要描述ansible 2.7.2 版本官方python 3 版本Api 封装, (内容:Ansible 数据结构返回,host inventory动态主机,playbook 执行返回)


 三. 原生ansible-api 封装代码部分;

  官网参考连接: https://docs.ansible.com/ansible/latest/dev_guide/developing_api.html

  完整项目代码请移步到githup:  https://github.com/breaklinux/devops-bmc-api/


四.项目内部设计如下图:

     devops-bmc-api.jpg



五.项目示例代码如下:

1.封装Ansible-api 实际代码片段如下: 

"""    
@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. Inventory动态主机代码如下;

#!/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"  ###获取动态主机接口###
    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)))