icinga2 0.9.0.1 → 0.9.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,431 @@
1
+
2
+ require_relative 'version'
3
+ require_relative 'network'
4
+ require_relative 'statistics'
5
+ require_relative 'converts'
6
+ require_relative 'tools'
7
+ require_relative 'downtimes'
8
+ require_relative 'notifications'
9
+ require_relative 'hosts'
10
+ require_relative 'hostgroups'
11
+ require_relative 'services'
12
+ require_relative 'servicegroups'
13
+ require_relative 'users'
14
+ require_relative 'usergroups'
15
+
16
+ # -------------------------------------------------------------------------------------------------------------------
17
+ #
18
+ # @abstract # Namespace for classes and modules that handle all Icinga2 API calls
19
+ #
20
+ # @author Bodo Schulz <bodo@boone-schulz.de>
21
+ #
22
+ #
23
+ module Icinga2
24
+
25
+ # static variable for hosts down
26
+ HOSTS_DOWN = 1
27
+ # static variable for hosts critical
28
+ HOSTS_CRITICAL = 2
29
+ # static variable for hosts unknown
30
+ HOSTS_UNKNOWN = 3
31
+
32
+ # static variables for handled warning
33
+ SERVICE_STATE_WARNING = 1
34
+ # static variables for handled critical
35
+ SERVICE_STATE_CRITICAL = 2
36
+ # static variables for handled unknown
37
+ SERVICE_STATE_UNKNOWN = 3
38
+
39
+ # Abstract base class for the API calls.
40
+ # Provides some helper methods
41
+ #
42
+ # @author Bodo Schulz
43
+ #
44
+ class Client
45
+
46
+ include Logging
47
+
48
+ include Icinga2::Network
49
+ include Icinga2::Statistics
50
+ include Icinga2::Converts
51
+ include Icinga2::Tools
52
+ include Icinga2::Downtimes
53
+ include Icinga2::Notifications
54
+ include Icinga2::Hosts
55
+ include Icinga2::Hostgroups
56
+ include Icinga2::Services
57
+ include Icinga2::Servicegroups
58
+ include Icinga2::Users
59
+ include Icinga2::Usergroups
60
+
61
+ # Returns a new instance of Client
62
+ #
63
+ # @param [Hash, #read] settings the settings for Icinga2
64
+ # @option settings [String] :host ('localhost') the Icinga2 Hostname
65
+ # @option settings [Integer] :port (5665) the Icinga2 API Port
66
+ # @option settings [String] :user the Icinga2 API User
67
+ # @option settings [String] :password the Icinga2 API Password
68
+ # @option settings [Integer] :version (1) the Icinga2 API Version
69
+ # @option settings [Bool] :cluster Icinga2 Cluster Mode
70
+ # @option settings [Bool] :notifications (false) enable Icinga2 Host Notifications
71
+ #
72
+ # @example to create an new Instance
73
+ # config = {
74
+ # icinga: {
75
+ # host: '192.168.33.5',
76
+ # api: {
77
+ # port: 5665,
78
+ # user: 'root',
79
+ # password: 'icinga',
80
+ # version: 1
81
+ # },
82
+ # cluster: false,
83
+ # satellite: true
84
+ # }
85
+ # }
86
+ #
87
+ # @icinga = Icinga2::Client.new(config)
88
+ #
89
+ # @return [instance, #read]
90
+ #
91
+ def initialize( settings )
92
+
93
+ raise ArgumentError.new('only Hash are allowed') unless( settings.is_a?(Hash) )
94
+ raise ArgumentError.new('missing settings') if( settings.size.zero? )
95
+
96
+ icinga_host = settings.dig(:icinga, :host) || 'localhost'
97
+ icinga_api_port = settings.dig(:icinga, :api, :port) || 5665
98
+ icinga_api_user = settings.dig(:icinga, :api, :user)
99
+ icinga_api_pass = settings.dig(:icinga, :api, :password)
100
+ icinga_api_version = settings.dig(:icinga, :api, :version) || 1
101
+ icinga_api_pki_path = settings.dig(:icinga, :api, :pki_path)
102
+ icinga_api_node_name = settings.dig(:icinga, :api, :node_name)
103
+ @icinga_cluster = settings.dig(:icinga, :cluster) || false
104
+ @icinga_satellite = settings.dig(:icinga, :satellite)
105
+ @icinga_notifications = settings.dig(:icinga, :notifications) || false
106
+
107
+ @last_call_timeout = 320
108
+ @last_cib_data_called = 0
109
+ @last_status_data_called = 0
110
+ @last_application_data_called = 0
111
+ @last_service_objects_called = 0
112
+ @last_host_objects_called = 0
113
+
114
+ @icinga_api_url_base = format( 'https://%s:%d/v%s', icinga_host, icinga_api_port, icinga_api_version )
115
+
116
+ _has_cert, @options = cert?(
117
+ pki_path: icinga_api_pki_path,
118
+ node_name: icinga_api_node_name,
119
+ user: icinga_api_user,
120
+ password: icinga_api_pass
121
+ )
122
+
123
+ @headers = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
124
+
125
+ self
126
+ end
127
+
128
+ # create a HTTP Header based on a Icinga2 Certificate or an User API Login
129
+ #
130
+ # @param [Hash, #read] params
131
+ # @option params [String] :pki_path the location of the Certificate Files
132
+ # @option params [String] :node_name the Icinga2 Hostname
133
+ # @option params [String] :user the Icinga2 API User
134
+ # @option params [String] :password the Icinga2 API Password
135
+ #
136
+ # @example with Certificate
137
+ # @icinga.cert?(pki_path: '/etc/icinga2', node_name: 'icinga2-dashing')
138
+ #
139
+ # @example with User
140
+ # @icinga.cert?(user: 'root', password: 'icinga')
141
+ #
142
+ # @return [Bool, #read]
143
+ #
144
+ def cert?( params )
145
+
146
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
147
+ raise ArgumentError.new('missing params') if( params.size.zero? )
148
+
149
+ pki_path = params.dig(:pki_path)
150
+ node_name = params.dig(:node_name)
151
+ user = params.dig(:user)
152
+ password = params.dig(:password)
153
+
154
+ if( node_name.nil? )
155
+ begin
156
+ node_name = Socket.gethostbyname(Socket.gethostname).first
157
+ logger.debug(format('node name: %s', node_name))
158
+ rescue SocketError => e
159
+
160
+ raise format("can't resolve hostname (%s)", e)
161
+ end
162
+ end
163
+
164
+ ssl_cert_file = format( '%s/%s.crt', pki_path, node_name )
165
+ ssl_key_file = format( '%s/%s.key', pki_path, node_name )
166
+ ssl_ca_file = format( '%s/ca.crt', pki_path )
167
+
168
+ if( File.file?( ssl_cert_file ) && File.file?( ssl_key_file ) && File.file?( ssl_ca_file ) )
169
+
170
+ logger.debug( 'PKI found, using client certificates for connection to Icinga 2 API' )
171
+
172
+ ssl_cert_file = File.read( ssl_cert_file )
173
+ ssl_key_file = File.read( ssl_key_file )
174
+ ssl_ca_file = File.read( ssl_ca_file )
175
+
176
+ cert = OpenSSL::X509::Certificate.new( ssl_cert_file )
177
+ key = OpenSSL::PKey::RSA.new( ssl_key_file )
178
+
179
+ [true, {
180
+ ssl_client_cert: cert,
181
+ ssl_client_key: key,
182
+ ssl_ca_file: ssl_ca_file,
183
+ verify_ssl: OpenSSL::SSL::VERIFY_NONE
184
+ } ]
185
+
186
+ else
187
+
188
+ logger.debug( 'PKI not found, using basic auth for connection to Icinga 2 API' )
189
+
190
+ raise ArgumentError.new('Missing user_name') if( user.nil? )
191
+ raise ArgumentError.new('Missing password') if( password.nil? )
192
+
193
+ [false, {
194
+ user: user,
195
+ password: password,
196
+ verify_ssl: OpenSSL::SSL::VERIFY_NONE
197
+ } ]
198
+ end
199
+
200
+ end
201
+
202
+ # return Icinga2 Application data
203
+ #
204
+ # @example
205
+ # @icinga.application_data
206
+ #
207
+ # @return [Hash]
208
+ #
209
+ def application_data
210
+
211
+ data = icinga_application_data(
212
+ url: format( '%s/status/IcingaApplication', @icinga_api_url_base ),
213
+ headers: @headers,
214
+ options: @options
215
+ )
216
+
217
+ #puts '---'
218
+ #puts data
219
+ #puts data.class.to_s
220
+ #puts '---'
221
+
222
+ return nil if( data.nil? )
223
+ # return data if( data.is_a?(Hash) && data.dig('code').to_i != 200 )
224
+ #puts '.'
225
+
226
+ app_data = data.dig('icingaapplication','app')
227
+
228
+ #puts '---'
229
+ #puts app_data
230
+ #puts app_data.class.to_s
231
+ #puts '---'
232
+
233
+ # version and revision
234
+ @version, @revision = parse_version(app_data.dig('version'))
235
+
236
+ # - node_name
237
+ @node_name = app_data.dig('node_name')
238
+
239
+ # - start_time
240
+ @start_time = Time.at(app_data.dig('program_start').to_f)
241
+
242
+ data
243
+ end
244
+
245
+ # return Icinga2 CIB
246
+ #
247
+ # @example
248
+ # @icinga.cib_data
249
+ #
250
+ # @return [Hash]
251
+ #
252
+ def cib_data
253
+
254
+ data = icinga_application_data(
255
+ url: format( '%s/status/CIB', @icinga_api_url_base ),
256
+ headers: @headers,
257
+ options: @options
258
+ )
259
+
260
+ return nil if( data.nil? )
261
+
262
+ @last_cib_data_called = 0 #Time.now.to_i
263
+
264
+ if( data.is_a?(Hash))
265
+
266
+ cib_data = data.clone
267
+
268
+ # extract
269
+ # - uptime
270
+ uptime = cib_data.dig('uptime').round(2)
271
+ @uptime = Time.at(uptime).utc.strftime('%H:%M:%S')
272
+ # - avg_latency / avg_execution_time
273
+ @avg_latency = cib_data.dig('avg_latency').round(2)
274
+ @avg_execution_time = cib_data.dig('avg_execution_time').round(2)
275
+
276
+ # - hosts
277
+ @hosts_up = cib_data.dig('num_hosts_up').to_i
278
+ @hosts_down = cib_data.dig('num_hosts_down').to_i
279
+ @hosts_pending = cib_data.dig('num_hosts_pending').to_i
280
+ @hosts_unreachable = cib_data.dig('num_hosts_unreachable').to_i
281
+ @hosts_in_downtime = cib_data.dig('num_hosts_in_downtime').to_i
282
+ @hosts_acknowledged = cib_data.dig('num_hosts_acknowledged').to_i
283
+
284
+ # - services
285
+ @services_ok = cib_data.dig('num_services_ok').to_i
286
+ @services_warning = cib_data.dig('num_services_warning').to_i
287
+ @services_critical = cib_data.dig('num_services_critical').to_i
288
+ @services_unknown = cib_data.dig('num_services_unknown').to_i
289
+ @services_pending = cib_data.dig('num_services_pending').to_i
290
+ @services_in_downtime = cib_data.dig('num_services_in_downtime').to_i
291
+ @services_acknowledged = cib_data.dig('num_services_acknowledged').to_i
292
+
293
+ # - check stats
294
+ @hosts_active_checks_1min = cib_data.dig('active_host_checks_1min')
295
+ @hosts_passive_checks_1min = cib_data.dig('passive_host_checks_1min')
296
+ @services_active_checks_1min = cib_data.dig('active_service_checks_1min')
297
+ @services_passive_checks_1min = cib_data.dig('passive_service_checks_1min')
298
+
299
+ end
300
+
301
+ data
302
+ end
303
+
304
+ # return Icinga2 Status Data
305
+ #
306
+ # @example
307
+ # @icinga.status_data
308
+ #
309
+ # @return [Hash]
310
+ #
311
+ def status_data
312
+
313
+ @last_status_data_called = Time.now.to_i
314
+
315
+ icinga_application_data(
316
+ url: format( '%s/status', @icinga_api_url_base ),
317
+ headers: @headers,
318
+ options: @options
319
+ )
320
+ end
321
+
322
+ # return Icinga2 API Listener
323
+ #
324
+ # @example
325
+ # @icinga.api_listener
326
+ #
327
+ # @return [Hash]
328
+ #
329
+ def api_listener
330
+
331
+ @last_application_data_called = Time.now.to_i
332
+
333
+ icinga_application_data(
334
+ url: format( '%s/status/ApiListener', @icinga_api_url_base ),
335
+ headers: @headers,
336
+ options: @options
337
+ )
338
+ end
339
+
340
+ # check the availability of a Icinga network connect
341
+ #
342
+ # @example
343
+ # @icinga.available?
344
+ #
345
+ # @return [Bool]
346
+ #
347
+ def available?
348
+
349
+ data = application_data
350
+
351
+ return true unless( data.nil? )
352
+
353
+ false
354
+ end
355
+
356
+ # return Icinga2 version and revision
357
+ #
358
+ # @example
359
+ # @icinga.version.values
360
+ #
361
+ # v = @icinga.version
362
+ # version = v.dig(:version)
363
+ #
364
+ # @return [Hash]
365
+ # * version
366
+ # * revision
367
+ #
368
+ def version
369
+
370
+ application_data if((Time.now.to_i - @last_application_data_called).to_i > @last_call_timeout)
371
+
372
+ version = @version.nil? ? 0 : @version
373
+ revision = @revision.nil? ? 0 : @revision
374
+
375
+ {
376
+ version: version.to_s,
377
+ revision: revision.to_s
378
+ }
379
+ end
380
+
381
+ # return Icinga2 node_name
382
+ #
383
+ # @example
384
+ # @icinga.node_name
385
+ #
386
+ # @return [String]
387
+ #
388
+ def node_name
389
+
390
+ application_data if((Time.now.to_i - @last_application_data_called).to_i > @last_call_timeout)
391
+
392
+ return @node_name if( @node_name )
393
+
394
+ nil
395
+ end
396
+
397
+ # return Icinga2 start time
398
+ #
399
+ # @example
400
+ # @icinga.start_time
401
+ #
402
+ # @return [String]
403
+ #
404
+ def start_time
405
+
406
+ application_data if((Time.now.to_i - @last_application_data_called).to_i > @last_call_timeout)
407
+
408
+ return @start_time if( @start_time )
409
+
410
+ nil
411
+ end
412
+
413
+ # return Icinga2 uptime
414
+ #
415
+ # @example
416
+ # @icinga.cib_data
417
+ # @icinga.uptime
418
+ #
419
+ # @return [String]
420
+ #
421
+ def uptime
422
+
423
+ cib_data if((Time.now.to_i - @last_cib_data_called).to_i > @last_call_timeout)
424
+
425
+ return @uptime if( @uptime )
426
+
427
+ nil
428
+ end
429
+
430
+ end
431
+ end
@@ -10,7 +10,7 @@ module Icinga2
10
10
  #
