Superviseur - Daemon 1.0
Ce logiciel correspond au daemon de la suite des trois logiciels composant le superviseur
|
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"