pvl/config/bind/zone.py
changeset 9 2156906bfbf1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/bind/zone.py	Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,355 @@
+"""
+    Configuration file output for the ISC DNS server: BIND
+"""
+
+from .. import file
+
+class Item (file.Item) :
+    """
+        Item that knows how to format comments
+    """
+
+    COMMENT_PREFIX = ';'
+
+class Zone (Item, file.Contents) :
+    """
+        A zone containing a bunch of directives, resource records and comments
+    """
+
+    def __init__ (self, ttl=None, comment=None) :
+        """
+            @param name the name of the zonefile, for status stuff
+            @param path the path to the zonefile
+            @param ttl default TTL to use
+        """
+        
+        Item.__init__(self, [comment])
+        file.Contents.__init__(self)
+
+        if ttl :
+            self.add(TTLDirective(ttl))
+    
+class Comment (Item) :
+    """
+        A comment, is, well, a comment :)
+    """
+
+    def __init__ (self, comment) :
+        """
+            @param comment the comment string
+        """
+
+        Item.__init__(self, [comment])
+    
+    # only need to iterate over our comments
+    iter_lines = Item.iter_comments
+
+class Label (object) :
+    """
+        A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types
+
+        You can also use strs, this just implements a __str__ method
+    """
+
+    def __init__ (self, label) :
+        """
+            @param label the literal label to use
+        """
+
+        self.label = label
+
+    def __str__ (self) :
+        return self.label
+
+class Origin (Label) :
+    """
+        A label that represents the zone's origin
+    """
+
+    def __init__ (self) :
+        pass
+
+    def __str__ (self) :
+        return '@'
+
+class FQDN (Label) :
+    """
+        A label that represents the given external domain (i.e. this adds the . to the end that people always forget).
+    """
+
+    def __init__ (self, fqdn) :
+        self.fqdn = fqdn
+
+    def __str__ (self) :
+        return "%s." % (self.fqdn, )
+
+class Interval (object) :
+    """
+        A time interval suitable for use in SOA records
+    """
+
+    def __init__ (self, s=None, m=None, h=None, d=None) :
+        """
+            @param s seconds
+            @param m minutes
+            @param h hours
+            @param d days
+        """
+
+        self.s = s
+        self.m = m
+        self.h = h
+        self.d = d
+
+    def __str__ (self) :
+        """
+            If only seconds were given, just return those directly, otherwise, apply units
+        """
+
+        if self.s and not self.m and not self.h and not self.d :
+            return str(self.s)
+
+        else :
+            return "%s%s%s%s" % (
+                    "%ds" % self.s if self.s else '',
+                    "%dm" % self.m if self.m else '',
+                    "%dh" % self.h if self.h else '',
+                    "%dd" % self.d if self.d else ''
+                )
+
+class ResourceRecord (Item) :
+    """
+        A generic resource record for a BIND zone file
+    """
+
+    def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) :
+        """
+            @param label the "name" of this record, or None to referr to the previous record's label
+            @param type the type as a string ('A', 'TXT', etc.)
+            @param rdata the rdata, as a raw string
+            @param cls the class, e.g. 'IN'
+            @param ttl the time-to-live value in seconds, or None to omit it
+        """
+
+        super(ResourceRecord, self).__init__(**kwargs)
+
+        self.label = label
+        self.type = type
+        self.rdata = rdata
+        self.cls = cls
+        self.ttl = ttl
+
+    def iter_lines (self) :
+        """
+            Just format the lines, eh
+        """
+        
+        # prefix comments
+        for line in self.iter_comments() :
+            yield line
+
+        # then format the line
+        # XXX: TTL?
+        yield "%-30s %-4s%-4s %-8s %s" % (self.label if self.label is not None else '', str(self.ttl) if self.ttl else '', self.cls, self.type, self.rdata)
+
+class SOA (ResourceRecord) :
+    """
+        "Identifies the start of a zone of authority", must be the first record
+    """
+
+    def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) :
+        """
+            @param label the "name" of the zone, usually ORIGIN
+            @param primary_ns the address of the primary NS server
+            @param hostmaster the mailbox of the zone's hostmaster
+            @param serial the serial number of the zone as an integer
+            @param refresh time interval between zone refreshes in seconds
+            @param retry time interval between retrys for failed refreshes
+            @param expire time interval before zone data can no longer be considered authorative
+            @param minimum minimum TTL for RRs in this zone
+        """
+
+        super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % (
+                primary_ns, hostmaster, serial, refresh, retry, expire, minimum
+            ), **kwargs)
+
+class A (ResourceRecord) :
+    """
+        An IPv4 forward address
+    """
+
+    def __init__ (self, label, addr, **kwargs) :
+        """
+            @param label the "name" of the address
+            @param addr the IPv4 target address
+        """
+
+        assert(addr.is_v4())
+
+        super(A, self).__init__(label, 'A', addr, **kwargs)
+
+class AAAA (ResourceRecord) :
+    """
+        An IPv6 forward address
+    """
+
+    def __init__ (self, label, addr, **kwargs) :
+        """
+            @param label the "name" of the address
+            @param addr the IPv6 target address
+        """
+        
+        assert(addr.is_v6())
+
+        super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs)
+
+class CNAME (ResourceRecord) :
+    """
+        A canonical-name alias
+    """
+
+    def __init__ (self, label, target, **kwargs) :
+        """
+            @param label the "name" of the alias
+            @param target the alias target
+        """
+
+        super(CNAME, self).__init__(label, 'CNAME', target, **kwargs)
+
+class TXT (ResourceRecord) :
+    """
+        A human-readable information record
+    """
+
+    def __init__ (self, label, text, **kwargs) :
+        """
+            @param label the "name" of the text record
+            @param text the text data, shouldn't contain any quotes...
+        """
+        
+        # XXX: escaping?
+        super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs)
+
+class MX (ResourceRecord) :
+    """
+        A mail-exchange definition
+    """
+
+    def __init__ (self, label, pref, exchange, **kwargs) :
+        """
+            @param label the "name" of the domain to handle mail for
+            @param pref the numerical preference for this exchange
+            @param exchange the domain name of the mail exchange (SMTP server)
+        """
+
+        super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs)
+
+class NS (ResourceRecord) :
+    """
+        An authorative name server
+    """
+
+    def __init__ (self, label, nsname, **kwargs) :
+        """
+            @param label the "name" of the domain to have a nameserver for
+            @param nsname the name of the nameserver
+        """
+
+        super(NS, self).__init__(label, 'NS', nsname)
+
+class PTR (ResourceRecord) :
+    """
+        An IPv4/IPv6 reverse address
+    """
+
+    def __init__ (self, addr, name, **kwargs) :
+        """
+            @param addr the addr.IP to map via in-addr.arpa
+            @param name the name to map the address to
+        """
+
+        # XXX: quick hack, this gives an absolute name
+        label = addr.reverseName()
+        
+        super(PTR, self).__init__(label, 'PTR', name)
+
+class Directive (Item) :
+    """
+        Special directives that can be used in zone files to control behaviour
+    """
+
+    def __init__ (self, name, *args, **kwargs) :
+        """
+            @param name the $NAME bit
+            @param args optional list of space-seprated arguments, Nones are ignored
+        """
+
+        super(Directive, self).__init__(**kwargs)
+
+        self.name = name
+        self.args = [arg for arg in args if arg is not None]
+
+    def iter_lines (self) :
+        # prefix comments
+        for line in self.iter_comments() :
+            yield line
+
+        # then format it
+        yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '')
+
+class OriginDirective (Directive) :
+    """
+        Set the origin used to resolve the zone's labels.
+
+        Note that the origin label is not absolute by default - use FQDN for that
+    """
+
+    def __init__ (self, origin, **kwargs) :
+        """
+            @param origin the origin label
+        """
+
+        super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs)
+
+class TTLDirective (Directive) :
+    """
+        Set the TTL used for records by default
+    """
+
+    def __init__ (self, ttl, **kwargs) :
+        """
+            @param ttl the new ttl to use
+        """
+
+        super(TTLDirective, self).__init__('TTL', ttl, **kwargs)
+
+class IncludeDirective (Directive) :
+    """
+        Include another zoen file, optionally with a different origin
+    """
+
+    def __init__ (self, filename, origin=None, **kwargs) :
+        """
+            @param filename the zone file to include
+            @param origin the optional origin to process the zonefile with
+        """
+
+        super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs)
+
+class GenerateDirective (Directive) :
+    """
+        Generate a bunch of numbered records using an expression for the label and rdata.
+
+        At the simplest, any "$" in the expression is replaced with the value of the iterator.
+    """
+
+    def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) :
+        """
+            @param range (start, stop, step) tuple
+            @param lhs expression to generate the label
+            @param type the resource record type
+            @param rhs expression to generate the rdata
+        """
+
+        super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs)
+