bin/pvl.hosts-dns
author Tero Marttila <terom@paivola.fi>
Tue, 17 Dec 2013 01:15:37 +0200
changeset 299 df653511caf9
parent 298 fa1ab8cda47e
child 308 08176bed21e3
permissions -rwxr-xr-x
pvl.hosts.Host.fqnd(): assume any host with a . is a fqdn
#!/usr/bin/env python

import pvl.args
import pvl.hosts
import pvl.dns.zone

import fnmatch
import ipaddr
import logging; log = logging.getLogger('pvl.hosts-dns')
import optparse

def process_hosts_names (options, hosts, domain) :
    """
        Yield ZoneRecords for the given domain's hosts.
    """

    for host in hosts :
        if not options.forward_zone :
            pass
        elif host.domain == domain :
            pass
        elif pvl.dns.zone.join('*', host.domain) == domain :
            pass
        elif fnmatch.fnmatch(host.domain, domain) :
            pass
        else :
            log.debug("%s: %s out of domain: %s", host, host.domain, domain)
            continue
        
        if host.ip :
            yield pvl.dns.zone.ZoneRecord.A(host, host.ip)
        
        for alias in host.alias :
            yield pvl.dns.zone.ZoneRecord.CNAME(alias, host)

def process_hosts_forward (options, hosts, domain) :
    """
        Generate DNS ZoneRecords for the given domain's zone for hosts.
    """

    by_name = dict()

    for rr in process_hosts_names(options, hosts, domain) :
        if rr.name in by_name :
            raise ValueError("%s: duplicate name: %s: %s" % (rr.name, rr, by_name[rr.name]))
        else :
            by_name[rr.name] = rr
        
        # preserve ordering
        yield rr

def process_hosts_ips (options, hosts, prefix) :
    """
        Yield (ip, fqnd) for hosts within given prefix.
    """

    for host in hosts :
        if prefix.version == 4 :
            ip = host.ip
        elif prefix.version == 6 :
            ip = host.ip6
        else :
            raise ValueError("%s: unknown ip version: %s" % (prefix, prefix.version))

        if not ip :
            log.debug("%s: no ip%d", host, prefix.version)
            continue

        if ip not in prefix :
            log.debug("%s: %s out of prefix: %s", host, ip, prefix)
            continue
        
        fqdn = host.fqdn()

        log.debug("%s: ip=%s domain=%s: %s", host, ip, host.domain, fqdn)

        yield ip, fqdn
 
def process_hosts_reverse (options, hosts, prefix) :
    """
        Generate DNS ZoneRecords within the given prefix's reverse-dns zone for hosts.
    """
    
    # collect data for records
    by_ip = dict()
    for ip, fqdn in process_hosts_ips(options, hosts, prefix) :
        if ip in by_ip :
            raise ValueError("%s: duplicate ip: %s: %s" % (fqdn, ip, by_ip[ip]))
        else :
            by_ip[ip] = fqdn

    if options.unknown_host :
        # enumerate all of them
        iter_ips = prefix.iterhosts()
    else :
        iter_ips = sorted(by_ip)

    for ip in iter_ips :
        if ip in by_ip :
            fqdn = by_ip[ip]
        elif options.unknown_host :
            fqdn = pvl.dns.zone.fqdn(options.unknown_host, options.hosts_domain)
        else :
            fqdn = None
        
        log.info("%s: %s", ip, fqdn)

        if fqdn :
            # reverse against the reverse-dns zone origin
            yield pvl.dns.zone.ZoneRecord.PTR(pvl.dns.zone.reverse_label(prefix, ip), fqdn)

def apply_zone (options, zone) :
    """
        Output given ZoneRecord's
    """

    for record in zone :
        print unicode(record)

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

    parser = optparse.OptionParser(main.__doc__)
    parser.add_option_group(pvl.args.parser(parser))
    parser.add_option_group(pvl.hosts.optparser(parser))

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

    parser.add_option('--unknown-host',         metavar='NAME',
            help="Generate records for unused IPs")

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

    # input
    hosts = pvl.hosts.apply(options, args)

    # process
    if options.forward_zone :
        apply_zone(options, 
                process_hosts_forward(options, hosts, options.forward_zone),
        )
    
    if options.reverse_zone :
        apply_zone(options, 
                process_hosts_reverse(options, hosts, ipaddr.IPNetwork(options.reverse_zone)),
        )

if __name__ == '__main__':
    pvl.args.main(main)