Zabbix : superviser les tablespaces Oracle

Dans l’article précédent, nous avons vu comment exécuter des requêtes Oracle depuis Linux. Maintenant nous allons nous en servir pour superviser des tablespaces avec Zabbix.

La supervision va se faire en deux parties : une découverte automatique des tablespaces, et la supervision de l’espace libre.

Un export de mon template et des fichiers nécessaires peut être téléchargé à cette adresse : https://palc.fr/wp-content/uploads/template_oracle_tablespace.zip

La découverte automatique

La découverte des tablespace a été abordée dans l’article précédent, mais je vais revenir dessus.

Je vais utiliser un externalscript. Chez moi ils se trouvent dans le dossier /usr/lib/zabbix/externalscripts. Je vais donc dedans et je créé le fichier oracle_tablespace_discovery.ext :

SET heading OFF;
SET feedback OFF;

SELECT
a.tablespace_name
FROM
dba_data_files a
GROUP BY
a.tablespace_name;

EXIT;

On notera la désactivation des en-tête et autres infos inutiles. Cette commande retourne uniquement les tablespaces, et rien d’autre.

Ensuite je créé un script oracle_tablespace_discovery.sh dans le même répertoire :

#!/bin/bash

LD_LIBRARY_PATH=/usr/lib/oracle/12.1/client64/lib

echo '{
  "data":['

for tablespace in `/usr/lib/oracle/12.1/client64/bin/sqlplus -s "$3/$4@ (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = $1)(PORT = $2))) (CONNECT_DATA = (SERVICE_NAME = $5)))" @/usr/lib/zabbix/externalscripts/oracle_tablespace_discovery.ext`
do
  echo -n '    {"{#ORACLE_TABLESPACE_NAME}":"'
  echo -n $tablespace
  echo '"},'
done

echo '    {}'
echo '  ]
}'

Ce script lance la requête Oracle vue dans l’article précédent, et mets le résultat sous un format JSON pour que Zabbix puisse l’interpréter.

À noter le echo ' {}' à la fin. C’est pas très propre, mais c’est une méthode efficace et rapide pour éviter les erreurs JSON sur le dernier résultat retourné.

Maintenant on lance la commande à la main, pour tester :

root@SV-TCA-ZPXSCP01+~# /usr/lib/zabbix/externalscripts/oracle_tablespace_discovery.sh IP 1521 user password NOM_DU_SERVICE
{
  "data":[
    {"{#ORACLE_TABLESPACE_NAME}":"ZABBIX_TS"},
    {"{#ORACLE_TABLESPACE_NAME}":"UNDOTBS1"},
    {"{#ORACLE_TABLESPACE_NAME}":"SYSAUX"},
    {"{#ORACLE_TABLESPACE_NAME}":"USERS"},
    {"{#ORACLE_TABLESPACE_NAME}":"SYSTEM"},
    {"{#ORACLE_TABLESPACE_NAME}":"WF_TABLE"},
    {}
  ]
}

C’est exactement ce que l’ont veut. Il ne reste plus qu’à créer la règle de découverte correspondante. Elle doit juste avoir pour clé oracle_tablespace_discovery.sh[{HOST.IP},{$ORACLE_PORT},{$ORACLE_USER},{$ORACLE_PASSWORD},{$ORACLE_SERVICENAME}] :

On notera l’utilisation des macros, cette règle de découverte étant destinée à finir dans un template.

La supervision de l’espace libre

Là encore je vais utiliser un externalscript. D’abord la requête SQL, dans le fichier oracle_tablespace_stat.ext :

SET heading OFF;
SET feedback OFF;
SET LINE 250;

SELECT
    a.tablespace_name,
    TO_CHAR(SUM(a.bytes)) "Curb",
    TO_CHAR(SUM(decode(b.maxextend, NULL, A.BYTES, b.maxextend*8192))) "Maxb",
    TO_CHAR((SUM(a.bytes) - ROUND(c."Free"))) "TotalUsed",
    TO_CHAR(SUM(decode(b.maxextend, NULL, A.BYTES, b.maxextend*8192)) - (SUM(a.bytes) - ROUND(c."Free"))) "TotalFree",
    100*(SUM(a.bytes) - ROUND(c."Free"))/(SUM(DECODE(b.maxextend, NULL, A.BYTES, b.maxextend*8192))) "UPercent"
