krakatoa-icmp4em 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "eventmachine", ">= 1.0.0.beta.4"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "shoulda", ">= 0"
12
+ gem "bundler", "~> 1.1.0"
13
+ gem "jeweler", "~> 1.6.4"
14
+ gem "simplecov"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ eventmachine (1.0.0.beta.4)
5
+ git (1.2.5)
6
+ jeweler (1.6.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ multi_json (1.1.0)
11
+ rake (0.9.2.2)
12
+ shoulda (3.0.1)
13
+ shoulda-context (~> 1.0.0)
14
+ shoulda-matchers (~> 1.0.0)
15
+ shoulda-context (1.0.0)
16
+ shoulda-matchers (1.0.0)
17
+ simplecov (0.6.1)
18
+ multi_json (~> 1.0)
19
+ simplecov-html (~> 0.5.3)
20
+ simplecov-html (0.5.3)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (~> 1.1.0)
27
+ eventmachine (>= 1.0.0.beta.4)
28
+ jeweler (~> 1.6.4)
29
+ shoulda
30
+ simplecov
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "krakatoa-icmp4em"
18
+ gem.homepage = "http://github.com/krakatoa/icmp4em"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Asynchronous implementation of ICMP ping over EventMachine}
21
+ gem.description = %Q{Asynchronous implementation of ICMP ping using EventMachine. Can be used to ping many hosts at once in a non-blocking fashion, with callbacks for success, timeout, and host failure/recovery based on specified threshold numbers.}
22
+ gem.email = "krakatoa1987@gmail.com"
23
+ gem.authors = ["Jake Douglas", "Fernando Alonso"]
24
+ gem.add_dependency "eventmachine", ">= 1.0.0.beta.4"
25
+ gem.require_paths = ["lib"]
26
+ # dependencies defined in Gemfile
27
+ end
28
+ Jeweler::RubygemsDotOrgTasks.new
29
+
30
+ require 'rake/testtask'
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ #require 'rcov/rcovtask'
38
+ #Rcov::RcovTask.new do |test|
39
+ # test.libs << 'test'
40
+ # test.pattern = 'test/**/test_*.rb'
41
+ # test.verbose = true
42
+ # test.rcov_opts << '--exclude "gems/*"'
43
+ #end
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "bliss #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'icmp4em'
3
+
4
+ # This is an example of non-stateful usage. Only the callbacks provided in on_success and on_expire are used,
5
+ # and the object does not keep track of up/down or execute callbacks on_failure/on_recovery.
6
+ # The data string can be set to anything, as long as the total packet size is less than MTU of the network.
7
+
8
+ pings = []
9
+ pings << ICMP4EM::ICMPv4.new("google.com")
10
+ pings << ICMP4EM::ICMPv4.new("slashdot.org")
11
+ pings << ICMP4EM::ICMPv4.new("10.99.99.99") # host that will not respond.
12
+
13
+ Signal.trap("INT") { EventMachine::stop_event_loop }
14
+
15
+ EM.run {
16
+ pings.each do |ping|
17
+ ping.data = "bar of foozz"
18
+ ping.on_success {|host, seq, latency| puts "SUCCESS from #{host}, sequence number #{seq}, Latency #{latency}ms"}
19
+ ping.on_expire {|host, seq, exception| puts "FAILURE from #{host}, sequence number #{seq}, Reason: #{exception.to_s}"}
20
+ ping.schedule
21
+ end
22
+ }
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'icmp4em'
3
+
4
+ # This example shows stateful usage, which tracks up/down state of the host based on consecutive number
5
+ # of successful or failing pings specified in failures_required and recoveries_required. Hosts start in
6
+ # 'up' state.
7
+
8
+ pings = []
9
+ pings << ICMP4EM::ICMPv4.new("google.com", :stateful => true)
10
+ pings << ICMP4EM::ICMPv4.new("10.1.0.175", :stateful => true) # host that will not respond.
11
+
12
+ Signal.trap("INT") { EventMachine::stop_event_loop }
13
+
14
+ EM.run {
15
+ pings.each do |ping|
16
+ ping.on_success {|host, seq, latency, count_to_recovery| puts "SUCCESS from #{host}, sequence number #{seq}, Latency #{latency}ms, Recovering in #{count_to_recovery} more"}
17
+ ping.on_expire {|host, seq, exception, count_to_failure| puts "FAILURE from #{host}, sequence number #{seq}, Reason: #{exception.to_s}, Failing in #{count_to_failure} more"}
18
+ ping.on_failure {|host| puts "HOST STATE WENT TO DOWN: #{host} at #{Time.now}"}
19
+ ping.on_recovery {|host| puts "HOST STATE WENT TO UP: #{host} at #{Time.now}"}
20
+ ping.schedule
21
+ end
22
+ }
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "krakatoa-icmp4em"
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jake Douglas", "Fernando Alonso"]
12
+ s.date = "2012-03-23"
13
+ s.description = "Asynchronous implementation of ICMP ping using EventMachine. Can be used to ping many hosts at once in a non-blocking fashion, with callbacks for success, timeout, and host failure/recovery based on specified threshold numbers."
14
+ s.email = "krakatoa1987@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "Gemfile",
20
+ "Gemfile.lock",
21
+ "README",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "examples/simple_example.rb",
25
+ "examples/stateful_example.rb",
26
+ "krakatoa-icmp4em.gemspec",
27
+ "lib/icmp4em.rb",
28
+ "lib/icmp4em/common.rb",
29
+ "lib/icmp4em/handler.rb",
30
+ "lib/icmp4em/icmpv4.rb"
31
+ ]
32
+ s.homepage = "http://github.com/krakatoa/icmp4em"
33
+ s.licenses = ["MIT"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = "1.8.10"
36
+ s.summary = "Asynchronous implementation of ICMP ping over EventMachine"
37
+
38
+ if s.respond_to? :specification_version then
39
+ s.specification_version = 3
40
+
41
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
42
+ s.add_runtime_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
43
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
44
+ s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
45
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
46
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
47
+ s.add_runtime_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
48
+ else
49
+ s.add_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
50
+ s.add_dependency(%q<shoulda>, [">= 0"])
51
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
52
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
53
+ s.add_dependency(%q<simplecov>, [">= 0"])
54
+ s.add_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
58
+ s.add_dependency(%q<shoulda>, [">= 0"])
59
+ s.add_dependency(%q<bundler>, ["~> 1.1.0"])
60
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
61
+ s.add_dependency(%q<simplecov>, [">= 0"])
62
+ s.add_dependency(%q<eventmachine>, [">= 1.0.0.beta.4"])
63
+ end
64
+ end
65
+
data/lib/icmp4em.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
2
+ require 'eventmachine'
3
+ require 'socket'
4
+ require 'icmp4em/common'
5
+ require 'icmp4em/handler'
6
+ require 'icmp4em/icmpv4'
@@ -0,0 +1,140 @@
1
+ module ICMP4EM
2
+
3
+ class Timeout < Exception; end;
4
+
5
+ module Common
6
+
7
+ ICMP_ECHOREPLY = 0
8
+ ICMP_ECHO = 8
9
+ ICMP_SUBCODE = 0
10
+
11
+ private
12
+
13
+ # Perform a checksum on the message. This is the sum of all the short
14
+ # words and it folds the high order bits into the low order bits.
15
+ # (This method was stolen directly from net-ping - yaki)
16
+ def generate_checksum(msg)
17
+ length = msg.length
18
+ num_short = length / 2
19
+ check = 0
20
+
21
+ msg.unpack("n#{num_short}").each do |short|
22
+ check += short
23
+ end
24
+
25
+ if length % 2 > 0
26
+ check += msg[length-1, 1].unpack('C').first << 8
27
+ end
28
+
29
+ check = (check >> 16) + (check & 0xffff)
30
+ return (~((check >> 16) + check) & 0xffff)
31
+ end
32
+
33
+ end
34
+
35
+ module HostCommon
36
+
37
+ # Set failure callback. The provided Proc or block will be called and yielded the host and sequence number, whenever the failure count exceeds the defined threshold.
38
+ def on_failure(proc = nil, &block)
39
+ @failure = proc || block unless proc.nil? and block.nil?
40
+ @failure
41
+ end
42
+
43
+ # Set recovery callback. The provided Proc or block will be called and yielded the host and sequence number, whenever the recovery count exceeds the defined threshold.
44
+ def on_recovery(proc = nil, &block)
45
+ @recovery = proc || block unless proc.nil? and block.nil?
46
+ @recovery
47
+ end
48
+
49
+ # Set success callback. This will be called and yielded the host, sequence number, and latency every time a ping returns successfully.
50
+ def on_success(proc = nil, &block)
51
+ @success = proc || block unless proc.nil? and block.nil?
52
+ @success
53
+ end
54
+
55
+ # Set 'expiry' callback. This will be called and yielded the host, sequence number, and Exception every time a ping fails.
56
+ # This is not just for timeouts! This can be triggered by failure of the ping for any reason.
57
+ def on_expire(proc = nil, &block)
58
+ @expiry = proc || block unless proc.nil? and block.nil?
59
+ @expiry
60
+ end
61
+
62
+ # Set the number of consecutive 'failure' pings required to switch host state to 'down' and trigger failure callback, assuming the host is up.
63
+ def failures_required=(failures)
64
+ @failures_required = failures
65
+ end
66
+
67
+ # Set the number of consecutive 'recovery' pings required to switch host state to 'up' and trigger recovery callback, assuming the host is down.
68
+ def recoveries_required=(recoveries)
69
+ @recoveries_required = recoveries
70
+ end
71
+
72
+ private
73
+
74
+ def success(seq, latency)
75
+ if @success
76
+ if @stateful
77
+ count_to_recover = @up ? 0 : @recoveries_required - @failcount.abs
78
+ @success.call(@host, seq, latency, count_to_recover)
79
+ else
80
+ @success.call(@host, seq, latency)
81
+ end
82
+ end
83
+ end
84
+
85
+ def expiry(seq, reason)
86
+ if @expiry
87
+ if @stateful
88
+ count_to_fail = @up ? @failures_required - @failcount : 0
89
+ @expiry.call(@host, seq, reason, count_to_fail)
90
+ else
91
+ @expiry.call(@host, seq, reason)
92
+ end
93
+ end
94
+ end
95
+
96
+ # Executes specified failure callback, passing the host to the block.
97
+ def fail
98
+ @failure.call(@host) if @failure
99
+ @up = false
100
+ end
101
+
102
+ # Executes specified recovery callback, passing the host to the block.
103
+ def recover
104
+ @recovery.call(@host) if @recovery
105
+ @up = true
106
+ end
107
+
108
+ # Trigger failure/recovery if either threshold is exceeded...
109
+ def check_for_fail_or_recover
110
+ if @failcount > 0
111
+ fail if @failcount >= @failures_required && @up
112
+ elsif @failcount <= -1
113
+ recover if @failcount.abs >= @recoveries_required && !@up
114
+ end
115
+ end
116
+
117
+ # Adjusts the failure counter after each ping. The failure counter is incremented positively to count failures,
118
+ # and decremented into negative numbers to indicate successful pings towards recovery after a failure.
119
+ # This is an awful mess..just like the rest of this file.
120
+ def adjust_failure_count(direction)
121
+ if direction == :down
122
+ if @failcount > -1
123
+ @failcount += 1
124
+ elsif @failcount <= -1
125
+ @failcount = 1
126
+ end
127
+ elsif direction == :up && !@up
128
+ if @failcount > 0
129
+ @failcount = -1
130
+ elsif @failcount <= -1
131
+ @failcount -= 1
132
+ end
133
+ else
134
+ @failcount = 0
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ end
@@ -0,0 +1,53 @@
1
+ module ICMP4EM
2
+
3
+ module Handler
4
+
5
+ include Common
6
+
7
+ def initialize(socket)
8
+ @socket = socket
9
+ end
10
+
11
+ def notify_readable
12
+ receive(@socket)
13
+ end
14
+
15
+ def unbind
16
+ @socket.close if @socket
17
+ end
18
+
19
+ private
20
+
21
+ def receive(socket)
22
+ # The data was available now
23
+ time = Time.now
24
+ # Get data
25
+ host, data = read_socket(socket)
26
+ # Rebuild message array
27
+ msg = data[20,30].unpack("C2 n3 A*")
28
+ # Verify the packet type is echo reply and verify integrity against the checksum it provided
29
+ return unless msg.first == ICMP_ECHOREPLY && verify_checksum?(msg)
30
+ # Find which object it is supposed to go to
31
+ recipient = ICMPv4.instances[msg[3]]
32
+ # Send time and seq number to recipient object
33
+ recipient.send(:receive, msg[4], time) unless recipient.nil?
34
+ end
35
+
36
+ def read_socket(socket)
37
+ # Recieve a common MTU, 1500 bytes.
38
+ data, sender = socket.recvfrom(1500)
39
+ # Get the host in case we want to use that later.
40
+ host = Socket.unpack_sockaddr_in(sender).last
41
+ [host, data]
42
+ end
43
+
44
+ def verify_checksum?(ary)
45
+ cs = ary[2]
46
+ ary_copy = ary.dup
47
+ ary_copy[2] = 0
48
+ cs == generate_checksum(ary_copy.pack("C2 n3 A*"))
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,174 @@
1
+ module ICMP4EM
2
+
3
+ class ICMPv4
4
+
5
+ include Common
6
+ include HostCommon
7
+
8
+ @instances = {}
9
+ @recvsocket = nil
10
+
11
+ class << self
12
+
13
+ attr_reader :instances
14
+ attr_accessor :recvsocket, :handler
15
+
16
+ end
17
+
18
+ attr_accessor :bind_host, :interval, :threshold, :timeout, :data, :block
19
+ attr_reader :id, :failures_required, :recoveries_required, :seq
20
+
21
+ # Create a new ICMP object (host). This takes a host and an optional hash of options for modifying the behavior. They are:
22
+ # * :bind_host
23
+ # Bind the socket to this address. The operating system will figure this out on it's own unless you need to set it for a special situation.
24
+ # * :timeout
25
+ # Timeout, in seconds, before the ping is considered expired and the appropriate callbacks are executed. This should be a numeric class.
26
+ # * :block
27
+ # True or false, default is false. True enables a blocking receive mode, for when accurate latency measurement is important. Due to the nature
28
+ # of event loop architecture, a noticable delay in latency can be added when other things are going on in the reactor.
29
+ # * :interval
30
+ # Interval, in seconds, for how often the ping should be sent. Should be a numeric class.
31
+ # * :stateful
32
+ # True or false, default is false. Indicates whether or not this ping object should keep track of it's successes and failures and execute
33
+ # the on_failure/on_recovery callbacks when the specified limits are hit.
34
+ # * :failures_required
35
+ # Indicates how many consequtive failures are required to switch to the 'failed' state and execute the on_failure callback. Applies only when :stateful => true
36
+ # * :recoveries_required
37
+ # Indicates how many consequtive successes are required to switch to the 'recovered' state and execute the on_recovery callback. Applies only when :stateful => true
38
+ def initialize(host, options = {})
39
+ raise 'requires root privileges' if Process.euid > 0
40
+ @host = host
41
+ @ipv4_sockaddr = Socket.pack_sockaddr_in(0, @host)
42
+ @interval = options[:interval] || 1
43
+ @timeout = options[:timeout] || 1
44
+ @stateful = options[:stateful] || false
45
+ @bind_host = options[:bind_host] || nil
46
+ @block = options[:block] || false
47
+ @recoveries_required = options[:recoveries_required] || 5
48
+ @failures_required = options[:failures_required] || 5
49
+ @up = true
50
+ @waiting = {}
51
+ set_id
52
+ @seq, @failcount = 0, 0
53
+ @data = "Ping from EventMachine"
54
+ end
55
+
56
+ # This must be called when the object will no longer be used, to remove
57
+ # the object from the class variable hash that is searched for recipients when
58
+ # an ICMP echo comes in. Also cancels the periodic timer. Better way to do this whole thing?...
59
+ def stop
60
+ @ptimer.cancel if @ptimer
61
+ self.class.instances[@id] = nil
62
+ end
63
+
64
+ # Send the echo request to @host and add sequence number to the waiting queue.
65
+ def ping
66
+ raise "EM not running" unless EM.reactor_running?
67
+ init_handler if self.class.recvsocket.nil?
68
+ @seq = ping_send
69
+ if @block
70
+ blocking_receive
71
+ else
72
+ EM.add_timer(@timeout) { self.send(:expire, @seq, Timeout.new("Ping timed out")) } unless @timeout == 0
73
+ end
74
+ @seq
75
+ end
76
+
77
+ # Uses a periodic timer to ping the host at @interval.
78
+ def schedule
79
+ raise "EM not running" unless EM.reactor_running?
80
+ @ptimer = EM::PeriodicTimer.new(@interval) { self.ping }
81
+ end
82
+
83
+ private
84
+
85
+ # Expire a sequence number from the waiting queue.
86
+ # Should only be called by the timer setup in #ping or the rescue Exception in #ping_send.
87
+ def expire(seq, exception = nil)
88
+ waiting = @waiting[seq]
89
+ if waiting
90
+ @waiting[seq] = nil
91
+ adjust_failure_count(:down) if @stateful
92
+ expiry(seq, exception)
93
+ check_for_fail_or_recover if @stateful
94
+ end
95
+ end
96
+
97
+ # Should only be called by the Handler. Passes the receive time and sequence number.
98
+ def receive(seq, time)
99
+ waiting = @waiting[seq]
100
+ if waiting
101
+ latency = (time - waiting) * 1000
102
+ adjust_failure_count(:up) if @stateful
103
+ success(seq, latency)
104
+ check_for_fail_or_recover if @stateful
105
+ @waiting[seq] = nil
106
+ end
107
+ end
108
+
109
+ # Construct and send the ICMP echo request packet.
110
+ def ping_send
111
+ seq = (@seq + 1) % 65536
112
+
113
+ socket = self.class.recvsocket
114
+
115
+ # Generate msg with checksum
116
+ msg = [ICMP_ECHO, ICMP_SUBCODE, 0, @id, seq, @data].pack("C2 n3 A*")
117
+ msg[2..3] = [generate_checksum(msg)].pack('n')
118
+
119
+ # Enqueue so we can expire properly if there is an exception raised during #send
120
+ @waiting[seq] = Time.now
121
+
122
+ begin
123
+ # Fire it off
124
+ socket.send(msg, 0, @ipv4_sockaddr)
125
+ # Re-enqueue AFTER sendto() returns. This ensures we aren't adding latency if the socket blocks.
126
+ @waiting[seq] = Time.now
127
+ # Return sequence number to caller
128
+ seq
129
+ rescue Exception => err
130
+ expire(seq, err)
131
+ seq
132
+ end
133
+ end
134
+
135
+ # Initialize the receiving socket and handler for incoming ICMP packets.
136
+ def init_handler
137
+ self.class.recvsocket = Socket.new(
138
+ Socket::PF_INET,
139
+ Socket::SOCK_RAW,
140
+ Socket::IPPROTO_ICMP
141
+ )
142
+ if @bind_host
143
+ saddr = Socket.pack_sockaddr_in(0, @bind_host)
144
+ self.class.recvsocket.bind(saddr)
145
+ end
146
+ self.class.handler = EM.watch self.class.recvsocket, Handler, self.class.recvsocket
147
+ self.class.handler.notify_readable = true
148
+ end
149
+
150
+ # Sets the instance id to a unique 16 bit integer so it can fit inside relevent the ICMP field.
151
+ # Also adds self to the pool so that incoming messages that it requested can be delivered.
152
+ def set_id
153
+ while @id.nil?
154
+ id = rand(65535)
155
+ unless self.class.instances[id]
156
+ @id = id
157
+ self.class.instances[@id] = self
158
+ end
159
+ end
160
+ end
161
+
162
+ def blocking_receive
163
+ r = select([self.class.recvsocket], nil, nil, @timeout)
164
+
165
+ if r and r.first.include?(self.class.recvsocket)
166
+ self.class.handler.notify_readable
167
+ else
168
+ expire(@seq, Timeout.new("Ping timed out"))
169
+ end
170
+ end
171
+
172
+ end
173
+
174
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: krakatoa-icmp4em
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jake Douglas
9
+ - Fernando Alonso
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-03-23 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ requirement: &27867940 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.0.beta.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *27867940
26
+ - !ruby/object:Gem::Dependency
27
+ name: shoulda
28
+ requirement: &27867460 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *27867460
37
+ - !ruby/object:Gem::Dependency
38
+ name: bundler
39
+ requirement: &27866980 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 1.1.0
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *27866980
48
+ - !ruby/object:Gem::Dependency
49
+ name: jeweler
50
+ requirement: &27866500 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.6.4
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *27866500
59
+ - !ruby/object:Gem::Dependency
60
+ name: simplecov
61
+ requirement: &27866020 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *27866020
70
+ - !ruby/object:Gem::Dependency
71
+ name: eventmachine
72
+ requirement: &27865540 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.0.beta.4
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: *27865540
81
+ description: Asynchronous implementation of ICMP ping using EventMachine. Can be used
82
+ to ping many hosts at once in a non-blocking fashion, with callbacks for success,
83
+ timeout, and host failure/recovery based on specified threshold numbers.
84
+ email: krakatoa1987@gmail.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files:
88
+ - README
89
+ files:
90
+ - Gemfile
91
+ - Gemfile.lock
92
+ - README
93
+ - Rakefile
94
+ - VERSION
95
+ - examples/simple_example.rb
96
+ - examples/stateful_example.rb
97
+ - krakatoa-icmp4em.gemspec
98
+ - lib/icmp4em.rb
99
+ - lib/icmp4em/common.rb
100
+ - lib/icmp4em/handler.rb
101
+ - lib/icmp4em/icmpv4.rb
102
+ homepage: http://github.com/krakatoa/icmp4em
103
+ licenses:
104
+ - MIT
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: 701688465436271762
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.10
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Asynchronous implementation of ICMP ping over EventMachine
130
+ test_files: []