srvmonitor 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.
- 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: []
|