averell23-watchdogger 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/watchdogger.rb CHANGED
@@ -105,17 +105,23 @@ module WatchDogger # :nodoc:
105
105
  # daemon is still running.
106
106
  def check_daemon
107
107
  return false unless(File.exists?(@pidfile))
108
- pid = get_pid
108
+ check_process(get_pid)
109
+ end
110
+
111
+ # Returns true if the system thinks that the given process is still alive
112
+ def check_process(pid)
109
113
  begin
110
114
  Process.kill(0, pid)
115
+ dog_log.debug('Watchdogger') { "Process #{pid} is alive" }
111
116
  true
112
117
  rescue Errno::EPERM
118
+ dog_log.debug('Watchdogger') { "No permissions for process #{pid} - seems to be running."}
113
119
  true
114
120
  rescue Errno::ESRCH
115
- dog_log.info('Watchdogger') { "Old process #{pid} is stale, good to go." }
121
+ dog_log.debug('Watchdogger') { "Found stale process for #{pid}." }
116
122
  false
117
123
  rescue Exception => e
118
- dog_log.error('Watchdogger') { "Could not find out if process #{pid} still runs (#{e.message}). Hoping for the best..." }
124
+ dog_log.info('Watchdogger') { "Could not find out if process #{pid} still runs (#{e.message}). Hoping for the best..." }
119
125
  false
120
126
  end
121
127
  end
@@ -174,4 +180,16 @@ class Hash # :nodoc:
174
180
  value
175
181
  end
176
182
 
183
+ # Gets the given key as an array. If it's already an array, it will be returned,
184
+ # otherwise we'll check if it's a comma-separated list. The
185
+ # default will be processed in the same way as if was read from the Hash.
186
+ def get_list(sym_or_string, default = nil)
187
+ value = get_value(sym_or_string, default)
188
+ return value if(value.is_a?(Array))
189
+ assit(value.is_a?(String) || value.is_a?(Symbol))
190
+ value = value.to_s.split(',').collect do |val|
191
+ val.strip
192
+ end
193
+ end
194
+
177
195
  end
data/lib/watcher/base.rb CHANGED
@@ -36,21 +36,10 @@ module Watcher
36
36
 
37
37
  # Sets up all actions for this watcher
38
38
  def setup_actions(configuration)
39
- action_config = configuration.get_value(:actions)
40
- raise(ArgumentError, "No actions passed to watcher.") unless(action_config)
41
- if(action_config.is_a?(Array))
42
- action_config.each { |ac| add_action_to(actions, ac) }
43
- else
44
- assit(action_config.is_a?(String) || action_config.is_a?(Symbol))
45
- add_action_to(actions, action_config)
46
- end
47
- warn_config = configuration.get_value(:warn_actions)
48
- if(warn_config.is_a?(Array))
49
- warn_config.each { |ac| add_action_to(warn_actions, ac) }
50
- elsif(warn_config)
51
- assit_kind_of(String, warn_config)
52
- add_action_to(warn_actions, warn_config)
53
- end
39
+ action_config = configuration.get_list(:actions, false)
40
+ action_config.each { |ac| add_action_to(actions, ac) }
41
+ warn_config = configuration.get_list(:warn_actions, [])
42
+ warn_config.each { |ac| add_action_to(warn_actions, ac) }
54
43
  end
55
44
 
56
45
  private
@@ -11,12 +11,16 @@ module Watcher
11
11
  # [*content_match*] A regular expression that is matched against the result.
12
12
  # The watcher fails if the expression doesn't match
13
13
  # [*timeout*] The timeout for the connection attempt. Defaults to 10 sec
14
- #
14
+ # [*falloff*] If a successful connection is made, this is subtracted from
15
+ # the internal severity. (Default: 100, completely reset previous
16
+ # failures)
17
+ #
15
18
  # If neither response nor content_match are given, the watcher will expect a
16
19
  # 200 OK response from the server.
17
20
  #
18
21
  # This watcher resets the current severity on each successful connect, so that
19
- # only continuous failures count against the trigger condition.
22
+ # only continuous failures count against the trigger condition - see the falloff
23
+ # option.
20
24
  class HttpWatcher < Watcher::Base
21
25
 
22
26
  def initialize(config)
@@ -26,6 +30,8 @@ module Watcher
26
30
  response = config.get_value(:response)
27
31
  @response = ((!response && !match) ? "200" : response)
28
32
  @timeout = config.get_value(:timeout, 10).to_i
33
+ @falloff = config.get_value(:falloff, 100).to_i
34
+ @current_severity = 0
29
35
  end
