#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import sys
if '/usr/share/eole/orl' not in sys.path:
	sys.path.append('/usr/share/eole/orl')
if '/usr/share/eole/orl/scripts' not in sys.path:
	sys.path.append('/usr/share/eole/orl/scripts')
###########################################################################
# Eole - 2014
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
#
# migration_2.4.py
#
# squelette de script permettant de générer des configurations de migration
# en mode batch (via l'API XMLRPC de Zéphir)
#
# Ce script génère une configuration de la même façon qu'en utilisant
# le bouton "générer un configuration de migration' de l'application web.
#
# Il est possible de personnaliser la fonction update_conf pour gérer
# des variables supplémentaires non gérées par la fonction d'origine.
# (variables déclarées au niveau des variantes / serveurs).
#
###########################################################################

import csv,getpass,os
import base64,xmlrpclib
from zephir.utils.creolewrap import ZephirDict
import sys
reload(sys)
import string
# force l'encodage par défaut à utf-8 pour les chaînes unicode
sys.setdefaultencoding('utf-8')

# à redéfinir si utilisé à distance
adresse_zephir='localhost'

# délimiteur de champ pour le fichier CSV décrivant les serveurs à traiter
delimiter = ';'
# contenu des champs du fichier csv:
# id_serveur;id_variante_destination

# !!  mettre à True pour vérifier les valeurs lors de tests (la configuration ne sera pas sauvegardée) !!
debug = True

#######################################################
## FONCTIONS UTILITAIRES POUR LE TRAITEMENT DES VALEURS

def is_empty(value):
    # renvoie True si value est 'vide' :
    # liste vide / chaine vide / liste de chaines vides
    return len(u"".join(value)) == 0

def is_equal(dico, nom_var, test):
    """renvoie True si la valeur de la variable nom_var est équivalente à test
    gère le cas des chaines unicode ou non et des listes 'vides'
    """
    if nom_var in dico.liste_vars:
        if test in ([], [''], [u'']):
            if dico.get_value(nom_var) in ([], [''], [u'']):
                return True
        if dico.get_value(nom_var) == test or \
           dico.get_value(nom_var) == [test] or \
           dico.get_value(nom_var) == [unicode(test)]:
            return True
        return False
    else:
        print u"Appel de is_equal sur une variable inconnue : {0}".format(nom_var)

def copy_val(dico, nom_var, value):
    """ définit une valeur sur une variable en transformant
    les chaînes non unicode en unicode dico est une configuration 2.4
    """
    if nom_var in dico.liste_vars:
        if value == "[u'']" or  value == "['']":
            value = []
        if value != '[]' and type(value) != list:
            if value.startswith(u"["):
                value = eval(value)
        # si le dictionnaire de destination est de type 'creole3' (eole 2.4),
        # on transforme les type 'str' en 'unicode'
        if dico.version == 'creole3':
            if type(value) == list:
                for  index ,val in enumerate(value):
                    if type(val) == str:
                        value[index] = unicode(val, 'utf-8')
            elif type(value) == str:
                value = unicode(value, 'utf-8')
        dico.get_var(nom_var)
        dico.set_value(value)
    else:
        print u"Appel de copy_val sur une variable inconnue : {0}".format(nom_var)

def read_val(dico, nom_var):
    if nom_var in dico.liste_vars:
        value = dico.get_value(nom_var)
        if value == "[u'']" or  value == "['']" or value == ['']:
            value = []
        if value != '[]' and type(value) != list:
            if value.startswith(u"["):
                value = eval(value)
        if type(value) == list:
            for index,v in enumerate(value):
                if type(v) == str:
                        value[index] = unicode(v, 'utf-8')
            return value
        else:
            return unicode(dico.get_value(nom_var), 'utf-8')
    else:
        print u"Appel de read_val sur une variable inconnue : {0}".format(nom_var)

###AJOUT ORLEANS
nat_agr={"18":"20",
	"28":"21",
	"36":"22",
	"37":"23",
	"41":"24",
	"45":"25",
	"99":"25"
	}

