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 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 "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
-
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( "Usage: servicemonitor <path to dsl>" ) if ARGV.length != 1
22
+ abort('Usage: servicemonitor <path to dsl>') if ARGV.length != 1
24
23
 
25
- dslName = ARGV[0]
24
+ dsl_name = ARGV[0]
26
25
 
27
- #Need to remove file name extension
28
- ENV["APP_NAME"] = File.basename( dslName ) if ENV["APP_NAME"].nil?
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, #{dslName}"
31
+ log "Loading dsl, #{dsl_name}"
33
32
  begin
34
- load dslName
35
- $a.run
36
- rescue ArgumentError=>e
37
- puts "*** Your dsl is not formatted correctly"
38
- puts "*** Ensure each line has the format,"
39
- puts "*** <command>, [:arg=>value]"
40
- rescue SystemExit=>e
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
- rescue Exception=>e
43
- puts "What the ..."
44
- puts e.class.name
45
- puts e.message
41
+ rescue StandardError
42
+ puts 'What the ...'
43
+ puts e.class.name
44
+ puts e.message
46
45
  end
@@ -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
@@ -0,0 +1,6 @@
1
+ # Base class / Interface for implementing alert types
2
+ class Alert
3
+ def send(_destination, _body)
4
+ fail 'Method needs to be overridden'
5
+ end
6
+ end
@@ -1,11 +1,9 @@
1
- def log( string, verbose=false )
2
- return if ENV["TESTING"]=="true"
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