pingmon 0.2

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,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in pingmon.gemspec
4
+ gemspec
@@ -0,0 +1,48 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pingmon (0.1)
5
+ eventmachine (~> 0.12.10)
6
+ pony (~> 1.0.1)
7
+ rufus-scheduler (~> 2.0.6)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activesupport (3.0.1)
13
+ diff-lcs (1.1.2)
14
+ eventmachine (0.12.10)
15
+ i18n (0.4.2)
16
+ mail (2.2.9)
17
+ activesupport (>= 2.3.6)
18
+ i18n (~> 0.4.1)
19
+ mime-types (~> 1.16)
20
+ treetop (~> 1.4.8)
21
+ mime-types (1.16)
22
+ polyglot (0.3.1)
23
+ pony (1.0.1)
24
+ mail (> 2.0)
25
+ rspec (2.0.1)
26
+ rspec-core (~> 2.0.1)
27
+ rspec-expectations (~> 2.0.1)
28
+ rspec-mocks (~> 2.0.1)
29
+ rspec-core (2.0.1)
30
+ rspec-expectations (2.0.1)
31
+ diff-lcs (>= 1.1.2)
32
+ rspec-mocks (2.0.1)
33
+ rspec-core (~> 2.0.1)
34
+ rspec-expectations (~> 2.0.1)
35
+ rufus-scheduler (2.0.6)
36
+ treetop (1.4.8)
37
+ polyglot (>= 0.3.1)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bundler (>= 1.0.0)
44
+ eventmachine (~> 0.12.10)
45
+ pingmon!
46
+ pony (~> 1.0.1)
47
+ rspec (~> 2.0.1)
48
+ rufus-scheduler (~> 2.0.6)
@@ -0,0 +1,73 @@
1
+ # PingMon: A stupid local-networking host 'ping' monitoring tool
2
+
3
+ One day at work I needed to periodically ping a host on our local network. Then I
4
+ thought it would be great (well... just nice) that when the pings were unsuccessful
5
+ I would get notified by email.
6
+
7
+ I was learning Ruby at that time and, instead of writing a simple shell script and set a cron job
8
+ I thought... *Maybe I could write a script in Ruby for this*, *Maybe I could practice
9
+ writing some tests*, *I could even create a gem and learn how it's done*, etc.
10
+
11
+ And this is the result:
12
+
13
+ A *stupid* monitoring tool which periodically pings a host and sends an email
14
+ notification when it becomes unavailable.
15
+
16
+ *It's been done merely for learning purposes so don't be too harsh on the code.*
17
+
18
+ ### Installation & Usage
19
+
20
+ 1. Install the gem as usual by entering:
21
+ $ gem install pingmon
22
+
23
+ 2. PingMon needs a *YAML configuration file* to operate, which is usually:
24
+ `~/.pingmon.yml`. To make PingMon create that file for you, type:
25
+ $ pingmon build-config
26
+
27
+ 3. Edit `~/.pingmon.yml` to suit your needs.
28
+
29
+ 4. Finally, type `pingmon` on your prompt and have fun! ;)
30
+
31
+ #### .pingmon.yml options
32
+
33
+ There are a number of options you can tweak in the `.pingmon.yml` config file. They
34
+ are pretty self-explanatory but I'll explain some of them here:
35
+
36
+ - **host:** specifies the host to 'ping' to.
37
+ - **mode:** PingMon operation mode which should be one of `monitor` or `ping`. In
38
+ `ping` mode, PingMon will ping the host specified one time only (for testing
39
+ purposes). When in `monitor` mode, PingMon will ping the host specified periodically
40
+ following the `monitor_interval` option.
41
+ - **monitor_interval:** when set to `monitor` mode it specifies the delay between
42
+ *pings* to check the host status. ex: Setting it to `15m` will ping the `host`
43
+ *every* 15 minutes.
44
+ - **notify_when_down:** `true` or `false`. Tells PingMon to send an email notification
45
+ when host can't be reached.
46
+ - **notify_to:** whom to notify (if more than one, comma separated). ex: `user@domain.com`
47
+ - **transport:** `:sendmail` or `:smtp`. `:sendmail` may only work on unix/linux OS
48
+ variants.
49
+ - **smtp_options:** when `transport:` is set to `:smtp` you should specify this
50
+ options. They are passed *as is* to `Pony.mail` method.
51
+
52
+ For more help, please take a look at the sample config file `config/pingmon.yml` or
53
+ at the one PingMon generates for you when you type `pingmon build-config` which is
54
+ usually located at: `$HOME/.pingmon.yml`.
55
+
56
+ ### External Dependencies
57
+
58
+ PingMon depends on the following gems:
59
+
60
+ - [eventmachine](http://github.com/eventmachine/eventmachine) ~> 0.12.10
61
+ - [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler) ~> 2.0.6
62
+ - [pony](http://github.com/adamwiggins/pony) ~> 1.0.1
63
+
64
+ Notes:
65
+
66
+ - [eventmachine](http://github.com/eventmachine/eventmachine) and [rufus-scheduler](http://github.com/jmettraux/rufus-scheduler) are used when doing "pings" in monitor mode.
67
+ - [Pony](http://github.com/adamwiggins/pony) adds the mail sending logic
68
+ - This requirements are specified in the gemspec as well.
69
+
70
+ ---
71
+
72
+ Coded by Estanislau Trepat, released under the MIT License: [http://www.opensource.org/licenses/mit-license.php](http://www.opensource.org/licenses/mit-license.php)
73
+
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + "/../lib"
4
+ require 'pingmon'
5
+
6
+ def usage(why = nil)
7
+ puts "failed because: #{why}" if why
8
+ puts "usage:\tpingmon [build-config] [/path/to/pingmon.yml]"
9
+ end
10
+
11
+ usage("wrong number of args") if ARGV.size != 0 && ARGV.size != 1
12
+
13
+ if ARGV.size == 1 && ARGV.first.strip == 'build-config'
14
+ PingMon::Config.build_config
15
+ ARGV.shift
16
+ end
17
+
18
+ begin
19
+ PingMon.log = ENV['PINGMON_LOG'] || 'stdout'
20
+ PingMon.execute_from_config(ARGV.first || PingMon::DEFAULT_CONFIG_FILE)
21
+ rescue PingMon::ConfigFileNotFound => e
22
+ puts "WTF! #{e.message}"
23
+ puts "If this is your first time using pingmon you should run \`pingmon build-config\`." if ARGV.size == 0
24
+ rescue StandardError => e
25
+ puts "WTF! #{e.message}"
26
+ rescue SystemExit, Interrupt
27
+ puts "See ya!"
28
+ end
29
+
@@ -0,0 +1,26 @@
1
+ # pingmon config options
2
+ config:
3
+ # host to be monitored
4
+ host: 192.168.0.7
5
+ # mode (ping/monitor)
6
+ mode: monitor
7
+ # monitor (ping) host every 2 minutes (if set to monitor mode)
8
+ monitor_interval: 2m
9
+ # wether it should send 'down' notifications by email
10
+ notify_when_down: true
11
+ # whom to notify (if more than one, comma separated)
12
+ notify_to: estanis.trepat@inson.es
13
+ # from address
14
+ from_address: estanis.trepat@inson.es
15
+ # :sendmail or :smtp (options below). :sendmail may only work on unix/linux
16
+ transport: :sendmail
17
+ # smtp_options ( if transport is set to :smtp. They are passed *as is* to `Pony.mail` method )
18
+ smtp_options:
19
+ address: your.smtpserver.com
20
+ port: 25
21
+ enable_starttls_auto: false
22
+ user_name: auser
23
+ password: apassword
24
+ authentication: :plain
25
+ domain: smtpserver.com
26
+
@@ -0,0 +1,88 @@
1
+ require 'ping'
2
+ require 'erb'
3
+ require 'yaml'
4
+ require 'rubygems'
5
+ require 'pony'
6
+ require 'eventmachine'
7
+ require 'rufus/scheduler'
8
+
9
+ require File.dirname(__FILE__) + '/pingmon/config.rb'
10
+ require File.dirname(__FILE__) + '/pingmon/pinger.rb'
11
+ require File.dirname(__FILE__) + '/pingmon/monitor.rb'
12
+ require File.dirname(__FILE__) + '/pingmon/version.rb'
13
+
14
+ module PingMon
15
+ DEFAULT_CONFIG_FILE = File.expand_path('~/.pingmon.yml')
16
+
17
+ def self.ping(config=PingMon::Config.new)
18
+ p = PingMon::Pinger.new(config)
19
+ p.ping
20
+ end
21
+
22
+ def self.monitor(config=PingMon::Config.new)
23
+ m = PingMon::Monitor.new(config)
24
+ m.monitor
25
+ end
26
+
27
+ def self.execute_from_config(config_file=PingMon::DEFAULT_CONFIG_FILE)
28
+ config = PingMon::Config.new(config_file).load
29
+ raise "wrong mode" unless %w(ping monitor).include?(config.mode)
30
+
31
+ case config.mode
32
+ when "ping"
33
+ self.ping(config)
34
+ when "monitor"
35
+ self.monitor(config)
36
+ end
37
+ end
38
+
39
+ @@log = nil
40
+
41
+ # value may be a logger instance or a string: 'stdout'|'stderr'|'an actual filename'
42
+ def self.log= log
43
+ @@log = create_log log
44
+ end
45
+
46
+ def self.log
47
+ @@log
48
+ end
49
+
50
+ # Create a log that respond to << like a logger
51
+ # param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
52
+ def self.create_log param
53
+ if param
54
+ if param.is_a? String
55
+ if param == 'stdout'
56
+ stdout_logger = Class.new do
57
+ def << obj
58
+ STDOUT.puts obj
59
+ end
60
+ end
61
+ stdout_logger.new
62
+ elsif param == 'stderr'
63
+ stderr_logger = Class.new do
64
+ def << obj
65
+ STDERR.puts obj
66
+ end
67
+ end
68
+ stderr_logger.new
69
+ else
70
+ file_logger = Class.new do
71
+ attr_writer :target_file
72
+
73
+ def << obj
74
+ File.open(@target_file, 'a') { |f| f.puts obj }
75
+ end
76
+ end
77
+ logger = file_logger.new
78
+ logger.target_file = param
79
+ logger
80
+ end
81
+ else
82
+ param
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+
@@ -0,0 +1,46 @@
1
+ require 'ftools'
2
+
3
+ module PingMon
4
+ class ConfigFileNotFound < StandardError
5
+ end
6
+
7
+ class Config
8
+ def initialize(config_file=PingMon::DEFAULT_CONFIG_FILE)
9
+ @config_file = config_file
10
+ @loaded = false
11
+ end
12
+
13
+ attr_accessor :config_file
14
+
15
+ def load
16
+ raise ConfigFileNotFound.new("Configuration file '#{config_file}' could not be found!") unless File.exists?(config_file)
17
+
18
+ PingMon.log << "Loading configuration from '#{@config_file}'" if PingMon.log
19
+
20
+ text = ERB.new(File.read(config_file)).result
21
+ hash = YAML.load(text)
22
+
23
+ base_config = (hash.symbolize_keys[:config]).stringify_keys
24
+ base_config.keys.each do |key|
25
+ instance_variable_set("@#{key}", base_config[key])
26
+ self.class.class_eval do
27
+ define_method(key.to_sym) { instance_variable_get("@#{key}") }
28
+ end
29
+ end
30
+ @loaded = true
31
+
32
+ self
33
+ end
34
+
35
+ def loaded?
36
+ @loaded
37
+ end
38
+
39
+ def self.build_config(where=PingMon::DEFAULT_CONFIG_FILE)
40
+ from = File.dirname(__FILE__) + '/../../config/pingmon.yml'
41
+ File.copy(File.expand_path(from), where)
42
+ PingMon.Log << "Created configuration file '#{where}'." if PingMon.log
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,23 @@
1
+ module PingMon
2
+ class Monitor
3
+ def initialize(configuration=Config.new)
4
+ @config = configuration
5
+ end
6
+
7
+ def monitor
8
+ @config.load unless @config.loaded?
9
+
10
+ PingMon.log << "Monitoring host '#{@config.host}'. Will ping every '#{@config.monitor_interval}'." if PingMon.log
11
+
12
+ EM.run {
13
+ pinger = Pinger.new(@config)
14
+ scheduler = Rufus::Scheduler::EmScheduler.start_new
15
+
16
+ scheduler.every @config.monitor_interval do
17
+ pinger.ping
18
+ end
19
+ }
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,49 @@
1
+ module PingMon
2
+ class Pinger
3
+ def self.ping(host)
4
+ ::Ping.pingecho(host)
5
+ end
6
+
7
+ def initialize(configuration=Config.new)
8
+ @config = configuration
9
+ end
10
+
11
+ def ping
12
+ @config.load unless @config.loaded?
13
+
14
+ PingMon.log << "[#{Time.now}] - Pinging host '#{@config.host}'." if PingMon.log
15
+ result = PingMon::Pinger.ping(@config.host)
16
+ unless result
17
+ PingMon.log << "[#{Time.now}] - '#{@config.host}' appears to be: DOWN. Sending notification." if PingMon.log
18
+ notify_down_status if @config.notify_when_down
19
+ else
20
+ PingMon.log << "[#{Time.now}] - '#{@config.host}' appears to be: UP." if PingMon.log
21
+ end
22
+
23
+ result
24
+ end
25
+
26
+ def host
27
+ @config.host if @config.loaded?
28
+ end
29
+
30
+ protected
31
+ def notify_down_status
32
+ params = build_pony_params_hash
33
+ Pony.mail(params)
34
+ end
35
+
36
+ def build_pony_params_hash
37
+ hash = {
38
+ :to => @config.notify_to,
39
+ :from => @config.from_address,
40
+ :subject => "Monitored host '#{@config.host}' seems to be down",
41
+ :body => "Ping to #{@config.host} failed at #{Time.now}!",
42
+ :via => @config.transport
43
+ }
44
+ hash[:via_options] = @config.smtp_options.symbolize_keys if hash[:via] == :smtp
45
+ return hash
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,8 @@
1
+ module PingMon
2
+ def self.version
3
+ version_path = File.dirname(__FILE__) + "/../../VERSION"
4
+ return File.read(version_path).chomp if File.file?(version_path)
5
+ "0.0"
6
+ end
7
+ end
8
+
@@ -0,0 +1,45 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/pingmon/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = %q{pingmon}
6
+ s.version = PingMon.version
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Estanislau Trepat"]
9
+ s.email = %q{estanis@etrepat.com}
10
+ s.homepage = "http://github.com/etrepat/pingmon"
11
+ s.summary = "A stupid local-networking host 'ping' monitoring tool"
12
+ s.description = %[
13
+ A stupid monitoring tool which periodically pings a host and sends an email
14
+ notification when it becomes unavailable. It was created merely for learning
15
+ purposes and became my first gem.
16
+ ]
17
+ s.date = %q{2010-07-24}
18
+
19
+ s.required_rubygems_version = ">= 1.3.6"
20
+ s.rubyforge_project = "pingmon"
21
+
22
+ s.add_development_dependency "bundler", ">= 1.0.0"
23
+ s.add_development_dependency "rspec", "~> 2.0.1"
24
+
25
+ s.add_dependency "pony", "~> 1.0.1"
26
+ s.add_dependency "eventmachine", "~> 0.12.10"
27
+ s.add_dependency "rufus-scheduler", "~> 2.0.6"
28
+
29
+ s.files = `git ls-files`.split("\n")
30
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
31
+ s.require_path = 'lib'
32
+ s.default_executable = %q{pingmon}
33
+
34
+ s.post_install_message = %[
35
+ ===========================================================================
36
+ Welcome to PingMon!
37
+
38
+ You may need to run \`pingmon build-config\` now if this is your first time
39
+ using pingmon. If not... well... you should just do... nothing ;).
40
+
41
+ Cheers!
42
+ ===========================================================================
43
+ ]
44
+ end
45
+
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pingmon
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ version: "0.2"
10
+ platform: ruby
11
+ authors:
12
+ - Estanislau Trepat
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-24 00:00:00 +02:00
18
+ default_executable: pingmon
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bundler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 23
29
+ segments:
30
+ - 1
31
+ - 0
32
+ - 0
33
+ version: 1.0.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 13
45
+ segments:
46
+ - 2
47
+ - 0
48
+ - 1
49
+ version: 2.0.1
50
+ type: :development
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: pony
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 21
61
+ segments:
62
+ - 1
63
+ - 0
64
+ - 1
65
+ version: 1.0.1
66
+ type: :runtime
67
+ version_requirements: *id003
68
+ - !ruby/object:Gem::Dependency
69
+ name: eventmachine
70
+ prerelease: false
71
+ requirement: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 59
77
+ segments:
78
+ - 0
79
+ - 12
80
+ - 10
81
+ version: 0.12.10
82
+ type: :runtime
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ name: rufus-scheduler
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 2
95
+ - 0
96
+ - 6
97
+ version: 2.0.6
98
+ type: :runtime
99
+ version_requirements: *id005
100
+ description: "\n\
101
+ A stupid monitoring tool which periodically pings a host and sends an email\n\
102
+ notification when it becomes unavailable. It was created merely for learning\n\
103
+ purposes and became my first gem.\n "
104
+ email: estanis@etrepat.com
105
+ executables:
106
+ - pingmon
107
+ extensions: []
108
+
109
+ extra_rdoc_files: []
110
+
111
+ files:
112
+ - .gitignore
113
+ - Gemfile
114
+ - Gemfile.lock
115
+ - README.md
116
+ - Rakefile
117
+ - VERSION
118
+ - bin/pingmon
119
+ - config/pingmon.yml
120
+ - lib/pingmon.rb
121
+ - lib/pingmon/config.rb
122
+ - lib/pingmon/monitor.rb
123
+ - lib/pingmon/pinger.rb
124
+ - lib/pingmon/version.rb
125
+ - pingmon.gemspec
126
+ has_rdoc: true
127
+ homepage: http://github.com/etrepat/pingmon
128
+ licenses: []
129
+
130
+ post_install_message: "\n\
131
+ ===========================================================================\n\
132
+ Welcome to PingMon!\n\n\
133
+ You may need to run `pingmon build-config` now if this is your first time\n\
134
+ using pingmon. If not... well... you should just do... nothing ;).\n\n\
135
+ Cheers!\n\
136
+ ===========================================================================\n "
137
+ rdoc_options: []
138
+
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ hash: 23
156
+ segments:
157
+ - 1
158
+ - 3
159
+ - 6
160
+ version: 1.3.6
161
+ requirements: []
162
+
163
+ rubyforge_project: pingmon
164
+ rubygems_version: 1.3.7
165
+ signing_key:
166
+ specification_version: 3
167
+ summary: A stupid local-networking host 'ping' monitoring tool
168
+ test_files: []
169
+