pvl.hosts: implement forward/reverse to delegate to a different zone
authorTero Marttila <terom@paivola.fi>
Sun, 22 Dec 2013 17:25:09 +0200
changeset 331 9dda6a0e9826
parent 330 3954e7b5ade1
child 332 bb8a18cffe8a
pvl.hosts: implement forward/reverse to delegate to a different zone
bin/pvl.hosts-dns
pvl/hosts.py
--- 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) :
     """
--- 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 :