#!/usr/bin/env python
# -*- coding: UTF-8 -*-
###########################################################################
# Eole NG - 2016
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
###########################################################################
import getpass, sys, codecs, time
try:
    # si disponible, on utilise la librairie modifiée du client Zéphir
    # (n'affiche pas les mots de passe dans l'url en cas de traceback)
    from zephir.eolerpclib import xmlrpclib
except:
    import xmlrpclib

HELP = """

usage: zephir_gen_csv.py

Ce script permet de récupérer des informations sur les serveurs

Le résultat est stocké dans un fichier de type CSV nommé
zephir_infos.csv contenant les informations suivantes:

id_serveur|libelle_module|libellé_serveur|rne|perte_contact|liste agents en erreur

"""
##################################
# délimiteur pour le fichier 'CSV'
##################################
delimiter = u";"

def selection_serveurs(zephir_proxy):

    # exemple : utiliser un groupe existant
    rc, liste_groupes = zephir_proxy.serveurs.get_groups()
    print "* groupes disponibles *"
    # affichage id et libelle des groupes
    groupes = {}
    for groupe in liste_groupes:
        print groupe[0], " - ", groupe[1]
        # dictionnaire id_groupe -> libelle, liste des serveurs
        groupes[groupe[0]] = (groupe[1], groupe[2])
    print ""
    id_groupe = raw_input('choix du groupe (rien pour choisir un module) : ')
    if id_groupe != '':
        try:
            id_groupe = int(id_groupe)
            assert id_groupe in groupes.keys()
        except:
            sys.exit('Erreur, groupe inconnu')
        # récupération des serveurs de ce groupe
        rc, groupe_serv = zephir_proxy.serveurs.groupe_reload(groupes[id_groupe][1])
        print "\nServeurs du groupe %s : " % groupes[id_groupe][0]
        return groupe_serv
    # récupération par type de module si pas de groupe demandé
    # si besoin, on peut récupérer la liste des variantes avec zephir_proxy.modules.get_variantes()
    rc, liste_modules = zephir_proxy.modules.get_module()
    print "* modules disponibles *"
    # affichage id et libelle des groupes
    modules = {}
    for module in liste_modules:
        print module['id'], " - ", module['libelle']
        # dictionnaire id_module -> libelle
        modules[module['id']] = module['libelle']
    print ""
    id_module = raw_input('choix du module : ')
    try:
        id_module = int(id_module)
        assert id_module in modules.keys()
    except:
        sys.exit('Erreur, module invalide')
    # critères de séléction des serveurs (d'autres critères sont possibles : rne, variante, libelle ...)
    criteres_selection = {'module_actuel':id_module}
    # récupération du groupe de serveurs correspondants
    rc, groupe_serv = zephir_proxy.serveurs.groupe_serveur(criteres_selection)
    print "\nServeurs de type %s : " % modules[id_module]
    return groupe_serv