30
36
 
31
37
  def watch_it!
@@ -40,7 +46,7 @@ module Watcher
40
46
  elsif(@content_match && !@content_match.match(res.body))
41
47
  test_failed = "Did not find #{@content_match.to_s} at #{@url}"
42
48
  end
43
- @current_severity = 0 unless(test_failed)
49
+ @current_severity = [0, @current_severity - @falloff].min unless(test_failed)
44
50
  dog_log.debug('HttpWatcher') { "Watch of #{@url} resulted in #{test_failed}" }
45
51
  test_failed
46
52
  rescue Exception => e
@@ -82,6 +82,7 @@ module Watcher
82
82
  :reopen_suspicious => true,
83
83
  :suspicious_interval => (@interval_max * 3)
84
84
  )
85
+ dog_log.info('Logger Thread') { "Log watcher thread started" }
85
86
  logfile.tail do |line|
86
87
  if(matcher.match(line))
87
88
  triggered!
@@ -34,6 +34,11 @@ module WatcherAction
34
34
  def has_action?(name)
35
35
  registered_actions[name.to_sym] != nil
36
36
  end
37
+
38
+ # Checks if the given action is registered with that name
39
+ def is_action?(name, action)
40
+ registered_actions[name.to_sym] == action
41
+ end
37
42
 
38
43
  private
39
44
 
@@ -5,17 +5,34 @@ module WatcherAction
5
5
  # =Options
6
6
  #
7
7
  # [*pidfile*] The file containing the process id
8
- # [*signal*] The signal to send to the process. Defaults to KILL
8
+ # [*signal*] The signal to send to the process.
9
+ # This may be an array or a comma-separated
10
+ # list of signals. If there is more than one signal, this
11
+ # will wait for _wait_ seconds before trying the next
12
+ # signal, if the process didn't die.
13
+ # Defaults to KILL
14
+ # [*wait*] Time to wait between signals (Default: 5)
9
15
  class KillProcess
10
16
 
11
17
  def initialize(config)
12
18
  @pidfile = config.get_value(:pidfile, false)
13
- @signal= config.get_value(:signal, 'KILL')
19
+ @signals = config.get_list(:signal, 'KILL')
20
+ @wait = config.get_value(:wait, 5).to_i
14
21
  end
15
22
 
16
23
  def execute(event)
17
- pid = File.open(@pidfile) { |io| io.read }
18
- Process.kill(@signal, pid.to_i)
24
+ Thread.new(@signals, @wait, @pidfile) do |signals, wait, pidfile|
25
+ pid = File.open(pidfile) { |io| io.read }.to_i
26
+ signals.each_with_index do |signal, index|
27
+ sleep(wait) if(index > 0)
28
+ if(WatchDogger.check_process(pid))
29
+ dog_log.debug('KillerThread') { "Sending signal #{signal} to process #{pid}" }
30
+ Process.kill(signal, pid)
31
+ else
32
+ dog_log.debug('KillerThread') { "Process #{pid} is dead." }
33
+ end
34
+ end
35
+ end
19
36
  rescue Exception => e
20
37
  dog_log.warn { "Unable to kill process: #{e}" }
21
38
  end
@@ -10,18 +10,20 @@ module WatcherAction
10
10
  class MetaAction
11
11
 
12
12
  def initialize(config)
13
- actions = config.get_value(:actions, false)
14
- if(actions.is_a?(Array))
15
- @actions = actions
16
- else
17
- @actions = [ actions ]
18
- end
13
+ actions = config.get_list(:actions, false)
14
+ actions.each { |ac| add_action(ac) }
19
15
  end
20
16
 
21
17
  def execute(event)
22
18
  @actions.each { |ac| WatcherAction.run_action(ac, event) }
23
19
  end
24
20
 
21
+ def add_action(action)
22
+ @actions ||= []
23
+ raise(ArgumentError, "Trying to add myself, creating a loop.") if(WatcherAction.is_action?(action, self))
24
+ @actions << action
25
+ end
26
+
25
27
  end
26
28
 
27
29
  end
@@ -1,11 +1,12 @@
1
1
  require 'rmail'
2
+ require 'tlsmail' if(/^1.8/ =~ RUBY_VERSION) # Include the hack for ruby 1.8
2
3
  require 'net/smtp'
4
+ require 'time'
3
5
 
4
6
  module WatcherAction
5
7
 
6
8
  # This action will send an email to a given receipient. It doesn't support any
