Zabbix学习资源由浅入深 关于我们 联系我们 加入我们
5

基于ZabbixAPI快速生成多Keys监控图表

Zabbix API

1. 前置条件

1.1. 接口分析

假若当前有个新的监控要求:业主希望快速将一批主机的某个监控项关联到一张图上来(非grpfunc叠加),即以CPU、MEM、DISK等维度纵向对比这批主机的资源使用情况。很显然,当主机数超过一定量时手工创建会非常枯燥和繁琐,所幸Zabbix提供了API。我们通过分析Zabbix Docs中的graph.create图表接口,得知要提供的主机HostID、监控项键值ItemID,使用到的Zagbix Method主要有:User Host HostGroup Item Graph等5种方法。微信图片_20220106170818.jpg如上图,描述本次批量创建图表的主要脚本逻辑,即先获取Zabbix的身份验证令牌,然后查询主机或主机群获得其HOSTID,接着以HOSTID依次获取指定监控键值ID加入列表池,最后一次性请求graph.create进行图表创建。

1.2. 环境约定

系统/工具版本
Zabbix5.0.1
CentOS7.6.1810
Python3.6.8

本自动化案例脚本在Zabbix5.0验证通过,理论上支持Zabbix5.4,主要版本内部具有向后兼容性。

2. 构建接口

2.1. 认证类(AUTH)

创建AUTH类并声明一个私有函数__init__,用来初始化Zabbix接口、Zabbix用户名,Zabbix密码。在条件允许的情况下,建议使用SuperAdmin超管用户,跑通之后再进行权限精细化管理。

class AUTH(object):
    def  __init__(self):
        self.zabbix_api = set.Zabbix["api"]
        self.zabbix_user = set.Zabbix["user"]
        self.zabbix_pass = set.Zabbix["pass"]

创建rcpResult函数,作为桥接Zabbix API数据通道的桥梁。Zabbix API使用JSON-RPC 2.0协议,作为Web前端的一部分提供,支持HTTP POST协议。

def rpcResult(self, params):
    headers = {"Content-Type""application/json-rpc"}
    rsp = requests.post(url=self.zabbix_api, headers=headers, data=json.dumps(params))
    try:
        return  rsp.json()["result"]
    except:
        set.logger.error(rsp.json()["error"])
        exit(1)

创建getToken函数,在进入ZabbixAPI神秘世界之前,需申请一张身份验证令牌。官方提供了一个叫user.login的method调用方法,其userpassword参数为Web端账密信息,而id作为请求的标识符,其支持整形、浮点型、字符串3种数据类型。

def getToken(self):
    params = {
        "jsonrpc""2.0",
        "method""user.login",
        "params": {
            "user": self.zabbix_user,
            "password": self.zabbix_pass,
        },
        "id""Chasii"
    }
    return self.rpcResult(params)

通过调用AUTH类的Get_Token函数AUTH().Get_Token(),得到接口返回的result,就是我们通往神秘世界的一把钥匙。

{
    "jsonrpc""2.0",
    "result""22e6f614a89ccc1c1226429c4b7b08a0",
    "id""Chasii"
}

值得注意的是,通过user.login方法申请到的身份验证令牌其永久生效,不会被系统释放。频繁调度会产生大量opensession记录,从而占用系统资源。这里给出了三种解决方案:

  • 1、在Web端将对应用户的Auto-logout选项勾上并设置时间使其自动失效。
  • 2、将身份校验令牌缓存成文本或存入中间件,可重复调用,
  • 3、使用user.logout方法,即用即销,安全绿色。

如下destroyToken函数,提供了官网注销Token的方法。

def destroyToken(self, token):
    params = {
        "jsonrpc""2.0",
        "method""user.logout",
        "params": [],
        "auth": token,
        "id""Chasii"
    }
    return self.rpcResult(params)

2.2. 主机类(HOST)

创建HOST类并声明__init__私有函数,通过入参zbx_token获取ZabbixAPI身份令牌,供本类函数调用。

class HOST(object):
    def  __init__(self, zbx_token):
        self.zbx_token = zbx_token

创建getGroupID函数,通过查询指定groupid群主ID,返回该组内所有主机ID及其主机名。

