Le système d'exploitation GNU-Linux/Scripts de surveillance

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche


  1. Qu'est-ce qu'un système d'exploitation ?
  2. Unix et Linux
  3. Partitionnement du disque
  4. Installation
    1. Installer Debian via le réseau
  5. Utilisateur Unix, l'interface console
    1. Le login
    2. Commandes de base
    3. L'aide en ligne man
    4. L'éditeur de texte vi
    5. Les shells
    6. La complétion
    7. Les jokers
    8. Les répertoires importants
    9. Redirection des entrées/sorties
    10. Invoquer un programme en tâche de fond
    11. Propriétaires et droits d'accès
    12. Processus
    13. Locale
  6. Administration du système
    1. Configuration du réseau
    2. Les utilisateurs et groupes
    3. Le processus d'initialisation
    4. Les systèmes de fichiers
    5. Le système virtuel /proc
    6. Les périphériques /dev
    7. L'ordonnanceur de travaux cron
    8. Le backup : tar et gzip
    9. ghost avec partimage
    10. sauvegarde de fichiers avec rsync
    11. Les fichiers journaux syslog
    12. Installation de nouveaux logiciels
    13. Le noyau Linux et les modules
    14. Autres commandes utiles
    15. Installation RAID1 logiciel + LVM + XFS
    16. Scripts de surveillance
    17. Réseaux sans fil
  7. Administration des services réseaux
    1. Le serveur de noms BIND
    2. Le serveur de configuration réseau DHCP
    3. Le serveur de shell distant SSH
    4. Le partage de fichiers Samba
    5. Le partage de fichiers NFS
    6. Le serveur d'impression CUPS
    7. Le serveur de fichiers FTP
    8. Le serveur Web Apache
    9. La base de données MySQL
    10. Le serveur de mails Postfix
    11. Les annuaires LDAP
    12. L'outil d'administration Webmin
    13. La supervision
  8. Sécurisation d'un serveur Linux
    1. Installation d'un service en mode chroot
    2. Protection avec iptables
  9. Médiagraphie
  10. Auteurs


Ce chapitre contient différents scripts de surveillance de l'activité d'un serveur Linux

En langage Python[modifier | modifier le wikicode]

alimon.py (A LInux MONitor)[modifier | modifier le wikicode]

Lien 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