一、动态Inventory的作用

在实际生产应用中,经常会遇到业务的快速发展或者流量的急剧增加等情况,需要在短时间内向架构中添加几十台甚至上百台服务器来提高整个架构的处理能力,这个时候,手动管理Inventory文件不仅没有效率,而且非常泛味。

二、调用CMDBA_PI,实时获取动态主机群组信息

以下脚本,在实际使用中,遇到较大的问题,如编历整个CMDB的业务信息,会导致ansible运行过久,执行效低,超时等问题,基于以上问题,我们考虑到用动态方法制作静态inventory文件。

#! /usr/bin/env python3
import os
import sys
import argparse,requests

try:
    import json
except ImportError:
    import simplejson as json


class ExampleInventory(object):
    # 读取并分析读入的选项和参数
    def read_cli_args(self):
        parser = argparse.ArgumentParser()
        parser.add_argument('--list', action='store_true')
        parser.add_argument('--host', action='store')
        self.args = parser.parse_args()

    # 用于展示效果的JSON格式的Inventory文件内容
    def example_inventory(self):
        return self.hosts

    # 返回仅用于测试的空Inventory
    def empty_inventory(self):
        return {'_meta': {'hostvars': {}}}

    def __init__(self,hosts):
        self.inventory = {}
        self.read_cli_args()
        self.hosts = hosts

        #定义'--list'选项
        if self.args.list:
            self.inventory = self.example_inventory()
        #定义'--host[hostname]'先项
        elif self.args.host:
            self.inventory = self.empty_inventory()
        #如果没有主机组或变量要设置,就返回一个空Inventory
        else:
            self.inventory = self.empty_inventory()

        print(json.dumps(self.inventory))

#获取当前所有业务的业务代码(作为ansible的主机组)
def get_sys_group():
    #关部信息
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

    #传递必要的登陆验证信息
    post_data = {
                 "bk_app_code": "bk_cmdb",
                 "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
                 "bk_username":"admin",
                 "page":{"start":0,
                         "limit":150}
                 }

    json_data = json.dumps(post_data)

    #数据请求
    res = requests.post('http://itpaas.xxxxx.com/api/c/compapi/v2/cc/search_business/',json_data)

    #API请求返回结果
    biz_data = json.loads(res.content.decode())
    #业务系统数量
    biz_sum = biz_data["data"]['count']
    #业务系统信息
    biz_info = biz_data["data"]["info"]

    rst= {}

    for i in biz_info:
        try:
            # print(i['bk_biz_id'],i['biz_code'])
            value = i['bk_biz_id']
            key = i['biz_code']
            rst[key] = value
        except:
            pass

    return rst

#获取每个业务系统的闲置主机ID
def get_idle_moduleid(bk_biz_id):
    # 传递必要的登陆验证信息
    post_data = {
        "bk_app_code": "bk_cmdb",
        "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
        "bk_username": "admin",
        "bk_biz_id": bk_biz_id,
        "bk_supplier_account": "0",
        "page": {"start": 0,
                 "limit": 150}
    }

    json_data = json.dumps(post_data)

    # 数据请求
    res = requests.post('http://itpaas.xxxxx.com/api/c/compapi/v2/cc/get_biz_internal_module/', json_data)

    # API请求返回结果
    biz_data = json.loads(res.content.decode())
    res = biz_data['data']['module'][0]['bk_module_id']
    return res

#根据业务代码ID,闲置主机的ID,查询该业务的所有主机(生产环境)
def get_biz_hosts(bk_biz_id,bk_modules_ids):
    post_data = {
        "bk_app_code": "bk_cmdb",
        "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
        "bk_username": "admin",
        "metadata": {
            "label": {
                "bk_biz_id": bk_biz_id
            }
        },
        "bk_module_ids": [bk_modules_ids],
        "page": {"start": 0,
                 "limit": 150}
    }

    json_data = json.dumps(post_data)

    # 数据请求
    res = requests.post('http://itpaas.xxxx.com/api/c/compapi/v2/cc/find_host_by_module/', json_data)

    # API请求返回结果
    biz_data = json.loads(res.content.decode())
    data_info = biz_data['data']['info']

    hosts_list = []
    for i in data_info:
        if i['host']['environment'] == "1" and i['host']['ping'] == "1":
            hosts_list.append(i['host']['bk_host_innerip'])

    return hosts_list

