pvl/config/bind/model.py
changeset 9 2156906bfbf1
parent 7 0f9cae2d7147
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/config/bind/model.py	Sun Jul 12 02:14:34 2009 +0300
@@ -0,0 +1,188 @@
+"""
+    High-level BIND stuff
+"""
+
+from __future__ import with_statement
+
+from . import zone
+
+import os.path, datetime
+
+DEFAULT_TTL = zone.Interval(3600)
+
+class Settings (object) :
+    """
+        A set of basic settings for a zone, mostly default TTL/refresh/retry/expire/minimum settings
+    """
+
+    def __init__ (self, ttl, hostmaster, refresh, retry, expire, minimum) :
+        self.ttl = ttl
+        self.hostmaster = hostmaster
+        self.refresh = refresh
+        self.retry = retry
+        self.expire = expire
+        self.minimum = minimum
+
+class AutoSerial (object) :
+    """
+        Automatically generate the next serial to use by loading it from a file.
+
+        The generated serials are in YYYYMMDDXX format.
+    """
+
+    def __init__ (self, path) :
+        """
+            Load the current serial 
+
+            @param path the path to the serial file
+        """
+        
+        # store
+        self.path = path
+            
+        # load it
+        # XXX: locking
+        serial = self.read()
+        
+        # current date
+        today = datetime.date.today()
+
+        # parse it
+        if serial :
+            date, code = self.parse(serial)
+
+        else :
+            date, code = today, 0
+        
+        # increment it
+        date, code = self.next(date, code)
+
+        # format it
+        self._serial = self.build(date, code)
+
+        # write it out
+        self.write(self._serial)
+
+    def parse (self, serial) :
+        """
+            Parse the given serial into a (datetime.date, code) format
+        """
+        
+        # build it into a date
+        date = datetime.date(
+                year    = int(serial[0:4]),
+                month   = int(serial[4:6]),
+                day     = int(serial[6:8])
+            )
+
+        code = int(serial[8:])
+
+        return date, code
+   
+    def next (self, date, code) :
+        """
+            Return the next valid serial following the given one
+        """
+        
+        # current date
+        today = datetime.date.today()
+
+        # now to increment?
+        if date < today :
+            # jump to today's first serial
+            date = today
+            code = 0
+        
+        else :
+            # today or overflowed into the future, just increment the code
+            code += 1
+        
+        # code overflowed into next day?
+        if code > 99 :
+            date += datetime.timedelta(days=1)
+            code = 0
+
+        # ok
+        return date, code
+    
+    def build (self, date, code) :
+        """
+            Build a serial code the given date/code
+        """
+
+        assert 0 <= code <= 99
+
+        return "%s%02d" % (date.strftime("%Y%m%d"), code)
+
+    def read (self) :
+        """
+            Read the current serial, returning it, or None, if not found...
+        """
+        
+        # if it doesn't exist, default
+        if not os.path.exists(self.path) :
+            return None
+        
+        # read it
+        with open(self.path) as fh :
+            return fh.read().strip()
+        
+    def write (self, serial) :
+        """
+            Write a new serial
+        """
+
+        with open(self.path, 'w') as fh :
+            fh.write("%s\n" % (serial, ))
+    
+    def serial (self) :
+        """
+            Return a new, unused serial code (before __init__)
+        """
+
+        return self._serial
+
+class Domain (zone.Zone) :
+    """
+        A domain has a skeleton of stuff defined, but the rest is $INCLUDE'd from elsewhere, which is useful for
+        multi-domain setups where the domains are mostly similar
+    """
+
+    def __init__ (self, domain, path, nameservers, mailservers, serial, settings, include=None, objs=None) :
+        """
+            @param domain the domain name
+            @param path the path to the zone file
+            @param nameservers list of nameservers as labels
+            @param mailservers list of (pref, label) tuples for MX records
+            @param serial the serial code to use
+            @param settings the TTL/SOA settings to use
+            @param include the optional zonefile to include
+            @param objs the optional other objects to add to the zonefile
+        """
+
+        super(Domain, self).__init__(domain, path)
+        
+        # the default TTL
+        self.add_directive(zone.TTLDirective(settings.ttl))
+
+        # the SOA record
+        self.add_record(zone.SOA(None, nameservers[0], 
+                settings.hostmaster, serial, settings.refresh, settings.retry, settings.expire, settings.minimum
+            ))
+
+        # the NS records
+        for label in nameservers :
+            self.add_record(zone.NS(None, label))
+
+        # the MX records
+        for pref, label in mailservers :
+            self.add_record(zone.MX(None, pref, label))
+        
+        # include?
+        if include :
+            self.add_directive(zone.IncludeDirective(include))
+
+        if objs :
+            for obj in objs :
+                self.add_obj(obj)
+