#!/usr/bin/env python # -*- coding: utf-8 -*- # statistics-summary.py # --------------------- # Copyright (C) 2008-2009 Michel Blockelet # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This file 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 # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. """Outil pour créer un résumé des statistiques de bcfg2.""" # Importations import os, sys, time import xml.dom.minidom ## Configuration # Fichier de statistiques statsfile = '/var/lib/bcfg2/etc/statistics.xml' # Nombre de jours à partir desquels l'hôte est considéré comme vieux daysold = 2 def processHost(node, ignore): """Traite les statistiques d'un hôte à partir de son noeud XML.""" # On prend les statistiques les plus récentes (le fichier de statistiques # peut contenir plusieurs runs) stamps = node.getElementsByTagName('OpStamps') curtime = 0 for stamp in stamps: if float(stamp.getAttribute('start')) > curtime: curtime = float(stamp.getAttribute('start')) stats = stamp.parentNode # On regarde l'état if stats.getAttribute('state') == 'clean': state = 'clean' lines = [u' * %s - %s - clean' % (node.getAttribute('name'), stats.getAttribute('time'))] nbbad = 0 ignored = 0 else: # On récupère les différents points pas à jour bad = stats.getElementsByTagName('Bad')[0] nbbad = (int(stats.getAttribute('total')) - int(stats.getAttribute('good'))) cfgfiles = bad.getElementsByTagName('ConfigFile') packages = bad.getElementsByTagName('Package') services = bad.getElementsByTagName('Service') actions = bad.getElementsByTagName('Action') # On ignore ce qu'il faut ignored = 0 ignorestr = u'' if 'config' in ignore and len(cfgfiles) > 0: ignored += len(cfgfiles) ignorestr += u' %d configfiles' % len(cfgfiles) if 'packages' in ignore and len(packages) > 0: ignored += len(packages) ignorestr += u' %d packages' % len(packages) if 'actions' in ignore and len(actions) > 0: ignored += len(actions) ignorestr += u' %d actions' % len(actions) if 'services' in ignore and len(services) > 0: ignored += len(services) ignorestr += u' %d services' % len(services) if ignored > 0: ignorestr = u' - %d ignored :%s' % (ignored, ignorestr) # On affiche tous les trucs pas bons if ignored == nbbad: # On a tout ignoré state = 'ignored' lines = [u' * %s - %s%s' % (node.getAttribute('name'), stats.getAttribute('time'), ignorestr)] else: # On a des trucs à afficher state = 'bad' lines = [] lines.append(u' * %s - %s' % (node.getAttribute('name'), stats.getAttribute('time'))) lines.append(u' State : %s - %d bad%s' % (stats.getAttribute('state'), nbbad, ignorestr)) # Fichiers de configuration if len(cfgfiles) > 0 and 'config' not in ignore: lines.append(u' * Config files :') for cfgfile in cfgfiles: if cfgfile.getAttribute('current_exists') == 'false': lines.append(u' + %s' % cfgfile.getAttribute('name')) else: lines.append(u' * %s' % cfgfile.getAttribute('name')) # Paquets if len(packages) > 0 and 'packages' not in ignore: lines.append(u' * Packages :') for package in packages: if package.getAttribute('current_version') == '': lines.append(u' + %s :: %s' % (package.getAttribute('name'), package.getAttribute('version'))) else: lines.append(u' * %s :: %s -> %s' % (package.getAttribute('name'), package.getAttribute('current_version'), package.getAttribute('version'))) # Actions if len(actions) > 0 and 'actions' not in ignore: lines.append(u' * Actions :') for action in actions: lines.append(u' * %s' % action.getAttribute('name')) # Services if len(services) > 0 and 'services' not in ignore: lines.append(u' * Services :') for service in services: lines.append(u' * %s' % service.getAttribute('name')) lines.append('') return (time.time() - curtime > daysold * 24 * 60 * 60, state, lines, nbbad - ignored) def deleteHosts(hostslist, writeback): """Supprime les hôtes de la liste hostslist du fichier de statistiques. Si hostslist évalue à False, demande une confirmation puis supprime tous les hôtes vieux.""" doc = xml.dom.minidom.parse(statsfile) hoststodel = [] for node in doc.getElementsByTagName('Node'): if not hostslist: # On sélectionne les hôtes vieux stamps = node.getElementsByTagName('OpStamps') curtime = 0 for stamp in stamps: if float(stamp.getAttribute('start')) > curtime: curtime = float(stamp.getAttribute('start')) if time.time() - curtime > daysold * 24 * 60 * 60: hoststodel.append(node) else: # On sélectionne les hôtes donnés en argument if node.getAttribute('name') in hostslist: hoststodel.append(node) # Suppression des hôtes dans le schéma XML for node in hoststodel: print u'Suppression de l\'hôte : %s' % node.getAttribute('name') node.parentNode.removeChild(node) if hoststodel: if writeback: # Enregistrement des données dans le fichier print u'Arrêt de bcfg2-server ...' if os.system('/etc/init.d/bcfg2-server stop'): print u'Erreur !' print u'Sauvegarde ...' doc.writexml(open(statsfile, 'w')) print u'Redémarrage de bcfg2-server ...' if os.system('/etc/init.d/bcfg2-server start'): print u'Erreur !' else: print u'Utilisez l\'option -w en tant que root pour enregistrer.' else: print u'Aucun hôte à supprimer !' def genStats(ignore): """Génère les statistiques, en ignorant les éléments de la liste ignore.""" # On traite le fichier de statistiques doc = xml.dom.minidom.parse(statsfile) oldhosts = [] dirtyhosts = [] cleanhosts = [] oldnb = 0 badnb = 0 cleannb = 0 # On récupère les informations des différents hôtes for curnode in doc.getElementsByTagName('Node'): (isold, state, hostlines, urgency) = processHost(curnode, ignore) if isold: oldnb += 1 if state in ['clean', 'ignored']: oldhosts.append((0, hostlines)) else: oldhosts.append((urgency, hostlines)) else: if state in ['clean', 'ignored']: cleanhosts.append((0, hostlines)) cleannb += 1 else: dirtyhosts.append((urgency, hostlines)) badnb += 1 # Get the dirtier hosts first. dirtyhosts.sort() dirtyhosts.reverse() oldhosts.sort() oldhosts.reverse() # Header print (u'*** Bcfg2 Statistics on %s, for %d hosts ***' % (time.strftime("%c"), oldnb + badnb + cleannb)) print u' * Old hosts : %d' % oldnb print u' * Bad hosts : %d' % badnb print u' * Clean hosts : %d' % cleannb print # Hôtes vieux if oldnb: print u'*** Old hosts (more than %s days) :' % daysold for host in oldhosts: for line in host[1]: print line print # Hôtes sales if badnb: print u'*** Bad hosts :' for host in dirtyhosts: for line in host[1]: print line print # Hôtes propres if cleannb: print u'*** Clean hosts (or with ignored dirtyness) :' for host in cleanhosts: for line in host[1]: print line if __name__ == '__main__': if '-h' in sys.argv: # Message d'utilisation print u'Outil pour créer un résumé des statistiques de bcfg2.' print u'' print u' * Usage :' print u' * Génération de statistiques :' print u' -c ou --no-config pour ignorer les ConfigFiles' print u' -p ou --no-packages pour ignorer les Packages' print u' -a ou --no-actions pour ignorer les Actions' print u' -s ou --no-services pour ignorer les Services' print u'' print u' * Suppression d\'hôtes des statistiques :' print u' -d ou --delete seuls pour supprimer tous les hôtes vieux' print u' -d ou --delete [hôte]... pour supprimer certains hôtes' print u' -w ou --write pour enregistrer les modifications' print u' (sinon, ne fait qu\'afficher les hôtes concernés)' else: # Lecture des options ignore = [] if '-c' in sys.argv or '--no-config' in sys.argv: ignore.append('config') if '-p' in sys.argv or '--no-packages' in sys.argv: ignore.append('packages') if '-a' in sys.argv or '--no-actions' in sys.argv: ignore.append('actions') if '-s' in sys.argv or '--no-services' in sys.argv: ignore.append('services') if os.access(statsfile, os.F_OK): if '-d' in sys.argv or '--delete' in sys.argv: # Mode suppression deleteHosts([x for x in sys.argv[1:] if x not in ['-d', '--delete', '-w']], os.getuid() == 0 and ('-w' in sys.argv or '--write' in sys.argv)) else: # Mode génération des statistiques genStats(ignore) else: print u'Impossible d\'accéder au fichier de statistiques :' print u' %s !' % statsfile