if __name__ == '__main__':
    biz_info = get_sys_group()
    group_list = []
    ansible_inventory = {}
    for i in biz_info:
        group_list.append(i)

    for i in group_list:
        bk_biz_id = str(biz_info[i])
        bk_module_ids = get_idle_moduleid(bk_biz_id)
        hosts_list = get_biz_hosts(bk_biz_id, bk_module_ids)
        # print(hosts_list)
        host_dict = dict(hosts=hosts_list)
        ansible_inventory[i] = host_dict
    hosts = ansible_inventory
    ExampleInventory(hosts)
	
#备注,实质上,我们只需要把拿到的数据,制作成相应格式的字段,然后转入ExampleInventory()类中即可,字典格式如下:
{"BK": {"hosts": ["10.3.153.9", "10.3.153.11"]}, "ZHAOBIAO": {"hosts": ["10.3.151.127", "10.3.151.129", "10.3.152.93"]}}

三、动态获取CMDB信息,生成静态inventory文件

#!/usr/bin/python3
import argparse,requests,json

#获取当前所有业务的业务代码(作为ansible的主机组)
def get_sys_group():
    #关部信息
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}

    #传递必要的登陆验证信息
    post_data = {
                 "bk_app_code": "bk_cmdb",
                 "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
                 "bk_username":"admin",
                 "page":{"start":0,
                         "limit":150}
                 }

    json_data = json.dumps(post_data)

    #数据请求
    res = requests.post('http://itpaas.xxxx.com/api/c/compapi/v2/cc/search_business/',json_data)

    #API请求返回结果
    biz_data = json.loads(res.content.decode())
    #业务系统数量
    biz_sum = biz_data["data"]['count']
    #业务系统信息
    biz_info = biz_data["data"]["info"]

    rst= {}

    for i in biz_info:
        try:
            # print(i['bk_biz_id'],i['biz_code'])
            value = i['bk_biz_id']
            key = i['biz_code']
            rst[key] = value
        except:
            pass

    return rst

#获取每个业务系统的闲置主机ID
def get_idle_moduleid(bk_biz_id):
    # 传递必要的登陆验证信息
    post_data = {
        "bk_app_code": "bk_cmdb",
        "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
        "bk_username": "admin",
        "bk_biz_id": bk_biz_id,
        "bk_supplier_account": "0",
        "page": {"start": 0,
                 "limit": 150}
    }

    json_data = json.dumps(post_data)

    # 数据请求
    res = requests.post('http://itpaas.xxxxx.com/api/c/compapi/v2/cc/get_biz_internal_module/', json_data)

    # API请求返回结果
    biz_data = json.loads(res.content.decode())
    res = biz_data['data']['module'][0]['bk_module_id']
    return res

#根据业务代码ID,闲置主机的ID,查询该业务的所有主机(生产环境)
def get_biz_hosts(bk_biz_id,bk_modules_ids):
    post_data = {
        "bk_app_code": "bk_cmdb",
        "bk_app_secret": "aba65ddd-30ec-42b2-a8d3-825954c6e5e3",
        "bk_username": "admin",
        "metadata": {
            "label": {
                "bk_biz_id": bk_biz_id
            }
        },
        "bk_module_ids": [bk_modules_ids],
        "page": {"start": 0,
                 "limit": 150}
    }

    json_data = json.dumps(post_data)

    # 数据请求
    res = requests.post('http://itpaas.xxxxxc.com/api/c/compapi/v2/cc/find_host_by_module/', json_data)

    # API请求返回结果
    biz_data = json.loads(res.content.decode())
    data_info = biz_data['data']['info']

    hosts_list = []
    for i in data_info:
        if i['host']['environment'] == "1" and i['host']['ping'] == "1":
            hosts_list.append(i['host']['bk_host_innerip'])

    return hosts_list

if __name__ == '__main__':
    f = open("/Users/kang/PycharmProjects/cmdbapi/Ansible自动化运维/cmdb", "w")
    biz_info = get_sys_group()
    group_list = []
    ansible_inventory = {}
    for i in biz_info:
        group_list.append(i)

    for i in group_list:
        group = "[" + str(i) + "]"
        f.writelines('{}\n'.format(group))
        bk_biz_id = str(biz_info[i])
        bk_module_ids = get_idle_moduleid(bk_biz_id)
        hosts_list = get_biz_hosts(bk_biz_id, bk_module_ids)
        for j in hosts_list:
            f.writelines('{}\n'.format(j))
    
    f.close()

四、生成的格式文件

五、Ansible与CMDB的对接方法

1、以业务系统为主体,建立主机群组,便于管理员的统一配置与管控、资产信息收集。 2、每台主机包括对Ansible的相关信息,如:远程用户,远程端口,是否sudo等信息,以便于快捷使用。