pvl.hosts-dns: split to pvl.dns.host, using Host class
authorTero Marttila <terom@paivola.fi>
Mon, 16 Dec 2013 19:00:58 +0200
changeset 270 bafcda3a3c0d
parent 269 713d0495288e
child 271 4dfa1a939153
pvl.hosts-dns: split to pvl.dns.host, using Host class
bin/pvl.hosts-dns
pvl/dns/hosts.py
pvl/dns/zone.py
--- a/bin/pvl.hosts-dns	Mon Dec 16 18:37:08 2013 +0200
+++ b/bin/pvl.hosts-dns	Mon Dec 16 19:00:58 2013 +0200
@@ -1,84 +1,53 @@
 #!/usr/bin/env python
 
-
 import pvl.args
+import pvl.hosts
 import pvl.dns.zone
 
-import configobj
 import ipaddr
 import logging; log = logging.getLogger('pvl.hosts-dns')
-import math
 import optparse
 
-def apply_hosts_file (options, file) :
-    """
-        Load hosts from a file.
+def process_hosts_forward (options, hosts, domain) :
     """
-    config = configobj.ConfigObj(file,
-            encoding    = options.hosts_charset,
-    )
-
-    for host in config.sections :
-        yield host, config[host]
-
-def apply_hosts (options, args) :
-    """
-        Load hosts from arguments.
+        Generate DNS ZoneRecords for the given domain's zone for hosts.
     """
 
-    for file in pvl.args.apply_files(args, 'r') :
-        for host in apply_hosts_file(options, file) :
-            yield host
-
-def process_hosts_forward (options, hosts) :
-    for host, fields in hosts :
-        domain = fields.get('domain', options.hosts_domain)
-
-        if options.forward_zone and domain != options.forward_zone :
+    for host in hosts :
+        if options.forward_zone and host.domain != domain :
             continue
         
-        ip = fields.get('ip')
-
-        if ip :
-            yield pvl.dns.zone.ZoneRecord.A(host, value)
+        if host.ip :
+            yield pvl.dns.zone.ZoneRecord.A(host, host.ip)
         
-        # aliases
-        for field, value in fields.iteritems() :
-            if field.startswith('alias.') :
-                yield pvl.dns.zone.ZoneRecord.CNAME(value, host)
+        for alias in host.alias :
+            yield pvl.dns.zone.ZoneRecord.CNAME(alias, host)
 
-def apply_reverse_zone (options, prefix, address) :
+def process_hosts_reverse (options, hosts, prefix) :
     """
-        Determine the correct reverse-dns name for the given address with the reverse zone for the given prefix.
+        Generate DNS ZoneRecords within the given prefix's reverse-dns zone for hosts.
     """
 
-    assert prefix.version == address.version
-    
-    hostbits = prefix.max_prefixlen - prefix.prefixlen
-
-    if prefix.version == 4 :
-        labelbytes = int(math.ceil(hostbits / 8.0))
-        labelraw = address.packed[-labelbytes:]
+    for host in hosts :
+        if not host.ip :
+            continue
 
-        return '.'.join(reversed([str(ord(x)) for x in labelraw]))
-    else :
-        raise ValueError("unsupported address version: %s" % (prefix, ))
-
-def process_hosts_reverse (options, hosts) :
-    for host, fields in hosts :
-        domain = fields.get('domain', options.hosts_domain)
-        ip = fields.get('ip')
-
-        if ip :
-            ip = ipaddr.IPAddress(ip)
-
-        if not ip or ip not in options.reverse_zone :
+        if host.ip not in prefix :
             continue
         
