diff options
author | Marco Ceresa <ceresa@gmail.com> | 2009-12-09 18:51:15 +0000 |
---|---|---|
committer | Marco Ceresa <ceresa@gmail.com> | 2009-12-09 18:51:15 +0000 |
commit | 3ed340719e71debc24c0dfc80464a082560c2a90 (patch) | |
tree | e428aa559f279e7c9a0f6c7542d11a53c3a986ee | |
parent | c6bbaffb29518a500e0cafdf01d804e510d92b57 (diff) | |
download | ipaddress-3ed340719e71debc24c0dfc80464a082560c2a90.tar.gz |
Added summarize class method
-rw-r--r-- | lib/ipaddress/ipv4.rb | 119 | ||||
-rw-r--r-- | test/ipaddress/ipv4_test.rb | 66 |
2 files changed, 129 insertions, 56 deletions
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb index 13d271e..675dd1d 100644 --- a/lib/ipaddress/ipv4.rb +++ b/lib/ipaddress/ipv4.rb @@ -1,5 +1,6 @@ require 'ipaddress/ipbase' require 'ipaddress/prefix' +require 'pp' # # =Name @@ -191,11 +192,11 @@ module IPAddress; class IPv4 < IPBase # Returns the broadcast address for the given IP. # # ip = IPAddress("172.16.10.64/24") - # ip.broadcast - # #=> "172.16.10.255" + # ip.broadcast.to_s + # #=> "172.16.10.255/24" # def broadcast - [broadcast_u32].pack("N").unpack("CCCC").join(".") + self.class.parse_u32(broadcast_u32, @prefix) end # @@ -214,61 +215,60 @@ module IPAddress; class IPv4 < IPBase end # - # Returns the network number for the given IP. + # Returns a new IPv4 object with the network number + # for the given IP. # # ip = IPAddress("172.16.10.64/24") - # ip.network - # #=> "172.16.10.0" + # ip.network.to_s + # #=> "172.16.10.0/24" # def network - [network_u32].pack("N").unpack("CCCC").join(".") + self.class.parse_u32(network_u32, @prefix) end # - # This method takes the object network number (if it's - # not already a network) and returns a string with the + # Returns a new IPv4 object with the # first host IP address in the range. # # Example: given the 192.168.100.0/24 network, the first # host IP address is 192.168.100.1. # # ip = IPAddress("192.168.100.0/24") - # ip.first - # #=> "192.168.100.1" + # ip.first.to_s + # #=> "192.168.100.1/24" # # The object IP doesn't need to be a network: the method # automatically gets the network number from it # # ip = IPAddress("192.168.100.50/24") - # ip.first - # #=> "192.168.100.1" + # ip.first.to_s + # #=> "192.168.100.1/24" # def first - [network_u32 + 1].pack("N").unpack("CCCC").join(".") + self.class.parse_u32(network_u32+1, @prefix) end # - # Like its sibling method IPv4#last, this method takes - # the object network number (if it's - # not already a network) and returns a string with the + # Like its sibling method IPv4#first, this method + # returns a new IPv4 object with the # last host IP address in the range. # # Example: given the 192.168.100.0/24 network, the last # host IP address is 192.168.100.1. # # ip = IPAddress("192.168.100.0/24") - # ip.last - # #=> "192.168.100.254" + # ip.last.to_s + # #=> "192.168.100.254/24" # # The object IP doesn't need to be a network: the method # automatically gets the network number from it # # ip = IPAddress("192.168.100.50/24") - # ip.last - # #=> "192.168.100.254" + # ip.last.to_s + # #=> "192.168.100.254/24" # def last - [broadcast_u32 - 1].pack("N").unpack("CCCC").join(".") + self.class.parse_u32(broadcast_u32-1, @prefix) end # @@ -296,9 +296,12 @@ module IPAddress; class IPv4 < IPBase # Iterates over all the IP addresses for the given # network (or IP address). # + # The object yielded is a new IPv4 object created + # from the iteration. + # # ip = IPaddress("10.0.0.1/29") # ip.each do |i| - # p i + # p i.address # end # #=> "10.0.0.0" # #=> "10.0.0.1" @@ -311,7 +314,7 @@ module IPAddress; class IPv4 < IPBase # def each (network_u32..broadcast_u32).each do |i| - yield [i].pack("N").unpack("CCCC").join(".") + yield self.class.parse_u32(i, @prefix) end end @@ -364,18 +367,19 @@ module IPAddress; class IPv4 < IPBase to_a.size end + # # Returns an array with the IP addresses of # all the hosts in the network. - # # # ip = IPaddress("10.0.0.1/29") - # ip.hosts + # ip.hosts.map {|i| i.address} # #=> ["10.0.0.1", # #=> "10.0.0.2", # #=> "10.0.0.3", # #=> "10.0.0.4", # #=> "10.0.0.5", # #=> "10.0.0.6"] + # def hosts to_a[1..-2] end @@ -414,11 +418,11 @@ module IPAddress; class IPv4 < IPBase # ip.include? addr # #=> true # - # ip.include? "172.16.0.48" + # ip.include? IPAddress("172.16.0.48/16") # #=> false # def include?(oth) - to_a.include?(oth.to_s.split("/").first) + to_a.map{|i| i.address}.include?(oth.address) and @prefix <= oth.prefix end # @@ -476,9 +480,33 @@ module IPAddress; class IPv4 < IPBase end alias_method :/, :subnet + + # + # Returns a new IPv4 object from the supernetting + # of the instance network. + # + # Supernetting is similar to subnetting, except + # that you getting as a result a network with a + # smaller prefix (bigger host space). For example, + # given the network + # + # ip = IPAddress("172.16.10.0/24") + # + # you can supernet it with a new /23 prefix + # + # ip.supernet(23).to_s + # #=> "172.16.10.0/23" + # + # However if you supernet it with a /22 prefix, the + # network address will change: + # + # ip.supernet(22).to_s + # #=> "172.16.8.0/22" + # def supernet(new_prefix) - raise ArgumentError, "Can't supernet a /1 network" if prefix < 1 - IPv4.new(@address+"/#{new_prefix}").network + raise ArgumentError, "Can't supernet a /1 network" if new_prefix < 1 + raise ArgumentError, "New prefix must be smaller than existing prefix" if new_prefix >= @prefix.to_i + self.class.new(@address+"/#{new_prefix}").network end @@ -511,8 +539,35 @@ module IPAddress; class IPv4 < IPBase end def self.summarize(*args) - arr = args.sort + puts "CHIAMATA A SUMMARIZE!!:" + pp args + + # return argument if only one network given + return args.flatten if args.size == 1 + + enum=args.sort.each_cons(2) + unless enum.all? {|x,y| x.broadcast_u32 == y.network_u32-1} + raise ArgumentError, "Networks must be contiguous to be summarized" + end + + result = [] + enum.each do |x,y| + snet = x.supernet(x.prefix.to_i-1) + if snet.include? y + result << snet + else + result << x.network unless result.any?{|i| i.include? x} + end + end + lst = enum.to_a.flatten.last.network + result << lst unless result.any?{|i| i.include? lst} + + if result.size == args.size + return result + else + return self.summarize(*result) + end end @@ -544,7 +599,7 @@ module IPAddress; class IPv4 < IPBase def subnet_even(subnets) - new_prefix = prefix.to_i + Math::log2(subnets).ceil + new_prefix = @prefix.to_i + Math::log2(subnets).ceil networks = Array.new (0..subnets-1).each do |i| mul = i * (2**(32-new_prefix)) diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb index 1fc0898..efeaeff 100644 --- a/test/ipaddress/ipv4_test.rb +++ b/test/ipaddress/ipv4_test.rb @@ -36,16 +36,16 @@ class IPv4Test < Test::Unit::TestCase @network = @klass.new("172.16.10.0/24") @broadcast = { - "10.0.0.0/8" => "10.255.255.255", - "172.16.0.0/16" => "172.16.255.255", - "192.168.0.0/24" => "192.168.0.255", - "192.168.100.4/30" => "192.168.100.7"} + "10.0.0.0/8" => "10.255.255.255/8", + "172.16.0.0/16" => "172.16.255.255/16", + "192.168.0.0/24" => "192.168.0.255/24", + "192.168.100.4/30" => "192.168.100.7/30"} @networks = { - "10.5.4.3/8" => "10.0.0.0", - "172.16.5.4/16" => "172.16.0.0", - "192.168.4.3/24" => "192.168.4.0", - "192.168.100.5/30" => "192.168.100.4"} + "10.5.4.3/8" => "10.0.0.0/8", + "172.16.5.4/16" => "172.16.0.0/16", + "192.168.4.3/24" => "192.168.4.0/24", + "192.168.100.5/30" => "192.168.100.4/30"} end def test_initialize @@ -107,14 +107,16 @@ class IPv4Test < Test::Unit::TestCase def test_method_broadcast @broadcast.each do |addr,bcast| ip = @klass.new(addr) - assert_equal bcast, ip.broadcast + assert_instance_of @klass, ip.broadcast + assert_equal bcast, ip.broadcast.to_s end end def test_method_network @networks.each do |addr,net| ip = @klass.new addr - assert_equal net, ip.network + assert_instance_of @klass, ip.network + assert_equal net, ip.network.to_s end end @@ -125,31 +127,38 @@ class IPv4Test < Test::Unit::TestCase def test_method_first ip = @klass.new("192.168.100.0/24") - assert_equal "192.168.100.1", ip.first + assert_instance_of @klass, ip.first + assert_equal "192.168.100.1/24", ip.first.to_s ip = @klass.new("192.168.100.50/24") - assert_equal "192.168.100.1", ip.first + assert_instance_of @klass, ip.first + assert_equal "192.168.100.1/24", ip.first.to_s end def test_method_last ip = @klass.new("192.168.100.0/24") - assert_equal "192.168.100.254", ip.last + assert_instance_of @klass, ip.last + assert_equal "192.168.100.254/24", ip.last.to_s ip = @klass.new("192.168.100.50/24") - assert_equal "192.168.100.254", ip.last + assert_instance_of @klass, ip.last + assert_equal "192.168.100.254/24", ip.last.to_s end def test_method_each_host ip = @klass.new("10.0.0.1/29") arr = [] - ip.each_host {|i| arr << i} - expected = ["10.0.0.1","10.0.0.2","10.0.0.3","10.0.0.4","10.0.0.5","10.0.0.6"] + ip.each_host {|i| arr << i.to_s} + expected = ["10.0.0.1/29","10.0.0.2/29","10.0.0.3/29", + "10.0.0.4/29","10.0.0.5/29","10.0.0.6/29"] assert_equal expected, arr end def test_method_each ip = @klass.new("10.0.0.1/29") arr = [] - ip.each {|i| arr << i} - expected = ["10.0.0.0","10.0.0.1","10.0.0.2","10.0.0.3","10.0.0.4","10.0.0.5","10.0.0.6","10.0.0.7"] + ip.each {|i| arr << i.to_s} + expected = ["10.0.0.0/29","10.0.0.1/29","10.0.0.2/29", + "10.0.0.3/29","10.0.0.4/29","10.0.0.5/29", + "10.0.0.6/29","10.0.0.7/29"] assert_equal expected, arr end @@ -160,8 +169,9 @@ class IPv4Test < Test::Unit::TestCase def test_method_hosts ip = @klass.new("10.0.0.1/29") - expected = ["10.0.0.1","10.0.0.2","10.0.0.3","10.0.0.4","10.0.0.5","10.0.0.6"] - assert_equal expected, ip.hosts + expected = ["10.0.0.1/29","10.0.0.2/29","10.0.0.3/29", + "10.0.0.4/29","10.0.0.5/29","10.0.0.6/29"] + assert_equal expected, ip.hosts.map {|i| i.to_s} end def test_method_network_u32 @@ -176,7 +186,7 @@ class IPv4Test < Test::Unit::TestCase ip = @klass.new("192.168.10.100/24") addr = @klass.new("192.168.10.102/24") assert_equal true, ip.include?(addr) - assert_equal false, ip.include?("172.16.0.48") + assert_equal false, ip.include?(@klass.new("172.16.0.48")) end def test_method_octet @@ -223,8 +233,8 @@ class IPv4Test < Test::Unit::TestCase end def test_method_subnet - assert_raise (ArgumentError) {@ip.subnet(0)} - assert_raise (ArgumentError) {@ip.subnet(257)} + assert_raise(ArgumentError) {@ip.subnet(0)} + assert_raise(ArgumentError) {@ip.subnet(257)} arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26", "172.16.10.192/26"] assert_equal arr, @network.subnet(4).map {|s| s.to_s} @@ -234,7 +244,10 @@ class IPv4Test < Test::Unit::TestCase end def test_method_supernet - assert_equal "172.16.8.0/23", @ip.supernet(23) + assert_raise(ArgumentError) {@ip.supernet(0)} + assert_raise(ArgumentError) {@ip.supernet(24)} + assert_equal "172.16.10.0/23", @ip.supernet(23).to_s + assert_equal "172.16.8.0/22", @ip.supernet(22).to_s end def test_classmethod_parse_u32 @@ -246,6 +259,11 @@ class IPv4Test < Test::Unit::TestCase end def test_classmethod_summarize + assert_raise(ArgumentError) do + ip1 = @klass.new("172.16.10.1/24") + ip2 = @klass.new("172.16.12.2/24") + @klass.summarize(ip1,ip2) + end ip1 = @klass.new("172.16.10.1/24") ip2 = @klass.new("172.16.11.2/24") # assert_equal "172.16.10.1/23", @klass.summarize(ip1,ip2).to_s |