nagiosharder 0.3.0 → 0.4.0.rc1

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/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ nagiosharder*.gem
23
+ .bundle
24
+ vendor/gems
25
+ config
26
+ *.yml
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ nagiosharder (0.4.0)
5
+ activesupport
6
+ hashie (~> 1.2.0)
7
+ httparty (~> 0.8.3)
8
+ i18n
9
+ nokogiri
10
+ rest-client (~> 1.6.1)
11
+ terminal-table
12
+
13
+ GEM
14
+ remote: http://rubygems.org/
15
+ specs:
16
+ activesupport (3.2.8)
17
+ i18n (~> 0.6)
18
+ multi_json (~> 1.0)
19
+ diff-lcs (1.1.3)
20
+ hashie (1.2.0)
21
+ httparty (0.8.3)
22
+ multi_json (~> 1.0)
23
+ multi_xml
24
+ i18n (0.6.1)
25
+ mime-types (1.19)
26
+ multi_json (1.3.6)
27
+ multi_xml (0.5.1)
28
+ nokogiri (1.5.5)
29
+ rest-client (1.6.7)
30
+ mime-types (>= 1.16)
31
+ rspec (2.7.0)
32
+ rspec-core (~> 2.7.0)
33
+ rspec-expectations (~> 2.7.0)
34
+ rspec-mocks (~> 2.7.0)
35
+ rspec-core (2.7.1)
36
+ rspec-expectations (2.7.0)
37
+ diff-lcs (~> 1.1.2)
38
+ rspec-mocks (2.7.0)
39
+ terminal-table (1.4.5)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ nagiosharder!
46
+ rspec (>= 1.2.9)
data/README.rdoc CHANGED
@@ -1,11 +1,18 @@
1
1
  = nagiosharder
2
2
 
3
- Query and command a Nagios install using the power of ruby.
3
+ Query and command a Nagios install using the power of ruby (and lots of screen-scaping)! Do the usual gem install jig:
4
+
5
+ gem install nagiosharder
6
+
7
+
8
+ Now you have access to both a Ruby interface and a command line interface.
9
+
10
+ Here's some examples to get you started with the Ruby API:
4
11
 
5
12
  require 'nagiosharder'
6
13
  site = NagiosHarder::Site.new('http://path/to/nagios/cgi/directory', 'user', 'password', 'version')
7
14
  # version defaults to 3
8
-
15
+
9
16
  # get details back about a host's services
10
17
  puts site.host_status('myhost')
11
18
 
@@ -13,16 +20,28 @@ Query and command a Nagios install using the power of ruby.
13
20
  site.schedule_host_check('myhost')
14
21
 
15
22
  # get details on all services
