nagiosharder 0.3.0 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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