I have a bunch of IP addresses. I want one IP network that has them all. ipaddr.collapse_address_list
already exists, but that gives you a list of all the IP networks that identify your IP addresses, no more, no less, whereas I only need one IP network.
I tried at first using XOR, but that fails on some situations (say, 10.0.0.1, 10.0.0.2, 10.0.0.255, 10.0.0.251: bin(1 ^ 2 ^ 255 ^ 251) = 0b111
). And'ing has similar problems. This is what I ended up writing.
#http://stackoverflow.com/a/3789000/13992
def _AllSame(items):
it = iter(items)
first = next(it, None)
return all(x == first for x in it)
def _GetOneNetmaskForAllTheseIPs(ips):
"""Get the one IP network that covers all IPs in input.
ipaddr.collapse_address_list is a little bit TOO smart and gives you the LIST
of networks that describe a number of IP addreses. Ceph just wants one
netmask. This function tries to get that one netmask.
>>> _GetOneNetmaskForAllTheseIPs(("10.0.0.1", "10.0.0.2",
... "10.0.0.251", "10.0.0.255"))
10.0.0.0/24
"""
#Turn each IP address in its binary representation, without 0b header
num_ip_addresses = [int(ipaddr.IPAddress(ip)) for ip in ips]
lowest_ip = ipaddr.IPAddress(min(num_ip_addresses))
bin_ip_addresses = [bin(ip)[2:] for ip in num_ip_addresses]
#"Transpose" the list (abc),(def) → (ad),(be),(cf)
bit_comparison = zip(*bin_ip_addresses)
differing_bits = len(bit_comparison)
#Find the first different bit
while _AllSame(bit_comparison[-differing_bits]):
differing_bits -= 1
#That's the no. of bits that differ. The mask is the number of bits that DON'T
mask_length = lowest_ip.max_prefixlen - differing_bits
#Return the network
network_ip = ipaddr.IPNetwork("%s/%d" % (lowest_ip, mask_length)).network
network = ipaddr.IPNetwork("%s/%d" % (network_ip, mask_length), strict = True)
return str(network)
I tried to be as IP-version agnostic as possible here, but chances are I've overlooked a few more-or-less obvious things here — or, even more likely, there's already a better inbuilt solution somewhere in the python libraries or even ipaddr
itself.