averell23-watchdogger 0.1.5 → 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.
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: