diff -r 8b633782f02d -r 86b05c0ab5cd bind.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bind.py Thu Apr 02 23:59:31 2009 +0300 @@ -0,0 +1,187 @@ +""" + High-level BIND stuff +""" + +from __future__ import with_statement + +import bind_conf as bindc + +import os.path, datetime + +DEFAULT_TTL = bindc.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 (bindc.ZoneFile) : + """ + 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(bindc.TTLDirective(settings.ttl)) + + # the SOA record + self.add_record(bindc.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(bindc.NS(None, label)) + + # the MX records + for pref, label in mailservers : + self.add_record(bindc.MX(None, pref, label)) + + # include? + if include : + self.add_directive(bindc.IncludeDirective(include)) + + if objs : + for obj in objs : + self.add_obj(obj)