I'm using the netaddr
module and trying to figure out how/if I can split a network into subnets of varying prefixes. For example take a /16 and split it into X /23s and Y /24s.
From what I can tell we can use the subnet
function to split a network in X number of a given prefix but it only takes 1 prefix.
The above would slice out 4 /23s out of the /16 which is great, but how do I take the remaining space and slice that into a differing prefix?
ip = IPNetwork('172.24.0.0/16')
subnets = list(ip.subnet(23, count=4))
Is there a way that I can achieve what I'm trying to do with netaddr?
Try the below
from netaddr import IPNetwork, cidr_merge, cidr_exclude
class IPSplitter(object):
def __init__(self, base_range):
self.avail_ranges = set((IPNetwork(base_range),))
def get_subnet(self, prefix, count=None):
for ip_network in self.get_available_ranges():
subnets = list(ip_network.subnet(prefix, count=count))
if not subnets:
continue
self.remove_avail_range(ip_network)
self.avail_ranges = self.avail_ranges.union(set(cidr_exclude(ip_network, cidr_merge(subnets)[0])))
return subnets
def get_available_ranges(self):
return sorted(self.avail_ranges, key=lambda x: x.prefixlen, reverse=True)
def remove_avail_range(self, ip_network):
self.avail_ranges.remove(ip_network)
You can use it like below. With some more math you can make it more efficient,like chopping of subnets from two different blocks when count is not satisfied from a single block.
In [1]: from ip_splitter import IPSplitter
In [2]: s = IPSplitter('172.24.0.0/16')
In [3]: s.get_available_ranges()
Out[3]: [IPNetwork('172.24.0.0/16')]
In [4]: s.get_subnet(23, count=4)
Out[4]:
[IPNetwork('172.24.0.0/23'),
IPNetwork('172.24.2.0/23'),
IPNetwork('172.24.4.0/23'),
IPNetwork('172.24.6.0/23')]
In [5]: s.get_available_ranges()
Out[5]:
[IPNetwork('172.24.8.0/21'),
IPNetwork('172.24.16.0/20'),
IPNetwork('172.24.32.0/19'),
IPNetwork('172.24.64.0/18'),
IPNetwork('172.24.128.0/17')]
In [6]: s.get_subnet(28, count=10)
Out[6]:
[IPNetwork('172.24.8.0/28'),
IPNetwork('172.24.8.16/28'),
IPNetwork('172.24.8.32/28'),
IPNetwork('172.24.8.48/28'),
IPNetwork('172.24.8.64/28'),
IPNetwork('172.24.8.80/28'),
IPNetwork('172.24.8.96/28'),
IPNetwork('172.24.8.112/28'),
IPNetwork('172.24.8.128/28'),
IPNetwork('172.24.8.144/28')]
In [7]: s.get_available_ranges()
Out[7]:
[IPNetwork('172.24.8.128/25'),
IPNetwork('172.24.9.0/24'),
IPNetwork('172.24.10.0/23'),
IPNetwork('172.24.12.0/22'),
IPNetwork('172.24.16.0/20'),
IPNetwork('172.24.32.0/19'),
IPNetwork('172.24.64.0/18'),
IPNetwork('172.24.128.0/17')]
In my case the prefixes aren't completely arbitrary and they'll always result in an even split. For example for a /24 I'll always ask for 6 /27s and 4 /28s and in a /23 I'll always ask for 2 /25s, 2 /26s, 2 /27s and 4 /28s and I'll always be starting with either a /24 or /23 so the scheme is fairly predictable.
This is what I'm going with for the /24 and similarly for the /23:
ip = IPNetwork('10.40.72.0/24')
network28s = list(ip.subnet(28, count=16))
presentation1 = list(network28s[0:1])
presentation2 = list(network28s[1:2])
database1 = list(network28s[2:3])
database2 = list(network28s[3:4])
internetlb1 = cidr_merge(list(network28s[4:6]))
internetlb2 = cidr_merge(list(network28s[6:8]))
internet1 = cidr_merge(list(network28s[8:10]))
internet2 = cidr_merge(list(network28s[10:12]))
application1 = cidr_merge(list(network28s[12:14]))
application2 = cidr_merge(list(network28s[14:16]))
I could only do this because of the fact that the scheme is so predictable and by predictable I mean, those 10 subnets will always be requested and they'll always be the same size the only variance is the CIDR itself.
You could first split it into a number of /23
and then split a certain number of those into the number of /24
you want. Quick example code off the top of my head.
>>> from netaddr import *
>>> ip = IPNetwork('172.24.0.0/16')
>>> subnet_23 = list(ip.subnet(23))
>>> subnet_23[:6]
[IPNetwork('172.24.0.0/23'), IPNetwork('172.24.2.0/23'), IPNetwork('172.24.4.0/23'), IPNetwork('172.24.6.0/23'), IPNetwork('172.24.8.0/23'), IPNetwork('172.24.10.0/23')]
>>> len(subnet_23)
128
>>> final_subnets = []
>>> number_of_24s_wanted = 10
>>> for s in subnet_23:
... if len(final_subnets) < number_of_24s_wanted:
... final_subnets.extend(list(s.subnet(24)))
... else:
... final_subnets.append(s)
...
>>> final_subnets[:20]
[IPNetwork('172.24.0.0/24'), IPNetwork('172.24.1.0/24'), IPNetwork('172.24.2.0/24'), IPNetwork('172.24.3.0/24'), IPNetwork('172.24.4.0/24'), IPNetwork('172.24.5.0/24'), IPNetwork('172.24.6.0/24'), IPNetwork('172.24.7.0/24'), IPNetwork('172.24.8.0/24'), IPNetwork('172.24.9.0/24'), IPNetwork('172.24.10.0/23'), IPNetwork('172.24.12.0/23'), IPNetwork('172.24.14.0/23'), IPNetwork('172.24.16.0/23'), IPNetwork('172.24.18.0/23'), IPNetwork('172.24.20.0/23'), IPNetwork('172.24.22.0/23'), IPNetwork('172.24.24.0/23'), IPNetwork('172.24.26.0/23'), IPNetwork('172.24.28.0/23')]
>>> len(final_subnets)
133
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With