# -*- coding: UTF-8 -*-
###########################################################################
# Eole NG - 2007
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
# basé sur une contribution de Nicolas Prunier (nicolas.prunier@ac-rouen.fr)
#
# sentinelle_rpc.py
#
# fonctions de traitement des serveurs pour Sentinelle
#
###########################################################################
"""module pour ajout de fonctionnalités (contributions)"""

# import principaux du backend zephir
from zephir.backend.config import u, log, CREOLE_VERSIONS, charset
from zephir.backend import config
from zephir.backend.xmlrpceole import XMLRPCEole as XMLRPC
# imports TwistedMatrix
from twisted.internet import defer, threads
# divers imports utiles
import os, re
import json
import psycopg2 as PgSQL
from glob import glob
from time import time, mktime, strptime


import traceback

class RPCSentinelle(XMLRPC):
    """serveur XMLRPC gérant les données pour Sentinelle
    """
    def __init__(self, parent, agents_manager):
        XMLRPC.__init__(self)
        self.parent = parent
        self.agents_manager = agents_manager
        self.CONF_RAC='/var/lib/zephir/conf/'
        self.dico = {}

    def send_result(self, result):
        log.msg("-- Sentinelle : envoi des données")
        return 1, u(str(self.dico))

    def recup_failed(self, failure):
        log.msg('!! erreur rencontrée lors de la récupération des données pour sentinelle')
        log.msg(failure.getTraceback())
        return 0, u("-- Sentinelle : erreur de récupération des données")

    def xmlrpc_get_all_conf(self, cred_user):
        self.func_list = [self.getAllConf, self.getAllServer]
        return self.startRecup().addCallbacks(self.send_result, self.recup_failed)

    def xmlrpc_get_all(self, cred_user):
        self.func_list = [self.getAllConf, self.getAllEtats, self.getAllStatus, self.getAllServer, self.getAllConfigZephir, self.getAllMeasures, self.getAllCheckMaj, self.getAllEtab]
        return self.startRecup().addCallbacks(self.send_result, self.recup_failed)

    def xmlrpc_get_all_for_sentinelle(self, cred_user):
        self.func_list = [self.getAllConf, self.getAllEtats, self.getAllStatus, self.getAllServer, self.getAllConfigZephir, self.getAllMeasures, self.getAllCheckMaj]
        return self.startRecup().addCallbacks(self.send_result, self.recup_failed)

    def xmlrpc_get_serveur_etab(self, cred_user):
        """renvoie la liste des serveurs par établissement avec id_serveur / id_module
        """
        serveurs_etabs = {}
        for id_serv, serv in self.parent.s_pool.items():
            serveurs = serveurs_etabs.get(serv.rne, [])
            serveurs.append((id_serv, serv.id_mod))
            serveurs_etabs[serv.rne] = serveurs
        return (1, u(serveurs_etabs))

    def startRecup(self):
        # on parcourt tous les serveurs
        log.msg("-- Sentinelle : début du traitement")
        for id_serv, serv in self.parent.s_pool.items():
            if serv.module.split('-')[0] in ['amon', 'horus', 'scribe', 'amonecole', 'hapy']:
                self.dico[str(id_serv)] = {}
        # initialisation de l'appel aux sous fonctions
        def_list = []
        for func in self.func_list:
            deferred = defer.maybeDeferred(func)
            def_list.append(deferred)
        return defer.DeferredList(def_list)

    def parseDict(self, txtDict, eole_version):
        tmpDict = {}
        creole_version = CREOLE_VERSIONS.get(eole_version, 'creole3')
        if creole_version == 'creole1':
            lines = txtDict.split('\n')
            for elem in lines:
                if elem.count('@@'):
                    tmpData = elem.split('#')[0].split('@@')
                    tmpDict[tmpData[0]] = tmpData[1]
        elif creole_version == 'creole2':
            lines = txtDict.split('\n')
            for i in range(len(lines)/5):
                var_name = lines[i*5][1:-1]
                var_value = lines[i*5+3][7:-1]
                tmpDict[var_name] = var_value.strip("'").strip('"')
        else:
            config = json.load(txtDict.strip())
            for var_name, data in config.items():
                if var_name != u'___version___':
                    if type(data['val']) == list:
                        tmpDict[var_name.encode(charset)] = ", ".join([str(val).replace('None','') for val in data['val']])
                    else:
                        tmpDict[var_name.encode(charset)] = str(data['val']).replace('None','')
        return tmpDict

    def getAllConf(self):
        for id_serv, serv in self.parent.s_pool.items():
            id_serv = str(id_serv)
            if id_serv in self.dico:
                confPath = os.path.join(serv.confdir, 'zephir.eol')
                if os.path.exists(confPath):
                    try:
                        tmpFile = open(confPath)
                        tmpData = tmpFile.read()
                        tmpFile.close()
                        self.dico[id_serv]['conf'] = self.parseDict(tmpData, serv.module_version)
                    except:
                        log.msg("** Sentinelle : erreur lors du traitement de %s" % confPath)
                if 'conf' not in self.dico[id_serv]:
                    # si pas de configuration, on ne traite pas le serveur
                    self.dico.pop(id_serv)
        return defer.succeed(True)

    def parseEtats(self, etatsDict):
        for k, v in etatsDict.items():
            if type(v) == list:
                etatsDict[k] = v[0]
        return etatsDict

    def getAllEtats(self):
        for id_serv, serv in self.parent.s_pool.items():
            if str(id_serv) in self.dico:
                self.dico[str(id_serv)]['etats'] = self.parseEtats(serv.get_params())
        return defer.succeed(True)

    def parseStatus(self, id_serv, statusRegX):
        tmpDict = {}
        for ficName in glob('/var/lib/zephir/data/%d/*/*.xml' % id_serv):
            tmpFic = open(ficName)
            ficContent = tmpFic.read().replace('\n','')
            tmpFic.close()
            tmpSearch = statusRegX.search(ficContent)
            if tmpSearch and tmpSearch.group(1) != 'Error':
                tmpDict[ficName.split('/')[6]] = 1
            else:
                tmpDict[ficName.split('/')[6]] = 0
        return tmpDict

    def getAllStatus(self):
        statusRegX = re.compile("""(?:last_status=(?:Ref\(1, *)?Instance\('zephir\.monitor\.agentmanager\.status\.([^']*)|value="last_status"></string><instance class="zephir\.monitor\.agentmanager\.status\.([^"]*))""")
        for id_serv, serv in self.parent.s_pool.items():
            if str(id_serv) in self.dico:
                self.dico[str(id_serv)]['status'] = self.parseStatus(id_serv, statusRegX)
        return defer.succeed(True)

    def zephtime2seconds(self, sTime):
        if (sTime.find('-')!=-1):
            sFormat='%Y-%m-%d %H:%M:%S'
        else:
            sFormat='%a %b %d %H:%M:%S %Y'
        try:
            i=int(mktime(strptime(sTime,sFormat)))
        except:
            i=0
        return i

    def getAllServer(self):
        for id_serv, serv in self.parent.s_pool.items():
            if str(id_serv) in self.dico:
                self.dico[str(id_serv)]['server'] = {'lastlog': self.zephtime2seconds(serv.get_params()['last_log']), 'modulename': serv.module}
        return defer.succeed(True)

    def getAllConfigZephir(self):
        cx = PgSQL.connect(database=config.DB_NAME,user=config.DB_USER,password=config.DB_PASSWD)
        cursor=cx.cursor()
        cursor.execute("select * from serveurs")
        data = cursor.fetchall()
        cursor.close()
        cx.close()
        matchDict = {'id':0,
                'rne':1,
                'libelle':2,
                'materiel':3,
                'processeur':4,
                'disque_dur':5,
                'date_install':6,
                'installateur':7,
                'tel':8,
                'remarques':9,
                'module_initial':10,
                'module_actuel':11,
                'variante':12,
                'timeout':14,
                'etat':15,
                'timestamp':16,
                #'params':17,
                'maj':18,
                'md5s':19,
                #'libelle':20
                }
        for elem in data:
            if not str(elem[0]) in self.dico:
                continue
            if not 'config_zephir' in self.dico[str(elem[0])]:
                self.dico[str(elem[0])]['config_zephir'] = {}
            for k, v in matchDict.items():
                    self.dico[str(elem[0])]['config_zephir'][k] = elem[v]
        return defer.succeed(True)

    def parseMeasures(self, id_serv, measuresRegX):
        tmpDict = {}
        for ficName in glob('/var/lib/zephir/data/%d/*/*.xml' % id_serv):
            tmpFic = open(ficName)
            ficContent = tmpFic.read().replace('\n','')
            tmpFic.close()
            tmpSearch = measuresRegX.search(ficContent)
            if tmpSearch:
                try:
                    tmpDict[ficName.split('/')[6]] = eval(re.sub(', *\}', '}', tmpSearch.group(1)))
                except:
                    pass
        return tmpDict

    def getAllMeasures(self):
        #measuresRegX = re.compile("""measure_data=(\{[^=]*\}),""")
        for id_serv in self.parent.s_pool.keys():
            id_serv = str(id_serv)
            if id_serv in self.dico:
                #self.dico[id_serv]['measures'] = self.parseMeasures(id_serv, measuresRegX)
                try:
                    if self.agents_manager[id_serv]:
                        measure = self.agents_manager[id_serv].get_measure()
                except:
                    log.msg('** Sentinelle : erreur de récupération des mesures pour le serveur %s' % id_serv)
                    measure = {}
                self.dico[id_serv]['measures'] = measure
        return defer.succeed(True)

    def getAllCheckMaj(self):
        for id_serv, serv in self.parent.s_pool.items():
            id_serv = str(id_serv)
            if id_serv in self.dico and serv.module_version > 1:
                self.dico[id_serv]['paquets_maj'] = ''
                try:
                    if not self.dico[id_serv]['etats']['maj_ok'] == 1:
                        # serveur non à jour, on récupère la liste des paquets non à jour
                        if self.parent.maj_checker:
                            serv.check_maj_status(self.parent.maj_checker)
                        if serv.maj_infos is not None:
                            self.dico[id_serv]['paquets_maj'] = serv.maj_infos[0]
                except KeyError:
                    traceback.print_exc()
                    log.msg("** Sentinelle : erreur de lecture du paramètre 'maj_ok' (serveur %s)" % id_serv)
        return defer.succeed(True)

    def getAllEtab(self):
        value_list = ['rne', 'libelle', 'adresse', 'tel', 'fax', 'mail', 'responsable', 'remarques', 'type', 'ville', 'cp']
        query = "SELECT serveurs.id, etablissements.* from serveurs LEFT join etablissements ON  serveurs.rne = etablissements.rne;"
        cx = PgSQL.connect(database=config.DB_NAME,user=config.DB_USER,password=config.DB_PASSWD)
        cursor=cx.cursor()
        cursor.execute(query)
        data = cursor.fetchall()
        cursor.close()
        cx.close()
        for elem in data:
            if not str(elem[0]) in self.dico:
                continue
            self.dico[str(elem[0])]['etab'] = {}
            for i in range(len(value_list)):
                self.dico[str(elem[0])]['etab'][value_list[i]] = elem[i+1]
        return defer.succeed(True)
