nagiosharder 0.5.0.rc1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 71649e8cef37e7a633343f8f23398b9b5d2ce993
4
+ data.tar.gz: 392dddb4fb37dae6e118f094cc75afb55e6e5e9f
5
+ SHA512:
6
+ metadata.gz: 28cde9bab1474bb38be4d8624b7d10737a921a671d8ca596aa39b03036e3f2368057c136466dd70b733d46efcfe8cfcaf79cf5971766a73ce301b29f66831d70
7
+ data.tar.gz: 48944c9bd6bc733314439726c60ff8568ea979e66df007c7be966a119d4721bf43bd5b8a4e8d353aaf1d91a7e5cfee6a0b7ccbbb80f9df41f47cc55a2e367a8e
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
@@ -1,42 +1,60 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nagiosharder (0.4.0)
4
+ nagiosharder (0.5.0.rc1)
5
5
  activesupport
6
6
  hashie (~> 1.2.0)
7
- httparty (~> 0.8.3)
7
+ httparty
8
8
  i18n
9
9
  nokogiri
10
- rest-client (~> 1.6.1)
10
+ rest-client
11
11
  terminal-table
12
12
 
13
13
  GEM
14
- remote: http://rubygems.org/
14
+ remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (3.2.8)
17
- i18n (~> 0.6)
18
- multi_json (~> 1.0)
19
- diff-lcs (1.1.3)
16
+ activesupport (4.0.0)
17
+ i18n (~> 0.6, >= 0.6.4)
18
+ minitest (~> 4.2)
19
+ multi_json (~> 1.3)
20
+ thread_safe (~> 0.1)
21
+ tzinfo (~> 0.3.37)
22
+ addressable (2.3.5)
23
+ atomic (1.1.14)
24
+ crack (0.4.1)
25
+ safe_yaml (~> 0.9.0)
26
+ diff-lcs (1.2.4)
20
27
  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)
28
+ httparty (0.12.0)
29
+ json (~> 1.8)
30
+ multi_xml (>= 0.5.2)
31
+ i18n (0.6.5)
32
+ json (1.8.1)
33
+ mime-types (2.0)
34
+ mini_portile (0.5.2)
35
+ minitest (4.7.5)
36
+ multi_json (1.8.2)
37
+ multi_xml (0.5.5)
38
+ nokogiri (1.6.0)
39
+ mini_portile (~> 0.5.0)
29
40
  rest-client (1.6.7)
30
41
  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)
42
+ rspec (2.14.1)
43
+ rspec-core (~> 2.14.0)
44
+ rspec-expectations (~> 2.14.0)
45
+ rspec-mocks (~> 2.14.0)
46
+ rspec-core (2.14.6)
47
+ rspec-expectations (2.14.3)
48
+ diff-lcs (>= 1.1.3, < 2.0)
49
+ rspec-mocks (2.14.4)
50
+ safe_yaml (0.9.7)
39
51
  terminal-table (1.4.5)
52
+ thread_safe (0.1.3)
53
+ atomic
54
+ tzinfo (0.3.38)
55
+ webmock (1.15.0)
56
+ addressable (>= 2.2.7)
57
+ crack (>= 0.3.2)
40
58
 
41
59
  PLATFORMS
42
60
  ruby
@@ -44,3 +62,4 @@ PLATFORMS
44
62
  DEPENDENCIES
45
63
  nagiosharder!
46
64
  rspec (>= 1.2.9)