7
- # fancy features (you may want to do that handling externally) and will only
8
- # use unencrypted smtp network connections.
9
+ # fancy features (you may want to do that handling externally).
9
10
  #
10
11
  # =Options
11
12
  #
@@ -21,18 +22,20 @@ module WatcherAction
21
22
  # [*user*] Mail server user name
22
23
  # [*pass*] Mail server password
23
24
  # [*authentication*] Authentication method (default: plain)
25
+ # [*enable_tls*] Use the TLS encryption (default: false)
24
26
  class SendMail
25
27
 
26
28
  def initialize(config)
27
29
  @mail_to = config.get_value(:to, false)
28
30
  @sender = config.get_value(:sender, false)
29
- @subject = config.get_value(:subject, "Watchdogger triggered: %s")
31
+ @subject = config.get_value(:subject, "Watchdogger needs your attention.")
30
32
  @body = config.get_value(:body)
31
33
  @server = config.get_value(:server, 'localhost')
32
34
  @port = config.get_value(:port, '25')
33
35
  @user = config.get_value(:user)
34
36
  @pass = config.get_value(:pass)
35
37
  @authentication = config.get_value(:authentication, :plain).to_sym
38
+ @enable_tls = config.get_value(:enable_tls) || false
36
39
  end
37
40
 
38
41
  def execute(event)
@@ -40,7 +43,8 @@ module WatcherAction
40
43
  msg.header.to = @mail_to
41
44
  receipient = msg.header.to.to_s.split(',').first
42
45
  msg.header.from = @sender
43
- msg.header.subject = @subject % [event.message]
46
+ msg.header.subject = @subject % [event.message]
47
+ msg.header.date = Time.now
44
48
  if(@body.to_s == 'xml')
45
49
  msg.body = event.to_xml
46
50
  elsif(@body)
@@ -48,18 +52,25 @@ module WatcherAction
48
52
  else
49
53
  msg.body = "The #{event.watcher.class.name} watcher of your watchdog triggered\nan event at #{event.timestamp}:\n#{event.message}"
50
54
  end
51
-
55
+
52
56
  smtp_params = [@server, @port]
53
57
  if(@user && @pass)
54
- smtp_params << [nil, @user, @pass, @authentication]
58
+ smtp_params.concat([nil, @user, @pass, @authentication])
55
59
  end
56
60
 
57
- Net::SMTP.start(*smtp_params) do |smtp|
58
- smtp.send_message(msg.to_str, msg.header.from, msg.header.to)
61
+ Net::SMTP.enable_tls(OpenSSL::SSL::VERIFY_NONE) if(@enable_tls)
62
+ Thread.new(smtp_params, msg, @sender, @mail_to) do |params, msg, sender, mail_to|
63
+ begin
64
+ Net::SMTP.start(*params) do |smtp|
65
+ smtp.send_message(msg.to_s, sender, mail_to)
66
+ end
67
+ dog_log.debug('SMTP Thread') { "Sent mail to #{mail_to} through #{params.first}" }
68
+ rescue Exception => e
69
+ dog_log.error('SMTP Thread') { "Could not send mail to #{mail_to} on #{params.first}: #{e.message}" }
70
+ end
59
71
  end
60
- dog_log.debug('SMTP Action') { "Sent mail to #{@mail_to} through #{@server}" }
61
72
  rescue Exception => e
62
- dog_log.warn('SMTP Action') { "Could not send mail to #{@mail_to} on #{@server}: #{e.message}" }
73
+ dog_log.error('SMTP Action') { "Could not send mail to #{@mail_to} on #{@server}: #{e.message}" }
63
74
  end
64
75
 
65
76
  end
data/sample_config.yml CHANGED
@@ -3,6 +3,16 @@
3
3
  actions:
4
4
  log_it:
5
5
  type: log_action
6
+ mail_message:
7
+ type: send_mail
8
+ to: one@gmail.com
9
+ sender: myself@gmail.com
10
+ server: smtp.gmail.com
11
+ user: test@gmail.com
12
+ pass: secret
13
+ port: 587
14
+ authentication: login
15
+ enable_tls: true
6
16
 
7
17
  watchers:
8
18
  test_hn:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: averell23-watchdogger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Hahn
@@ -62,6 +62,16 @@ dependencies:
62
62
  - !ruby/object:Gem::Version
63
63
  version: 0.6.5
64
64
  version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: tlsmail
67
+ type: :runtime
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.0.1
74
+ version:
65
75
  description: A small flexible watchdog system to monitor servers.
66
76
  email: ghub@limitedcreativity.org
67
77
  executables: