#!/usr/bin/env python
#-*- coding: utf-8 -*-
#Description:应用场景,从cmdb中直接获取主机信息,然后生成ansible支持的ini格式inventory文件;并且划分了原子组、业务组(高级分组)、应用组(高级分组),其中,原子组是业务组和应用组的交集名称,业务组和应用组的成员都是原子组;
import shutil
import os
from datetime import datetime
import logging
import ConfigParser
import re
import requests
import json
import sys
reload(sys)
sys.setdefaultencoding('utf8')
#变量、配置初始化
#backuptime = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
backuptime = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
logging.basicConfig(filename="inventory.log", level = logging.DEBUG,
format='levelname: %(levelname)s, process-id: %(process)d, filename: %(filename)s, messages: %(message)s')
hosts_all = []
atomic_groups = []
advanced_groups_app = []
advanced_groups_business = []
#从数据库获取主机信息数据,看实际情况返回的数据结构类型,最终所有的主机要构造成一个列表,备用;
#comment: 备注
#service: 运行的服务
#idc: 机房名称
#project:所属项目,主机组
#eth1: 公网IP
#eth2: 内网IP
def get_hosts_all():
token = 'yyyyyyyyyyyyyyyyyyyyyy'
headers = {'token': token}
url = 'http://xxxxxxxx:xxxx/yyyyyy/get_hosts/'
res = requests.get(url=url, headers=headers)
for i in res.json():
re1 = r'^[a-z]+'
comment = i['comment']
lcomment = comment.split(';')
if re.match(re1, comment) and re.search('lt;', comment):
#过滤掉lt-test、lt-gray组的机器,构造一个完全lt组的主机名列表
if 'lt-' not in comment:
sighost = lcomment[0]
hosts_all.append(sighost)
return hosts_all
#备份ansible原来的inventory文件
def backup_ansible_inventory():
bakFile = 'hosts_' + backuptime
if os.path.isfile('hosts'):
if not os.path.isfile(bakFile):
shutil.move('hosts', bakFile)
#构造初始的原子组列表
def build_atomic_groups():
with open('init_atomic_groups_name.txt', 'r') as iagn:
iagn_lines = iagn.readlines()
for iagn_line in iagn_lines:
iagn_line = iagn_line.strip()
atomic_groups.append(iagn_line)
return atomic_groups
#构造初始的高级分组列表,基于应用名称
def build_advanced_groups_app():
with open('init_advanced_groups_app.txt', 'r') as iaga:
iaga_lines = iaga.readlines()
for iaga_line in iaga_lines:
iaga_line = iaga_line.strip()
advanced_groups_app.append(iaga_line)
# print 'advanced_groups_app: ', advanced_groups_app
return advanced_groups_app
#构造初始的高级分组列表,基于业务模块
def build_advanced_groups_business():
with open('init_advanced_groups_business.txt', 'r') as iagb:
iagb_lines = iagb.readlines()
for iagb_line in iagb_lines:
iagb_line = iagb_line.strip()
advanced_groups_business.append(iagb_line)
# print 'advanced_groups_business: ', advanced_groups_business
return advanced_groups_business
#构造ConfigParser对象,并初始化section对象,包括初始化的原子组和高级分组
def build_sections():
conf = ConfigParser.ConfigParser(allow_no_value = True)
for agroup in atomic_groups:
conf.add_section(agroup)
return conf
#构造一个原子组名的正则表达式列表,用于匹配hosts主机名,然后放到对应的原子组中;
def gen_groups_re(atomic_groups):
global groups_re
groups_re = []
for group_re in atomic_groups:
group_re = group_re.split('_')
group_re[0] = group_re[0] + '[0-9]*'
group_re = '.'.join(group_re)
groups_re.append(group_re)
return groups_re
#打开hosts配置文件,准备写入
def main():
with open('hosts', 'wb+') as host_file:
groups_re = gen_groups_re(atomic_groups)
groups_re_num = len(groups_re)
groups_app_num = len(advanced_groups_app)
groups_business_num = len(advanced_groups_business)
print 'init_groups_business_num: ', groups_business_num
print 'init_groups_app_num: ', groups_app_num
# print "groups_re_num: ", groups_re_num
#遍历所有主机名
for h in hosts_all:
#遍历所有正则表达式,将主机名与正则表达式列表进行匹配,如果没有匹配的,则创建新的原子组名称,并把该主机添加到新增的原子组内,此时数据都还是在内存中的ConfigParser对象中;
h_i = 0
for r in groups_re:#[1,2,3]
match = re.search(r, h)
if match:
#从正则表达式构造对应的原子组名称
group_tmp = r.split('.')
group_tmp[0] = group_tmp[0].split('[')[0]
group_tmp = '_'.join(group_tmp)
conf.set(group_tmp, h)
break
else:
h_i += 1
#如果循环次数与已有原子组个数相等,则说明该主机没有属于的原子组,需要增加原子组并把该主机添加到该原子组中;
if h_i == groups_re_num:
new_group = h.split('.')
new_group[0] = re.sub(r'[0-9]+', '', new_group[0])
new_group = '_'.join(new_group[0:3])
print 'new_group: ', new_group
#更新原子组名称列表
atomic_groups.append(new_group)
#更新正则表达式列表
groups_re = gen_groups_re(atomic_groups)
groups_re_num = len(groups_re)
#更新原子组初始化文件
with open('init_atomic_groups_name.txt', 'a') as iagn:
iagn.write('\n' + new_group)
conf.add_section(new_group)
conf.set(new_group, h)
print 'host: {0}, h_i: {1}'.format(h,h_i)
#此时ConfigParser对象还在内存中,没有消失,后面再add_sections,则是在写入到文件那一刻时的内容后面再次追加了;文件的追加和ConfigParser对象内容的追加是两个概念;
#构造基于应用名称的高级分组
for appgroup in advanced_groups_app:
conf.add_section(appgroup)
for g in atomic_groups:
j = 0
for k in advanced_groups_app:
kre = k + '_'
kmatch = re.match(kre, g)
if kmatch:
conf.set(k, g)
break
else:
j += 1
if j == groups_app_num:
new_group_app = g.split('_')[0]
print 'new_group_app: ', new_group_app
#更新advanced_groups_app列表,下一次循环开始使用最新的列表;
advanced_groups_app.append(new_group_app)
#更新groups_app_num的值;
groups_app_num = len(advanced_groups_app)
with open('init_advanced_groups_app.txt', 'a') as iaga:
iaga.write('\n' + new_group_app)
conf.add_section(new_group_app)
conf.set(new_group_app, g)
print 'advanced_groups_app new: ', advanced_groups_app
#
#
#构造基于业务模块的高级分组
for busgroup in advanced_groups_business:
conf.add_section(busgroup)
for g in atomic_groups:
j = 0
# print 'business_g: ', g
for k in advanced_groups_business:
kre = '_' + k
kmatch = re.search(kre, g)
if kmatch:
conf.set(k, g)
break
else:
j += 1
# print 'bussiness_j: ', j
if j == groups_business_num:
new_group_business = g.split('_')
new_group_business = '_'.join(new_group_business[1:])
# print 'new_group_business: ', new_group_business
#更新advanced_groups_app列表,下一次循环开始使用最新的列表;
advanced_groups_business.append(new_group_business)
#更新groups_business_num的值,下一次循环开始使用;
groups_business_num = len(advanced_groups_business)
with open('init_advanced_groups_business.txt', 'a') as iagb:
iagb.write('\n' + new_group_business)
conf.add_section(new_group_business)
conf.set(new_group_business, g)
conf.write(host_file)
if __name__ == '__main__':
backup_ansible_inventory()
hosts_all = get_hosts_all()
atomic_groups = build_atomic_groups()
advanced_groups_app = build_advanced_groups_app()
advanced_groups_business = build_advanced_groups_business()
conf = build_sections()
main()