=IPSet Tests= Copyright (c) 2008-2014, David P. D. Moss. All rights reserved. {{{ >>> from netaddr import * }}} Basic operations. {{{ >>> IPSet() IPSet([]) >>> IPSet([]) IPSet([]) >>> len(IPSet([])) 0 len() fails when the IPSet is longer than sys.maxint, which is most likely with IPv6. >>> from netaddr.compat import _sys_maxint >>> s = IPSet(IPRange(IPAddress("::0"), IPAddress(_sys_maxint, 6))) >>> len(s) Traceback (most recent call last): File "", line 1, in ... IndexError: range contains more than ... >>> s = IPSet(IPRange(IPAddress("::0"), IPAddress(_sys_maxint - 1, 6))) >>> len(s) == _sys_maxint True IPSets must be usable in boolean context, even when they are very large. >>> s6 = IPSet(['2405:8100::/32']) >>> bool(s6) True >>> bool(IPSet()) False >>> IPSet(['192.0.2.0']) IPSet(['192.0.2.0/32']) >>> IPSet([IPAddress('192.0.2.0')]) IPSet(['192.0.2.0/32']) >>> IPSet([IPNetwork('192.0.2.0')]) IPSet(['192.0.2.0/32']) >>> IPSet(IPNetwork('1234::/32')) IPSet(['1234::/32']) >>> IPSet([IPNetwork('192.0.2.0/24')]) IPSet(['192.0.2.0/24']) >>> IPSet(IPSet(['192.0.2.0/32'])) IPSet(['192.0.2.0/32']) >>> IPSet(IPRange("10.0.0.0", "10.0.1.31")) IPSet(['10.0.0.0/24', '10.0.1.0/27']) >>> IPSet(IPRange('0.0.0.0', '255.255.255.255')) IPSet(['0.0.0.0/0']) >>> for ip in IPSet(['192.0.2.0/28', '::192.0.2.0/124']): ... print(ip) 192.0.2.0 192.0.2.1 192.0.2.2 192.0.2.3 192.0.2.4 192.0.2.5 192.0.2.6 192.0.2.7 192.0.2.8 192.0.2.9 192.0.2.10 192.0.2.11 192.0.2.12 192.0.2.13 192.0.2.14 192.0.2.15 ::192.0.2.0 ::192.0.2.1 ::192.0.2.2 ::192.0.2.3 ::192.0.2.4 ::192.0.2.5 ::192.0.2.6 ::192.0.2.7 ::192.0.2.8 ::192.0.2.9 ::192.0.2.10 ::192.0.2.11 ::192.0.2.12 ::192.0.2.13 ::192.0.2.14 ::192.0.2.15 }}} Adding and removing elements. {{{ >>> s1 = IPSet() >>> s1.add('192.0.2.0') >>> s1 IPSet(['192.0.2.0/32']) >>> s1.remove('192.0.2.0') >>> s1 IPSet([]) >>> s1.remove('192.0.2.0') >>> s1 IPSet([]) >>> s1.add(IPRange("10.0.0.0", "10.0.0.255")) >>> s1 IPSet(['10.0.0.0/24']) >>> s1.remove(IPRange("10.0.0.128", "10.10.10.10")) >>> s1 IPSet(['10.0.0.0/25']) This hits a special case in IPSet._compact_single_network() >>> s1.add('10.0.0.0/24') >>> s1 IPSet(['10.0.0.0/24']) }}} Set membership. {{{ >>> iprange = IPRange('192.0.1.255', '192.0.2.16') >>> iprange.cidrs() [IPNetwork('192.0.1.255/32'), IPNetwork('192.0.2.0/28'), IPNetwork('192.0.2.16/32')] >>> ipset = IPSet(['192.0.2.0/28']) >>> for ip in iprange: ... print(ip, ip in ipset) 192.0.1.255 False 192.0.2.0 True 192.0.2.1 True 192.0.2.2 True 192.0.2.3 True 192.0.2.4 True 192.0.2.5 True 192.0.2.6 True 192.0.2.7 True 192.0.2.8 True 192.0.2.9 True 192.0.2.10 True 192.0.2.11 True 192.0.2.12 True 192.0.2.13 True 192.0.2.14 True 192.0.2.15 True 192.0.2.16 False >>> bigone = IPSet(['0.0.0.0/0']) >>> IPAddress("10.0.0.1") in bigone True >>> IPAddress("0.0.0.0") in bigone True >>> IPAddress("255.255.255") in bigone True >>> IPNetwork("10.0.0.0/24") in bigone True >>> IPAddress("::1") in bigone False >>> smallone = IPSet(["10.0.0.42/32"]) >>> IPAddress("10.0.0.42") in smallone True >>> IPAddress("10.0.0.41") in smallone False >>> IPAddress("10.0.0.43") in smallone False >>> IPNetwork("10.0.0.42/32") in smallone True >>> IPNetwork("10.0.0.42/31") in smallone False }}} Set union. {{{ >>> IPSet(['192.0.2.0']) IPSet(['192.0.2.0/32']) >>> IPSet(['192.0.2.0']) | IPSet(['192.0.2.1']) IPSet(['192.0.2.0/31']) >>> IPSet(['192.0.2.0']) | IPSet(['192.0.2.1']) | IPSet(['192.0.2.3']) IPSet(['192.0.2.0/31', '192.0.2.3/32']) >>> IPSet(['192.0.2.0']) | IPSet(['192.0.2.1']) | IPSet(['192.0.2.3/30']) IPSet(['192.0.2.0/30']) >>> IPSet(['192.0.2.0']) | IPSet(['192.0.2.1']) | IPSet(['192.0.2.3/31']) IPSet(['192.0.2.0/30']) >>> IPSet(['192.0.2.0/24']) | IPSet(['192.0.3.0/24']) | IPSet(['192.0.4.0/24']) IPSet(['192.0.2.0/23', '192.0.4.0/24']) }}} A joined up example of the union, intersection and symmetric difference operations. {{{ >>> adj_cidrs = list(IPNetwork('192.0.2.0/24').subnet(28)) >>> even_cidrs = adj_cidrs[::2] >>> evens = IPSet(even_cidrs) >>> evens IPSet(['192.0.2.0/28', '192.0.2.32/28', '192.0.2.64/28', '192.0.2.96/28', '192.0.2.128/28', '192.0.2.160/28', '192.0.2.192/28', '192.0.2.224/28']) >>> IPSet(['192.0.2.0/24']) & evens IPSet(['192.0.2.0/28', '192.0.2.32/28', '192.0.2.64/28', '192.0.2.96/28', '192.0.2.128/28', '192.0.2.160/28', '192.0.2.192/28', '192.0.2.224/28']) >>> odds = IPSet(['192.0.2.0/24']) ^ evens >>> odds IPSet(['192.0.2.16/28', '192.0.2.48/28', '192.0.2.80/28', '192.0.2.112/28', '192.0.2.144/28', '192.0.2.176/28', '192.0.2.208/28', '192.0.2.240/28']) >>> evens | odds IPSet(['192.0.2.0/24']) >>> evens & odds IPSet([]) >>> evens ^ odds IPSet(['192.0.2.0/24']) }}} Superset and subset tests. {{{ >>> s1 = IPSet(['192.0.2.0/24', '192.0.4.0/24']) >>> s2 = IPSet(['192.0.2.0', '192.0.4.0']) >>> s1 IPSet(['192.0.2.0/24', '192.0.4.0/24']) >>> s2 IPSet(['192.0.2.0/32', '192.0.4.0/32']) >>> s1.issuperset(s2) True >>> s2.issubset(s1) True >>> s2.issuperset(s1) False >>> s1.issubset(s2) False }}} {{{ >>> ipv4_addr_space = IPSet(['0.0.0.0/0']) >>> private = IPSet(['10.0.0.0/8', '172.16.0.0/12', '192.0.2.0/24', '192.168.0.0/16', '239.192.0.0/14']) >>> reserved = IPSet(['225.0.0.0/8', '226.0.0.0/7', '228.0.0.0/6', '234.0.0.0/7', '236.0.0.0/7', '238.0.0.0/8', '240.0.0.0/4']) >>> unavailable = reserved | private >>> available = ipv4_addr_space ^ unavailable >>> for cidr in available.iter_cidrs(): ... print(cidr, cidr[0], cidr[-1]) 0.0.0.0/5 0.0.0.0 7.255.255.255 8.0.0.0/7 8.0.0.0 9.255.255.255 11.0.0.0/8 11.0.0.0 11.255.255.255 12.0.0.0/6 12.0.0.0 15.255.255.255 16.0.0.0/4 16.0.0.0 31.255.255.255 32.0.0.0/3 32.0.0.0 63.255.255.255 64.0.0.0/2 64.0.0.0 127.255.255.255 128.0.0.0/3 128.0.0.0 159.255.255.255 160.0.0.0/5 160.0.0.0 167.255.255.255 168.0.0.0/6 168.0.0.0 171.255.255.255 172.0.0.0/12 172.0.0.0 172.15.255.255 172.32.0.0/11 172.32.0.0 172.63.255.255 172.64.0.0/10 172.64.0.0 172.127.255.255 172.128.0.0/9 172.128.0.0 172.255.255.255 173.0.0.0/8 173.0.0.0 173.255.255.255 174.0.0.0/7 174.0.0.0 175.255.255.255 176.0.0.0/4 176.0.0.0 191.255.255.255 192.0.0.0/23 192.0.0.0 192.0.1.255 192.0.3.0/24 192.0.3.0 192.0.3.255 192.0.4.0/22 192.0.4.0 192.0.7.255 192.0.8.0/21 192.0.8.0 192.0.15.255 192.0.16.0/20 192.0.16.0 192.0.31.255 192.0.32.0/19 192.0.32.0 192.0.63.255 192.0.64.0/18 192.0.64.0 192.0.127.255 192.0.128.0/17 192.0.128.0 192.0.255.255 192.1.0.0/16 192.1.0.0 192.1.255.255 192.2.0.0/15 192.2.0.0 192.3.255.255 192.4.0.0/14 192.4.0.0 192.7.255.255 192.8.0.0/13 192.8.0.0 192.15.255.255 192.16.0.0/12 192.16.0.0 192.31.255.255 192.32.0.0/11 192.32.0.0 192.63.255.255 192.64.0.0/10 192.64.0.0 192.127.255.255 192.128.0.0/11 192.128.0.0 192.159.255.255 192.160.0.0/13 192.160.0.0 192.167.255.255 192.169.0.0/16 192.169.0.0 192.169.255.255 192.170.0.0/15 192.170.0.0 192.171.255.255 192.172.0.0/14 192.172.0.0 192.175.255.255 192.176.0.0/12 192.176.0.0 192.191.255.255 192.192.0.0/10 192.192.0.0 192.255.255.255 193.0.0.0/8 193.0.0.0 193.255.255.255 194.0.0.0/7 194.0.0.0 195.255.255.255 196.0.0.0/6 196.0.0.0 199.255.255.255 200.0.0.0/5 200.0.0.0 207.255.255.255 208.0.0.0/4 208.0.0.0 223.255.255.255 224.0.0.0/8 224.0.0.0 224.255.255.255 232.0.0.0/7 232.0.0.0 233.255.255.255 239.0.0.0/9 239.0.0.0 239.127.255.255 239.128.0.0/10 239.128.0.0 239.191.255.255 239.196.0.0/14 239.196.0.0 239.199.255.255 239.200.0.0/13 239.200.0.0 239.207.255.255 239.208.0.0/12 239.208.0.0 239.223.255.255 239.224.0.0/11 239.224.0.0 239.255.255.255 >>> ipv4_addr_space ^ available IPSet(['10.0.0.0/8', '172.16.0.0/12', '192.0.2.0/24', '192.168.0.0/16', '225.0.0.0/8', '226.0.0.0/7', '228.0.0.0/6', '234.0.0.0/7', '236.0.0.0/7', '238.0.0.0/8', '239.192.0.0/14', '240.0.0.0/4']) }}} ==Tests on combined IPv4 and IPv6 sets== {{{ >>> s1 = IPSet(['192.0.2.0', '::192.0.2.0', '192.0.2.2', '::192.0.2.2']) >>> s2 = IPSet(['192.0.2.2', '::192.0.2.2', '192.0.2.4', '::192.0.2.4']) IPSets with IPNetworks that need to be merged or split (sometimes multiple times) during various set operations. IPNetwork('10.0.0.64/30') is the same as IPRange('10.0.0.64', '10.0.0.67') >>> s3 = IPSet(['0.0.0.1', '10.0.0.64/30', '255.255.255.1']) >>> s4 = IPSet(['10.0.0.64', '10.0.0.66']) >>> s4b = IPSet(['10.0.0.64', '10.0.0.66', '111.111.111.111']) >>> s5 = IPSet(['10.0.0.65', '10.0.0.67']) >>> s1 IPSet(['192.0.2.0/32', '192.0.2.2/32', '::192.0.2.0/128', '::192.0.2.2/128']) >>> s2 IPSet(['192.0.2.2/32', '192.0.2.4/32', '::192.0.2.2/128', '::192.0.2.4/128']) }}} Set union. {{{ >>> s1 | s2 IPSet(['192.0.2.0/32', '192.0.2.2/32', '192.0.2.4/32', '::192.0.2.0/128', '::192.0.2.2/128', '::192.0.2.4/128']) >>> s2 | s1 IPSet(['192.0.2.0/32', '192.0.2.2/32', '192.0.2.4/32', '::192.0.2.0/128', '::192.0.2.2/128', '::192.0.2.4/128']) }}} Set intersection. {{{ >>> s1 & s2 IPSet(['192.0.2.2/32', '::192.0.2.2/128']) >>> s2 & s1 IPSet(['192.0.2.2/32', '::192.0.2.2/128']) >>> s3 & s4 IPSet(['10.0.0.64/32', '10.0.0.66/32']) >>> s4 & s3 IPSet(['10.0.0.64/32', '10.0.0.66/32']) >>> s3 & s5 IPSet(['10.0.0.65/32', '10.0.0.67/32']) >>> s5 & s3 IPSet(['10.0.0.65/32', '10.0.0.67/32']) }}} Set difference. {{{ >>> s1 - s2 IPSet(['192.0.2.0/32', '::192.0.2.0/128']) >>> s2 - s1 IPSet(['192.0.2.4/32', '::192.0.2.4/128']) >>> s3 - s4 IPSet(['0.0.0.1/32', '10.0.0.65/32', '10.0.0.67/32', '255.255.255.1/32']) >>> s4 - s3 IPSet([]) >>> s3 - s4b IPSet(['0.0.0.1/32', '10.0.0.65/32', '10.0.0.67/32', '255.255.255.1/32']) >>> s3 - s5 IPSet(['0.0.0.1/32', '10.0.0.64/32', '10.0.0.66/32', '255.255.255.1/32']) >>> s5 - s3 IPSet([]) }}} Symmetric set difference. {{{ >>> s1 ^ s2 IPSet(['192.0.2.0/32', '192.0.2.4/32', '::192.0.2.0/128', '::192.0.2.4/128']) >>> s2 ^ s1 IPSet(['192.0.2.0/32', '192.0.2.4/32', '::192.0.2.0/128', '::192.0.2.4/128']) >>> IPSet([]) ^ IPSet([]) IPSet([]) >>> IPSet(['0.0.0.1/32']) ^ IPSet([]) IPSet(['0.0.0.1/32']) >>> IPSet(['0.0.0.1/32']) ^ IPSet(['0.0.0.1/32']) IPSet([]) >>> s3 ^ s4 IPSet(['0.0.0.1/32', '10.0.0.65/32', '10.0.0.67/32', '255.255.255.1/32']) >>> s4 ^ s3 IPSet(['0.0.0.1/32', '10.0.0.65/32', '10.0.0.67/32', '255.255.255.1/32']) >>> s3 ^ s4b IPSet(['0.0.0.1/32', '10.0.0.65/32', '10.0.0.67/32', '111.111.111.111/32', '255.255.255.1/32']) >>> s3 ^ s5 IPSet(['0.0.0.1/32', '10.0.0.64/32', '10.0.0.66/32', '255.255.255.1/32']) >>> s5 ^ s3 IPSet(['0.0.0.1/32', '10.0.0.64/32', '10.0.0.66/32', '255.255.255.1/32']) }}} Disjointed sets. {{{ >>> s1 = IPSet(['192.0.2.0', '192.0.2.1', '192.0.2.2']) >>> s2 = IPSet(['192.0.2.2', '192.0.2.3', '192.0.2.4']) >>> s1 & s2 IPSet(['192.0.2.2/32']) >>> s1.isdisjoint(s2) False >>> s1 = IPSet(['192.0.2.0', '192.0.2.1']) >>> s2 = IPSet(['192.0.2.3', '192.0.2.4']) >>> s1 & s2 IPSet([]) >>> s1.isdisjoint(s2) True }}} Updating a set. {{{ >>> s1 = IPSet(['192.0.2.0/25']) >>> s1 IPSet(['192.0.2.0/25']) >>> s2 = IPSet(['192.0.2.128/25']) >>> s2 IPSet(['192.0.2.128/25']) >>> s1.update(s2) >>> s1 IPSet(['192.0.2.0/24']) >>> s1.update(['192.0.0.0/24', '192.0.1.0/24', '192.0.3.0/24']) >>> s1 IPSet(['192.0.0.0/22']) >>> s2 = IPSet(['10.0.0.0/16']) >>> s2.update(IPRange('10.1.0.0', '10.1.255.255')) >>> s2 IPSet(['10.0.0.0/15']) >>> s2.clear() >>> s2 IPSet([]) }}} Removing IP addresses from an IPSet. {{{ >>> s1 = IPSet(['0.0.0.0/0']) >>> s1 IPSet(['0.0.0.0/0']) >>> s1.remove('255.255.255.255') >>> s1 IPSet(['0.0.0.0/1', '128.0.0.0/2', ..., '255.255.255.252/31', '255.255.255.254/32']) >>> list(s1.iter_cidrs()) [IPNetwork('0.0.0.0/1'), IPNetwork('128.0.0.0/2'), ..., IPNetwork('255.255.255.252/31'), IPNetwork('255.255.255.254/32')] >>> len(list(s1.iter_cidrs())) 32 >>> list(s1.iter_cidrs()) == cidr_exclude('0.0.0.0/0', '255.255.255.255') True >>> s1.remove('0.0.0.0') >>> s1 IPSet(['0.0.0.1/32', '0.0.0.2/31', ..., '255.255.255.252/31', '255.255.255.254/32']) >>> len(list(s1.iter_cidrs())) 62 }}} Adding IP address to an IPSet. {{{ >>> s1.add('255.255.255.255') >>> s1 IPSet(['0.0.0.1/32', '0.0.0.2/31', ..., '64.0.0.0/2', '128.0.0.0/1']) >>> list(s1.iter_cidrs()) [IPNetwork('0.0.0.1/32'), IPNetwork('0.0.0.2/31'), ..., IPNetwork('64.0.0.0/2'), IPNetwork('128.0.0.0/1')] >>> len(list(s1.iter_cidrs())) 32 >>> s1.add('0.0.0.0') >>> s1 IPSet(['0.0.0.0/0']) }}} Converting an IP set to an IP range {{{ >>> s1 = IPSet(['10.0.0.0/25', '10.0.0.128/25']) >>> s1.iprange() IPRange('10.0.0.0', '10.0.0.255') >>> s1.iscontiguous() True >>> s1.remove('10.0.0.16') >>> s1 IPSet(['10.0.0.0/28', '10.0.0.17/32', '10.0.0.18/31', '10.0.0.20/30', '10.0.0.24/29', '10.0.0.32/27', '10.0.0.64/26', '10.0.0.128/25']) >>> s1.iscontiguous() False >>> s1.iprange() Traceback (most recent call last): ... ValueError: IPSet is not contiguous >>> list(s1.iter_ipranges()) [IPRange('10.0.0.0', '10.0.0.15'), IPRange('10.0.0.17', '10.0.0.255')] >>> list(IPSet().iter_ipranges()) [] >>> list(IPSet([IPAddress('10.0.0.1')]).iter_ipranges()) [IPRange('10.0.0.1', '10.0.0.1')] Adjacent, non-mergable CIDRs must be merged by iter_ipranges(). >>> list(IPSet([IPAddress('10.0.0.1'), IPAddress('10.0.0.2')]).iter_ipranges()) [IPRange('10.0.0.1', '10.0.0.2')] IPv4 and IPv6 addresses must not be merged. >>> list(IPSet([IPAddress(1, 4), IPAddress(1, 6)]).iter_ipranges()) [IPRange('0.0.0.1', '0.0.0.1'), IPRange('::1', '::1')] >>> s2 = IPSet(['0.0.0.0/0']) >>> s2.iscontiguous() True >>> s2.iprange() IPRange('0.0.0.0', '255.255.255.255') >>> s3 = IPSet() >>> s3.iscontiguous() True >>> s3.iprange() >>> s4 = IPSet(IPRange('10.0.0.0', '10.0.0.8')) >>> s4.iscontiguous() True }}} Pickling of IPSet objects {{{ >>> import pickle >>> ip_data = IPSet(['10.0.0.0/16', 'fe80::/64']) >>> buf = pickle.dumps(ip_data) >>> ip_data_unpickled = pickle.loads(buf) >>> ip_data == ip_data_unpickled True }}} Compare IPSet objects {{{ >>> x = IPSet(['fc00::/2']) >>> y = IPSet(['fc00::/3']) >>> x > y True >>> x < y False >>> x != y True }}}