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

""" Génération de la configuration pour bind9

Copyright (C) Frédéric Pauget
Licence : GPLv2
"""
import time, sys, re, hashlib, base64, os
sys.path.append('/usr/scripts/gestion')
from socket import gethostname
from gen_confs import gen_config

import config
import config.dns
from iptools import AddrInNet, AddrInNets
import ip6tools

import netaddr
import ldap_crans

def short_name(fullhostname):
    return fullhostname.split(".")[0]



def netv4_to_arpa(net):
    addr, prefixlen = net.split('/')
    if prefixlen == '8':
        return ["%s.in-addr.arpa" % addr.split('.')[0]]
    if prefixlen == '16':
        return ["%s.in-addr.arpa" % '.'.join(reversed(addr.split('.')[0:2]))]
    zones=[]
    n = map(int,net.split('/')[0].split('.')[:3])
    while 1 :
        try:
            innet = AddrInNet("%d.%d.%d.1" % tuple(n),net)
        except ValueError:
            break
        else:
            if not innet:
                break
            else :
                n.reverse()
                zones.append("%d.%d.%d.in-addr.arpa" % tuple(n))
                n.reverse()
        n[2] += 1
    return zones
    
def netv6_to_arpa(net):
    n = netaddr.IPNetwork(net)
    network_reverse = netaddr.IPAddress(n.first).reverse_dns
    zone = network_reverse.split('.')[(128-n.prefixlen)/4:-1]
    return '.'.join(zone)


