在运维工具中,选择了用ansible api来与服务器进行交互,用ansible的目的主要是使用简单,而且客户端不需要安装agent,服务端安装完ansbile后,便可对新机器进行管理。 为了结合资产管理系统(CMDB),所以要使用到动态获取inventory的方法,这样可以省去配置ansible服务端的hosts,所有的客户端IP,帐号,密码,端口都可以从CMDB中获取到。

用于生成 JSON 的脚本对实现语言没有要求,它可以是一个可执行脚本、二进制文件,或者其他任何可以运行文件,但是必须输出为 JSON 格式,动态inventory脚本同时必须支持两个参数:–list 和 --host : –list 和 --host 。 –list:用于返回所有的主机组信息,每个组所包含的主机列表 hosts、所含子组列表 children、主机组变量列表 vars 都应该是字典形式的,_meta 用来存放主机变量。

test.py脚本源码(为了测试,所以把内容都写死在test.py中,此脚本必须有可执行权限

#!/usr/bin/python
# coding:utf8
import json
import sys
def group():
    info_dict = {
    "mj":{"hosts": ["10.5.189.181", "10.5.189.180"],"vars":{"ansible_ssh_user":"root","ansible_ssh_port": 22},"children":["ddz"]},
    "dz":["10.5.189.180"],
    "ddz":["10.5.189.182"]
    }
    print(json.dumps(info_dict, indent=4))
def host(ip):
    info_dict = {
        "10.5.189.180":
            {"ansible_ssh_host": "10.5.189.180", "ansible_ssh_port": 22, "ansible_ssh_user": "root",
             "ansible_ssh_port"",ansible_ssh_pass": "xxxx"},
        "10.5.189.181":
            {"ansible_ssh_host": "10.5.189.181", "ansible_ssh_port": 22, "ansible_ssh_user": "root",
             "ansible_ssh_port"",ansible_ssh_pass": "xxxx"}
    }
    print(json.dumps(info_dict[ip], indent=4))

if len(sys.argv) == 2 and (sys.argv[1] == '--list'):
    group()
elif len(sys.argv) == 3 and (sys.argv[1] == '--host'):
    host(sys.argv[2])
else:
    print("Usage: %s --list or --host <hostname>" % sys.argv[0])
    sys.exit(1)

要从CMDB中获取,当然就是要从数据库中查询了,所以,我们按以上测试脚本的输出格式,把数据库中取出来的数据,重新整理下,符合动态inventyory规则即可我写的脚本test.py:

#! -*- coding: utf-8 -*-
# @Time    : 2018/9/12 13:50

import pymysql
import json
import sys
db = pymysql.connect(user="***",password="***",host="10.5.189.180", db="jumpserver", port=3306)
cursor = db.cursor()

def group_sql(group):
    if group == "all":
        sql = "SELECT ip from jasset_asset;"
    else:
        sql = """SELECT ip from jasset_asset where id in (SELECT asset_id from jasset_asset_group WHERE assetgroup_id 
  in ( SELECT id as group_id from jasset_assetgroup where name="{}"))""".format(group)
    return sql

def get_all_group():
    sql = "SELECT name from jasset_assetgroup;"
    group_list = get_result(sql)
    return group_list 

def get_result(sql):
    try:
        cursor.execute(sql)
        result = cursor.fetchall()
        return result
    except Exception as e:
        raise e

def group_List():
    big_dict = {}
    groups = get_all_group()
    for group in groups:
        group_name = group[0]
        sql = group_sql(group_name)
        result = get_result(sql)
        host_list=[]
        for host in result:
            host_list.append(host[0])
        big_dict[group_name] = {"hosts":host_list, "vars":{"ansible_ssh_user":"root","ansible_ssh_port": 22}, "chrildren":{}}
    print(json.dumps(big_dict, indent=4))



def hostList(hostip):
    host_dict = {}
    sql = """SELECT ip,port,username from jasset_asset where ip="{}";""".format(hostip)
    result = get_result(sql)
    if len(result) != 0:
        for host in result:
            host_ip = host[0]
            ssh_port = host[1]
            ssh_user = host[2]
            host_dict[host_ip] = {"ansible_ssh_host": host_ip, "ansible_ssh_port": ssh_port, "ansible_ssh_user": ssh_user, "ansible_ssh_pass":"xxxx"}
    else:
        host_dict[hostip]= "no this host"
    print(json.dumps(host_dict, indent=4))


def main():
    if len(sys.argv) == 2 and sys.argv[1] == "--list":
        group_List()
    elif len(sys.argv) == 3 and sys.argv[1] == "--host":
         hostList(sys.argv[2])
    else:
        print("Usage: %s --list or --host <hostname>" % sys.argv[0])
        sys.exit(1)

if __name__ == "__main__":
    main()
    db.close()

测试(脚本需要可执行权限,可别忘了,chmod a+x test.py): 根据–list 参数,从mysql 数据库中的查询到的group_name 来操作:

“ddz”,“mj” 参数即是数据库中的group_name
 [root@docker-service ansible]# ansible -i test.py ddz -m ping
 10.5.189.180 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 10.5.189.192 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 [root@docker-service ansible]# ansible -i test.py mj -m ping
 10.5.189.182 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 “all” 默认list参数所有组中的主机的总和
 [root@docker-service ansible]# ansible -i test.py all -m ping
 10.5.189.181 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 10.5.189.180 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 10.5.189.182 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }
 10.5.189.192 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }根据–host 参数查询指定的主机 来操作:
 [root@docker-service ansible]# ansible -i ./test.py 10.5.189.180 -m ping
 10.5.189.180 | SUCCESS => {
 “changed”: false,
 “ping”: “pong”
 }

如果脚本不支持–list和–host的话,是会报错的,不过就算你写了支持–list和–host,也可能会被其它奇怪的问题搞晕,它还是报以上的错误,–list ([Errno 2] No such file or directory) ,说下我遇到的问题,我在命令行中运行python test.py --list时,是没有问题的,能正常输出群组信息,但我用ansible -i test.py 10.5.189.180 -a uptime时,还是报–list ([Errno 2] No such file or directory)错误。后来我用直接用./test.py --list,也报错,原因大概找到了,就是不会自动用python来解析这人脚本,所以脚本输出错误,才会报上面的错误,找了许久都没发现问题,后来才发现是脚本第一行:#!/usr/bin/python, 主要原因是windows下编码字符格式与linux 系统下是不一样导致, 解决方案1:可以把整个脚本内容复制粘贴在linux系统下的文件中,保存. 解决方案2:vim test.py 输入:set ff 可以查看该文件的编码 可以看到fileformat=dos 我们需要通过 :set ff =unix 或set fileformat = unix 改变文件的编码就可以解决该问题