servicemonitor 0.1.4 → 0.2.0
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.
- checksums.yaml +7 -0
- data/bin/servicemonitor +30 -31
- data/lib/alert/email.rb +41 -0
- data/lib/alert.rb +6 -0
- data/lib/helper_functions.rb +8 -10
- data/lib/monitor_manager.rb +35 -0
- data/lib/monitor_type/beanstalk.rb +35 -0
- data/lib/monitor_type/dir.rb +45 -0
- data/lib/monitor_type/drive.rb +49 -0
- data/lib/monitor_type/fluiddb.rb +71 -0
- data/lib/monitor_type/http_get_json_list.rb +53 -0
- data/lib/monitor_type/process.rb +60 -0
- data/lib/monitor_type/threshold.rb +44 -0
- data/lib/monitor_type.rb +114 -0
- metadata +48 -38
- data/lib/Alert/Email.rb +0 -43
- data/lib/Alert.rb +0 -7
- data/lib/MonitorManager.rb +0 -36
- data/lib/MonitorType/Beanstalk.rb +0 -45
- data/lib/MonitorType/Dir.rb +0 -45
- data/lib/MonitorType/Drive.rb +0 -50
- data/lib/MonitorType/FluidDb.rb +0 -77
- data/lib/MonitorType/HttpGetJsonList.rb +0 -58
- data/lib/MonitorType/Process.rb +0 -57
- data/lib/MonitorType/Threshold.rb +0 -50
- data/lib/MonitorType.rb +0 -119
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'monitor_type/threshold'
|
2
|
+
require 'helper_functions'
|
3
|
+
|
4
|
+
InvalidProcessNameError = Class.new(StandardError)
|
5
|
+
|
6
|
+
# A class for checking if a Process is running in Unix based systems
|
7
|
+
class MonitorTypeProcess < MonitorTypeThreshold
|
8
|
+
# Extract parameters
|
9
|
+
#
|
10
|
+
# @param [String] :process_name THe name of the process to monitor
|
11
|
+
def extract_params
|
12
|
+
if @params[:process_name].nil?
|
13
|
+
string = "*** Process Name parameter missing, process_name\n" \
|
14
|
+
'*** :process_name => <name of the process to ' \
|
15
|
+
'be monitored>'
|
16
|
+
fail MonitorTypeParameterMissingError, string
|
17
|
+
end
|
18
|
+
@process_name = @params[:process_name]
|
19
|
+
|
20
|
+
log "#{@process_name}", "result: #{(@process_name =~ /^(.*\[{1}.+\]{1}.*)$|^(\w+)$/) == 0}"
|
21
|
+
|
22
|
+
unless (@process_name =~ /^(.*\[{1}.+\]{1}.*)$|^(\w+)$/) == 0
|
23
|
+
string = '*** Process Name parameter doest not match the required ' \
|
24
|
+
"pattern, #{@process_name}\n" \
|
25
|
+
"*** :process_name => <plain string, or a string " \
|
26
|
+
'with one or more characters enclosed in square brackets, ' \
|
27
|
+
"i.e. 'foo', '[f]oo' or '[foo]'>"
|
28
|
+
fail InvalidProcessNameError, string
|
29
|
+
end
|
30
|
+
|
31
|
+
log '*** Min value will be ignored, setting to 1' unless (params[:min].nil? || params[:min] == 0)
|
32
|
+
@min = 1
|
33
|
+
|
34
|
+
log '*** Max value will be ignored, setting to nil' unless params[:max].nil?
|
35
|
+
@max = nil
|
36
|
+
|
37
|
+
@context_sentence = "Checking that process is running, #{@process_name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup
|
41
|
+
sanitise
|
42
|
+
rescue MonitorTypeExceptionHandled => e
|
43
|
+
puts e.message
|
44
|
+
abort
|
45
|
+
end
|
46
|
+
|
47
|
+
def sanitise
|
48
|
+
# Ensure that the process name contains a single character surrounded
|
49
|
+
# by square brackets
|
50
|
+
@process_name = @process_name.insert(0,'[').insert(2,']') unless @process_name =~ /^.*\[.+\].*/
|
51
|
+
end
|
52
|
+
|
53
|
+
def derived_value
|
54
|
+
`ps aux | grep #{@process_name}`.length
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def process(params)
|
59
|
+
$a.add(MonitorTypeProcess.new(params))
|
60
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'monitor_type'
|
2
|
+
|
3
|
+
# A base class for checking a number against a min and/or max value
|
4
|
+
class MonitorTypeThreshold < MonitorType
|
5
|
+
# See super method for generic description
|
6
|
+
def initialize(params)
|
7
|
+
@min = params[:min] ||= 0
|
8
|
+
@max = params[:max]
|
9
|
+
super(params)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get the context dependent value which is to be checked
|
13
|
+
def derived_value
|
14
|
+
fail 'Method needs to be overridden'
|
15
|
+
end
|
16
|
+
|
17
|
+
def process
|
18
|
+
if @block.nil?
|
19
|
+
value = derived_value
|
20
|
+
else
|
21
|
+
value = @block.call(@params)
|
22
|
+
string = "value: #{value}\n"
|
23
|
+
puts string
|
24
|
+
end
|
25
|
+
|
26
|
+
check(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Provides the means to check a value against thresholds
|
30
|
+
def check(value)
|
31
|
+
context_sentence = @context_sentence.nil? ? '' : "#{@context_sentence}\n"
|
32
|
+
url = @url.nil? ? '' : "\n\n#{@url}\n"
|
33
|
+
|
34
|
+
value = value.to_i
|
35
|
+
|
36
|
+
alert("#{context_sentence}Minimum threshold exceeded. " \
|
37
|
+
"Minimum: #{@min}, " \
|
38
|
+
"Actual: #{value}#{url}") if !@min.nil? && value < @min
|
39
|
+
|
40
|
+
alert("#{context_sentence}Maximum threshold exceeded. " \
|
41
|
+
"Maximum: #{@max}, " \
|
42
|
+
"Actual: #{value}#{url}") if !@max.nil? && value > @max
|
43
|
+
end
|
44
|
+
end
|
data/lib/monitor_type.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'parse-cron'
|
2
|
+
# This is provided for reporting purposes.
|
3
|
+
# It essentially indicates if an error case was expected
|
4
|
+
#
|
5
|
+
# The idea is to provide clear direction to the user to fix the error, but
|
6
|
+
# not stop all monitors in case of a production issue.
|
7
|
+
class MonitorTypeExceptionHandled < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class MonitorTypeParameterMissingError < MonitorTypeExceptionHandled
|
11
|
+
end
|
12
|
+
|
13
|
+
class MonitorTypeMustHaveNameError < MonitorTypeParameterMissingError
|
14
|
+
end
|
15
|
+
|
16
|
+
class MonitorTypeMustHaveSenderEmailAddressForEmailAlertError < MonitorTypeParameterMissingError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Base class for Monitors
|
20
|
+
#
|
21
|
+
# Given that this is a DSL, it extracts named parameters from a hash in order
|
22
|
+
# to provide more precise reporting of errors, without spewing syntax errors
|
23
|
+
# at a user
|
24
|
+
class MonitorType
|
25
|
+
# Check that all required parameters have been passed in
|
26
|
+
# Make sure that any errors encountered are reported in a way that
|
27
|
+
# fixing the error is made easier
|
28
|
+
def initialize(params)
|
29
|
+
if params[:name].nil?
|
30
|
+
string = '*** Monitor parameter missing, name' \
|
31
|
+
'*** :name => <name of monitor>'
|
32
|
+
fail MonitorTypeMustHaveNameError, string
|
33
|
+
end
|
34
|
+
@params = params
|
35
|
+
@block = params[:block] unless params[:block].nil?
|
36
|
+
@name = params[:name]
|
37
|
+
@email = params[:email]
|
38
|
+
@url = params[:url] unless params[:url].nil?
|
39
|
+
|
40
|
+
if !@email.nil?
|
41
|
+
# Sender email address from ENV allows for a single sender across all alerts
|
42
|
+
# Checking params before ENV allows a particular entry to be different
|
43
|
+
if params[:email_sender].nil?
|
44
|
+
if ENV['EMAIL_SENDER'].nil?
|
45
|
+
string = '*** Alert parameter missing, email_sender' \
|
46
|
+
'*** An email recipient has been specified for monitor, ' \
|
47
|
+
"#{@name}, but no email sender has been specified" \
|
48
|
+
'*** :email_sender => <email of sender>' \
|
49
|
+
'*** or, a catch all environment variable' \
|
50
|
+
'*** EMAIL_SENDER=<email of sender>'
|
51
|
+
|
52
|
+
fail MonitorTypeMustHaveSenderEmailAddressForEmailAlertError, string
|
53
|
+
else
|
54
|
+
@sender_email = ENV['EMAIL_SENDER']
|
55
|
+
end
|
56
|
+
else
|
57
|
+
@sender_email = params[:admin_email]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
cron_string = params[:cron] || '0 1 * * *'
|
62
|
+
@cron = CronParser.new(cron_string)
|
63
|
+
@next = Time.now - 1
|
64
|
+
|
65
|
+
log "Loaded Monitor, #{@name}."
|
66
|
+
end
|
67
|
+
|
68
|
+
# Overload this method if any parameters should be checked in context
|
69
|
+
def extract_params
|
70
|
+
end
|
71
|
+
|
72
|
+
# Overload this method if any parameters should be checked in context
|
73
|
+
def setup
|
74
|
+
end
|
75
|
+
|
76
|
+
# Overload this method if any parameters should be checked in context
|
77
|
+
def teardown
|
78
|
+
end
|
79
|
+
|
80
|
+
# Check if the monitor has tripped
|
81
|
+
def process
|
82
|
+
fail 'Method needs to be overridden'
|
83
|
+
end
|
84
|
+
|
85
|
+
# An extention of the main run loop.
|
86
|
+
# Each monitor is responsible for knowing when it should run, so this function
|
87
|
+
# is called pretty much continuosuly.
|
88
|
+
def run
|
89
|
+
return unless Time.now > @next
|
90
|
+
|
91
|
+
@next = @cron.next(Time.now)
|
92
|
+
log "Monitor, #{@name}, next run time, #{@next}"
|
93
|
+
extract_params
|
94
|
+
setup
|
95
|
+
process
|
96
|
+
teardown
|
97
|
+
rescue MonitorTypeExceptionHandled => e
|
98
|
+
alert(e.message)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Called when a monitor has been tripped
|
102
|
+
#
|
103
|
+
# @param [String] string A description of the trip that occurred
|
104
|
+
def alert(string)
|
105
|
+
body = "#{@name} tripped.\n#{string}"
|
106
|
+
puts '*** '
|
107
|
+
if !@email.nil?
|
108
|
+
AlertEmail.new(@sender_email, @email, body).Send
|
109
|
+
puts "Emailed, #{@email}"
|
110
|
+
else
|
111
|
+
puts body
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
metadata
CHANGED
@@ -1,60 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: servicemonitor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Guy Irvine
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-08-16 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: parse-cron
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
25
27
|
- !ruby/object:Gem::Dependency
|
26
28
|
name: beanstalk-client
|
27
|
-
requirement:
|
28
|
-
none: false
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
|
-
- -
|
31
|
+
- - '>='
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '0'
|
33
34
|
type: :runtime
|
34
35
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
36
41
|
- !ruby/object:Gem::Dependency
|
37
42
|
name: fluiddb
|
38
|
-
requirement:
|
39
|
-
none: false
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
40
44
|
requirements:
|
41
|
-
- -
|
45
|
+
- - '>='
|
42
46
|
- !ruby/object:Gem::Version
|
43
47
|
version: '0'
|
44
48
|
type: :runtime
|
45
49
|
prerelease: false
|
46
|
-
version_requirements:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
47
55
|
- !ruby/object:Gem::Dependency
|
48
56
|
name: rest-client
|
49
|
-
requirement:
|
50
|
-
none: false
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
51
58
|
requirements:
|
52
|
-
- -
|
59
|
+
- - '>='
|
53
60
|
- !ruby/object:Gem::Version
|
54
61
|
version: '0'
|
55
62
|
type: :runtime
|
56
63
|
prerelease: false
|
57
|
-
version_requirements:
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
58
69
|
description: The fastest way to reliably monitor your system.
|
59
70
|
email: guy@guyirvine.com
|
60
71
|
executables:
|
@@ -62,44 +73,43 @@ executables:
|
|
62
73
|
extensions: []
|
63
74
|
extra_rdoc_files: []
|
64
75
|
files:
|
65
|
-
- lib/
|
66
|
-
- lib/
|
76
|
+
- lib/alert/email.rb
|
77
|
+
- lib/alert.rb
|
67
78
|
- lib/helper_functions.rb
|
68
|
-
- lib/
|
69
|
-
- lib/
|
70
|
-
- lib/
|
71
|
-
- lib/
|
72
|
-
- lib/
|
73
|
-
- lib/
|
74
|
-
- lib/
|
75
|
-
- lib/
|
76
|
-
- lib/
|
79
|
+
- lib/monitor_manager.rb
|
80
|
+
- lib/monitor_type/beanstalk.rb
|
81
|
+
- lib/monitor_type/dir.rb
|
82
|
+
- lib/monitor_type/drive.rb
|
83
|
+
- lib/monitor_type/fluiddb.rb
|
84
|
+
- lib/monitor_type/http_get_json_list.rb
|
85
|
+
- lib/monitor_type/process.rb
|
86
|
+
- lib/monitor_type/threshold.rb
|
87
|
+
- lib/monitor_type.rb
|
77
88
|
- bin/servicemonitor
|
78
89
|
- LICENSE
|
79
90
|
- README.md
|
80
91
|
homepage: http://rubygems.org/gems/servicemonitor
|
81
|
-
licenses:
|
92
|
+
licenses:
|
93
|
+
- LGPL-3.0
|
94
|
+
metadata: {}
|
82
95
|
post_install_message:
|
83
96
|
rdoc_options: []
|
84
97
|
require_paths:
|
85
98
|
- lib
|
86
99
|
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
-
none: false
|
88
100
|
requirements:
|
89
|
-
- -
|
101
|
+
- - '>='
|
90
102
|
- !ruby/object:Gem::Version
|
91
103
|
version: '0'
|
92
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
-
none: false
|
94
105
|
requirements:
|
95
|
-
- -
|
106
|
+
- - '>='
|
96
107
|
- !ruby/object:Gem::Version
|
97
108
|
version: '0'
|
98
109
|
requirements: []
|
99
110
|
rubyforge_project:
|
100
|
-
rubygems_version:
|
111
|
+
rubygems_version: 2.0.14.1
|
101
112
|
signing_key:
|
102
|
-
specification_version:
|
113
|
+
specification_version: 4
|
103
114
|
summary: ServiceMonitor
|
104
115
|
test_files: []
|
105
|
-
has_rdoc:
|
data/lib/Alert/Email.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require "net/smtp"
|
2
|
-
require "Alert"
|
3
|
-
|
4
|
-
#Email alert class
|
5
|
-
#Uses localhost for sending email - Probably need to change this in the future.
|
6
|
-
class Alert_Email
|
7
|
-
def initialize( sender, destination, body )
|
8
|
-
@sender = sender
|
9
|
-
@body = body
|
10
|
-
|
11
|
-
@smtp_address = ENV['smtp_address'].nil? ? 'localhost' : ENV['smtp_address']
|
12
|
-
@smtp_port = ENV['smtp_port'].nil? ? 25 : ENV['smtp_port']
|
13
|
-
|
14
|
-
@destination = destination
|
15
|
-
if destination.is_a? Array then
|
16
|
-
@destination_fmt = "<#{destination.join( ">,<" )}>"
|
17
|
-
else
|
18
|
-
@destination_fmt = destination
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def Send
|
23
|
-
message = <<MESSAGE_END
|
24
|
-
From: #{ENV['APP_NAME']} #{@sender}
|
25
|
-
To: #{@destination_fmt}
|
26
|
-
Subject: #{ENV['APP_NAME']} Alert
|
27
|
-
|
28
|
-
#{@body}
|
29
|
-
.
|
30
|
-
MESSAGE_END
|
31
|
-
|
32
|
-
Net::SMTP.start(@smtp_address,@smtp_port) do |smtp|
|
33
|
-
smtp.send_message message, @sender,
|
34
|
-
@destination
|
35
|
-
end
|
36
|
-
|
37
|
-
rescue Errno::ECONNREFUSED => e
|
38
|
-
string = "*** Conection refused while attempting to connect to SMTP server\n"
|
39
|
-
string = "#{string}*** Recipient, #{@destination}. Body,\n"
|
40
|
-
string = "#{string}*** #{@body}\n"
|
41
|
-
puts string
|
42
|
-
end
|
43
|
-
end
|
data/lib/Alert.rb
DELETED
data/lib/MonitorManager.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
|
2
|
-
class MonitorManager
|
3
|
-
def initialize
|
4
|
-
@list = Array.new
|
5
|
-
end
|
6
|
-
|
7
|
-
def add( monitor )
|
8
|
-
@list.push monitor
|
9
|
-
end
|
10
|
-
|
11
|
-
#The main run loop
|
12
|
-
def run
|
13
|
-
while true do
|
14
|
-
@list.each do |m|
|
15
|
-
begin
|
16
|
-
m.run
|
17
|
-
rescue MonitorTypeExceptionHandled => e
|
18
|
-
m.alert( e.message )
|
19
|
-
end
|
20
|
-
end
|
21
|
-
sleep 0.2
|
22
|
-
end
|
23
|
-
|
24
|
-
rescue Interrupt => e
|
25
|
-
string = "Exiting on request ...\n"
|
26
|
-
puts string
|
27
|
-
|
28
|
-
rescue Exception => e
|
29
|
-
string = "#{e.class.name}\n"
|
30
|
-
string = "#{string}*** This is really unexpected.\n"
|
31
|
-
string = "#{string}Message: #{e.message}\n"
|
32
|
-
string = "#{string}e.backtrace\n"
|
33
|
-
puts string
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|