class dns(gen_config) :
    """
    Génération des fichiers de configuration de bind9 :
        * fichier DNS_CONF qui contient les définitions de zone conformément
à zone_template. Ce fichier doit être inclus à partir de la config statique
de bind
        * les fichiers de zones, ce sont eux qui contiennent les données du
dns, ils ont appellés par le fichier DNS_CONF et sont générés dans DNS_DIR
Leur entète est générée à partir de zone_entete.

    Les fichiers générés placent bind comme autoritaire sur les noms de
zones_direct et les adresses de zones_reverse. Les données proviennent de
la base LDAP
    """
    ######################################PARTIE DE CONFIGURATION

    ### Fichiers à écrire
    # Répertoire d'écriture des fichiers de zone
    DNS_DIR = '/etc/bind/generated/' # Avec un / à la fin
    DNSSEC_DIR = '/etc/bind/signed/' # Avec un / à la fin
    # Fichier de définition des zones pour le maître
    DNS_CONF = DNS_DIR + 'zones_crans'

    # Fichier de définition des zones pour les esclaves géré par BCfg2
    DNS_CONF_BCFG2 = "/var/lib/bcfg2/Cfg/etc/bind/generated/zones_crans/zones_crans"

    ### Sur quelles zones on a autorité ?
    ## En cas de modification de ces zones penser à regéner le fichier de
    ## zone des esclaves (sur le serveur principal de bcfg2 : python /usr/scripts/gestion/gen_confs/bind.py puis lancer bcfg2 sur les miroirs)
    # Résolution directe
    zones_direct = config.dns.zones_direct
    # Zones signée par opendnssec sur le serveur maitre
    zones_dnssec = config.dns.zones_dnssec
    # Zones alias pour les enregistrement A AAAA CNAME TXT et SSHFP
    zone_alias = config.dns.zone_alias
    zones_v4_to_v6 = {
        'wifi.crans.eu': 'wifi.v6.crans.eu',
        'crans.eu': 'v6.crans.eu',
        'crans.org': 'v6.crans.org',
        'wifi.crans.org': 'wifi.v6.crans.org',
        'adm.crans.org': 'adm.v6.crans.org',
        'ferme.crans.org': 'ferme.v6.crans.org',
        }
    zone_alias.update({
        'wifi.v6.crans.eu': ['v6.wifi.crans.eu'],
        'wifi.v6.crans.org': ['v6.wifi.crans.org'],
        'adm.v6.crans.org': ['v6.adm.crans.org'],
        'ferme.v6.crans.org': ['v6.ferme.crans.org'],
        })

    # Résolution inverse
    zones_reverse = config.dns.zones_reverse
    zones_v6_to_net = {
        'crans.eu': config.prefix["fil"][0],
        'crans.org': config.prefix["fil"][0],
        'wifi.crans.org': config.prefix["wifi"][0],
        'adm.crans.org': config.prefix["adm"][0],
        'ferme.crans.org': config.prefix["fil"][0],
        # Hack pour générer un fichier de zone vide
        '##HACK##': config.prefix["subnet"][0],
        }
    ### Liste DNS
    # Le premier doit être le  maitre
    DNSs = config.dns.DNSs
    DNSs_private = []
    ip_master_DNS = config.dns.master
    
    zone_multicast = config.dns.zone_tv

    ### Liste des délégations de zone
    # Pour les demandes de ces zones, le DNS dira d'aller voir les serveurs listés ici
    # Pour les noms des serveurs on met l'IP sans point ou le nom avec un point
    DELEG = { 
      zone_multicast : [ 'sable.crans.org.' , 'mdr.crans.org.', 'freebox.crans.org.', 'ovh.crans.org.'] ,
    }

    ### Serveurs de mail
    # format : [ priorité serveur , .... ]
    MXs = ['10 redisdead.crans.org', '20 ovh.crans.org', '25 freebox.crans.org']
    SRVs = [
            '_jabber._tcp.crans.org.         86400 IN SRV 5 0 5269 xmpp.crans.org.',
            '_xmpp-server._tcp.crans.org.    86400 IN SRV 5 0 5269 xmpp.crans.org.',
            '_xmpp-client._tcp.crans.org.    86400 IN SRV 5 0 5222 xmpp.crans.org.',
            '_sip._udp.crans.org.            86400 IN SRV 5 0 5060 asterisk.crans.org.',
            '_sip._tcp.crans.org.            86400 IN SRV 5 0 5060 asterisk.crans.org.',
            '_sips._tcp.crans.org.           86400 IN SRV 5 0 5061 asterisk.crans.org.',
           ]

    NATPRs = [
              '@  86400 IN NAPTR  5 100 "S" "SIPS+D2T" "" _sips._tcp.crans.org.',
              '@  86400 IN NAPTR 10 100 "S" "SIP+D2U" "" _sip._udp.crans.org.',
              '@  86400 IN NAPTR 15 100 "S" "SIP+D2T" "" _sip._tcp.crans.org.',
             ]
    
    # DS à publier dans zone parentes : { parent : [ zone. TTL IN DS key_id algo_id 1 hash ] }
    # ex : { 'crans.eu' :  ['wifi.crans.eu.		86400	IN	DS	33131 8 1 3B573B0E2712D8A8B1B0C3'] }
    # /!\ Il faut faire attention au rollback des keys, il faudrait faire quelque chose d'automatique avec opendnssec
    DS = {
        'crans.eu': [
            'wifi.crans.eu.  3600    IN      DS      37582 8 2 51809b508e450d8fec44572a3fa31754c27507465775c7d1c86570abd7a21024',
            'v6.crans.eu.    3600    IN      DS      12562 8 2 0a8e92398c5213cf2907b79f0fa8bd7db6729e0d2f6ca7443ac5a4b8441e38bd',
          ],
        'v6.crans.eu' : [
            'wifi.v6.crans.eu.	3600	IN	DS	1799 8 2 52a40a7dfb3e9c88aee032c21c59be756c8d3de29149c408ed8b699d83e30032',
         ],
        'crans.org': [
            'v6.crans.org.       3600    IN      DS      23641 8 2 3fff97a2581f0f2f49257b4914d5badf8ccb0a49c5a6f4cbf2f520b97de332d0',
            'adm.crans.org.      3600    IN      DS      565 8 2 498f6cd5bcf291aae4129700a7569fa6e9a86821185bd655f0b9efc6a3bf547e',
            'ferme.crans.org.    3600    IN      DS      35156 8 2 b63a1443b3d7434429e879e046bc8ba89056cdcb4b9c3566853e64fd521895b8',
            'wifi.crans.org.     3600    IN      DS      41320 8 2 024799c1d53f1e827f03d17bc96709b85ee1c05d77eb0ebeadcfbe207ee776a4',
            'tv.crans.org.       3600    IN      DS      30910 8 2 3317f684081867ab94402804fbb3cd187e29655cc7f34cb92c938183fe0b71f5',
          ],
        'v6.crans.org' : [
            'adm.v6.crans.org.   3600    IN      DS      1711 8 2 f154eeb8eb346d2ca5cffb3f9cc464a17c0c4d69ee425b4fe44eaed7f5dd253b',
            'ferme.v6.crans.org. 3600    IN      DS      44434 8 2 fb87cb4216599cb6574add543078a9e48d0e50438483386585a9960557434ab0',
            'wifi.v6.crans.org.  3600    IN      DS      59539 8 2 dbe86f2f2e92d6a27bd1436f03ec1588f2948a2aa02124de0383be801cced85e',
         ]
    }
        

    ### Entète des fichiers de zone
    zone_entete="""
$ORIGIN %(zone)s.
$TTL 3600
@\tIN\tSOA %(serveur_autoritaire)s. root.crans.org. (
    %(serial)i ; numero de serie
    21600 ; refresh (s)
    3600 ; retry (s)
    1209600 ; expire (s)
    3600 ; TTL (s)
    )
"""

    # Syntaxe utilisée dans le fichier DNS_CONF pour définir une zone sur le maître
    zone_template="""
zone "%(NOM_zone)s" {
    type master;
    file "%(FICHIER_zone)s";
};
"""
    # Syntaxe utilisée dans le fichier DNS_CONF_BCFG2 pour définir une zone sur un esclave
    zone_template_slave="""
zone "%(NOM_zone)s" {
    type slave;
    file "%(FICHIER_zone)s";
    masters { %(ip_master_DNS)s; };
};
"""

    ### Verbosité
    # Si =2, ralera (chaine warnings) si machines hors zone trouvée
    # Si =1, comme ci-dessus, mais ne ralera pas pour freebox
    # Si =0, ralera seulement contre les machines ne pouvant être classées
    verbose = 1

    hostname = short_name(gethostname())
    if hostname ==  short_name(DNSs[0]):
        restart_cmd = '/usr/sbin/ods-signer sign --all && /etc/init.d/bind9 reload'
    else:
        restart_cmd = '/etc/init.d/bind9 reload'

    ######################################FIN PARTIE DE CONFIGURATION

    def __str__(self) :
        return "DNS"

    def reverse(self, net, ip):
        """Renvoie la zone DNS inverse correspondant au réseau et à
        l'adresse donnés, ainsi que le nombre d'éléments de l'ip a
        mettre dans le fichier de zone."""
        n = netaddr.IPNetwork(net)
        a = netaddr.IPAddress(ip)
        rev_dns_a = a.reverse_dns.split('.')[:-1]
        assert a in n
        if n.version == 4:
            if n.prefixlen == 16:
                return ('.'.join(rev_dns_a[2:]), 2)
            else:
                return ('.'.join(rev_dns_a[1:]), 1)
        elif n.version == 6:
            return ('.'.join(rev_dns_a[(128-n.prefixlen)/4:]), (128-n.prefixlen)/4)


    def is_ascii(self, s):
        return all(ord(c) < 128 for c in s)
            
    def gen_tv(self):
        sys.path.append('/usr/scripts')
        import tv.dns
        serial = time.time() + 1000000000
        zone_reverse=netv4_to_arpa(config.NETs['multicast'][0])[0]
        sap=open('/usr/scripts/var/tv/sap.txt').readlines()
         
        DNS='; DNS de la zone par ordre de priorité\n'
        for d in self.DELEG[self.zone_multicast] :
            DNS += '@\tIN\tNS %s\n' % d
        DNS += '\n'
        
        lignes_d = '\n'
        lignes_r = '\n'
        lignes_d +='@\tIN\tA\t%s\n' % '138.231.136.88'
        for line in sap:
            [nom,ip]=line.split(':')
            nom=unicode(nom, 'utf-8')
            nom_ascii=tv.dns.ascii(nom)
            nom_punycode=tv.dns.punycode(nom)
            try:
                [ip1,ip2,ip3,ip4]=ip.strip().split('.')
                lignes_r += '%s.%s.%s\tIN\tPTR\t%s.%s.\n' % (ip4,ip3,ip2,nom_ascii,self.zone_multicast)
                lignes_d +='%s\tIN\tA\t%s' % (nom_ascii,ip)
                if nom_punycode:
                    lignes_d +='%s\tIN\tA\t%s' % (nom_punycode, ip)
            except:
                raise
        # Écriture de la zone directe
        file = self.DNS_DIR  + 'db.' + self.zone_multicast
        fd = self._open_conf(file,';')
        fd.write(self.zone_entete % \
        { 'zone' : self.zone_multicast, 'serveur_autoritaire' : self.DELEG[self.zone_multicast][0][0:-1] , 'serial' : serial } )
        fd.write('\n')
        fd.write(DNS)
        fd.write(lignes_d)
        fd.close()
        
        # Écriture du reverse
        file = self.DNS_DIR  + 'db.' + zone_reverse
        fd = self._open_conf(file,';')
        fd.write(self.zone_entete % \
        { 'zone' : zone_reverse, 'serveur_autoritaire' : self.DELEG[self.zone_multicast][0][0:-1] , 'serial' : serial } )
        fd.write('\n')
        fd.write(DNS)
        fd.write(lignes_r)
        fd.close()
        
    
    def gen_slave(self) :
        """ Génération du fichier de config de zone pour les esclaves """
        zones = self.zones_direct
        zones.extend(self.zones_v4_to_v6.values())
        zones.extend([z for l in self.zone_alias.values() for z in l])
        zones = list(set(zones))
        zones.sort()

        # Ajout des zones reverse
        for net in self.zones_reverse:
            # IPv4 reverse
            zones.extend(netv4_to_arpa(net))

        for net in set(self.zones_v6_to_net.values()):
            # IPv6 reverse
            zones.append(netv6_to_arpa(net))

        # Ecriture
        fd = self._open_conf(self.DNS_CONF_BCFG2,'//')
        for zone in zones :
            if zone in self.zones_dnssec:
                path=self.DNSSEC_DIR  + 'db.' + zone
            else:
                path=self.DNS_DIR  + 'db.' + zone
            fd.write(self.zone_template_slave % { 'NOM_zone' : zone,
                       'FICHIER_zone' : path,
                       'ip_master_DNS': self.ip_master_DNS})

        fd.close()

    def _gen(self) :
        ### Génération du numéro de série
        # Le + 1000.... s'explique pas l'idée précédente et peu pratique d'avoir
        # le numéro de série du type AAAAMMJJNN (année, mois, jour, incrément par jour)
        serial = time.time() + 1000000000

        ### DNS
        DNS='; DNS de la zone par ordre de priorité\n'
        for d in self.DNSs :
            DNS += '@\tIN\tNS %s.\n' % d
        DNS += '\n'

        ### Serveurs de mail
        MX='; Serveurs de mails\n'
        for m in self.MXs :
            MX += '@\t' # Sera remplacé par le nom de zone plus tard
            MX += 'IN\tMX\t%s.\n' % m
        MX += '\n'

        direct = {} # format : { zone : [ lignes correspondantes] }
        reverse = {}
        warnings = ''

        direct['crans.org'] = ""

        # P'tit lien vers irc.rezosup.org
        #direct["crans.org"]  = "\n; irc.crans.org -> irc.rezosup.org\n"
        #direct["crans.org"] += "irc\tIN\tCNAME\tirc.rezosup.org.\n\n"

        ### Tri des machines
        self.anim.iter=len(self.machines)
        for machine in self.machines :
            self.anim.cycle()
            # Calculs préliminaires
            try :
                nom , zone = machine.nom().split('.',1)
                zone = zone.encode('utf-8')
            except :
                warnings += u'Machine ignorée (mid=%s) : format nom incorrect (%s)\n' % ( machine.id().encode('utf-8'), machine.nom().encode('utf-8') )
                continue

            # Le direct
            if zone in self.zones_direct :
                ligne=''
                if machine.ip() != '<automatique>':
                    ligne += "%s\tIN\tA\t%s\n" % ( nom, machine.ip() )
                # Si la machine est une borne wifi, on ajoute la position
                if isinstance(machine,ldap_crans.BorneWifi) and machine.position():
                    ligne +="%s\tIN\tTXT\t\"LOC %s,%s \"\n" %  (nom,machine.position()[0],machine.position()[1])
                # Si la machine Ã  des clefs ssh, on ajoute les champs SSFP correspondant
                for sshkey in machine.sshFingerprint():
                    try:
                        [algo_txt,key]=sshkey.split()[:2]
                        algo=None
                        for value in config.sshfp_algo.values():
                            if algo_txt == value[1]:
                                algo=value[0]
                                break
                        if not algo: 
                            raise ValueError("Invalid Algorithms %s" % algo_txt)
                        key1=hashlib.sha1(base64.b64decode(key)).hexdigest()
                        key2=hashlib.sha256(base64.b64decode(key)).hexdigest()
                        ligne +="%s\tIN\tSSHFP\t%s\t1\t%s\n" % (nom,algo,key1)
                        ligne +="%s\tIN\tSSHFP\t%s\t2\t%s\n" % (nom,algo,key2)
                    except(ValueError,TypeError): pass
                direct[zone] = direct.get(zone, "") + ligne
                if isinstance(machine,ldap_crans.BorneWifi):
                    direct['ap.crans.org'] = direct.get('ap.crans.org', "") + ligne
            elif self.verbose and machine.nom() != "ftp.federez.net":
                warnings += u'Résolution directe ignorée (mid=%s) : zone non autoritaire (%s)\n' % ( machine.id().encode('utf-8'), zone.encode('utf-8') )

            # IPv6
            if zone in self.zones_v4_to_v6:
                # Direct
                zone_v6 = self.zones_v4_to_v6[zone]
                ipv6 = machine.ipv6()
                net_v6 = machine.netv6()
                ligne = "%s\tIN\tAAAA\t%s\n" % (nom, ipv6)
                direct[zone_v6] = direct.get(zone_v6, "") + ligne
                if machine.dnsIpv6():
                    direct[zone] = direct.get(zone, "") + ligne
                # Reverse
                zone_rev, length = self.reverse(net_v6, ipv6)
                rev = '.'.join(ipv6.reverse_dns.split('.')[:length])
                ligne = "%s\tIN\tPTR\t%s.\n" % (rev, machine.nom6())
                reverse[zone_rev] = reverse.get(zone_rev, "") + ligne


            # Le direct avec alias
            for alias in machine.alias() :
                alias = alias.encode('utf-8')
                # Cas particulier : nom de l'alias = nom de la zone
                if alias in self.zones_direct :
                    if machine.ip() != '<automatique>':
                        ligne = "@\tIN\tA\t%s\n" % machine.ip()
                        ligne = ligne.encode('utf-8')
                        direct[alias] = direct.get(alias, "") + ligne
                    if machine.dnsIpv6():
                        ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
                        ligne = ligne.encode('utf-8')
                        direct[alias]= direct.get(alias, "") + ligne
                    if alias in self.zones_v4_to_v6:
                        ligne = "@\tIN\tAAAA\t%s\n" % machine.ipv6()
                        ligne = ligne.encode('utf-8')
                        zone6 = self.zones_v4_to_v6[alias]
                        direct[zone6] = direct.get(zone6, '') + ligne
                    continue

                # Bon format ?
                alias_l = alias.split('.')
                ok = 0
                for i in range(len(alias_l)) :
                    zone_essai = '.'.join(alias_l[i:])
                    if zone_essai in self.zones_direct :
                        # On est autoritaire sur cette zone
                        # On place donc l'alias dans le fichier de cette zone
                        zone = zone_essai
                        nom = '.'.join(alias_l[:i])
                        ok = 1
                        break
                if not ok:
                    warnings += u'Alias ignoré (mid=%s) : %s\n' % ( machine.id().encode('utf-8'), alias.encode('utf-8') )
                    continue
                zone = zone.encode('utf-8')
                ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom() )
                direct[zone] = direct.get(zone, '') + ligne
                if zone in self.zones_v4_to_v6:
                    zone6 = self.zones_v4_to_v6[zone]
                    ligne = "%s\tIN\tCNAME\t%s.\n" % ( nom, machine.nom6() )
                    direct[zone6] = direct.get(zone6, '') + ligne

            # Le reverse
            ip = machine.ip()
            if ip == '<automatique>':
                net=False
                continue
            else:
                net = AddrInNets(ip, self.zones_reverse)
            if net:
                base_ip = ip.split('.')
                base_ip.reverse()
                zone, length = self.reverse(net, ip)
                zone = zone.encode('utf-8')
                ligne = '%s\tIN\tPTR\t%s.\n' % ('.'.join(base_ip[:length]), machine.nom())
                try : reverse[zone] += ligne
                except : reverse[zone] = ligne
            elif self.verbose >= 2 or machine.nom() not in ('freebox.crans.org', 'ovh.crans.org', 'kokarde.crans.org'):
                warnings += u'Résolution inverse ignorée (mid=%s) : ip sur zone non autoritaire (%s)\n' % ( machine.id().encode('utf-8'), machine.ip().encode('utf-8') )

        ### Ajouts pour les fichiers de résolution directs
        for zone in direct.keys() :
            # MXs
            direct[zone] = MX + direct[zone]

        ### XXX: création de la zone inverse pour le /48 IPv6 complet du Cr@ns
        full_net_v6 = self.zones_v6_to_net["##HACK##"]
        zone_rev, length = self.reverse(full_net_v6, netaddr.IPNetwork(full_net_v6).first)
        reverse[zone_rev] = reverse.get(zone_rev, "")

        ### Alias de zone
        zone_todo = [zone for zone in self.zone_alias]
        while zone_todo:
            for zone in zone_todo:
                for alias in self.zone_alias[zone]:
                    try:
                        direct[alias] = direct[zone]
                        zone_todo.remove(zone)
                    except KeyError:
                        pass
                    if alias in self.zones_v4_to_v6:
                        alias_v6=self.zones_v4_to_v6[alias]
                        zone_v6 = self.zones_v4_to_v6[zone]
                        direct[alias_v6] = direct[zone_v6]

        ### Ajout des parametres SPF
        direct['crans.org'] +='; Parametres SPF\n'
        direct['crans.org'] +='crans.org.\tIN\tSPF\t"v=spf1 a mx ip6:2a01:240:fe3d:4::/64 ?all"\n'
        for m in self.MXs:
            direct['crans.org'] +='%s.\tIN\tSPF\t"v=spf1 a ip6:2a01:240:fe3d:4::/64 ?all"\n' % m.split()[-1]
        direct['crans.org'] += '\n'

        direct['crans.ens-cachan.fr'] += '; Parametres SPF\n'
        direct['crans.ens-cachan.fr'] += 'crans.ens-cachan.fr.\tIN\tSPF\t"v=spf1 a:crans.org mx ip6:2a01:240:fe3d:4::/64 ?all"\n\n'

        ### Ajout d'eventuels champs SRV
        direct['crans.org'] +='; Champs SRV\n'
        for s in self.SRVs:
            direct['crans.org'] += s + '\n'
        for s in self.NATPRs:
            direct['crans.org'] += s + '\n'
        direct['crans.org'] += '\n'

        ### Ajout des délégations de zones
        for deleg in self.DELEG.keys():
            nom, zone = deleg.split('.',1)
            if not zone in direct.keys():
                warnings += u'Délégation ignorée %s : on ne génère pas la zone parent\n' % deleg
                continue
            for serv in self.DELEG[deleg]:
                direct[zone] = direct[zone] + "%s\tIN\tNS\t%s\n" % ( nom, serv )

        for zone in direct.keys():
            child, parent = zone.split('.',1)
            if not zone in self.DELEG.keys() and parent in self.zones_direct + [z for l in self.zone_alias.values() for z in l] + self.zones_v4_to_v6.values():
                for d in self.DNSs:
                    direct[parent] = direct.get(parent, "") + '%s\tIN\tNS %s.\n' % (child, d)


        ### Ajout d'eventuel champs DS pour les délégation dnssec
        for zone,ds in self.DS.items():
            for s in ds:
              direct[zone] += s + '\n'
            direct[zone] += '\n'

        ### Ecriture des fichiers de zone et préparation du fichier de définition
        f = ''
        for zone, lignes in direct.items() + reverse.items() :
            if zone in self.zones_dnssec:
                path = self.DNSSEC_DIR  + 'db.' + zone
            else:
                path = self.DNS_DIR  + 'db.' + zone
            file = self.DNS_DIR  + 'db.' + zone
            fd = self._open_conf(file,';')
            fd.write(self.zone_entete % \
            { 'zone' : zone, 'serveur_autoritaire' : self.DNSs[0] , 'serial' : serial } )
            fd.write('\n')
            fd.write(DNS)
            fd.write(lignes)
            fd.close()
            os.chmod(file,0664)
            if short_name(gethostname()) in map(short_name, dns.DNSs[1:]):
                f += self.zone_template_slave % {'NOM_zone': zone, 'FICHIER_zone': path,
                        'ip_master_DNS': self.ip_master_DNS}
            else:
                f += self.zone_template % { 'NOM_zone' : zone, 'FICHIER_zone' : path }

        ### Ecriture fichier de définition
        fd = self._open_conf(self.DNS_CONF,'//')
        fd.write(f)
        fd.close()

        return warnings


