Hi,

Here is attached the tinydns export script for sauron following an off- list request, with many domains (100's/1000's) this is _MUCH_ faster (never looked exactly) than the built in sauron tinydns export.

Thomas


#!/usr/bin/python

# Generate one big configuration file and does not care about the servers 
defined in the DB

# XXX: To check: Take care of treating the numeric in the DB correctly and not 
string (convert if needed)
# XXX: Sauron check as well the modification date to only write modified files, 
we do not as those query are really fast
# XXX: we do not set the record as "exported" neither (so the interface still 
show them as not-exported)        

import sys
import os
import pgdb as db

# XXX: This is IPV4 only ...
# quick hack as we have lib somewhere 
def iptoarpa (data):
        return '.'.join(data.split('.')[::-1]) + ".in-addr.arpa."

option = {}
option['export-tinydns'] = True
option['export-bind'] = False
option['all-in-one'] = True
option['output-stdout'] = False
option['tmp-directory'] = './export'
option['one-zone-only'] = False

BIND_LINE = "%-32s %6s %2s  %-6s %s\n"

domain_template = """
select
        zones.name,
        servers.hostname as ns,
        coalesce(zones.hostmaster,servers.hostmaster) as hostmaster,
        zones.serial,
        coalesce(zones.refresh,servers.refresh) as refresh,
        coalesce(zones.retry,servers.retry) as retry,
        coalesce(zones.expire,servers.expire) as expire,
        coalesce(zones.minimum,servers.minimum) as minimum,
        coalesce(zones.ttl,servers.ttl),
        zones.type,
        zones.cuser,
        zones.cdate,
        zones.mdate,
        zones.muser,
        zones.active,
        zones.serial_date,
        zones.comment
from servers
        join zones
                on servers.id = zones.server
where
        zones.active = true
        %s
order by zones.name;

"""

