--- a/bin/pvl.hosts-dns Tue Feb 24 21:37:36 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-#!/usr/bin/env python
-
-import pvl.args
-import pvl.hosts
-import pvl.dns.zone
-
-import ipaddr
-import logging; log = logging.getLogger('pvl.hosts-dns')
-import optparse
-
-def process_hosts_alias (options, origin, host_domain, alias, host) :
- """
- Resolve alias@domain within given zone.
- """
-
- if host_domain :
- alias = pvl.dns.join(alias, host_domain)
- else :
- raise ValueError("no domain given for host %s alias %s" % (host, alias, ))
-
- if alias.endswith('.' + origin) :
- # strip
- alias = alias[:(len(alias) - len(origin) - 1)]
- else:
- raise ValueError("alias domain outside of origin: %s / %s" % (alias, origin))
-
- return pvl.dns.zone.ZoneRecord.CNAME(alias, host)
-
-def process_hosts_names (options, hosts, origin) :
- """
- Yield ZoneRecords for hosts within the given zone.
- """
-
- for host in hosts :
- # determine label within zone
- if not origin :
- label = pvl.dns.join(host.name, host.domain)
- elif host.domain == origin :
- label = host.name
- elif host.domain and host.domain.endswith('.' + origin) :
- fqdn = pvl.dns.join(host.name, host.domain)
- label = fqdn[:(len(fqdn) - len(origin) - 1)]
- elif host.domain :
- log.debug("%s: domain out of zone: %s", host, origin)
- continue
- else :
- log.debug("%s: fqdn out of zone: %s", host, origin)
- continue
-
- if host.forward is None :
- pass
- elif host.forward :
- forward = pvl.dns.zone.fqdn(host.forward)
-
- log.info("%s: forward: %s", host, forward)
-
- yield pvl.dns.zone.ZoneRecord.CNAME(label, forward)
- continue
- else :
- log.info("%s: skip forward", host)
- continue
-
- if host.ip :
- yield pvl.dns.zone.ZoneRecord.A(label, host.ip)
-
- if host.alias4 :
- yield pvl.dns.zone.ZoneRecord.A(host.ALIAS4_FMT.format(host=label), host.ip)
-
- if host.ip6 :
- yield pvl.dns.zone.ZoneRecord.AAAA(label, host.ip6)
-
- if host.alias6 :
- yield pvl.dns.zone.ZoneRecord.AAAA(label.ALIAS6_FMT.format(host=host), host.ip6)
-
- if host.location and host.location_domain:
- yield process_hosts_alias(options, origin, host.location_domain, host.location, label)
- elif host.location:
- yield process_hosts_alias(options, origin, host.domain, host.location, label)
-
- for alias in host.alias :
- yield process_hosts_alias(options, origin, host.domain, alias, label)
-
- for alias4 in host.alias4 :
- yield process_hosts_alias(options, origin, host.domain, alias4, host.ALIAS4_FMT.format(host=label))
-
- for alias6 in host.alias6 :
- yield process_hosts_alias(options, origin, host.domain, alias6, host.ALIAS6_FMT.format(host=label))
-
-def process_hosts_forward (options, hosts, origin) :
- """
- Generate DNS ZoneRecords for for hosts within the given zone origin.
- """
-
- if options.add_origin :
- yield pvl.dns.zone.ZoneDirective.build(None, 'ORIGIN', origin)
-
- by_name = dict()
- by_name_type = dict()
-
- # list of types thare are allowed to be present for a host
- MULTI_TYPES = ('A', 'AAAA')
-
- for rr in process_hosts_names(options, hosts, origin) :
- if (rr.name, rr.type) in by_name_type :
- raise ValueError("%s: duplicate name/type: %s: %s" % (rr.name, rr, by_name_type[(rr.name, rr.type)]))
- elif rr.type in MULTI_TYPES :
- by_name_type[(rr.name, rr.type)] = rr
- elif rr.name in by_name :
- raise ValueError("%s: duplicate name: %s: %s" % (rr.name, rr, by_name[rr.name]))
-
- # always check these
- by_name[rr.name] = rr
-
- # preserve ordering
- yield rr
-
-def split_ipv6_parts (prefix) :
- for hextet in prefix.rstrip(':').split(':') :
- for nibble in hextet.rjust(4, '0') :
- yield nibble
-
-def build_ipv6_parts (parts) :
- for i in xrange(0, len(parts), 4) :
- yield ''.join(parts[i:i+4])
-
- # suffix ::
- if len(parts) < 32 :
- yield ''
- yield ''
-
-def parse_prefix (prefix) :
- """
- Return an ipaddr.IPNetwork from given IPv4/IPv6 prefix.
-
- >>> parse_prefix('127.0.0.0/8')
- IPv4Network('127.0.0.0/8')
- >>> parse_prefix('192.0.2.128/26')
- IPv4Network('192.0.2.128/26')
- >>> parse_prefix('192.0.2.128-26')
- IPv4Network('192.0.2.128-26')
- >>> parse_prefix('127.')
- IPv4Network('127.0.0.0/8')
- >>> parse_prefix('10')
- IPv4Network('10.0.0.0/8')
- >>> parse_prefix('192.168')
- IPv4Network('192.168.0.0/16')
- >>> parse_prefix('fe80:')
- IPv6Network('fe80::/16')
- >>> parse_prefix('2001:db8::')
- IPv6Network('2001:db8::/32')
- >>> parse_prefix('2001:db8:1:2')
- IPv6Network('2001:db8:1:2::/64')
- """
-
- if '/' in prefix :
- return ipaddr.IPNetwork(prefix)
-
- elif '-' in prefix :
- return ipaddr.IPNetwork(prefix.replace('-', '/'))
-
- elif '.' in prefix or prefix.isdigit() :
- parts = prefix.rstrip('.').split('.')
- prefixlen = len(parts) * 8
-
- return ipaddr.IPv4Network('{prefix}/{prefixlen}'.format(
- prefix = '.'.join(parts + ['0' for i in xrange(4 - len(parts))]),
- prefixlen = prefixlen,
- ))
-
- elif ':' in prefix :
- parts = list(split_ipv6_parts(prefix))
- prefixlen = len(parts) * 4
-
- return ipaddr.IPv6Network('{prefix}/{prefixlen}'.format(
- prefix = ':'.join(build_ipv6_parts(parts)),
- prefixlen = prefixlen,
- ))
-
- else :
- raise ValueError("Unrecognized IP prefix string: %s" % (prefix, ))
-
-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
-
- label = pvl.dns.zone.reverse_label(prefix, ip)
-
- if host.reverse is None :
- fqdn = host.fqdn()
-
- log.info("%s %s[%s]: PTR %s", host, prefix, ip, fqdn)
-
- yield host, ip, pvl.dns.zone.ZoneRecord.PTR(label, fqdn)
-
- elif host.reverse :
- alias = pvl.dns.zone.fqdn(host.reverse)
-
- log.info("%s %s[%s]: CNAME %s", host, prefix, ip, alias)
-
- yield host, ip, pvl.dns.zone.ZoneRecord.CNAME(label, alias)
-
- else :
- log.info("%s %s[%s]: omit", host, prefix, ip)
- continue
-
-
-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 host, ip, rr in process_hosts_ips(options, hosts, prefix) :
- if ip in by_ip :
- raise ValueError("%s: duplicate ip: %s: %s" % (host, ip, by_ip[ip]))
- else :
- by_ip[ip] = rr
-
- 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 :
- yield by_ip[ip]
- elif options.unknown_host :
- label = pvl.dns.zone.reverse_label(prefix, ip)
- fqdn = pvl.dns.zone.fqdn(options.unknown_host, options.hosts_domain)
-
- log.info("%s %s[%s]: unused PTR %s", options.unknown_host, ip, prefix, fqdn)
-
- yield pvl.dns.zone.ZoneRecord.PTR(label, fqdn)
- else :
- continue
-
-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('--add-origin', action='store_true',
- help="Include $ORIGIN directive in zone")
-
- 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 prefix")
-
- 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, parse_prefix(options.reverse_zone)),
- )
-
-if __name__ == '__main__':
- pvl.args.main(main)