65
+ webmock
@@ -0,0 +1,186 @@
1
+ # nagiosharder
2
+
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
+ Now you have access to both a Ruby interface and a command line interface.
8
+
9
+ Here are some examples to get you started with the Ruby API:
10
+
11
+ require 'nagiosharder'
12
+
13
+ cgi = 'https://path/to/nagios/cgi/directory'
14
+ user = 'user'
15
+ pass = 'pass'
16
+ version = 3
17
+ time_format = 'iso8601'
18
+ verify_ssl = true
19
+
20
+ site = NagiosHarder::Site.new(cgi, user, pass, version, time_format, verify_ssl)
21
+ # version defaults to 3
22
+ # time_format defaults to 'strict-iso8601' for version 3 and 'us' for all other versions
23
+ # verify_ssl defaults to true, pass false to override
24
+
25
+ Get details back about a host's services:
26
+
27
+ puts site.host_status('myhost')
28
+
29
+ Schedule a host to have services checks run again right now:
30
+
31
+ site.schedule_host_check('myhost')
32
+
33
+ Get details on all services:
34
+
35
+ site.service_status
36
+
37
+ Or just things with problems:
38
+
39
+ site.service_status(
40
+ :service_status_types => [
41
+ :critical,
42
+ :warning,
43
+ :unknown
44
+ ]
45
+ )
46
+
47
+ Or just muted services, sorted desc by duration:
48
+
49
+ site.service_status(
50
+ :service_props => [
51
+ :notifications_disabled,
52
+ ],
53
+ :sort_type => :descending,
54
+ :sort_option => :state_duration,
55
+ )
56
+
57
+ Or get the details for a single service group:
58
+
59
+ site.service_status(:group => "AWESOME")
60
+
61
+ Schedule a host to have services checks run again right now:
62
+
63
+ site.schedule_service_check('myhost', 'myservice')
64
+
65
+ Schedule 20 minutes of downtime, starting now:
66
+
67
+ site.schedule_host_downtime('myhost', :start_time => Time.now, :end_time => Time.now + 20.minutes)
68
+
69
+ Schedule a flexible 20 minutes of downtime between now and 2 hours from now:
70
+
71
+ site.schedule_host_downtime('myhost', :type => :flexible, :start_time => Time.now, :end_time => Time.now + 2.hours, :hours => 0, :minutes => 20)
72
+
73
+ Schedule 20 minutes of downtime for a service, starting now:
74
+
75
+ site.schedule_service_downtime('myhost', 'myservice', :start_time => Time.now, :end_time => Time.now + 20.minutes)
76
+
77
+ Cancel a scheduled host downtime:
78
+
79
+ site.cancel_downtime('downtime_id')
80
+
81
+ Cancel a scheduled service downtime:
82
+
83
+ site.cancel_downtime('downtime_id', :service_downtime)
84
+
85
+ Acknowledge a down service:
86
+
87
+ site.acknowledge_service('myhost', 'myservice', 'something bad happened')
88
+
89
+ Or unacknowledge a down service:
90
+
91
+ site.unacknowledge_service('myhost', 'myservice')
92
+
93
+ Acknowledge a down host:
94
+
95
+ site.acknowledge_host('myhost', 'something bad happened')
96
+
97
+ Or unacknowledge a down host:
98
+
99
+ site.unacknowledge_host('myhost')
100
+
101
+ Schedule next host check for right now:
102
+
103
+ site.schedule_host_check('myhost')
104
+
105
+ Schedule next service check for right now:
106
+
107
+ site.schedule_service_check('myhost', 'myservice')
108
+
109
+ Disable notifications for a service:
110
+
111
+ site.disable_service_notifications('myhost', 'myservice')
112
+
113
+ Check if notifications are disabled:
114
+
115
+ site.service_notifications_disabled?('myhost', 'myservice')
116
+
117
+ Enable notifications for a service:
118
+
119
+ site.enable_service_notifications('myhost', 'myservice')
120
+
121
+ Disable notifications, and wait for nagios to process it:
122
+
123
+ site.disable_service_notifications('myhost', 'myservice')
124
+ until site.service_notifications_disabled?('myhost', 'myservice')
125
+ sleep 3
126
+ end
127
+
128
+ Get a summary on all hostgroups:
129
+
130
+ site.hostgroups_summary
131
+
132
+ Or a summary for a specific hostgroup:
133
+
134
+ site.hostgroups_summary('myhostgroup')
135
+
136
+ Get a summary on all servicegroups:
137
+
138
+ site.servicegroups_summary
139
+
140
+ Or a summary for a specific servicegroup:
141
+
142
+ site.servicegroups_summary('myservicegroup')
143
+
144
+ Get detailed output for all hostgroups:
145
+
146
+ site.hostgroups_detail
147
+
148
+ Get detailed output for a specific hostgroup:
149
+
150
+ site.hostgroups_detail('myhostgroup')
151
+
152
+ Get alert history:
153
+
154
+ site.alert_history
155
+
156
+ Or all HARD state alerts:
157
+
158
+ site.alert_history(:state_type => :hard, :type => :all)
159
+
160
+ ---
161
+
162
+ Then there's the command line. Start with --help
163
+
164
+ nagiosharder --help
165
+
166
+ 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.
167
+
168
+ For example:
169
+
170
+ nagiosharder --config /path/to/yaml
171
+
172
+ This will display all available commands.
173
+
174
+ ---
175
+
176
+ #### Note on Patches/Pull Requests
177
+
178
+ * Fork the project.
179
+ * Make your feature addition or bug fix.
180
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
181
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
182
+ * Send me a pull request. Bonus points for topic branches.
183
+
184
+ #### Copyright
185
+
186
+ Copyright (c) 2010 Josh Nichols. See LICENSE for details.
@@ -6,4 +6,4 @@ rescue ArgumentError => e
6
6
  puts e.message
