icinga2 0.5.2 → 0.6.1
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.
- checksums.yaml +4 -4
- data/README.md +63 -38
- data/doc/downtimes.md +4 -3
- data/doc/hostgroups.md +10 -7
- data/doc/hosts.md +19 -7
- data/doc/notifications.md +24 -7
- data/doc/servicegroups.md +11 -8
- data/doc/services.md +29 -6
- data/doc/usergroups.md +8 -4
- data/doc/users.md +12 -11
- data/examples/test.rb +117 -113
- data/lib/icinga2.rb +71 -61
- data/lib/icinga2/converts.rb +68 -53
- data/lib/icinga2/downtimes.rb +73 -90
- data/lib/icinga2/hostgroups.rb +50 -73
- data/lib/icinga2/hosts.rb +133 -170
- data/lib/icinga2/network.rb +182 -117
- data/lib/icinga2/notifications.rb +82 -79
- data/lib/icinga2/servicegroups.rb +50 -71
- data/lib/icinga2/services.rb +144 -162
- data/lib/icinga2/status.rb +32 -32
- data/lib/icinga2/tools.rb +10 -4
- data/lib/icinga2/usergroups.rb +49 -67
- data/lib/icinga2/users.rb +65 -70
- data/lib/icinga2/version.rb +7 -3
- data/lib/logging.rb +10 -6
- metadata +2 -2
data/lib/icinga2.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
2
3
|
#
|
3
4
|
#
|
4
5
|
#
|
@@ -48,85 +49,96 @@ module Icinga2
|
|
48
49
|
include Icinga2::Users
|
49
50
|
include Icinga2::Usergroups
|
50
51
|
|
51
|
-
|
52
|
+
# Returns a new instance of Client
|
53
|
+
#
|
54
|
+
# @param [Hash, #read] settings the settings for Icinga2
|
55
|
+
# @option settings [String] :icinga, :host the Icinga2 Hostname (default: 'localhost')
|
56
|
+
# @option settings [Integer] :icinga, :api, :port the Icinga2 API Port (default: 5665)
|
57
|
+
# @option settings [String] :icinga, :api, :user the Icinga2 API User
|
58
|
+
# @option settings [String] :icinga, :api, :password the Icinga2 API Password
|
59
|
+
# @option settings [Bool] :icinga, :cluster Icinga2 Cluster Mode
|
60
|
+
# @option settings [Bool] :icinga, :notifications enable Icinga2 Host Notifications (default: false)
|
61
|
+
#
|
62
|
+
# @example to create an new Instance
|
63
|
+
# config = {
|
64
|
+
# icinga: {
|
65
|
+
# host: '192.168.33.5',
|
66
|
+
# api: {
|
67
|
+
# port: 5665,
|
68
|
+
# user: 'root',
|
69
|
+
# password: 'icinga'
|
70
|
+
# },
|
71
|
+
# cluster: false,
|
72
|
+
# satellite: true
|
73
|
+
# }
|
74
|
+
# }
|
75
|
+
#
|
76
|
+
# i = Icinga2::Client.new config
|
77
|
+
# @return [instance, #read]
|
52
78
|
def initialize( settings = {} )
|
53
79
|
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
57
|
-
@
|
58
|
-
@
|
59
|
-
@
|
60
|
-
@
|
61
|
-
|
62
|
-
@icingaApiUrlBase = sprintf( 'https://%s:%d', @icingaHost, @icingaApiPort )
|
63
|
-
@nodeName = Socket.gethostbyname( Socket.gethostname ).first
|
64
|
-
|
65
|
-
date = '2017-06-08'
|
66
|
-
|
67
|
-
logger.info( '-----------------------------------------------------------------' )
|
68
|
-
logger.info( ' Icinga2 Management' )
|
69
|
-
logger.info( " Version #{VERSION} (#{date})" )
|
70
|
-
logger.info( ' Copyright 2016-2017 Bodo Schulz' )
|
71
|
-
logger.info( " Backendsystem #{@icingaApiUrlBase}" )
|
72
|
-
logger.info( sprintf( ' cluster enabled: %s', @icingaCluster ? 'true' : 'false' ) )
|
73
|
-
logger.info( sprintf( ' notifications enabled: %s', @icingaNotifications ? 'true' : 'false' ) )
|
74
|
-
if( @icingaCluster )
|
75
|
-
logger.info( sprintf( ' satellite endpoint: %s', @icingaSatellite ) )
|
76
|
-
end
|
77
|
-
logger.info( '-----------------------------------------------------------------' )
|
78
|
-
logger.info( '' )
|
79
|
-
|
80
|
-
logger.debug( sprintf( ' server : %s', @icingaHost ) )
|
81
|
-
logger.debug( sprintf( ' port : %s', @icingaApiPort ) )
|
82
|
-
logger.debug( sprintf( ' api url : %s', @icingaApiUrlBase ) )
|
83
|
-
logger.debug( sprintf( ' api user : %s', @icingaApiUser ) )
|
84
|
-
logger.debug( sprintf( ' api pass : %s', @icingaApiPass ) )
|
85
|
-
logger.debug( sprintf( ' node name: %s', @nodeName ) )
|
80
|
+
@icinga_host = settings.dig(:icinga, :host) || 'localhost'
|
81
|
+
@icinga_api_port = settings.dig(:icinga, :api, :port) || 5665
|
82
|
+
@icinga_api_user = settings.dig(:icinga, :api, :user)
|
83
|
+
@icinga_api_pass = settings.dig(:icinga, :api, :password)
|
84
|
+
@icinga_cluster = settings.dig(:icinga, :cluster) || false
|
85
|
+
@icinga_satellite = settings.dig(:icinga, :satellite)
|
86
|
+
@icinga_notifications = settings.dig(:icinga, :notifications) || false
|
86
87
|
|
87
|
-
@
|
88
|
-
@
|
88
|
+
@icinga_api_url_base = format( 'https://%s:%d', @icinga_host, @icinga_api_port )
|
89
|
+
@node_name = Socket.gethostbyname( Socket.gethostname ).first
|
89
90
|
|
90
|
-
|
91
|
+
@has_cert = cert?( user: @icinga_api_user, password: @icinga_api_pass )
|
92
|
+
@headers = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
|
91
93
|
|
94
|
+
self
|
92
95
|
end
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
97
|
+
# @param [Hash, #read] params create a HTTP Header based on a Icinga2 Certificate or an User PAI Login
|
98
|
+
# @option params [String] :node_name the Icinga2 Hostname (default: 'localhost')
|
99
|
+
# @option params [Integer] :user the Icinga2 API User
|
100
|
+
# @option params [Integer] :password the Icinga2 API Password
|
101
|
+
# @example with Certificate
|
102
|
+
# cert? name_name: 'icinga2-dashing'
|
103
|
+
#
|
104
|
+
# @example with User
|
105
|
+
# cert? user: 'root' password: 'icinga'
|
106
|
+
#
|
107
|
+
# @return [Bool, #read]
|
108
|
+
def cert?( params = {} )
|
109
|
+
|
110
|
+
node_name = params.dig(:node_name) || 'localhost'
|
111
|
+
user = params.dig(:user)
|
112
|
+
password = params.dig(:password)
|
101
113
|
|
102
114
|
# check whether pki files are there, otherwise use basic auth
|
103
|
-
if File.file?(
|
115
|
+
if File.file?( format( 'pki/%s.crt', node_name ) )
|
104
116
|
|
105
|
-
logger.debug(
|
117
|
+
logger.debug( 'PKI found, using client certificates for connection to Icinga 2 API' )
|
106
118
|
|
107
|
-
|
108
|
-
|
109
|
-
|
119
|
+
ssl_cert_file = File.read( format( 'pki/%s.crt', node_name ) )
|
120
|
+
ssl_key_file = File.read( format( 'pki/%s.key', node_name ) )
|
121
|
+
ssl_ca_file = File.read( 'pki/ca.crt' )
|
110
122
|
|
111
|
-
cert = OpenSSL::X509::Certificate.new(
|
112
|
-
key = OpenSSL::PKey::RSA.new(
|
123
|
+
cert = OpenSSL::X509::Certificate.new( ssl_cert_file )
|
124
|
+
key = OpenSSL::PKey::RSA.new( ssl_key_file )
|
113
125
|
|
114
126
|
@options = {
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:ssl_ca_file
|
118
|
-
:
|
127
|
+
ssl_client_cert: cert,
|
128
|
+
ssl_client_key: key,
|
129
|
+
ssl_ca_file: ssl_ca_file,
|
130
|
+
verify_ssl: OpenSSL::SSL::VERIFY_NONE
|
119
131
|
}
|
120
132
|
|
121
133
|
return true
|
122
134
|
else
|
123
135
|
|
124
|
-
logger.debug(
|
136
|
+
logger.debug( 'PKI not found, using basic auth for connection to Icinga 2 API' )
|
125
137
|
|
126
138
|
@options = {
|
127
|
-
:
|
128
|
-
:
|
129
|
-
:
|
139
|
+
user: user,
|
140
|
+
password: password,
|
141
|
+
verify_ssl: OpenSSL::SSL::VERIFY_NONE
|
130
142
|
}
|
131
143
|
|
132
144
|
return false
|
@@ -136,5 +148,3 @@ module Icinga2
|
|
136
148
|
|
137
149
|
end
|
138
150
|
end
|
139
|
-
|
140
|
-
# EOF
|
data/lib/icinga2/converts.rb
CHANGED
@@ -1,77 +1,92 @@
|
|
1
1
|
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Icinga2
|
4
5
|
|
6
|
+
# many convert functions
|
7
|
+
#
|
8
|
+
#
|
5
9
|
module Converts
|
6
10
|
|
7
|
-
|
11
|
+
# convert a Icinga2 state into a human readable state
|
12
|
+
#
|
13
|
+
# @param [String, #read] state the Icinga2 State
|
14
|
+
# @param [Bool, #read] is_host if this a Host or a Service Check
|
15
|
+
#
|
16
|
+
# @return [String, #read]
|
17
|
+
def self.state_to_string( state, is_host = false )
|
8
18
|
|
19
|
+
state =
|
9
20
|
if( is_host == true )
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
21
|
+
case state
|
22
|
+
when 0
|
23
|
+
'Up'
|
24
|
+
when 1
|
25
|
+
'Down'
|
26
|
+
else
|
27
|
+
'Undefined'
|
28
|
+
end
|
19
29
|
else
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
30
|
+
case state
|
31
|
+
when 0
|
32
|
+
'OK'
|
33
|
+
when 1
|
34
|
+
'Warning'
|
35
|
+
when 2
|
36
|
+
'Critical'
|
37
|
+
when 3
|
38
|
+
'Unknown'
|
39
|
+
else
|
40
|
+
'Undefined'
|
41
|
+
end
|
33
42
|
end
|
34
|
-
|
35
|
-
return state
|
36
|
-
|
43
|
+
state
|
37
44
|
end
|
38
45
|
|
39
|
-
|
46
|
+
# convert a Icinga2 state into a named color
|
47
|
+
#
|
48
|
+
# @param [String, #read] state the Icinga2 State
|
49
|
+
# @param [Bool, #read] is_host if this a Host or a Service Check
|
50
|
+
#
|
51
|
+
# @return [String, #read]
|
52
|
+
def self.state_to_color( state, is_host = false )
|
40
53
|
|
54
|
+
state =
|
41
55
|
if( is_host == true )
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
56
|
+
case state
|
57
|
+
when 0
|
58
|
+
'green'
|
59
|
+
when 1
|
60
|
+
'red'
|
61
|
+
else
|
62
|
+
'blue'
|
63
|
+
end
|
51
64
|
else
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
+
case state
|
66
|
+
when 0
|
67
|
+
'green'
|
68
|
+
when 1
|
69
|
+
'yellow'
|
70
|
+
when 2
|
71
|
+
'red'
|
72
|
+
when 3
|
73
|
+
'purple'
|
74
|
+
else
|
75
|
+
'blue'
|
76
|
+
end
|
65
77
|
end
|
66
|
-
|
67
|
-
return state
|
78
|
+
state
|
68
79
|
end
|
69
80
|
|
70
|
-
|
81
|
+
# reformat a service check name
|
82
|
+
#
|
83
|
+
# @param [String, #read] name
|
84
|
+
#
|
85
|
+
# @return [String, #read]
|
86
|
+
def self.format_service( name )
|
71
87
|
service_map = name.split('!', 2)
|
72
88
|
service_map.join( ' - ' )
|
73
89
|
end
|
74
90
|
|
75
91
|
end
|
76
|
-
|
77
92
|
end
|
data/lib/icinga2/downtimes.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
module Icinga2
|
3
5
|
|
6
|
+
#
|
7
|
+
#
|
8
|
+
#
|
4
9
|
module Downtimes
|
5
10
|
|
6
|
-
|
7
|
-
|
11
|
+
#
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def add_downtime( params = {} )
|
8
15
|
|
9
16
|
name = params.dig(:name)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
endTime = params.dig(:end_time)
|
17
|
+
host_name = params.dig(:host)
|
18
|
+
host_group = params.dig(:host_group)
|
19
|
+
start_time = params.dig(:start_time) || Time.now.to_i
|
20
|
+
end_time = params.dig(:end_time)
|
15
21
|
author = params.dig(:author)
|
16
22
|
comment = params.dig(:comment)
|
17
23
|
type = params.dig(:type)
|
@@ -19,120 +25,99 @@ module Icinga2
|
|
19
25
|
|
20
26
|
# sanitychecks
|
21
27
|
#
|
22
|
-
if( name
|
23
|
-
|
28
|
+
if( name.nil? )
|
24
29
|
return {
|
25
|
-
:
|
26
|
-
:
|
30
|
+
status: 404,
|
31
|
+
message: 'missing downtime name'
|
27
32
|
}
|
28
33
|
end
|
29
34
|
|
30
|
-
if( [
|
31
|
-
|
35
|
+
if( %w[host service].include?(type.downcase) == false )
|
32
36
|
return {
|
33
|
-
:
|
34
|
-
:
|
37
|
+
status: 404,
|
38
|
+
message: "wrong downtype type. only 'host' or' service' allowed ('#{type}' giving"
|
35
39
|
}
|
36
40
|
else
|
37
41
|
# we need the first char as Uppercase
|
38
|
-
type.capitalize
|
42
|
+
type = type.capitalize
|
39
43
|
end
|
40
44
|
|
41
|
-
if(
|
42
|
-
|
45
|
+
if( !host_group.nil? && !host_name.nil? )
|
43
46
|
return {
|
44
|
-
:
|
45
|
-
:
|
47
|
+
status: 404,
|
48
|
+
message: 'choose host or host_group, not both'
|
46
49
|
}
|
47
|
-
|
48
50
|
end
|
49
51
|
|
50
|
-
if(
|
52
|
+
if( !host_name.nil? )
|
51
53
|
|
52
|
-
filter =
|
53
|
-
elsif(
|
54
|
+
filter = format( 'host.name=="%s"', host_name )
|
55
|
+
elsif( !host_group.nil? )
|
54
56
|
|
55
57
|
# check if hostgroup available ?
|
56
58
|
#
|
57
|
-
filter =
|
59
|
+
filter = format( '"%s" in host.groups', host_group )
|
58
60
|
else
|
59
61
|
|
60
62
|
return {
|
61
|
-
:
|
62
|
-
:
|
63
|
+
status: 404,
|
64
|
+
message: 'missing host or host_group for downtime'
|
63
65
|
}
|
64
66
|
end
|
65
67
|
|
66
|
-
if( comment
|
67
|
-
|
68
|
+
if( comment.nil? )
|
68
69
|
return {
|
69
|
-
:
|
70
|
-
:
|
70
|
+
status: 404,
|
71
|
+
message: 'missing downtime comment'
|
71
72
|
}
|
72
73
|
end
|
73
74
|
|
74
|
-
if( author
|
75
|
-
|
75
|
+
if( author.nil? )
|
76
76
|
return {
|
77
|
-
:
|
78
|
-
:
|
77
|
+
status: 404,
|
78
|
+
message: 'missing downtime author'
|
79
|
+
}
|
80
|
+
elsif( exists_user?( author ) == false )
|
81
|
+
return {
|
82
|
+
status: 404,
|
83
|
+
message: "these author ar not exists: #{author}"
|
79
84
|
}
|
80
|
-
else
|
81
|
-
|
82
|
-
if( self.existsUser?( author ) == false )
|
83
|
-
|
84
|
-
return {
|
85
|
-
:status => 404,
|
86
|
-
:message => "these author ar not exists: #{author}"
|
87
|
-
}
|
88
|
-
end
|
89
85
|
end
|
90
86
|
|
91
87
|
|
92
|
-
if(
|
93
|
-
|
88
|
+
if( end_time.nil? )
|
94
89
|
return {
|
95
|
-
:
|
96
|
-
:
|
90
|
+
status: 404,
|
91
|
+
message: 'missing end_time'
|
92
|
+
}
|
93
|
+
elsif( end_time.to_i <= start_time )
|
94
|
+
return {
|
95
|
+
status: 404,
|
96
|
+
message: 'end_time are equal or smaller then start_time'
|
97
97
|
}
|
98
|
-
else
|
99
|
-
|
100
|
-
if( endTime.to_i <= startTime )
|
101
|
-
|
102
|
-
return {
|
103
|
-
:status => 404,
|
104
|
-
:message => 'end_time are equal or smaller then start_time'
|
105
|
-
}
|
106
|
-
end
|
107
98
|
end
|
108
99
|
|
109
|
-
# logger.debug( Time.at(
|
110
|
-
# logger.debug( Time.at(
|
100
|
+
# logger.debug( Time.at( start_time ).strftime( '%Y-%m-%d %H:%M:%S' ) )
|
101
|
+
# logger.debug( Time.at( end_time ).strftime( '%Y-%m-%d %H:%M:%S' ) )
|
111
102
|
|
112
103
|
payload = {
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
104
|
+
'type' => type,
|
105
|
+
'start_time' => start_time,
|
106
|
+
'end_time' => end_time,
|
107
|
+
'author' => author,
|
108
|
+
'comment' => comment,
|
109
|
+
'fixed' => true,
|
110
|
+
'duration' => 30,
|
111
|
+
'filter' => filter
|
121
112
|
}
|
122
113
|
|
123
114
|
# logger.debug( JSON.pretty_generate( payload ) )
|
124
115
|
|
125
|
-
result = Network.post(
|
126
|
-
:
|
127
|
-
:
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:payload => payload
|
131
|
-
} )
|
132
|
-
|
133
|
-
logger.debug( result.class.to_s )
|
134
|
-
|
135
|
-
return JSON.pretty_generate( result )
|
116
|
+
result = Network.post( host: name,
|
117
|
+
url: format( '%s/v1/actions/schedule-downtime', @icinga_api_url_base ),
|
118
|
+
headers: @headers,
|
119
|
+
options: @options,
|
120
|
+
payload: payload )
|
136
121
|
|
137
122
|
|
138
123
|
# schedule downtime for a host
|
@@ -146,28 +131,26 @@ module Icinga2
|
|
146
131
|
|
147
132
|
# --data '{ "type": "Service", "filter": "\"api_dummy_hostgroup\" in host.groups)", ... }'
|
148
133
|
|
149
|
-
|
134
|
+
JSON.pretty_generate( result )
|
150
135
|
|
151
136
|
end
|
152
137
|
|
153
|
-
|
154
|
-
|
138
|
+
#
|
139
|
+
#
|
140
|
+
#
|
141
|
+
def downtimes( params = {} )
|
155
142
|
|
156
143
|
name = params.dig(:name)
|
157
144
|
|
158
|
-
result = Network.get(
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:options => @options
|
163
|
-
} )
|
145
|
+
result = Network.get( host: name,
|
146
|
+
url: format( '%s/v1/objects/downtimes/%s', @icinga_api_url_base, name ),
|
147
|
+
headers: @headers,
|
148
|
+
options: @options )
|
164
149
|
|
165
|
-
|
150
|
+
JSON.pretty_generate( result )
|
166
151
|
|
167
152
|
|
168
153
|
end
|
169
154
|
|
170
|
-
|
171
155
|
end
|
172
|
-
|
173
156
|
end
|