16
- site.service_status(:all)
17
- # or just services that have problems
18
- site.service_status(:all_problems)
19
- # (other valid symbols: :ok, :warning, :unknown, :critical, :pending, :all_problems)
20
- # or make it sorted by duration, descending
21
- site.service_status(:all_problem, :sort_option => :duration, :sort_type => :desc
22
- # (other sort_options: :host, :service, :status, :last_check, :duration, :attempts)
23
- # (other sort_types: :asc, :desc)
23
+ site.service_status
24
+
25
+ # or just things with problems
26
+ site.service_status(
27
+ :service_status_types => [
28
+ :critical,
29
+ :warning,
30
+ :unknown
31
+ ]
32
+ )
33
+
34
+ # or just muted services, sorted desc by duration
35
+ site.service_status(
36
+ :service_props => [
37
+ :notifications_disabled,
38
+ ],
39
+ :sort_type => :descending,
40
+ :sort_option => :state_duration,
41
+ )
42
+
24
43
  # or get the details for a single service group
25
- site.service_status(:all_problem, :group => "AWESOME")
44
+ site.service_status(:group => "AWESOME")
26
45
 
27
46
  # schedule a host to have services checks run again right now
28
47
  site.schedule_service_check('myhost', 'myservice')
@@ -39,6 +58,9 @@ Query and command a Nagios install using the power of ruby.
39
58
  # or unacknowledge
40
59
  site.unacknowledge_service('myhost', 'myservice')
41
60
 
61
+ # acknowledge a down service
62
+ site.acknowledge_host('myhost', 'something bad happened')
63
+
42
64
  # disable notifications for a service:
43
65
  site.disable_service_notifications('myhost', 'myservice')
44
66
 
@@ -54,10 +76,19 @@ Query and command a Nagios install using the power of ruby.
54
76
  sleep 3
55
77
  end
56
78
 
57
- To be continue?
79
+ Then there's the command line. Start with --help
80
+
81
+ nagiosharder --help
82
+
83
+ This will show you how you configure nagiosharder enough to talk to your nagios. You need at least a username, password, and nagios url. These can alternatively be in a config file. For example:
84
+
85
+ nagiosharder --config /path/to/yaml
86
+
87
+ This will display all available commands.
88
+
58
89
 
59
90
  == Note on Patches/Pull Requests
60
-
91
+
61
92
  * Fork the project.
62
93
  * Make your feature addition or bug fix.
63
94
  * Add tests for it. This is important so I don't break it in a future version unintentionally.
data/Rakefile CHANGED
@@ -1,51 +1,2 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "nagiosharder"
8
- gem.version = "0.3.0"
9
- gem.summary = %Q{Nagios access at your ruby fingertips}
10
- gem.description = %Q{Nagios access at your ruby fingertips}
11
- gem.email = "josh@technicalpickles.com"
12
- gem.homepage = "http://github.com/railsmachine/nagiosharder"
13
- gem.authors = ["Joshua Nichols"]
14
- gem.add_dependency 'rest-client', '~> 1.6.1'
15
- gem.add_dependency 'nokogiri', '~> 1.4.3'
16
- gem.add_dependency 'activesupport'
17
- gem.add_dependency 'httparty', '~> 0.6.1'
18
- gem.add_dependency 'hashie', '~> 1.0.0'
19
- gem.add_development_dependency "rspec", ">= 1.2.9"
20
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
- end
22
- Jeweler::GemcutterTasks.new
23
- rescue LoadError
24
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
- end
26
-
27
- require 'spec/rake/spectask'
28
- Spec::Rake::SpecTask.new(:spec) do |spec|
29
- spec.libs << 'lib' << 'spec'
30
- spec.spec_files = FileList['spec/**/*_spec.rb']
31
- end
32
-
33
- Spec::Rake::SpecTask.new(:rcov) do |spec|
34
- spec.libs << 'lib' << 'spec'
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
37
- end
38
-
39
- task :spec => :check_dependencies
40
-
41
- task :default => :spec
42
-
43
- require 'rake/rdoctask'
44
- Rake::RDocTask.new do |rdoc|
45
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
-
47
- rdoc.rdoc_dir = 'rdoc'
48
- rdoc.title = "nagiosharder #{version}"
49
- rdoc.rdoc_files.include('README*')
50
- rdoc.rdoc_files.include('lib/**/*.rb')
51
- end
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/nagiosharder ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'nagiosharder/cli'
3
+ begin
4
+ exit NagiosHarder::Cli.new(ARGV).run
5
+ rescue ArgumentError => e
6
+ puts e.message
7
+ puts e.backtrace.join("\n") if ENV['DEBUG']
8
+ exit 1
9
+ end
data/lib/nagiosharder.rb CHANGED
@@ -3,6 +3,7 @@ require 'nokogiri'
3
3
  require 'active_support' # fine, we'll just do all of activesupport instead of the parts I want. thank Rails 3 for shuffling requires around.
4
4
  require 'cgi'
5
5
  require 'hashie'
6
+ require 'nagiosharder/filters'
6
7
 
7
8
  # :(
8
9
  require 'active_support/version' # double and triplely ensure ActiveSupport::VERSION is around
@@ -24,32 +25,54 @@ class NagiosHarder
24
25
  attr_accessor :nagios_url, :user, :password, :default_options, :default_cookies, :version, :nagios_time_format
25
26
  include HTTParty::ClassMethods
26
27
 
27
- def initialize(nagios_url, user, password, version = 3)
28
+ def initialize(nagios_url, user, password, version = 3, nagios_time_format = nil)
28
29
  @nagios_url = nagios_url.gsub(/\/$/, '')
29
30
  @user = user
30
31
  @password = password
31
32
  @default_options = {}
32
33
  @default_cookies = {}
33
34
  @version = version
35
+ debug_output if ENV['DEBUG']
34
36
  basic_auth(@user, @password) if @user && @password
35
-
36
- @nagios_time_format = if @version.to_i < 3
37
- "%m-%d-%Y %H:%M:%S"
38
- else
39
- "%Y-%m-%d %H:%M:%S"
40
- end
37
+ @nagios_time_format = if nagios_time_format == 'us'
38
+ "%m-%d-%Y %H:%M:%S"
39
+ else
40
+ if @version.to_i < 3
41
+ "%m-%d-%Y %H:%M:%S"
42
+ else
43
+ "%Y-%m-%d %H:%M:%S"
44
+ end
45
+ end
46
+ self
41
47
  end
42
48
 
43
49
  def acknowledge_service(host, service, comment)
44
- # extra options: sticky_arg, send_notification, persistent
45
-
46
50
  request = {
47
51
  :cmd_typ => 34,
48
52
  :cmd_mod => 2,
49
53
  :com_author => @user,
50
54
  :com_data => comment,
51
55
  :host => host,
52
- :service => service
56
+ :service => service,
57
+ :send_notification => true,
58
+ :persistent => false,
59
+ :sticky_ack => true
60
+ }
61
+
62
+ response = post(cmd_url, :body => request)
63
+ response.code == 200 && response.body =~ /successful/
64
+ end
65
+
66
+ def acknowledge_host(host, comment)
67
+ request = {
68
+ :cmd_typ => 33,
69
+ :cmd_mod => 2,
70
+ :com_author => @user,
71
+ :com_data => comment,
72
+ :host => host,
73
+ :send_notification => true,
74
+ :persistent => false,
75
+ :sticky_ack => true
53
76
  }
54
77
 
55
78
  response = post(cmd_url, :body => request)
@@ -63,7 +86,7 @@ class NagiosHarder
63
86
  :host => host,
64
87
  :service => service
65
88
  }
