elbping 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require 'elbping/cli.rb'
7
+
8
+ $stderr.sync = true
9
+ $stdout.sync = true
10
+
11
+ ElbPing::CLI.main
12
+
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ require 'elbping/pinger.rb'
6
+ require 'elbping/resolver.rb'
7
+ require 'elbping/display.rb'
8
+
9
+ module ElbPing
10
+ module CLI
11
+
12
+ # Set up default options
13
+ OPTIONS = {}
14
+ OPTIONS[:verb_len] = ENV['PING_ELB_MAXVERBLEN'] || 128
15
+ OPTIONS[:nameserver] = ENV['PING_ELB_NS'] || 'ns-941.amazon.com'
16
+ OPTIONS[:count] = ENV['PING_ELB_PINGCOUNT'] || 4
17
+ OPTIONS[:timeout] = ENV['PING_ELB_TIMEOUT'] || 10
18
+ OPTIONS[:wait] = ENV['PING_ELB_WAIT'] || 0
19
+
20
+ # Build parser for command line options
21
+ PARSER = OptionParser.new do |opts|
22
+ opts.banner = "Usage: #{$0} [options] <elb hostname>"
23
+
24
+ opts.on("-N NAMESERVER", "--nameserver NAMESERVER", "Use NAMESERVER to perform DNS queries") do |ns|
25
+ OPTIONS[:nameserver] = ns
26
+ end
27
+ opts.on("-L LENGTH", "--verb-length LENGTH", Integer, "Use verb LENGTH characters long") do |n|
28
+ OPTIONS[:verb_len] = n
29
+ end
30
+ opts.on("-W SECONDS", "--timeout SECONDS", Integer, "Use timeout of SECONDS for HTTP requests") do |n|
31
+ OPTIONS[:timeout] = n
32
+ end
33
+ opts.on("-w SECONDS", "--wait SECONDS", Integer, "Wait SECONDS between pings (default: 0)") do |n|
34
+ OPTIONS[:wait] = n
35
+ end
36
+ opts.on("-c COUNT", "--count COUNT", Integer, "Ping each node COUNT times") do |n|
37
+ OPTIONS[:count] = n
38
+ end
39
+ end
40
+
41
+ # Parse options
42
+ def self.usage
43
+ puts PARSER.help
44
+ exit(false)
45
+ end
46
+
47
+ # Main entry point of the program
48
+ def self.main
49
+ PARSER.parse!(ARGV) rescue usage
50
+
51
+ if ARGV.size < 1
52
+ usage
53
+ end
54
+
55
+ target = ARGV[0]
56
+ nodes = ElbPing::Resolver.find_elb_nodes(target, OPTIONS[:nameserver])
57
+
58
+ # Set up summary objects
59
+ total_summary = {
60
+ :reqs_attempted => 0,
61
+ :reqs_completed => 0,
62
+ :latencies => [],
63
+ }
64
+ node_summary = {}
65
+ nodes.each { |node| node_summary[node] = total_summary.clone }
66
+
67
+ # Catch ctrl-c
68
+ trap("INT") {
69
+ ElbPing::Display.summary(total_summary, node_summary)
70
+ exit
71
+ }
72
+
73
+ (1..OPTIONS[:count]).each { |i|
74
+ sleep OPTIONS[:wait] if i > 1
75
+
76
+ nodes.map { |node|
77
+ total_summary[:reqs_attempted] += 1
78
+ node_summary[node][:reqs_attempted] += 1
79
+ status = ElbPing::HttpPinger.ping_node(node, OPTIONS[:verb_len], OPTIONS[:timeout])
80
+
81
+ unless status[:code] == :timeout
82
+ total_summary[:reqs_completed] += 1
83
+ total_summary[:latencies] += [status[:duration]]
84
+ node_summary[node][:reqs_completed] += 1
85
+ node_summary[node][:latencies] += [status[:duration]]
86
+ end
87
+
88
+ ElbPing::Display.response(status)
89
+ }
90
+ }
91
+ ElbPing::Display.summary(total_summary, node_summary)
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,43 @@
1
+
2
+ module ElbPing
3
+ module Display
4
+ # Format and display the ping data
5
+ def self.response(status)
6
+ node = status[:node]
7
+ code = status[:code]
8
+ duration = status[:duration]
9
+
10
+ puts "Response from #{node}: code=#{code.to_s} time=#{duration} ms"
11
+ end
12
+
13
+ # Display summary of results (in aggregate and per-node)
14
+ def self.summary(total_summary, node_summary)
15
+ requests = total_summary[:reqs_attempted]
16
+ responses = total_summary[:reqs_completed]
17
+ loss = (1 - (responses.to_f/requests)) * 100
18
+
19
+ latencies = total_summary[:latencies]
20
+ avg_latency = (latencies.inject { |sum, el| sum + el }.to_f / latencies.size).to_i # ms
21
+
22
+ node_summary.each { |node, summary|
23
+ requests = summary[:reqs_attempted]
24
+ responses = summary[:reqs_completed]
25
+ loss = (1 - (responses.to_f/requests)) * 100
26
+
27
+ latencies = summary[:latencies]
28
+ avg_latency = (latencies.inject { |sum, el| sum + el }.to_f / latencies.size).to_i # ms
29
+
30
+ puts "--- #{node} statistics ---"
31
+ puts "#{requests} requests, #{responses} responses, #{loss.to_i}% loss"
32
+ puts "min/avg/max = #{latencies.min}/#{avg_latency}/#{latencies.max} ms"
33
+ }
34
+
35
+ puts '--- total statistics ---'
36
+ puts "#{requests} requests, #{responses} responses, #{loss.to_i}% loss"
37
+ puts "min/avg/max = #{latencies.min}/#{avg_latency}/#{latencies.max} ms"
38
+
39
+ end
40
+
41
+ end
42
+ end
43
+
@@ -0,0 +1,31 @@
1
+
2
+ require "net/http"
3
+
4
+ module ElbPing
5
+ module HttpPinger
6
+ # Make HTTP request to given node using custom request method
7
+ def self.ping_node(node, verb_len, timeout, port=80, path="/")
8
+
9
+ ping_request = Class.new(Net::HTTPRequest) do
10
+ const_set :METHOD, "A" * verb_len
11
+ const_set :REQUEST_HAS_BODY, false
12
+ const_set :RESPONSE_HAS_BODY, false
13
+ end
14
+
15
+ start = Time.now.getutc
16
+ http = Net::HTTP.new(node, port.to_s)
17
+ http.open_timeout = timeout
18
+ http.read_timeout = timeout
19
+ http.continue_timeout = timeout
20
+ http.ssl_timeout = timeout # untested
21
+
22
+ error = nil
23
+ response = http.request(ping_request.new(path)) rescue error = :timeout
24
+
25
+ {:code => error || response.code,
26
+ :node => node,
27
+ :duration => ((Time.now.getutc - start) * 1000).to_i} # returns in ms
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,26 @@
1
+
2
+ require 'net/dns'
3
+
4
+ module ElbPing
5
+ module Resolver
6
+ # Resolve an ELB address to a list of node IPs
7
+ def self.find_elb_nodes(target, nameserver)
8
+
9
+ # First resolve our nameserver IP
10
+ ns_addrs = Resolver(nameserver).answer.map { |rr| rr.address.to_s }
11
+
12
+ # Now resolve our ELB nodes
13
+ resolver = Net::DNS::Resolver.new(
14
+ :use_tcp => true,
15
+ :nameservers => ns_addrs,
16
+ :retry => 5)
17
+
18
+ resp = resolver.query(target, Net::DNS::ANY)
19
+
20
+ nodes = []
21
+ resp.each_address { |a| nodes += [a.to_s] }
22
+ nodes
23
+ end
24
+ end
25
+ end
26
+
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elbping
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Charles Hooper
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-dns
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.8.0
30
+ description: elbping is a tool to ping all of the nodes behind an Amazon Elastic Load
31
+ Balancer. It only works for ELBs in HTTP mode and works by triggering an HTTP 405
32
+ (METHOD NOT ALLOWED) error caused when the ELB receives a HTTP verb that is too
33
+ long.
34
+ email: chooper@plumata.com
35
+ executables:
36
+ - elbping
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - lib/elbping/cli.rb
41
+ - lib/elbping/display.rb
42
+ - lib/elbping/pinger.rb
43
+ - lib/elbping/resolver.rb
44
+ - bin/elbping
45
+ homepage: https://github.com/chooper/elbping
46
+ licenses:
47
+ - MIT
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.25
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Small tool to 'ping' the nodes that make up an Amazon Elastic Load Balancer
70
+ test_files: []