pvl.hosts.hosts: drop support for instanced ip.* in favor of improved interface:ip.* =
authorTero Marttila <terom@paivola.fi>
Mon, 09 Mar 2015 23:31:13 +0200
changeset 738 3104fdf7ea26
parent 737 6ba76ac0bc72
child 739 5149c39f3dfc
pvl.hosts.hosts: drop support for instanced ip.* in favor of improved interface:ip.* =
pvl/hosts/host.py
pvl/hosts/interface.py
--- a/pvl/hosts/host.py	Mon Mar 09 23:21:43 2015 +0200
+++ b/pvl/hosts/host.py	Mon Mar 09 23:31:13 2015 +0200
@@ -138,7 +138,7 @@
         # empty value
         return False
 
-def parse_dict(value, parse):
+def parse_dict(value, parse, **opts):
     if not value:
         return { }
 
@@ -147,7 +147,7 @@
     else:
         values = {None: value}
 
-    return { instance: parse(value) for instance, value in values.iteritems() }
+    return {instance: parse(value, **opts) for instance, value in values.iteritems()}
 
 class Host (object) :
     """
@@ -184,17 +184,10 @@
             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,
-                ip4         = ip4.get(None),
-                ip6         = ip6.get(None),
-                ip          = ip,
+                ip4         = parse_ip(ip, ipaddr.IPv4Address),
+                ip6         = parse_ip(ip6, ipaddr.IPv6Address),
                 ethernet    = parse_dict(ethernet,  parse_ethernet),
                 owner       = owner,
                 location    = parse_location(location, domain),
@@ -210,7 +203,6 @@
 
     def __init__ (self, name, domain,
             ip4=None, ip6=None,
-            ip={},
             ethernet={ },
             owner=None,
             location=None,
@@ -225,7 +217,6 @@
             domain      - str
             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
@@ -245,7 +236,6 @@
         self.domain = domain
         self.ip4 = ip4
         self.ip6 = ip6
-        self.ip = ip
         self.ethernet = ethernet
         self.alias = alias
         self.alias4 = alias4
@@ -263,8 +253,8 @@
             Stable sort ordering
         """
 
-        if self.ip:
-            return self.ip
+        if self.ip4:
+            return self.ip4
         else:
             # sorts first
             return ipaddr.IPAddress(0)
@@ -274,19 +264,21 @@
             Yield (sublabel, ipaddr) records.
         """
 
-        for sublabel, (ip4, ip6) in self.ip.iteritems():
+        if self.ip4:
+            yield None, self.ip4
 
-            if ip4:
-                yield sublabel, ip4
-
-            if ip6:
-                yield sublabel, ip6
+        if self.ip6:
+            yield None, self.ip6
 
         for extension in self.extensions.itervalues():
             for sublabel, ip in extension.addresses():
                 yield sublabel, ip
 
     def fqdn (self):
+        """
+            Return DNS FQDN for this host in its domain.
+        """
+
         if self.domain:
             return pvl.dns.fqdn(self.name, self.domain)
         else:
@@ -299,10 +291,16 @@
 
 class HostExtension (object):
     """
-        Extension hooks
+        Base class for Host.EXTENSIONS
+
+        Provides default no-op behaviours for extension hooks.
     """
 
     def addresses (self):
+        """
+            Yield additional (sublabel, ipaddr) records.
+        """
+
         return ()
 
 def extension (cls):
--- a/pvl/hosts/interface.py	Mon Mar 09 23:21:43 2015 +0200
+++ b/pvl/hosts/interface.py	Mon Mar 09 23:31:13 2015 +0200
@@ -1,26 +1,69 @@
+import collections
 import ipaddress, ipaddr # XXX: conversion
 import pvl.hosts
+import pvl.hosts.host
 
-def parse_interfaces(interfaces):
-    for interface, value in interfaces.iteritems():
-        if not isinstance(value, dict):
-            yield (interface, None), ipaddress.ip_interface(value)
-        else:
-            for unit, ip in value.iteritems():
-                yield (interface, int(unit)), ipaddress.ip_interface(value)
+class HostInterface(object):
+    """
+        A single host-interface.
+    """
 
+    ip4 = None
+
+    def __init__(self, name):
+        self.name = name
+
+    def __str__(self):
+        return self.name
+    
 @pvl.hosts.extension
-class HostInterface(object):
+class HostInterfaces(object):
+    """
+        A host with multiple sub-interfaces.
+
+        Typically used for point-to-point interfaces between routers. For multi-homed hosts, it might make
+        more sense to use multiple hosts in different domains.
+
+            [foo]
+                interface:ip.eth0 = 10.255.1.1/30
+
+            [bar]
+                interface:ip.eth1 = 10.255.1.2/30
+    """
+
     EXTENSION = 'interface'
 
     @classmethod
-    def build (cls, **interfaces):
-        return cls(dict(parse_interfaces(interfaces)))
+    def build (cls, ip={}):
+        interfaces = collections.defaultdict(HostInterface)
+        
+        for iface, ip in pvl.hosts.host.parse_dict(ip, parse=ipaddress.ip_interface).iteritems():
+            if iface in interfaces:
+                iface = interfaces[iface]
+            else:
+                iface = interfaces[iface] = HostInterface(iface)
+
+            interfaces[iface].ip4 = ip
+        
+        return cls(interfaces)
 
     def __init__ (self, interfaces):
         self.interfaces = interfaces
 
     def addresses (self):
-        for (iface, unit), ip in self.interfaces.iteritems():
-            # XXX: ipaddr
-            yield iface, ipaddr.IPAddress(str(ip.ip))
+        """
+            Yield additional sub-addresses for host interfaces.
+        """
+
+        for iface in self:
+            if iface.ip4:
+                # XXX: convert
+                yield iface.name, ipaddr.IPv4Address(str(iface.ip4.ip))
+    
+    def __iter__(self):
+        """
+            HostInterface's with stable ordering.
+        """
+
+        return iter(sorted(self.interfaces.itervalues(), key=str))
+