《cmdb事件推送实现zabbix资产同步》一文实现了cmdb和zabbix的资产同步,接下来咱们就离着《运维思索:cmdb打通zabbix、jumpserver探索》这个小目标就不远了。node
言归正传,jumpserver的资产分组同步,即jumpserver的资产列表与cmdb的业务拓扑保持一致,按照业务–集群–模块的树形结构分布。python
而cmdb和jumpserver的同步思路与前面基本保持一致,即:git
只有当主机转移至相关模块时,才会触发业务、集群、模块(例如分别对应消金生产环境–运维平台–禅道)的节点建立;而在cmdb侧只建立空的集群、模块时不会触发jumpserver同步,以避免致使建立空节点。redis
cmdb只有在集群和模块下资产都为空的状况才能执行删除,这个策略和jumpserver一致,所以在主机标识更新保证资产粒度级别的分组一致后,删除操做就不是问题了。django
《腾讯蓝鲸实现vsphere虚拟机交付》一文咱们经过蓝鲸标准运维已经实现了新建主机在jumpserver中建立、自动注册到cmdb,在保证主机上架流程前提下,咱们能够认为全部的主机默认都是在cmdb和jumpserver中已经存在的,咱们只需保证jumpserver资产分组和cmdb一致便可。json
注意:因为时间的问题,咱们本文只介绍“cmdb主机标识更新”触发的jumpserver资产分组同步。vim
咱们在gateway网关中,增长了jumpserver模块用于实现资产分组同步。api
D:\work\blueking>tree gateway /F
D:\WORK\BLUEKING\GATEWAY
│ .gitignore
│ README.md
│
├─.vscode
│ settings.json
│
└─gateway
│ manage.py
│
├─.vscode
│ settings.json
│
├─gateway
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
└─gw_cmdb
│ admin.py
│ apps.py
│ models.py
│ tests.py
│ urls.py
│ views.py
│ __init__.py
│
├─common
│ hostidentifier_cmdb.py
│ main.py
│ module_cmdb.py
│ select_cmdb.py
│
├─jumpserver
│ api.py
│ main.py
│
└─zabbix
group.py
host.py
main.py
template.py
复制代码
其中:markdown
jumpserver目录是与jumpserver相关的模块:app
因为分组同步能够在不影响zabbix使用的状况下操做,所以咱们在此着重介绍此功能。
cmdb事件推送将参数发送给网关,由views.py接收,json格式数据为:
其中:
from django.http import HttpResponse
from .common.main import main
from .zabbix.main import zabbix_main
from .jumpserver.main import jumpserver_main
import json
import logging
logger = logging.getLogger('log')
# Create your views here
def cmdb_request(request):
if request.method == 'POST':
data = json.loads(request.body)
logger.info('cmdb发送消息:{}'.format(data))
## 整理cmdb数据
res=main(data)
##是否须要联动zabbix及jumpserver
if res['result'] == 1:
return HttpResponse("ok")
else:
logger.info(res)
#zabbix同步
zabbix_request = zabbix_main(res)
logger.info('zabbix 同步完毕:{}'.format(zabbix_request))
#jumpserver同步
jumpserver_request = jumpserver_main(res)
logger.info('jumpserver 同步完毕:{}'.format(jumpserver_request))
return HttpResponse("ok")
else:
logger.info('本接口只支持POST模式')
return HttpResponse("本接口只支持POST模式")
复制代码
vim common/main.py
import logging
from .hostidentifier_cmdb import hostidentifier
from .module_cmdb import module_action
logger = logging.getLogger('log')
def main(data):
result = {'result': 1,'data': 0}
## 模块操做
if data['obj_type'] == 'module':
return module_action(data)
## 主机标识操做
elif data['obj_type'] == 'hostidentifier':
if data['action'] == 'update' and data['event_type'] == 'relation' :
logger.info("主机标识更新: {}".format(data))
return hostidentifier(data)
else:
logger.info("主机标识未知操做: {}".format(data))
else:
logger.info("未知操做: {}".format(data))
return result
复制代码
cmdb主机模块转移可能会触发屡次请求,所以咱们须要借助redis将请求进行去重。
import redis
import json
import hashlib
import logging
logger = logging.getLogger('log')
r = redis.StrictRedis(host='127.0.0.1',port=6379,db=1)
## 模块变动获取主机全部模板定制分组
def hostidentifier(data):
##定义数据格式
datajson= {'tex_id': '','action': data['action'],'obj_type': data['obj_type'],'data': {'cur_data': {'ip': '','group': []},'bk_host_id':data['data']['cur_data']['bk_host_id'],'pre_data': 'None'},'result': 1}
## 获取主机组信息,并清理记录reids去除重复会话
for i in data['data']:
datajson['data']['cur_data']['ip'] = i['cur_data']['bk_host_innerip']
grouplist = i['cur_data']['associations']
for j in grouplist:
groupname = grouplist[j]['bk_biz_name']+"_"+grouplist[j]['bk_set_name']+"_"+grouplist[j]['bk_module_name']
datajson['data']['cur_data']['group'].append(groupname)
datajson['tex_id']= hashlib.md5((data['request_id']+ i['cur_data']['bk_host_innerip']).encode('utf-8')).hexdigest()
rkey = r.hget('cmdb',datajson['tex_id'])
logger.info(rkey)
if rkey is None:
r.hset('cmdb',datajson['tex_id'],json.dumps(datajson['data']))
datajson['result'] = 0
logger.info(datajson)
return datajson
复制代码
import logging
from django.conf import settings
from urllib import request
import json
from .api import Assets
logger = logging.getLogger('log')
def jumpserver_main(data):
""" jumpserver api入口函数,只对主机标识更新响应 """
if data['obj_type'] == 'hostidentifier':
if data['action'] == 'update':
cur_data = data['data']['cur_data']
assets = Assets(cur_data)
assets.perform()
else:
logger.info("主机标识未知操做: {}".format(data))
else:
logger.info("未知操做: {}".format(data))
return data
复制代码
import logging
from django.conf import settings
import json
import uuid
import requests
logger = logging.getLogger('log')
class HTTP:
server = settings.JUMPSERVER_BASEURL
@classmethod
def get(cls, url, params=None, **kwargs):
url = cls.server + url
headers = settings.JUMPSERVER_HEADERS
kwargs['headers'] = headers
res = requests.get(url, params, **kwargs)
return res
@classmethod
def post(cls, url, data=None, json=None, **kwargs):
url = cls.server + url
headers = settings.JUMPSERVER_HEADERS
kwargs['headers'] = headers
res = requests.post(url, data, json, **kwargs)
return res
@classmethod
def put(cls, url, data=None, **kwargs):
url = cls.server + url
headers = settings.JUMPSERVER_HEADERS
kwargs['headers'] = headers
res = requests.put(url, data, **kwargs)
return res
class Node(object):
def __init__(self, *args):
self.id = ''
self.name = None
self.group = args
self.full_value = "/Default"
def exist(self):
url = '/api/v1/assets/nodes/'
logger.info('group Is {}'.format(self.group))
for item in self.group[0].split('_'):
self.name = item
params = {'value': self.name}
res = HTTP.get(url, params=params)
res_data = res.json()
if res.status_code in [200, 201] and res_data:
self.id = res_data[0].get('id')
self.full_value = res_data[0].get('full_value')
logger.info('节点已存在 {}'.format(self.full_value))
else:
self.create()
return self.id
def create(self):
url = '/api/v1/assets/nodes/' + str(self.id) + '/children/'
data = {
'id': str(uuid.uuid1()),
'value': self.name,
'full_value': self.full_value
}
if self.full_value == "/Default":
url = '/api/v1/assets/nodes/'
logger.info('url Is {}'.format(url))
logger.info('data Is {}'.format(data))
res = HTTP.post(url, json=data)
res_data = res.json()
if res.status_code in [200, 201] and res_data:
self.id = res_data.get('id')
self.full_value = res_data.get('full_value')
logger.info('节点建立成功: {}'.format(res_data))
else:
logger.info('节点建立失败:{}'.format(res_data))
return self.id
def delete(self):
pass
def perform(self):
self.exist()
return self.id
class Assets(object):
def __init__(self, *args):
self.id = None
self.node = None
self.nodeid = []
self.hostname = None
self.platform = None
self.ip = args[0].get('ip')
self.group = args[0].get('group')
def exist(self):
url = '/api/v1/assets/assets/'
params = {
'ip': self.ip
}
res = HTTP.get(url, params)
res_data = res.json()
if res.status_code in [200, 201] and res_data:
self.id = res_data[0].get('id')
self.hostname = res_data[0].get('hostname')
self.platform = res_data[0].get('platform')
logger.info('资产查询成功: {}'.format(res_data))
self.update()
else:
self.create()
def create(self):
pass
def update(self):
url = "/api/v1/assets/assets/" + self.id + "/"
params = {
'hostname': self.hostname,
'ip': self.ip,
'platform': self.platform,
'nodes': self.nodeid
}
res = HTTP.put(url, json.dumps(params))
res_data = res.json()
if res.status_code in [200, 201] and res_data:
self.id = res_data.get('id')
logger.info('资产更新成功: {}'.format(res_data))
else:
logger.info('资产更新失败:{}'.format(res_data))
def perform(self):
for group in self.group:
self.node = Node(group)
self.nodeid.append(self.node.perform())
self.exist()
logger.info('nodeid Is {}'.format(self.nodeid))
复制代码
经过事件推送网关咱们初步实现了cmdb和jumpserver的分组同步,咱们只须要在cmdb一侧就能够管理主机资源,不只节省了咱们跨平台处理的时间,并且规范了咱们基础设施的管理。