-        # reverse against the prefix origin
-        ptr = apply_reverse_zone(options, options.reverse_zone, ip)
-        
-        yield pvl.dns.zone.ZoneRecord.PTR(ptr, pvl.dns.zone.fqdn(host, domain))
+        # reverse against the reverse-dns zone origin
+        yield pvl.dns.zone.ZoneRecord.PTR(
+                pvl.dns.zone.reverse_label(prefix, host.ip),
+                pvl.dns.zone.fqdn(host, host.domain)
+        )
+
+def apply_zone (options, zone) :
+    """
+        Output given ZoneRecord's
+    """
+
+    for record in zone :
+        print unicode(record)
 
 def main (argv) :
     """
@@ -87,15 +56,7 @@
 
     parser = optparse.OptionParser(main.__doc__)
     parser.add_option_group(pvl.args.parser(parser))
-    
-    hosts = optparse.OptionGroup(parser, "Hosts input")
-    hosts.add_option('--hosts-charset',         metavar='CHARSET',  default='utf-8', 
-            help="Encoding used for host files")
-
-    hosts.add_option('--hosts-domain',          metavar='DOMAIN',
-            help="Default domain for hosts")
-    
-    parser.add_option_group(hosts)
+    parser.add_option_group(pvl.hosts.optparser(parser))
 
     parser.add_option('--forward-zone',         metavar='DOMAIN',
             help="Generate forward zone for domain")
@@ -107,23 +68,18 @@
     pvl.args.apply(options)
 
     # input
-    hosts = list(apply_hosts(options, args))
+    hosts = pvl.hosts.apply(options, args)
 
-
+    # process
     if options.forward_zone :
-        zone = process_hosts_forward(options, hosts)
+        apply_zone(options, 
+                process_hosts_forward(options, hosts, options.forward_zone),
+        )
     
-    elif options.reverse_zone :
-        options.reverse_zone = ipaddr.IPNetwork(options.reverse_zone)
-
-        zone = process_hosts_reverse(options, hosts)
-
-    else :
-        log.error("nothing to do")
-        return 1
-    
-    for record in zone :
-        print unicode(record)
+    if options.reverse_zone :
+        apply_zone(options, 
+                process_hosts_reverse(options, hosts, ipaddr.IPNetwork(options.reverse_zone)),
+        )
 
 if __name__ == '__main__':
     pvl.args.main(main)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/dns/hosts.py	Mon Dec 16 19:00:58 2013 +0200
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+"""
+    Process network hosts.
+"""
+
+class Hosts (object) :
+    def __init__ (self) :
+
--- a/pvl/dns/zone.py	Mon Dec 16 18:37:08 2013 +0200
+++ b/pvl/dns/zone.py	Mon Dec 16 19:00:58 2013 +0200
@@ -7,6 +7,7 @@
 import codecs
 import datetime
 import logging
+import math
 import os.path
 
 log = logging.getLogger('pvl.dns.zone')
@@ -273,26 +274,26 @@
 
     @classmethod
     def A (cls, name, ip, **opts) :
-        return cls(name, 'A', [ip], **opts)
+        return cls(str(name), 'A', [str(ip)], **opts)
 
     @classmethod
     def CNAME (cls, name, host, **opts) :
-        return cls(name, 'CNAME', [host], **opts)
+        return cls(str(name), 'CNAME', [str(host)], **opts)
 
     @classmethod
     def TXT (cls, name, text, **opts) :
-        return cls(name, 'TXT',
+        return cls(str(name), 'TXT',
             [u'"{0}"'.format(text.replace('"', '\\"'))], 
             **opts
         )
 
     @classmethod
     def PTR (cls, name, ptr, **opts) :
-        return cls(name, 'PTR', [ptr], **opts)
+        return cls(str(name), 'PTR', [str(ptr)], **opts)
 
     @classmethod
     def MX (cls, name, priority, mx, **opts) :
-        return cls(name, 'MX', [priority, mx], **opts)
+        return cls(str(name), 'MX', [int(priority), str(mx)], **opts)
 
     def __init__ (self, name, type, data, origin=None, ttl=None, cls=None, line=None, comment=None) :
         self.name = name
@@ -537,7 +538,7 @@
     return '.'.join(tuple(reversed(parts)) + ( 'ip6', 'arpa'))
 
 def fqdn (*parts) :
-    fqdn = '.'.join(parts)
+    fqdn = '.'.join(str(part) for part in parts)
     
     # we may be given an fqdn in parts
     if not fqdn.endswith('.') :
@@ -545,4 +546,23 @@
     
     return fqdn
 
+def reverse_label (prefix, address) :
+    """
+        Determine the correct label for the given IP address within the reverse zone for the given prefix.
 
+        This includes all suffix octets (partially) covered by the prefix.
+    """
+
+    assert prefix.version == address.version
+    
+    hostbits = prefix.max_prefixlen - prefix.prefixlen
+
+    if prefix.version == 4 :
+        labelbytes = int(math.ceil(hostbits / 8.0))
+        labelraw = address.packed[-labelbytes:]
+
+        return '.'.join(reversed([str(ord(x)) for x in labelraw]))
+    else :
+        raise ValueError("unsupported address version: %s" % (prefix, ))
+
+