7
7
  puts e.backtrace.join("\n") if ENV['DEBUG']
8
8
  exit 1
9
- end
9
+ end
@@ -1,9 +1,12 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'restclient'
2
4
  require 'nokogiri'
3
5
  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
6
  require 'cgi'
5
7
  require 'hashie'
6
8
  require 'nagiosharder/filters'
9
+ require 'nagiosharder/commands'
7
10
 
8
11
  # :(
9
12
  require 'active_support/version' # double and triplely ensure ActiveSupport::VERSION is around
@@ -25,169 +28,192 @@ class NagiosHarder
25
28
  attr_accessor :nagios_url, :user, :password, :default_options, :default_cookies, :version, :nagios_time_format
26
29
  include HTTParty::ClassMethods
27
30
 
28
- def initialize(nagios_url, user, password, version = 3, nagios_time_format = nil)
31
+ def initialize(nagios_url, user, password, version = 3, nagios_time_format = nil, ssl_verify = true)
29
32
  @nagios_url = nagios_url.gsub(/\/$/, '')
30
33
  @user = user
31
34
  @password = password
32
- @default_options = {}
35
+ @default_options = {:verify => ssl_verify}
33
36
  @default_cookies = {}
34
37
  @version = version
35
38
  debug_output if ENV['DEBUG']
36
39
  basic_auth(@user, @password) if @user && @password
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
40
+ @nagios_time_format = case nagios_time_format
41
+ when 'us'
42
+ "%m-%d-%Y %H:%M:%S"
43
+ when 'euro'
44
+ "%d-%m-%Y %H:%M:%S"
45
+ when 'iso8601'
46
+ "%Y-%m-%d %H:%M:%S"
47
+ else
48
+ if @version.to_i == 3 # allows compatability with nagios 4
49
+ "%Y-%m-%dT%H:%M:%S"
50
+ else
51
+ "%m-%d-%Y %H:%M:%S"
52
+ end
53
+ end
46
54
  self
47
55
  end
48
56
 
49
- def acknowledge_service(host, service, comment)
57
+ def post_command(body)
58
+ # cmd_mod is always CMDMODE_COMMIT
59
+ body = {:cmd_mod => 2}.merge(body)
60
+ response = post(cmd_url, :body => body)
61
+ response.code == 200 && response.body.match(/successful/) && true
62
+ end
63
+
64
+ def acknowledge_host(host, comment)
50
65
  request = {
51
- :cmd_typ => 34,
52
- :cmd_mod => 2,
66
+ :cmd_typ => COMMANDS[:acknowledge_host_problem],
53
67
  :com_author => @user,
54
68
  :com_data => comment,
55
69
  :host => host,
56
- :service => service,
57
70
  :send_notification => true,
58
71
  :persistent => false,
59
72
  :sticky_ack => true
60
73
  }
61
74
 
62
- response = post(cmd_url, :body => request)
63
- response.code == 200 && response.body =~ /successful/
75
+ post_command(request)
64
76
  end
65
77
 
66
- def acknowledge_host(host, comment)
78
+ def unacknowledge_host(host)
67
79
  request = {
68
- :cmd_typ => 33,
69
- :cmd_mod => 2,
80
+ :cmd_typ => COMMANDS[:remove_host_acknowledgement],
81
+ :host => host
82
+ }
83
+
84
+ post_command(request)
85
+ end
86
+
87
+ def acknowledge_service(host, service, comment)
88
+ request = {
89
+ :cmd_typ => COMMANDS[:acknowledge_service_problem],
70
90
  :com_author => @user,
71
91
  :com_data => comment,
72
92
  :host => host,
93
+ :service => service,
73
94
  :send_notification => true,
74
95
  :persistent => false,
75
96
  :sticky_ack => true
76
97
  }
77
98
 
78
- response = post(cmd_url, :body => request)
79
- response.code == 200 && response.body =~ /successful/
99
+ post_command(request)
80
100
  end
81
-
101
+
82
102
  def unacknowledge_service(host, service)
83
103
  request = {
84
- :cmd_typ => 52,
85
- :cmd_mod => 2,
104
+ :cmd_typ => COMMANDS[:remove_service_acknowledgement],
86
105
  :host => host,
87
106
  :service => service
88
107
  }
89
108
 
90
- response = post(cmd_url, :body => request)
91
- response.code == 200 && response.body =~ /successful/
109
+ post_command(request)
92
110
  end
93
111
 
94
- def schedule_service_downtime(host, service, options = {})
112
+ def schedule_host_downtime(host, options = {})
113
+ options[:type] ||= :fixed
114
+
95
115
  request = {
96
- :cmd_mod => 2,
97
- :cmd_typ => 56,
116
+ :cmd_typ => COMMANDS[:schedule_host_downtime],
98
117
  :com_author => options[:author] || "#{@user} via nagiosharder",
99
118
  :com_data => options[:comment] || 'scheduled downtime by nagiosharder',
100
119
  :host => host,
101
- :service => service,
120
+ :childoptions => 0,
102
121
  :trigger => 0
103
122
  }
104
123
 
124
+ # FIXME we could use some option checking...
125
+
105
126
  request[:fixed] = case options[:type].to_sym
106
127
  when :fixed then 1
107
128
  when :flexible then 0
108
- else 1
129
+ else 1 # default to fixed
109
130
  end
110
131
 
111
-
112
- if request[:fixed] == 0
132
+ if request[:fixed] == 0
113
133
  request[:hours] = options[:hours]
114
134
  request[:minutes] = options[:minutes]
115
135
  end
116
136
 
117
- request[:start_time] = formatted_time_for(options[:start_time])
118
- request[:end_time] = formatted_time_for(options[:end_time])
137
+ request[:start_time] = formatted_time_for(options[:start_time] || Time.now)
138
+ request[:end_time] = formatted_time_for(options[:end_time] || Time.now + 1.hour)
119
139
 
120
- response = post(cmd_url, :body => request)
121
-
122
- response.code == 200 && response.body =~ /successful/
140
+ post_command(request)
123
141
  end
142
+
143
+ def schedule_service_downtime(host, service, options = {})
144
+ options[:type] ||= :fixed
124
145
 
125
- def schedule_host_downtime(host, options = {})
126
146
  request = {
127
- :cmd_mod => 2,
128
- :cmd_typ => 55,
147
+ :cmd_typ => COMMANDS[:schedule_service_downtime],
129
148
  :com_author => options[:author] || "#{@user} via nagiosharder",
130
149
  :com_data => options[:comment] || 'scheduled downtime by nagiosharder',
131
150
  :host => host,
132
- :childoptions => 0,
151
+ :service => service,
133
152
  :trigger => 0
134
153
  }
135
154
 
136
- # FIXME we could use some option checking...
137
-
138
155
  request[:fixed] = case options[:type].to_sym
139
156
  when :fixed then 1
140
157
  when :flexible then 0
141
- else 1 # default to fixed
158
+ else 1
142
159
  end
143
160
 
144
- if request[:fixed] == 0
161
+
162
+ if request[:fixed] == 0
145
163
  request[:hours] = options[:hours]
146
164
  request[:minutes] = options[:minutes]
147
165
  end
148
166
 
149
- request[:start_time] = formatted_time_for(options[:start_time])
150
- request[:end_time] = formatted_time_for(options[:end_time])
167
+ request[:start_time] = formatted_time_for(options[:start_time] || Time.now)
168
+ request[:end_time] = formatted_time_for(options[:end_time] || Time.now + 1.hour)
151
169
 
152
- response = post(cmd_url, :body => request)
153
-
154
- response.code == 200 && response.body =~ /successful/
170
+ post_command(request)
155
171
  end
156
172
 
157
173
  def cancel_downtime(downtime_id, downtime_type = :host_downtime)
158
- downtime_types = {
159
- :host_downtime => 78,
160
- :service_downtime => 79
174
+ request = {
175
+ :cmd_typ => COMMANDS["del_#{downtime_type}".to_sym],
176
+ :down_id => downtime_id
161
177
  }
162
- response = post(cmd_url, :body => {
163
- :cmd_typ => downtime_types[downtime_type],
164
- :cmd_mod => 2,
165
- :down_id => downtime_id
166
- })
167
- response.code == 200 && response.body =~ /successful/
178
+
179
+ post_command(request)
168
180
  end
169
181
 
170
182
  def schedule_host_check(host)
171
- response = post(cmd_url, :body => {
172
- :start_time => formatted_time_for(Time.now),
173
- :host => host,
174
- :force_check => true,
175
- :cmd_typ => 96,
176
- :cmd_mod => 2
177
- })
178
- response.code == 200 && response.body =~ /successful/
183
+ request = {
184
+ :start_time => formatted_time_for(Time.now),
185
+ :host => host,
186
+ :force_check => true,
187
+ :cmd_typ => COMMANDS[:schedule_host_check],
188
+ }
189
+
190
+ post_command(request)
179
191
  end
180
192
 
181
193
  def schedule_service_check(host, service)
182
- response = post(cmd_url, :body => {
183
- :start_time => formatted_time_for(Time.now),
184
- :host => host,
185
- :service => service,
186
- :force_check => true,
187
- :cmd_typ => 7,
188
- :cmd_mod => 2
189
- })
190
- response.code == 200 && response.body =~ /successful/
194
+ request = {
195
+ :start_time => formatted_time_for(Time.now),
196
+ :host => host,
197
+ :service => service,
198
+ :force_check => true,
199
+ :cmd_typ => COMMANDS[:schedule_service_check],
200
+ }
201
+
202
+ post_command(request)
203
+ end
204
+
205
+ def host_status(host)
206
+ host_status_url = "#{status_url}?host=#{host}&embedded=1&noheader=1&limit=0"
207
+ response = get(host_status_url)
208
+
209
+ raise "wtf #{host_status_url}? #{response.code}" unless response.code == 200
210
+
211
+ services = {}
212
+ parse_status_html(response) do |status|
213
+ services[status[:service]] = status
214
+ end
215
+
216
+ services
191
217
  end
192
218
 
193
219
  def service_status(options = {})
@@ -215,7 +241,7 @@ class NagiosHarder
215
241
  :hostprops,
216
242
  :serviceprops,
217
243
  ).each do |key|
218
- params[key.to_s] = options[:val] if !options[:val].nil? && options[:val].match(/^\d*$/)
244
+ params[key.to_s] = options[:val] if options[:val] && options[:val].match(/^\d*$/)
219
245
  end
220
246
 
221
247
  if @version == 3
@@ -223,6 +249,7 @@ class NagiosHarder
223
249
  params['style'] = 'detail'
224
250
  params['embedded'] = '1'
225
251
  params['noheader'] = '1'
252
+ params['limit'] = 0
226
253
  else
227
254
  if options[:group]
228
255
  params['servicegroup'] = options[:group]
@@ -246,76 +273,124 @@ class NagiosHarder
246
273
  statuses
247
274
  end
248
275
 
249
- def host_status(host)
250
- host_status_url = "#{status_url}?host=#{host}&embedded=1&noheader=1"
251
- response = get(host_status_url)
276
+ def hostgroups_summary(hostgroup = "all")
277
+ hostgroups_summary_url = "#{status_url}?hostgroup=#{hostgroup}&style=summary"
278
+ response = get(hostgroups_summary_url)
252
279
 
253
- raise "wtf #{host_status_url}? #{response.code}" unless response.code == 200
280
+ raise "wtf #{hostgroups_summary_url}? #{response.code}" unless response.code == 200
254
281
 
255
- services = {}
256
- parse_status_html(response) do |status|
257
- services[status[:service]] = status
282
+ hostgroups = {}
283
+ parse_summary_html(response) do |status|
284
+ hostgroups[status[:group]] = status
258
285
  end
259
286
 
