pvl.hosts: rename Host.ip -> Host.ip4; support instanced ip.foo = ... for foo.host A .... sub-labels
authorTero Marttila <terom@paivola.fi>
Mon, 09 Mar 2015 18:00:18 +0200
changeset 733 45bedeba92e5
parent 732 ddb77f32b35a
child 734 5770ed34c1f0
pvl.hosts: rename Host.ip -> Host.ip4; support instanced ip.foo = ... for foo.host A .... sub-labels
pvl/hosts/dhcp.py
pvl/hosts/host.py
pvl/hosts/tests.py
pvl/hosts/zone.py
--- a/pvl/hosts/dhcp.py	Sat Mar 07 16:31:11 2015 +0200
+++ b/pvl/hosts/dhcp.py	Mon Mar 09 18:00:18 2015 +0200
@@ -24,8 +24,8 @@
     yield 'option', 'host-name', host.name
     yield 'hardware', 'ethernet', pvl.dhcp.config.Field(ethernet)
 
-    if host.ip:
-        yield 'fixed-address', pvl.dhcp.config.Field(str(host.ip))
+    if host.ip4:
+        yield 'fixed-address', pvl.dhcp.config.Field(str(host.ip4))
       
     for bootopt in ('next-server', 'filename'):
         if bootopt in host.boot:
--- a/pvl/hosts/host.py	Sat Mar 07 16:31:11 2015 +0200
+++ b/pvl/hosts/host.py	Mon Mar 09 18:00:18 2015 +0200
@@ -176,10 +176,16 @@
             This handles all string parsing to our data types.
         """
 
+        ip4 = parse_dict(ip, ipaddr.IPv4Address)
+        ip6 = parse_dict(ip6, ipaddr.IPv6Address)
+
+        ip = {label: (ip4.get(label), ip6.get(label)) for label in set(ip4) | set(ip6)}
+
         return cls(name,
                 domain      = domain,
-                ip          = parse_ip(ip, ipaddr.IPv4Address),
-                ip6         = parse_ip(ip6, ipaddr.IPv6Address),
+                ip4         = ip4.get(None),
+                ip6         = ip6.get(None),
+                ip          = ip,
                 ethernet    = parse_dict(ethernet,  parse_ethernet),
                 owner       = owner,
                 location    = parse_location(location, domain),
@@ -194,7 +200,8 @@
         )
 
     def __init__ (self, name, domain,
-            ip=None, ip6=None,
+            ip4=None, ip6=None,
+            ip={},
             ethernet={ },
             owner=None,
             location=None,
@@ -207,8 +214,9 @@
         """
             name        - str
             domain      - str
-            ip          - ipaddr.IPv4Address
-            ip6         - ipaddr.IPv6Address
+            ip4         - primary ipaddr.IPv4Address
+            ip6         - primary ipaddr.IPv6Address
+            ip          - secondary { index: (ip4, ip6) } interface addresses
             ethernet    - { index: ethernet }
             alias       - [ str ]: generate CNAMEs for given relative names
             owner       - str: LDAP uid
@@ -226,8 +234,9 @@
 
         self.name = name
         self.domain = domain
+        self.ip4 = ip4
+        self.ip6 = ip6
         self.ip = ip
-        self.ip6 = ip6
         self.ethernet = ethernet
         self.alias = alias
         self.alias4 = alias4
--- a/pvl/hosts/tests.py	Sat Mar 07 16:31:11 2015 +0200
+++ b/pvl/hosts/tests.py	Mon Mar 09 18:00:18 2015 +0200
@@ -99,10 +99,12 @@
         ])
 
     def testApplyHostExpand(self):
