需求:
自动化扩容期间,想给通过saltstack将主机自动加入SOA组,自动添加JMX(监控jvm)模板;
期间没有记录模块执行日志,而是使用golang和lua的处理方式,将错误直接返回;
另外实现的功能非常少,主要是注释比较多
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Create from 2018/11/05
import subprocess
import json
import requests
#import salt.utils
from salt.exceptions import CommandExecutionError
__virtualname__ = "zabbix_modify"
class zabbix_check():
def __init__(self,template=None,group=None):
self.user = "devops"
self.password = "1234567890"
self.zabbix_url = "http://1.2.3.4/api_jsonrpc.php"
self.zabbix_header = {"Content-Type": "application/json"}
if group:
self.zabbix_group_name = group
else:
self.zabbix_group_name = "SOA"
if template:
self.zabbix_template_name = template
else:
self.zabbix_template_name = "JMX"
self.zabbix_etc = "/usr/local/services/zabbix-3.0.0/etc/zabbix_agentd.conf"
def _get_hostname(self):
"""
用系统命令获取zabbix的Hostname
"""
cmd = "grep ^Hostname %s | awk -F'=' '{print $2}'" % self.zabbix_etc
ret = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
ret.wait()
if ret.returncode == 0:
return ret.stdout.read(),None
else:
return None,ret.stderr.read()
def _get_test_return(self,res):
"""
统一进行 临时测试/验证 数据返回,一般用于zabbix api的get相关方法,直接回复ret["result"]进行测试
"""
if res.status_code == 200:
ret = res.json()
if "result" in ret.keys():
return ret["result"]
else:
return None,"发生错误: {1}".format(ret['error'])
else:
return None,"结果非200!"
def _get_return(self,res,keywords):
"""
统一进行数据返回,一般用于zabbix api的get相关方法
"""
if res.status_code == 200:
ret = res.json()
if "result" in ret.keys():
if keywords in ret['result'][0].keys():
return ret['result'][0][keywords],None
else:
return None,"zabbix-server上没有{0}相关信息".format(keywords)
else:
return None,"获取{0}发生错误: {1}".format(keywords,ret['error'])
else:
return None,"获取{0}结果非200!".format(keywords)
def _update_return(self,res,keywords,ext_keywords=None):
"""
统一进行结果验证,一般用于zabbix api的update相关方法
"""
if res.status_code == 200:
ret = res.json()
if "result" in ret.keys():
if keywords in ret['result']:
return ret['result'][keywords],None
else:
return None,"更新zabbix-server上{0}.{1}相关信息".format(keywords,ext_keywords)
else:
return None,"更新{0}.{1}发生错误: {2}".format(keywords,ext_keywords,ret['error'])
else:
return None,"更新{0}.{1}结果非200!".format(keywords,ext_keywords)
def _get_sessionid(self):
"""
获得访问zabbix的session,在这一步抓捕网络连接异常,剩下的与zabbix api交互中忽略了网络异常的处理
"""
data = {
"jsonrpc": "2.0",
"method": "user.login",
"params": {
"user": self.user,
"password": self.password
},
"id": 0
}
try:
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
except Exception as e:
return None,"Connection Error: {0}".format(e)
# 这个结果单独处理了
if res.status_code == 200:
if "result" in res.json().keys():
return eval(str(res.text))['result'],None
else:
return None,"获取session id失败!"
else:
return None,"获取zabbix session_id结果非200!"
def _get_hosid(self,hostname,session_id):
"""
判断此主机是否存在于zabbi-server上,存在就返回hostid
Request:
{
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid"],
"filter": {
"host": [
"Zabbix server"
]
}
},
"auth": "038e1d7b1735c6a5436ee9eae095879e",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": [{
"hostid": "10160"
},]
"id": 1
}
"""
data = {
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid"],
"filter": {
"host": [hostname]
}
},
"auth": session_id,
"id": 1}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._get_return(res,"hostid")
def _get_groupid(self,session_id):
"""
返回某个特定group的id
Request:
{
"jsonrpc": "2.0",
"method": "hostgroup.get",
"params": {
"output": "extend",
"filter": {
"name": [
"Zabbix servers",
"Linux servers"
]
}
},
"auth": "6f38cddc44cfbb6c1bd186f9a220b5a0",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": [
{
"groupid": "2",
"name": "Linux servers",
"internal": "0"
},
{
"groupid": "4",
"name": "Zabbix servers",
"internal": "0"
}
],
"id": 1
}
"""
data = {
"jsonrpc": "2.0",
"method": "hostgroup.get",
"params": {
"output": ["groupid"],
"filter": {
"name": [self.zabbix_group_name]
}
},
"auth": session_id,
"id": 0}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._get_return(res,"groupid")
def _get_templateid(self,session_id):
"""
返回指定template的id
{
"jsonrpc": "2.0",
"method": "template.get",
"params": {
"output": ["templateid"],
"filter": {
"host": [
"Template OS Linux",
"Template OS Windows"
]
}
},
"auth": "038e1d7b1735c6a5436ee9eae095879e",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": [
{
"templateid": "10001"
},
{
"templateid": "10081",
}
],
"id": 1
}
"""
data = {
"jsonrpc": "2.0",
"method": "template.get",
"params": {
"output": ["templateid"],
"filter": {
"host": [self.zabbix_template_name]
}
},
"auth": session_id,
"id": 0
}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._get_return(res,"templateid")
def _get_groups(self,session_id,hostid):
"""
返回指定主机所属的所有groups id
Request:
{
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["groupid"],
"selectGroups": ["groupid"],
"hostids": "10084"
},
"auth": "038e1d7b1735c6a5436ee9eae095879e",
"id": 2
}
Response:
{
"jsonrpc": "2.0",
"result": [
{
"hostid": "10085",
"groups": [ #注意,groups的值是一个列表,列表下的元素是字典
{
"groupid": "2",
},
{
"groupid": "4",
}
]
}
],
"id": 2
}
"""
data = {
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid"],
"selectGroups": ["groupid"],
"hostids": hostid
},
"auth": session_id,
"id": 2
}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._get_return(res,"groups")
def _get_templates(self,session_id,hostid):
"""
返回主机关联的所有template id
Request:
{
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid"],
"selectParentTemplates": ["templateid"],
"hostids": "10084"
},
"id": 1,
"auth": "70785d2b494a7302309b48afcdb3a401"
}
Response:
{
"jsonrpc": "2.0",
"result": [
{
"hostid": "10084",
"parentTemplates": [
{
"templateid": "10001"
},
{
"templateid": "10047"
}
]
}
],
"id": 1
}
"""
data = {
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid"],
"selectParentTemplates": ["templateid"],
"hostids": hostid
},
"auth": session_id,
"id": 2
}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._get_return(res,"parentTemplates")
def _update_groups(self,session_id,hostid,groups):
"""
注意: 这里的groups还是一个列表,列表的元素是字典,字典只有一个键值对,键是"groupid"
更新主机所属的组
{
"jsonrpc": "2.0",
"method": "host.update",
"params": {
"hostid": "10126",
"groups": [
{
"groupid": "10124"
},
]
},
"auth": "038e1d7b1735c6a5436ee9eae095879e",
"id": 1
}
结果:
{
"jsonrpc": "2.0",
"result": {
"hostids": [
"10126"
]
},
"id": 1
}
判断里面是否有"hostids"
"""
data = {
"jsonrpc": "2.0",
"method": "host.update",
"params": {
"hostid": hostid,
"groups": groups
},
"auth": session_id,
"id": 1
}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._update_return(res,"hostids","groups")
def _update_templates(self,session_id,hostid,templates,operate):
"""
给主机添加template,注意,templates其实是替换,类似groups,这里templates仍然是一个列表,统一进行更新或者clear template
涉及到update,一定要小心,必须验证
Request:
{
"jsonrpc": "2.0",
"method": "host.update",
"params": {
"hostid": "10126",
"templates": [
{
"templateid": "10124"
},
{
"templateid": "10125"
}
]
},
"auth": "038e1d7b1735c6a5436ee9eae095879e",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": {
"hostids": [
"10126"
]
},
"id": 1
}
"""
if operate == "add":
operation = "templates"
else:
operation = "templates_clear"
data = {
"jsonrpc": "2.0",
"method": "host.update",
"params": {
"hostid": hostid,
operation: templates
},
"auth": session_id,
"id": 1
}
res = requests.post(url=self.zabbix_url, headers=self.zabbix_header, data=json.dumps(data))
return self._update_return(res,"hostids","templates")
def add_group(self):
zabagent_hostname, err = self._get_hostname()
if err:
raise CommandExecutionError("无法获取本机zabbix-agent的hostname: {0}".format(err))
#print("无法获取本机zabbix-agent的hostname: {0}".format(err))
else:
zabagent_hostname = zabagent_hostname.strip("\n")
zabbix_session, err = self._get_sessionid()
if err:
raise CommandExecutionError(err)
#print(err)
host_id,err = self._get_hosid(zabagent_hostname,zabbix_session)
if err:
raise CommandExecutionError(err)
# print(host_id_err)
# 判断是否在soa主机组里面,如果不在,添加
target_groupid, err = self._get_groupid(zabbix_session)
if err:
raise CommandExecutionError(err)
#print(err)
groups,err = self._get_groups(zabbix_session,host_id)
if err:
raise CommandExecutionError(err)
#print(err)
groups_list = [id["groupid"] for id in groups]
if target_groupid not in groups_list:
groups.append({"groupid": target_groupid})
update_groups_ret,err = self._update_groups(zabbix_session,host_id,groups)
if err:
raise CommandExecutionError(err)
#print(err)
else:
return "success: 当前主机{0}加入{1}组内成功".format(zabagent_hostname,self.zabbix_group_name)
else:
return "success: 当前主机{0}已经在{1}组内,不需添加".format(zabagent_hostname,self.zabbix_group_name)
def _template_common(self,operate):
zabagent_hostname, err = self._get_hostname()
if err:
raise CommandExecutionError("无法获取本机zabbix-agent的hostname: {0}".format(err))
#print("无法获取本机zabbix-agent的hostname: {0}".format(err))
else:
zabagent_hostname = zabagent_hostname.strip("\n")
zabbix_session, err = self._get_sessionid()
if err:
raise CommandExecutionError(err)
#print(err)
host_id,err = self._get_hosid(zabagent_hostname,zabbix_session)
if err:
raise CommandExecutionError(err)
# print(host_id_err)
# 判断是否在关联了目标监控模板,如果没有,则关联
target_templateid,err = self._get_templateid(zabbix_session)
if err:
raise CommandExecutionError(err)
#print(err)
templates,err = self._get_templates(zabbix_session,host_id)
if err:
raise CommandExecutionError(err)
#print(err)
templates_list = [template["templateid"] for template in templates]
if operate== "add" and target_templateid not in templates_list:
templates.append({"templateid": target_templateid})
update_templates_ret,err = self._update_templates(zabbix_session,host_id,templates,operate)
if err:
raise CommandExecutionError(err)
#print(update_templates_ret_err)
else:
return "sucess: 当前主机{0}关联{1}模板成功".format(zabagent_hostname,self.zabbix_template_name)
elif operate == "add" and target_templateid in templates_list:
return "success: 当前主机{0}已经关联{1}模板,不需要继续关联.".format(zabagent_hostname,self.zabbix_template_name)
elif operate == "unlink_clear" and target_templateid in templates_list:
# 这边就是清除当前主机关联的目标template
update_templates_ret,err = self._update_templates(zabbix_session,host_id,[{"templateid": target_templateid}],operate)
if err:
raise CommandExecutionError(err)
#print(update_templates_ret_err)
else:
return "sucess: 当前主机{0} unlink and clear {1} 模板成功".format(zabagent_hostname,self.zabbix_template_name)
elif operate == "unlink_clear" and target_templateid not in templates_list:
return "success: 当前主机{0}没有关联{1}模板,不需要删除关联.".format(zabagent_hostname,self.zabbix_template_name)
def add_template(self):
return self._template_common("add")
def unlink_clear_template(self):
return self._template_common("unlink_clear")
def add_host_to_group(group=None):
zabbix_operation = zabbix_check(group)
return zabbix_operation.add_group()
def link_template(template=None):
zabbix_operation = zabbix_check(template)
return zabbix_operation.add_template()
def unlink_clear_template(template=None):
zabbix_operation = zabbix_check(template)
return zabbix_operation.unlink_clear_template()
# if __name__ == "__main__":
# add_template()
#模块经测试有效,只是暂时没有记录日志