def get_infos(zephir_proxy, groupe_serv, alert_only=False):
    """récupère les informations pour les serveurs demandés.
    """
    if alert_only:
        # si alert_only == True: on ne liste que les serveurs considérés en alerte
        print "\nRécupération des serveurs en alerte ..."
        ret_code, serveurs_alerte = zephir_proxy.serveurs.get_alertes()
        # constitution d'une liste d'identifiant des serveurs en alerte. l'id est dans l'avant dernier champ
        serveurs_alerte = [serv[-2] for serv in serveurs_alerte]
    else:
        serveurs_alerte = []

    # Récupération des informations sur les modules
    module_infos = {}
    ret_code, modules = zephir_proxy.modules.get_module()
    for mod in modules:
        module_infos[mod['id']] = mod['libelle']

    # création d'un dictionnaire {id_serveur:infos_serveur}
    csv_data = []
    infos_serveur = {}
    for serv in groupe_serv:
        if  alert_only == False or serv['id'] in serveurs_alerte:
            # informations de base des serveurs (rne, module, libelle, ...)
            # {'etat': -1, 'remarques': '', 'md5s': -1, 'tel': '', 'timeout': 0, 'module_initial': 69, 'materiel': '', 'variante': 69, 'installateur': '', 'module_actuel': 69, 'rne': '0000000A', 'date_install': '2016-02-13', 'no_alert': 0, 'params': "{'cle_ok': 0, 'maj_ok': [-2, ''], 'perso_ok': [-2, ''], 'config_ok': 1, 'new_key': (0, ''), 'dictpaqs_ok': [-2, ''], 'service_restart_ok': [-2, ''], 'dico_ok': 0, 'configure_ok': [-2, ''], 'reconfigure_ok': [-2, ''], 'uucp_cmd': 0, 'upgrade_ok': [-2, ''], 'migration_ok': -1, 'uucp_transfert': 0, 'lock_ok': [1, ''], 'md5s': [-1, ''], 'sauvegarde_ok': [-2, ''], 'query_maj': [-2, ''], 'reboot_ok': [-2, ''], 'timeout': [1, ''], 'agents': 1, 'last_log': 'journal vide'}", 'disque_dur': '', 'libelle': 'aca.eolebase-basique-2.4.2', 'timestamp': 1455632654.43577, 'ip_publique': '', 'maj': -1, 'id': 24, 'processeur': ''}
            infos_serveur[serv['id']] = serv

    ####################################################################################################
    # récupération des informations d'état (état des actions zéphir, données des agents de surveillance)
    #
    # Si ces appels surchargent Zéphir, utiliser une boucle sur la liste des identifiants et
    # faire l'appel à get_status/agents_status pour chaque serveur. Il est possible de temporiser
    # les appels en ajoutant un délai à chaque itération (time.sleep(2) pour 2 secondes d'attente)
    #
    # commenter les appels non utiles
    # (dans la version fournie, agents_status et measure_infos en sont pas utilisés)
    ####################################################################################################
    serv_ids = infos_serveur.keys()
    print "Récupération des informations d'état (actions, perte de contact, etc.) ..."
    ret_code, status_infos = zephir_proxy.serveurs.get_status(serv_ids)
    # >>> zephir_proxy.serveurs.get_status(24)
    # [1, {'cle_ok': 1, 'maj_ok': [1, '2016-02-09 16:43:48', u'Mise \xe0 jour OK'], 'agents': 1, 'config_ok': 0, 'new_key': [0, ''], 'dictpaqs_ok': [1, ''], 'service_restart_ok': [-2, ''], 'dico_ok': 0, 'reboot_ok': [-2, ''], 'reconfigure_ok': [-2, ''], 'uucp_cmd': 0, 'upgrade_ok': [-2, ''], 'migration_ok': -1, 'uucp_transfert': 0, 'lock_ok': [1, ''], 'md5s': [0, u'contenu modifi\xe9 : zephir.eol'], 'sauvegarde_ok': [-2, ''], 'query_maj': [0, 'Tue Feb  9 18:03:21 2016'], 'configure_ok': [1, 'Tue Feb  9 17:03:21 2016', 'Configuration OK'], 'timeout': [1, ''], 'perso_ok': [-2, ''], 'last_log': 'Tue Feb  9 17:03:22 2016'}]
    print "Récupération de l'état des agents de surveillance ..."
    ret_code, agents_infos = zephir_proxy.serveurs.agents_status(serv_ids)
    # >>> zephir_proxy.serveurs.agents_status(24)
    # [1, {'web': ['Services distants', 1, '2016-02-09 16:58:13'], 'network': [u'Etat des interfaces r\xe9seau', 1, '2016-02-09 16:59:13'], 'ead_server': ['Etat du service EAD (Serveur)', 1, '2016-02-09 16:58:33'], 'diag': ['Diagnostic', 1, '2016-02-09 16:43:13'], 'diskspace': ['Occupation des disques', 1, '2016-02-09 16:58:13'], 'bilan': [u'Resum\xe9 mensuel', 1, '2016-02-09 16:59:13'], 'tcpservices': ['Etat des services', 1, '2016-02-09 16:59:13'], 'ead_web': ['Etat du service EAD Web', 1, '2016-02-09 16:58:33'], 'debsums': [u'\xc9tat des sommes MD5 de paquets', 1, '2016-02-09 16:58:13'], 'sentinelle': [u'Donn\xe9es suppl\xe9mentaires pour Sentinelle', 1, '2016-02-09 16:58:13'], 'sysinfo': [u'Informations syst\xe8me', 1, '2016-02-09 16:59:13'], 'netstats': [u'Statistiques r\xe9seau', 1, '2016-02-09 16:59:13'], 'RootDebsums': [u'\xc9tat des sommes MD5 de paquets pour root', 1, '2016-02-09 16:43:13'], 'ssh': ['Etat du service SSH', 1, '2016-02-09 16:58:33']}]
    print "Récupération des données de surveillance ..."
    ret_code, str_agents_measures = zephir_proxy.serveurs.get_measure(serv_ids)
    # Mesures remontées par les agents. Seuls certains agents implémentent cette fonction (fonction développée pour Sentinelle)
    # dictionnaire préfixé par identifiant de serveur et 'sérialisé' sous forme d'une chaîne
    agents_measures = eval(str_agents_measures)
    # exemple sur 2 serveurs :
    # eval(zephir_proxy.serveurs.get_measure([770,379])[1])
    # { 770: {'web': {'R\xc3\xa9solution': 'On', 'Proxy (admin.ldij-carnot.loc)': 'On', 'Acc\xc3\xa8s Distant': 'On', 'DNS distant : 10.21.10.1': 'On'}, 'netbios': {'status': 'On'}, 'http': {'status': 'On'}, 'ead_server': {'status': 'On'}, 'diskspace': {'/dev': (1, 491, 0, 491, 'devtmpfs'), '/boot': (6, 703, 36, 631, 'ext4'), '/var': (100, 930172, 882923, 0, 'ext4'), '/tmp': (2, 1876, 35, 1745, 'ext4'), '/': (26, 3284, 786, 2331, 'ext4')}, 'diag': {}, 'bilan': {}, 'tcpservices': {'http': ['On', 'Serveur web'], 'ead_server': ['On', 'EAD 2 (Serveur)'], 'ead_web': ['On', 'EAD 2 (Web)'], 'ssh': ['On', 'shell s\xc3\xa9curis\xc3\xa9 (SSH)'], 'smb': ['On', 'Serveur de fichiers (SMB)'], 'netbios': ['On', 'Service netbios']}, 'netstats': {'out_lo': 2683668, 'out_eth0': 2390386, 'iner_eth0': 0, 'iner_lo': 0, 'in_eth0': 59481, 'outer_eth0': 0, 'in_lo': 2683668, 'outer_lo': 0}, 'ead_web': {'status': 'On'}, 'RootDebsums': {}, 'debsums': {}, 'sentinelle': {'maj_auto': ''}, 'sysinfo': {'kernel': '2.6.32-73-eole', 'swap_used': '12 Mo', 'load15': '0.00', 'swap_free': '2893 Mo', 'uptime': 14894426, 'swap_total': '2905 Mo', 'physique_used': '859 Mo', 'physique': '86 %', 'physique_total': '992 Mo', 'physique_free': '133 Mo', 'swap': '0', 'load1': '0.00', 'load5': '0.00'}, 'smb': {'status': 'On'}, 'ssh': {'status': 'On'}, 'network': {'eth0': 'On'}},
    #  379: {'bacula-fd': {}, 'diskspace': {'/mnt/sauvegardes': (45, 924314, 394200, 483161, 'cifs'), '/tmp': (2, 1874, 35, 1743, 'ext4'), '/var': (16, 8445, 1271, 6744, 'ext4'), '/home': (67, 185297, 117581, 58303, 'ext4'), '/dev': (1, 1504, 0, 1504, 'devtmpfs'), '/boot': (6, 703, 34, 633, 'ext4'), '/': (36, 3283, 1092, 2023, 'ext4')}, 'mysql': {'status': 'On'}, 'sauvegarde': {}, 'RootDebsums': {}, 'ftp': {'status': 'On'}, 'bacula-sd': {}, 'ead_server': {'status': 'On'}, 'baculaservices': {'bacula-fd': ['On', 'bacula-fd'], 'bacula-dir': ['On', 'bacula-dir'], 'bacula-sd': ['On', 'bacula-sd']}, 'diag': {}, 'frontend_horus': {}, 'tcpservices': {'ftp': ['On', 'Transfert de fichiers (FTP)'], 'ead_web': ['Off', 'EAD 2 (Web)'], 'http': ['On', 'Serveur Web (HTTP)'], 'ssh': ['On', 'shell s\xc3\xa9curis\xc3\xa9 (SSH)'], 'ead_server': ['On', 'EAD 2 (Serveur)'], 'mysql': ['On', 'Bases de donn\xc3\xa9es (MySQL)'], 'ldap': ['On', "Annuaire d'authentification (LDAP)"], 'smb': ['On', 'Serveur de fichiers (SMB)']}, 'annuaire': {}, 'conn': {'connected': 0}, 'ldap': {'status': 'On'}, 'smb': {'status': 'On'}, 'http': {'status': 'On'}, 'web': {'DNS distant : 10.121.16.5': 'On', 'R\xc3\xa9solution': 'On', 'Acc\xc3\xa8s Distant': 'On'}, 'bilan': {}, 'bacula-dir': {}, 'ssh': {'status': 'On'}, 'netstats': {'out_lo': 305828178, 'out_eth0': 1302616276, 'iner_eth0': 0, 'iner_lo': 0, 'in_eth0': 40663804, 'outer_eth0': 0, 'in_lo': 305828178, 'outer_lo': 0}, 'sysinfo': {'kernel': '2.6.32-73-eole', 'swap_used': '3 Mo', 'load15': '0.00', 'swap_free': '1948 Mo', 'uptime': 4921380, 'swap_total': '1951 Mo', 'physique_used': '2955 Mo', 'physique': '97 %', 'physique_total': '3019 Mo', 'physique_free': '64 Mo', 'swap': '0', 'load1': '0.01', 'load5': '0.00'}, 'patches': {'/usr/share/eole/creole//patch/variante/sudoers.patch': {'/var/lib/creole/sudoers': []}, '/usr/share/eole/creole//patch/variante/vimrc.patch': {'/var/lib/creole/vimrc': []}}, 'sso': {'Application web EoleSSO': 'On'}, 'network': {'eth0': 'On'}, 'ead_web': {'status': 'Off'}, 'debsums': {}, 'sentinelle': {'maj_auto': ''}}}


    ###################################################################################
    # mise en forme des données pour chaque serveur, à personnaliser selon les besoins
    ###################################################################################

    for index, id_serveur in enumerate(serv_ids):
        # index permet de récupérer la bonne entrée dans les données récupérées ci dessus
        rne = infos_serveur[id_serveur]['rne']
        libelle_serveur = infos_serveur[id_serveur]['libelle']
        module = module_infos[infos_serveur[id_serveur]['module_actuel']]
        # vérification des pertes de contact
        global_status = infos_serveur[id_serveur]['etat']
        if global_status == 2:
            perte_contact = 1
        else:
            perte_contact = 0
        # liste des agents en erreur
        ag_errors = []
        for nom_agent, agent_data in agents_infos[index].items():
            descr, etat, date_measure = agent_data
            if etat == 0:
                ag_errors.append("%s (%s)" % (nom_agent, descr))
        # ajout des données de ce serveur
        csv_data.append(delimiter.join([str(id_serveur), module, libelle_serveur, rne, str(perte_contact), ", ".join(ag_errors)]))
    try:
        # écriture du fichier résultat
        csv_name = 'zephir_infos.csv'
        f = codecs.open(csv_name, 'w', encoding='utf-8')
        f.write(u"\n".join(csv_data))
        f.close()
        print "Fichier sauvegardé (%s)\n" % csv_name
    except:
        import traceback
        traceback.print_exc()
        print "! Erreur à l'écriture du fichier %s !\n" % csv_name


