replace ipaddr with ipaddress
authorTero Marttila <terom@paivola.fi>
Tue, 10 Mar 2015 00:26:31 +0200
changeset 740 74352351d6f5
parent 739 5149c39f3dfc
child 741 569d13a07ff5
replace ipaddr with ipaddress
pvl/dns/reverse.py
pvl/dns/zone.py
pvl/hosts/host.py
pvl/hosts/interface.py
pvl/hosts/tests.py
pvl/hosts/zone.py
requirements.txt
setup.py
--- a/pvl/dns/reverse.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/dns/reverse.py	Tue Mar 10 00:26:31 2015 +0200
@@ -1,4 +1,4 @@
-import ipaddr
+import ipaddress
 import math
 
 def reverse_ipv4 (ip) :
@@ -56,7 +56,7 @@
         # take the suffix
         nibbles = nibbles[-(hostbits / 4):]
         
-        # reverse in hex
+        # reveurse in hex
         return '.'.join(reversed(["{0:x}".format(x) for x in nibbles]))
 
     else :
@@ -80,53 +80,55 @@
     """
 
     for i in xrange(0, len(parts), 4) :
-        yield ''.join(parts[i:i+4])
+        yield u''.join(parts[i:i+4])
     
     # suffix ::
     if len(parts) < 32 :
-        yield ''
-        yield ''
+        yield u''
+        yield u''
 
 def parse_prefix (prefix) :
     """
-        Return an ipaddr.IPNetwork from given filesystem-compatbile IPv4/IPv6 prefix-ish variant.
+        Return an ipaddress.IPNetwork from given filesystem-compatbile IPv4/IPv6 prefix-ish variant.
 
         Supports partial IPv4 prefixes on octet boundaries.
         Supports partial IPv6 prefixes on nibble boundaries.
         Supports IPv4 prefxies using "-" as the prefix separator in place of "/".
 
-        >>> parse_prefix('127.0.0.0/8')
-        IPv4Network('127.0.0.0/8')
-        >>> parse_prefix('192.0.2.128/26')
-        IPv4Network('192.0.2.128/26')
-        >>> parse_prefix('192.0.2.128-26')
-        IPv4Network('192.0.2.128/26')
-        >>> parse_prefix('127.')
-        IPv4Network('127.0.0.0/8')
-        >>> parse_prefix('10')
-        IPv4Network('10.0.0.0/8')
-        >>> parse_prefix('192.168')
-        IPv4Network('192.168.0.0/16')
-        >>> parse_prefix('fe80::')
-        IPv6Network('fe80::/16')
-        >>> parse_prefix('2001:db8::')
-        IPv6Network('2001:db8::/32')
-        >>> parse_prefix('2001:db8:1:2')
-        IPv6Network('2001:db8:1:2::/64')
+        >>> print parse_prefix('127.0.0.0/8')
+        127.0.0.0/8
+        >>> print parse_prefix('192.0.2.128/26')
+        192.0.2.128/26
+        >>> print parse_prefix('192.0.2.128-26')
+        192.0.2.128/26
+        >>> print parse_prefix('127.')
+        127.0.0.0/8
+        >>> print parse_prefix('10')
+        10.0.0.0/8
+        >>> print parse_prefix('192.168')
+        192.168.0.0/16
+        >>> print parse_prefix('fe80::')
+        fe80::/16
+        >>> print parse_prefix('2001:db8::')
+        2001:db8::/32
+        >>> print parse_prefix('2001:db8:1:2')
+        2001:db8:1:2::/64
     """
 
+    prefix = unicode(prefix)
+
     if '/' in prefix :
-        return ipaddr.IPNetwork(prefix)
+        return ipaddress.ip_network(prefix)
     
     elif '-' in prefix :
-        return ipaddr.IPNetwork(prefix.replace('-', '/'))
+        return ipaddress.ip_network(prefix.replace('-', '/'))
 
     elif '.' in prefix or prefix.isdigit() :
         parts = prefix.rstrip('.').split('.')
         prefixlen = len(parts) * 8
         
-        return ipaddr.IPv4Network('{prefix}/{prefixlen}'.format(
-            prefix      = '.'.join(parts + ['0' for i in xrange(4 - len(parts))]),
+        return ipaddress.IPv4Network(u'{prefix}/{prefixlen}'.format(
+            prefix      = u'.'.join(parts + [u'0' for i in xrange(4 - len(parts))]),
             prefixlen   = prefixlen,
         ))
    
@@ -134,8 +136,8 @@
         parts = list(_split_ipv6_parts(prefix))
         prefixlen = len(parts) * 4
 
-        return ipaddr.IPv6Network('{prefix}/{prefixlen}'.format(
-            prefix      = ':'.join(_build_ipv6_parts(parts)),
+        return ipaddress.IPv6Network(u'{prefix}/{prefixlen}'.format(
+            prefix      = u':'.join(_build_ipv6_parts(parts)),
             prefixlen   = prefixlen,
         ))
 
--- a/pvl/dns/zone.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/dns/zone.py	Tue Mar 10 00:26:31 2015 +0200
@@ -6,7 +6,7 @@
 
 import codecs
 import datetime
-import ipaddr
+import ipaddress
 import logging
 import math
 import os.path
@@ -476,18 +476,18 @@
     @classmethod
     def A (_cls, name, ip4, **opts):
         """
-            Build from ipaddr.IPv4Address.
+            Build from ipaddress.IPv4Address.
         """
 
-        return _cls.build(name, 'A', ipaddr.IPv4Address(ip4), **opts)
+        return _cls.build(name, 'A', ipaddress.IPv4Address(unicode(ip4)), **opts)
 
     @classmethod
     def AAAA (_cls, name, ip6, **opts):
         """
-            Build from ipaddr.IPv6Address.
+            Build from ipaddress.IPv6Address.
         """
 
-        return _cls.build(name, 'AAAA', ipaddr.IPv6Address(ip6), **opts)
+        return _cls.build(name, 'AAAA', ipaddress.IPv6Address(unicode(ip6)), **opts)
 
     @classmethod
     def CNAME (_cls, name, alias, **opts):
--- a/pvl/hosts/host.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/hosts/host.py	Tue Mar 10 00:26:31 2015 +0200
@@ -1,5 +1,5 @@
 import collections
-import ipaddr
+import ipaddress
 import logging; log = logging.getLogger('pvl.hosts.host')
 import pvl.dns
 
@@ -156,8 +156,8 @@
 
         return cls(name,
                 domain      = domain,
-                ip4         = parse_ip(ip, ipaddr.IPv4Address),
-                ip6         = parse_ip(ip6, ipaddr.IPv6Address),
+                ip4         = parse_ip(ip, ipaddress.IPv4Address),
+                ip6         = parse_ip(ip6, ipaddress.IPv6Address),
                 ethernet    = parse_dict(ethernet,  parse_ethernet),
                 owner       = owner,
                 location    = parse_location(location, domain),
@@ -183,8 +183,8 @@
         """
             name        - str
             domain      - str
-            ip4         - primary ipaddr.IPv4Address
-            ip6         - primary ipaddr.IPv6Address
+            ip4         - ipaddress.IPv4Address
+            ip6         - ipaddress.IPv6Address
             ethernet    - { index: ethernet }
             alias       - [ str ]: generate CNAMEs for given relative names
             owner       - str: LDAP uid
@@ -224,11 +224,11 @@
             return self.ip4
         else:
             # sorts first
-            return ipaddr.IPAddress(0)
+            return ipaddress.IPvAddress(0)
 
     def addresses (self):
         """
-            Yield (sublabel, ipaddr) records.
+            Yield (sublabel, ipaddress.IP*Address) records.
         """
 
         if self.ip4:
--- a/pvl/hosts/interface.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/hosts/interface.py	Tue Mar 10 00:26:31 2015 +0200
@@ -1,5 +1,5 @@
 import collections
-import ipaddress, ipaddr # XXX: conversion
+import ipaddress
 import pvl.hosts
 import pvl.hosts.host
 
@@ -57,8 +57,7 @@
 
         for iface in self:
             if iface.ip4:
-                # XXX: convert
-                yield iface.name, ipaddr.IPv4Address(str(iface.ip4.ip))
+                yield iface.name, iface.ip4.ip
     
     def __iter__(self):
         """
--- a/pvl/hosts/tests.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/hosts/tests.py	Tue Mar 10 00:26:31 2015 +0200
@@ -1,4 +1,4 @@
-import ipaddr
+import ipaddress
 import itertools
 import pvl.args
 import unittest
@@ -122,11 +122,11 @@
 
     def testApplyHostExpand(self):
         self.assertHostsEqual(config.apply_hosts('test', 'asdf{1-3}', 
-                { 'ip': '10.100.100.$' }
+                { 'ip': u'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'))),
+                ('asdf1@test', dict(ip4=ipaddress.ip_address(u'10.100.100.1'))),
+                ('asdf2@test', dict(ip4=ipaddress.ip_address(u'10.100.100.2'))),
+                ('asdf3@test', dict(ip4=ipaddress.ip_address(u'10.100.100.3'))),
         ])
 
     def testApplyHostsFileError(self):
@@ -143,8 +143,8 @@
         """)
         
         self.assertHostsEqual(config.apply_hosts_config(self.options, conf_file), [
-                ('foo@test', dict(ip4=ipaddr.IPAddress('127.0.0.1'))),
-                ('bar@test', dict(ip4=ipaddr.IPAddress('127.0.0.2'))),
+                ('foo@test', dict(ip4=ipaddress.ip_address(u'127.0.0.1'))),
+                ('bar@test', dict(ip4=ipaddress.ip_address(u'127.0.0.2'))),
         ])
 
     def testApplyHostsConfigNested(self):
@@ -159,8 +159,8 @@
         """)
 
         self.assertHostsEqual(config.apply_hosts_config(self.options, conf_file), [
-                ('foo@asdf.test', dict(ip4=ipaddr.IPAddress('127.0.0.1'))),
-                ('bar@quux.test', dict(ip4=ipaddr.IPAddress('127.0.0.2'))),
+                ('foo@asdf.test', dict(ip4=ipaddress.ip_address(u'127.0.0.1'))),
+                ('bar@quux.test', dict(ip4=ipaddress.ip_address(u'127.0.0.2'))),
         ])
 
     def testHostsConfigDdefaults(self):
@@ -175,7 +175,7 @@
         
         self.assertHostsEqual(hosts, [
                 ('foo@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.1'),
                     ethernet    = { 'eth0': '00:11:22:33:44:55' },
                     extensions  = dict(
                         dhcp        = { 'next_server': 'boot.lan', 'filename': '/pxelinux.0' }
@@ -188,10 +188,10 @@
     def testApplyIncludes(self):
         self.assertHostsEqual(config.apply_hosts_files(self.options, ['etc/hosts/test']), [
                 ('bar@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.1'),
                 )),
         ])
 
@@ -202,10 +202,10 @@
 include = etc/hosts/test
         """)), [
                 ('bar@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.1'),
                 )),
         ])
 
@@ -220,13 +220,13 @@
 
         self.assertHostsEqual(hosts, [
                 ('quux@asdf.test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.5'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.5'),
                 )),
                 ('bar@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.2'),
                 )),
                 ('foo@test', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.1'),
                 )),
         ])
 
@@ -242,11 +242,11 @@
     def testApply(self):
         self.assertHostsEqual(config.apply(self.options, ['etc/hosts/example.com']), [
                 ('foo@example.com', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.1'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.1'),
                     ethernet    = {None: '00:11:22:33:44:55'},
                 )),
                 ('bar@example.com', dict(
-                    ip4         = ipaddr.IPAddress('192.0.2.2'),
+                    ip4         = ipaddress.ip_address(u'192.0.2.2'),
                     ethernet    = {None: '01:23:45:67:89:ab'},
                 )),
         ])
@@ -278,15 +278,15 @@
 class TestForwardZone(TestZoneMixin, unittest.TestCase):
     def testHostOutOfOrigin(self):
         h = Host.build('host', 'domain', 
-                ip  = '10.0.0.1',
+                ip  = u'10.0.0.1',
         )
 
         self.assertZoneEquals(zone.host_forward(h, 'test'), { })
 
     def testHostIP(self):
         h = Host.build('host', 'domain',
-                ip  = '192.0.2.1',
-                ip6 = '2001:db8::192.0.2.1',
+                ip  = u'192.0.2.1',
+                ip6 = u'2001:db8::192.0.2.1',
         )
 
         self.assertZoneEquals(zone.host_forward(h, 'domain'), {
@@ -296,7 +296,7 @@
     
     def testHostAlias(self):
         h = Host.build('host', 'domain',
-                ip      = '192.0.2.1',
+                ip      = u'192.0.2.1',
                 alias   = 'test *.test',
         )
 
@@ -310,8 +310,8 @@
 
     def testHostAlias46(self):
         h = Host.build('host', 'domain',
-                ip      = '192.0.2.1',
-                ip6     = '2001:db8::192.0.2.1',
+                ip      = u'192.0.2.1',
+                ip6     = u'2001:db8::192.0.2.1',
                 alias4  = 'test4',
                 alias6  = 'test6',
         )
@@ -325,7 +325,7 @@
 
     def testHostAlias4Missing(self):
         h = Host.build('host', 'domain',
-                ip6     = '2001:db8::192.0.2.1',
+                ip6     = u'2001:db8::192.0.2.1',
                 alias4  = 'test4',
                 alias6  = 'test6',
         )
@@ -335,7 +335,7 @@
 
     def testHostAlias6Missing(self):
         h = Host.build('host', 'domain',
-                ip      = '192.0.2.1',
+                ip      = u'192.0.2.1',
                 alias4  = 'test4',
                 alias6  = 'test6',
         )
@@ -345,7 +345,7 @@
 
     def testHostFQDN(self):
         h = Host.build('host.example.net', None,
-                ip          = '192.0.2.3',
+                ip          = u'192.0.2.3',
         )
 
         self.assertZoneEquals(zone.host_forward(h, 'example.com'), {
@@ -374,7 +374,7 @@
 
     def testHostLocation(self):
         h = Host.build('host', 'domain',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
                 location    = 'test',
         )
 
@@ -387,7 +387,7 @@
 
     def testHostLocationDomain(self):
         h = Host.build('host', 'foo.domain',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
                 location    = 'test@bar.domain',
         )
 
@@ -400,7 +400,7 @@
 
     def testHostLocationDomainOutOfOrigin(self):
         h = Host.build('host', 'foo.domain',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
                 location    = 'test@bar.domain',
         )
 
@@ -419,15 +419,15 @@
     def testHostsForward(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
-                    ip6     = '2001:db8::192.0.2.1',
+                    ip      = u'192.0.2.1',
+                    ip6     = u'2001:db8::192.0.2.1',
                     alias   = 'test',
                 ),
                 Host.build('bar', 'domain',
-                    ip      = '192.0.2.2',
+                    ip      = u'192.0.2.2',
                 ),
                 Host.build('quux', 'example',
-                    ip      = '192.0.2.3',
+                    ip      = u'192.0.2.3',
                 ),
         ]
                 
@@ -448,11 +448,11 @@
     def testHostsMultiAlias(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                     alias4  = 'test',
                 ),
                 Host.build('bar', 'domain',
-                    ip      = '192.0.2.2',
+                    ip      = u'192.0.2.2',
                     alias4  = 'test',
                 )
         ]
@@ -466,10 +466,10 @@
     def testHostsConflict(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                 ),
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.2',
+                    ip      = u'192.0.2.2',
                 )
         ]
         
@@ -479,10 +479,10 @@
     def testHostsAliasConflict(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip          = '192.0.2.1',
+                    ip          = u'192.0.2.1',
                 ),
                 Host.build('bar', 'domain',
-                    ip          = '192.0.2.2',
+                    ip          = u'192.0.2.2',
                     alias       = 'foo',
                 )
         ]
@@ -498,10 +498,10 @@
     def testHostsAlias4Conflict(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip          = '192.0.2.1',
+                    ip          = u'192.0.2.1',
                 ),
                 Host.build('bar', 'domain',
-                    ip          = '192.0.2.2',
+                    ip          = u'192.0.2.2',
                     alias4      = 'foo',
                 )
         ]