def getHostID(self, groupid):
    params = {
        "jsonrpc""2.0",
        "method""host.get",
        "params": {
            "output": ["hostid","host"],
            "groupids": groupid
        },
        "auth": self.zbx_token,
        "id""Chasii"
    }
    return AUTH().rpcResult(params)

2.3. 群组类(HOSTGROUP)

创建HOSTGROUP类并声明__init__私有函数,通过入参zbx_token获取ZabbixAPI身份令牌,供本类函数调用。

class HOSTGROUP(object):
    def  __init__(self, zbx_token):
        self.zbx_token = zbx_token

创建getGroupID函数,通过查询指定群组名hostgroup,返回群组ID。

def getGroupID(self, hostgroup):
    params = {
        "jsonrpc""2.0",
        "method""hostgroup.get",
        "params": {
            "output""groupid",
            "filter": {"name": [hostgroup]}
        },
        "auth": self.zbx_token,
        "id""Chasii"
    }
    return AUTH().rpcResult(params)

2.4. 指标类(ITEM)

创建ITEM指标类并声明一个私有函数,通过入参zbx_token获取ZabbixAPI身份令牌,供本类函数调用。

class ITEM(object):
    def  __init__(self, zbx_token):
        self.zbx_token = zbx_token

创建getItemID函数,通过查询指定主机名host和指标项key,返回监控项ID。

def getItemID(self, host, key):
    params = {
        "jsonrpc""2.0",
        "method""item.get",
        "params": {
            "output""itemid",
            "host": host,
            "search": { "key_": key},
        },
        "auth": self.zbx_token,
        "id""Chasii"
    }
    return AUTH().rpcResult(params)

2.5. 图表类(GRAPH)

Zabbixgraph.create接口参数说明:

必选参数支持类型示例
name字符串图表的名称、如Biz_Group001_CPU、Biz_Group001_MEM
width整数型图表的高度,如900像素,在config.py文件中指定
height整数型图表的高度,如200像素,在config.py文件中指定
gitems列表型图标的监控项列表,如[{"itemid":"22828","color":"00AA00"}]

创建一个GRAPH指标类并声明一个私有函数,通过入参zbx_token获取ZabbixAPI身份令牌,供本类函数调用。

class GRAPH(object):
    def  __init__(self, zbx_token):
        self.zbx_token = zbx_token

创建createGrapth函数,指定图表名gname,图表宽度gwidth,图表高度gheigth,图表监控项参数gitems

def createGrapth(self, gname, gwidth, gheight, gitems):
    params = {
        "jsonrpc""2.0",
        "method""graph.create",
        "params": {
            "name": gname,"width": gwidth,"height": gheight,"gitems": gitems
        },
        "auth": self.zbx_token,
        "id""Chasii"
    }
    return AUTH().rpcResult(params)

3. 调用接口

3.1. 项目介绍

通过上面对官方API的解读,我们已经知道如何使用它了。那么接下来的任务就是如何将这些配件、模块组装起来,让它running起来。为更方便讲解本案例,笔者已将上述相关API脚本上传至Github。读者可关注和克隆到本地使用,未来会支持更多功能。

项目代码Git地址:https://github.com/Chasii/ZabbixCli.git

项目框架说明

ZabbixCli
├── app # Zabbix接口
│   └── api.py
├── conf # Zabbix配置
│   └── settings.py
├── docs # Zabbix说明
│   ├── Nginx-Established.png
│   └── Sequence-Diagram.png
├── logs # 程序日志
│   └── zbx.log
├── readme.md
└── zbxcli.py # 统一入口

3.2. 配置分离

通过配置分离,将易变的模块拎出来作为独立文件,增强项目稳定性和可塑性。config.py文件定义了日志输出格式、日志输出方式(文件、控制台),同时指定Zabbix账号及图表的默认像素,读者可根据自身情况及需求进行调整,下面摘取部分代码进行展示。

# Log Set
...
log_fmt = '%(asctime)s  %(filename)s [line:%(lineno)d] %(levelname)s:%(message)s'
# Export Console
....
logger.addHandler(rf_handler)
# Export Logfile
...
logger.addHandler(f_handler)
# Env Set
Zabbix = {
    "api""http:///api_jsonrpc.php",
    "user""",
    "pass""",
...
}

3.3. 创建入口

创建ZBXCLI入口类并声明一个私有函数,调用AUTH类获得初始ZabbixAPI身份令牌,供本类函数调用。

