Le système d'exploitation GNU-Linux/Scripts de surveillance
Ce chapitre contient différents scripts de surveillance de l'activité d'un serveur Linux
En langage Python
modifieralimon.py (A LInux MONitor)
modifierLien direct : alimon.py.
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
##############################################################################
#
# ALiMon.py : A LInux MONitor
#
##############################################################################
#
# Ce script réalise différentes opérations de monitoring et met en évidence
# certains points importants comme une partition disque bientôt pleine ou une
# charge processeur trop élevée.
#
# Ce script a été réalisé durant une scéance de travaux pratiques et a des fins
# didactiques. Il est issu du travail collectif des personnes citées ci-dessous
# en auteurs et a nécessité uniquement deux heures de développement.
#
##############################################################################
#
# Auteurs :
#
# David BISPO, Christophe CARLIER, Jonathan DUHAIL, Jonathan GAULUPEAU,
# Lahoucine HAMOUCHE, Hicham OUHNA, Manuel PIRES, Yann VAITILINGOM,
# Jérémy PELLAUMAIL et Alexandre GUY
#
# Nous remercions également les connectés du canal #afpy du réseau Freenode
# pour leur aide concernant l'unicode et l'encodage utf-8.
#
# Version : 0.3
#
##############################################################################
#
# Ce script est diffusé sous la licence EUPL v1.1
#
# This script is released under EUPL v1.1
#
# http://ec.europa.eu/idabc/eupl
#
##############################################################################
#
# Ce programme est un logiciel libre ; vous pouvez le re-distribuer et/ou le
# modifier au titre des clauses de la European Union Public Licence (EUPL)
# version 1.1, telle que publiée par l'Union Européenne.
#
# Ce programme est distribué dans l'espoir qu'il sera utile,
# mais SANS AUCUNE GARANTIE ; sans même une garantie implicite de
# COMMERCIABILITÉ ou DE CONFORMITÉ À UNE UTILISATION PARTICULIÈRE.
# Voir la EUPL version 1.1 pour plus de détails.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the European Union Public Licence (EUPL) version 1.1
# as published by the European Union.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the EUPL version 1.1 for more details.
#
##############################################################################
#
# Utilisation manuelle : ./alimon.py
#
# Utilisation automatisée : Rajouter dans le cron :
# 55 23 * * * root alimon.py
#
##############################################################################
#########################################
#### Configuration & pré-traitements ####
#########################################
#### Modules importés ####
import commands, unicodedata, os, sys, getopt
#### Fonction Affichage du titre formaté ####
def titre(message):
message = unicode(message,'utf-8')
print "\n", "#" * 59
print "######", message.center(45).encode('utf-8'), "######"
print "#" * 59, "\n"
#### Fonction affichant l'aide ####
def usage():
print "###### Aide de alimon ######"
print "./alimon.py [-f] [-d] [-h]"
print " -f Lance le programme en mode dégradé. Utile si certaines commandes"
print " ne sont pas installées sur le serveur."
print " -d Lance le programme en mode debug."
print " -h Affiche l'aide du programme."
#### Variables globables ####
# Active / désactive le mode debug
debug = 0
# Active / désactive le mode dégradé
force = 0
# Seuil maxi du loadavg
seuil_maxi_loadavg = 1.0
# Température maxi des disques durs
temperature_hdd_maxi = 45
# Taux d'occupation maxi des disques durs
taux_occupation_maxi = 80
# Logins autorisés
loginsok = ['alex', 'jo']
# Liste des daemons qui doivent être en cours d'exécution
proclist = ['sshd', 'apache2', 'mysqld', 'named', 'master', 'murmurd', 'pop3-login', 'teamspeak-serve', 'couriertcpd']
# Pourcentage maxi de swap
pourcentage_mem_maxi = 10
# Mémoire mini disponible en Mo
mem_mini = 200
# Liste des sites à 'pinguer'
url_ping = ['www.google.fr']
# Liste des périphériques RAID (exemple : ['md0', 'md1'])
raidlist = []
#### Récupère les arguments ####
# Récupère la liste des arguments
try:
optlist, list = getopt.getopt(sys.argv[1:], 'dhf')
# Si un argument ne figure pas dans la liste prédéfinie, affiche la fonction 'usage' (aide) puis quitte
except getopt.GetoptError:
usage()
sys.exit(1)
# Traite les arguments donnés
for opt in optlist:
if opt[0] == '-h':
usage()
sys.exit(0)
if opt[0] == '-d':
debug = 1
if opt[0] == '-f':
force = 1
#### Test de présence des commandes shell et récupération de leur chemin ####
commandes_utilisees = ['cat','hostname','last','hddtemp','df','ps','free','ping','grep','uniq','who','uname']
# Contrôle la commande 'mdadm' uniquement si une liste de disques raid est définie
if raidlist:
commandes_utilisees.append('mdadm')
commandes = {}
for comm in commandes_utilisees:
(res,commande) = commands.getstatusoutput("/usr/bin/which %s" % comm)
# Si une commande n'existe pas mais que le mode Force est activé
if res and force:
print "La commande", comm, "n'est pas présente sur votre système, fonctionnement en mode dégradé."
commandes[comm] = ""
# Si une commande n'exite pas
elif res:
print "La commande", comm, "n'est pas présente sur votre système. Arrêt du programme."
print "Tapez './alimon.py -h' pour en savoir plus."
sys.exit(1)
else:
commandes[comm] = commande
#### Récupération de la liste des disques durs ####
result = commands.getoutput("%s /proc/partitions" % commandes['cat']).split("\n")
result.pop(0)
hddlist = []
for ligne in result:
if debug: print "debug> Ligne du fichier '/proc/partitions' en cours de traitement :", ligne
hdd = ''.join(ligne.split()[-1:])[:3]
if debug: print "debug> Disque dur à ajouter à la liste :", hdd
if hdd and hdd not in hddlist:
hddlist.append(hdd)
#############################
#### Programme principal ####
#############################
#### INFOS SERVEUR ####
if commandes['hostname'] and commandes['uname'] and commandes['cat'] and commandes['grep'] and commandes['uniq']:
titre("Informations sur le serveur")
# On récupère la première ligne du fichier /etc/issue qui contient généralement le nom et la version de la distribution Linux
version = open("/etc/issue", "r").readlines()[0][:-1]
if debug: print "debug> Version de la distribution :", version
# On récupère le nom du serveur
hostname = commands.getoutput(commandes['hostname'])
if debug: print "debug> Hostname :", hostname
print "Serveur %s sous %s" % (hostname, version)
print
# On récupère et affiche la version du noyau par la commande 'uname -r'
print "Noyau Linux :", commands.getoutput("%s -r" % commandes['uname'])
# On récupère et affiche le(s) processeur(s) installé(s) sur le serveur
cpu = commands.getoutput("%s /proc/cpuinfo | %s \"model name\" | %s" % (commandes['cat'], commandes['grep'], commandes['uniq']))
if debug: print "debug> Liste des processeurs non post-traitée :", cpu
print "Processeur(s) :", cpu.split(': ')[1]
# On récupère et affiche la quantité de mémoire installée sur le serveur
mem = commands.getoutput("%s /proc/meminfo | %s \"MemTotal\"" % (commandes['cat'], commandes['grep']))
if debug: print "debug> Quantité de mémoire non post-traitée :", mem
print "Mémoire vive :", ' '.join(mem.split()[1:])
#### DUREE DE FONCTIONNEMENT DU SERVEUR ####
if commandes['cat']:
titre("Durée de fonctionnement")
# On récupère la durée de fonctionnement en secondes dans /proc/uptime
result = commands.getoutput("%s /proc/uptime" % (commandes['cat'])).split()[0]
duree = float(result)
if debug: print "debug> Résultat de cat '/proc/uptime' :", duree
# On calcule le reste en secondes
secondes = duree%60
if debug: print "debug> Secondes :", secondes
# On calcule le reste en minutes
minutes = duree/60%60
if debug: print "debug> Minutes :", minutes
# On calcule le reste en heures
heures = duree/60/60%24
if debug: print "debug> Heures :", heures
# On calcule le nombre de jours
jours = int(duree/60/60/24)
if debug: print "debug> Jours :", jours
print "Serveur lancé depuis %i jour(s), %i heure(s), %i minute(s) et %i seconde(s)." % (jours, heures, minutes, secondes)
#### VERIFICATION DU LOAD AVERAGE ####
if commandes['cat']:
titre("Vérification du load average")
# On appelle une commande Unix et on récupère le résultat dans la variable result
result = commands.getoutput("%s /proc/loadavg" % (commandes['cat']))
if debug: print "debug> Résultat de 'cat /proc/loadavg' :", result
# On découpe la chaine de caractères en une liste selon le caractère espace
liste_result = result.split()
if debug: print "debug> La liste découpée :", liste_result
if debug: print "debug> La première valeur de la liste :", liste_result[0]
# On compare le load avg de la dernière minute avec le seuil maxi
# Attention, on converti la chaine de caractère liste_result[0] en nombre flottant via float()
if float(liste_result[0]) > seuil_maxi_loadavg:
print "Alerte ! charge CPU supérieure à", seuil_maxi_loadavg, "!!!"
else:
print "Charge CPU %s normale car inférieure à %.2f" % (liste_result[0],seuil_maxi_loadavg)
#### CONNECTIONS DU JOUR ####
if commandes['last'] and commandes['grep']:
titre("Connections du jour")
# On récupère les dernières connections avec la commande 'last', on filtre avec 'grep'
login = commands.getoutput("%s | %s \"$(LANG=C date +\"%%a %%b %%-d\")\"" % (commandes['last'], commandes['grep']))
if login == "":
login = commands.getoutput("%s | %s \"$(LANG=C date +\"%%a %%b %%-d\")\"" % (commandes['last'], commandes['grep']))
print login
#### TEMPERATURE DES HDD ####
if commandes['hddtemp']:
titre("Températures des disques durs")
# On vérifie la température pour chaque disque dans la liste 'hddlist' avec la commande 'hddtemp'
for disque in hddlist:
if debug: print "debug> Disque en cours de contrôle :", disque
# On récupère la température du disque
temperature = commands.getoutput("%s -n /dev/%s" % (commandes['hddtemp'], disque))
if debug: print "debug> Température du disque en cours :", temperature
# On vérifie que la température du disque n'est pas supérieure au seuil de tolérance, sinon on imprime un message d'alerte
# S'il y a une erreur (disque non compatible SMART), on passe au disque suivant
try:
if int(temperature) > temperature_hdd_maxi:
print "Alerte ! Le disque dur /dev/%s a dépassé %s°C, il est actuellement à %s°C !!!" % \
(disque, temperature_hdd_maxi, temperature)
else:
print "Le disque dur /dev/%s est à %s°C et inférieur au seuil de %s°C" % \
(disque, temperature, temperature_hdd_maxi)
except:
pass
#### VERIFICATION DE L'ESPACE DISQUE ####
if commandes['df'] and commandes['grep']:
titre("Vérification de l'espace disque")
# On récupère le pourcentage d'espace libre sur toutes les partitions dont le périphérique commence par '/dev' avec la commande 'df'
result = commands.getoutput("%s -P | %s -e '^/dev'" %(commandes['df'], commandes['grep']))
if debug: print "debug> Résultat du 'df' :", result
# On fait un contrôle sur chaque ligne du résultat du 'df'
for disque in result.split('\n'):
taux_occupation = disque.split()[4][:-1]
if debug: print "debug> Disque en cours de contrôle :", disque
if debug: print "debug> Taux d'occupation du disque en cours :", taux_occupation
# On vérifie que le taux d'occupation n'est pas supérieur au seuil de tolérance, sinon on imprime un message d'alerte
if int(taux_occupation) > taux_occupation_maxi:
print "Attention la partition %s est rempli à %s !!!" %(disque.split()[0], disque.split()[4])
else:
print "La partition %s est pleine à %s." %(disque.split()[0], disque.split()[4])
#### VERIFICATION ETAT RAID ####
# Se lance si une liste de périphériques a été définie
if raidlist:
if commandes['mdadm']:
titre("Vérification de l'état du RAID")
# On teste chaque périphérique de la liste
for hddraid in raidlist:
# On récupère le status de la commande qui vérifie l'état d'un RAID
(raidstatus, raid) = commands.getstatusoutput("%s --detail -t /dev/%s" %(commandes['mdadm'], hddraid))
# On teste si le status est différent de 0 (donc status en erreur)
if raidstatus!=0:
print "Attention, le périphérique RAID /dev/%s a au moins un disque en dysfonctionnement !" % hddraid
else:
print "Le périphérique RAID /dev/%s fonctionne normalement." % hddraid
#### VERIFICATION CONNECTIONS EN COURS ####
if commandes['who']:
titre("Vérification des logins actuellement connectés")
# On récupère les noms des utilisateurs actuellement connectés avec la commande 'who'
wholiste = commands.getoutput(commandes['who']).split('\n')
if debug: print "debug> Liste des utilisateurs connectés :", wholiste
liste_logins_connectes = []
# On récupère uniquement la première colonne de chaque ligne
for ligne in wholiste:
if ligne:
if debug: print "debug> Ligne en cours de traitement :", ligne
user = ligne.split()[0]
if debug: print "debug> Utilisateur en cours de traitement :", user
# On ajoute l'utilisateur à une liste s'il n'y est pas déjà (évite les doublons)
if user not in liste_logins_connectes:
liste_logins_connectes.append(user)
# On vérifie que les utilisateurs précédemment récupérés sont bien dans la liste des utilisateurs autorisés, sinon on imprime un message d'alerte
for user in liste_logins_connectes:
if user in loginsok:
print "utilisateur", user, "OK"
else:
print "ATTENTION L'utilisateur", user, "est connecte MAIS n'est pas dans la liste"
#### VERIFICATION DES PROCESSUS ####
if commandes['ps']:
titre("Vérification des processus")
# On récupère les processus lancés avec la commande 'ps'
result = commands.getoutput("%s -e" % (commandes['ps'])).split('\n')
# On supprime la première ligne
result.pop(0)
if debug: print "debug> Résultat de la commande 'ps' :", result
# On récupère uniquement le nom de chaque processus (dernière colonne)
psliste=[]
for processus in result:
if debug: print "debug> Processus à ajouter :", processus
psliste.append(processus.split()[-1])
if debug: print "debug> Contenu de la liste des processus :", psliste
# On vérifie que chaque processus de la liste définie au début du script est présent dans la liste récupérée précedemment
for processus in proclist:
if debug: print "debug> Processus en cours de vérification :", processus
if not processus in psliste:
print "Attention : le process %s n'est pas lancé actuellement !!!" %(processus)
else:
print "Le processus", processus, "est bien en cours d'exécution."
#### VERIFICATION DE LA MEMOIRE ####
if commandes['free']:
titre("Vérification de la mémoire")
# On récupère la quantité de mémoire libre avec la commande 'free'
result = commands.getoutput(commandes['free'])
if debug: print "debug> Résultat de la commande 'free' :", result
listefree = result.split("\n")
if debug:
print "debug> On truque le résultat de la commande free pour tester"
listefree = [' total used free shared buffers cached', 'Mem: 2066032 850780 1215252 0 114496 368584', '-/+ buffers/cache: 367700 1698332', 'Swap: 2048276 350500 2048276']
print "debug> Free truqué :", listefree
# On récupère la quantité de mémoire totale
ligne1=listefree[1].split()
memoire=float(ligne1[1])
if debug: print "debug> Mémoire totale :", memoire
# On récupère la quantité de swap totale
ligne3=listefree[3].split()
swap=float(ligne3[2])
if debug: print "debug> Swap total :", swap
# On calcule le rapport de swap par rapport à la mémoire installée
pourcentage = (swap/memoire)*100.0
if debug: print "debug> Pourcentage de Swap :", pourcentage
# On vérifie que le rapport ne dépasse pas le seuil limite autorisé, sinon on imprime un message d'alerte
if pourcentage > pourcentage_mem_maxi:
print "Alerte ! memoire swap supérieur a %d%% de la mémoire (utilisation %.2f%%) !!!" % (pourcentage_mem_maxi, pourcentage)
else:
print "Mémoire swap inférieure à %d%% (utilisation %.2f%%)" % (pourcentage_mem_maxi, pourcentage)
# On récupère la quantité de mémoire libre en Mo avec la commande 'free -m'
result = commands.getoutput("%s -m" % (commandes['free']))
result = result.split('\n')
if debug: print "debug> Résultat de la commande 'free -m' :", result
mem = result[2].split()[3]
if debug: print "debug> Quantité de mémoire libre en Mo :", mem
# On vérifie que la quantité de mémoire libre ne soit pas inférieure au seuil limite, sinon on imprime un message d'alerte
if int(mem) < mem_mini:
print "Attention !! il reste moins de %sMo de mémoire libre !!! (%sMo)" %(mem_mini, mem)
else:
print "Il y a", mem, "Mo de mémoire libre."
#### TEMPS DE REPONSE SERVEUR WEB ####
if commandes['ping']:
titre("Temps de réponse du serveur")
# On lance la commande 'ping' sur chaque url définie dans 'url_ping'
for url in url_ping:
print "Résultat de la commande ping sur %s" % (url)
print commands.getoutput("%s -c 1 %s" % (commandes['ping'], url)).split("\n")[-1]
print