@@ -513,84 +513,84 @@
 class TestReverseZone(TestZoneMixin, unittest.TestCase):
     def testHostIP(self):
         h = Host.build('host', 'domain',
-                ip  = '192.0.2.1',
-                ip6 = '2001:db8::192.0.2.1',
+                ip  = u'192.0.2.1',
+                ip6 = u'2001:db8::192.0.2.1',
         )
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.2.1/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.2.0/24'))), {
             ('1', 'PTR'): 'host.domain.',
         })
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8::/64'))), {
             ('1.0.2.0.0.0.0.c.0.0.0.0.0.0.0.0', 'PTR'): 'host.domain.',
         })
 
     def testHostIP4(self):
         h = Host.build('host', 'domain',
-                ip  = '192.0.2.1',
+                ip  = u'192.0.2.1',
         )
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.2.1/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.2.0/24'))), {
             ('1', 'PTR'): 'host.domain.',
         })
         
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.0.0/16'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.0.0/16'))), {
             ('1.2', 'PTR'): 'host.domain.',
         })
         
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.0.0/12'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.0.0/12'))), {
             ('1.2.0', 'PTR'): 'host.domain.',
         })
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8::/64'))), {
 
         })
 
     def testHostIP6(self):
         h = Host.build('host', 'domain',
-                ip6 = '2001:db8::192.0.2.1',
+                ip6 = u'2001:db8::192.0.2.1',
         )
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.2.1/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.2.0/24'))), {
         })
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8::/64'))), {
             ('1.0.2.0.0.0.0.c.0.0.0.0.0.0.0.0', 'PTR'): 'host.domain.',
         })
 
     def testHostIPOutOfPrefix(self):
         h = Host.build('host', 'domain',
-                ip  = '192.0.2.1',
-                ip6 = '2001:db8::192.0.2.1',
+                ip  = u'192.0.2.1',
+                ip6 = u'2001:db8::192.0.2.1',
         )
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.1.0/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.1.0/24'))), {
 
         })
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8:1::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8:1::/64'))), {
 
         })
 
     def testHostFQDN(self):
         h = Host.build('host.example.net', None,
-                ip          = '192.0.2.3',
-                ip6         = '2001:db8::192.0.2.3',
+                ip          = u'192.0.2.3',
+                ip6         = u'2001:db8::192.0.2.3',
         )
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.2.1/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.2.0/24'))), {
             ('3', 'PTR'): 'host.example.net.',
 
         })
         
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8::/64'))), {
             ('3.0.2.0.0.0.0.c.0.0.0.0.0.0.0.0', 'PTR'): 'host.example.net.',
         })
 
     def testHostDelegate(self):
         h = Host.build('host', 'example.com',
-                ip      = '192.0.2.1',
-                ip6     = '2001:db8::192.0.2.1',
+                ip      = u'192.0.2.1',
+                ip6     = u'2001:db8::192.0.2.1',
                 forward = '',
                 reverse = '1.0/28.2.0.192.in-addr.arpa',
         )
@@ -599,31 +599,31 @@
 
         })
 
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('192.0.2.1/24'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'192.0.2.0/24'))), {
             ('1', 'CNAME'): '1.0/28.2.0.192.in-addr.arpa.',
         })
         
-        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddr.IPNetwork('2001:db8::/64'))), {
+        self.assertZoneEquals((rr for ip, rr in zone.host_reverse(h, ipaddress.ip_network(u'2001:db8::/64'))), {
 
         })
 
     def testHosts(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                 ),
                 Host.build('bar', 'domain',
-                    ip      = '192.0.2.2',
+                    ip      = u'192.0.2.2',
                 )
         ]
         
-        self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddr.IPNetwork('192.0.2.1/24')), {
+        self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddress.ip_network(u'192.0.2.0/24')), {
             ('1', 'PTR'): 'foo.domain.',
             ('2', 'PTR'): 'bar.domain.',
         })
         
         # in ip order
-        self.assertZoneEquals(zone.apply_hosts_reverse(reversed(hosts), ipaddr.IPNetwork('192.0.2.1/24')), {
+        self.assertZoneEquals(zone.apply_hosts_reverse(reversed(hosts), ipaddress.ip_network(u'192.0.2.0/24')), {
             ('1', 'PTR'): 'foo.domain.',
             ('2', 'PTR'): 'bar.domain.',
         })
@@ -631,36 +631,36 @@
     def testHostsConflict(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                 ),
                 Host.build('bar', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                 )
         ]
         
         with self.assertRaises(zone.HostZoneError):
-            self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddr.IPNetwork('192.0.2.1/24')), { })
+            self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddress.ip_network(u'192.0.2.0/24')), { })
 
     def testHostsGenerateUnknown(self):
         hosts = [
                 Host.build('foo', 'domain',
-                    ip      = '192.0.2.1',
+                    ip      = u'192.0.2.1',
                 ),
                 Host.build('bar', 'domain',
-                    ip      = '192.0.2.5',
+                    ip      = u'192.0.2.5',
                 ),
         ]
         
-        self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddr.IPNetwork('192.0.2.1/29'),
+        self.assertZoneEquals(zone.apply_hosts_reverse(hosts, ipaddress.ip_network(u'192.0.2.0/29'),
                 unknown_host = 'ufc',
                 unknown_domain = 'domain',
         ), {
-            ('1', 'PTR'): 'foo.domain.',
-            ('2', 'PTR'): 'ufc.domain.',
-            ('3', 'PTR'): 'ufc.domain.',
-            ('4', 'PTR'): 'ufc.domain.',
-            ('5', 'PTR'): 'bar.domain.',
-            ('6', 'PTR'): 'ufc.domain.',
+            ('1', 'PTR'): u'foo.domain.',
+            ('2', 'PTR'): u'ufc.domain.',
+            ('3', 'PTR'): u'ufc.domain.',
+            ('4', 'PTR'): u'ufc.domain.',
+            ('5', 'PTR'): u'bar.domain.',
+            ('6', 'PTR'): u'ufc.domain.',
         })
 
 class TestDhcp(unittest.TestCase):