11
11
  # @param [Hash] params
12
12
  # @option params [String] :name
13
- # @option params [String] :host
13
+ # @option params [String] :host_name
14
14
  # @option params [String] :host_group
15
15
  # @option params [Integer] :start_time (Time.new.to_i)
16
16
  # @option params [Integer] :end_time
@@ -22,7 +22,7 @@ module Icinga2
22
22
  # param = {
23
23
  # name: 'test',
24
24
  # type: 'service',
25
- # host: 'icinga2',
25
+ # host_name: 'icinga2',
26
26
  # comment: 'test downtime',
27
27
  # author: 'icingaadmin',
28
28
  # start_time: Time.now.to_i,
@@ -34,17 +34,24 @@ module Icinga2
34
34
  #
35
35
  def add_downtime( params )
36
36
 
37
- raise ArgumentError.new('only Hash are allowed') unless( params.is_a?(Hash) )
38
- raise ArgumentError.new('missing params') if( params.size.zero? )
37
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
38
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
39
39
 
40
40
  name = params.dig(:name)
41
- host_name = params.dig(:host)
41
+ host_name = params.dig(:host_name)
42
42
  host_group = params.dig(:host_group)
43
43
  start_time = params.dig(:start_time) || Time.now.to_i
44
44
  end_time = params.dig(:end_time)
