pvl.hosts.hosts: drop support for instanced ip.* in favor of improved interface:ip.* =
--- 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))
+