[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