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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c21b93e6591e1fdb7d9ab33bfbb185c2fc1a770d
|
4
|
+
data.tar.gz: 70a604920114a521584fc6776aa495d0e1893aca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 30e355380f2cad7c5f25592b3d5eb95c2bcf2efcf402001f42e496b6abbafdb27cda3af48faa548d1e40b6350cec7c97cea3aaf136539e8fa5f06f8a5f6e3a59
|
7
|
+
data.tar.gz: c177caef1a73ae735546cdef63113a7807710c9c287114395e331a21e227fcad977ee5c5f4907bef01aefc855667274b1e68ca709b7aea06e7d1b985d6d281a3
|
data/bin/servicemonitor
CHANGED
@@ -1,46 +1,45 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
#Add the currently running directory to the start of the load path
|
3
|
+
# Add the currently running directory to the start of the load path
|
4
4
|
$:.unshift './'
|
5
5
|
|
6
6
|
require 'net/smtp'
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
|
19
|
-
|
20
|
-
#Don't buffer stdout
|
7
|
+
require 'Alert'
|
8
|
+
require 'Alert/Email'
|
9
|
+
require 'MonitorType'
|
10
|
+
require 'MonitorType/Threshold'
|
11
|
+
require 'MonitorType/Beanstalk'
|
12
|
+
require 'MonitorType/Dir'
|
13
|
+
require 'MonitorType/FluidDb'
|
14
|
+
require 'MonitorType/Process'
|
15
|
+
require 'MonitorType/HttpGetJsonList'
|
16
|
+
require 'MonitorManager'
|
17
|
+
require 'helper_functions'
|
18
|
+
|
19
|
+
# Don't buffer stdout
|
21
20
|
$stdout.sync = true
|
22
21
|
|
23
|
-
abort(
|
22
|
+
abort('Usage: servicemonitor <path to dsl>') if ARGV.length != 1
|
24
23
|
|
25
|
-
|
24
|
+
dsl_name = ARGV[0]
|
26
25
|
|
27
|
-
#Need to remove file name extension
|
28
|
-
ENV[
|
26
|
+
# Need to remove file name extension
|
27
|
+
ENV['APP_NAME'] = File.basename(dsl_name) if ENV['APP_NAME'].nil?
|
29
28
|
|
30
29
|
$a = MonitorManager.new
|
31
30
|
|
32
|
-
log "Loading dsl, #{
|
31
|
+
log "Loading dsl, #{dsl_name}"
|
33
32
|
begin
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
load dsl_name
|
34
|
+
$a.run
|
35
|
+
rescue ArgumentError
|
36
|
+
puts "*** Your dsl is not formatted correctly\n" \
|
37
|
+
"*** Ensure each line has the format,\n" \
|
38
|
+
"*** <command>, [:arg=>value]\n"
|
39
|
+
rescue SystemExit
|
41
40
|
# rescue SIGTERM=>e
|
42
|
-
|
43
|
-
puts
|
44
|
-
|
45
|
-
|
41
|
+
rescue StandardError
|
42
|
+
puts 'What the ...'
|
43
|
+
puts e.class.name
|
44
|
+
puts e.message
|
46
45
|
end
|
data/lib/alert/email.rb
ADDED
@@ -0,0 +1,41 @@
|
|
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 AlertEmail
|
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
|
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, @destination
|
34
|
+
end
|
35
|
+
|
36
|
+
rescue Errno::ECONNREFUSED
|
37
|
+
puts "*** Conection refused while attempting to connect to SMTP server\n" \
|
38
|
+
"*** Recipient, #{@destination}. Body,\n" \
|
39
|
+
"*** #{@body}\n"
|
40
|
+
end
|
41
|
+
end
|
data/lib/alert.rb
ADDED
data/lib/helper_functions.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
def log(
|
2
|
-
|
3
|
-
|
4
|
-
type = verbose ? "VERB" : "INFO"
|
5
|
-
if !ENV["VERBOSE"].nil? || verbose==false then
|
6
|
-
timestamp = Time.new.strftime( "%Y-%m-%d %H:%M:%S" )
|
7
|
-
puts "[#{type}] #{timestamp} :: #{string}"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
1
|
+
def log(string, verbose = false)
|
2
|
+
return if ENV['TESTING'] == 'true'
|
11
3
|
|
4
|
+
if !ENV['VERBOSE'].nil? || verbose == false
|
5
|
+
type = verbose ? 'VERB' : 'INFO'
|
6
|
+
timestamp = Time.new.strftime('%Y-%m-%d %H:%M:%S')
|
7
|
+
puts "[#{type}] #{timestamp} :: #{string}"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Monitor Manager
|
2
|
+
class MonitorManager
|
3
|
+
def initialize
|
4
|
+
@list = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(monitor)
|
8
|
+
@list.push monitor
|
9
|
+
end
|
10
|
+
|
11
|
+
# The main run loop
|
12
|
+
def run
|
13
|
+
Kernel.loop 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
|
25
|
+
string = "Exiting on request ...\n"
|
26
|
+
puts string
|
27
|
+
|
28
|
+
rescue StandardError
|
29
|
+
string = "#{e.class.name}\n" \
|
30
|
+
"*** This is really unexpected.\n" \
|
31
|
+
"Message: #{e.message}\n" \
|
32
|
+
"#{e.backtrace}\n"
|
33
|
+
puts string
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'beanstalk-client'
|
2
|
+
require 'monitor_type/threshold'
|
3
|
+
|
4
|
+
# A Beanstalk class for checking how many msgs are in a Queue
|
5
|
+
class MonitorTypeBeanstalk < MonitorTypeThreshold
|
6
|
+
# Extract parameters
|
7
|
+
#
|
8
|
+
# @param [String] beanstalk Optional connection string. Default to local
|
9
|
+
# @param [String] queue Name of queue to monitor
|
10
|
+
def extract_params
|
11
|
+
@connection_string = @params[:beanstalk] || 'localhost:11300'
|
12
|
+
|
13
|
+
if @params[:queue].nil?
|
14
|
+
string = "*** Beanstalk parameter missing, queue\n" \
|
15
|
+
'*** :queue => <queue name>'
|
16
|
+
fail MonitorTypeParameterMissingError, string
|
17
|
+
end
|
18
|
+
@queue = @params[:queue]
|
19
|
+
|
20
|
+
@context_sentence = "Checking number of jobs in queue, #{@queue}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup
|
24
|
+
@beanstalk = Beanstalk::Pool.new([@connection_string])
|
25
|
+
end
|
26
|
+
|
27
|
+
def derived_value
|
28
|
+
tube_stats = @beanstalk.stats_tube(@queue)
|
29
|
+
tube_stats['current-jobs-ready']
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def beanstalk(params)
|
34
|
+
$a.add(MonitorTypeBeanstalk.new(params))
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'monitor_type/threshold'
|
2
|
+
|
3
|
+
# A directory class for checking how many files are in a directory
|
4
|
+
class MonitorTypeDir < MonitorTypeThreshold
|
5
|
+
# Extract parameters
|
6
|
+
#
|
7
|
+
# @param [String] path Path to directory to check
|
8
|
+
def extract_params
|
9
|
+
if @params[:path].nil?
|
10
|
+
string = "*** Dir parameter missing, path\n" \
|
11
|
+
'*** :path => <path to directory to be monitored>'
|
12
|
+
fail MonitorTypeParameterMissingError, string
|
13
|
+
end
|
14
|
+
@path = @params[:path]
|
15
|
+
|
16
|
+
@context_sentence = "Checking number of files in, #{@path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup
|
20
|
+
input_dir = Dir.new(@path)
|
21
|
+
@path = input_dir.path
|
22
|
+
@params[:dir] = input_dir
|
23
|
+
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
str = "***** Directory does not exist, #{@path}.\n" \
|
26
|
+
"***** Create the directory, #{@path}, and try again.\n" \
|
27
|
+
"***** eg, mkdir #{@path}"
|
28
|
+
raise MonitorTypeExceptionHandled, str
|
29
|
+
rescue Errno::ENOTDIR
|
30
|
+
str = '***** The specified path does not point to a ' \
|
31
|
+
"directory, #{@path}.\n" \
|
32
|
+
'***** Either repoint path to a directory, ' \
|
33
|
+
"or remove, #{@path}, and create it as a directory.\n" \
|
34
|
+
"***** eg, rm #{@path} && mkdir #{@path}"
|
35
|
+
raise MonitorTypeExceptionHandled, str
|
36
|
+
end
|
37
|
+
|
38
|
+
def derived_value
|
39
|
+
Dir.glob("#{@path}/*").length
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def dir(params)
|
44
|
+
$a.add(MonitorTypeDir.new(params))
|
45
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'monitor_type/threshold'
|
2
|
+
require 'sys/filesystem'
|
3
|
+
include Sys
|
4
|
+
|
5
|
+
# MonitorType Drive
|
6
|
+
class MonitorTypeDrive < MonitorTypeThreshold
|
7
|
+
def extract_params
|
8
|
+
if @params[:path].nil?
|
9
|
+
string = "*** Drive parameter missing, drive\n" \
|
10
|
+
'*** :drive => <name of the drive to be monitored>'
|
11
|
+
fail MonitorTypeParameterMissingError, string
|
12
|
+
end
|
13
|
+
@path = @params[:path]
|
14
|
+
|
15
|
+
log "#{@process_name}", "result: #{(@process_name =~ /^(.*\[{1}.+\]{1}.*)$|^(\w+)$/) == 0}"
|
16
|
+
|
17
|
+
if @params[:min].nil?
|
18
|
+
string = "*** Min parameter missing, min\n" \
|
19
|
+
'*** :min => <the minimum amount of free space on ' \
|
20
|
+
'the drive to be monitored>'
|
21
|
+
fail MonitorTypeParameterMissingError, string
|
22
|
+
end
|
23
|
+
|
24
|
+
log '*** Max value will be ignored, setting to nil' unless @params[:max].nil?
|
25
|
+
@max = nil
|
26
|
+
|
27
|
+
@context_sentence = 'Checking that available drive space is greater ' \
|
28
|
+
"than min, #{@process_name}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
# Check that the path exists
|
33
|
+
Filesystem.stat(@path)
|
34
|
+
|
35
|
+
rescue
|
36
|
+
string = "*** Unable to mount the specifed path\n" \
|
37
|
+
"*** path: #{@path}\n" \
|
38
|
+
"*** Please fix the error and run again\n"
|
39
|
+
raise MonitorTypeExceptionHandled, string
|
40
|
+
end
|
41
|
+
|
42
|
+
def derived_value
|
43
|
+
((Filesystem.stat(@path).blocks_available.to_f / Filesystem.stat(@path).blocks.to_f) * 100).round(2)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def process(params)
|
48
|
+
$a.add(MonitorTypeDrive.new(params))
|
49
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'FluidDb'
|
2
|
+
require 'monitor_type/threshold'
|
3
|
+
|
4
|
+
# A database class for checking a single number against a threshold.
|
5
|
+
# For example,
|
6
|
+
# get the max timestamp from a table as a date.
|
7
|
+
# subtract this from now
|
8
|
+
# => check that the number is not greater than 2
|
9
|
+
class MonitorTypeFluidDb < MonitorTypeThreshold
|
10
|
+
# Extract parameters
|
11
|
+
#
|
12
|
+
# @param [String] uri Connection string to db
|
13
|
+
# @param [String] sql SQL statement to gather a single value
|
14
|
+
def extract_params
|
15
|
+
if @params[:uri].nil?
|
16
|
+
string = "*** FluidDb parameter missing, uri\n" \
|
17
|
+
"*** :uri => <uri pointing to db to be monitored>"
|
18
|
+
fail MonitorTypeParameterMissingError, string
|
19
|
+
end
|
20
|
+
begin
|
21
|
+
@uri = URI.parse(@params[:uri])
|
22
|
+
rescue URI::InvalidURIError
|
23
|
+
string = '*** FluidDb encountered an error while parsing the uri' \
|
24
|
+
"*** uri: #{@params[:uri]}" \
|
25
|
+
"*** Please fix the uri and run again"
|
26
|
+
raise MonitorTypeParameterMissingError, string
|
27
|
+
end
|
28
|
+
|
29
|
+
if @params[:sql].nil?
|
30
|
+
string = '*** FluidDb parameter missing, sql' \
|
31
|
+
"*** :sql => <sql statement, producing a single " \
|
32
|
+
'column, single row which yeidls a number>'
|
33
|
+
fail MonitorTypeParameterMissingError, string
|
34
|
+
end
|
35
|
+
@sql = @params[:sql]
|
36
|
+
@context_sentence = "Checking result of sql query, #{@sql}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create the connection to the db, and get the value
|
40
|
+
# This ensures that all params are correct.
|
41
|
+
def setup
|
42
|
+
begin
|
43
|
+
@fluid_db = FluidDb.Db(@uri)
|
44
|
+
rescue StandardError => e
|
45
|
+
string = "*** FluidDb encountered an error while connecting to the db\n" \
|
46
|
+
"*** Error: #{e.message}\n" \
|
47
|
+
"*** uri: #{@uri}\n" \
|
48
|
+
"*** Please fix the error and run again\n"
|
49
|
+
raise MonitorTypeExceptionHandled, string
|
50
|
+
end
|
51
|
+
|
52
|
+
@params[:fluidDb] = @fluid_db
|
53
|
+
end
|
54
|
+
|
55
|
+
def derived_value
|
56
|
+
@fluid_db.queryForValue(@sql, [])
|
57
|
+
rescue StandardError
|
58
|
+
string = "*** FluidDb encountered an error while running the sql\n" \
|
59
|
+
"*** sql: #{@sql}\n" \
|
60
|
+
"*** Please fix the query and run again\n"
|
61
|
+
raise MonitorTypeExceptionHandled, string
|
62
|
+
end
|
63
|
+
|
64
|
+
def teardown
|
65
|
+
@fluid_db.close
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def fluiddb(params)
|
70
|
+
$a.add(MonitorTypeFluidDb.new(params))
|
71
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'restclient'
|
2
|
+
require 'monitor_type/threshold'
|
3
|
+
|
4
|
+
# An http class for checking the length of a json list.
|
5
|
+
# For example,
|
6
|
+
# Get the list of outstandinig errors
|
7
|
+
# => check that the number is not greater than 2
|
8
|
+
class MonitorTypeHttpGetJsonList < MonitorTypeThreshold
|
9
|
+
# Extract parameters
|
10
|
+
#
|
11
|
+
# @param [String] uri Connection string to db
|
12
|
+
# @param [String] sql SQL statement to gather a single value
|
13
|
+
def extract_params
|
14
|
+
if @params[:uri].nil?
|
15
|
+
string = "*** HttpGetJsonList parameter missing, uri\n" \
|
16
|
+
'*** :uri => <uri pointing to url to be monitored>'
|
17
|
+
fail MonitorTypeParameterMissingError, string
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
@uri = URI.parse(@params[:uri])
|
22
|
+
rescue URI::InvalidURIError
|
23
|
+
str = '*** HttpGetJsonList encountered an error while parsing the uri' \
|
24
|
+
"*** uri: #{@params[:uri]}" \
|
25
|
+
'*** Please fix the uri and run again'
|
26
|
+
raise MonitorTypeParameterMissingError, str
|
27
|
+
end
|
28
|
+
|
29
|
+
url = "#{@uri.scheme}://#{@uri.host}#{@uri.path}"
|
30
|
+
@context_sentence = "Checking size of json list returned from, #{url}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def derived_value
|
34
|
+
content = RestClient.get(@uri.to_s)
|
35
|
+
list = JSON.parse(content)
|
36
|
+
str = "Expected type, Array - Actual type, #{list.class.name}"
|
37
|
+
fail MonitorTypeExceptionHandled, str unless list.class.name == 'Array'
|
38
|
+
list.length
|
39
|
+
rescue MonitorTypeExceptionHandled
|
40
|
+
raise e
|
41
|
+
|
42
|
+
rescue StandardError
|
43
|
+
string = '*** HttpGetJsonList encountered an error while running the ' \
|
44
|
+
'HTTP Get\n' \
|
45
|
+
"*** uri: #{@uri}\n" \
|
46
|
+
"*** Please fix the query and run again\n"
|
47
|
+
raise MonitorTypeExceptionHandled, string
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def httpgetjsonlist(params)
|
52
|
+
$a.add(MonitorTypeHttpGetJsonList.new(params))
|
53
|
+
end
|