def sub_octet_4(ip, octet4):
    octets=ip.split(".")
    octets.__delitem__(3)
    octets.append(str(octet4))
    return ".".join(octets)

def sub_octet_1D(ip):
    octets=ip.split(".")
    octets[1]="1"+octets[1]
    return ".".join(octets)

def concat_list(liste1, liste2):
    new_liste=[]
    for i in liste1:
        new_liste.append(i)
    for j in liste2:
        new_liste.append(j)
    return new_liste

######################################
## FONCTION PERSONNALISEE DE MIGRATION

def update_conf(d_eole1, d_eole2, variante, domaine, ip_amon, smtp, type_etab, statut, vm, sid, version):
    """
	traitement automatique des variables

    met à jour la configuration de migration en fonction de la configuration actuelle

    d_eole1 : dictionnaire avec la configuration d'origine du serveur
    d_eole2 : dictionnaire qui sera sauvegardé sur Zéphir comme 'migration.eol'
              (configuration à appliquer sur le serveur migré)
    variante: identifiant de la variante du serveur après migration
    proxy: proxy XMLRPC utilisable pour faire des appels à l'API Zéphir

    lire la valeur d'une variable (dico 1 ou 2):

          d_eoleX.get_value('nom_var')
          !ATTENTION! : get_value renvoie toujours une liste de valeur.
          En cas de test sur une valeur, récupérer la valeur voulue
          ex: if read_val(d_eole1,'activer_squid_auth')[0] == 'pedago':

    copier la valeur d'une variable de dico1 vers une variabel de dico2:
          copy_val(d_eole2,'nom_variable2.4',d_eole1.get_value('nom_variable2.2'))

    modifier la valeur d'une variable du dictionnaire eole 2:

          copy_val(d_eole2,'nom_var',u'valeur')

    pour une variable multiple :  copy_val(d_eole2,'nom_var',[u'valeur1',u'valeur2', ...])

    vérifier si une variable est présente dans un dictionnaire:

          if 'nom_variable' in d_eoleX.liste_vars:
              .....

    une bonne pratique peut être de créer des sous fonctions spécifiques à chaque variante

    Pour plus d'informations sur l'utilisation de l'API de Zéphir:
    https://<adresse_ip_zephir>:8070/aide/devel
    https://<adresse_ip_zephir>:8070/aide/api
    """

    # ----- inclure les traitements des variables ici
    # --- !! pour éviter des problèmes d'indentation, configurez l'éditeur pour remplacer
    # --- les tabulations par 4 espaces (ou éditez le fichier avec vi sur un module eole)
    #
    # --- Dans la mesure du possible, utilisez de préférence la modification des valeurs par défaut
    # --- des modules/variantes eole NG dans l'application web si les valeurs à renseigner sont les mêmes
    # --- pour tous les serveurs d'un(e) variante/module.


    ### Attention, pour la migration vers EOLE 2.4 :
    #
    # pour set_value sur d_eole2, il faut utiliser des chaînes unicode pour les chaînes de caractères :
    # par exemple:
    # - d_eole2.get_var('passerelle_smtp') : sélectionne la variable
    # - d_eole2.set_value(u'smtp.in.ac-dijon.fr') : applique la valeur (unicode)
    #
    # utiliser de préférence les fonction copy_val et read_val, qui convertissent les chaînes si besoin :
    # copy_val(d_eole2, 'passerelle_smtp', u'smtp.in.ac-dijon.fr') : ici, u est optionnel
    ###

    # exemple1 : mettre le nombre de cartes à 3 si le modèle de pare-feu dans la configuration amon-1.5
    # contient la chaine '3zones'
    #
    #if 'type_amon' in d_eole1.liste_vars:
    #    if '3zones' in d_eole1.get_value('type_amon'):
    #        d_eole2.dico.set_value('nombre_interfaces','3')

    # exemple2 : pour la variante 8, activer le serveur sso si le frontend ead-web est activé
    #
    #if variante == 8:
    #    if isequal(d_eole2, 'ead_web', 'oui'):
    #        copy_val(d_eole2,'adresse_ip_sso', read_val(d_eole1,'adresse_ip_eth0'))

    # exemple 3:
    #
    # passage à oui de 'activer_ajout_hosts' si des noms supplémentaires ont été ajoutés par la variante 2.4
    # (forcé à non au moment de l'upgrade si la configuration d_eole1 ne contient pas de noms supplémentaire)
    #
    #if len(d_eole2.get_value('adresse_ip_hosts')) != 0:
    #    copy_val(d_eole2, 'activer_ajout_hosts', u'oui')
	###
    # Migration vers 2.4.2 => dictionnaire inchangé
	# Calul du département
    #
    #Cas particulier de l'upgrade 2.4.2 vers 2.5.2 => recopier le dico à l'identique
    #if version=="2.4.2":
    #    return d_eole2
    #
    rne=read_val(d_eole1,'numero_etab')[0]
    dept=rne[1:3]
    # Mise à jour des variables
    copy_val(d_eole2,'nom_domaine_local',domaine+".lan")
    copy_val(d_eole2,'web_url', read_val(d_eole1,'adresse_ip_eth0')[0])
    copy_val(d_eole2,'web_redirection','/roundcube')
    copy_val(d_eole2,'activer_exim_relay_smtp','oui')
    copy_val(d_eole2,'exim_relay_smtp', string.lower(smtp))
    copy_val(d_eole2,'system_mail_from','noreply-'+rne+'@ac-orleans-tours.fr')
    copy_val(d_eole2,'exim_address_rewrite','oui')
    copy_val(d_eole2,'exim_address_rewrite_pattern','bacula@*')
    copy_val(d_eole2,'exim_address_rewrite_replacement','noreply-'+rne+'@ac-orleans-tours.fr')
    copy_val(d_eole2,'system_mail_from_for_headers','oui') # new
    #copy_val(d_eole2,'exim_address_rewrite_flags','Ffrs') obsolète
    copy_val(d_eole2,'serveur_maj','eole.ac-dijon.fr')
    copy_val(d_eole2,'synchro_aaf_sendmail','oui') # new
    copy_val(d_eole2,'interface_gw','eth0') # forcer eth0
    copy_val(d_eole2,'nom_carte_eth0','eth0') # forcer eth0
    copy_val(d_eole2,'nom_zone_eth0','eth0') # forcer eth0
    copy_val(d_eole2,'synchro_aaf_mail','eole-exploitation@ac-orleans-tours.fr') # new
    if dept=="37" and type_etab=="clg" and statut=="PU":
        copy_val(d_eole2,'orl_bcdi','oui')
    copy_val(d_eole2,'orl_active_infoquota',read_val(d_eole1,'orl_active_infoquota')[0])
    copy_val(d_eole2,'ip_ssh_eth0','0.0.0.0')
    copy_val(d_eole2,'netmask_ssh_eth0','0.0.0.0')
    copy_val(d_eole2,'ip_admin_eth0','0.0.0.0')
    copy_val(d_eole2,'netmask_admin_eth0','0.0.0.0')
    copy_val(d_eole2,'esu_proxy','oui')
    copy_val(d_eole2,'esu_proxy_server',ip_amon)
    copy_val(d_eole2,'activer_snmpd','oui')
    copy_val(d_eole2,'snmp_allow_ip',['195.83.89.139','127.0.0.1'])
    copy_val(d_eole2,'snmp_allow_mask',['255.255.255.255','255.255.255.255'])
    copy_val(d_eole2,'snmp_community',['public','public'])
    copy_val(d_eole2,'orl_nagios','oui')
    copy_val(d_eole2,'ssh_allow_groups','nagios')
    if dept=="37" and type_etab=="clg" and statut=="PU":
        copy_val(d_eole2,'esu_proxy_bypass',['172.19.*','172.20.*','172.21.*','172.22.*','172.23.*','localhost','127.0.0.1','192.168.227.125','10.137.*'])
    else:
        copy_val(d_eole2,'esu_proxy_bypass',['172.19.*','172.20.*','172.21.*','172.22.*','172.23.*','localhost','127.0.0.1'])
    copy_val(d_eole2,'activer_bareos_dir','oui')
    copy_val(d_eole2,'activer_bareos_sd','oui')
    copy_val(d_eole2,'bareos_db_type','mysql')
    copy_val(d_eole2,'bareos_full_retention','40')
    copy_val(d_eole2,'bareos_full_retention_unit','days')
    copy_val(d_eole2,'bareos_diff_retention','40')
    copy_val(d_eole2,'bareos_diff_retention_unit','days')
    copy_val(d_eole2,'bareos_inc_retention','8')
    copy_val(d_eole2,'bareos_inc_retention_unit','days')
    if (version=="2.2" and read_val(d_eole1,'dhcp')[0]=='oui') or (version=="2.3" and read_val(d_eole1,'activer_dhcp')[0]=='oui'):
        copy_val(d_eole2,'activer_dhcp','oui')
        if read_val(d_eole1,'orl_multi_vlan_dhcp')[0] == 'oui' and read_val(d_eole1,'adresse_network_dhcp') != ['']:
            copy_val(d_eole2,'adresse_network_dhcp',concat_list(read_val(d_eole1,'adresse_network_dhcp'),read_val(d_eole1,'orl_adresse_network_dhcp')))
            copy_val(d_eole2,'adresse_netmask_dhcp',concat_list(read_val(d_eole1,'adresse_netmask_dhcp'),read_val(d_eole1,'orl_adresse_netmask_dhcp')))
            copy_val(d_eole2,'ip_basse_dhcp',concat_list(read_val(d_eole1,'ip_basse_dhcp'),read_val(d_eole1,'orl_ip_basse_dhcp')))
            copy_val(d_eole2,'ip_haute_dhcp',concat_list(read_val(d_eole1,'ip_haute_dhcp'),read_val(d_eole1,'orl_ip_haute_dhcp')))
            copy_val(d_eole2,'adresse_ip_gw_dhcp',concat_list(read_val(d_eole1,'adresse_ip_gw_dhcp'),read_val(d_eole1,'orl_adresse_ip_gw_dhcp')))
            copy_val(d_eole2,'adresse_ip_dns_dhcp',concat_list(read_val(d_eole1,'adresse_ip_dns_dhcp'),read_val(d_eole1,'orl_adresse_ip_dns_dhcp')))
            copy_val(d_eole2,'nom_domaine_dhcp',(domaine+".lan"+(','+domaine+".lan")*(len(read_val(d_eole1,'adresse_network_dhcp')) - 1)).split(","))
            copy_val(d_eole2,'orl_multi_vlan_dhcp','non')
        elif read_val(d_eole1,'orl_multi_vlan_dhcp')[0] == 'oui' and read_val(d_eole1,'adresse_network_dhcp') == ['']:
            copy_val(d_eole2,'adresse_network_dhcp',read_val(d_eole1,'orl_adresse_network_dhcp'))
            copy_val(d_eole2,'adresse_netmask_dhcp',read_val(d_eole1,'orl_adresse_netmask_dhcp'))
            copy_val(d_eole2,'ip_basse_dhcp',read_val(d_eole1,'orl_ip_basse_dhcp'))
            copy_val(d_eole2,'ip_haute_dhcp',read_val(d_eole1,'orl_ip_haute_dhcp'))
            copy_val(d_eole2,'adresse_ip_gw_dhcp',read_val(d_eole1,'orl_adresse_ip_gw_dhcp'))
            copy_val(d_eole2,'adresse_ip_dns_dhcp',read_val(d_eole1,'orl_adresse_ip_dns_dhcp'))
            if len(read_val(d_eole1,'adresse_network_dhcp')) == 1:
                copy_val(d_eole2,'nom_domaine_dhcp',domaine+".lan")
            elif len(read_val(d_eole1,'adresse_network_dhcp')) > 1:
                copy_val(d_eole2,'nom_domaine_dhcp',(domaine+".lan"+(','+domaine+".lan")*(len(read_val(d_eole1,'adresse_network_dhcp')) - 1)).split(","))
            copy_val(d_eole2,'orl_multi_vlan_dhcp','non')
        elif len(read_val(d_eole1,'adresse_network_dhcp')) == 1:
            copy_val(d_eole2,'nom_domaine_dhcp',domaine+".lan")
        elif len(read_val(d_eole1,'adresse_network_dhcp')) > 1:
            copy_val(d_eole2,'nom_domaine_dhcp',(domaine+".lan"+(','+domaine+".lan")*(len(read_val(d_eole1,'adresse_network_dhcp')) - 1)).split(","))
    if vm=="1":
        copy_val(d_eole2,'orl_vmware_tools','oui')
    #réplication
    copy_val(d_eole2,'ldap_replication','oui')
    copy_val(d_eole2,'serverID',sid)
    #paratage scan du cd45
    if dept=="45" and type_etab=="clg" and statut=="PU":
            copy_val(d_eole2,'orl_scantofolder_cd45','oui')
    if version=="2.3":
        if dept=="41" and type_etab=="clg" and statut=="PU":
        #wpkg
        #le 41 n'en veut plus
            copy_val(d_eole2,'activer_wpkg','non')
        elif version=="2.2":
            copy_val(d_eole2,'activer_wpkg','non')
        else:
            copy_val(d_eole2,'activer_wpkg',read_val(d_eole1,'orl_active_wpkg')[0])
    #ocs
    copy_val(d_eole2,'ocsinventory_agent','oui')
    copy_val(d_eole2,'ocs_agent_server_port','80')
    copy_val(d_eole2,'ocs_agent_server','http://ocs-eple.adr.ac-orleans-tours.fr')
    if vm=="1":
        copy_val(d_eole2,'ocs_agent_use_rne_as_tag','oui')
    #clamav samba - on le desactive car pose des soucis au niveau de W10
    copy_val(d_eole2,'activer_clam','non')
    #Désactivation antivirus temps réel à cause du problème  https://dev-eole.ac-dijon.fr/issues/12454
    #copy_val(d_eole2,'smb_vscan','non')
    #logs samba
    copy_val(d_eole2,'smb_log_level','1')
    #limite ibdata mysql
    copy_val(d_eole2,'orl_limit_size_mysql','oui')
    #sinon les acces ead bug
    copy_val(d_eole2,'eolesso_adresse', read_val(d_eole1,'adresse_ip_eth0')[0])
    #GLPI #0121882
    copy_val(d_eole2,'smb_socket_options', 'TCP_NODELAY')


    # ---- fin des traitements

    return d_eole2