@@ -685,7 +685,7 @@
     
     def testHost(self):
         host = Host.build('foo', 'test',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
                 ethernet    = '00:11:22:33:44:55',
                 owner       = 'foo',
         )
@@ -700,7 +700,7 @@
 
     def testHostFQDN(self):
         host = Host.build('foo.test', 'test',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
                 ethernet    = '00:11:22:33:44:55',
         )
 
@@ -714,7 +714,7 @@
 
     def testHostStatic(self):
         host = Host.build('foo', 'test',
-                ip          = '192.0.2.1',
+                ip          = u'192.0.2.1',
         )
 
         self.assertBlocksEqual(list(dhcp.dhcp_host(host)), [
@@ -781,11 +781,11 @@
     def testHosts(self):
         hosts = [
                 Host.build('foo', 'test',
-                        ip          = '192.0.2.1',
+                        ip          = u'192.0.2.1',
                         ethernet    = '00:11:22:33:44:55',
                 ),
                 Host.build('bar', 'test',
-                        ip          = '192.0.2.2',
+                        ip          = u'192.0.2.2',
                         ethernet    = '01:23:45:67:89:ab',
                 ),
         ]
@@ -819,11 +819,11 @@
     def testHostMultinet(self):
         hosts = [
                 Host.build('foo', 'test1',
-                    ip              = '192.0.1.1',
+                    ip              = u'192.0.1.1',
                     ethernet        = { 'eth1': '00:11:22:33:44:55' },
                 ),
                 Host.build('foo', 'test2',
-                    ip              = '192.0.2.1',
+                    ip              = u'192.0.2.1',
                     ethernet        = { 'eth2': '01:23:45:67:89:ab' },
                 ),
         ]
--- a/pvl/hosts/zone.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/pvl/hosts/zone.py	Tue Mar 10 00:26:31 2015 +0200
@@ -2,7 +2,6 @@
     Generate zonefile records from hosts
 """
 
-import ipaddr
 import logging; log = logging.getLogger('pvl.hosts.zone')
 import pvl.dns
 import pvl.hosts.host
@@ -79,7 +78,7 @@
 
 def host_reverse (host, prefix) :
     """
-        Yield (ipaddr.IPAddress, ZoneRecord) tuples for host within given prefix's reverse-dns zone.
+        Yield (ipaddress.IPAddress, ZoneRecord) tuples for host within given prefix's reverse-dns zone.
     """
     
     for sublabel, ip in host.addresses():
@@ -168,7 +167,7 @@
         Generate DNS ZoneRecords within the given prefix's reverse-dns zone for hosts.
 
             hosts: [Host]               - Host's to render PTRs for
-            prefix: ipaddr.IPNetwork    - IPv4/IPv6 prefix to render PTRs for within associated in-addr.arpa/ip6.arpa zone
+            prefix: ipaddress.IPNetwork - IPv4/IPv6 prefix to render PTRs for within associated in-addr.arpa/ip6.arpa zone
             unknown_host: str           - render a PTR to the given host @unknown_domain for unassigned IPs within prefix
             unknown_domain: str         - required if unknown_host is given
 
@@ -191,7 +190,7 @@
 
     if unknown_host :
         # enumerate all of them
-        iter_ips = prefix.iterhosts()
+        iter_ips = prefix.hosts()
     else :
         iter_ips = sorted(by_ip)
 
--- a/requirements.txt	Tue Mar 10 00:11:43 2015 +0200
+++ b/requirements.txt	Tue Mar 10 00:26:31 2015 +0200
@@ -1,1 +1,2 @@
 coverage
+ipaddress
--- a/setup.py	Tue Mar 10 00:11:43 2015 +0200
+++ b/setup.py	Tue Mar 10 00:26:31 2015 +0200
@@ -35,8 +35,7 @@
         'pvl-common',
 
         # pvl.hosts
-        # TODO: replace with ipaddress for py3 forward-compat
-        'ipaddr',
+        'ipaddress',
     ],
     extras_require = {
         # pvl.hosts-import