summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Ceresa <ceresa@gmail.com>2009-12-09 18:51:15 +0000
committerMarco Ceresa <ceresa@gmail.com>2009-12-09 18:51:15 +0000
commit3ed340719e71debc24c0dfc80464a082560c2a90 (patch)
treee428aa559f279e7c9a0f6c7542d11a53c3a986ee
parentc6bbaffb29518a500e0cafdf01d804e510d92b57 (diff)
downloadipaddress-3ed340719e71debc24c0dfc80464a082560c2a90.tar.gz
Added summarize class method
-rw-r--r--lib/ipaddress/ipv4.rb119
-rw-r--r--test/ipaddress/ipv4_test.rb66
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