diff options
author | David Moss <drkjam@gmail.com> | 2016-09-17 22:00:54 +0100 |
---|---|---|
committer | David Moss <drkjam@gmail.com> | 2016-09-17 22:00:54 +0100 |
commit | aa3d8fc168e69bd7366d58b4a6eddabd51721256 (patch) | |
tree | b93146810f96aea90bca56bb327f5d0772c01048 | |
parent | fdd126f68a5e471f8b2c64a9a1791e44becbb900 (diff) | |
download | netaddr-aa3d8fc168e69bd7366d58b4a6eddabd51721256.tar.gz |
- fixed issue #133 by adding a new SubnetSplitter class
-rw-r--r-- | CHANGELOG | 31 | ||||
-rw-r--r-- | netaddr/__init__.py | 2 | ||||
-rw-r--r-- | netaddr/contrib/__init__.py | 7 | ||||
-rw-r--r-- | netaddr/contrib/subnet_splitter.py | 41 | ||||
-rw-r--r-- | netaddr/tests/ip/test_ip_splitter.py | 74 |
5 files changed, 155 insertions, 0 deletions
@@ -1,4 +1,35 @@ --------------- +Release: 0.7.19 +--------------- +Date: ? Sep 2016 + +^^^^^^^^^^^^^^^^^^^^ +Changes since 0.7.18 +^^^^^^^^^^^^^^^^^^^^ + +* added a new SubnetSplitter class for those looking to divide up subnets. + Thanks alanwill and RyPeck and those on (Stack Overflow discussion). + +* removed bundled pytest dependency code for "python setup.py test". + +* setup.py now uses setuptools only (no more distutils) and setup_egg.py removed. + +* cleaned up INSTALL docs so they accurately reflect current Python packaging. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Specific bug fixes addressed in this release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +FIXED Issue 133: https://github.com/drkjam/netaddr/issues/133 + - Splitting a single network into multiple prefixed networks + +FIXED Issue 129: https://github.com/drkjam/netaddr/issues/129 + - fix IPAddress().netmask_bits to return 0 for 0.0.0.0 and [::] addresses + +FIXED Issue 117: https://github.com/drkjam/netaddr/issues/117 + - (python setup.py test) failing with python3 >= 3.5 + +--------------- Release: 0.7.18 --------------- Date: 4 Sep 2015 diff --git a/netaddr/__init__.py b/netaddr/__init__.py index 3b81257..383f75c 100644 --- a/netaddr/__init__.py +++ b/netaddr/__init__.py @@ -44,3 +44,5 @@ from netaddr.strategy.eui48 import (mac_eui48, mac_unix, mac_unix_expanded, from netaddr.strategy.eui64 import (eui64_base, eui64_unix, eui64_unix_expanded, eui64_cisco, eui64_bare, valid_str as valid_eui64) + +from netaddr.contrib.subnet_splitter import SubnetSplitter diff --git a/netaddr/contrib/__init__.py b/netaddr/contrib/__init__.py new file mode 100644 index 0000000..c5d098b --- /dev/null +++ b/netaddr/contrib/__init__.py @@ -0,0 +1,7 @@ +""" +The netaddr.contrib namespace for non-core code contributed by users. + +It is a testing ground for new ideas. Depending on the interest in +functionality found here, code may find its way into the core in various +ways, either as is or as additions to existing APIs. +""" diff --git a/netaddr/contrib/subnet_splitter.py b/netaddr/contrib/subnet_splitter.py new file mode 100644 index 0000000..2e39d41 --- /dev/null +++ b/netaddr/contrib/subnet_splitter.py @@ -0,0 +1,41 @@ +from netaddr.ip import IPNetwork, cidr_exclude, cidr_merge + + +class SubnetSplitter(object): + """ + A handy utility class that takes a single (large) subnet and allows + smaller subnet within its range to be extracted by CIDR prefix. Any + leaving address space is available for subsequent extractions until + all space is exhausted. + """ + def __init__(self, base_cidr): + """ + Constructor. + + :param base_cidr: an IPv4 or IPv6 address with a CIDR prefix. + (see IPNetwork.__init__ for full details). + """ + self._subnets = set([IPNetwork(base_cidr)]) + + def extract_subnet(self, prefix, count=None): + """Extract 1 or more subnets of size specified by CIDR prefix.""" + for cidr in self.available_subnets(): + subnets = list(cidr.subnet(prefix, count=count)) + if not subnets: + continue + self.remove_subnet(cidr) + self._subnets = self._subnets.union( + set( + cidr_exclude(cidr, cidr_merge(subnets)[0]) + ) + ) + return subnets + return [] + + def available_subnets(self): + """Returns a list of the currently available subnets.""" + return sorted(self._subnets, key=lambda x: x.prefixlen, reverse=True) + + def remove_subnet(self, ip_network): + """Remove a specified IPNetwork from available address space.""" + self._subnets.remove(ip_network) diff --git a/netaddr/tests/ip/test_ip_splitter.py b/netaddr/tests/ip/test_ip_splitter.py new file mode 100644 index 0000000..0db4bf7 --- /dev/null +++ b/netaddr/tests/ip/test_ip_splitter.py @@ -0,0 +1,74 @@ +import pytest + +from netaddr.ip import IPNetwork +from netaddr.contrib.subnet_splitter import SubnetSplitter + + +def test_ip_splitter(): + splitter = SubnetSplitter('172.24.0.0/16') + assert splitter.available_subnets() == [IPNetwork('172.24.0.0/16')] + + assert splitter.extract_subnet(23, count=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'), + ] + + assert splitter.available_subnets() == [ + 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'), + ] + + assert splitter.extract_subnet(28, count=10) == [ + 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'), + ] + + splitter.available_subnets() == [ + 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'), + ] + + +def test_ip_splitter_remove_same_input_range(): + s = SubnetSplitter('172.24.0.0/16') + assert s.available_subnets() == [IPNetwork('172.24.0.0/16')] + + assert s.extract_subnet(16, count=1) == [ + IPNetwork('172.24.0.0/16'), + ] + + assert s.available_subnets() == [] + + +def test_ip_splitter_remove_more_than_input_range(): + s = SubnetSplitter('172.24.0.0/16') + assert s.available_subnets() == [IPNetwork('172.24.0.0/16')] + + with pytest.raises(ValueError): + s.extract_subnet(16, count=2) + + +def test_ip_splitter_remove_prefix_larger_than_input_range(): + s = SubnetSplitter('172.24.0.0/16') + assert s.available_subnets() == [IPNetwork('172.24.0.0/16')] + assert s.extract_subnet(15, count=1) == [] + assert s.available_subnets() == [IPNetwork('172.24.0.0/16')] |