class  ZBXCLI(object):
    def  __init__(self):
        self.zbx_token = zbx.AUTH().getToken()

如下函数主要处理CLI传来参数,其中函数randomColor返回随机16进制颜色码、函数handleItem根据主机名和监控指标获取ItemID、函数handleHosthandleGroup负责处理HostID,函数createGraph请求接口创建图表。

def  randomColor(self):
     return ("".join(random.choice("0123456789ABCDEF"for  i  in  range(6)))
def  handleItem(self, key, hosts):
     items = []
     for host in hosts:
         items.append(zbx.ITEM(self.zbx_token).getItemID(host, key)[0]["itemid"])
     return items
def  handleHost(self, hosts):
     items = self.handleItem(args.key, hosts)
     self.createGraph(items)
     ...
def  handleGroup(self, groups):
     for group  in  groups:
         result = zbx.HOSTGROUP(self.zbx_token).getGroupID(group)
     ...
def  createGraph(self, items):
     gitems = []
     ...

3.4. 调用示例

ZabbixCli参数说明

参数说明
key监控键值(Key),注意不是监控的指标名
name图表名,需保持唯一性,支持中文
type支持类型指定主机群组or主机,有特殊字符请加上双引号
nargs主机群组/主机列表参数,多参数请使用空格分开,支持中文

$ python3 zbxcli.py -h
usage: zbxcli.py [-h] -key KEY -name NAME -type {hostgraph,groupgraph} -nargs
                 NARGS [NARGS ...]
=== Zabbix Cli ===
optional arguments:
  -h, --help            show this help message and exit
  -key KEY              Zabbix Item Key
  -type {hostgraph,groupgraph}
  -nargs NARGS [NARGS ...]

3.4.1. 主机模式

指定主机host1host2,关联操作系统CPU负载的键值system.cpu.load[all,avg5],并将图表命名为RELATE_GRAPH.CPU.LOAD.AVG5

$ python3 zbxcli.py -key "system.cpu.load[all,avg5]" \
-name "RELATE_GRAPH.CPU.LOAD.AVG5" \
-type hostgraph \
-nargs "host1" "host2"
2021-12-11 17:32:58,507 zbxcli.py [line:52] INFO:{'graphids': ['57138']}

打开任意指定主机的Graph配置项,查找RELATE_GRAPH.CPU.LOAD.AVG5图表名字可以看到,我们创建的主机键值已经关联到一张图表上了,通过Preview可正常显示。微信图片_20220106170840.jpg微信图片_20220106170845.jpg

3.4.2. 群组模式

指定群组BizGroup001主机群组BizGroup002主机群组,关联所有主机的Nginx-Established链接数,键值为tcp_conn_established自定义Key,图表命名为RELATE_GRAPH.TCP_CONN_ESTABLISH

$ python3 zbxcli.py -key "tcp_conn_established" -name "RELATE_GRAPH.TCP_CONN_ESTABLISH" -type groupgraph -nargs "BizGroup001主机群组" "BizGroup002主机群组"
2021-12-11 17:45:36,212 zbxcli.py [line:42] INFO:"BizGroup001主机群组" includes these hosts: host1,host2,...
2021-12-11 17:45:36,851 zbxcli.py [line:52] INFO:{'graphids': ['57139']}
2021-12-11 17:45:36,212 zbxcli.py [line:42] INFO:"BizGroup002主机群组" includes these hosts: host3,host4,...
2021-12-11 17:45:36,851 zbxcli.py [line:52] INFO:{'graphids': ['57140']}

通过指定单台/多台群组,自动将指标值关联到一张表上。我们通过控制台日志可以看到,ZabbixCli分别在BizGroup001主机群组BizGroup002主机群组的主机里创建graphids为57139和57140的图表,通过快速访问https:///chart2.php?graphid=得知它们展示的数据一样。微信图片_20220106170850.jpg

本文从监控需求分析入手,介绍了整个自动化思路、ZabbixAPI调用方法、图形接口构建与整合、统一Cli入口,相信对于Zabbix和Python入门的同学来说也能轻松理解。最后希望本案例能给各位朋友带来启发,如有疑问欢迎一起交流。

蔡斯

Zabbix开源社区签约专家

2021-12-23