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