###AJOUT ORLEANS
def get_pass():
    fic=open("/root/pwd", "r")
    pwd=fic.readline().strip()
    fic.close()
    os.system("rm -f /root/pwd")
    login=pwd.split(";")[0]
    passwd=pwd.split(";")[1]
    return [login, passwd]

######################
## FONCTION PRINCIPALE

def migrate_serveurs(fic_csv_name="", main_loop=False):
    """
    boucle principal de parcours des serveurs
    """
    login, passwd = get_pass()
    # lecture du fichier csv
    if fic_csv_name == "":
        fic_csv_name = "/root/scribe.csv"
    else:
        fic_csv_name = "/root/"+fic_csv_name
    fic_csv = open(fic_csv_name)
    parser = csv.reader(fic_csv,delimiter=delimiter)
    proxy = xmlrpclib.ServerProxy('http://%s:%s@%s:7081' % (login,passwd,adresse_zephir))
    # informations sur les module et variantes

    # parsing des données
    erreurs = []
    while True:
        try:
            # lecture d'une ligne du fichier csv
            data_serveur = parser.next()
        except StopIteration:
            # fin du fichier
            fic_csv.close()
            break
        # chaque ligne indique un numéro de serveur et le n° de la variante à utiliser après migration
        id_serveur, variante, domaine, ip_amon, smtp, type_etab, statut, vm, sid, version = data_serveur
        id_serveur = int(id_serveur)
        variante = int(variante)
        # migration du serveur (équivalent de 'générer la 'générer les données de migration' dans  l'appli web)
        try:
            # récupération du dictionnaire creole du serveur dans sa version actuelle
            code, data_ori = proxy.serveurs.get_dico(id_serveur,'modif_config',True)
            if version in ("2.2","2.3"): # passage 2.2 ou 2.3 à 2.6.2
                d_eole1 = ZephirDict(mode='')
            elif version in ("2.5.2"): # passage 2.5.2 à 2.6.2
                d_eole1 = ZephirDict(mode='', version='creole3')
            d_eole1.init_from_zephir(data_ori)
            # initialisation du dictionnaire creole2
            code, data_migration = proxy.serveurs.migrate_conf(int(id_serveur), 'migration', int(variante))
            if code == 0:
                raise Exception, data_migration
            ##  IMPORTANT, pour la migration vers eole 2.4, bien spécifier version='creole3'
            d_eole2 = ZephirDict(mode='', version='creole3')
            # initialisation du dictionnaire depuis les valeurs renvoyées par Zéphir
            d_eole2.init_from_zephir(data_migration)
            # déblocage temporaire des variables cachées et vérouillées ?
            #settings = d_eole2.dico.cfgimpl_get_settings()
            #settings.remove('disabled')
            #settings.remove('frozen')
            # mise à jour du dictionnaire de migration
            # APPEL DE LA FONCTION DE MIGRATION PERSONNALISEE
            d_eole2 = update_conf(d_eole1, d_eole2, int(variante), domaine, ip_amon, smtp, type_etab, statut, vm, sid, version)
            if debug:
                print "\n--- configuration du serveur %s\n" % id_serveur
                for var in d_eole2.liste_vars:
                    print var, '-->', d_eole2.get_value(var)
                if not main_loop:
                    # mod debug et appel depuis shell python: on retourne
                    # les 2 dictionnaires pour le premier serveur du fichier CSV
                    # pour faciliter les diagnostics
                    fic_csv.close()
                    return d_eole1, d_eole2
            else:
                save_data = d_eole2.save(force=False) #,eol_file='/tmp/test.txt')
                # sauvegarde de la conf sur zephir (migration.eol)
                code, detail = proxy.serveurs.save_conf(id_serveur, save_data, 'migration')
                print "serveur modifié : %s" % id_serveur
        except Exception, e:
            if debug:
                # trace complète en mode debug
                import traceback
                traceback.print_exc()
            print "erreur de modification du serveur %s : %s" % (id_serveur, str(e))
            erreurs.append(delimiter.join(data_serveur))

    # on stocke dans un fichier csv les serveurs non ajoutés
    if len(erreurs) > 0:
        f=open("error.csv","w")
        f.write('\n'.join(erreurs))
        f.close()
        print "\nDes erreurs ont été rencontrées :\n"
        print "- Vous pouvez consulter les logs du service zephir pour plus d'informations:\n"
        print "     /var/log/rsyslog/local/zephir_backend/zephir_backend.info.log\n"
        print "- un fichier error.csv a été généré pour relancer ce script sur les serveurs en erreur\n"

if __name__ == '__main__':
    try:
        # on regarde si un fichier csv est passé en argument
        assert os.path.isfile(sys.argv[1])
        fic_csv = sys.argv[1]
    except:
        fic_csv = ""
    migrate_serveurs(fic_csv, main_loop=True)