FROM
    dba_data_files a,
    sys.filext$ b,
    (SELECT d.tablespace_name , SUM(nvl(c.bytes,0)) "Free"
    FROM dba_tablespaces d, DBA_FREE_SPACE c
    WHERE d.tablespace_name = c.tablespace_name(+) GROUP BY d.tablespace_name
    ) c
WHERE a.file_id = b.file#(+)
    AND a.tablespace_name = c.tablespace_name
GROUP BY
    a.tablespace_name, c."Free"
ORDER BY
    ROUND(100*(SUM(a.bytes) - ROUND(c."Free"))/(SUM(decode(b.maxextend, NULL, A.BYTES, b.maxextend*8192)))) DESC;

EXIT;

Cette requête retourne le nom du tablespace, deux colonnes dont je ne comprends pas trop l’utilité, puis la taille totale et l’espace libre en octets, et enfin le pourcentage d’utilisation. Comme la requête doit obligatoirement être stockée dans un fichier, je suis obligé de tout retourner à chaque fois, sinon ça impliquerait de créer un fichier à la volée et ça créerait de la complexité.

LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 /opt/oracle/instantclient_12_2/sqlplus -s "user/password@ (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 1.2.3.4)(PORT = 1521))) (CONNECT_DATA = (SERVICE_NAME = NOM_DU_SERVICE)))" @/usr/lib/zabbix/externalscripts/oracle_tablespace_stat.ext

TABLESPACE_NAME Curb Maxb TotalUsed TotalFree UPercent
------------------------------ ---------------------------------------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------
WF_TABLE 89120571392 93415538688 83884572672 9530966016 89.797237
SYSAUX 933232640 34359721984 879296512 33480425472 2.55909088
SYSTEM 807403520 34359721984 801243136 33558478848 2.33192555
ZABBIX_TS 104857600 104857600 1048576 103809024 1
USERS 5242880 34359721984 1376256 34358345728 .004005434
UNDOTBS1 728760320 34359721984 14548992 34345172992 .04234316

(ici j’ai réactivé l’affichage de l’en-tête des colonnes, pour que le résultat soit plus facile à comprendre)

Maintenant il reste à exploiter le résultat, via le script oracle_tablespace_stat.sh :

#!/bin/bash

LD_LIBRARY_PATH=/usr/lib/oracle/12.1/client64/lib

/usr/lib/oracle/12.1/client64/bin/sqlplus -s "$3/$4@ (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = $1)(PORT = $2))) (CONNECT_DATA = (SERVICE_NAME = $5)))" @/usr/lib/zabbix/externalscripts/oracle_tablespace_stat.ext | grep -E "^$6\W" | awk "{print \$$7}"

Ce script est assez simple. Il prends les mêmes paramètres que le script de découverte mais en rajouter deux autres : le nom du tablespace, et le numéro de la colonne dont on veut retrouver la donnée. J’ai choisis de faire le filtrage sur le numéro de colonne plutôt que sur son nom pour simplifier la commande. La clé Zabbix est moins clair, mais le script est beaucoup plus simple. C’est un compromis.

Testons le script à la main. Je vais demander le pourcentage d’espace utilisé sur le tablespace WF_TABLE :

root@SV-TCA-ZPXSCP01+~# /usr/lib/zabbix/externalscripts/stat.sh IP 1521 user password NOM_DU_SERVICE WF_TABLE 6
89.797237

Ce tablespace est bien utilisé à 89%. Le script est donc fonctionnel. Il ne reste plus qu’à créer les items.

