pvl/dns/zone.py
changeset 640 620d5a3beec4
parent 639 cc27c830a911
child 642 c25834508569
--- a/pvl/dns/zone.py	Fri Feb 27 14:06:03 2015 +0200
+++ b/pvl/dns/zone.py	Fri Feb 27 16:45:53 2015 +0200
@@ -54,7 +54,11 @@
         """
 
         for line in cls.parse(file) :
-            if line.parts[0].startswith('$'):
+            if not line.parts:
+                log.debug("%s: Skip empty line: %r", line, line)
+                continue
+
+            elif line.parts[0].startswith('$'):
                 # control directive
                 line_type = ZoneDirective
 
@@ -213,6 +217,19 @@
 
         return cls(directive, arguments, **opts)
 
+    @classmethod
+    def INCLUDE (cls, path, origin=None, **opts):
+        """
+            Build $INCLUDE "path" [origin]
+
+            Note that origin should be a FQDN, or it will be interpreted relative to the current origin!
+        """
+
+        if origin:
+            return cls.build('INCLUDE', zone_quote(path), origin, **opts)
+        else:
+            return cls.build('INCLUDE', zone_quote(path), **opts)
+
     def __init__ (self, directive, arguments, comment=None, line=None, origin=None):
         """
             directive       - uppercase directive name, withtout leading $
@@ -314,7 +331,11 @@
         name = None
 
         for line in ZoneLine.parse(file):
-            if line.parts[0].startswith('$'):
+            if not line.parts:
+                log.debug("%s: Skip empty line: %r", line, line)
+                continue
+
+            elif line.parts[0].startswith('$'):
                 directive = ZoneDirective.parse(line.parts,
                         origin      = origin,
                         line        = line,
@@ -326,14 +347,14 @@
                 if directive.directive == 'ORIGIN':
                     directive_origin, = directive.arguments
                     
-                    log.info("%s: $ORIGIN %s -> %s", file, origin, directive_origin)
+                    log.info("%s: $ORIGIN %s <- %s", line, directive_origin, origin)
                     
                     origin = pvl.dns.labels.join(origin, directive_origin)
 
                 elif directive.directive == 'TTL' :
                     directive_ttl, = directive.arguments
                     
-                    log.info("%s: $TTL %d -> %s", file, ttl, directive_ttl)
+                    log.info("%s: $TTL %s <- %s", line, directive_ttl, ttl)
                     
                     ttl = int(directive_ttl)
 
@@ -398,20 +419,23 @@
         _cls = None
 
         if parts and parts[0][0].isdigit() :
-            ttl = int(parts.pop(0))
+            ttl = parts.pop(0)
 
         if parts and parts[0].upper() in ('IN', 'CH') :
-            _cls = parts.pop(0).upper()
+            _cls = parts.pop(0)
 
         # always have type
-        type = parts.pop(0).upper()
+        type = parts.pop(0)
 
         # remaining parts are data
         data = parts
 
         log.debug("  ttl=%r, cls=%r, type=%r, data=%r", ttl, _cls, type, data)
 
-        return cls(name, ttl, _cls, type, data, line=line, **opts)
+        # optional subclass for build()
+        cls = ZONE_RECORD_TYPES.get(type.upper(), cls)
+
+        return cls.build(name, type, *data, ttl=ttl, cls=_cls, line=line, **opts)
     
     @classmethod
     def build (_cls, name, type, *data, **opts):
@@ -536,29 +560,32 @@
             self.name, self.ttl, self.cls, self.type, self.data
         )))
 
-class SOA (ZoneRecord) :
+class ZoneRecordSOA (ZoneRecord):
+    """
+        Specialized SOA record.
+    """
+
     @classmethod
-    def build (cls, line, name, ttl, _cls, type, data, **opts) :
-        assert name == '@'
+    def build (_cls, name, type,
+            master, contact, serial, refresh, retry, expire, nxttl, 
+            line=None,
+            **opts
+    ):
+        assert type == 'SOA'
 
-        return cls(*data,
-            ttl     = ttl,
-            cls     = cls,
-            line    = line,
+        if name != '@':
+            raise ZoneLineError(line, "SOA should be @: {name}", name=name)
+                
+        return super(ZoneRecordSOA, _cls).build('@', 'SOA', 
+            master, contact, serial, refresh, retry, expire, nxttl,
             **opts
         )
 
-    def __init__ (self, master, contact, serial, refresh, retry, expire, nxttl, **opts) :
-        super(SOA, self).__init__('@', 'SOA',
-            [master, contact, serial, refresh, retry, expire, nxttl],
-            **opts
-        )
+    def __init__ (self, name, ttl, cls, type, data, **opts):
+        super(ZoneRecordSOA, self).__init__(name, ttl, cls, type, data, **opts)
 
-        self.master = master
-        self.contact = contact
-        self.serial = serial
-        self.refresh = refresh
-        self.retry = retry
-        self.expire = expire
-        self.nxttl = nxttl
+        self.master, self.contact, self.serial, self.refresh, self.retry, self.expire, self.nxttl = data
 
+ZONE_RECORD_TYPES = {
+    'SOA':      ZoneRecordSOA,
+}