summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Moss <drkjam@gmail.com>2016-09-17 22:00:54 +0100
committerDavid Moss <drkjam@gmail.com>2016-09-17 22:00:54 +0100
commitaa3d8fc168e69bd7366d58b4a6eddabd51721256 (patch)
treeb93146810f96aea90bca56bb327f5d0772c01048
parentfdd126f68a5e471f8b2c64a9a1791e44becbb900 (diff)
downloadnetaddr-aa3d8fc168e69bd7366d58b4a6eddabd51721256.tar.gz
- fixed issue #133 by adding a new SubnetSplitter class
-rw-r--r--CHANGELOG31
-rw-r--r--netaddr/__init__.py2
-rw-r--r--netaddr/contrib/__init__.py7
-rw-r--r--netaddr/contrib/subnet_splitter.py41
-rw-r--r--netaddr/tests/ip/test_ip_splitter.py74
5 files changed, 155 insertions, 0 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 78a8406..dd3746c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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')]