averell23-watchdogger 0.1.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.
@@ -0,0 +1,42 @@
1
+ module WatcherAction
2
+
3
+ # This action posts the event to a given URL. It may use plain HTTP Authentication
4
+ #
5
+ # Options for this Action:
6
+ #
7
+ # url - The URL to post the information to (required)
8
+ # user - The user for HTTP Authentication (optional)
9
+ # pass - The password for HTTP Authentication.
10
+ class Htpost
11
+
12
+ def initialize(options)
13
+ @url = options.get_value(:url, false)
14
+ @user = options.get_value(:user)
15
+ @pass = options.get_value(:pass)
16
+ end
17
+
18
+ def execute(event)
19
+ con_url = URI.parse(@url)
20
+ req = Net::HTTP::Post.new(url.path)
21
+ if(@user && @pass)
22
+ req.basic_auth(@user, @pass)
23
+ end
24
+ req.set_form_data( {
25
+ 'type' => 'watchdogger_event',
26
+ 'event' => event.to_xml
27
+ }, ';'
28
+ )
29
+ res = Net::HTTP.new(url.host, url.port).start { |http| http.request(req) }
30
+ case res
31
+ when Net::HTTPSuccess, Net::HTTPRedirection:
32
+ dog_log.debug("HTPOST Action") { "Posted event to #{@url} "}
33
+ else
34
+ dog_log.warn("HTPOST Action") { "Could not post to #{@url}: #{@res}"}
35
+ end
36
+ rescue Exception => e
37
+ dog_log.warn("HTPOST Action") { "Error posting to #{@url}: #{e.message}"}
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,24 @@
1
+ module WatcherAction
2
+
3
+ # Kills the process with the given PID.
4
+ # Options:
5
+ #
6
+ # pidfile - The file containing the process id
7
+ # signal - The signal to send to the process. Defaults to KILL
8
+ class KillProcess
9
+
10
+ def initialize(config)
11
+ @pidfile = config.get_value(:pidfile, false)
12
+ @signal= config.get_value(:signal, 'KILL')
13
+ end
14
+
15
+ def execute(event)
16
+ pid = File.open(@pidfile) { |io| io.read }
17
+ Process.kill(@signal, pid.to_i)
18
+ rescue Exception => e
19
+ dog_log.warn { "Unable to kill process: #{e}" }
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'logger'
2
+
3
+ module WatcherAction
4
+
5
+ # Logs the event information to the standard log file
6
+ # Options:
7
+ #
8
+ # format - A format string that will receive the timestamp, watcher name and
9
+ # event message (in that order) as parameters (default message if not given)
10
+ # severity - The severity of the log message (default: warn)
11
+ class LogAction
12
+
13
+ def initialize(options)
14
+ @format = options.get_value(:format, "Watcher %s triggered at %s: %s")
15
+ if(severity = options.get_value(:severity))
16
+ @severity = Logger.const_get(severity.upcase)
17
+ else
18
+ @severity = Logger::WARN
19
+ end
20
+ end
21
+
22
+ def execute(event)
23
+ dog_log.add(@severity, nil, 'LoggerAction') { @format % [event.watcher, event.timestamp, event.message] }
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ require 'rmail'
2
+ require 'net/smtp'
3
+
4
+ module WatcherAction
5
+
6
+ # 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
+ #
10
+ # Options for this action:
11
+ #
12
+ # to - Email address to which to send the message. May be a list. (required)
13
+ # sender - Email address of the person sending the mail (required)
14
+ # subject - Subject of the message. You can put %s for the event message. (defaults if not set)
15
+ # body - Body of the email message. If set to 'xml', it will include an
16
+ # XML representation of the event. If not set, it will default
17
+ # to a sensible description of the event. You can include the
18
+ # event's message as for the subject
19
+ # server - Address or name of the mail server to use (required)
20
+ # port - Port to connect to (default: 25)
21
+ # user - Mail server user name
22
+ # pass - Mail server password
23
+ # authentication - Authentication method (default: plain)
24
+ class SendMail
25
+
26
+ def initialize(config)
27
+ @mail_to = config.get_value(:to, false)
28
+ @sender = config.get_value(:sender, false)
29
+ @subject = config.get_value(:subject, "Watchdogger triggered: %s")
30
+ @body = config.get_value(:body)
31
+ @server = config.get_value(:server, false)
32
+ @port = config.get_value(:port, '25')
33
+ @user = config.get_value(:user)
34
+ @pass = config.get_value(:pass)
35
+ @authentication = config.get_value(:authentication, :plain).to_sym
36
+ end
37
+
38
+ def execute(event)
39
+ msg = RMail::Message.new
40
+ msg.header.to = @mail_to
41
+ receipient = msg.header.to.split(',').first
42
+ msg.header.from = @sender
43
+ msg.header.subject = @subject % [event.message]
44
+ if(@body.to_s == 'xml')
45
+ msg.body = event.to_xml
46
+ elsif(@body)
47
+ msg.body = @body % [event.message]
48
+ else
49
+ msg.body = "The #{event.watcher.class.name} watcher of your watchdog triggered\nan event at #{event.timestamp}:\n#{event.message}"
50
+ end
51
+
52
+ smtp_params = [@server, @port]
53
+ if(@user && @pass)
54
+ smtp_params << [nil, @user, @pass, @authentication]
55
+ end
56
+
57
+ Net::SMTP.start(*smtp_params) do |smtp|
58
+ smtp.send_message(msg.to_str, msg.header.from, msg.header.to)
59
+ end
60
+ dog_log.debug('SMTP Action') { "Sent mail to #{@mail_to} through #{@server}" }
61
+ rescue Exception => e
62
+ dog_log.warn('SMTP Action') { "Could not send mail to #{@mail_to} on #{@server}: #{e.message}" }
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,41 @@
1
+ # Each action object contains the action that will be taken when a watched condition is triggered.
2
+ #
3
+ # Each action must respond to the #execute(event) method, which will execute the action.
4
+ #
5
+ # Actions should *not* log the executing to the log file, unless there is an unexpected error
6
+ # in the execution of the action itself. If the user wants to log to the log file,
7
+ # the logger action should be used. (All actions may use the log for debug-level information
8
+ # in all places.)
9
+ module WatcherAction
10
+
11
+ class << self
12
+
13
+ # creates a new action of the given type. The Action object itself will not
14
+ # be made public, instead call the #run_action method to execute it
15
+ def register(name, config_options)
16
+ assit_kind_of(String, name)
17
+ raise(ArgumentError, "Illegal options.") unless(config_options.is_a?(Hash))
18
+ type = config_options.get_value(:type, false)
19
+ type = WatchDogger.camelize(type)
20
+ registered_actions[name.to_sym] = WatcherAction.const_get(type).new(config_options)
21
+ dog_log.debug('Action Handler') { "Registered action '#{name}' of type #{type}"}
22
+ end
23
+
24
+ # Runs the named action. Returns true if the action ran without exception.
25
+ def run_action(name, event)
26
+ registered_actions[name.to_sym].execute(event)
27
+ true
28
+ rescue Exception => e
29
+ dog_log.error('Action Handler') { "Could not execute #{name}: #{e.message} (Registered actions: #{registered_actions.keys.join(', ')})" }
30
+ false
31
+ end
32
+
33
+ private
34
+
35
+ def registered_actions
36
+ @registered_actions ||= {}
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,23 @@
1
+ require 'builder'
2
+
3
+ # Encodes the information of an event that was triggered.
4
+ class WatcherEvent
5
+
6
+ attr_accessor :timestamp
7
+ attr_accessor :watcher
8
+ attr_accessor :message
9
+
10
+ def to_xml
11
+ builder = Builder::XmlMarkup.new()
12
+ builder.instruct!
13
+ builder.event do
14
+ builder.timestamp(timestamp)
15
+ builder.message(message)
16
+ builder.watcher do
17
+ builder.name(watcher.name)
18
+ builder.watcher_type(watcher.class.name)
19
+ end
20
+ end
21
+ end
22
+
23
+ end
data/sample_config.yml ADDED
@@ -0,0 +1,16 @@
1
+ # Watchdogger sample configuration
2
+
3
+ actions:
4
+ log_it:
5
+ type: log_action
6
+
7
+ watchers:
8
+ test_hn:
9
+ type: http_watcher
10
+ url: http://www.foobar.or/
11
+ actions: log_it
12
+
13
+ interval: 3
14
+ logfile: daemon.log
15
+ pidfile: watcher.pid
16
+
@@ -0,0 +1,63 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ # The URL that will be used for testing. Should return a 200 result and a body.
4
+ TEST_URL = 'http://wiki.github.com/averell23/watchdogger'
5
+ # Text that will be expected in the page body
6
+ TEST_URL_TEXT = 'averell23'
7
+
8
+ class HttpWatcherTest < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @default_options = {
12
+ :url => TEST_URL,
13
+ :actions => 'default',
14
+ :timeout => '2'
15
+ }
16
+ end
17
+
18
+ def test_simple
19
+ watcher = Watcher::HttpWatcher.new(@default_options)
20
+ assert_equal(false, watcher.watch_it!)
21
+ end
22
+
23
+ def test_simple_match
24
+ @default_options[:content_match] = TEST_URL_TEXT
25
+ watcher = Watcher::HttpWatcher.new(@default_options)
26
+ assert_equal(false, watcher.watch_it!)
27
+ end
28
+
29
+ def test_simple_response
30
+ @default_options[:response] = '200'
31
+ watcher = Watcher::HttpWatcher.new(@default_options)
32
+ assert_equal(false, watcher.watch_it!)
33
+ end
34
+
35
+ def test_404_response
36
+ @default_options[:url] = TEST_URL + '/narfnarf'
37
+ @default_options[:response] = '404'
38
+ watcher = Watcher::HttpWatcher.new(@default_options)
39
+ assert_equal(false, watcher.watch_it!)
40
+ end
41
+
42
+ def test_404_response_fail
43
+ @default_options[:url] = TEST_URL + '/narfnarf'
44
+ watcher = Watcher::HttpWatcher.new(@default_options)
45
+ assert_kind_of(String, watcher.watch_it!)
46
+ end
47
+
48
+ def test_simple_match_fail
49
+ @default_options[:content_match] = 'grubellnurf'
50
+ watcher = Watcher::HttpWatcher.new(@default_options)
51
+ assert_kind_of(String, watcher.watch_it!)
52
+ end
53
+
54
+ def test_illegal_open_fail
55
+ watcher = Watcher::HttpWatcher.new(
56
+ :url => 'http://www.gurgl.gurl/',
57
+ :actions => 'default',
58
+ :content_match => 'grubellnurf'
59
+ )
60
+ assert_kind_of(String, watcher.watch_it!)
61
+ end
62
+
63
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ PIDFILE = 'test.pid'
4
+
5
+ class KillProcessTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @killer = WatcherAction::KillProcess.new(:pidfile => PIDFILE)
9
+ # Fork a dummy process that we can kill
10
+ @pid = Process.fork { loop { sleep 1 }}
11
+ @proc = Process.detach(@pid)
12
+ File.open(PIDFILE, 'w') { |io| io << @pid }
13
+ end
14
+
15
+ def teardown
16
+ FileUtils.remove(PIDFILE) if(File.exists?(PIDFILE))
17
+ @proc.terminate! if(@proc.status != false)
18
+ rescue Exception => e
19
+ puts "! unclean teardown #{e}"
20
+ end
21
+
22
+ def test_killer
23
+ assert_not_equal(false, @proc.status)
24
+ @killer.execute(WatcherEvent.new)
25
+ sleep 1 # Wait for the sleep - the ruby process will handle the kill only then
26
+ assert_equal(false, @proc.status)
27
+ end
28
+
29
+ end
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class LogWatcherTest < Test::Unit::TestCase
4
+
5
+ def logfile
6
+ File.expand_path(File.join(File.dirname(__FILE__), 'log_test.tmp'))
7
+ end
8
+
9
+ def setup
10
+ FileUtils.remove(logfile) if(File.exists?(logfile))
11
+ File.open(logfile, 'w') { |io| io << 'test' }
12
+ @watcher = Watcher::LogWatcher.new(
13
+ :logfile => logfile,
14
+ :match => 'da_test',
15
+ :interval_first => '1',
16
+ :interval_max => '1'
17
+ )
18
+ end
19
+
20
+ def teardown
21
+ @watcher.cleanup
22
+ FileUtils.remove(logfile) if(File.exists?(logfile))
23
+ end
24
+
25
+ def test_watcher_plain
26
+ assert_equal(false, @watcher.watch_it!)
27
+ end
28
+
29
+ def test_watcher_trigger
30
+ File.open(logfile, 'a') do |io|
31
+ 20.times { io << 'nothing special' }
32
+ io << 'da_test'
33
+ end
34
+ sleep 2
35
+ assert_kind_of(String, @watcher.watch_it!)
36
+ end
37
+
38
+ end
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'watchdogger')
5
+
6
+ DogLog.setup('tmp.log', Logger::DEBUG)
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ module Watcher
4
+ class DummyWatcher < Watcher::Base
5
+ def initialize(config)
6
+ @watchit = config[:watchit]
7
+ end
8
+ def watch_it!
9
+ @watchit
10
+ end
11
+
12
+ end
13
+ end
14
+
15
+ module WatcherAction
16
+ class DummyAction
17
+ def initialize(config)
18
+ end
19
+ def execute(event)
20
+ @executed = true
21
+ raise(Exception) unless(event.is_a?(WatcherEvent))
22
+ raise(ArgumentError, "Boom!")
23
+ end
24
+ end
25
+ end
26
+
27
+ class WatchdoggerTest < Test::Unit::TestCase
28
+
29
+ def setup
30
+ Watcher.instance_variable_set(:@registered_watchers, nil)
31
+ WatcherAction.instance_variable_set(:@registered_actions, nil)
32
+ WatcherAction.register('default', { :type => 'dummy_action' })
33
+ WatcherAction.register('log_action', { 'type' => :log_action })
34
+ Watcher.register('dummy', { :type => 'dummy_watcher', 'actions' => [ :default, :log_action ] })
35
+ end
36
+
37
+ def test_create_watcher
38
+ watchers = Watcher.instance_variable_get(:@registered_watchers)
39
+ assert_equal(1, watchers.size)
40
+ assert_kind_of(Watcher::DummyWatcher, watchers.first)
41
+ end
42
+
43
+ def test_watcher_actions
44
+ actions = Watcher.instance_variable_get(:@registered_watchers).first.send(:actions)
45
+ assert_equal([:default, :log_action], actions)
46
+ end
47
+
48
+ def test_registered_action
49
+ actions = WatcherAction.instance_variable_get(:@registered_actions)
50
+ assert_equal(2, actions.size)
51
+ assert_kind_of(WatcherAction::DummyAction, actions[:default])
52
+ assert_kind_of(WatcherAction::LogAction, actions[:log_action])
53
+ end
54
+
55
+ def test_run_action
56
+ event = WatcherEvent.new
57
+ event.timestamp = Time.now
58
+ event.message = "TEST MESSAGE"
59
+ event.watcher = "TEST"
60
+ assert_equal(false, WatcherAction.run_action('default', event))
61
+ assert_equal(true, action_status('default'))
62
+ WatcherAction.run_action('log_action', event)
63
+ end
64
+
65
+ def test_run_watcher
66
+ assert_nothing_raised { Watcher.watch_all! }
67
+ end
68
+
69
+ def test_run_watcher_execute
70
+ assert_equal(nil, action_status('default'))
71
+ Watcher.register('dummy2', { 'type' => :dummy_watcher, 'actions' => :default, :watchit => 'Fail' })
72
+ Watcher.watch_all!
73
+ assert_equal(true, action_status('default'))
74
+ end
75
+
76
+ private
77
+
78
+ def action_status(name)
79
+ action = WatcherAction.instance_variable_get(:@registered_actions).get_value(name)
80
+ action.instance_variable_get(:@executed)
81
+ end
82
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: averell23-watchdogger
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Hahn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-16 00:00:00 -07:00
13
+ default_executable: watchdogger
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: file-tail
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: averell23-assit
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rmail
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: builder
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 2.1.2
54
+ version:
55
+ description: A small flexible watchdog system to monitor servers.
56
+ email: ghub@limitedcreativity.org
57
+ executables:
58
+ - watchdogger
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - README.rdoc
63
+ - CHANGES
64
+ - LICENSE
65
+ - sample_config.yml
66
+ files:
67
+ - lib/dog_log.rb
68
+ - lib/watchdogger.rb
69
+ - lib/watcher
70
+ - lib/watcher/base.rb
71
+ - lib/watcher/http_watcher.rb
72
+ - lib/watcher/log_watcher.rb
73
+ - lib/watcher.rb
74
+ - lib/watcher_action
75
+ - lib/watcher_action/htpost.rb
76
+ - lib/watcher_action/kill_process.rb
77
+ - lib/watcher_action/log_action.rb
78
+ - lib/watcher_action/send_mail.rb
79
+ - lib/watcher_action.rb
80
+ - lib/watcher_event.rb
81
+ - test/http_watcher_test.rb
82
+ - test/kill_process_test.rb
83
+ - test/log_watcher_test.rb
84
+ - test/test_helper.rb
85
+ - test/watchdogger_test.rb
86
+ - bin/watchdogger
87
+ - README.rdoc
88
+ - CHANGES
89
+ - LICENSE
90
+ - sample_config.yml
91
+ has_rdoc: true
92
+ homepage: http://averell23.github.com/watchdogger
93
+ post_install_message:
94
+ rdoc_options:
95
+ - --inline-source
96
+ - --charset=UTF-8
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: "0"
104
+ version:
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: "0"
110
+ version:
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.2.0
115
+ signing_key:
116
+ specification_version: 2
117
+ summary: Simple and flexible watchdog running on Ruby.
118
+ test_files: []
119
+