[svn] commit: r1895 - in /branches/tingting-loadzone/src: bin/loadzone/b10-loadzone.py.in lib/python/isc/datasrc/master.py
BIND 10 source code commits
bind10-changes at lists.isc.org
Fri May 21 09:28:10 UTC 2010
Author: shentingting
Date: Fri May 21 09:28:10 2010
New Revision: 1895
Log:
1. Fix bug for task 25(to not assume "." is origin if origin is not set and sees a @ or a label without a ".". ).
Modified:
branches/tingting-loadzone/src/bin/loadzone/b10-loadzone.py.in
branches/tingting-loadzone/src/lib/python/isc/datasrc/master.py
Modified: branches/tingting-loadzone/src/bin/loadzone/b10-loadzone.py.in
==============================================================================
--- branches/tingting-loadzone/src/bin/loadzone/b10-loadzone.py.in (original)
+++ branches/tingting-loadzone/src/bin/loadzone/b10-loadzone.py.in Fri May 21 09:28:10 2010
@@ -19,7 +19,8 @@
import re, getopt
import isc.datasrc
from isc.datasrc.master import MasterFile
-
+import time
+import os
#########################################################################
# usage: print usage note and exit
#########################################################################
@@ -57,23 +58,34 @@
if len(args) != 1:
usage()
zonefile = args[0]
+ verbose = os.isatty(sys.stdout.fileno())
try:
- master = MasterFile(zonefile, initial_origin)
+ master = MasterFile(zonefile, initial_origin, verbose)
except Exception as e:
- print("Error reading zone file: " + str(e))
+ sys.stderr.write("Error reading zone file: %s\n" % str(e))
exit(1)
try:
zone = master.zonename()
+ if verbose:
+ sys.stdout.write("Using SQLite3 database file %s\n" % dbfile)
+ sys.stdout.write("Zone name is %s\n" % zone)
+ sys.stdout.write("Loading file \"%s\"" % zonefile)
+ master.verbose()
except Exception as e:
- print("Error reading zone file: " + str(e))
+ sys.stdout.write("\n")
+ sys.stderr.write("Error reading zone file: %s\n" % str(e))
exit(1)
try:
isc.datasrc.sqlite3_ds.load(dbfile, zone, master.zonedata)
+ if verbose:
+ master.closeverbose()
+ sys.stdout.write("\nCommitting changes... done.\n")
except Exception as e:
- print("Error loading database: " + str(e))
+ sys.stdout.write("\n")
+ sys.stderr.write("Error loading database: %s\n"% str(e))
exit(1)
if __name__ == "__main__":
Modified: branches/tingting-loadzone/src/lib/python/isc/datasrc/master.py
==============================================================================
--- branches/tingting-loadzone/src/lib/python/isc/datasrc/master.py (original)
+++ branches/tingting-loadzone/src/lib/python/isc/datasrc/master.py Fri May 21 09:28:10 2010
@@ -16,7 +16,9 @@
# $Id$
import sys, re, string
-
+import time
+import os
+import threading
#########################################################################
# define exceptions
#########################################################################
@@ -147,7 +149,9 @@
record = []
complete = True
paren = 0
+ size = 0
for line in input:
+ size += len(line)
list = cleanup(line).split()
for word in list:
if paren == 0:
@@ -169,10 +173,12 @@
if paren == 1 or not record:
continue
-
+
ret = ' '.join(record)
record = []
- yield ret
+ oldsize = size
+ size = 0
+ yield ret, oldsize
#########################################################################
# define the MasterFile class for reading zone master files
@@ -181,24 +187,54 @@
__rrclass = 'IN'
__maxttl = 0x7fffffff
__ttl = ''
+ __lastttl= ''
__zonefile = ''
__name = ''
-
- def __init__(self, filename, initial_origin = ''):
- if initial_origin == '.':
- initial_origin = ''
+ __file_level = 0
+ __file_type = ""
+ __init_time = time.time()
+ __records_num = 0
+
+ def __init__(self, filename, initial_origin = '', verbose = False):
self.__initial_origin = initial_origin
self.__origin = initial_origin
+ self.__datafile = filename
+ self.__filesize = os.stat(filename).st_size
+ self.__cur = 0
+ self.__numback = 0
+ self.__verbose = verbose
try:
self.__zonefile = open(filename, 'r')
except:
raise MasterFileError("Could not open " + filename)
+ def __status(self):
+ interval = time.time() - MasterFile.__init_time
+ percent = (self.__cur * 100)/self.__filesize
+ sys.stdout.write("\r%s" % (80 * " "))
+ sys.stdout.write("\r%d RR(s) loaded in %d second(s)(%.2f%% of %s%s)"\
+ % (MasterFile.__records_num, interval, percent, MasterFile.__file_type, self.__datafile))
+ self.__t = threading.Timer(1.0, self.__status)
+ self.__t.start()
+
def __del__(self):
if self.__zonefile:
self.__zonefile.close()
-
- #########################################################################
+ ########################################################################
+ # check if the zonename is relative
+ # no then return
+ # yes , sets the relative domain name to the stated name
+ #######################################################################
+ def __statedname(self, name, record):
+ if name[-1] != '.':
+ if not self.__origin:
+ raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
+ elif self.__origin == '.':
+ name += '.'
+ else:
+ name += '.' + self.__origin
+ return name
+ #####################################################################
# handle $ORIGIN, $TTL and $GENERATE directives
# (currently only $ORIGIN and $TTL are implemented)
# input:
@@ -216,20 +252,22 @@
raise MasterFileError('Invalid $ORIGIN')
if more:
raise MasterFileError('Invalid $ORIGIN')
- if second == '.':
- self.__origin = ''
- elif second[-1] == '.':
+ if second[-1] == '.':
self.__origin = second
+ elif not self.__origin:
+ raise MasterFileError("$ORIGIN is not absolute in record:%s" % s)
+ elif self.__origin != '.':
+ self.__origin = second + '.' + self.__origin
else:
- self.__origin = second + '.' + self.__origin
+ self.__origin = second + '.'
return True
elif re.match('\$ttl', first, re.I):
if not second or not isttl(second):
raise MasterFileError('Invalid TTL: "' + second + '"')
if more:
raise MasterFileError('Invalid $TTL statement')
- self.__ttl = parse_ttl(second)
- if int(self.__ttl) > self.__maxttl:
+ MasterFile.__ttl = parse_ttl(second)
+ if int(MasterFile.__ttl) > self.__maxttl:
raise MasterFileError('TTL too high: ' + second)
return True
elif re.match('\$generate', first, re.I):
@@ -248,12 +286,21 @@
#########################################################################
__filename = re.compile('[\"\']*([^\'\"]+)[\"\']*')
def __include(self, s):
- first, rest = pop(s)
- if re.match('\$include', first, re.I):
- m = self.__filename.match(rest)
+ list = s.split()
+ file, origin = "", ""
+ if re.match('\$include', list[0], re.I):
+ if len(list) != 2 and len(list) != 3:
+ raise MasterFileError('Invalid $include formal')
+ m = self.__filename.match(list[1])
if m:
file = m.group(1)
- return file
+ if len(list) == 3:
+ if not isname(list[2]):
+ raise MasterFileError('Invalid $include formal')
+ origin = self.__statedname(list[2], s)
+ if origin == "":
+ origin = self.__origin
+ return file, origin
#########################################################################
# try parsing an RR on the assumption that the type is specified in
@@ -272,6 +319,8 @@
if istype(list[3]):
if isclass(list[2]) and isttl(list[1]) and isname(list[0]):
name, ttl, rrclass, rrtype = list[0:4]
+ ttl = parse_ttl(ttl)
+ MasterFile.__lastttl = ttl or MasterFile.__lastttl
rdata = ' '.join(list[4:])
ret = name, ttl, rrclass, rrtype, rdata
return ret
@@ -292,15 +341,17 @@
if istype(list[2]) and not istype(list[1]):
if isclass(list[1]) and not isttl(list[0]) and isname(list[0]):
rrclass = list[1]
- ttl = self.__ttl
+ ttl = MasterFile.__ttl or MasterFile.__lastttl
name = list[0]
elif not isclass(list[1]) and isttl(list[1]) and isname(list[0]):
rrclass = self.__rrclass
ttl = parse_ttl(list[1])
+ MasterFile.__lastttl = ttl or MasterFile.__lastttl
name = list[0]
elif curname and isclass(list[1]) and isttl(list[0]):
rrclass = self.__rrclass
ttl = parse_ttl(list[0])
+ MasterFile.__lastttl = ttl or MasterFile.__lastttl
name = curname
else:
return ret
@@ -325,13 +376,12 @@
list = record.split()
if len(list) <= 2:
return ret
-
if istype(list[1]):
rrclass = self.__rrclass
rrtype = list[1]
if list[0].lower() == 'rrsig':
name = curname
- ttl = self.__ttl
+ ttl = MasterFile.__ttl or MasterFile.__lastttl
rrtype = list[0]
rdata = ' '.join(list[1:])
elif isttl(list[0]):
@@ -340,14 +390,34 @@
rdata = ' '.join(list[2:])
elif isname(list[0]):
name = list[0]
- ttl = self.__ttl
+ ttl = MasterFile.__ttl or MasterFile.__lastttl
rdata = ' '.join(list[2:])
else:
raise MasterFileError("Cannot parse RR: " + record)
ret = name, ttl, rrclass, rrtype, rdata
-
return ret
+ ########################################################################
+ # print status information, the info include:
+ # filename
+ # datasource location
+ # the count of rrdata
+ #######################################################################
+ def verbose(self):
+ self.__t = threading.Timer(0.0, self.__status)
+ self.__t.daemon = True
+ self.__t.start()
+
+ ########################################################################
+ #close timer
+ ######################################################################
+ def closeverbose(self):
+ interval = time.time() - MasterFile.__init_time
+ percent = (self.__cur * 100)/self.__filesize
+ sys.stdout.write("\r%s" % (80 * " "))
+ sys.stdout.write("\r%d RR(s) loaded in %.2f second(s)(%.2f%% of %s%s)"\
+ % (MasterFile.__records_num, interval, percent, MasterFile.__file_type, self.__datafile))
+ self.__t.cancel()
#########################################################################
# zonedata: generator function to parse a zone master file and return
@@ -356,15 +426,28 @@
def zonedata(self):
name = ''
- for record in records(self.__zonefile):
+ for record, size in records(self.__zonefile):
+ self.__cur += size
if self.__directive(record):
continue
- incl = self.__include(record)
+ incl, suborigin = self.__include(record)
if incl:
- sub = MasterFile(incl, self.__origin)
- for name, ttl, rrclass, rrtype, rdata in sub.zonedata():
- yield (name, ttl, rrclass, rrtype, rdata)
+ percent = (self.__cur * 100)/self.__filesize
+ sys.stdout.write("\r%s" % (80 * " "))
+ sys.stdout.write("\rincluding \"%s\" from \"%s\"\n" % (incl, self.__datafile))
+ MasterFile.__file_level += 1
+ MasterFile.__file_type = "included "
+ sub = MasterFile(incl, suborigin, self.__verbose)
+ if self.__verbose:
+ sub.verbose()
+ for rrname, ttl, rrclass, rrtype, rdata in sub.zonedata():
+ yield (rrname, ttl, rrclass, rrtype, rdata)
+ if self.__verbose:
+ sub.closeverbose()
+ MasterFile.__file_level -= 1
+ if MasterFile.__file_level == 0:
+ MasterFile.__file_type = ""
del sub
continue
@@ -373,7 +456,7 @@
if rl[0] == '@':
rl[0] = self.__origin
if not self.__origin:
- rl[0] = '.'
+ raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
record = ' '.join(rl)
result = self.__four(record, name)
@@ -387,36 +470,40 @@
if not result:
first, rdata = pop(record)
if istype(first):
- result = name, self.__ttl, self.__rrclass, first, rdata
+ result = name, MasterFile.__ttl or MasterFile.__lastttl, self.__rrclass, first, rdata
if not result:
raise MasterFileError("Cannot parse RR: " + record)
name, ttl, rrclass, rrtype, rdata = result
- if name[-1] != '.':
- name += '.' + self.__origin
+ name = self.__statedname(name, record)
if rrclass.lower() != 'in':
raise MasterFileError("CH and HS zones not supported")
- if not ttl:
- raise MasterFileError("No TTL specified; zone rejected")
+# if not ttl:
+# raise MasterFileError("No TTL specified; zone rejected")
# add origin to rdata containing names, if necessary
if rrtype.lower() in ('cname', 'dname', 'ns', 'ptr'):
if not isname(rdata):
raise MasterFileError("Invalid " + rrtype + ": " + rdata)
- if rdata[-1] != '.':
- rdata += '.' + self.__origin
+ rdata = self.__statedname(rdata, record)
+
if rrtype.lower() == 'soa':
soa = rdata.split()
if len(soa) < 2 or not isname(soa[0]) or not isname(soa[1]):
raise MasterFileError("Invalid " + rrtype + ": " + rdata)
- if soa[0][-1] != '.':
- soa[0] += '.' + self.__origin
- if soa[1][-1] != '.':
- soa[1] += '.' + self.__origin
+ soa[0] = self.__statedname(soa[0], record)
+ soa[1] = self.__statedname(soa[1], record)
+ if not MasterFile.__ttl and not ttl:
+ MasterFile.__ttl = MasterFile.__ttl or parse_ttl(soa[-1])
+ ttl = MasterFile.__ttl
rdata = ' '.join(soa)
+
+ if not ttl:
+ raise MasterFileError("No TTL specified; zone rejected")
+
if rrtype.lower() == 'mx':
mx = rdata.split()
if len(mx) != 2 or not isname(mx[1]):
@@ -424,7 +511,7 @@
if mx[1][-1] != '.':
mx[1] += '.' + self.__origin
rdata = ' '.join(mx)
-
+ MasterFile.__records_num += 1
yield (name, ttl, rrclass, rrtype, rdata)
#########################################################################
@@ -436,6 +523,7 @@
return self.__name
old_origin = self.__origin
self.__origin = self.__initial_origin
+ cur_value = self.__cur
old_location = self.__zonefile.tell()
self.__zonefile.seek(0)
for name, ttl, rrclass, rrtype, rdata in self.zonedata():
@@ -443,6 +531,7 @@
break
self.__zonefile.seek(old_location)
self.__origin = old_origin
+ self.__cur = cur_value
if rrtype.lower() != 'soa':
raise MasterFileError("No SOA found")
self.__name = name
@@ -454,7 +543,8 @@
def reset(self):
self.__zonefile.seek(0)
self.__origin = self.__initial_origin
- self.__ttl = ''
+ MasterFile.__ttl = ''
+ MasterFile.__lastttl = ''
#########################################################################
# main: used for testing; parse a zone file and print out each record
More information about the bind10-changes
mailing list