260
- services
287
+ hostgroups
288
+ end
289
+
290
+ def servicegroups_summary(servicegroup = "all")
291
+ servicegroups_summary_url = "#{status_url}?servicegroup=#{servicegroup}&style=summary"
292
+ response = get(servicegroups_summary_url)
293
+
294
+ raise "wtf #{servicegroups_summary_url}? #{response.code}" unless response.code == 200
295
+
296
+ servicegroups = {}
297
+ parse_summary_html(response) do |status|
298
+ servicegroups[status[:group]] = status
299
+ end
300
+
301
+ servicegroups
302
+ end
303
+
304
+ def hostgroups_detail(hostgroup = "all")
305
+ hostgroups_detail_url = "#{status_url}?hostgroup=#{hostgroup}&style=hostdetail&embedded=1&noheader=1&limit=0"
306
+ response = get(hostgroups_detail_url)
307
+
308
+ raise "wtf #{hostgroups_detail_url}? #{response.code}" unless response.code == 200
309
+
310
+ hosts = {}
311
+ parse_detail_html(response) do |status|
312
+ hosts[status[:host]] = status
313
+ end
314
+
315
+ hosts
261
316
  end
262
317
 
263
318
  def disable_service_notifications(host, service, options = {})
264
319
  request = {
265
- :cmd_mod => 2,
266
320
  :host => host
267
321
  }
268
322
 
269
323
  if service
270
- request[:cmd_typ] = 23
324
+ request[:cmd_typ] = COMMANDS[:disable_service_notifications]
271
325
  request[:service] = service
272
326
  else
273
- request[:cmd_typ] = 29
327
+ request[:cmd_typ] = COMMANDS[:disable_host_service_checks]
274
328
  request[:ahas] = true
275
329
  end
276
330
 
277
- response = post(cmd_url, :body => request)
278
- if response.code == 200 && response.body =~ /successful/
279
- # TODO enable waiting. seems to hang intermittently
280
- #if options[:wait]
281
- # sleep(3) until service_notifications_disabled?(host, service)
282
- #end
283
- true
284
- else
285
- false
286
- end
331
+ # TODO add option to block until the service shows as disabled
332
+ post_command(request)
287
333
  end
288
334
 
289
335
  def enable_service_notifications(host, service, options = {})
290
336
  request = {
291
- :cmd_mod => 2,
292
337
  :host => host
293
338
  }
294
339
 
295
340
  if service
296
- request[:cmd_typ] = 22
341
+ request[:cmd_typ] = COMMANDS[:enable_service_notifications]
297
342
  request[:service] = service
298
343
  else
299
- request[:cmd_typ] = 28
344
+ request[:cmd_typ] = COMMANDS[:enable_host_service_notifications]
300
345
  request[:ahas] = true
301
346
  end
302
347
 
303
- response = post(cmd_url, :body => request)
304
- if response.code == 200 && response.body =~ /successful/
305
- # TODO enable waiting. seems to hang intermittently
306
- #if options[:wait]
307
- # sleep(3) while service_notifications_disabled?(host, service)
308
- #end
309
- true
310
- else
311
- false
312
- end
348
+ # TODO add option to block until the service shows as disabled
349
+ post_command(request)
313
350
  end
314
351
 
315
352
  def service_notifications_disabled?(host, service)
316
353
  self.host_status(host)[service].notifications_disabled
317
354
  end
355
+
356
+ def alert_history(options = {})
357
+ params = {}
358
+
359
+ {
360
+ :state_type => :history_state,
361
+ :type => :history
362
+ }.each do |key, val|
363
+ if options[key] && (options[key].is_a?(Array) || options[key].is_a?(Symbol))
364
+ params[key.to_s.gsub(/_/, '')] = Nagiosharder::Filters.value(val, *options[key])
365
+ end
366
+ end
367
+
368
+ # if any of the standard filter params are already integers, those win
369
+ %w(
370
+ :statetype,
371
+ :type
372
+ ).each do |key|
373
+ params[key.to_s] = options[:val] if !options[:val].nil? && options[:val].match(/^\d*$/)
374
+ end
375
+
376
+ params['host'] = options[:host] || 'all'
377
+ params['archive'] = options[:archive] || '0'
378
+
379
+ query = params.select {|k,v| v }.map {|k,v| "#{k}=#{v}" }.join('&')
380
+
381
+ alert_history_url = "#{history_url}?#{query}"
382
+ puts alert_history_url
383
+ response = get(alert_history_url)
318
384
 
385
+ raise "wtf #{alert_history_url}? #{response.code}" unless response.code == 200
386
+
387
+ alerts = []
388
+ parse_history_html(response) do |alert|
389
+ alerts << alert
390
+ end
391
+
392
+ alerts
393
+ end
319
394
 
