# HG changeset patch # User Tero Marttila # Date 1387725909 -7200 # Node ID 9dda6a0e98263748e89ab55d7cea41f3d091f500 # Parent 3954e7b5ade13b7ce32b74d58aa68e1d824edff8 pvl.hosts: implement forward/reverse to delegate to a different zone diff -r 3954e7b5ade1 -r 9dda6a0e9826 bin/pvl.hosts-dns --- a/bin/pvl.hosts-dns Sat Dec 21 22:57:48 2013 +0200 +++ b/bin/pvl.hosts-dns Sun Dec 22 17:25:09 2013 +0200 @@ -42,7 +42,20 @@ fqdn = pvl.dns.join(host, host.domain) label = fqdn[:(len(fqdn) - len(origin) - 1)] else : - log.debug("%s: %s out of zone: %s", host, host.domain, origin) + log.debug("%s: domain %s out of zone: %s", host, host.domain, origin) + continue + + if host.forward is None : + pass + elif host.forward : + forward = pvl.dns.join(host, host.forward, host.domain) + + 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 : @@ -173,11 +186,26 @@ log.debug("%s: %s out of prefix: %s", host, ip, prefix) continue - fqdn = host.fqdn() + label = pvl.dns.zone.reverse_label(prefix, ip) + + if host.reverse is None : + fqdn = host.fqdn() - log.debug("%s: ip=%s domain=%s: %s", host, ip, host.domain, fqdn) + log.info("%s %s[%s]: PTR %s", host, prefix, ip, fqdn) - yield ip, fqdn + yield host, ip, pvl.dns.zone.ZoneRecord.PTR(label, fqdn) + + elif host.reverse : + alias = pvl.dns.join(label, host.reverse, host.domain) + + 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) : """ @@ -186,11 +214,11 @@ # collect data for records by_ip = dict() - for ip, fqdn in process_hosts_ips(options, hosts, prefix) : + for host, ip, rr in process_hosts_ips(options, hosts, prefix) : if ip in by_ip : - raise ValueError("%s: duplicate ip: %s: %s" % (fqdn, ip, by_ip[ip])) + raise ValueError("%s: duplicate ip: %s: %s" % (host, ip, by_ip[ip])) else : - by_ip[ip] = fqdn + by_ip[ip] = rr if options.unknown_host : # enumerate all of them @@ -200,17 +228,16 @@ for ip in iter_ips : if ip in by_ip : - fqdn = by_ip[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 : - 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) + continue def apply_zone (options, zone) : """ diff -r 3954e7b5ade1 -r 9dda6a0e9826 pvl/hosts.py --- a/pvl/hosts.py Sat Dec 21 22:57:48 2013 +0200 +++ b/pvl/hosts.py Sun Dec 22 17:25:09 2013 +0200 @@ -10,6 +10,8 @@ import optparse import os.path +import logging; log = logging.getLogger('pvl.hosts') + def optparser (parser) : hosts = optparse.OptionGroup(parser, "Hosts input") hosts.add_option('--hosts-charset', metavar='CHARSET', default='utf-8', @@ -26,22 +28,41 @@ ALIAS6_FMT = '{host}-ipv6' @classmethod - def expand (cls, options, host, range, ip, **opts) : + def expand (cls, options, range, host, domain, ip, **opts) : host = pvl.dns.zone.parse_generate_field(host) ip = pvl.dns.zone.parse_generate_field(ip) for i in range : - yield cls.build(options, host(i), + yield cls.build(options, host(i), domain, ip = ip(i), **opts ) @classmethod - def config (cls, options, host, ip=None, **extra) : + def config (cls, options, host, section=None, domain=None, ip=None, **extra) : """ Yield Hosts from a config section's scalars. + + options - pvl.args Options + host - the name of the section (or file) containing this host item. + section - the parent section containing this host item, or None + used for domain """ + if domain : + log.debug("%s: explicit domain: %s", host, domain) + elif options.hosts_domain : + log.debug("%s: default domain to --hots-domain: %s", host, options.hosts_domain) + domain = options.hosts_domain + elif section : + log.debug("%s: default domain to section: %s", host, section) + domain = section + elif '.' in host : + log.debug("%s: using as fqdn without domain", host) + domain = None + else : + raise ValueError("%s: no domain given" % (host, )) + if '{' in host : pre, host = host.split('{', 1) range, post = host.rsplit('}', 1) @@ -49,15 +70,18 @@ range = pvl.dns.zone.parse_generate_range(range) host = pre + "$" + post - for host in cls.expand(options, host, range, ip, **extra) : + for host in cls.expand(options, range, host, domain, ip, **extra) : yield host else : - yield cls.build(options, host, ip=ip, **extra) + yield cls.build(options, host, domain, ip=ip, **extra) @classmethod - def build (cls, options, host, domain=None, ip=None, ip6=None, owner=None, boot=None, alias=None, alias4=None, alias6=None, **extra) : + def build (cls, options, host, domain, + ip=None, ip6=None, owner=None, boot=None, alias=None, alias4=None, alias6=None, forward=None, reverse=None, + **extra) : """ Return a Host from a config section's scalars. + """ if alias : @@ -92,9 +116,27 @@ else : raise ValueError("%s: Unknown host field: %s=%s" % (host, field, value)) - if domain is None : - domain = options.hosts_domain + + if forward is None : + # normal zone + pass + elif forward : + # alias to external zone + pass + else : + # omit + forward = False + if reverse is None : + # normal zone + pass + elif reverse : + # alias to external zone + pass + else : + # omit + reverse = False + return cls(host, domain = domain, ip = ipaddr.IPv4Address(ip) if ip else None, @@ -105,9 +147,20 @@ alias6 = alias6, owner = owner, boot = boot, + forward = forward, + reverse = reverse, ) - def __init__ (self, host, domain=None, ip=None, ip6=None, ethernet={ }, alias=(), owner=None, boot=None, alias4=None, alias6=None) : + def __init__ (self, host, + domain=None, + ip=None, ip6=None, + ethernet={ }, + alias=(), + owner=None, + boot=None, + alias4=None, alias6=None, + forward=None, reverse=None, + ) : """ host - str domain - str @@ -118,7 +171,10 @@ owner - str: LDAP uid alias4 - list (CNAME -> A) alias6 - list (CNAME -> AAAA) + forward - generate forward records, or CNAME into given zone + reverse - generate reverse records, or CNAME into given zone """ + self.host = host self.domain = domain self.ip = ip @@ -129,6 +185,8 @@ self.alias6 = alias6 self.owner = owner self.boot = boot + self.forward = forward + self.reverse = reverse def fqdn (self) : if '.' in self.host : @@ -141,7 +199,7 @@ def __str__ (self) : return str(self.host) -def apply_hosts_config (options, config, name, defaults={}) : +def apply_hosts_config (options, config, name, defaults={}, parent=None) : """ Load hosts from a ConfigObj section. """ @@ -150,18 +208,18 @@ if config.sections : # recurse; this is a domain meta-section - params = dict(defaults, domain=name) + params = dict(defaults) params.update(**scalars) # override for section in config.sections : - for host in apply_hosts_config(options, config[section], section, params) : + for host in apply_hosts_config(options, config[section], section, params, name) : yield host elif name : params = dict(defaults, **scalars) # this is a host section - for host in Host.config(options, name, **params) : + for host in Host.config(options, name, parent, **params) : yield host else :