if __name__ == '__main__':

    # affichage aide
    if '-h' in sys.argv or '--help' in sys.argv:
        print HELP
        sys.exit(0)

    zephir_infos = {}
    # informations sur le serveur à contacter et le compte à utiliser pour la lecture
    # ex : zephir_infos = {'adresse_zephir':'localhost','user':'admin_zephir', 'pwd':'xxx'}
    if zephir_infos == {}:
        # saisie manuelle des infos zéphir/utilisateur pour débug du script
        zephir_infos['adresse_zephir'] = raw_input("Entrez l'adresse du serveur Zéphir :")
        # proxy xmlrpc
        zephir_infos['user'] = raw_input("nom de l'utilisateur zephir :")
        zephir_infos['pwd'] = getpass.getpass("mot de passe                :")

    # définition du proxy XMLRPC ver le serveur Zéphir
    if zephir_infos['adresse_zephir'] in ('localhost', '127.0.0.1'):
        zephir_proxy = xmlrpclib.ServerProxy('http://%(user)s:%(pwd)s@localhost:7081' % zephir_infos)
    else:
        zephir_proxy = xmlrpclib.ServerProxy('https://%(user)s:%(pwd)s@%(adresse_zephir)s:7080' % zephir_infos)

    try:
        ####################################################################################
        # pour récupérer l'ensemble des serveurs, remplacer l'appel à selection_serveur par:
        # rc, serveurs = zephir_proxy.serveurs.get_serveur()
        ####################################################################################
        groupe_serv = selection_serveurs(zephir_proxy)
        ####################################################################
        # alert_only : traiter uniquement les serveurs de la page d'alertes
        ####################################################################
        get_infos(zephir_proxy, groupe_serv, alert_only = True)
    except xmlrpclib.ProtocolError:
        print "Erreur, autorisations insuffisantes"
        sys.exit(1)