320
395
  def status_url
321
396
  "#{nagios_url}/status.cgi"
@@ -328,6 +403,10 @@ class NagiosHarder
328
403
  def extinfo_url
329
404
  "#{nagios_url}/extinfo.cgi"
330
405
  end
406
+
407
+ def history_url
408
+ "#{nagios_url}/history.cgi"
409
+ end
331
410
 
332
411
  private
333
412
 
@@ -335,10 +414,112 @@ class NagiosHarder
335
414
  time.strftime(nagios_time_format)
336
415
  end
337
416
 
338
- def parse_status_html(response)
417
+ def parse_summary_html(response)
339
418
  doc = Nokogiri::HTML(response.to_s)
340
419
  rows = doc.css('table.status > tr')
341
420
 
421
+ rows.each do |row|
422
+ columns = Nokogiri::HTML(row.inner_html).css('body > td').to_a
423
+ if columns.any?
424
+
425
+ # Group column
426
+ group = columns[0].inner_text.gsub(/\n/, '').match(/\((.*?)\)/)[1]
427
+ end
428
+
429
+ if group
430
+ host_status_url, host_status_counts = parse_host_status_summary(columns[1]) if columns[1]
431
+ service_status_url, service_status_counts = parse_service_status_summary(columns[2]) if columns[2]
432
+
433
+ status = Hashie::Mash.new :group => group,
434
+ :host_status_url => host_status_url,
435
+ :host_status_counts => host_status_counts,
436
+ :service_status_url => service_status_url,
437
+ :service_status_counts => service_status_counts
438
+
439
+ yield status
440
+ end
441
+ end
442
+ end
443
+
444
+ def parse_host_status_summary(column)
445
+ text = column.css('td a')[0]
446
+ link = text['href'] rescue nil
447
+ counts = {}
448
+ counts['up'] = column.inner_text.match(/(\d+)\s(UP)/)[1] rescue 0
449
+ counts['down'] = column.inner_text.match(/(\d+)\s(DOWN)/)[1] rescue 0
450
+ return link, counts
451
+ end
452
+
453
+ def parse_service_status_summary(column)
454
+ text = column.css('td a')[0]
455
+ link = text['href'] rescue nil
456
+ counts = {}
457
+ counts['ok'] = column.inner_text.match(/(\d+)\s(OK)/)[1] rescue 0
458
+ counts['warning'] = column.inner_text.match(/(\d+)\s(WARNING)/)[1] rescue 0
459
+ counts['critical'] = column.inner_text.match(/(\d+)\s(CRITICAL)/)[1] rescue 0
460
+ counts['unknown'] = column.inner_text.match(/(\d+)\s(UNKNOWN)/)[1] rescue 0
461
+ return link, counts
462
+ end
463
+
464
+ def parse_detail_html(response)
465
+ doc = Nokogiri::HTML(response.to_s)
466
+ rows = doc.css('table.status > tr')
467
+
468
+ rows.each do |row|
469
+ columns = Nokogiri::HTML(row.inner_html).css('body > td').to_a
470
+ if columns.any?
471
+
472
+ # Host column
473
+ host = columns[0].css('a').text.strip
474
+
475
+ # Status
476
+ status = columns[1].inner_html if columns[1]
477
+
478
+ # Last Check
479
+ last_check = if columns[2] && columns[2].inner_html != 'N/A'
480
+ last_check_str = columns[2].inner_html
481
+ debug "Need to parse #{columns[2].inner_html} in #{nagios_time_format}"
482
+ DateTime.strptime(columns[2].inner_html, nagios_time_format).to_s
483
+ end
484
+ debug 'parsed last check column'
485
+
486
+ # Duration
487
+ duration = columns[3].inner_html.squeeze(' ').gsub(/^ /, '') if columns[3]
488
+ started_at = if duration && match_data = duration.match(/^\s*(\d+)d\s+(\d+)h\s+(\d+)m\s+(\d+)s\s*$/)
489
+ (
490
+ match_data[1].to_i.days +
491
+ match_data[2].to_i.hours +
492
+ match_data[3].to_i.minutes +
493
+ match_data[4].to_i.seconds
494
+ ).ago
495
+ end
496
+ debug 'parsed duration column'
497
+
498
+ # Status info
499
+ status_info = columns[4].inner_html.gsub('&nbsp;', '').gsub("\302\240", '').gsub("&#160;", '') if columns[4]
500
+ debug 'parsed status info column'
501
+
502
+ if host && status && last_check && duration && started_at && status_info
503
+ host_extinfo_url = "#{extinfo_url}?type=1&host=#{host}"
504
+
505
+ status = Hashie::Mash.new :host => host,
506
+ :status => status,
507
+ :last_check => last_check,
508
+ :duration => duration,
509
+ :started_at => started_at,
510
+ :extended_info => status_info,
511
+ :host_extinfo_url => host_extinfo_url
512
+
513
+ yield status
514
+ end
515
+ end
516
+ end
517
+ end
518
+
519
+ def parse_status_html(response)
520
+ doc = Nokogiri::HTML(response.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '').to_s)
521
+ rows = doc.css('table.status > tr')
522
+
342
523
  last_host = nil
