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,122 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # check-mysql-threads.rb
4
+ #
5
+ # DESCRIPTION:
6
+ # MySQL Threads Health plugin
7
+ # This plugin evaluates the number of MySQL running threads and warns you according to specified limits
8
+ # -w for high threshold warning
9
+ # -c for high threshold critical
10
+ # -m for low threshold warning
11
+ # -l for low threshold critical
12
+ #
13
+ # OUTPUT:
14
+ # plain text
15
+ #
16
+ # PLATFORMS:
17
+ # All
18
+ #
19
+ # DEPENDENCIES:
20
+ # gem: sensu-plugin
21
+ #
22
+ # USAGE:
23
+ # check-mysql-threads.rb -w [threshold] -c [threshold] -m [threshold] -l [threshold]
24
+ #
25
+ # NOTES:
26
+ #
27
+ # LICENSE:
28
+ # Author: Guillaume Lefranc <guillaume@mariadb.com>
29
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
30
+ # for details.
31
+ #
32
+
33
+ require 'sensu-plugin/check/cli'
34
+ require 'mysql'
35
+ require 'inifile'
36
+
37
+ class CheckMySQLHealth < Sensu::Plugin::Check::CLI
38
+ option :user,
39
+ description: 'MySQL User',
40
+ short: '-u USER',
41
+ long: '--user USER',
42
+ default: 'root'
43
+
44
+ option :password,
45
+ description: 'MySQL Password',
46
+ short: '-p PASS',
47
+ long: '--password PASS'
48
+
49
+ option :ini,
50
+ description: 'My.cnf ini file',
51
+ short: '-i',
52
+ long: '--ini VALUE'
53
+
54
+ option :ini_section,
55
+ description: 'Section in my.cnf ini file',
56
+ long: '--ini-section VALUE',
57
+ default: 'client'
58
+
59
+ option :hostname,
60
+ description: 'Hostname to login to',
61
+ short: '-h HOST',
62
+ long: '--hostname HOST',
63
+ default: 'localhost'
64
+
65
+ option :port,
66
+ description: 'Port to connect to',
67
+ short: '-P PORT',
68
+ long: '--port PORT',
69
+ default: '3306'
70
+
71
+ option :socket,
72
+ description: 'Socket to use',
73
+ short: '-s SOCKET',
74
+ long: '--socket SOCKET'
75
+
76
+ option :maxwarn,
77
+ description: "Number of running threads upon which we'll issue a warning",
78
+ short: '-w NUMBER',
79
+ long: '--warnnum NUMBER',
80
+ default: 20
81
+
82
+ option :maxcrit,
83
+ description: "Number of running threads upon which we'll issue an alert",
84
+ short: '-c NUMBER',
85
+ long: '--critnum NUMBER',
86
+ default: 25
87
+
88
+ option :minwarn,
89
+ description: "Number of running threads under which we'll issue a warning",
90
+ short: '-m NUMBER',
91
+ long: '--warnlow NUMBER',
92
+ default: 1
93
+
94
+ option :mincrit,
95
+ description: "Number of running threads under which we'll issue an alert",
96
+ short: '-l NUMBER',
97
+ long: '--critlow NUMBER',
98
+ default: 0
99
+
100
+ def run
101
+ if config[:ini]
102
+ ini = IniFile.load(config[:ini])
103
+ section = ini[config[:ini_section]]
104
+ db_user = section['user']
105
+ db_pass = section['password']
106
+ else
107
+ db_user = config[:user]
108
+ db_pass = config[:password]
109
+ end
110
+ db = Mysql.real_connect(config[:hostname], db_user, db_pass, config[:database], config[:port].to_i, config[:socket])
111
+ run_thr = db.query("SHOW GLOBAL STATUS LIKE 'Threads_running'").fetch_hash.fetch('Value').to_i
112
+ critical "MySQL currently running threads: #{run_thr}" if run_thr >= config[:maxcrit].to_i
113
+ warning "MySQL currently running threads: #{run_thr}" if run_thr >= config[:maxwarn].to_i
114
+ critical "MySQL currently running threads: #{run_thr}" if run_thr <= config[:mincrit].to_i
115
+ warning "MySQL currently running threads: #{run_thr}" if run_thr <= config[:minwarn].to_i
116
+ ok "Currently running threads are under limit in MySQL: #{run_thr}"
117
+ rescue Mysql::Error => e
118
+ critical "MySQL check failed: #{e.error}"
119
+ ensure
120
+ db.close if db
121
+ end
122
+ end
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Push mysql stats into graphite
4
+ # ===
5
+ #
6
+ # NOTE: This plugin will attempt to get replication stats but the user
7
+ # must have SUPER or REPLICATION CLIENT privileges to run 'SHOW SLAVE
8
+ # STATUS'. It will silently ignore and continue if 'SHOW SLAVE STATUS'
9
+ # fails for any reason. The key 'slaveLag' will not be present in the
10
+ # output.
11
+ #
12
+ # Copyright 2012 Pete Shima <me@peteshima.com>
13
+ # Additional hacks by Joe Miller - https://github.com/joemiller
14
+ # Updated by Oluwaseun Obajobi 2014 to accept ini argument
15
+ #
16
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
17
+ # for details.
18
+ #
19
+ # USING INI ARGUMENT
20
+ # This was implemented to load mysql credentials without parsing the username/password.
21
+ # The ini file should be readable by the sensu user/group.
22
+ # Ref: http://eric.lubow.org/2009/ruby/parsing-ini-files-with-ruby/
23
+ #
24
+ # EXAMPLE
25
+ # mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf'
26
+ # mysql-alive.rb -h db01 --ini '/etc/sensu/my.cnf' --ini-section customsection
27
+ #
28
+ # MY.CNF INI FORMAT
29
+ # [client]
30
+ # user=sensu
31
+ # password="abcd1234"
32
+ #
33
+ # [customsection]
34
+ # user=user
35
+ # password="password"
36
+ #
37
+
38
+ require 'sensu-plugin/metric/cli'
39
+ require 'mysql'
40
+ require 'socket'
41
+ require 'inifile'
42
+
43
+ class MysqlGraphite < Sensu::Plugin::Metric::CLI::Graphite
44
+ option :host,
45
+ short: '-h HOST',
46
+ long: '--host HOST',
47
+ description: 'Mysql Host to connect to',
48
+ required: true
49
+
50
+ option :port,
51
+ short: '-P PORT',
52
+ long: '--port PORT',
53
+ description: 'Mysql Port to connect to',
54
+ proc: proc(&:to_i),
55
+ default: 3306
56
+
57
+ option :username,
58
+ short: '-u USERNAME',
59
+ long: '--user USERNAME',
60
+ description: 'Mysql Username'
61
+
62
+ option :password,
63
+ short: '-p PASSWORD',
64
+ long: '--pass PASSWORD',
65
+ description: 'Mysql password',
66
+ default: ''
67
+
68
+ option :ini,
69
+ short: '-i',
70
+ long: '--ini VALUE',
71
+ description: 'My.cnf ini file'
72
+
73
+ option :ini_section,
74
+ description: 'Section in my.cnf ini file',
75
+ long: '--ini-section VALUE',
76
+ default: 'client'
77
+
78
+ option :scheme,
79
+ description: 'Metric naming scheme, text to prepend to metric',
80
+ short: '-s SCHEME',
81
+ long: '--scheme SCHEME',
82
+ default: "#{Socket.gethostname}.mysql"
83
+
84
+ option :socket,
85
+ short: '-S SOCKET',
86
+ long: '--socket SOCKET'
87
+
88
+ option :verbose,
89
+ short: '-v',
90
+ long: '--verbose',
91
+ boolean: true
92
+
93
+ def run
94
+ # props to https://github.com/coredump/hoardd/blob/master/scripts-available/mysql.coffee
95
+
96
+ metrics = {
97
+ 'general' => {
98
+ 'Bytes_received' => 'rxBytes',
99
+ 'Bytes_sent' => 'txBytes',
100
+ 'Key_read_requests' => 'keyRead_requests',
101
+ 'Key_reads' => 'keyReads',
102
+ 'Key_write_requests' => 'keyWrite_requests',
103
+ 'Key_writes' => 'keyWrites',
104
+ 'Binlog_cache_use' => 'binlogCacheUse',
105
+ 'Binlog_cache_disk_use' => 'binlogCacheDiskUse',
106
+ 'Max_used_connections' => 'maxUsedConnections',
107
+ 'Aborted_clients' => 'abortedClients',
108
+ 'Aborted_connects' => 'abortedConnects',
109
+ 'Threads_connected' => 'threadsConnected',
110
+ 'Open_files' => 'openFiles',
111
+ 'Open_tables' => 'openTables',
112
+ 'Opened_tables' => 'openedTables',
113
+ 'Prepared_stmt_count' => 'preparedStmtCount',
114
+ 'Seconds_Behind_Master' => 'slaveLag',
115
+ 'Select_full_join' => 'fullJoins',
116
+ 'Select_full_range_join' => 'fullRangeJoins',
117
+ 'Select_range' => 'selectRange',
118
+ 'Select_range_check' => 'selectRange_check',
119
+ 'Select_scan' => 'selectScan',
120
+ 'Slow_queries' => 'slowQueries'
121
+ },
122
+ 'querycache' => {
123
+ 'Qcache_queries_in_cache' => 'queriesInCache',
124
+ 'Qcache_hits' => 'cacheHits',
125
+ 'Qcache_inserts' => 'inserts',
126
+ 'Qcache_not_cached' => 'notCached',
127
+ 'Qcache_lowmem_prunes' => 'lowMemPrunes'
128
+ },
129
+ 'commands' => {
130
+ 'Com_admin_commands' => 'admin_commands',
131
+ 'Com_begin' => 'begin',
132
+ 'Com_change_db' => 'change_db',
133
+ 'Com_commit' => 'commit',
134
+ 'Com_create_table' => 'create_table',
135
+ 'Com_drop_table' => 'drop_table',
136
+ 'Com_show_keys' => 'show_keys',
137
+ 'Com_delete' => 'delete',
138
+ 'Com_create_db' => 'create_db',
139
+ 'Com_grant' => 'grant',
140
+ 'Com_show_processlist' => 'show_processlist',
141
+ 'Com_flush' => 'flush',
142
+ 'Com_insert' => 'insert',
143
+ 'Com_purge' => 'purge',
144
+ 'Com_replace' => 'replace',
145
+ 'Com_rollback' => 'rollback',
146
+ 'Com_select' => 'select',
147
+ 'Com_set_option' => 'set_option',
148
+ 'Com_show_binlogs' => 'show_binlogs',
149
+ 'Com_show_databases' => 'show_databases',
150
+ 'Com_show_fields' => 'show_fields',
151
+ 'Com_show_status' => 'show_status',
152
+ 'Com_show_tables' => 'show_tables',
153
+ 'Com_show_variables' => 'show_variables',
154
+ 'Com_update' => 'update',
155
+ 'Com_drop_db' => 'drop_db',
156
+ 'Com_revoke' => 'revoke',
157
+ 'Com_drop_user' => 'drop_user',
158
+ 'Com_show_grants' => 'show_grants',
159
+ 'Com_lock_tables' => 'lock_tables',
160
+ 'Com_show_create_table' => 'show_create_table',
161
+ 'Com_unlock_tables' => 'unlock_tables',
162
+ 'Com_alter_table' => 'alter_table'
163
+ },
164
+ 'counters' => {
165
+ 'Handler_write' => 'handlerWrite',
166
+ 'Handler_update' => 'handlerUpdate',
167
+ 'Handler_delete' => 'handlerDelete',
168
+ 'Handler_read_first' => 'handlerRead_first',
169
+ 'Handler_read_key' => 'handlerRead_key',
170
+ 'Handler_read_next' => 'handlerRead_next',
171
+ 'Handler_read_prev' => 'handlerRead_prev',
172
+ 'Handler_read_rnd' => 'handlerRead_rnd',
173
+ 'Handler_read_rnd_next' => 'handlerRead_rnd_next',
174
+ 'Handler_commit' => 'handlerCommit',
175
+ 'Handler_rollback' => 'handlerRollback',
176
+ 'Handler_savepoint' => 'handlerSavepoint',
177
+ 'Handler_savepoint_rollback' => 'handlerSavepointRollback'
178
+ },
179
+ 'innodb' => {
180
+ 'Innodb_buffer_pool_pages_total' => 'bufferTotal_pages',
181
+ 'Innodb_buffer_pool_pages_free' => 'bufferFree_pages',
182
+ 'Innodb_buffer_pool_pages_dirty' => 'bufferDirty_pages',
183
+ 'Innodb_buffer_pool_pages_data' => 'bufferUsed_pages',
184
+ 'Innodb_page_size' => 'pageSize',
185
+ 'Innodb_pages_created' => 'pagesCreated',
186
+ 'Innodb_pages_read' => 'pagesRead',
187
+ 'Innodb_pages_written' => 'pagesWritten',
188
+ 'Innodb_row_lock_current_waits' => 'currentLockWaits',
189
+ 'Innodb_row_lock_waits' => 'lockWaitTimes',
190
+ 'Innodb_row_lock_time' => 'rowLockTime',
191
+ 'Innodb_data_reads' => 'fileReads',
192
+ 'Innodb_data_writes' => 'fileWrites',
193
+ 'Innodb_data_fsyncs' => 'fileFsyncs',
194
+ 'Innodb_log_writes' => 'logWrites',
195
+ 'Innodb_rows_updated' => 'rowsUpdated',
196
+ 'Innodb_rows_read' => 'rowsRead',
197
+ 'Innodb_rows_deleted' => 'rowsDeleted',
198
+ 'Innodb_rows_inserted' => 'rowsInserted'
199
+ },
200
+ 'configuration' => {
201
+ 'max_connections' => 'MaxConnections',
202
+ 'Max_prepared_stmt_count' => 'MaxPreparedStmtCount'
203
+ }
204
+ }
205
+
206
+ config[:host].split(' ').each do |mysql_host|
207
+ mysql_shorthostname = mysql_host.split('.')[0]
208
+ if config[:ini]
209
+ ini = IniFile.load(config[:ini])
210
+ section = ini[config[:ini_section]]
211
+ db_user = section['user']
212
+ db_pass = section['password']
213
+ else
214
+ db_user = config[:username]
215
+ db_pass = config[:password]
216
+ end
217
+ begin
218
+ mysql = Mysql.new(mysql_host, db_user, db_pass, nil, config[:port], config[:socket])
219
+
220
+ results = mysql.query('SHOW GLOBAL STATUS')
221
+ rescue => e
222
+ puts e.message
223
+ end
224
+
225
+ results.each_hash do |row|
226
+ metrics.each do |category, var_mapping|
227
+ if var_mapping.key?(row['Variable_name'])
228
+ output "#{config[:scheme]}.#{mysql_shorthostname}.#{category}.#{var_mapping[row['Variable_name']]}", row['Value']
229
+ end
230
+ end
231
+ end
232
+
233
+ begin
234
+ slave_results = mysql.query('SHOW SLAVE STATUS')
235
+ # should return a single element array containing one hash
236
+ # #YELLOW
237
+ slave_results.fetch_hash.each_pair do |key, value|
238
+ if metrics['general'].include?(key)
239
+ # Replication lag being null is bad, very bad, so negativate it here
240
+ value = -1 if key == 'Seconds_Behind_Master' && value.nil?
241
+ output "#{config[:scheme]}.#{mysql_shorthostname}.general.#{metrics['general'][key]}", value
242
+ end
243
+ end
244
+ rescue => e
245
+ puts "Error querying slave status: #{e}" if config[:verbose]
246
+ end
247
+
248
+ begin
249
+ variables_results = mysql.query('SHOW GLOBAL VARIABLES')
250
+
251
+ category = 'configuration'
252
+ variables_results.each_hash do |row|
253
+ metrics[category].each do |metric, desc|
254
+ if metric.casecmp(row['Variable_name']) == 0
255
+ output "#{config[:scheme]}.#{mysql_shorthostname}.#{category}.#{desc}", row['Value']
256
+ end
257
+ end
258
+ end
259
+ rescue => e
260
+ puts e.message
261
+ end
262
+
263
+ mysql.close if mysql
264
+ end
265
+
266
+ ok
267
+ end
268
+ end
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # MySQL Select Count Metric
4
+ #
5
+ # Creates a graphite-formatted metric for the first value of a result set from a MySQL query.
6
+ #
7
+ # Copyright 2017 Andrew Thal <athal7@me.com>
8
+ # Copyright 2018 Tibor Nagy <nagyt@hu.inter.net>
9
+ #
10
+ # Released under the same terms as Sensu (the MIT license); see LICENSE
11
+ # for details.
12
+
13
+ require 'sensu-plugin/metric/cli'
14
+ require 'mysql'
15
+ require 'inifile'
16
+ require 'json'
17
+
18
+ class MysqlQueryCountMetric < Sensu::Plugin::Metric::CLI::Graphite
19
+ option :host,
20
+ short: '-h HOST',
21
+ long: '--host HOST',
22
+ description: 'MySQL Host to connect to',
23
+ required: true
24
+
25
+ option :port,
26
+ short: '-P PORT',
27
+ long: '--port PORT',
28
+ description: 'MySQL Port to connect to',
29
+ proc: proc(&:to_i),
30
+ default: 3306
31
+
32
+ option :username,
33
+ short: '-u USERNAME',
34
+ long: '--user USERNAME',
35
+ description: 'MySQL Username'
36
+
37
+ option :password,
38
+ short: '-p PASSWORD',
39
+ long: '--pass PASSWORD',
40
+ description: 'MySQL password'
41
+
42
+ option :database,
43
+ short: '-d DATABASE',
44
+ long: '--database DATABASE',
45
+ description: 'MySQL database',
46
+ default: ''
47
+
48
+ option :ini,
49
+ short: '-i',
50
+ long: '--ini VALUE',
51
+ description: 'My.cnf ini file'
52
+
53
+ option :ini_section,
54
+ description: 'Section in my.cnf ini file',
55
+ long: '--ini-section VALUE',
56
+ default: 'client'
57
+
58
+ option :socket,
59
+ short: '-S SOCKET',
60
+ long: '--socket SOCKET',
61
+ description: 'MySQL Unix socket to connect to'
62
+
63
+ option :name,
64
+ short: '-n NAME',
65
+ long: '--name NAME',
66
+ description: 'Metric name for a configured handler',
67
+ default: 'mysql.query_count'
68
+
69
+ option :query,
70
+ short: '-q SELECT_COUNT_QUERY',
71
+ long: '--query SELECT_COUNT_QUERY',
72
+ description: 'Queries to execute in JSON',
73
+ required: true
74
+
75
+ def run
76
+ if config[:ini]
77
+ ini = IniFile.load(config[:ini])
78
+ section = ini[config[:ini_section]]
79
+ db_user = section['user']
80
+ db_pass = section['password']
81
+ else
82
+ db_user = config[:username]
83
+ db_pass = config[:password]
84
+ end
85
+
86
+ begin
87
+ query_hash = ::JSON.parse config[:query]
88
+ rescue ::JSON::ParserError => e
89
+ critical "JSON.parse error: #{e}"
90
+ end
91
+
92
+ # traverse all SQL
93
+ query_hash.each do |key, sql|
94
+ raise "invalid query : #{sql}" unless sql =~ /^select\s+count\(\s*\*\s*\)/i
95
+
96
+ db = Mysql.real_connect(config[:host], db_user, db_pass, config[:database], config[:port], config[:socket])
97
+ count = db.query(sql).fetch_row[0].to_i
98
+
99
+ output "#{config[:name]}.#{key}", count
100
+ end
101
+
102
+ ok
103
+
104
+ rescue Mysql::Error => e
105
+ errstr = "Error code: #{e.errno} Error message: #{e.error}"
106
+ critical "#{errstr} SQLSTATE: #{e.sqlstate}" if e.respond_to?('sqlstate')
107
+
108
+ rescue StandardError => e
109
+ critical "unhandled exception: #{e}"
110
+
111
+ end
112
+ end