-        self.assertHostsEqual(config.apply_hosts('test', 'asdf{1-3}', { 'ip': '10.100.100.$' }), [
-                ('asdf1@test', dict(ip=ipaddr.IPAddress('10.100.100.1'))),
-                ('asdf2@test', dict(ip=ipaddr.IPAddress('10.100.100.2'))),
-                ('asdf3@test', dict(ip=ipaddr.IPAddress('10.100.100.3'))),
+        self.assertHostsEqual(config.apply_hosts('test', 'asdf{1-3}', 
+                { 'ip': '10.100.100.$' }
+        ), [
+                ('asdf1@test', dict(ip4=ipaddr.IPAddress('10.100.100.1'))),
+                ('asdf2@test', dict(ip4=ipaddr.IPAddress('10.100.100.2'))),
+                ('asdf3@test', dict(ip4=ipaddr.IPAddress('10.100.100.3'))),
         ])
 
     def testApplyHostsFileError(self):
@@ -119,8 +121,8 @@
         """)
         
         self.assertHostsEqual(config.apply_hosts_config(self.options, conf_file), [
-                ('foo@test', dict(ip=ipaddr.IPAddress('127.0.0.1'))),
-                ('bar@test', dict(ip=ipaddr.IPAddress('127.0.0.2'))),
+                ('foo@test', dict(ip4=ipaddr.IPAddress('127.0.0.1'))),
+                ('bar@test', dict(ip4=ipaddr.IPAddress('127.0.0.2'))),
         ])
 
     def testApplyHostsConfigNested(self):
@@ -135,8 +137,8 @@
         """)
 
         self.assertHostsEqual(config.apply_hosts_config(self.options, conf_file), [
-                ('foo@asdf.test', dict(ip=ipaddr.IPAddress('127.0.0.1'))),
-                ('bar@quux.test', dict(ip=ipaddr.IPAddress('127.0.0.2'))),
+                ('foo@asdf.test', dict(ip4=ipaddr.IPAddress('127.0.0.1'))),
+                ('bar@quux.test', dict(ip4=ipaddr.IPAddress('127.0.0.2'))),
         ])
 
     def testHostsConfigDdefaults(self):
@@ -151,7 +153,7 @@
         
         self.assertHostsEqual(hosts, [
                 ('foo@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddr.IPAddress('192.0.2.1'),
                     ethernet    = { 'eth0': '00:11:22:33:44:55' },
                     boot        = { 'next-server': 'boot.lan', 'filename': '/pxelinux.0' },
                 )),
@@ -162,10 +164,10 @@
     def testApplyIncludes(self):
         self.assertHostsEqual(config.apply_hosts_files(self.options, ['etc/hosts/test']), [
                 ('bar@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddr.IPAddress('192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddr.IPAddress('192.0.2.1'),
                 )),
         ])
 
@@ -176,10 +178,10 @@
 include = etc/hosts/test
         """)), [
                 ('bar@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddr.IPAddress('192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddr.IPAddress('192.0.2.1'),
                 )),
         ])
 
@@ -194,13 +196,13 @@
 
         self.assertHostsEqual(hosts, [
                 ('quux@asdf.test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.5'),
+                    ip4         = ipaddr.IPAddress('192.0.2.5'),
                 )),
                 ('bar@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddr.IPAddress('192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddr.IPAddress('192.0.2.1'),
                 )),
         ])
 
@@ -216,11 +218,11 @@
     def testApply(self):
         self.assertHostsEqual(config.apply(self.options, ['etc/hosts/example.com']), [
                 ('foo@example.com', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddr.IPAddress('192.0.2.1'),
                     ethernet    = {None: '00:11:22:33:44:55'},
                 )),
                 ('bar@example.com', dict(
-                    ip          = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddr.IPAddress('192.0.2.2'),
                     ethernet    = {None: '01:23:45:67:89:ab'},
                 )),
         ])
--- a/pvl/hosts/zone.py	Sat Mar 07 16:31:11 2015 +0200
+++ b/pvl/hosts/zone.py	Mon Mar 09 18:00:18 2015 +0200
@@ -31,15 +31,21 @@
     
     elif host.forward is None:
         # forward
-        if host.ip :
-            log.info("%s: ip: %s@%s A %s", host, label, origin, host.ip)
+        for sublabel, (ip4, ip6) in host.ip.iteritems():
+            if sublabel:
+                sublabel = pvl.dns.join(sublabel, label)
+            else:
+                sublabel = label
+            
+            if ip4:
+                log.info("%s: ip: %s@%s A %s", host, sublabel, origin, ip4)
 
-            yield pvl.dns.ZoneRecord.A(label, host.ip)
+                yield pvl.dns.ZoneRecord.A(sublabel, ip4)
 
-        if host.ip6 :
-            log.info("%s: ip6: %s@%s AAAA %s", host, label, origin, host.ip6)
+            if ip6:
+                log.info("%s: ip6: %s@%s AAAA %s", host, label, origin, ip6)
 
-            yield pvl.dns.ZoneRecord.AAAA(label, host.ip6)
+                yield pvl.dns.ZoneRecord.AAAA(sublabel, ip6)
 
     else:
         log.info("%s: skip forward", host)
@@ -57,10 +63,10 @@
         yield pvl.dns.ZoneRecord.CNAME(pvl.dns.relative(origin, host.domain, alias), label)
 
     for alias in host.alias4:
-        if not host.ip:
-            raise HostZoneError(host, "alias4={host.alias4} without ip=".format(host=host))
+        if not host.ip4:
+            raise HostZoneError(host, "alias4={host.alias4} without ip4=".format(host=host))
 
-        yield pvl.dns.ZoneRecord.A(pvl.dns.relative(origin, host.domain, alias), host.ip)
+        yield pvl.dns.ZoneRecord.A(pvl.dns.relative(origin, host.domain, alias), host.ip4)
 
     for alias in host.alias6:
         if not host.ip6:
@@ -72,50 +78,54 @@
     """
         Yield (ipaddr.IPAddress, ZoneRecord) tuples for host within given prefix's reverse-dns zone.
     """
-
-    if prefix.version == 4 :
-        ip = host.ip
-        
-        # reverse= is IPv4-only
-        reverse = host.reverse
-
-    elif prefix.version == 6 :
-        ip = host.ip6
-        
-        # if reverse= is set, always omit, for lack of reverse6=
-        reverse = None if host.reverse is None else False
-
-    else :
-        raise ValueError("%s: unknown ip version: %s" % (prefix, prefix.version))
-
-    if not ip :
-        log.debug("%s: no ip%d", host, prefix.version)
-        return
+    
+    for sublabel, (ip4, ip6) in host.ip.iteritems():
+        if prefix.version == 4:
+            ip = ip4
+            
+            # reverse= is IPv4-only
+            reverse = host.reverse
 
-    if ip not in prefix :
-        log.debug("%s: %s out of prefix: %s", host, ip, prefix)
-        return
-    
-    # relative label
-    label = pvl.dns.reverse_label(prefix, ip)
-   
-    if reverse:
-        alias = pvl.dns.fqdn(reverse)
-        
-        log.info("%s %s[%s]: CNAME %s", host, prefix, ip, alias)
+        elif prefix.version == 6:
+            ip = ip6
+            
+            # if reverse= is set, always omit, for lack of reverse6=
+            reverse = None if host.reverse is None else False
 
-        yield ip, pvl.dns.zone.ZoneRecord.CNAME(label, alias)
-
-    elif reverse is None :
-        fqdn = host.fqdn()
+        else:
+            raise ValueError("%s: unknown ip version: %s" % (prefix, prefix.version))
 
-        log.info("%s %s[%s]: PTR %s", host, prefix, ip, fqdn)
+        if not ip:
+            log.debug("%s: no ip%d", host, prefix.version)
+            continue
 
-        yield ip, pvl.dns.zone.ZoneRecord.PTR(label, fqdn)
+        if ip not in prefix:
+            log.debug("%s: %s out of prefix: %s", host, ip, prefix)
+            continue
+        
+        # relative label
+        label = pvl.dns.reverse_label(prefix, ip)
+       
+        if reverse:
+            alias = pvl.dns.fqdn(reverse)
+            
+            log.info("%s %s[%s]: CNAME %s", host, prefix, ip, alias)
 
-    else:
-        log.info("%s %s[%s]: omit", host, prefix, ip)
- 
+            yield ip, pvl.dns.zone.ZoneRecord.CNAME(label, alias)
+
+        elif reverse is None:
+            fqdn = host.fqdn()
+
+            if sublabel:
+                fqdn = pvl.dns.join(sublabel, fqdn)
+
+            log.info("%s %s[%s]: PTR %s", host, prefix, ip, fqdn)
+
+            yield ip, pvl.dns.zone.ZoneRecord.PTR(label, fqdn)
+
+        else:
+            log.info("%s %s[%s]: omit", host, prefix, ip)
+     
 def apply_hosts_forward (hosts, origin,
         add_origin      = False,
         check_conflicts = False,