343
524
  rows.each do |row|
344
525
  columns = Nokogiri::HTML(row.inner_html).css('body > td').to_a
@@ -374,10 +555,15 @@ class NagiosHarder
374
555
  acknowledged = other_links.any? do |link|
375
556
  link.css('img').attribute('src').to_s =~ /ack\.gif/
376
557
  end
558
+
377
559
  notifications_disabled = other_links.any? do |link|
378
560
  link.css('img').attribute('src').to_s =~ /ndisabled\.gif/
379
561
  end
380
562
 
563
+ downtime = other_links.any? do |link|
564
+ link.css('img').attribute('src').to_s =~ /downtime\.gif/
565
+ end
566
+
381
567
  extra_service_notes_link = other_links.detect do |link|
382
568
  link.css('img').any? do |img|
383
569
  img.attribute('src').to_s =~ /notes\.gif/
@@ -441,7 +627,8 @@ class NagiosHarder
441
627
  :flapping => flapping,
442
628
  :comments_url => comments_url,
443
629
  :extra_service_notes_url => extra_service_notes_url,
444
- :notifications_disabled => notifications_disabled
630
+ :notifications_disabled => notifications_disabled,
631
+ :downtime => downtime
445
632
 
446
633
  yield status
447
634
  end
@@ -450,6 +637,44 @@ class NagiosHarder
450
637
 
451
638
  nil
452
639
  end
640
+
641
+ def parse_history_html(response)
642
+ doc = Nokogiri::HTML(response.to_s)
643
+ alerts = doc.css('div.logEntries img')
644
+
645
+ if alerts.any?
646
+ alerts.each do |row|
647
+ text = row.next.text.gsub(' ',';').split(/;|: /) unless row.next.text.nil?
648
+ # differentiate host vs service alert output in nagios.log
649
+ case
650
+ when text.length >= 8 # service alert
651
+ last_check, alert_type, host, service, status, state, attempt, *extended_info = row.next.text.gsub(' ',';').split(/;|: /)
652
+ when text.length == 7 # host alert
653
+ last_check, alert_type, host, status, state, attempt, *extended_info = row.next.text.gsub(' ',';').split(/;|: /)
654
+ when text.length == 6 # service flapping alert
655
+ last_check, alert_type, host, service, status, state, *extended_info = row.next.text.gsub(' ',';').split(/;|: /)
656
+ when text.length == 5 # scheduled host downtime
657
+ last_check, alert_type, host, status, *extended_info = row.next.text.gsub(' ',';').split(/;|: /)
658
+ end
659
+
660
+ service_extinfo_url = service ? "#{extinfo_url}?type=2&host=#{host}&service=#{CGI.escape(service)}" : nil
661
+ host_extinfo_url = "#{extinfo_url}?type=1&host=#{host}"
662
+
663
+ alert = Hashie::Mash.new :last_check => last_check.gsub('[','').gsub(']',''),
664
+ :alert_type => alert_type,
665
+ :host => host,
666
+ :service => service,
667
+ :status => status,
668
+ :state => state,
669
+ :attempt => attempt,
670
+ :extended_info => extended_info.nil? ? extended_info : extended_info.join(': ').strip,
671
+ :host_extinfo_url => host_extinfo_url,
672
+ :service_extinfo_url => service_extinfo_url
673
+
674
+ yield alert
675
+ end
676
+ end
677
+ end
453
678
 
454
679
  def debug(*args)
455
680
  $stderr.puts *args if ENV['DEBUG']