Un exemple de clé : oracle_tablespace_stat.sh[{HOST.IP},{$ORACLE_PORT},{$ORACLE_USER},{$ORACLE_PASSWORD},{$ORACLE_SERVICENAME},{#ORACLE_TABLESPACE_NAME},2]

La configuration d’un des items :

Tous les items :

Il ne reste plus qu’à créer le trigger, avec l’expression : {Template Oracle:oracle_tablespace_stat.sh[{HOST.IP},{$ORACLE_PORT},{$ORACLE_USER},{$ORACLE_PASSWORD},{$ORACLE_SERVICENAME},{#ORACLE_TABLESPACE_NAME},6].min(#3)}>90

Linux : se connecter à un serveur Oracle

L’installation

Dans le cadre de mon travail, j’ai dû lancer des requêtes Oracles depuis un serveur sous Linux. Et quand on n’a jamais travaillé avec Oracle c’est une grosse galère. Voici ma procédure, testée sur Ubuntu 18.04. Elle est très largement inspirée de https://manjaro.site/how-to-install-sqlplus-utility-on-ubuntu-18-04-and-ubuntu-18-10/.

D’abord il faut aller sur le site officiel d’Oracle pour télécharger le client SQL*Plus. Les deux fichiers à télécharger sont instantclient-basic-linux.x64-12.2.0.1.0.zip et instantclient-sqlplus-linux.x64-12.2.0.1.0.zip. Le téléchargement nécessite un compte Oracle. J’en ai créé un pour l’occasion.

Ensuite on mets les deux fichiers dans le dossier /opt/oracle, et on les extrait :

unzip instantclient-basic-linux.x64-12.2.0.1.0.zip
unzip instantclient-sqlplus-linux.x64-12.2.0.1.0.zip

Puis on corrige quelques liens :

cd /opt/oracle/instantclient_12_2
ln -s libclntsh.so.12.1 libclntsh.so
ln -s libocci.so.12.1 libocci.so

Et pour terminer on installe libaio :

apt-get install libaio1

Il ne reste plus qu’à lancer la commande sqlplus pour tester :

LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 /opt/oracle/instantclient_12_2/sqlplus

Il est obligatoire de renseigner la variable d’environnement LD_LIBRARY_PATH, sinon SQL*Plus ne retrouve pas ses bibliothèques.

L’utilisation

L’utilisation de SQL*Plus est particulière. Je pense que c’est dû au fonctionnement d’Oracle. Il n’est pas possible de passer la requête en paramètre, comme avec MySQL ou PostgreSQL. J’ai d’ailleurs bien galéré à trouver comment spécifier les paramètres de connexion en ligne de commande.

Donc, il faut créer un fichier /usr/lib/oracle/tablespace.ext (par exemple), contenant :

SELECT
    tablespace_name
FROM
    dba_data_files
GROUP BY
    tablespace_name;

EXIT;

Notez le EXIT; à la fin. C’est indispensable sinon SQL*Plus ne rends pas la main.

Cette commande doit afficher la liste des tablespaces sur la base Oracle. Ça me servira plus tard pour mettre en place la supervision.

Pour lancer la requête, ça se passe avec la commande suivante :

LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 /opt/oracle/instantclient_12_2/sqlplus -s "user/password@ (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 1.2.3.4)(PORT = 1521))) (CONNECT_DATA = (SERVICE_NAME = NOM_DU_SERVICE)))" @/usr/lib/oracle/tablespace.ext

Je ne pourrais pas détailler les paramètres de cette commande, c’est un gros sac de nœuds. C’est un admin Oracle qui m’a fourni le tout, je me contente d’appliquer. Pensez juste à remplacer les bons paramètres (nom d’utilisateur, mot de passe, adresse IP, nom du service et éventuellement port).

Chez moi ça donne :

root@SV-TCA-ZPXSCP01+~# LD_LIBRARY_PATH=/opt/oracle/instantclient_12_2 /opt/oracle/instantclient_12_2/sqlplus -s "user/password@ (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 1.2.3.4)(PORT = 1521))) (CONNECT_DATA = (SERVICE_NAME = NOM_DU_SERVICE)))" @/usr/lib/oracle/tablespace.ext

TABLESPACE_NAME
------------------------------
ZABBIX_TS
UNDOTBS1
SYSAUX
USERS
SYSTEM
WF_TABLE

6 rows selected

C’est gagné, j’ai bien récupéré la liste des tablespaces. Ma requête a fonctionné.

Dans le prochain article je vais détailler comment mettre en place une supervision des tablespaces Oracle avec Zabbix, en utilisant SQL*Plus.

 

Firefox : régler le zoom pour toutes les pages

Je travaille sur un ordinateur portable avec un écran de 38 cm (15 pouces) en 1920×1080. Sur certains sites, le texte peut être vraiment petit et difficile à lire. Par exemple :

Il y a une solution pour corriger ce problème. Dans la barre d’adresse, taper about:config. Firefox va probablement vous afficher un avertissement. Après l’avoir lu, cliquer sur Je prends le risque. Dans la barer de recherche en haut, taper layout.css.devPixelsPerPx. Double-cliquez sur le seul résultat pour modifier sa valeur (elle est à -1 par défaut).

Le nombre correspond en gros au pourcentage du zoom que vous souhaitez obtenir. Par exemple :

  • 0.5 : texte deux fois plus petit
  • 1 : texte normal (équivalent à -1)
  • 2 : texte deux fois plus gros

Pour mon cas particulier, j’ai mis à 1.25. Les textes sont nettement plus agréables à lire :

À noter que tous les textes sont grossis, y compris ceux de l’interface de Firefox.

Utiliser un proxy Squid via un tunnel SSH inverse pour accéder à internet

Ça c’est du titre qui en impose ! Personne n’a rien compris, y compris moi.

Je vais vous expliquer ma problématique. On m’a fourni un serveur de test sous Ubuntu 18.04. Je dois y installer l’applicatif Zabbix via les dépôts et faire quelques tests pour valider une procédure. Le problème est que ce serveur n’a pas accès à internet, ni a aucune autre connexion sortantes. Seules les connexions entrantes depuis une petite plage d’IP (dont la mienne) sont autorisées. Un schéma pour mieux visualiser la situation :

Dans ces conditions, impossible d’installer Zabbix via les dépôts, puisque l’accès internet n’est pas possible. J’ai donc installé Squid sur mon ordinateur (un Ubuntu 18.04 lui aussi), pour qu’il serve de proxy entre le serveur et internet : apt-get install squid

Ensuite je me suis connecté en SSH au serveur, en montant au passage un tunnel SSH inverse pour permettre la communication serveur → ordinateur : ssh -R 3128:localhost:3128 root@1.2.3.4
1.2.3.4 est l’IP du serveur et 3128 est le port Squid par défaut.
Avec cette commande, le port 3128 sur le serveur sera automatiquement redirigé vers le port 3128 de mon ordinateur. C’est un peu comme si Squid avait été installé en local sur le serveur mais avait quand même accès à internet.

En pratique, les données vers internet vont suivre ce trajet :

Il ne reste plus qu’à modifier la configuration de Aptitude pour qu’il utilise le proxy Squid. Il faut créer le fichier /etc/apt/apt.conf et mettre dedans :

Acquire::http {
        Proxy "http://127.0.0.1:3128";
};

Linux : utiliser des caractères spéciaux dans Bash

Supposons que j’ai un fichier qui contient :

Ligne1 aze
Ligne2 rty
Ligne3 ui
Ligne4 op

Note : il n’y a pas d’espace dans le fichier, uniquement des tabulations.

Comment faire pour afficher uniquement la deuxième colonne ? La commande cut serait parfaite mais il n’est pas possible de mettre une tabulation dans une ligne de commande Bash.

En fait c’est plutôt facile, Il est possible d’utiliser des séquences d’échappement grâce à l’ANSI-C Quoting (désolé, j’ai pas trouvé de traduction). Exemple :

$ cut -d $'\t' -f 2 fichier.txt
aze
rty
ui
op

La liste des séquences d’échappement est disponible ici : https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html

L’ANSI-C Quoting est disponible avec toutes les commandes Bash, et permets d’utiliser pas mal de commandes de manière « non standard ». Par exemple, pour affiche la troisième ligne d’un fichier :

$ cut -d $'\n' -f 3 fichier.txt
Ligne3 ui

Linux : changer le séparateur dans une boucle for

Admettons que j’ai un fichier qui contienne :

Linux, y’a moins bien mais c’est plus chère.
rm -fr en root, system dans la choucroute.

Faisons une boucle for basique dessus :

for i in `cat fichier.txt`
do
  echo $i
done

Le résultat ne correspond pas à ce à quoi on pourrait s’attendre :

Linux,
y’a
moins
bien
mais
c’est
plus
chère.
rm
-fr
en
root,
system
dans
la
choucroute.

La raison est simple, la boucle for prends comme séparateur les retours à la ligne, mais aussi les espaces, les tabulations et tous les autres caractères du style. Heureusement, la variable d’environnement $IFS (comme Internal Field Separator) permets de fournir sa propre liste de séparateurs. Il suffit de faire :

IFS=$'\n'
for i in `cat fichier.txt`
do
  echo $i
done

Le résultat correspond à ce qui est attendu :

Linux, y’a moins bien mais c’est plus chère.
rm -fr en root, system dans la choucroute.

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.

#!/usr/bin/env python3
#  Copyright 2018 palc.fr
#
#  Licensed under the WTFPL, Version 2
#            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 
# 0. You just DO WHAT THE FUCK YOU WANT TO.

"""
  A trigger status viewer

  This script displays the status of triggers on some devices

  Usage :
      ./triggerstatus.py zabbix_server [list of triggers id]
"""

import os       # To manage cursor and window size
import signal   # To manage Ctrl+C and widnow resizing
import sys      # To manage script arguments and terminal buffer
import time     # To get date and for temporisation

import getpass  # For password input without displaying it
import math     # For calculating graph datas
from pyzabbix import ZabbixAPI

def signal_sigint(sig, frame):
    """
        When receiving SIGINT (Ctrl+c), the script exit proprely
    """
    os.system('setterm -cursor on')
    print()
    exit (0)

def signal_sigwinch(sig, frame):
    """
        When the window is resized, redraw the screen
    """
    draw(ok, notclassified, information, warning, average, high, disaster, disable, date)

def draw(ok, notclassified, information, warning, average, high, disaster, disable, date):
    """
        Clear the screen and redraw all the datas
    """

    os.system('clear')

    # Get screen width and height (in characters)
    rows, columns = os.popen('stty size', 'r').read().split()
    rows=int(rows)
    columns=int(columns)

    # General informations
    sum_triggers=len(disable)+len(ok)+len(notclassified)+len(information)+len(warning)+len(average)+len(high)+len(disaster)
    print(date, end='')
    print(" | " + str(sum_triggers) + " (", end='')
    print("\033[32m" + str(len(ok)) + "\033[0m/", end='')
    print("\033[31m" + str(len(notclassified)+len(information)+len(warning)+len(average)+len(high)+len(disaster)) + "\033[0m/", end='')
    print("\033[90m" + str(len(disable)) + "\033[0m)")

    # Calculate graph width
    coef_graph=1
    if(sum_triggers>(columns-6)):
        coef_graph=(columns-6)/sum_triggers

    # Draw the graph
    print('[', end='')
    for i in (range(0, math.ceil(len(disaster)*coef_graph))):
        print("\033[91m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(high)*coef_graph))):
        print("\033[31m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(average)*coef_graph))):
        print("\033[93m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(warning)*coef_graph))):
        print("\033[33m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(information)*coef_graph))):
        print("\033[34m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(notclassified)*coef_graph))):
        print("\033[37m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(disable)*coef_graph))):
        print("\033[90m", end='')
        print('|', end='')
    for i in (range(0, math.ceil(len(ok)*coef_graph))):
        print("\033[32m", end='')
        print('|', end='')
    print("\033[0m]", end='')

    length=3

    # Display items in PROBLEM state
    for i in disaster:
        print("\033[91m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1
    for i in high:
        print("\033[31m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1
    for i in average:
        print("\033[93m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1
    for i in warning:
        print("\033[33m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1
    for i in information:
        print("\033[34m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1
    for i in notclassified:
        print("\033[37m", end='')
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1

    # Display disabled items
    print("\033[90m", end='')
    for i in disable:
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1

    # Display items in OK state
    print("\033[32m", end='')
    for i in ok:
        if(length<rows):
            print("\n" + i[:columns], end='')
            length+=1

    print("\033[0m", end='')

    sys.stdout.flush()

# When receiving SIGINT (Ctrl+c)
signal.signal(signal.SIGINT, signal_sigint)
# When the window is resized
signal.signal(signal.SIGWINCH, signal_sigwinch)

# Connection to Zabbix server
user = input("Username: ")
password = getpass.getpass("Password for " + user + ": ")
try:
    zapi = ZabbixAPI('https://' + sys.argv[1])
    zapi.login(user, password)
except:
    print("Cannot conect to Zabbix server ☹")
    exit(1)

# Disable cursor
os.system('setterm -cursor off')

# main loop
while 1:
    # Tables for each trigger status
    ok=[]
    notclassified=[]
    information=[]
    warning=[]
    average=[]
    high=[]
    disaster=[]
    disable=[]

    for hostid in sys.argv[2:]:
        # Get triggers status
        try:
            triggers=zapi.trigger.get(hostids=hostid)
        except:
            print("Problem with Zabbix server ☹")
            exit(1)

        for trigger in triggers:
            # Item is in OK state
            if(trigger['status'] == '0' and trigger['value'] == '0'):
                ok.append(hostid + ' ' + trigger['description'])

            # Item is in PROBLEM state
            if(trigger['status'] == '0' and trigger['value'] == '1'):
                if(trigger['priority'] == '0'):
                    notclassified.append(hostid + ' ' + trigger['description'] + ' (not classified)')
                if(trigger['priority'] == '1'):
                    information.append(hostid + ' ' + trigger['description'] + ' (information)')
                if(trigger['priority'] == '2'):
                    warning.append(hostid + ' ' + trigger['description'] + ' (warning)')
                if(trigger['priority'] == '3'):
                    average.append(hostid + ' ' + trigger['description'] + ' (average)')
                if(trigger['priority'] == '4'):
                    high.append(hostid + ' ' + trigger['description'] + ' (high)')
                if(trigger['priority'] == '5'):
                    disaster.append(hostid + ' ' + trigger['description'] + ' (disaster)')

            # Item is disabled
            if(trigger['status'] == '1'):
                disable.append(hostid + ' ' + trigger['description'])

    date=time.strftime("%d/%m/%Y %H:%M:%S")

    draw(ok, notclassified, information, warning, average, high, disaster, disable, date)

    time.sleep(10)

exit(0)
triggerstatus.py

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
  • 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.

Zabbix : planifier le lancement d’un item

Zabbix 4 offre quelques possibilités pour planifier le lancement d’un item à un moment précis. Mais avec les versions précédentes, il faut ruser.

Il faut créer l’item avec Update interval (in sec) à 0, pour que l’item ne soit jamais exécuté. Ensuite il faut configurer un Flexible intervals avec un Interval à 60 et une Period de une minute. De cette manière, l’item sera exécuté exactement une seul fois dans l’intervalle de une minute.

Par exemple, pour lancer un item à 10h du lundi au samedi :

Firefox : masquer la barre d’onglets

J’utilise le module Firefox Tree Style Tab, qui permets d’afficher la barre d’onglets sur le côté du navigateur. C’est beaucoup plus simple pour organiser ses onglets. Il est également possible de les organiser de façon arborescente, ce qui est génial si on travaille sur plusieurs trucs en même temps.

Mais sur les versions récentes de Firefox il n’est pas possible de masquer la barre d’onglets en haut de l’écran. Ça fait doublon.

Pour corriger le problème, il faut créer un fichier ~/.mozilla/firefox/XXXXXXXX.default/chrome/userChrome.css (XXXXXXXX est une suite alphanumérique aléatoire) :

#TabsToolbar {
  visibility: collapse;
}

#titlebar {
  visibility: collapse;
}

#TabsToolbar {
  visibility: collapse;
}

Zabbix : faire un export de tous les équipements supervisés et de leur statut

Cette technique est super crade. Je l’ai utilisé pour faire un export en urgence des serveurs supervisés. Elle tient en une seule requête SQL :

SELECT
  hosts.hostid AS ID,
  hosts.host AS Serveur,
  CASE
    WHEN hosts.status=0 THEN 'Activé'
    WHEN hosts.status=1 THEN 'Désactivé'
  END AS Statut,
  CASE
    WHEN hosts.maintenance_status=0 THEN 'Pas en maintenance'
    WHEN hosts.maintenance_status=1 THEN 'En maintenance'
  END AS Maintenance,
  interface.ip AS IP
  FROM hosts, interface
  WHERE hosts.status<>3
  AND hosts.hostid=interface.hostid;

Cette requête fait un export des serveurs contenant :

  • L’ID de l’équipement dans Zabbix
  • Le nom de l’équipement
  • Son status (Activé ou Désactivé)
  • L’état de sa maintenance (En maintenance ou Pas en maintenance)
  • Son adresse IP

La requête est assez simple mais comporte quelques subtilités :

  • Les conditions en utilisant CASE / WHEN / THEN / END, qui permettent de convertir le numéro du status en son texte
  • Les AS qui permettent d’affiche un en-tête de colonne plus clair
  • La condition WHERE hosts.status<>3 qui permets de masquer les templates