bind.py
changeset 5 86b05c0ab5cd
equal deleted inserted replaced
4:8b633782f02d 5:86b05c0ab5cd
       
     1 """
       
     2     High-level BIND stuff
       
     3 """
       
     4 
       
     5 from __future__ import with_statement
       
     6 
       
     7 import bind_conf as bindc
       
     8 
       
     9 import os.path, datetime
       
    10 
       
    11 DEFAULT_TTL = bindc.Interval(3600)
       
    12 
       
    13 class Settings (object) :
       
    14     """
       
    15         A set of basic settings for a zone, mostly default TTL/refresh/retry/expire/minimum settings
       
    16     """
       
    17 
       
    18     def __init__ (self, ttl, hostmaster, refresh, retry, expire, minimum) :
       
    19         self.ttl = ttl
       
    20         self.hostmaster = hostmaster
       
    21         self.refresh = refresh
       
    22         self.retry = retry
       
    23         self.expire = expire
       
    24         self.minimum = minimum
       
    25 
       
    26 class AutoSerial (object) :
       
    27     """
       
    28         Automatically generate the next serial to use by loading it from a file.
       
    29 
       
    30         The generated serials are in YYYYMMDDXX format.
       
    31     """
       
    32 
       
    33     def __init__ (self, path) :
       
    34         """
       
    35             Load the current serial 
       
    36 
       
    37             @param path the path to the serial file
       
    38         """
       
    39         
       
    40         # store
       
    41         self.path = path
       
    42             
       
    43         # load it
       
    44         # XXX: locking
       
    45         serial = self.read()
       
    46         
       
    47         # current date
       
    48         today = datetime.date.today()
       
    49 
       
    50         # parse it
       
    51         if serial :
       
    52             date, code = self.parse(serial)
       
    53 
       
    54         else :
       
    55             date, code = today, 0
       
    56         
       
    57         # increment it
       
    58         date, code = self.next(date, code)
       
    59 
       
    60         # format it
       
    61         self._serial = self.build(date, code)
       
    62 
       
    63         # write it out
       
    64         self.write(self._serial)
       
    65 
       
    66     def parse (self, serial) :
       
    67         """
       
    68             Parse the given serial into a (datetime.date, code) format
       
    69         """
       
    70         
       
    71         # build it into a date
       
    72         date = datetime.date(
       
    73                 year    = int(serial[0:4]),
       
    74                 month   = int(serial[4:6]),
       
    75                 day     = int(serial[6:8])
       
    76             )
       
    77 
       
    78         code = int(serial[8:])
       
    79 
       
    80         return date, code
       
    81    
       
    82     def next (self, date, code) :
       
    83         """
       
    84             Return the next valid serial following the given one
       
    85         """
       
    86         
       
    87         # current date
       
    88         today = datetime.date.today()
       
    89 
       
    90         # now to increment?
       
    91         if date < today :
       
    92             # jump to today's first serial
       
    93             date = today
       
    94             code = 0
       
    95         
       
    96         else :
       
    97             # today or overflowed into the future, just increment the code
       
    98             code += 1
       
    99         
       
   100         # code overflowed into next day?
       
   101         if code > 99 :
       
   102             date += datetime.timedelta(days=1)
       
   103             code = 0
       
   104 
       
   105         # ok
       
   106         return date, code
       
   107     
       
   108     def build (self, date, code) :
       
   109         """
       
   110             Build a serial code the given date/code
       
   111         """
       
   112 
       
   113         assert 0 <= code <= 99
       
   114 
       
   115         return "%s%02d" % (date.strftime("%Y%m%d"), code)
       
   116 
       
   117     def read (self) :
       
   118         """
       
   119             Read the current serial, returning it, or None, if not found...
       
   120         """
       
   121         
       
   122         # if it doesn't exist, default
       
   123         if not os.path.exists(self.path) :
       
   124             return None
       
   125         
       
   126         # read it
       
   127         with open(self.path) as fh :
       
   128             return fh.read().strip()
       
   129         
       
   130     def write (self, serial) :
       
   131         """
       
   132             Write a new serial
       
   133         """
       
   134 
       
   135         with open(self.path, 'w') as fh :
       
   136             fh.write("%s\n" % (serial, ))
       
   137     
       
   138     def serial (self) :
       
   139         """
       
   140             Return a new, unused serial code (before __init__)
       
   141         """
       
   142 
       
   143         return self._serial
       
   144 
       
   145 class Domain (bindc.ZoneFile) :
       
   146     """
       
   147         A domain has a skeleton of stuff defined, but the rest is $INCLUDE'd from elsewhere, which is useful for
       
   148         multi-domain setups where the domains are mostly similar
       
   149     """
       
   150 
       
   151     def __init__ (self, domain, path, nameservers, mailservers, serial, settings, include=None, objs=None) :
       
   152         """
       
   153             @param domain the domain name
       
   154             @param path the path to the zone file
       
   155             @param nameservers list of nameservers as labels
       
   156             @param mailservers list of (pref, label) tuples for MX records
       
   157             @param serial the serial code to use
       
   158             @param settings the TTL/SOA settings to use
       
   159             @param include the optional zonefile to include
       
   160             @param objs the optional other objects to add to the zonefile
       
   161         """
       
   162 
       
   163         super(Domain, self).__init__(domain, path)
       
   164         
       
   165         # the default TTL
       
   166         self.add_directive(bindc.TTLDirective(settings.ttl))
       
   167 
       
   168         # the SOA record
       
   169         self.add_record(bindc.SOA(None, nameservers[0], 
       
   170                 settings.hostmaster, serial, settings.refresh, settings.retry, settings.expire, settings.minimum
       
   171             ))
       
   172 
       
   173         # the NS records
       
   174         for label in nameservers :
       
   175             self.add_record(bindc.NS(None, label))
       
   176 
       
   177         # the MX records
       
   178         for pref, label in mailservers :
       
   179             self.add_record(bindc.MX(None, pref, label))
       
   180         
       
   181         # include?
       
   182         if include :
       
   183             self.add_directive(bindc.IncludeDirective(include))
       
   184 
       
   185         if objs :
       
   186             for obj in objs :
       
   187                 self.add_obj(obj)