66
-
89
+
67
90
  response = post(cmd_url, :body => request)
68
91
  response.code == 200 && response.body =~ /successful/
69
92
  end
@@ -130,7 +153,7 @@ class NagiosHarder
130
153
 
131
154
  response.code == 200 && response.body =~ /successful/
132
155
  end
133
-
156
+
134
157
  def cancel_downtime(downtime_id, downtime_type = :host_downtime)
135
158
  downtime_types = {
136
159
  :host_downtime => 78,
@@ -143,7 +166,7 @@ class NagiosHarder
143
166
  })
144
167
  response.code == 200 && response.body =~ /successful/
145
168
  end
146
-
169
+
147
170
  def schedule_host_check(host)
148
171
  response = post(cmd_url, :body => {
149
172
  :start_time => formatted_time_for(Time.now),
@@ -167,65 +190,49 @@ class NagiosHarder
167
190
  response.code == 200 && response.body =~ /successful/
168
191
  end
169
192
 
170
- def service_status(type, options = {})
171
- service_status_type = case type
172
- when :ok then 2
173
- when :warning then 4
174
- when :unknown then 8
175
- when :critical then 16
176
- when :pending then 1
177
- when :all_problems then 28
178
- when :all then nil
179
- else
180
- raise "Unknown type"
181
- end
182
-
183
- sort_type = case options[:sort_type]
184
- when :asc then 1
185
- when :desc then 2
186
- when nil then nil
187
- else
188
- raise "Invalid options[:sort_type]"
189
- end
190
-
191
- sort_option = case options[:sort_option]
192
- when :host then 1
193
- when :service then 2
194
- when :status then 3
195
- when :last_check then 4
196
- when :duration then 6
197
- when :attempts then 5
198
- when nil then nil
199
- else
200
- raise "Invalid options[:sort_option]"
201
- end
202
-
203
- service_group = options[:group]
204
-
205
-
206
- params = {
207
- 'hoststatustype' => 15,
208
- 'servicestatustype' => service_status_type,
209
- 'host' => 'all'
210
- }
193
+ def service_status(options = {})
194
+ params = {}
195
+
196
+ {
197
+ :host_status_types => :notification_host,
198
+ :service_status_types => :notification_service,
199
+ :sort_type => :sort,
200
+ :sort_option => :sort,
201
+ :host_props => :host,
202
+ :service_props => :service,
203
+ }.each do |key, val|
204
+ if options[key] && (options[key].is_a?(Array) || options[key].is_a?(Symbol))
205
+ params[key.to_s.gsub(/_/, '')] = Nagiosharder::Filters.value(val, *options[key])
206
+ end
207
+ end
208
+
209
+ # if any of the standard filter params are already integers, those win
210
+ %w(
211
+ :hoststatustypes,
212
+ :servicestatustypes,
213
+ :sorttype,
214
+ :sortoption,
215
+ :hostprops,
216
+ :serviceprops,
217
+ ).each do |key|
218
+ params[key.to_s] = options[:val] if !options[:val].nil? && options[:val].match(/^\d*$/)
219
+ end
211
220
 
221
+ if @version == 3
222
+ params['servicegroup'] = options[:group] || 'all'
223
+ params['style'] = 'detail'
224
+ params['embedded'] = '1'
225
+ params['noheader'] = '1'
226
+ else
227
+ if options[:group]
228
+ params['servicegroup'] = options[:group]
229
+ params['style'] = 'detail'
230
+ else
231
+ params['host'] = 'all'
232
+ end
233
+ end
212
234
 
213
- params = if @version == 3
214
- [ "servicegroup=all", "style=detail" ]
215
- else
216
- if service_group
217
- ["servicegroup=#{service_group}", "style=detail"]
218
- else
219
- ["host=all"]
220
- end
221
- end
222
- params += [
223
- service_status_type ? "servicestatustypes=#{service_status_type}" : nil,
224
- sort_type ? "sorttype=#{sort_type}" : nil,
225
- sort_option ? "sortoption=#{sort_option}" : nil,
226
- "hoststatustypes=15"
227
- ]
228
- query = params.compact.join('&')
235
+ query = params.select {|k,v| v }.map {|k,v| "#{k}=#{v}" }.join('&')
229
236
  url = "#{status_url}?#{query}"
230
237
  response = get(url)
231
238
 
@@ -240,7 +247,7 @@ class NagiosHarder
240
247
  end
241
248
 
242
249
  def host_status(host)
243
- host_status_url = "#{status_url}?host=#{host}"
250
+ host_status_url = "#{status_url}?host=#{host}&embedded=1&noheader=1"
244
251
  response = get(host_status_url)
245
252
 
246
253
  raise "wtf #{host_status_url}? #{response.code}" unless response.code == 200
@@ -249,18 +256,24 @@ class NagiosHarder
249
256
  parse_status_html(response) do |status|
250
257
  services[status[:service]] = status
251
258
  end
252
-
259
+
253
260
  services
254
261
  end
255
262
 
256
263
  def disable_service_notifications(host, service, options = {})
257
264
  request = {
258
265
  :cmd_mod => 2,
259
- :cmd_typ => 23,
260
- :host => host,
261
- :service => service,
266
+ :host => host
262
267
  }
263
268
 
269
+ if service
270
+ request[:cmd_typ] = 23
271
+ request[:service] = service
272
+ else
273
+ request[:cmd_typ] = 29
274
+ request[:ahas] = true
275
+ end
276
+
264
277
  response = post(cmd_url, :body => request)
265
278
  if response.code == 200 && response.body =~ /successful/
266
279
  # TODO enable waiting. seems to hang intermittently
@@ -276,11 +289,17 @@ class NagiosHarder
276
289
  def enable_service_notifications(host, service, options = {})
277
290
  request = {
278
291
  :cmd_mod => 2,
279
- :cmd_typ => 22,
280
- :host => host,
281
- :service => service,
292
+ :host => host
282
293
  }
283
294
 
295
+ if service
296
+ request[:cmd_typ] = 22
297
+ request[:service] = service
298
+ else
299
+ request[:cmd_typ] = 28
300
+ request[:ahas] = true
301
+ end
302
+
284
303
  response = post(cmd_url, :body => request)
285
304
  if response.code == 200 && response.body =~ /successful/
286
305
  # TODO enable waiting. seems to hang intermittently
@@ -292,12 +311,12 @@ class NagiosHarder
292
311
  false
293
312
  end
294
313
  end
295
-
314
+
296
315
  def service_notifications_disabled?(host, service)
297
316
  self.host_status(host)[service].notifications_disabled
298
317
  end
299
-
300
-
318
+
319
+
301
320
  def status_url
302
321
  "#{nagios_url}/status.cgi"
303
322
  end
@@ -317,14 +336,15 @@ class NagiosHarder
317
336
  end
318
337
 
319
338
  def parse_status_html(response)
320
- doc = Nokogiri::HTML(response)
339
+ doc = Nokogiri::HTML(response.to_s)
321
340
  rows = doc.css('table.status > tr')
322
341
 
323
342
  last_host = nil
324
343
  rows.each do |row|
325
344
  columns = Nokogiri::HTML(row.inner_html).css('body > td').to_a
326
345
  if columns.any?
327
-
346
+
347
+ # Host column
328
348
  host = columns[0].inner_text.gsub(/\n/, '')
329
349
 
330
350
  # for a given host, the host details are blank after the first row
@@ -335,7 +355,9 @@ class NagiosHarder
335
355
  # or save it for later
336
356
  host = last_host
337
357
  end
358
+ debug 'parsed host column'
338
359
 
360
+ # Service Column
339
361
  if columns[1]
340
362
  service_links = columns[1].css('td a')
341
363
  service_link, other_links = service_links[0], service_links[1..-1]
@@ -366,13 +388,21 @@ class NagiosHarder
366
388
 
367
389
  service = service_links[0].inner_html
368
390
  end
369
-
391
+ debug 'parsed service column'
392
+
393
+ # Status
370
394
  status = columns[2].inner_html if columns[2]
395
+ debug 'parsed status column'
396
+
397
+ # Last Check
371
398
  last_check = if columns[3] && columns[3].inner_html != 'N/A'
372
399
  last_check_str = columns[3].inner_html
373
-
374
- DateTime.strptime(columns[3].inner_html, nagios_time_format).to_time
400
+ debug "Need to parse #{columns[3].inner_html} in #{nagios_time_format}"
401
+ DateTime.strptime(columns[3].inner_html, nagios_time_format).to_s
375
402
  end
403
+ debug 'parsed last check column'
404
+
405
+ # Duration
376
406
  duration = columns[4].inner_html.squeeze(' ').gsub(/^ /, '') if columns[4]
377
407
  started_at = if duration && match_data = duration.match(/^\s*(\d+)d\s+(\d+)h\s+(\d+)m\s+(\d+)s\s*$/)
378
408
  (
@@ -382,8 +412,16 @@ class NagiosHarder
382
412
  match_data[4].to_i.seconds
383
413
  ).ago
384
414
  end
415
+ debug 'parsed duration column'
416
+
417
+ # Attempts
385
418
  attempts = columns[5].inner_html if columns[5]
386
- status_info = columns[6].inner_html.gsub('&nbsp;', '') if columns[6]
419
+ debug 'parsed attempts column'
420
+
421
+ # Status info
422
+ status_info = columns[6].inner_html.gsub('&nbsp;', '').gsub("\302\240", '') if columns[6]
423
+ debug 'parsed status info column'
424
+
387
425
 
388
426
  if host && service && status && last_check && duration && attempts && started_at && status_info
389
427
  service_extinfo_url = "#{extinfo_url}?type=2&host=#{host}&service=#{CGI.escape(service)}"
@@ -412,7 +450,10 @@ class NagiosHarder
412
450
 
413
451
  nil
414
452
  end
415
-
416
- end
417
453
 
454
+ def debug(*args)
455
+ $stderr.puts *args if ENV['DEBUG']
456
+ end
457
+
458
+ end
418
459
  end