srvmonitor 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +5 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +7 -0
- data/Rakefile +27 -0
- data/lib/srvmonitor/application.rb +83 -0
- data/lib/srvmonitor/expectations/load_average.rb +28 -0
- data/lib/srvmonitor/expectations/response_body.rb +37 -0
- data/lib/srvmonitor/expectations/response_code.rb +23 -0
- data/lib/srvmonitor/expectations/response_time.rb +30 -0
- data/lib/srvmonitor/expectations.rb +5 -0
- data/lib/srvmonitor/monitor.rb +78 -0
- data/lib/srvmonitor/notifiers/mailer.rb +102 -0
- data/lib/srvmonitor/notifiers.rb +2 -0
- data/lib/srvmonitor/report.rb +24 -0
- data/lib/srvmonitor/scout.rb +82 -0
- data/lib/srvmonitor/scouts/load_average.rb +48 -0
- data/lib/srvmonitor/scouts/timed_http.rb +80 -0
- data/lib/srvmonitor/scouts.rb +3 -0
- data/lib/srvmonitor/server.rb +40 -0
- data/lib/srvmonitor/version.rb +7 -0
- data/lib/srvmonitor.rb +11 -0
- data/srvmonitor.gemspec +20 -0
- metadata +67 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.yardoc
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Vinicius Baggio Fuentes http://www.vinibaggio.net
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
require "srvmonitor/version"
|
6
|
+
|
7
|
+
desc 'Default: run tests'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run tests.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.libs << 'test'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Build a gem from gemspec'
|
19
|
+
task :build do
|
20
|
+
system "gem build srvmonitor.gemspec"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Release new gem version'
|
24
|
+
task :release => :build do
|
25
|
+
system "gem push srvmonitor-#{SrvMonitor::VERSION}.gem"
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
class Application
|
3
|
+
attr_reader :scout_klass, :scout_config, :scouts
|
4
|
+
attr_reader :servers, :reports, :name, :status
|
5
|
+
|
6
|
+
SERVER_OPTIONS = [:host, :method, :user, :password]
|
7
|
+
|
8
|
+
def self.create(&block)
|
9
|
+
self.new.tap do |application|
|
10
|
+
application.instance_eval(&block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@scouts = []
|
16
|
+
@reports = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def name(name)
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
|
23
|
+
def scout(klass, args={})
|
24
|
+
@scout_klass = klass
|
25
|
+
@scout_config = args
|
26
|
+
end
|
27
|
+
|
28
|
+
def server(server, options={})
|
29
|
+
server_config = options.select{ |key, _| key if SERVER_OPTIONS.include?(key) }
|
30
|
+
|
31
|
+
config = options.delete_if{ |key, _| server_config.include?(key) }
|
32
|
+
config[:server] = server.clone(server_config)
|
33
|
+
|
34
|
+
@scouts << @scout_klass.new(@scout_config.merge config )
|
35
|
+
end
|
36
|
+
|
37
|
+
def servers(*args)
|
38
|
+
servers = args.find_all{ |r| r.is_a?(SrvMonitor::Server) }
|
39
|
+
options = args.find{ |r| r.is_a?(Hash) }
|
40
|
+
servers.each{ |r| server(r, options) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reads reporting as:
|
44
|
+
# report :up, :response_code => 200
|
45
|
+
#
|
46
|
+
# gets stored as:
|
47
|
+
# {:response_code => 200} = up
|
48
|
+
def report(status, params)
|
49
|
+
@reports[params] = status
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
puts "\n### Runing #{label}"
|
54
|
+
statuses = []
|
55
|
+
@scouts.each do |scout|
|
56
|
+
scout.run(@reports)
|
57
|
+
statuses << scout.status
|
58
|
+
end
|
59
|
+
@status = SrvMonitor::Report.summarize(statuses)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if the last status is :up
|
63
|
+
def up?
|
64
|
+
@status == :up
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns true if the last status is :warning
|
68
|
+
def warning?
|
69
|
+
@status == :warning
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns true if the last status is :down
|
73
|
+
def down?
|
74
|
+
@status == :down
|
75
|
+
end
|
76
|
+
|
77
|
+
def label
|
78
|
+
@name || @scout_klass.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
module Expectations
|
3
|
+
# Module containing load_average matching expectations.
|
4
|
+
#
|
5
|
+
# It respond to the following rules:
|
6
|
+
# * less_than => If the load_average percent is less than the associated number
|
7
|
+
# * more_than => If the load_average percent is below than the associated number
|
8
|
+
module LoadAverage
|
9
|
+
LOAD_AVERAGE_MAPPING = {
|
10
|
+
:less_than => "<",
|
11
|
+
:more_than => ">",
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
# Installs the response time expectation
|
15
|
+
def self.extended(base)
|
16
|
+
base.expect :load_average, base.method(:evaluate_load_average)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Method that will be used as an expectation to evaluate load average
|
20
|
+
def evaluate_load_average(scout, rules)
|
21
|
+
rules.all? do |rule, comparison|
|
22
|
+
scout.load_average.send(LOAD_AVERAGE_MAPPING[rule], comparison)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
module Expectations
|
3
|
+
# Module containing response_body matching expectations. Extend your Scout
|
4
|
+
# with ResponseBody and it will gain response_body evaluation powers!
|
5
|
+
#
|
6
|
+
# It respond to the following rules:
|
7
|
+
# * match => If the response body matches the associated regular expression
|
8
|
+
# * not_match => If the response body does not match the associated regular
|
9
|
+
# expression
|
10
|
+
# * equals => If the response body matches exactly the associated string
|
11
|
+
# * differs => If the response body differs in any way the associated
|
12
|
+
# string.
|
13
|
+
module ResponseBody
|
14
|
+
# Installs the response body expectation
|
15
|
+
def self.extended(base)
|
16
|
+
base.expect :response_body, base.method(:evaluate_response_body)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Method that will be used as an expectation to evaluate response body
|
20
|
+
def evaluate_response_body(scout, rules)
|
21
|
+
rules.all? do |rule, comparison|
|
22
|
+
case rule
|
23
|
+
when :match
|
24
|
+
scout.response_body =~ comparison
|
25
|
+
when :not_match
|
26
|
+
scout.response_body !~ comparison
|
27
|
+
when :equals
|
28
|
+
scout.response_body == comparison
|
29
|
+
when :differs
|
30
|
+
scout.response_body != comparison
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
module Expectations
|
3
|
+
# Module containing response_code logic. It is the simplest of all,
|
4
|
+
# it is a simple direct equality check. Extend your Scout to win instant
|
5
|
+
# response_code checking.
|
6
|
+
module ResponseCode
|
7
|
+
# Installs the response code expectation
|
8
|
+
def self.extended(base)
|
9
|
+
base.expect :response_code, base.method(:evaluate_response_code)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Method that will be used as an expectation to evaluate response code
|
13
|
+
def evaluate_response_code(scout, response_code)
|
14
|
+
if response_code.is_a?(Array)
|
15
|
+
response_code.include?(scout.response_code)
|
16
|
+
else
|
17
|
+
scout.response_code == response_code.to_i
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
module Expectations
|
3
|
+
# Module containing response_time matching expectations. Extend your Scout
|
4
|
+
# with ResponseTime and it will evaluate timely expressions, all in
|
5
|
+
# milisseconds.
|
6
|
+
#
|
7
|
+
# It respond to the following rules:
|
8
|
+
# * less_than => If the response time is less than the associated number
|
9
|
+
# * more_than => If the response time is below than the associated number
|
10
|
+
module ResponseTime
|
11
|
+
RESPONSE_TIME_MAPPING = {
|
12
|
+
:less_than => "<",
|
13
|
+
:more_than => ">",
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
# Installs the response time expectation
|
17
|
+
def self.extended(base)
|
18
|
+
base.expect :response_time, base.method(:evaluate_response_time)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Method that will be used as an expectation to evaluate response time
|
22
|
+
def evaluate_response_time(scout, rules)
|
23
|
+
rules.all? do |rule, comparison|
|
24
|
+
scout.response_time.send(RESPONSE_TIME_MAPPING[rule], comparison)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
class Monitor
|
3
|
+
attr_reader :applications, :wait, :forever, :alert, :notifier
|
4
|
+
|
5
|
+
def self.create(&block)
|
6
|
+
self.new.tap do |monitor|
|
7
|
+
monitor.instance_eval(&block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@applications = []
|
13
|
+
@wait = 5
|
14
|
+
@forever = true
|
15
|
+
@alert = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def applications(*args)
|
19
|
+
apps = args.find_all{ |r| r.is_a?(SrvMonitor::Application) }
|
20
|
+
options = args.find{ |r| r.is_a?(Hash) }
|
21
|
+
apps.each { |r| @applications << r }
|
22
|
+
@applications
|
23
|
+
end
|
24
|
+
|
25
|
+
def wait(seconds)
|
26
|
+
@wait = seconds
|
27
|
+
end
|
28
|
+
|
29
|
+
def forever(bol)
|
30
|
+
@forever = bol
|
31
|
+
end
|
32
|
+
|
33
|
+
def alert(args={})
|
34
|
+
@alert[:if] = args[:if] || :down
|
35
|
+
@alert[:attempts] = args[:attempts] || 1
|
36
|
+
@alert[:count] = 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def notifier(notifier_klass, args={})
|
40
|
+
@notifier = notifier_klass.new(args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def status
|
44
|
+
SrvMonitor::Report.summarize @applications.map(&:status)
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
loop do
|
49
|
+
@applications.each(&:run)
|
50
|
+
|
51
|
+
unless @alert.empty?
|
52
|
+
@alert[:count] = 0 unless status == @alert[:if]
|
53
|
+
|
54
|
+
if status == @alert[:if]
|
55
|
+
if @alert[:count] < @alert[:attempts]
|
56
|
+
@alert[:count] += 1
|
57
|
+
sleep @wait
|
58
|
+
redo
|
59
|
+
else
|
60
|
+
notify!
|
61
|
+
@alert[:count] = 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
break unless @forever
|
67
|
+
sleep @wait
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def notify!
|
72
|
+
return unless @notifier
|
73
|
+
@notifier.send(self)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
begin
|
2
|
+
require 'mail'
|
3
|
+
rescue LoadError => e
|
4
|
+
puts "Please install mail gem: gem install mail"
|
5
|
+
raise
|
6
|
+
end
|
7
|
+
|
8
|
+
module SrvMonitor
|
9
|
+
module Notifiers
|
10
|
+
# The Email notifier issues Outpost notifications to through email. It
|
11
|
+
# uses the 'mail' gem send the emails. You can see mail's documentation
|
12
|
+
# in order to change how emails will be delivered:
|
13
|
+
# https://github.com/mikel/mail
|
14
|
+
#
|
15
|
+
# This requires the 'mail' gem to be installed.
|
16
|
+
class Mailer
|
17
|
+
|
18
|
+
# @param [Hash] Options to create an email notification.
|
19
|
+
# @option options [String] :delivery_method The smtp / sendmail
|
20
|
+
# @option options [String] :address The "smtp address"
|
21
|
+
# @option options [String] :port The smtp port
|
22
|
+
# @option options [String] :authentication The smtp authentication type
|
23
|
+
# @option options [String] :username The smtp username
|
24
|
+
# @option options [String] :password The smtp password
|
25
|
+
# @option options [Boolean] :enable_starttls_auto Enable smtp start tls
|
26
|
+
|
27
|
+
# @option options [String] :from The "from" email field
|
28
|
+
# @option options [String] :to Where e-mails will be delivered
|
29
|
+
# @option options [String] :subject The email's subject
|
30
|
+
# @option options [Array] :status The status to be reported
|
31
|
+
def initialize(options={})
|
32
|
+
@delivery_method = options[:delivery_method] || :sendmail
|
33
|
+
@address = options[:address]
|
34
|
+
@port = options[:port]
|
35
|
+
@username = options[:username]
|
36
|
+
@password = options[:password]
|
37
|
+
@authentication = options[:authentication] || :plain
|
38
|
+
@enable_starttls_auto = options[:enable_starttls_auto] || false
|
39
|
+
@from = options[:from] || options[:username]
|
40
|
+
@to = options[:to]
|
41
|
+
@subject = options[:subject] || 'SrvMonitor notification'
|
42
|
+
@status = [ options[:status] ].flatten if options[:status]
|
43
|
+
|
44
|
+
raise ArgumentError, 'You need to set :from and :to to send emails.' if @from.nil? || @to.nil?
|
45
|
+
|
46
|
+
if @delivery_method == :smtp && (@address.nil? || @port.nil? || @username.nil? || @password.nil?)
|
47
|
+
raise ArgumentError, 'You need to set :address, :port, :username, :password, :from and :to to send emails by smtp.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Issues a notification through email. This is a callback, called by
|
52
|
+
# an Outpost instance.
|
53
|
+
# @param [Outpost::Application, #read] outpost an instance of an outpost, containing
|
54
|
+
# latest status, messages and reports that can be queried to build
|
55
|
+
# a notification message.
|
56
|
+
def send(monitor)
|
57
|
+
smtp_connect! if @delivery_method == :smtp
|
58
|
+
|
59
|
+
mail = Mail.new
|
60
|
+
mail.from = @from
|
61
|
+
mail.to = @to
|
62
|
+
mail.subject = @subject
|
63
|
+
mail.body = build_message(monitor)
|
64
|
+
|
65
|
+
#mail.delivery_method @delivery_method
|
66
|
+
|
67
|
+
mail.deliver
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def smtp_connect!
|
72
|
+
smtp_conn = Net::SMTP.new(@address, @port)
|
73
|
+
smtp_conn.enable_starttls if @enable_starttls_auto
|
74
|
+
smtp_conn.start(@address, @username, @password, @authentication)
|
75
|
+
|
76
|
+
Mail.defaults do
|
77
|
+
delivery_method :smtp_connection, { :connection => smtp_conn }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_message(monitor)
|
82
|
+
status = monitor.status.to_s
|
83
|
+
|
84
|
+
message = "System is #{status.upcase}!\n\n"
|
85
|
+
|
86
|
+
monitor.applications.each do |application|
|
87
|
+
application_message = ""
|
88
|
+
|
89
|
+
for scout in application.scouts
|
90
|
+
next if @status && !@status.include?(scout.status)
|
91
|
+
application_message += "\n#{scout.output}"
|
92
|
+
end
|
93
|
+
|
94
|
+
message += "### #{application.label} #{application_message}\n\n" if application_message.size > 0
|
95
|
+
end
|
96
|
+
|
97
|
+
message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
# Contain the status report of an Outpost execution. Holds the name,
|
3
|
+
# description and status of the reported item.
|
4
|
+
class Report
|
5
|
+
# Summarizes the list of statuses in a single status only.
|
6
|
+
# The logic is rather simple - it will return the lowest status
|
7
|
+
# present in the list.
|
8
|
+
#
|
9
|
+
# Examples:
|
10
|
+
#
|
11
|
+
# if passed [:up, :up, :up], will result on :up
|
12
|
+
#
|
13
|
+
# if passed [:up, :down, :up], will result on :down
|
14
|
+
#
|
15
|
+
# @params [Array] status_list a list of statuses to be analyzed
|
16
|
+
# @return [Symbol] the final status to be considered.
|
17
|
+
def self.summarize(status_list)
|
18
|
+
return :down if status_list.empty? || status_list.include?(:down)
|
19
|
+
return :warning if status_list.include?(:warning)
|
20
|
+
return :up
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module SrvMonitor
|
2
|
+
class Scout
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def expectations
|
6
|
+
@expectations ? @expectations.dup : {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def expect(expectation, callable=nil, &callable_block)
|
10
|
+
callable ||= callable_block
|
11
|
+
|
12
|
+
if callable.respond_to?(:call)
|
13
|
+
@expectations ||= {}
|
14
|
+
@expectations[expectation] = callable
|
15
|
+
else
|
16
|
+
raise ArgumentError, 'Object must respond to method #call to be a valid expectation.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :server, :status
|
22
|
+
|
23
|
+
# Executes the Scout and go through all the registered expectations to find
|
24
|
+
# out all expectations that match and return the associated status.
|
25
|
+
#
|
26
|
+
# @return [Symbol] the current status of the Scout (:up, :down, :warning)
|
27
|
+
# @raise [NotImplementedError] raised when a configured expectation was not
|
28
|
+
# registered in the Scout.
|
29
|
+
def run(reports={})
|
30
|
+
statuses = []
|
31
|
+
execute
|
32
|
+
|
33
|
+
unless reports.empty?
|
34
|
+
reports.each do |response_pair, status|
|
35
|
+
response_pair.each do |expectation, value|
|
36
|
+
if self.class.expectations[expectation].nil?
|
37
|
+
raise NotImplementedError, "expectation '#{expectation}' wasn't implemented by #{self.class.name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
if self.class.expectations[expectation].call(self, value)
|
41
|
+
statuses << status
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@status = SrvMonitor::Report.summarize(statuses)
|
48
|
+
puts output
|
49
|
+
end
|
50
|
+
|
51
|
+
# Called when the Scout must take action and gather all the data needed to be analyzed.
|
52
|
+
#
|
53
|
+
# @raise [NotImplementedError] raised when method is not overriden.
|
54
|
+
def options
|
55
|
+
raise NotImplementedError, 'You must implement the options method for Scout to work correctly.'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Called when the Scout must take action and gather all the data needed to be analyzed.
|
59
|
+
#
|
60
|
+
# @raise [NotImplementedError] raised when method is not overriden.
|
61
|
+
def execute
|
62
|
+
raise NotImplementedError, 'You must implement the execute method for Scout to work correctly.'
|
63
|
+
end
|
64
|
+
|
65
|
+
# Called when the Scout must take action and gather all the data needed to be analyzed.
|
66
|
+
#
|
67
|
+
# @raise [NotImplementedError] raised when method is not overriden.
|
68
|
+
def output
|
69
|
+
raise NotImplementedError, 'You must implement the output method for Scout to work correctly.'
|
70
|
+
end
|
71
|
+
|
72
|
+
def remote?
|
73
|
+
@server && @server.remote?
|
74
|
+
end
|
75
|
+
|
76
|
+
def local?
|
77
|
+
@server.nil? || @server.local?
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'srvmonitor/expectations'
|
2
|
+
|
3
|
+
module SrvMonitor
|
4
|
+
module Scouts
|
5
|
+
class LoadAverage < SrvMonitor::Scout
|
6
|
+
extend SrvMonitor::Expectations::LoadAverage
|
7
|
+
|
8
|
+
attr_reader :load_average
|
9
|
+
#report_data :load_average
|
10
|
+
|
11
|
+
# Configure the scout with given options.
|
12
|
+
# @param [Hash] Options to setup the scout
|
13
|
+
def initialize(options)
|
14
|
+
@server = options[:server]
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
case
|
19
|
+
when remote? then remote_exec
|
20
|
+
when local? then local_exec
|
21
|
+
end
|
22
|
+
rescue
|
23
|
+
@load_average = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def output
|
27
|
+
st = "#{@status}\tload: #{@load_average}"
|
28
|
+
st = "[#{@server.name}]\t#{st}"
|
29
|
+
st
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def local_exec
|
34
|
+
loadavg = File.open('/proc/loadavg').read
|
35
|
+
@load_average = loadavg.split(' ').first.to_f
|
36
|
+
end
|
37
|
+
|
38
|
+
def remote_exec
|
39
|
+
server.exec do |ssh|
|
40
|
+
loadavg = ssh.exec!('cat /proc/loadavg')
|
41
|
+
@load_average = loadavg.split(' ').first.to_f
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'srvmonitor/expectations'
|
2
|
+
require 'net/http'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module SrvMonitor
|
6
|
+
module Scouts
|
7
|
+
# Uses ruby's own Net:HTTP to send HTTP requests and evaluate response body, response time and response code.
|
8
|
+
class TimedHttp < SrvMonitor::Scout
|
9
|
+
extend SrvMonitor::Expectations::ResponseCode
|
10
|
+
extend SrvMonitor::Expectations::ResponseBody
|
11
|
+
extend SrvMonitor::Expectations::ResponseTime
|
12
|
+
|
13
|
+
attr_reader :response_code, :response_body, :response_time
|
14
|
+
#report_data :response_code, :response_body, :response_time
|
15
|
+
|
16
|
+
# Configure the scout with given options.
|
17
|
+
# @param [Hash] Options to setup the scout
|
18
|
+
# @option options [Number] :port The port that will be used to.
|
19
|
+
# @option options [String] :path The path that will be fetched from the host.
|
20
|
+
# @option options [String] :http_class The class that will be used to fetch the page, defaults to Net::HTTP
|
21
|
+
# @option options [Number] :timout
|
22
|
+
def initialize(options)
|
23
|
+
@port = options[:port] || 80
|
24
|
+
@path = options[:path] || '/'
|
25
|
+
@http_class = options[:http_class] || Net::HTTP
|
26
|
+
@timeout = options[:timeout] || 15
|
27
|
+
@server = options[:server]
|
28
|
+
@host = @server ? @server.host : options[:host] || '127.0.0.1'
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute
|
32
|
+
previous_time = Time.now
|
33
|
+
Timeout::timeout(@timeout) do
|
34
|
+
case
|
35
|
+
when remote? then remote_exec
|
36
|
+
when local? then local_exec
|
37
|
+
end
|
38
|
+
end
|
39
|
+
rescue Timeout::Error
|
40
|
+
@response_time = (Time.now - previous_time).round
|
41
|
+
@response_code = @response_body = nil
|
42
|
+
rescue SocketError, Errno::ECONNREFUSED
|
43
|
+
@response_code = @response_body = @response_time = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def output
|
47
|
+
st = "#{@status}\tresponse code: #{@response_code} response time: #{@response_time.round}ms"
|
48
|
+
st = "[#{@server.name}]\t#{st}"
|
49
|
+
st
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def local_exec
|
54
|
+
previous_time = Time.now
|
55
|
+
response = @http_class.get_response(@host, @path, @port)
|
56
|
+
@response_time = (Time.now - previous_time) * 1000 # Miliseconds
|
57
|
+
@response_code = response.code.to_i
|
58
|
+
@response_body = response.body
|
59
|
+
end
|
60
|
+
|
61
|
+
# Runs the scout, connecting to the host and getting the response code, body and time.
|
62
|
+
def remote_exec
|
63
|
+
server.exec do |ssh|
|
64
|
+
response = ssh.exec!("curl --write-out %{http_code}:%{time_total} --silent --output /dev/null #{ url }")
|
65
|
+
@response_code = response.split(':').first.to_i
|
66
|
+
@response_time = response.split(':').last.to_f * 1000
|
67
|
+
@response_body = ssh.exec!("curl #{ url }")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def url
|
72
|
+
url = @host
|
73
|
+
url+= ":#{@port}" if @port && @port!=80
|
74
|
+
url+= "/#{@path}" if @path
|
75
|
+
"http://#{url.gsub('//', '/')}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
begin
|
2
|
+
require 'net/ssh'
|
3
|
+
rescue LoadError => e
|
4
|
+
puts "Please install ssh gem: gem install net-ssh if you want remote access"
|
5
|
+
end
|
6
|
+
|
7
|
+
module SrvMonitor
|
8
|
+
class Server
|
9
|
+
attr_reader :name, :description, :host, :method, :user, :password
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@name = options[:name]
|
13
|
+
@description = options[:description]
|
14
|
+
@host = options[:host] || '127.0.0.1'
|
15
|
+
@method = options[:method] || :local
|
16
|
+
@user = options[:user]
|
17
|
+
@password = options[:password]
|
18
|
+
end
|
19
|
+
|
20
|
+
def exec(&block)
|
21
|
+
if remote?
|
22
|
+
Net::SSH.start(@host, @user) { |ssh| block.call(ssh) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def clone(args={})
|
27
|
+
config = { :name => @name, :description => @description, :host => @host, :method => @method, :user => @user }
|
28
|
+
Server.new config.merge(args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def remote?
|
32
|
+
@method == :remote
|
33
|
+
end
|
34
|
+
|
35
|
+
def local?
|
36
|
+
@method == :local
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
data/lib/srvmonitor.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# SrvMonitor definition
|
2
|
+
require 'srvmonitor/server'
|
3
|
+
require 'srvmonitor/report'
|
4
|
+
require 'srvmonitor/application'
|
5
|
+
require 'srvmonitor/monitor'
|
6
|
+
# require 'srvmonitor/report'
|
7
|
+
|
8
|
+
# Scouts
|
9
|
+
require 'srvmonitor/scout'
|
10
|
+
require 'srvmonitor/notifiers'
|
11
|
+
|
data/srvmonitor.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "srvmonitor/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "srvmonitor"
|
6
|
+
s.version = SrvMonitor::VERSION.dup
|
7
|
+
s.description = "Server monitor."
|
8
|
+
s.summary = "Server monitor."
|
9
|
+
s.author = "Click Jogos"
|
10
|
+
s.email = "clickjogos@clickjogos.com.br"
|
11
|
+
s.homepage = "http://www.github.com/clickjogos/srvmonitor"
|
12
|
+
|
13
|
+
s.rubyforge_project = "srvmonitor"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
20
|
+
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: srvmonitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Click Jogos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-12 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Server monitor.
|
15
|
+
email: clickjogos@clickjogos.com.br
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- Gemfile
|
22
|
+
- MIT-LICENSE
|
23
|
+
- README.markdown
|
24
|
+
- Rakefile
|
25
|
+
- lib/srvmonitor.rb
|
26
|
+
- lib/srvmonitor/application.rb
|
27
|
+
- lib/srvmonitor/expectations.rb
|
28
|
+
- lib/srvmonitor/expectations/load_average.rb
|
29
|
+
- lib/srvmonitor/expectations/response_body.rb
|
30
|
+
- lib/srvmonitor/expectations/response_code.rb
|
31
|
+
- lib/srvmonitor/expectations/response_time.rb
|
32
|
+
- lib/srvmonitor/monitor.rb
|
33
|
+
- lib/srvmonitor/notifiers.rb
|
34
|
+
- lib/srvmonitor/notifiers/mailer.rb
|
35
|
+
- lib/srvmonitor/report.rb
|
36
|
+
- lib/srvmonitor/scout.rb
|
37
|
+
- lib/srvmonitor/scouts.rb
|
38
|
+
- lib/srvmonitor/scouts/load_average.rb
|
39
|
+
- lib/srvmonitor/scouts/timed_http.rb
|
40
|
+
- lib/srvmonitor/server.rb
|
41
|
+
- lib/srvmonitor/version.rb
|
42
|
+
- srvmonitor.gemspec
|
43
|
+
homepage: http://www.github.com/clickjogos/srvmonitor
|
44
|
+
licenses: []
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project: srvmonitor
|
63
|
+
rubygems_version: 1.8.6
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Server monitor.
|
67
|
+
test_files: []
|