45
45
  author = params.dig(:author)
46
46
  comment = params.dig(:comment)
47
47
  type = params.dig(:type)
48
+ fixed = params.dig(:fixed) || true
49
+ duration = params.dig(:duration) || 30
50
+ entry_time = params.dig(:entry_time)
51
+ scheduled_by = params.dig(:scheduled_by)
52
+ service_name = params.dig(:service_name)
53
+ triggered_by = params.dig(:triggered_by)
54
+ config_owner = params.dig(:config_owner)
48
55
  filter = nil
49
56
 
50
57
  # sanitychecks
@@ -53,11 +60,18 @@ module Icinga2
53
60
  raise ArgumentError.new("wrong downtype type. only 'host' or' service' allowed ('#{type}' giving)") if( %w[host service].include?(type.downcase) == false )
54
61
  raise ArgumentError.new('choose host or host_group, not both') if( !host_group.nil? && !host_name.nil? )
55
62
  raise ArgumentError.new('Missing downtime author') if( author.nil? )
56
- raise ArgumentError.new("these author ar not exists: #{author}") unless( exists_user?( author ) )
63
+ raise ArgumentError.new("these author are not exists: #{author}") unless( exists_user?( author ) )
57
64
  raise ArgumentError.new('Missing downtime comment') if( comment.nil? )
