bin/pvl.hosts-dns
author Tero Marttila <terom@paivola.fi>
Mon, 16 Dec 2013 18:37:08 +0200
changeset 269 713d0495288e
child 270 bafcda3a3c0d
permissions -rwxr-xr-x
pvl.hosts-dns: generate --forward-zone=paivola.fi and --reverse-zone=194.197.235.0/24
#!/usr/bin/env python


import pvl.args
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.
    """
    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.
    """

    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 :
            continue
        
        ip = fields.get('ip')

        if ip :
            yield pvl.dns.zone.ZoneRecord.A(host, value)
        
        # aliases
        for field, value in fields.iteritems() :
            if field.startswith('alias.') :
                yield pvl.dns.zone.ZoneRecord.CNAME(value, host)

def apply_reverse_zone (options, prefix, address) :
    """
        Determine the correct reverse-dns name for the given address with the reverse zone for the given 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, ))

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 :
            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))

def main (argv) :
    """
        Generate bind zonefiles from host definitions.
    """

    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('--forward-zone',         metavar='DOMAIN',
            help="Generate forward zone for domain")

    parser.add_option('--reverse-zone',         metavar='PREFIX',
            help="Generate reverse zone for prefx")

    options, args = parser.parse_args(argv[1:])
    pvl.args.apply(options)

    # input
    hosts = list(apply_hosts(options, args))


    if options.forward_zone :
        zone = process_hosts_forward(options, hosts)
    
    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 __name__ == '__main__':
    pvl.args.main(main)