Zabbix : surveiller l’évolution du statut de triggers

Dans le cadre de mon travail il m’est arrivé de devoir surveiller l’évolution des triggers de plusieurs hôtes de façon arbitraire. Il est possible de s’en sortir via les Host groups et la page Monitoring > Triggers, mais ça peut vite être lourd à gérer quand on doit surveiller simultanément plusieurs groupes d’équipements qui n’ont rien en commun.

Comme à l’époque je débutais en Python, j’ai décidé de développer mon propre script pour me faire la main.

[pastacode lang= »python » manual= »%23!%2Fusr%2Fbin%2Fenv%20python3%0A%23%20%20Copyright%202018%20palc.fr%0A%23%0A%23%20%20Licensed%20under%20the%20WTFPL%2C%20Version%202%0A%23%20%20%20%20%20%20%20%20%20%20%20%20DO%20WHAT%20THE%20FUCK%20YOU%20WANT%20TO%20PUBLIC%20LICENSE%20%0A%23%20TERMS%20AND%20CONDITIONS%20FOR%20COPYING%2C%20DISTRIBUTION%20AND%20MODIFICATION%20%0A%23%200.%20You%20just%20DO%20WHAT%20THE%20FUCK%20YOU%20WANT%20TO.%0A%0A%22%22%22%0A%20%20A%20trigger%20status%20viewer%0A%0A%20%20This%20script%20displays%20the%20status%20of%20triggers%20on%20some%20devices%0A%0A%20%20Usage%20%3A%0A%20%20%20%20%20%20.%2Ftriggerstatus.py%20zabbix_server%20%5Blist%20of%20triggers%20id%5D%0A%22%22%22%0A%0Aimport%20os%20%20%20%20%20%20%20%23%20To%20manage%20cursor%20and%20window%20size%0Aimport%20signal%20%20%20%23%20To%20manage%20Ctrl%2BC%20and%20widnow%20resizing%0Aimport%20sys%20%20%20%20%20%20%23%20To%20manage%20script%20arguments%20and%20terminal%20buffer%0Aimport%20time%20%20%20%20%20%23%20To%20get%20date%20and%20for%20temporisation%0A%0Aimport%20getpass%20%20%23%20For%20password%20input%20without%20displaying%20it%0Aimport%20math%20%20%20%20%20%23%20For%20calculating%20graph%20datas%0Afrom%20pyzabbix%20import%20ZabbixAPI%0A%0Adef%20signal_sigint(sig%2C%20frame)%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20When%20receiving%20SIGINT%20(Ctrl%2Bc)%2C%20the%20script%20exit%20proprely%0A%20%20%20%20%22%22%22%0A%20%20%20%20os.system(‘setterm%20-cursor%20on’)%0A%20%20%20%20print()%0A%20%20%20%20exit%20(0)%0A%0Adef%20signal_sigwinch(sig%2C%20frame)%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20When%20the%20window%20is%20resized%2C%20redraw%20the%20screen%0A%20%20%20%20%22%22%22%0A%20%20%20%20draw(ok%2C%20notclassified%2C%20information%2C%20warning%2C%20average%2C%20high%2C%20disaster%2C%20disable%2C%20date)%0A%0Adef%20draw(ok%2C%20notclassified%2C%20information%2C%20warning%2C%20average%2C%20high%2C%20disaster%2C%20disable%2C%20date)%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Clear%20the%20screen%20and%20redraw%20all%20the%20datas%0A%20%20%20%20%22%22%22%0A%0A%20%20%20%20os.system(‘clear’)%0A%0A%20%20%20%20%23%20Get%20screen%20width%20and%20height%20(in%20characters)%0A%20%20%20%20rows%2C%20columns%20%3D%20os.popen(‘stty%20size’%2C%20’r’).read().split()%0A%20%20%20%20rows%3Dint(rows)%0A%20%20%20%20columns%3Dint(columns)%0A%0A%20%20%20%20%23%20General%20informations%0A%20%20%20%20sum_triggers%3Dlen(disable)%2Blen(ok)%2Blen(notclassified)%2Blen(information)%2Blen(warning)%2Blen(average)%2Blen(high)%2Blen(disaster)%0A%20%20%20%20print(date%2C%20end%3D »)%0A%20%20%20%20print(%22%20%7C%20%22%20%2B%20str(sum_triggers)%20%2B%20%22%20(%22%2C%20end%3D »)%0A%20%20%20%20print(%22%5C033%5B32m%22%20%2B%20str(len(ok))%20%2B%20%22%5C033%5B0m%2F%22%2C%20end%3D »)%0A%20%20%20%20print(%22%5C033%5B31m%22%20%2B%20str(len(notclassified)%2Blen(information)%2Blen(warning)%2Blen(average)%2Blen(high)%2Blen(disaster))%20%2B%20%22%5C033%5B0m%2F%22%2C%20end%3D »)%0A%20%20%20%20print(%22%5C033%5B90m%22%20%2B%20str(len(disable))%20%2B%20%22%5C033%5B0m)%22)%0A%0A%20%20%20%20%23%20Calculate%20graph%20width%0A%20%20%20%20coef_graph%3D1%0A%20%20%20%20if(sum_triggers%3E(columns-6))%3A%0A%20%20%20%20%20%20%20%20coef_graph%3D(columns-6)%2Fsum_triggers%0A%0A%20%20%20%20%23%20Draw%20the%20graph%0A%20%20%20%20print(‘%5B’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(disaster)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B91m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(high)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B31m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(average)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B93m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(warning)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B33m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(information)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B34m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(notclassified)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B37m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(disable)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B90m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20(range(0%2C%20math.ceil(len(ok)*coef_graph)))%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B32m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20print(‘%7C’%2C%20end%3D »)%0A%20%20%20%20print(%22%5C033%5B0m%5D%22%2C%20end%3D »)%0A%0A%20%20%20%20length%3D3%0A%0A%20%20%20%20%23%20Display%20items%20in%20PROBLEM%20state%0A%20%20%20%20for%20i%20in%20disaster%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B91m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%20%20%20%20for%20i%20in%20high%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B31m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%20%20%20%20for%20i%20in%20average%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B93m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%20%20%20%20for%20i%20in%20warning%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B33m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%20%20%20%20for%20i%20in%20information%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B34m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%20%20%20%20for%20i%20in%20notclassified%3A%0A%20%20%20%20%20%20%20%20print(%22%5C033%5B37m%22%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%0A%20%20%20%20%23%20Display%20disabled%20items%0A%20%20%20%20print(%22%5C033%5B90m%22%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20disable%3A%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%0A%20%20%20%20%23%20Display%20items%20in%20OK%20state%0A%20%20%20%20print(%22%5C033%5B32m%22%2C%20end%3D »)%0A%20%20%20%20for%20i%20in%20ok%3A%0A%20%20%20%20%20%20%20%20if(length%3Crows)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5Cn%22%20%2B%20i%5B%3Acolumns%5D%2C%20end%3D »)%0A%20%20%20%20%20%20%20%20%20%20%20%20length%2B%3D1%0A%0A%20%20%20%20print(%22%5C033%5B0m%22%2C%20end%3D »)%0A%0A%20%20%20%20sys.stdout.flush()%0A%0A%23%20When%20receiving%20SIGINT%20(Ctrl%2Bc)%0Asignal.signal(signal.SIGINT%2C%20signal_sigint)%0A%23%20When%20the%20window%20is%20resized%0Asignal.signal(signal.SIGWINCH%2C%20signal_sigwinch)%0A%0A%23%20Connection%20to%20Zabbix%20server%0Auser%20%3D%20input(%22Username%3A%20%22)%0Apassword%20%3D%20getpass.getpass(%22Password%20for%20%22%20%2B%20user%20%2B%20%22%3A%20%22)%0Atry%3A%0A%20%20%20%20zapi%20%3D%20ZabbixAPI(‘https%3A%2F%2F’%20%2B%20sys.argv%5B1%5D)%0A%20%20%20%20zapi.login(user%2C%20password)%0Aexcept%3A%0A%20%20%20%20print(%22Cannot%20conect%20to%20Zabbix%20server%20%E2%98%B9%22)%0A%20%20%20%20exit(1)%0A%0A%23%20Disable%20cursor%0Aos.system(‘setterm%20-cursor%20off’)%0A%0A%23%20main%20loop%0Awhile%201%3A%0A%20%20%20%20%23%20Tables%20for%20each%20trigger%20status%0A%20%20%20%20ok%3D%5B%5D%0A%20%20%20%20notclassified%3D%5B%5D%0A%20%20%20%20information%3D%5B%5D%0A%20%20%20%20warning%3D%5B%5D%0A%20%20%20%20average%3D%5B%5D%0A%20%20%20%20high%3D%5B%5D%0A%20%20%20%20disaster%3D%5B%5D%0A%20%20%20%20disable%3D%5B%5D%0A%0A%20%20%20%20for%20hostid%20in%20sys.argv%5B2%3A%5D%3A%0A%20%20%20%20%20%20%20%20%23%20Get%20triggers%20status%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20triggers%3Dzapi.trigger.get(hostids%3Dhostid)%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22Problem%20with%20Zabbix%20server%20%E2%98%B9%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20exit(1)%0A%0A%20%20%20%20%20%20%20%20for%20trigger%20in%20triggers%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Item%20is%20in%20OK%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’status’%5D%20%3D%3D%20’0’%20and%20trigger%5B’value’%5D%20%3D%3D%20’0′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ok.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Item%20is%20in%20PROBLEM%20state%0A%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’status’%5D%20%3D%3D%20’0’%20and%20trigger%5B’value’%5D%20%3D%3D%20’1′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’0′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20notclassified.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(not%20classified)’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’1′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20information.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(information)’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’2′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20warning.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(warning)’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’3′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20average.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(average)’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’4′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20high.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(high)’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’priority’%5D%20%3D%3D%20’5′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20disaster.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D%20%2B%20’%20(disaster)’)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Item%20is%20disabled%0A%20%20%20%20%20%20%20%20%20%20%20%20if(trigger%5B’status’%5D%20%3D%3D%20’1′)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20disable.append(hostid%20%2B%20’%20’%20%2B%20trigger%5B’description’%5D)%0A%0A%20%20%20%20date%3Dtime.strftime(%22%25d%2F%25m%2F%25Y%20%25H%3A%25M%3A%25S%22)%0A%0A%20%20%20%20draw(ok%2C%20notclassified%2C%20information%2C%20warning%2C%20average%2C%20high%2C%20disaster%2C%20disable%2C%20date)%0A%0A%20%20%20%20time.sleep(10)%0A%0Aexit(0) » message= »triggerstatus.py » highlight= » » provider= »manual »/]

Ce script plusieurs plusieurs paramètres :

  • L’URL du serveur Zabbix (en HTTPS uniquement)
  • la liste des ID des équipements à surveiller, séparés par des espaces

Par exemple, pour superviser les équipements 13111 13112 13123 sur le serveur zabbix.palc.fr :

./triggerstatus.py zabbix.palc.fr 13111 13112 13123

Je me suis clairement inspiré de htop pour faire ce script. Il affiche, dans l’ordre :

  • La date (pour vérifier que le script n‘est pas planté), le nombre total de triggers, et le détail par statut (OK, KO et DISABLED)
  • Une barre affichant ces données de manière plus graphique
  • Tous les triggers classés, d’abord ceux en KO (classé par ordre de sévérité), puis les DISABLED et enfin les OK

C’est mon tout premier « vrai » script fait en Pyhton (hors Hello word! ou équivalent), donc soyez indulgents.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.