pvl/config/bind_conf.py
changeset 9 2156906bfbf1
parent 8 46d36bc33086
child 10 65d91f6a2d2a
equal deleted inserted replaced
8:46d36bc33086 9:2156906bfbf1
     1 """
       
     2     Configuration file output for the ISC DNS server: BIND
       
     3 """
       
     4 
       
     5 import conf
       
     6 
       
     7 class Object (conf.ConfObject) :
       
     8     """
       
     9         Our own version of ConfObject that knows how to format comments
       
    10     """
       
    11 
       
    12     def _fmt_comments (self) :
       
    13         """
       
    14             Format comments using the standard format:
       
    15                 ";" <comment>
       
    16         """
       
    17 
       
    18         for comment in self.comments :
       
    19             if comment is not None :
       
    20                 yield "; %s" % (comment, )
       
    21 
       
    22 class ZoneFile (Object, conf.File) :
       
    23     """
       
    24         A zone file containing a bunch of directives, resource records and comments
       
    25     """
       
    26 
       
    27     def __init__ (self, name, path, ttl=None, comment=None) :
       
    28         """
       
    29             @param name the name of the zonefile, for status stuff
       
    30             @param path the path to the zonefile
       
    31             @param ttl default TTL to use
       
    32             @param comment optional comments
       
    33         """
       
    34         
       
    35         Object.__init__(self, comment)
       
    36         conf.File.__init__(self, name, path)
       
    37 
       
    38         # init
       
    39         self.objects = []
       
    40 
       
    41         if ttl :
       
    42             self.add_directive(TTLDirective(ttl))
       
    43 
       
    44     def add_obj (self, obj) :
       
    45         """
       
    46             Add an Object onto the list of things to include in this zone file
       
    47         """
       
    48 
       
    49         self.objects.append(obj)
       
    50 
       
    51     # various aliases...
       
    52     add_comment = add_record = add_directive = add_obj
       
    53 
       
    54     def fmt_lines (self) :
       
    55         """
       
    56             Just format all our objects
       
    57         """
       
    58         
       
    59         # prefix comments
       
    60         for line in self._fmt_comments() :
       
    61             yield line
       
    62         
       
    63         # and then all objects
       
    64         for obj in self.objects :
       
    65             # ...and their lines
       
    66             for line in obj.fmt_lines() :
       
    67                 yield line
       
    68     
       
    69 class Comment (Object) :
       
    70     """
       
    71         A comment, is, well, a comment :)
       
    72     """
       
    73 
       
    74     def __init__ (self, comment) :
       
    75         """
       
    76             @param comment the comment string
       
    77         """
       
    78 
       
    79         Object.__init__(self, [comment])
       
    80     
       
    81     def fmt_lines (self) :
       
    82         """
       
    83             Yield a single line with the comment
       
    84         """
       
    85 
       
    86         return self._fmt_comments()
       
    87 
       
    88 class Label (object) :
       
    89     """
       
    90         A label, as used in a ResourceRecord, either as the label, or the rdata for various resource types
       
    91 
       
    92         You can also use strs, this just implements a __str__ method
       
    93     """
       
    94 
       
    95     def __init__ (self, label) :
       
    96         """
       
    97             @param label the literal label to use
       
    98         """
       
    99 
       
   100         self.label = label
       
   101 
       
   102     def __str__ (self) :
       
   103         return self.label
       
   104 
       
   105 class Origin (Label) :
       
   106     """
       
   107         A label that represents the zone's origin
       
   108     """
       
   109 
       
   110     def __init__ (self) :
       
   111         pass
       
   112 
       
   113     def __str__ (self) :
       
   114         return '@'
       
   115 
       
   116 class FQDN (Label) :
       
   117     """
       
   118         A label that represents the given external domain (i.e. this adds the . to the end that people always forget).
       
   119     """
       
   120 
       
   121     def __init__ (self, fqdn) :
       
   122         self.fqdn = fqdn
       
   123 
       
   124     def __str__ (self) :
       
   125         return "%s." % (self.fqdn, )
       
   126 
       
   127 class Interval (object) :
       
   128     """
       
   129         A time interval suitable for use in SOA records
       
   130     """
       
   131 
       
   132     def __init__ (self, s=None, m=None, h=None, d=None) :
       
   133         """
       
   134             @param s seconds
       
   135             @param m minutes
       
   136             @param h hours
       
   137             @param d days
       
   138         """
       
   139 
       
   140         self.s = s
       
   141         self.m = m
       
   142         self.h = h
       
   143         self.d = d
       
   144 
       
   145     def __str__ (self) :
       
   146         """
       
   147             If only seconds were given, just return those directly, otherwise, apply units
       
   148         """
       
   149 
       
   150         if self.s and not self.m and not self.h and not self.d :
       
   151             return str(self.s)
       
   152 
       
   153         else :
       
   154             return "%s%s%s%s" % (
       
   155                     "%ds" % self.s if self.s else '',
       
   156                     "%dm" % self.m if self.m else '',
       
   157                     "%dh" % self.h if self.h else '',
       
   158                     "%dd" % self.d if self.d else ''
       
   159                 )
       
   160 
       
   161 class ResourceRecord (Object) :
       
   162     """
       
   163         A generic resource record for a BIND zone file
       
   164     """
       
   165 
       
   166     def __init__ (self, label, type, rdata, cls='IN', ttl=None, **kwargs) :
       
   167         """
       
   168             @param label the "name" of this record, or None to referr to the previous record's label
       
   169             @param type the type as a string ('A', 'TXT', etc.)
       
   170             @param rdata the rdata, as a raw string
       
   171             @param cls the class, e.g. 'IN'
       
   172             @param ttl the time-to-live value in seconds, or None to omit it
       
   173         """
       
   174 
       
   175         super(ResourceRecord, self).__init__(**kwargs)
       
   176 
       
   177         self.label = label
       
   178         self.type = type
       
   179         self.rdata = rdata
       
   180         self.cls = cls
       
   181         self.ttl = ttl
       
   182 
       
   183     def fmt_lines (self) :
       
   184         """
       
   185             Just format the lines, eh
       
   186         """
       
   187         
       
   188         # prefix comments
       
   189         for line in self._fmt_comments() :
       
   190             yield line
       
   191 
       
   192         # then format the line
       
   193         # XXX: TTL?
       
   194         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)
       
   195 
       
   196 class SOA (ResourceRecord) :
       
   197     """
       
   198         "Identifies the start of a zone of authority", must be the first record
       
   199     """
       
   200 
       
   201     def __init__ (self, label, primary_ns, hostmaster, serial, refresh, retry, expire, minimum, **kwargs) :
       
   202         """
       
   203             @param label the "name" of the zone, usually ORIGIN
       
   204             @param primary_ns the address of the primary NS server
       
   205             @param hostmaster the mailbox of the zone's hostmaster
       
   206             @param serial the serial number of the zone as an integer
       
   207             @param refresh time interval between zone refreshes in seconds
       
   208             @param retry time interval between retrys for failed refreshes
       
   209             @param expire time interval before zone data can no longer be considered authorative
       
   210             @param minimum minimum TTL for RRs in this zone
       
   211         """
       
   212 
       
   213         super(SOA, self).__init__(label, 'SOA', "%s %s ( %s %s %s %s %s )" % (
       
   214                 primary_ns, hostmaster, serial, refresh, retry, expire, minimum
       
   215             ), **kwargs)
       
   216 
       
   217 class A (ResourceRecord) :
       
   218     """
       
   219         An IPv4 forward address
       
   220     """
       
   221 
       
   222     def __init__ (self, label, addr, **kwargs) :
       
   223         """
       
   224             @param label the "name" of the address
       
   225             @param addr the IPv4 target address
       
   226         """
       
   227 
       
   228         assert(addr.is_v4())
       
   229 
       
   230         super(A, self).__init__(label, 'A', addr, **kwargs)
       
   231 
       
   232 class AAAA (ResourceRecord) :
       
   233     """
       
   234         An IPv6 forward address
       
   235     """
       
   236 
       
   237     def __init__ (self, label, addr, **kwargs) :
       
   238         """
       
   239             @param label the "name" of the address
       
   240             @param addr the IPv6 target address
       
   241         """
       
   242         
       
   243         assert(addr.is_v6())
       
   244 
       
   245         super(AAAA, self).__init__(label, 'AAAA', addr.strCompressed(), **kwargs)
       
   246 
       
   247 class CNAME (ResourceRecord) :
       
   248     """
       
   249         A canonical-name alias
       
   250     """
       
   251 
       
   252     def __init__ (self, label, target, **kwargs) :
       
   253         """
       
   254             @param label the "name" of the alias
       
   255             @param target the alias target
       
   256         """
       
   257 
       
   258         super(CNAME, self).__init__(label, 'CNAME', target, **kwargs)
       
   259 
       
   260 class TXT (ResourceRecord) :
       
   261     """
       
   262         A human-readable information record
       
   263     """
       
   264 
       
   265     def __init__ (self, label, text, **kwargs) :
       
   266         """
       
   267             @param label the "name" of the text record
       
   268             @param text the text data, shouldn't contain any quotes...
       
   269         """
       
   270 
       
   271         super(TXT, self).__init__(label, 'TXT', '"%s"' % text, **kwargs)
       
   272 
       
   273 class MX (ResourceRecord) :
       
   274     """
       
   275         A mail-exchange definition
       
   276     """
       
   277 
       
   278     def __init__ (self, label, pref, exchange, **kwargs) :
       
   279         """
       
   280             @param label the "name" of the domain to handle mail for
       
   281             @param pref the numerical preference for this exchange
       
   282             @param exchange the domain name of the mail exchange (SMTP server)
       
   283         """
       
   284 
       
   285         super(MX, self).__init__(label, 'MX', "%d %s" % (pref, exchange), **kwargs)
       
   286 
       
   287 class NS (ResourceRecord) :
       
   288     """
       
   289         An authorative name server
       
   290     """
       
   291 
       
   292     def __init__ (self, label, nsname, **kwargs) :
       
   293         """
       
   294             @param label the "name" of the domain to have a nameserver for
       
   295             @param nsname the name of the nameserver
       
   296         """
       
   297 
       
   298         super(NS, self).__init__(label, 'NS', nsname)
       
   299 
       
   300 class PTR (ResourceRecord) :
       
   301     """
       
   302         An IPv4/IPv6 reverse address
       
   303     """
       
   304 
       
   305     def __init__ (self, addr, name, **kwargs) :
       
   306         """
       
   307             @param addr the addr.IP to map via in-addr.arpa
       
   308             @param name the name to map the address to
       
   309         """
       
   310 
       
   311         # XXX: quick hack, this gives an absolute name
       
   312         label = addr.reverseName()
       
   313         
       
   314         super(PTR, self).__init__(label, 'PTR', name)
       
   315 
       
   316 class Directive (Object) :
       
   317     """
       
   318         Special directives that can be used in zone files to control behaviour
       
   319     """
       
   320 
       
   321     def __init__ (self, name, *args, **kwargs) :
       
   322         """
       
   323             @param name the $NAME bit
       
   324             @param args optional list of space-seprated arguments, Nones are ignored
       
   325         """
       
   326 
       
   327         super(Directive, self).__init__(**kwargs)
       
   328 
       
   329         self.name = name
       
   330         self.args = [arg for arg in args if arg is not None]
       
   331 
       
   332     def fmt_lines (self) :
       
   333         # prefix comments
       
   334         for line in self._fmt_comments() :
       
   335             yield line
       
   336 
       
   337         # then format it
       
   338         yield "$%s%s" % (self.name, (' ' + ' '.join(str(arg) for arg in self.args)) if self.args else '')
       
   339 
       
   340 class OriginDirective (Directive) :
       
   341     """
       
   342         Set the origin used to resolve the zone's labels.
       
   343 
       
   344         Note that the origin label is not absolute by default - use FQDN for that
       
   345     """
       
   346 
       
   347     def __init__ (self, origin, **kwargs) :
       
   348         """
       
   349             @param origin the origin label
       
   350         """
       
   351 
       
   352         super(OriginDirective, self).__init__('ORIGIN', origin, **kwargs)
       
   353 
       
   354 class TTLDirective (Directive) :
       
   355     """
       
   356         Set the TTL used for records by default
       
   357     """
       
   358 
       
   359     def __init__ (self, ttl, **kwargs) :
       
   360         """
       
   361             @param ttl the new ttl to use
       
   362         """
       
   363 
       
   364         super(TTLDirective, self).__init__('TTL', ttl, **kwargs)
       
   365 
       
   366 class IncludeDirective (Directive) :
       
   367     """
       
   368         Include another zoen file, optionally with a different origin
       
   369     """
       
   370 
       
   371     def __init__ (self, filename, origin=None, **kwargs) :
       
   372         """
       
   373             @param filename the zone file to include
       
   374             @param origin the optional origin to process the zonefile with
       
   375         """
       
   376 
       
   377         super(IncludeDirective, self).__init__('INCLUDE', filename, origin, **kwargs)
       
   378 
       
   379 class GenerateDirective (Directive) :
       
   380     """
       
   381         Generate a bunch of numbered records using an expression for the label and rdata.
       
   382 
       
   383         At the simplest, any "$" in the expression is replaced with the value of the iterator.
       
   384     """
       
   385 
       
   386     def __init__ (self, range, lhs, type, rhs, ttl=None, cls=None, **kwargs) :
       
   387         """
       
   388             @param range (start, stop, step) tuple
       
   389             @param lhs expression to generate the label
       
   390             @param type the resource record type
       
   391             @param rhs expression to generate the rdata
       
   392         """
       
   393 
       
   394         super(GenerateDirective, self).__init__('GENERATE', '%d-%d' % range, lhs, ttl, cls, type, rhs, **kwargs)
       
   395