sensu-plugins-mysql-checks 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b5fd0d7edc5bb266573a286dde1620c2ce3c3b81
4
+ data.tar.gz: 22b3303c6b3e2794f2c033a6f92a458d4174c080
5
+ SHA512:
6
+ metadata.gz: 6f7829206dd1bd32c4451a7b7d72ee0ccd3841b966936aabba345f70ccd2c9ae89d1fe93b37e2da0dc172c490c926224dba2a20fa661a5da3dc1b7749c0d7609
7
+ data.tar.gz: 3452e50d14dc9d7bb9968172b432b92702a90a8dd3414c2ef297c2991e9c966e95545e0218b28847ec4c30e837941f3caf49ca08df5112bcb13d20be002b9873
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Change Log
2
+ This project adheres to [Semantic Versioning](http://semver.org/).
3
+
4
+ This CHANGELOG follows the format listed at [Keep A Changelog](http://keepachangelog.com/)
5
+
6
+ ## Unreleased
7
+
8
+ ## [0.0.1] - 2016-01-18
9
+ ### Added
10
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2015 Matteo Cerutti
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Sensu plugin for monitoring MySQL
2
+
3
+ A sensu plugin to monitor MySQL, including slave replication.
4
+
5
+ The plugin generates multiple OK/WARN/CRIT/UNKNOWN events via the sensu client socket (https://sensuapp.org/docs/latest/clients#client-socket-input).
6
+
7
+ What is currently supported:
8
+
9
+ * Active connections check
10
+ * Max used connections check
11
+ * Slave IO/SQL threads running checks
12
+ * Slave replication lag check
13
+ * Uptime check
14
+ * Operational check (up/down)
15
+
16
+ ## Usage
17
+
18
+ The plugin accepts the following command line options:
19
+
20
+ ```
21
+ -c, --config <PATH> Optional configuration file (default: ./mysql.json)
22
+ --crit-conn <PERCENTAGE> Critical if PERCENTAGE exceeds the current number of open connections in relation to max connections (default: 90)
23
+ --crit-slave-lag <SECONDS> Critical if SECONDS exceeds the current slave replication lag (default: 120)
24
+ --dryrun Do not send events to sensu client socket
25
+ --handlers <HANDLER> Comma separated list of handlers
26
+ -H, --host <HOST> MySQL host (default: localhost)
27
+ --password <PASSWORD> MySQL password
28
+ -p, --port <PORT> MySQL port (default: 3306)
29
+ --uptime <UPTIME> Uptime in seconds
30
+ -u, --user <USER> MySQL user
31
+ --warn-conn <PERCENTAGE> Warn if PERCENTAGE exceeds the current number of open connections in relation to max connections (default: 80)
32
+ --warn-slave-lag <SECONDS> Warn if SECONDS exceeds the current slave replication lag (default: 60)
33
+ ```
34
+
35
+ For security reasons, it's recommended you placed the MySQL credentials in the JSON configuration file as follows:
36
+
37
+ ```
38
+ {
39
+ "username": "sensu",
40
+ "password": "secret"
41
+ }
42
+ ```
43
+
44
+ ## Author
45
+ Matteo Cerutti - <matteo.cerutti@hotmail.co.uk>
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # check-mysql.rb
4
+ #
5
+ # Author: Matteo Cerutti <matteo.cerutti@hotmail.co.uk>
6
+ #
7
+
8
+ require 'sensu-plugin/check/cli'
9
+ require 'mysql2'
10
+ require 'json'
11
+
12
+ class CheckMySQL < Sensu::Plugin::Check::CLI
13
+ option :host,
14
+ :description => "MySQL host (default: localhost)",
15
+ :short => "-H <HOST>",
16
+ :long => "--host <HOST>",
17
+ :default => "localhost"
18
+
19
+ option :port,
20
+ :description => "MySQL port (default: 3306)",
21
+ :short => "-p <PORT>",
22
+ :long => "--port <PORT>",
23
+ :proc => proc(&:to_i),
24
+ :default => 3306
25
+
26
+ option :username,
27
+ :description => "MySQL user",
28
+ :short => "-u <USER>",
29
+ :long => "--user <USER>",
30
+ :default => nil
31
+
32
+ option :password,
33
+ :description => "MySQL password",
34
+ :long => "--password <PASSWORD>",
35
+ :default => nil
36
+
37
+ option :config_file,
38
+ :description => "Optional configuration file (default: #{File.dirname(__FILE__)}/mysql.json)",
39
+ :short => "-c <PATH>",
40
+ :long => "--config <PATH>",
41
+ :default => File.dirname(__FILE__) + "/mysql.json"
42
+
43
+ option :uptime,
44
+ :description => "Uptime in seconds",
45
+ :long => "--uptime <UPTIME>",
46
+ :proc => proc(&:to_i),
47
+ :default => 300
48
+
49
+ option :warn_conn,
50
+ :description => "Warn if PERCENTAGE exceeds the current number of open connections in relation to max connections (default: 80)",
51
+ :long => "--warn-conn <PERCENTAGE>",
52
+ :proc => proc(&:to_i),
53
+ :default => 80
54
+
55
+ option :crit_conn,
56
+ :description => "Critical if PERCENTAGE exceeds the current number of open connections in relation to max connections (default: 90)",
57
+ :long => "--crit-conn <PERCENTAGE>",
58
+ :proc => proc(&:to_i),
59
+ :default => 90
60
+
61
+ option :warn_slave_lag,
62
+ :description => "Warn if SECONDS exceeds the current slave replication lag (default: 60)",
63
+ :long => "--warn-slave-lag <SECONDS>",
64
+ :proc => proc(&:to_i),
65
+ :default => 60
66
+
67
+ option :crit_slave_lag,
68
+ :description => "Critical if SECONDS exceeds the current slave replication lag (default: 120)",
69
+ :long => "--crit-slave-lag <SECONDS>",
70
+ :proc => proc(&:to_i),
71
+ :default => 120
72
+
73
+ option :handlers,
74
+ :description => "Comma separated list of handlers",
75
+ :long => "--handlers <HANDLER>",
76
+ :proc => proc { |s| s.split(',') },
77
+ :default => []
78
+
79
+ option :dryrun,
80
+ :description => "Do not send events to sensu client socket",
81
+ :long => "--dryrun",
82
+ :boolean => true,
83
+ :default => false
84
+
85
+ def initialize()
86
+ super
87
+
88
+ if File.exists?(config[:config_file])
89
+ config.merge!(JSON.parse(File.read(config[:config_file]), :symbolize_names => true))
90
+ end
91
+
92
+ raise "MySQL user is required" if config[:username].nil?
93
+ raise "MySQL password is required" if config[:password].nil?
94
+
95
+ begin
96
+ @client = Mysql2::Client.new(:host => config[:host], :port => config[:port], :username => config[:username], :password => config[:password])
97
+ rescue
98
+ critical("MySQL server is down (#{$!})")
99
+ end
100
+ end
101
+
102
+ def send_client_socket(data)
103
+ if config[:dryrun]
104
+ puts data.inspect
105
+ else
106
+ sock = UDPSocket.new
107
+ sock.send(data + "\n", 0, "127.0.0.1", 3030)
108
+ end
109
+ end
110
+
111
+ def send_ok(check_name, msg)
112
+ event = {"name" => check_name, "status" => 0, "output" => "#{self.class.name} OK: #{msg}", "handlers" => config[:handlers]}
113
+ send_client_socket(event.to_json)
114
+ end
115
+
116
+ def send_warning(check_name, msg)
117
+ event = {"name" => check_name, "status" => 1, "output" => "#{self.class.name} WARNING: #{msg}", "handlers" => config[:handlers]}
118
+ send_client_socket(event.to_json)
119
+ end
120
+
121
+ def send_critical(check_name, msg)
122
+ event = {"name" => check_name, "status" => 2, "output" => "#{self.class.name} CRITICAL: #{msg}", "handlers" => config[:handlers]}
123
+ send_client_socket(event.to_json)
124
+ end
125
+
126
+ def send_unknown(check_name, msg)
127
+ event = {"name" => check_name, "status" => 3, "output" => "#{self.class.name} UNKNOWN: #{msg}", "handlers" => config[:handlers]}
128
+ send_client_socket(event.to_json)
129
+ end
130
+
131
+ def get_uptime()
132
+ res = @client.query("SHOW GLOBAL STATUS LIKE 'Uptime'")
133
+ res.first['Value'].to_i
134
+ end
135
+
136
+ def get_threads_connected()
137
+ res = @client.query("SHOW GLOBAL STATUS LIKE 'Threads_connected'")
138
+ res.first['Value'].to_i
139
+ end
140
+
141
+ def get_max_connections_setting()
142
+ res = @client.query("SHOW VARIABLES LIKE 'max_connections'")
143
+ res.first['Value'].to_i
144
+ end
145
+
146
+ def get_max_used_connections()
147
+ res = @client.query("SHOW GLOBAL STATUS LIKE 'Max_used_connections'")
148
+ res.first['Value'].to_i
149
+ end
150
+
151
+ def get_slave_status()
152
+ res = @client.query("SHOW SLAVE STATUS")
153
+ res.first
154
+ end
155
+
156
+ def run
157
+ check_name = "mysql-active_connections"
158
+ threads_connected = get_threads_connected()
159
+ max_connections = get_max_connections_setting()
160
+ if threads_connected >= config[:crit_conn] * max_connections / 100
161
+ send_critical(check_name, "Too many active connections - Current: #{threads_connected} (>= #{config[:crit_conn] * max_connections / 100})")
162
+ elsif threads_connected >= config[:warn_conn] * max_connections / 100
163
+ send_warning(check_name, "High number of active connections - Current: #{threads_connected} (>= #{config[:warn_conn] * max_connections / 100})")
164
+ else
165
+ send_ok(check_name, "#{threads_connected} active connections (< #{config[:warn_conn] * max_connections / 100})")
166
+ end
167
+
168
+ check_name = "mysql-max_used_connections"
169
+ max_used = get_max_used_connections()
170
+ if max_used >= config[:crit_conn] * max_connections / 100
171
+ send_warning(check_name, "MySQL server max used connections reached #{max_used} (>= #{config[:crit_conn] * max_connections / 100})")
172
+ else
173
+ send_ok(check_name, "MySQL server max used connections is #{max_used} (< #{config[:crit_conn] * max_connections / 100})")
174
+ end
175
+
176
+ # is replication enabled?
177
+ slave = get_slave_status()
178
+ if slave.has_key?('Slave_IO_Running')
179
+ check_name = "mysql-slave-io_thread"
180
+ if slave['Slave_IO_Running'].downcase != 'yes'
181
+ send_critical(check_name, "MySQL slave IO thread not running (Errno: #{slave['Last_IO_Errno']}, Error: #{slave['Last_IO_Error']})")
182
+ else
183
+ send_ok(check_name, "MySQL slave IO thread is running")
184
+ end
185
+ end
186
+
187
+ if slave.has_key?('Slave_SQL_Running')
188
+ check_name = "mysql-slave-sql_thread"
189
+ if slave['Slave_SQL_Running'].downcase != 'yes'
190
+ send_critical(check_name, "MySQL slave SQL thread not running (Errno: #{slave['Last_SQL_Errno']}, Error: #{slave['Last_SQL_Error']})")
191
+ else
192
+ send_ok(check_name, "MySQL slave SQL thread is running")
193
+ end
194
+ end
195
+
196
+ if slave.has_key?('Last_Errno')
197
+ check_name = "mysql-slave-last_errno"
198
+ if slave['Last_Errno'] != 0
199
+ send_critical(check_name, "MySQL slave replication has failed with #{slave['Last_errno']} error (#{slave['Last_Error']})")
200
+ else
201
+ send_ok(check_name, "MySQL slave replication has no errors")
202
+ end
203
+ end
204
+
205
+ if slave.has_key?('Seconds_Behind_Master')
206
+ check_name = "mysql-slave-lag"
207
+ msg = "MySQL slave replication is #{slave['Seconds_Behind_Master']}s behind master"
208
+ if slave['Seconds_Behind_Master'] >= config[:crit_slave_lag]
209
+ send_critical(check_name, "#{msg} (>= #{config[:crit_slave_lag]}s)")
210
+ elsif slave['Seconds_Behind_Master'] >= config[:warn_slave_lag]
211
+ send_warning(check_name, "#{msg} (>= #{config[:warn_slave_lag]}s)")
212
+ else
213
+ send_ok(check_name, "MySQL slave replication is in sync")
214
+ end
215
+ end
216
+
217
+ uptime = get_uptime()
218
+ if uptime <= config[:uptime]
219
+ warning("MySQL server restarted #{uptime}s ago")
220
+ end
221
+
222
+ ok("MySQL server is running")
223
+ end
224
+ end
@@ -0,0 +1,9 @@
1
+ module SensuPluginsMySQLChecks
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ VER_STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'sensu-plugins-mysql-checks/version'
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu-plugins-mysql-checks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matteo Cerutti
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sensu-plugin
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: mysql2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.4.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.4.2
41
+ description: This plugin provides facilities for monitoring MySQL
42
+ email: "<matteo.cerutti@hotmail.co.uk>"
43
+ executables:
44
+ - check-mysql.rb
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - LICENSE
50
+ - README.md
51
+ - bin/check-mysql.rb
52
+ - lib/sensu-plugins-mysql-checks.rb
53
+ - lib/sensu-plugins-mysql-checks/version.rb
54
+ homepage: https://github.com/m4ce/sensu-plugins-mysql-checks
55
+ licenses:
56
+ - MIT
57
+ metadata:
58
+ maintainer: "@m4ce"
59
+ development_status: active
60
+ production_status: stable
61
+ release_draft: 'false'
62
+ release_prerelease: 'false'
63
+ post_install_message: You can use the embedded Ruby by setting EMBEDDED_RUBY=true
64
+ in /etc/default/sensu
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.9.3
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 2.4.5.1
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Sensu plugins for monitoring MySQL
84
+ test_files: []