if __name__ == '__main__' :
    from config import bcfg2_main
    hostname = short_name(gethostname())
    if hostname == short_name(bcfg2_main):
        print "Reconfiguration du fichier de BCfg2 pour configurer le bind d'un serveur en esclave (pensez à lancer bcfg2 sur les esclaves)."
        c = dns()
        c.gen_slave()
        if hostname ==  short_name(dns.DNSs[0]):
            print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
    elif hostname == short_name(dns.DELEG[config.dns.zone_tv][0][0:-1]):
        print "Serveur maître pour tv.crans.org, génération de la zone"
        c = dns()
        c.gen_tv()
        import subprocess
        args="/usr/sbin/ods-signer sign tv.crans.org".split()
        p=subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        ret=p.communicate()
        print ret[0].strip()
        print ret[1].strip()
        if hostname ==  short_name(dns.DNSs[0]):
            print "Ce serveur est également serveur maitre, mais la reconfiguration du DNS maitre se fait par generate."
    elif hostname ==  short_name(dns.DNSs[0]):
        print "Ce serveur est maître ! Utilisez generate."
    elif hostname in map(lambda fullhostname : short_name(fullhostname),dns.DNSs[1:]+dns.DNSs_private):
        print "Ce serveur est esclave! Lancez ce script sur %s, puis lancez bcfg2 ici" % bcfg2_main
    else:
        print "Ce serveur ne correspond à rien pour la configuration DNS."