58
65
  raise ArgumentError.new('Missing downtime end_time') if( end_time.nil? )
59
66
  raise ArgumentError.new('end_time are equal or smaller then start_time') if( end_time.to_i <= start_time )
60
67
 
68
+ raise ArgumentError.new(format('wrong type. \'duration\' must be Integer, given \'%s\'', duration.class.to_s)) unless( duration.is_a?(Integer) )
69
+ raise ArgumentError.new(format('wrong type. \'fixed\' must be True or False, given \'%s\'', fixed.class.to_s)) unless( fixed.is_a?(TrueClass) || fixed.is_a?(FalseClass) )
70
+
71
+ # TODO
72
+ # check if host_name exists!
73
+
74
+
61
75
  if( !host_name.nil? )
62
76
 
63
77
  filter = format( 'host.name=="%s"', host_name )
@@ -69,17 +83,27 @@ module Icinga2
69
83
  end
70
84
 
71
85
  payload = {
72
- 'type' => type.capitalize, # we need the first char as Uppercase
73
- 'start_time' => start_time,
74
- 'end_time' => end_time,
75
- 'author' => author,
76
- 'comment' => comment,
77
- 'fixed' => true,
78
- 'duration' => 30,
79
- 'filter' => filter
86
+ type: type.capitalize, # we need the first char as Uppercase
87
+ start_time: start_time,
88
+ end_time: end_time,
89
+ author: author,
90
+ comment: comment,
91
+ fixed: fixed,
92
+ duration: duration,
93
+ filter: filter,
94
+ entry_time: entry_time,
95
+ scheduled_by: scheduled_by,
96
+ host_name: host_name,
97
+ host_group: host_group,
98
+ service_name: service_name,
99
+ triggered_by: triggered_by,
100
+ config_owner: config_owner
80
101
  }
81
102
 
82
- Network.post(
103
+ # remove all empty attrs
104
+ payload.reject!{ |_k, v| v.nil? }
105
+
106
+ post(
83
107
  url: format( '%s/actions/schedule-downtime', @icinga_api_url_base ),
84
108
  headers: @headers,
85
109
  options: @options,
@@ -96,15 +120,11 @@ module Icinga2
96
120
  #
97
121
  def downtimes
98
122
 
99
- data = Network.api_data(
123
+ api_data(
100
124
  url: format( '%s/objects/downtimes' , @icinga_api_url_base ),
101
125
  headers: @headers,
102
126
  options: @options
103
127
  )
104
-
105
- return data.dig('results') if( data.dig(:status).nil? )
106
-
107
- nil
108
128
  end
109
129
 
110
130
  end