From eb017ad756f4ba8db98d20dbb777baddb6836d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juho=20M=C3=A4kinen?= Date: Fri, 4 Nov 2016 13:28:07 +0200 Subject: Add allocate() method The allocate() method allows to use an IPAddress::IPv4 or ::IPv6 object to allocate individual host addresses so that the IPAddress object tracks the state of which address was previously allocated. Example: ip = IPAddress("10.0.0.0/24") ip.allocate #=> "10.0.0.1/24" ip.allocate #=> "10.0.0.2/24" There is support for both IPv4 and IPv6 with full test coverage. --- lib/ipaddress/ipv4.rb | 31 +++++++++++++++++++++++++++++++ lib/ipaddress/ipv6.rb | 31 +++++++++++++++++++++++++++++++ test/ipaddress/ipv4_test.rb | 24 ++++++++++++++++++++++++ test/ipaddress/ipv6_test.rb | 24 ++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index a123e71..080f3d6 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -89,6 +89,7 @@ module IPAddress; # 32 bits interger containing the address @u32 = (@octets[0]<< 24) + (@octets[1]<< 16) + (@octets[2]<< 8) + (@octets[3]) + @allocator = 0 end # def initialize # @@ -1063,6 +1064,36 @@ module IPAddress; self.new "#{address}/#{prefix}" end + # + # Allocates a new ip from the current subnet. Optional skip parameter + # can be used to skip addresses. + # + # Will raise StopIteration exception when all addresses have been allocated + # + # Example: + # + # ip = IPAddress("10.0.0.0/24") + # ip.allocate + # #=> "10.0.0.1/24" + # ip.allocate + # #=> "10.0.0.2/24" + # ip.allocate(2) + # #=> "10.0.0.5/24" + # + # + # Uses an internal @allocator which tracks the state of allocated + # addresses. + # + def allocate(skip=0) + @allocator += 1 + skip + + next_ip = network_u32+@allocator + if next_ip > broadcast_u32+1 + raise StopIteration + end + self.class.parse_u32(network_u32+@allocator, @prefix) + end + # # private methods # diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb index 6ae651d..284308c 100644 --- a/lib/ipaddress/ipv6.rb +++ b/lib/ipaddress/ipv6.rb @@ -102,6 +102,7 @@ module IPAddress; end @prefix = Prefix128.new(netmask ? netmask : 128) + @allocator = 0 end # def initialize @@ -634,6 +635,36 @@ module IPAddress; def self.parse_hex(hex, prefix=128) self.parse_u128(hex.hex, prefix) end + + # + # Allocates a new ip from the current subnet. Optional skip parameter + # can be used to skip addresses. + # + # Will raise StopIteration exception when all addresses have been allocated + # + # Example: + # + # ip = IPAddress("10.0.0.0/24") + # ip.allocate + # #=> "10.0.0.1/24" + # ip.allocate + # #=> "10.0.0.2/24" + # ip.allocate(2) + # #=> "10.0.0.5/24" + # + # + # Uses an internal @allocator which tracks the state of allocated + # addresses. + # + def allocate(skip=0) + @allocator += 1 + skip + + next_ip = network_u128+@allocator + if next_ip > broadcast_u128 + raise StopIteration + end + self.class.parse_u128(next_ip, @prefix) + end private diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 19264e2..df60bb1 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -595,6 +595,30 @@ class IPv4Test < Minitest::Test assert_equal "192.168.200.0/24", ip.to_string end + def test_allocate_addresses + ip = @klass.new("10.0.0.0/24") + ip1 = ip.allocate + ip2 = ip.allocate + ip3 = ip.allocate + assert_equal "10.0.0.1/24", ip1.to_string + assert_equal "10.0.0.2/24", ip2.to_string + assert_equal "10.0.0.3/24", ip3.to_string + end + + def test_allocate_can_skip_addresses + ip = @klass.new("10.0.0.0/24") + ip1 = ip.allocate(2) + assert_equal "10.0.0.3/24", ip1.to_string + end + + def test_allocate_will_raise_stopiteration + ip = @klass.new("10.0.0.0/30") + ip.allocate(3) + assert_raises (StopIteration) do + ip.allocate + end + end + end # class IPv4Test diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb index 7eaa765..3990129 100644 --- a/test/ipaddress/ipv6_test.rb +++ b/test/ipaddress/ipv6_test.rb @@ -234,6 +234,30 @@ class IPv6Test < Minitest::Test assert_equal expected, arr end + def test_allocate_addresses + ip = @klass.new("2001:db8::4/125") + ip1 = ip.allocate + ip2 = ip.allocate + ip3 = ip.allocate + assert_equal "2001:db8::1", ip1.compressed + assert_equal "2001:db8::2", ip2.compressed + assert_equal "2001:db8::3", ip3.compressed + end + + def test_allocate_can_skip_addresses + ip = @klass.new("2001:db8::4/125") + ip1 = ip.allocate(2) + assert_equal "2001:db8::3", ip1.compressed + end + + def test_allocate_will_raise_stopiteration + ip = @klass.new("2001:db8::4/125") + ip.allocate(6) + assert_raises (StopIteration) do + ip.allocate + end + end + def test_method_compare ip1 = @klass.new("2001:db8:1::1/64") ip2 = @klass.new("2001:db8:2::1/64") -- cgit v1.2.1