domain_query = domain_template % ("and zones.dummy = 'f'\n\tand zones.reverse = 
'f'\n\t%s")
domain_reverse = domain_template % ("and zones.reverse = 't'\n\t%s")

ns_query = """
select
        hosts.domain as host,
        zones.name as zone,
        ns_entries.ns,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl  
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
        join ns_entries
                on ns_entries.ref = hosts.id
where
        (hosts.type = 10 or hosts.type = 2)
        and (zones.active = true)
        %s
order by zones.name, hosts.domain;

"""

mx_query = """
select
        hosts.domain as host,
        zones.name as zone,
        mx_entries.mx as mx_entries,
        mx_entries.pri as mx_pri,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
        join mx_entries
                on hosts.type in (3,10)
                and mx_entries.type = 2
                and mx_entries.ref = hosts.id
where
        zones.active = true
        %s
order by zones.name, mx_entries.pri, hosts.domain;

"""

a_query = """
select
        hosts.domain as host,
        zones.name as zone,
        a_entries.ip as a_ip,
        a_entries.forward,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl,
        hosts.type
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
        join a_entries
                on hosts.type in (1,10)
                and a_entries.host = hosts.id
where
        zones.active = true
        %s
order by zones.name, hosts.domain;

"""

a_reverse = a_query % "and a_entries.reverse = 't' %s"
a_forward = a_query % "and a_entries.forward = 't' %s"

txt_query = """
select
        hosts.domain as host,
        zones.name as zone,
        txt_entries.txt as txt,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
        join txt_entries
                on txt_entries.ref = hosts.id
                and txt_entries.txt != ''
where
        zones.active = true
        %s
order by zones.name, hosts.domain;

"""

# This query takes AGES ... :(
internal_cname_query = """
select
        hosts.domain as host,
        zones.name as zone,
        int_hosts.domain as cname_host,
        int_zones.name as cname_zone,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
        join hosts as int_hosts
                on hosts.alias = int_hosts.id
        join zones as int_zones
                on int_zones.id = int_hosts.zone
where
        hosts.alias > 0
        and zones.active = true
        %s
order by zones.name, hosts.domain;

"""

external_cname_query = """
select
        hosts.domain as host,
        zones.name as zone,
        hosts.cname_txt as host_cname,
        coalesce(hosts.ttl,zones.ttl) as ttl,
        coalesce(zones.ttl,servers.ttl) as default_ttl
from servers
        join zones 
                on servers.id = zones.server
        join hosts
                on zones.id = hosts.zone
where
        hosts.cname_txt != ''
        and zones.active = true
        %s
order by zones.name, hosts.domain;

"""

def err (code,*str):
        sys.stderr.write(*str)
        sys.exit(code)

class ExportZone:
        def __init__ (self,server,database,user,password):
                self.file = None
                self.sql = None
                self.path = None
                self.remove_file = False

                try:
                        self.sql = db.connect(dsn='%s:%s' % 
(server,database),user=user)
                except:
                        err(1,'could not establish connection to database\n')

                if option['export-tinydns']:
                        option['export-bind'] = False

                if option['export-bind']:
                        option['all-in-one'] = False

                        if option['output-stdout']:
                                option['one-zone-only'] = True
                                
                if option['all-in-one'] and not option['output-stdout']:
                        # clear the file
                        self._create('data')

                        

        def __del__ (self):
                if self.file:
                        try:
                                self.file.close()
                        except:
                                err(1,'could not close the file')

                if self.sql:
                        try:
                                self.sql.close()
                        except:
                                err(1,'issue closing DB connection')
                        
                if self.remove_file and self.path and self.file:
                        try:
                                self.file.close()
                                self.file = None
                                self.unlink(self.path)
                                self.path = None
                        except:
                                err(1,'could not removed the files created')


        def _fqdn (self,*args):
                host = args[0]
                if host[-1] == '.':
                        return host
                
                fqdn = '.'.join(args)
                if fqdn[-1] != '.':
                        fqdn += '.'
                fqdn = fqdn.replace('@.','')
                return fqdn

        def _ttl (self,ttl,default=None):
                if option['export-bind']:
                        if ttl == default:
                                return ''

                if ttl == None:
                        if default == None:
                                return ''
                        else:
                                return default
                else:
                        return ttl


        def _make_path (self,name):
                if name[-1] == '.':
                        name = name[:-1]
                        
                if option['output-stdout'] == True:
                        path = '/dev/stdout'
                else:
                        path = os.path.join(option['tmp-directory'],name)

                return path
                        
        def _create (self,name):
                path = self._make_path(name)

                if self.path == path:
                        return

                try:
                        if self.file:
                                self.file.close()
                        
                        if option['output-stdout'] == True:
                                self.file = 
os.fdopen(os.dup(sys.stdout.fileno()),'w')
                        else:
                                self.file = open(path,'w')
                        self.path = path
                except:
                        err(1,'issue opening file for creation %s\n' % path)
                
        def _append (self,name):
                try:
                        if option['all-in-one']:
                                name = 'data'
                
                        path = self._make_path(name)

                        if self.path == path:
                                return

                        if self.file:
                                self.file.close()

                        if option['output-stdout'] == True:
                                self.file = 
os.fdopen(os.dup(sys.stdout.fileno()),'w')
                        else:
                                self.file = open(path,'a')
                        self.path = path
                
                except:
                        err(1,'issue opening file for creation')
                
        
        def process (self,zones = None):
                if zones == None:
                        if option['one-zone-only']:
                                sys.stderr.write('can only export one zone on 
stdout\n')
                                sys.exit(1)
                        self._process("")
                else:
                        for zone in zones:
                                select_and = "and zones.name = '%s'" % zone
                                self._process(select_and)

        def _process (self,select_and):
                actions = []
                actions.append((domain_query,select_and,self.SOA))
                actions.append((ns_query,select_and,self.NS))
                actions.append((mx_query,select_and,self.MX))
                actions.append((a_forward,select_and,self.A))
                actions.append((txt_query,select_and,self.TXT))
                
actions.append((internal_cname_query,select_and,self.CNAME_internal))
                
actions.append((external_cname_query,select_and,self.CNAME_external))
                
                if option['one-zone-only'] == False:
                        actions.append((domain_reverse,"",self.ARPA))
                        actions.append((a_reverse,"",self.REVERSE))

                for query,select,function in actions:
                        query = query % select
                        cursor = self.sql.cursor()
                        execution = cursor.execute(query)
                        row = cursor.fetchone ()
                        while row != None:
                                function(row)
                                row = cursor.fetchone ()

        
        def SOA(self,*args):
                value = args[0]

                name = value[0]
                ns = value[1]
                hostmaster = value[2]
                serial = value[3]
                refresh = self._ttl(value[4])
                retry = self._ttl(value[5])
                expire = self._ttl(value[6])
                minimum = self._ttl(value[7])
                ttl = self._ttl(value[8])

                name = self._fqdn(name)

                # XXX: IS THE TTL done correctly ? use the self._ttl function

                if option['export-tinydns']:
                        if option['all-in-one']:
                                self._append(name)
                        else:
                                self._create(name)
                        self.file.write('Z%s:%s:%s:%s:%s:%s:%s:%s::\n' % 
(name,ns,hostmaster,serial,refresh,retry,expire,minimum))
                if option['export-bind']:
                        zone_ttl = '' # Always blank in sauron
                        self._create(name)
                        # XXX: This is always IN as class in the DB for the 
moment, for every type of record
                        self.file.write('; %s - exa dns export tool version 
%s\n' % (name,'0.1'))
                        self.file.write('; generated by %s %s\n' % 
('thomas','around now'))
                        self.file.write(';\n')
                        self.file.write('$TTL %s; default TTL for this zone\n' 
% ttl)
                        self.file.write('$ORIGIN %s\n' % name)
                        self.file.write('@\t%s\t%s\tSOA\t%s %s (\n' % 
(zone_ttl,'IN',name,hostmaster))
                        self.file.write('\t\t\t\t\t%s\t; %s\n' % 
(refresh,'refresh'))
                        self.file.write('\t\t\t\t\t%s\t; %s\n' % 
(retry,'retry'))
                        self.file.write('\t\t\t\t\t%s\t; %s\n' % 
(expire,'expire'))
                        self.file.write('\t\t\t\t\t%s\t; %s\n' % 
(minimum,'minimum'))
                        self.file.write('\t\t\t\t)\n')

        def NS (self,*args):
                value = args[0]

                host = value[0]
                zone = value[1]
                ns = value[2]
                ttl = self._ttl(value[3],value[4])
                
                name = self._fqdn(host,zone)
                zone = self._fqdn(zone)
                ns = self._fqdn(ns)
                
                self._append(zone)
                
                if option['export-tinydns']:
                        self.file.write('&%s::%s:%s::\n' % (name,ns,ttl))

                if option['export-bind']:
                        self.file.write(BIND_LINE % ('',ttl,'IN','NS',ns))
                        

        def MX (self,*args):
                value = args[0]

                host = value[0]
                zone = value[1]
                destination = value[2]
                priority = value[3]
                ttl = self._ttl(value[4],value[5])

                name = self._fqdn(host,zone)
                zone = self._fqdn(zone)
                
                self._append(zone)

                if option['export-tinydns']:
                        self.file.write('@%s::%s:%s:%s::\n' % 
(name,destination,priority,ttl))
                        
                if option['export-bind']:
                        self.file.write(BIND_LINE % (name,ttl,'IN','MX %s' % 
priority,destination))
                
                
        def A (self,*args):
                # True for TTL in the file
                value = args[0]

                host = value[0]
                zone = value[1]
                ip = value[2]
                forward = value[3]
                ttl = self._ttl(value[4],value[5])
                type = value[6]

                name = self._fqdn(host,zone)
                zone = self._fqdn(zone)

                self._append(zone)

                if option['export-tinydns']:
                        self.file.write('+%s:%s:%s::\n' % (name,ip,ttl))

                if option['export-bind']:
                        self.file.write(BIND_LINE % (name,ttl,'IN','A',ip))

                        
        def TXT (self,*args):
                value = args[0]
                
                host = value[0]
                zone = value[1]
                string = value[2]
                ttl = self._ttl(value[3],value[4])

                name = self._fqdn(host,zone)
                zone = self._fqdn(zone)
                
                self._append(zone)

                if option['export-tinydns']:
                        string = string.replace(':','\\072')
                        string = string.replace('\t','\\011')
                        string = string.replace('\r','\\015')
                        string = string.replace('\n','\\012')
                        self.file.write("'%s:%s:%s::\n" % (name,string,ttl))
                        
                if option['export-bind']:
                        string = '"' + string + '"'
                        self.file.write(BIND_LINE % 
(name,ttl,'IN','TXT',string))


        def CNAME_external (self,*args):
                value = args[0]
                
                host = value[0]
                zone = value[1]
                cname = value[2]
                ttl = self._ttl(value[3],value[4])

                name = self._fqdn(host,zone)
                cname = self._fqdn(cname)
                zone = self._fqdn(zone)
                
                self._append(zone)

                if option['export-tinydns']:
                        self.file.write("C%s:%s:%s::\n" % (name,cname,ttl))

                if option['export-bind']:
                        self.file.write(BIND_LINE % 
(name,ttl,'IN','CNAME',cname))

        def CNAME_internal (self,*args):
                value = args[0]
                
                host = value[0]
                zone = value[1]
                cname_host = value[2]
                cname_zone = value[3]
                ttl = self._ttl(value[4],value[5])

                name = self._fqdn(host,zone)
                cname = self._fqdn(cname_host,cname_zone)
                zone = self._fqdn(zone)
                
                self._append(zone)

                if option['export-tinydns']:
                        self.file.write("C%s:%s:%s::\n" % (name,cname,ttl))

                if option['export-bind']:
                        self.file.write(BIND_LINE % 
(name,ttl,'IN','CNAME',cname))


        def ARPA (self,*args):
                value = args[0]

                name = value[0]
                ns = value[1]
                hostmaster = value[2]
                serial = value[3]
                refresh = self._ttl(value[4])
                retry = self._ttl(value[5])
                expire = self._ttl(value[6])
                minimum = self._ttl(value[7])
                ttl = self._ttl(value [8])

                name = self._fqdn(name)

                if option['export-tinydns']:
                        if option['all-in-one']:
                                self._append(name)
                        else:
                                self._create(name)
                        self.file.write('Z%s:%s:%s:%s:%s:%s:%s:%s::\n' % 
(name,ns,hostmaster,serial,refresh,retry,expire,minimum))


        def REVERSE (self,*args):
                # True for TTL in the file
                value = args[0]

                host = value[0]
                zone = value[1]
                ip = value[2]
                forward = value[3]
                ttl = self._ttl(value[4],value[5])
                type = value[6]

                name = self._fqdn(host,zone)
                zone = self._fqdn(zone)
                arpa = iptoarpa(ip)

                self._append(zone)

                if option['export-tinydns']:
                        self.file.write('^%s:%s:%s::\n' % (arpa,name,ttl))


export = ExportZone('127.0.0.1','dbuser','dbpassword','')
export.process(None)
#export.process(["workingtestzone.net"])
sys.exit(0)


_______________________________________________
swinog mailing list
swinog@lists.swinog.ch
http://lists.swinog.ch/cgi-bin/mailman/listinfo/swinog

Antwort per Email an