Skip to content

Instantly share code, notes, and snippets.

@eliphaslevy
Last active July 28, 2020 20:55
Show Gist options
  • Save eliphaslevy/41bc3cc8c3c127629ae71dc636b12d38 to your computer and use it in GitHub Desktop.
Save eliphaslevy/41bc3cc8c3c127629ae71dc636b12d38 to your computer and use it in GitHub Desktop.
Create chart with multiple items from LLD
#!/usr/bin/env python2
# coding:utf-8
# vim:ts=8:sw=8:si:ci:ai
"""
Automatically create zabbix host graphs based on LLD items.
Recursively gets all hosts that have the template(s) and adds/updates
one or more charts on each host.
Add the ID of the template to masterTemplates dict, and add graph filters
that will match the items on the host to graphItems.
Unlike the perl script linked below, this script was built to generate one
chart for a bunch of specific items for a OLT port, rinse, repeat, for all
14 ports. Not that it cannot do a chart for all port items, but this is adds
too much interpositions/colors to be really useful, at least for me.
Inspiration: https://github.com/Eccenux/lld_all_graph
Depends on: https://pypi.org/project/zabbix-api/
Why: why not?
The script reads a file named zabbix_api.ini containing:
[zabbix_api]
url=https://zabbix.url/zabbix/
user=zabbix-api-user
password=c0a4bc75dd7ff08f83e2db77bbe0384e
"""
import os, sys, json, re, logging
from logging import info, warning, debug
from datetime import datetime as dt
from zabbix_api import ZabbixAPI
from ConfigParser import ConfigParser
mydir = os.path.dirname(os.path.realpath(__file__))
myini = os.path.join(mydir,"zabbix_api.ini")
ini = ConfigParser()
ini.read(myini)
zbx_url = ini.get('zabbix_api','url')
zbx_usr = ini.get('zabbix_api','user')
zbx_pwd = ini.get('zabbix_api','password')
# templateId, templateName for searching
masterTemplates = {
18058: "Template Huawei Gpon Counter",
}
# templateId, graphName, itemName to search for, manual filter for item name in regex format (optional)
graphItems = {
18058: [
#[ "Todas as portas - Contagem de clientes", "gpon.PortPonTotal", None, ], # this: too much items, unusable
],
}
# now customizing: adding single board graphs
for i in range(14):
graphItems[18058].append(
[ "Porta 0/%d - Contagem agregada de clientes" % i, "gpon.PortPonTotal", "gpon\.PortPonTotal\[\"0/%d/[0-9]+\"]" % i, ],
)
# max items are defined by max colors FIXME
colors = [ "5299AD", "5D549A", "87B457", "CF545E", "CDDA13", "5DAE99", "DD844C", "AE5C8A", "BD9F83", "6B9BD4", "B75F73", "646560", "335098", "5FBFDB", "D1CE85", "909090", "A16254", "E8678D", "62B55A", "A599AD", "6A5DD9", ]
DEBUG = os.environ.get('DEBUG','0')
assert DEBUG.isdigit()
DEBUG = int(DEBUG)
# ZAPI is too much verbose for me so I set separate level
if DEBUG == 0:
LOGLVL = logging.WARNING
ZAPI_LOGLVL = 0
elif DEBUG == 1:
LOGLVL = logging.INFO
ZAPI_LOGLVL = logging.WARNING
else:
LOGLVL = logging.DEBUG
ZAPI_LOGLVL = logging.DEBUG
log = logging.basicConfig(level=LOGLVL)
zapi = ZabbixAPI(server=zbx_url, log_level=ZAPI_LOGLVL, timeout=180)
info('zapi logging in')
zapi.login(zbx_usr, zbx_pwd)
def searchTemplateHosts(filterHostName):
"""Search and returns this template's direcly assigned hosts and recurse on the template's children"""
info('zapi template get for %s' % filterHostName)
myTemplates = zapi.template.get({
"filter": { "host": filterHostName, },
"selectHosts": [ "hostid", "host", ],
"selectTemplates": [ "templateid", "host", ],
"output": [ "hosts", "templates", ],
})
assert len(myTemplates) == 1
myTemplate = myTemplates[0]
childHosts = myTemplate["hosts"]
childTemplates = myTemplate["templates"]
for template in childTemplates:
tplHosts = searchTemplateHosts(template["host"])
childHosts += tplHosts
debug('got childHosts: %s' % childHosts)
return childHosts
def searchHostItems(filterHostId, searchItemName):
info('zapi item get for %s / %s' % (filterHostId, searchItemName))
items = zapi.item.get({
"hostids": filterHostId,
"search": { "key_": searchItemName, },
"output": [ "hostid", "itemid", "key_", "lastvalue" ],
"sortfield": "itemid",
})
debug('zapi items: %d' % len(items))
return items
def searchHostGraphs(filterHostId, filterGraphName):
info('zapi graph get for %s / %s' % (filterHostId, filterGraphName))
graphs = zapi.graph.get({
"hostids": filterHostId,
"filter": { "name": filterGraphName, },
"selectItems": [ "itemid", ],
"output": [ "name", ]
})
debug('zapi graphs: %d' % len(graphs))
return graphs
for templateId in masterTemplates:
info('locating hosts for templateId %d' % templateId)
hosts = searchTemplateHosts(masterTemplates[templateId])
debug('got hosts: %s' % hosts)
for graphName, graphItemName, graphItemSearch in graphItems[templateId]:
info('adding/updating graph name: %s' % graphName)
for host in hosts:
debug('looking for items %s in host %s (item filter: %s)' % (graphItemName, host["hostid"], graphItemSearch))
allItems = searchHostItems(host["hostid"], graphItemName)
if graphItemSearch: # regex filter
selectedItems = []
re_items = re.compile(graphItemSearch)
for item in allItems:
if re_items.match(item["key_"]):
selectedItems.append(item)
else:
selectedItems = allItems
debug('items selected: %s' % selectedItems)
if len(selectedItems) > len(colors): # FIXME
warning('TOO MUCH ITEMS, IGNORING:\ngraph: %s\nhost: %s\nitem count: %d' % (graphName, host['host'], len(selectedItems)))
continue
if not selectedItems:
info('no items selected for this host')
continue
# add colors
i = 0
for item in selectedItems:
item["color"] = colors[i]
i+=1
graph = searchHostGraphs(host["hostid"], graphName)
if graph:
graph = graph[0] # one graph, but comes as a list
setCurr = set([item["itemid"] for item in graph["items"]])
setSelected = set([item["itemid"] for item in selectedItems])
if setCurr == setSelected:
debug("graph up to date!")
continue
debug("updating graph: %s" % graph)
zapi.graph.update({
"graphid": graph["graphid"],
"gitems": selectedItems,
})
else:
debug("creating graph")
zapi.graph.create({
"hostid": host["hostid"],
"name": graphName,
"gitems": selectedItems,
})
zapi.logout()
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment