Superviseur - Daemon 1.0
Ce logiciel correspond au daemon de la suite des trois logiciels composant le superviseur
daemon.py
Aller à la documentation de ce fichier.
00001 #!/usr/bin/python
00002 # -*- coding: utf-8 -*-
00003 
00004 import libvirt
00005 #import string
00006 import socket
00007 import struct
00008 import json
00009 import time
00010 import sys
00011 import os
00012 import re
00013 
00014 
00015 ## Classe de communication réseau.
00016 #
00017 #  Cette classe est la classe de base permettant d'envoyer les données via le réseau.
00018 class NetworkCommunication(object):
00019     
00020     ## Nom du fichier contenant la liste des collecteurs.
00021     FILENAME = 'collecteurs.conf.txt'
00022     
00023     ## Port par défaut
00024     DEFAULTPORT = int(55000)
00025     
00026     ## Liste contenant l'adresse et le port des collecteurs.
00027     collecteurs = list()
00028 
00029     ## Constructeur.
00030     #  Il exécute la méthode recupCollecteurs().
00031     def __init__(self):
00032         self.recupCollecteurs()
00033 
00034     ## Méthode qui envoie les données msg aux collecteurs.
00035     #  Cette méthode est purement virtuelle et doit être réimplémentée par les classes filles.
00036     def envoyer(self, msg):
00037         pass
00038 
00039     ## Récupère la liste des collecteurs depuis le fichier de configuration.
00040     #  Cette méthode récupère la list des collecteurs depuis le fichier de configuration et les stocke dans la liste 'collecteurs' sous la forme d'un dictionnaire avec comme champ 'ip' et 'port'.
00041     def recupCollecteurs(self):
00042         try:
00043             file = open(self.FILENAME, 'r')
00044             t = file.read()
00045             file.close()
00046         except IOError:
00047             print '''Vous devez créer un fichier "%s" qui contient la liste des collecteurs sous la forme ip/host[:port]''' % self.FILENAME
00048             sys.exit(1)
00049         liste = str(t).splitlines()
00050         for collecteur in liste:
00051             tmp = collecteur.split(':')
00052             c = dict()
00053             c['ip'] = tmp[0]
00054             if len(tmp) < 2:
00055                 c['port'] = self.DEFAULTPORT
00056             else:
00057                 c['port'] = int(tmp[1])
00058             self.collecteurs.append(c)
00059 
00060 
00061 ## Classe de communication réseau UDP.
00062 #
00063 #  Cette classe implémente la communication réseau via un socket UDP.
00064 class UDPScoketCommunication(NetworkCommunication):
00065 
00066     ## Constructeur.
00067     #  Il appelle le constructeur de la classe parente puis ajoute un champs seqNb contenant le numéro de séquence courant pour chaque collecteur.
00068     #  Ce numéro est initialisé à 0.
00069     def __init__(self):
00070         super(UDPScoketCommunication, self).__init__()
00071         for c in self.collecteurs:
00072             c['seqNb'] = int(0)
00073 
00074     ## Méthode qui envoie les données msg aux collecteurs via un socket UDP.
00075     #  Cette méthode envoie les données à chaque collecteur de la liste et incrémente le numéro de séquence.
00076     #  @param msg données à envoyer.
00077     def envoyer(self, msg):
00078         for c in self.collecteurs:
00079             s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
00080             s.connect((c['ip'], c['port']))
00081             msg2 = struct.pack('>1I'+str(len(msg))+'s', int(c['seqNb']), msg)
00082             if len(msg2) != s.send(msg2):
00083                 print "Erreur lors de l'envois à %s:%d." % (c['ip'], c['port'])
00084             #else:
00085                 #print "Envoyé à %s:%d." % (c['ip'], c['port'])
00086             c['seqNb'] = c['seqNb'] + 1
00087             s.close()
00088 
00089 
00090 ## Classe de base pour les classes récoltant des informations.
00091 #
00092 #  Cette classe est la classe de base pour les classes récoltant des informations.
00093 #  Chaque classe fille doit implémenter la méthode infos.
00094 class AbstractInfos(object):
00095 
00096     ## Constructeur.
00097     def __init__(self):
00098         pass
00099 
00100     ## Méthode qui renvoie des informations sous la forme d'une liste ou d'un dict.
00101     #  Cette méthode est purement virtuelle et doit être réimplémentée par les classes filles.
00102     #  @return des informations sous la forme d'une liste ou d'un dict.
00103     def infos(self):
00104         pass
00105 
00106 
00107 ## Classe de base pour les classes récoltant des informations.
00108 #
00109 #  Cette classe est la classe de base pour les classes récoltant des informations.
00110 #  Chaque classe fille doit implémenter la méthode infos.
00111 class HostInfos(AbstractInfos):
00112     ## Temps CPU.
00113     CPUTime = int()
00114     ## Date de la dernière mesure de la charge CPU.
00115     CPUMesureTime = float()
00116     ## Temps CPU passé dans les accès disques.
00117     DisqueTime = int()
00118     ## Date de la dernière mesure de la charge disque.
00119     DisqueMesureTime = float()
00120     ## Température du CPU.
00121     temperatureCPU = float(0)
00122     ## Température de la carte mère.
00123     temperatureMB = float(0)
00124 
00125     ## Constructeur.
00126     #  Le constructeur initialise diverses valeurs.
00127     def __init__(self):
00128         self.CPUInfos()
00129         self.disqueInfos()
00130         self.nomHyperviseur = self.nomMachine()
00131 
00132     ## Renvoie le nom de la machine.
00133     #
00134     #  @return le nom de la machine.
00135     def nomMachine(self):
00136         file = open('/etc/sysconfig/network', 'r')
00137         t = file.read()
00138         file.close()
00139         return re.findall('HOSTNAME=(.+)', t)[0]
00140 
00141     ## Lit la température du cpu et de la carte mère.
00142     #  Cette méthode lit la température du cpu et de la carte mère puis elle les stocke dans les propriétés temperatureCPU et temperatureMB.
00143     def temperatures(self):
00144         buff = os.popen('sensors | grep Temperature | awk \'{print $3}\'')
00145         res = str(buff.read())
00146         buff.close()
00147         temps = re.findall('[0-9]+\.[0-9]*', res)
00148         if len(temps) >= 2:
00149             self.temperatureCPU = float(temps[0])
00150             self.temperatureMB = float(temps[1])
00151 
00152     ## Renvoie l'utilisation de la mémoire RAM en pourcents.
00153     #  Cette méthode calcule l'utilisation de la mémoire RAM grâce aux informations contenues dans /proc/meminfo.
00154     #  @return l'utilisation de la mémoire RAM en pourcents.
00155     def RAMInfos(self):
00156         file = open('/proc/meminfo', 'r')
00157         t = file.read()
00158         file.close()
00159         total = int(re.findall('MemTotal: *?([0-9]+) kB', t)[0])
00160         free = int(re.findall('MemFree: *?([0-9]+) kB', t)[0])
00161         buffers = int(re.findall('Buffers: *?([0-9]+) kB', t)[0])
00162         cached = int(re.findall('Cached: *?([0-9]+) kB', t)[0])
00163         return float(total - free - buffers - cached) / float(total) * 100.0
00164 
00165     ## Renvoie le temps pendant lequel le CPU était innactif depuis le démarrage du système.
00166     #  La valeur est obtenue gâce au fichier /proc/stat.
00167     #  @return la valeur du temps CPU passé en innactivité.
00168     def CPUIdleTime(self):
00169         buff = os.popen('cat /proc/stat | grep -m1 cpu | awk \'{print $5}\'')
00170         res = int(buff.read())
00171         buff.close()
00172         return res
00173 
00174     ## Retourne l'utilisation du CPU en pourcents.
00175     #  Cette méthode calcule la moyenne du pourcentage d'utilisation du CPU depuis son dernier appel.
00176     #  @return l'utilisation du CPU en pourcents.
00177     def CPUInfos(self):
00178         LastCPUMesureTime = self.CPUMesureTime
00179         LastCPUTime = self.CPUTime
00180         self.CPUTime = self.CPUIdleTime()
00181         self.CPUMesureTime = time.time()
00182         deltaCPU = float(self.CPUTime-LastCPUTime)
00183         deltaT = float(self.CPUMesureTime-LastCPUMesureTime)
00184         coeff = float(os.sysconf_names['SC_CLK_TCK'])
00185         return (1 - deltaCPU / (100.0 * deltaT * coeff)) * 100
00186 
00187     ## Retourne la valeur du temps CPU passé dans les accès disques.
00188     #  La valeur est obtenue grâce au fichier /proc/diskstats.
00189     #  @return la valeur du temps CPU passé dans les accès disques.
00190     def disqueTime(self):
00191         buff = os.popen("cat /proc/diskstats | grep -m 1 da | awk '{print $13}'")
00192         res = int(buff.read())
00193         buff.close()
00194         return res
00195 
00196     ## Retourne l'utilisation du disque en pourcents.
00197     #  Cette méthode calcule la moyenne du pourcentage d'utilisation du disque depuis son dernier appel.
00198     #  @return l'utilisation du disque en pourcents.
00199     def disqueInfos(self):
00200         LastDisqueMesureTime = self.DisqueMesureTime
00201         LastDisqueTime = self.DisqueTime
00202         self.DisqueTime = self.disqueTime()
00203         self.DisqueMesureTime = time.time()
00204         deltaDisqueT = float(self.DisqueTime-LastDisqueTime)
00205         deltaT = float(self.DisqueMesureTime-LastDisqueMesureTime)
00206         return (deltaDisqueT/1000.0) / deltaT * 100.0
00207 
00208     ## Retourne les informations sur l'hôte sous la forme d'un dict.
00209     #  Cett méthode appelle les autres méthodes de la classe pour récupérer les informations sur l'hôte.
00210     #  @n Elle crée ensuite le dict et y stocke les informations.
00211     #  @return les informations sur l'hôte sous la forme d'un dict.
00212     def infos(self):
00213         res = dict()
00214         self.temperatures()
00215         res['name'] = self.nomHyperviseur
00216         res['time'] = time.time()
00217         res['cputemp'] = self.temperatureCPU
00218         res['mbtemp'] = self.temperatureMB
00219         res['cpu'] = self.CPUInfos()
00220         res['ram'] = self.RAMInfos()
00221         res['disk'] = self.disqueInfos()
00222         #res['network'] =
00223         return res
00224 
00225 
00226 ## Classe de base pour les classes récoltant des informations.
00227 #
00228 #  Cette classe est la classe de base pour les classes récoltant des informations.
00229 #  Chaque classe fille doit implémenter la méthode infos.
00230 class VMInfos(AbstractInfos):
00231     Conn = None
00232     VMs = list()
00233     nbpCPU = int()
00234     CPUTime = dict()
00235     pCPUInfos = float()
00236     pCPUIdleTime = dict()
00237     Mesuretime = dict()
00238     hostinf = HostInfos()
00239 
00240     ## Constructeur.
00241     #  Le constructeur initialise diverses valeurs.
00242     def __init__(self):
00243         self.connexion()
00244         self.nbpCPU = self.Conn.getInfo()[2]
00245         self.pCPUInfos = self.hostinf.CPUInfos()
00246         self.listeVM()
00247         self.infos()
00248 
00249     ## Se connecte à libvirt.
00250     #  Cette méthode ouvre une connexion avec l'API libvirt.
00251     def connexion(self):
00252         self.Conn = libvirt.openReadOnly(None)
00253         if self.Conn == None:
00254             print 'Impossible de se connecter à l\'hyperviseur'
00255             sys.exit(1)
00256 
00257     ## Génère la liste des machines virtuelles.
00258     #  Utilise libvirt pour lister les machines virtuelles démarrées et arrêtées, puis stocke la liste dans la propriété VMs.
00259     def listeVM(self):
00260         self.VMs = list()
00261         for name in self.Conn.listDefinedDomains():
00262             self.VMs.append(self.Conn.lookupByName(name))
00263         for id in self.Conn.listDomainsID():
00264             self.VMs.append(self.Conn.lookupByID(id))
00265 
00266     ## Retourne l'utilisation du CPU en pourcents pour la VM passée en paramètre.
00267     #  Cette méthode calcule la moyenne du pourcentage d'utilisation du CPU d'une machine virtuelle depuis son dernier appel.
00268     #  @return l'utilisation du CPU en pourcents.
00269     def utilisationCPU(self, VM):
00270         if VM.name() not in self.Mesuretime:
00271             self.Mesuretime[VM.name()] = 1
00272             self.CPUTime[VM.name()] = 1
00273         LastMesuretime = self.Mesuretime[VM.name()]
00274         LastCPUTime = self.CPUTime[VM.name()]
00275         self.Mesuretime[VM.name()] = time.time()
00276         self.CPUTime[VM.name()] = VM.info()[4]
00277         deltaCPU = float(self.CPUTime[VM.name()]-LastCPUTime)
00278         deltaT = float(self.Mesuretime[VM.name()]-LastMesuretime)
00279         nbvCPU = float(VM.info()[3])
00280         return deltaCPU/(1000000000.0*deltaT*float(self.nbpCPU))/nbvCPU*100.0
00281 
00282     ## Cette méthode a la même fonction que la méthode utilisationCPU() mais utilise un algorithme différent.
00283     #  Cette méthode calcule le pourcentage d'utilisation du CPU de la VM en prenant en compte le temps qu'elle avait à disposition.
00284     #  @n En résumé elle soustrait le temps utilisé par l'hôte et les autres VMs.
00285     #  @return l'utilisation du CPU en pourcents.
00286     def utilisationCPUv2(self, VM): # Expérimental
00287         if VM.name() not in self.Mesuretime:
00288             self.Mesuretime[VM.name()] = 1
00289             self.CPUTime[VM.name()] = 1
00290         LastMesuretime = self.Mesuretime[VM.name()]
00291         LastCPUTime = self.CPUTime[VM.name()]
00292         self.Mesuretime[VM.name()] = time.time()
00293         self.CPUTime[VM.name()] = VM.info()[4]
00294         deltaCPU = float(self.CPUTime[VM.name()]-LastCPUTime)
00295         deltaT = float(self.Mesuretime[VM.name()]-LastMesuretime)
00296         nbvCPU = float(VM.info()[3])
00297 
00298         if self.pCPUInfos > 90:
00299             if VM.name() not in self.pCPUIdleTime:
00300                 self.pCPUIdleTime[VM.name()] = 1
00301             LastpCPUIdleTime = self.pCPUIdleTime[VM.name()]
00302             self.pCPUIdleTime[VM.name()] = self.hostinf.CPUIdleTime()
00303             coeff = float(os.sysconf_names['SC_CLK_TCK'])
00304             deltapCPUIdleTime = (self.pCPUIdleTime[VM.name()] - LastpCPUIdleTime) / (100*coeff)
00305             deltavCPU = float(float(deltaCPU) / (1000000000.0*float(self.nbpCPU)))
00306             if (deltaCPU + deltapCPUIdleTime) > 0:
00307                 return deltavCPU / float(deltapCPUIdleTime + deltavCPU) *100.0
00308         return deltaCPU/(1000000000.0*deltaT*float(self.nbpCPU))/nbvCPU*100.0
00309 
00310     ## Retourne les informations sur les machines virtuelles sous la forme d'une liste contenant des dict.
00311     #  Cett méthode appelle, pour chaque VM, les autres méthodes de la classe pour récupérer les informations.
00312     #  @n Elle crée ensuite le dict et y stocke les informations de la VM.
00313     #  @n Elle ajoute ensuite ce dict à la liste.
00314     #  @return les informations sur l'hôte sous la forme d'un dict.
00315     def infos(self):
00316         self.listeVM()
00317         res = list()
00318         self.nbpCPU = self.Conn.getInfo()[2]
00319         self.pCPUInfos = self.hostinf.CPUInfos()
00320         for VM in self.VMs:
00321             inf = dict()
00322             inf["name"] = VM.name()
00323             inf["uuid"] = VM.UUIDString()
00324             inf["state"] = VM.info()[0]
00325             inf["cpu"] = self.utilisationCPU(VM)
00326             inf["ram"] = int(VM.info()[2])
00327             inf["maxram"] = int(VM.info()[1])
00328             #inf["network"] =
00329             res.append(inf)
00330         return res
00331 
00332 
00333 ## Point d'entrée du logiciel
00334 #  Cette fonction crée les instances des objets et appelle les méthodes infos().
00335 #  @n Elle crée ensuite la chaîne JSON et l'envoie aux collecteurs.
00336 #  @n Puis elle attend un certain temps avant de recommencer.
00337 #  @return 
00338 if __name__ == "__main__":
00339     print "debut"
00340 
00341     hosti = HostInfos()
00342     vmi = VMInfos()
00343     comm = UDPScoketCommunication()
00344 
00345     while(1):
00346         time.sleep(1)
00347         infos = hosti.infos()
00348         infos['vms'] = vmi.infos()
00349         jsonData = json.dumps(infos)
00350         comm.envoyer(jsonData)
00351         #print jsonData
00352 
00353     #sys.exit(0)
00354 
00355 print "fin"
 Tout Classes Espaces de nommage Fichiers Fonctions Variables