sensu-plugins-mysql-nagyt 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # metrics-mysql-processes
4
+ #
5
+ # DESCRIPTION:
6
+ # Gets metrics out of of MySQL's "SHOW PROCESSLIST" query.
7
+ #
8
+ # Output number of connections per-users, number of connections
9
+ # per-databases, number of the different commands running.
10
+ #
11
+ # OUTPUT:
12
+ # metric-data
13
+ #
14
+ # PLATFORMS:
15
+ # Linux, Windows, BSD, Solaris, etc
16
+ #
17
+ # DEPENDENCIES:
18
+ # gem: sensu-plugin
19
+ # gem: mysql
20
+ #
21
+ # USAGE:
22
+ # This was implemented to load mysql credentials without parsing the username/password.
23
+ # The ini file should be readable by the sensu user/group.
24
+ # Ref: http://eric.lubow.org/2009/ruby/parsing-ini-files-with-ruby/
25
+ #
26
+ # EXAMPLE
27
+ # mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf'
28
+ # mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf' --ini-section customsection
29
+ #
30
+ # MY.CNF INI FORMAT
31
+ # [client]
32
+ # user=sensu
33
+ # password="abcd1234"
34
+ #
35
+ # [customsection]
36
+ # user=user
37
+ # password="password"
38
+ #
39
+ # NOTES:
40
+ #
41
+ # LICENSE:
42
+ # Jonathan Ballet <jballet@edgelab.ch>
43
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
44
+ # for details.
45
+
46
+ require 'sensu-plugin/metric/cli'
47
+ require 'mysql'
48
+ require 'socket'
49
+ require 'inifile'
50
+
51
+ class MetricsMySQLProcesses < Sensu::Plugin::Metric::CLI::Graphite
52
+ option :host,
53
+ short: '-h HOST',
54
+ long: '--host HOST',
55
+ description: 'MySQL Host to connect to',
56
+ required: true
57
+
58
+ option :port,
59
+ short: '-P PORT',
60
+ long: '--port PORT',
61
+ description: 'MySQL Port to connect to',
62
+ proc: proc(&:to_i),
63
+ default: 3306
64
+
65
+ option :username,
66
+ short: '-u USERNAME',
67
+ long: '--user USERNAME',
68
+ description: 'MySQL Username'
69
+
70
+ option :password,
71
+ short: '-p PASSWORD',
72
+ long: '--pass PASSWORD',
73
+ description: 'MySQL password',
74
+ default: ''
75
+
76
+ option :ini,
77
+ short: '-i',
78
+ long: '--ini VALUE',
79
+ description: 'My.cnf ini file'
80
+
81
+ option :ini_section,
82
+ description: 'Section in my.cnf ini file',
83
+ long: '--ini-section VALUE',
84
+ default: 'client'
85
+
86
+ option :scheme,
87
+ description: 'Metric naming scheme, text to prepend to metric',
88
+ short: '-s SCHEME',
89
+ long: '--scheme SCHEME',
90
+ default: "#{Socket.gethostname}.mysql"
91
+
92
+ option :socket,
93
+ short: '-S SOCKET',
94
+ long: '--socket SOCKET',
95
+ description: 'MySQL Unix socket to connect to'
96
+
97
+ def run
98
+ config[:host].split(' ').each do |mysql_host|
99
+ mysql_shorthostname = mysql_host.split('.')[0]
100
+ if config[:ini]
101
+ ini = IniFile.load(config[:ini])
102
+ section = ini[config[:ini_section]]
103
+ db_user = section['user']
104
+ db_pass = section['password']
105
+ else
106
+ db_user = config[:username]
107
+ db_pass = config[:password]
108
+ end
109
+ begin
110
+ mysql = Mysql.new(mysql_host, db_user, db_pass, nil, config[:port], config[:socket])
111
+
112
+ results = mysql.query('SHOW PROCESSLIST')
113
+ rescue => e
114
+ unknown "Unable to query MySQL: #{e.message}"
115
+ end
116
+
117
+ metrics = {
118
+ 'user' => {},
119
+ 'database' => {},
120
+ 'command' => {}
121
+ }
122
+
123
+ metrics.each_value { |value| value.default = 0 }
124
+
125
+ results.each_hash do |row|
126
+ metrics['user'][row['User']] += 1
127
+ if row['db'] # If no database has been selected by the process, it is set to nil.
128
+ metrics['database'][row['db']] += 1
129
+ end
130
+ metrics['command'][row['Command']] += 1
131
+ end
132
+
133
+ metrics.each do |key, value|
134
+ value.each do |instance, count|
135
+ output "#{config[:scheme]}.#{mysql_shorthostname}.#{key}.#{instance}", count
136
+ end
137
+ end
138
+ end
139
+
140
+ ok
141
+ end
142
+ end
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # MySQL Query Result Count Metric
4
+ #
5
+ # Creates a graphite-formatted metric for the length of a result set from a MySQL query.
6
+ #
7
+ # Copyright 2017 Andrew Thal <athal7@me.com>
8
+ #
9
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
10
+ # for details.
11
+
12
+ require 'sensu-plugin/metric/cli'
13
+ require 'mysql'
14
+ require 'inifile'
15
+
16
+ class MysqlQueryCountMetric < Sensu::Plugin::Metric::CLI::Graphite
17
+ option :host,
18
+ short: '-h HOST',
19
+ long: '--host HOST',
20
+ description: 'MySQL Host to connect to',
21
+ required: true
22
+
23
+ option :port,
24
+ short: '-P PORT',
25
+ long: '--port PORT',
26
+ description: 'MySQL Port to connect to',
27
+ proc: proc(&:to_i),
28
+ default: 3306
29
+
30
+ option :username,
31
+ short: '-u USERNAME',
32
+ long: '--user USERNAME',
33
+ description: 'MySQL Username'
34
+
35
+ option :password,
36
+ short: '-p PASSWORD',
37
+ long: '--pass PASSWORD',
38
+ description: 'MySQL password',
39
+ default: ''
40
+
41
+ option :database,
42
+ short: '-d DATABASE',
43
+ long: '--database DATABASE',
44
+ description: 'MySQL database',
45
+ default: ''
46
+
47
+ option :ini,
48
+ short: '-i',
49
+ long: '--ini VALUE',
50
+ description: 'My.cnf ini file'
51
+
52
+ option :ini_section,
53
+ description: 'Section in my.cnf ini file',
54
+ long: '--ini-section VALUE',
55
+ default: 'client'
56
+
57
+ option :socket,
58
+ short: '-S SOCKET',
59
+ long: '--socket SOCKET',
60
+ description: 'MySQL Unix socket to connect to'
61
+
62
+ option :name,
63
+ short: '-n NAME',
64
+ long: '--name NAME',
65
+ description: 'Metric name for a configured handler',
66
+ default: 'mysql.query_count'
67
+
68
+ option :query,
69
+ short: '-q QUERY',
70
+ long: '--query QUERY',
71
+ description: 'Query to execute',
72
+ required: true
73
+
74
+ def run
75
+ if config[:ini]
76
+ ini = IniFile.load(config[:ini])
77
+ section = ini[config[:ini_section]]
78
+ db_user = section['user']
79
+ db_pass = section['password']
80
+ else
81
+ db_user = config[:username]
82
+ db_pass = config[:password]
83
+ end
84
+ db = Mysql.real_connect(config[:host], db_user, db_pass, config[:database], config[:port].to_i, config[:socket])
85
+ length = db.query(config[:query]).count
86
+
87
+ output config[:name], length
88
+ ok
89
+
90
+ rescue Mysql::Error => e
91
+ errstr = "Error code: #{e.errno} Error message: #{e.error}"
92
+ critical "#{errstr} SQLSTATE: #{e.sqlstate}" if e.respond_to?('sqlstate')
93
+
94
+ rescue => e
95
+ critical e
96
+
97
+ ensure
98
+ db.close if db
99
+ end
100
+ end
@@ -0,0 +1,396 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # MySQL metrics Plugin without mysql gem requirement
4
+ # ===
5
+ #
6
+ # This plugin attempts to login to mysql with provided credentials.
7
+ # and outputs metrics in graphite format
8
+ #
9
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
10
+ # for details.
11
+ #
12
+ # USING INI ARGUMENT
13
+ # This was implemented to load mysql credentials without parsing
14
+ # the username/password.
15
+ # The ini file should be readable by the sensu user/group.
16
+ #
17
+ # EXAMPLE
18
+ # metrics-mysql-raw.rb -h localhost --ini '/etc/sensu/my.cnf'
19
+ # metrics-mysql-raw.rb -h localhost --ini '/etc/sensu/my.cnf' --ini-section customsection
20
+ #
21
+ # MY.CNF INI FORMAT
22
+ # [client]
23
+ # user=sensu
24
+ # password="abcd1234"
25
+ # socket="/var/lib/mysql/mysql.sock"
26
+ #
27
+ # [customsection]
28
+ # user=user
29
+ # password="password"
30
+ #
31
+ # LICENSE:
32
+ # Copyright 2012 Pete Shima <me@peteshima.com>
33
+ # Additional hacks by Joe Miller - https://github.com/joemiller
34
+ # Updated by Oluwaseun Obajobi 2014 to accept ini argument
35
+ # Forked by Magic Online 11.2016 to not depend on mysql gem
36
+ # - www.magic.fr <hanynowsky@gmail.com>
37
+ # MIT - Same as Sensu License
38
+ #
39
+
40
+ require 'sensu-plugin/metric/cli'
41
+ require 'open3'
42
+ require 'socket'
43
+ require 'inifile'
44
+ require 'timeout'
45
+
46
+ #
47
+ # Metrics Mysql Raw
48
+ #
49
+ class MetricsMySQLRaw < Sensu::Plugin::Metric::CLI::Graphite
50
+ option(
51
+ :user,
52
+ description: 'MySQL User',
53
+ short: '-u USER',
54
+ long: '--user USER',
55
+ default: 'mosim'
56
+ )
57
+
58
+ option(
59
+ :password,
60
+ description: 'MySQL Password',
61
+ short: '-p PASS',
62
+ long: '--password PASS',
63
+ default: 'mysqlPassWord'
64
+ )
65
+
66
+ option(
67
+ :ini,
68
+ description: 'My.cnf ini file',
69
+ short: '-i',
70
+ long: '--ini VALUE'
71
+ )
72
+
73
+ option(
74
+ :ini_section,
75
+ description: 'Section in my.cnf ini file',
76
+ long: '--ini-section VALUE',
77
+ default: 'client'
78
+ )
79
+
80
+ option(
81
+ :hostname,
82
+ description: 'Hostname to login to',
83
+ short: '-h HOST',
84
+ long: '--hostname HOST',
85
+ default: 'localhost'
86
+ )
87
+
88
+ option(
89
+ :database,
90
+ description: 'Database schema to connect to. NOT YET IMPlemented',
91
+ short: '-d DATABASE',
92
+ long: '--database DATABASE',
93
+ default: 'test'
94
+ )
95
+
96
+ option(
97
+ :timeout,
98
+ description: 'Timeout',
99
+ short: '-T TIMEOUT',
100
+ long: '--timeout TIMEOUT',
101
+ default: 10
102
+ )
103
+
104
+ option(
105
+ :port,
106
+ description: 'Port to connect to',
107
+ short: '-P PORT',
108
+ long: '--port PORT',
109
+ default: '3306'
110
+ )
111
+
112
+ option(
113
+ :socket,
114
+ description: 'Socket to use',
115
+ short: '-s SOCKET',
116
+ long: '--socket SOCKET',
117
+ default: '/var/run/mysqld/mysqld.sock'
118
+ )
119
+
120
+ option(
121
+ :binary,
122
+ description: 'Absolute path to mysql binary',
123
+ short: '-b BINARY',
124
+ long: '--binary BINARY',
125
+ default: 'mysql'
126
+ )
127
+
128
+ option(
129
+ :check,
130
+ description: 'type of check: metric',
131
+ short: '-c CHECK',
132
+ long: '--check CHECK',
133
+ default: 'metric'
134
+ )
135
+
136
+ option(
137
+ :scheme,
138
+ description: 'Metric naming scheme, text to prepend to metric',
139
+ short: '-s SCHEME',
140
+ long: '--scheme SCHEME',
141
+ default: "#{Socket.gethostname}.mysql"
142
+ )
143
+
144
+ option(
145
+ :verbose,
146
+ short: '-v',
147
+ long: '--verbose',
148
+ boolean: true
149
+ )
150
+
151
+ option(
152
+ :off,
153
+ description: 'Turn Metrics OFF',
154
+ long: '--off',
155
+ boolean: true,
156
+ default: false
157
+ )
158
+
159
+ # Metrics hash
160
+ def metrics_hash
161
+ metrics = {
162
+ 'general' => {
163
+ 'Bytes_received' => 'rxBytes',
164
+ 'Bytes_sent' => 'txBytes',
165
+ 'Key_read_requests' => 'keyRead_requests',
166
+ 'Key_reads' => 'keyReads',
167
+ 'Key_write_requests' => 'keyWrite_requests',
168
+ 'Key_writes' => 'keyWrites',
169
+ 'Binlog_cache_use' => 'binlogCacheUse',
170
+ 'Binlog_cache_disk_use' => 'binlogCacheDiskUse',
171
+ 'Max_used_connections' => 'maxUsedConnections',
172
+ 'Aborted_clients' => 'abortedClients',
173
+ 'Aborted_connects' => 'abortedConnects',
174
+ 'Threads_connected' => 'threadsConnected',
175
+ 'Open_files' => 'openFiles',
176
+ 'Open_tables' => 'openTables',
177
+ 'Opened_tables' => 'openedTables',
178
+ 'Prepared_stmt_count' => 'preparedStmtCount',
179
+ 'Seconds_Behind_Master' => 'slaveLag',
180
+ 'Select_full_join' => 'fullJoins',
181
+ 'Select_full_range_join' => 'fullRangeJoins',
182
+ 'Select_range' => 'selectRange',
183
+ 'Select_range_check' => 'selectRange_check',
184
+ 'Select_scan' => 'selectScan',
185
+ 'Slow_queries' => 'slowQueries'
186
+ },
187
+ 'querycache' => {
188
+ 'Qcache_queries_in_cache' => 'queriesInCache',
189
+ 'Qcache_hits' => 'cacheHits',
190
+ 'Qcache_inserts' => 'inserts',
191
+ 'Qcache_not_cached' => 'notCached',
192
+ 'Qcache_lowmem_prunes' => 'lowMemPrunes',
193
+ 'Qcache_free_memory' => 'freeMemory'
194
+ },
195
+ 'commands' => {
196
+ 'Com_admin_commands' => 'admin_commands',
197
+ 'Com_begin' => 'begin',
198
+ 'Com_change_db' => 'change_db',
199
+ 'Com_commit' => 'commit',
200
+ 'Com_create_table' => 'create_table',
201
+ 'Com_drop_table' => 'drop_table',
202
+ 'Com_show_keys' => 'show_keys',
203
+ 'Com_delete' => 'delete',
204
+ 'Com_create_db' => 'create_db',
205
+ 'Com_grant' => 'grant',
206
+ 'Com_show_processlist' => 'show_processlist',
207
+ 'Com_flush' => 'flush',
208
+ 'Com_insert' => 'insert',
209
+ 'Com_purge' => 'purge',
210
+ 'Com_replace' => 'replace',
211
+ 'Com_rollback' => 'rollback',
212
+ 'Com_select' => 'select',
213
+ 'Com_set_option' => 'set_option',
214
+ 'Com_show_binlogs' => 'show_binlogs',
215
+ 'Com_show_databases' => 'show_databases',
216
+ 'Com_show_fields' => 'show_fields',
217
+ 'Com_show_status' => 'show_status',
218
+ 'Com_show_tables' => 'show_tables',
219
+ 'Com_show_variables' => 'show_variables',
220
+ 'Com_update' => 'update',
221
+ 'Com_drop_db' => 'drop_db',
222
+ 'Com_revoke' => 'revoke',
223
+ 'Com_drop_user' => 'drop_user',
224
+ 'Com_show_grants' => 'show_grants',
225
+ 'Com_lock_tables' => 'lock_tables',
226
+ 'Com_show_create_table' => 'show_create_table',
227
+ 'Com_unlock_tables' => 'unlock_tables',
228
+ 'Com_alter_table' => 'alter_table'
229
+ },
230
+ 'counters' => {
231
+ 'Handler_write' => 'handlerWrite',
232
+ 'Handler_update' => 'handlerUpdate',
233
+ 'Handler_delete' => 'handlerDelete',
234
+ 'Handler_read_first' => 'handlerRead_first',
235
+ 'Handler_read_key' => 'handlerRead_key',
236
+ 'Handler_read_next' => 'handlerRead_next',
237
+ 'Handler_read_prev' => 'handlerRead_prev',
238
+ 'Handler_read_rnd' => 'handlerRead_rnd',
239
+ 'Handler_read_rnd_next' => 'handlerRead_rnd_next',
240
+ 'Handler_commit' => 'handlerCommit',
241
+ 'Handler_rollback' => 'handlerRollback',
242
+ 'Handler_savepoint' => 'handlerSavepoint',
243
+ 'Handler_savepoint_rollback' => 'handlerSavepointRollback'
244
+ },
245
+ 'innodb' => {
246
+ 'Innodb_buffer_pool_pages_total' => 'bufferTotal_pages',
247
+ 'Innodb_buffer_pool_pages_free' => 'bufferFree_pages',
248
+ 'Innodb_buffer_pool_pages_dirty' => 'bufferDirty_pages',
249
+ 'Innodb_buffer_pool_pages_data' => 'bufferUsed_pages',
250
+ 'Innodb_page_size' => 'pageSize',
251
+ 'Innodb_pages_created' => 'pagesCreated',
252
+ 'Innodb_pages_read' => 'pagesRead',
253
+ 'Innodb_pages_written' => 'pagesWritten',
254
+ 'Innodb_row_lock_current_waits' => 'currentLockWaits',
255
+ 'Innodb_row_lock_waits' => 'lockWaitTimes',
256
+ 'Innodb_row_lock_time' => 'rowLockTime',
257
+ 'Innodb_data_reads' => 'fileReads',
258
+ 'Innodb_data_writes' => 'fileWrites',
259
+ 'Innodb_data_fsyncs' => 'fileFsyncs',
260
+ 'Innodb_log_writes' => 'logWrites',
261
+ 'Innodb_rows_updated' => 'rowsUpdated',
262
+ 'Innodb_rows_read' => 'rowsRead',
263
+ 'Innodb_rows_deleted' => 'rowsDeleted',
264
+ 'Innodb_rows_inserted' => 'rowsInserted'
265
+ },
266
+ 'configuration' => {
267
+ 'Max_prepared_stmt_count' => 'MaxPreparedStmtCount'
268
+ }
269
+ }
270
+ metrics
271
+ end
272
+
273
+ # Credentials
274
+ def credentials
275
+ if config[:ini]
276
+ ini = IniFile.load(config[:ini])
277
+ section = ini[config[:ini_section]]
278
+ db_user = section['user']
279
+ db_pass = section['password']
280
+ db_socket = section['socket']
281
+ else
282
+ db_user = config[:user]
283
+ db_pass = config[:password]
284
+ db_socket = config[:socket]
285
+ end
286
+ [db_user, db_pass, db_socket]
287
+ end
288
+
289
+ # Slave metrics
290
+ def slave_metrics(metrics)
291
+ # should return a single element array containing one hash
292
+ # #YELLOW
293
+ mysql_shorthostname = config[:hostname].tr('.', '_')
294
+ slave_results = Hash['a' => 100, 'b' => 200]
295
+ slave_results.first.each do |key, value|
296
+ if metrics['general'].include?(key)
297
+ # Replication lag being null is bad, very bad, so negativate it here
298
+ value = -1 if key == 'Seconds_Behind_Master' && value.nil?
299
+ output "#{config[:scheme]}.#{mysql_shorthostname}.general.#{metrics['general'][key]}", value
300
+ end
301
+ end
302
+ rescue => e
303
+ puts "Error querying slave status: #{e}" if config[:verbose]
304
+ end
305
+
306
+ # Configuration metrics
307
+ def configuration_metrics(metrics, db_user, db_pass, db_socket)
308
+ mysql_shorthostname = config[:hostname].tr('.', '_')
309
+ table = []
310
+ cmd = "#{config[:binary]} -u #{db_user} -h #{config[:hostname]} \
311
+ --port #{config[:port]} --socket #{db_socket} -p\"#{db_pass.chomp}\" --batch \
312
+ --disable-column-names -e 'SHOW GLOBAL VARIABLES;'"
313
+ stdout, _stderr, status = Open3.capture3(cmd)
314
+ puts status.to_s.split(' ')[3] if config[:verbose]
315
+ if status == 0
316
+ puts status.to_s if config[:verbose]
317
+ stdout.split("\n").each do |row|
318
+ line = row.tr("\t", ':')
319
+ key = line.split(':')[0]
320
+ value = line.split(':')[1]
321
+ table.push('Variable_name' => key, 'Value' => value)
322
+ end
323
+ else
324
+ critical "Error message: Global variables - status: #{status}"
325
+ end
326
+ variables_results = table
327
+ category = 'configuration'
328
+ variables_results.each do |row|
329
+ metrics[category].each do |metric, desc|
330
+ if metric.casecmp(row['Variable_name']) == 0
331
+ output "#{config[:scheme]}.#{mysql_shorthostname}.#{category}.#{desc}", row['Value']
332
+ end
333
+ end
334
+ end
335
+ rescue => e
336
+ puts e.message
337
+ end
338
+
339
+ # Fetch MySQL metrics
340
+ def fetcher(db_user, db_pass, db_socket)
341
+ metrics = metrics_hash
342
+ if config[:check] == 'metric'
343
+ mysql_shorthostname = config[:hostname].tr('.', '_')
344
+ begin
345
+ table = []
346
+ cmd = "#{config[:binary]} -u #{db_user} -h #{config[:hostname]} \
347
+ --port #{config[:port]} --socket #{db_socket} -p\"#{db_pass.chomp}\" --batch \
348
+ --disable-column-names -e 'SHOW GLOBAL STATUS;'"
349
+ stdout, _stderr, status = Open3.capture3(cmd)
350
+ puts status.to_s.split(' ')[3] if config[:verbose]
351
+ if status == 0
352
+ puts status.to_s if config[:verbose]
353
+ stdout.split("\n").each do |row|
354
+ line = row.tr("\t", ':')
355
+ key = line.split(':')[0]
356
+ value = line.split(':')[1]
357
+ table.push('Variable_name' => key, 'Value' => value)
358
+ end
359
+ else
360
+ critical "Error message: status: #{status}"
361
+ end
362
+ table.each do |row|
363
+ metrics.each do |category, var_mapping|
364
+ row_var_name = row['Variable_name'].to_s
365
+ var_mapping.keys.each do |vmkey|
366
+ if row_var_name.to_s == vmkey.to_s
367
+ prefix = "#{config[:scheme]}.#{mysql_shorthostname}.#{category}.#{vmkey[row_var_name]}"
368
+ output prefix, row['Value'] unless mysql_shorthostname.to_s.chomp.empty?
369
+ end
370
+ end
371
+ end
372
+ end
373
+ # Slave and configuration metrics here
374
+ slave_metrics(metrics)
375
+ configuration_metrics(metrics, db_user, db_pass, db_socket)
376
+ rescue => e
377
+ critical "Error message: status: #{status} | Exception: #{e.backtrace}"
378
+ ensure
379
+ ok ''
380
+ end
381
+ end
382
+ end
383
+
384
+ # Main Function
385
+ def run
386
+ ok 'Metrics deactivated by user using option --off' if config[:off] == true
387
+ begin
388
+ Timeout.timeout(config[:timeout]) do
389
+ fetcher(credentials[0], credentials[1], credentials[2])
390
+ end
391
+ rescue Timeout::Error => e
392
+ unknown "Timed out #{e.message}"
393
+ end
394
+ unknown 'Did not succeed to retrieve MySQL metrics. Check your options'
395
+ end
396
+ end