mysql_health 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +63 -0
- data/Rakefile +2 -0
- data/bin/mysql_health +30 -0
- data/lib/mysql_health.rb +27 -0
- data/lib/mysql_health/command_line.rb +199 -0
- data/lib/mysql_health/health.rb +181 -0
- data/lib/mysql_health/server.rb +76 -0
- data/lib/mysql_health/version.rb +22 -0
- data/mysql_health.gemspec +23 -0
- data/redhat/mysql_health.initrc +117 -0
- data/redhat/rubygem-mysql_health.spec +73 -0
- metadata +161 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 osterman
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# MysqlHealth
|
2
|
+
|
3
|
+
MySQL Health is a standalone HTTP server that will respond with a 200 status code when MySQL is operating as expected.
|
4
|
+
|
5
|
+
This script is intended to be used in conjuction with HAProxy "option httpchk" for a TCP load balancer distributing load across mysql servers.
|
6
|
+
|
7
|
+
## FAQs
|
8
|
+
|
9
|
+
1. If you get the error "caught DBI::InterfaceError exception 'Could not load driver (uninitialized constant MysqlError)'" on OSX, try doing this:
|
10
|
+
`export DYLD_LIBRARY_PATH="/usr/local/mysql/lib:$DYLD_LIBRARY_PATH"`
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'mysql_health'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install mysql_health
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Usage: mysql_health options
|
30
|
+
--check:master Master health check
|
31
|
+
--check:slave Slave health check
|
32
|
+
--check:allow-overlapping Allow overlapping health checks (default: false)
|
33
|
+
--check:interval INTERVAL Check health every INTERVAL (default: 10s)
|
34
|
+
--check:delay DELAY Delay health checks for INTERVAL (default: 0s)
|
35
|
+
--check:dsn DSN MySQL DSN (default: DBI:Mysql:mysql:localhost)
|
36
|
+
--check:username USERNAME MySQL Username (default: root)
|
37
|
+
--check:password PASSWORD MySQL Password (default: )
|
38
|
+
-l, --server:listen ADDR Server listen address (default: 0.0.0.0)
|
39
|
+
-p, --server:port PORT Server listen port (default: 3305)
|
40
|
+
-d, --server:daemonize Daemonize the process (default: false)
|
41
|
+
-P, --server:pid-file PID-FILE Pid-File to save the process id (default: false)
|
42
|
+
--log:level LEVEL Logging level (default: INFO)
|
43
|
+
--log:file FILE Write logs to FILE (default: STDERR)
|
44
|
+
--log:age DAYS Rotate logs after DAYS pass (default: 7)
|
45
|
+
--log:size SIZE Rotate logs after the grow past SIZE bytes (default: 10485760)
|
46
|
+
|
47
|
+
## Examples
|
48
|
+
|
49
|
+
Start the server on port 1234 and check the status of the slave every 30 seconds:
|
50
|
+
|
51
|
+
mysql_health --check:slave --check:interval 30 --server:port 1234
|
52
|
+
|
53
|
+
Start the server on port 1234 and check the status of the master every 30 seconds:
|
54
|
+
|
55
|
+
mysql_health --check:master --check:interval 30 --server:port 1234
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
1. Fork it
|
60
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
61
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
62
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
63
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/mysql_health
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
4
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
5
|
+
#
|
6
|
+
# This file is part of mysql_health.
|
7
|
+
#
|
8
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# mysql_health is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU General Public License
|
19
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
#
|
21
|
+
|
22
|
+
$:.unshift(File.expand_path('.')) # Ruby 1.9 doesn't have . in the load path...
|
23
|
+
$:.push(File.expand_path('lib/'))
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require 'mysql_health'
|
27
|
+
|
28
|
+
command_line = MysqlHealth::CommandLine.new
|
29
|
+
command_line.execute
|
30
|
+
|
data/lib/mysql_health.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
3
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
4
|
+
#
|
5
|
+
# This file is part of mysql_health.
|
6
|
+
#
|
7
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# mysql_health is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
require 'mysql_health/version'
|
21
|
+
require 'mysql_health/health'
|
22
|
+
require 'mysql_health/server'
|
23
|
+
require 'mysql_health/command_line'
|
24
|
+
|
25
|
+
module MysqlHealth
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
#
|
2
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
3
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
4
|
+
#
|
5
|
+
# This file is part of mysql_health.
|
6
|
+
#
|
7
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# mysql_health is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
require 'logger'
|
21
|
+
require 'optparse'
|
22
|
+
|
23
|
+
module MysqlHealth
|
24
|
+
@@health = nil
|
25
|
+
@@log = nil
|
26
|
+
|
27
|
+
def self.health
|
28
|
+
@@health
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.health=(health)
|
32
|
+
@@health = health
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.log
|
36
|
+
@@log
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.log=(log)
|
40
|
+
@@log = log
|
41
|
+
end
|
42
|
+
|
43
|
+
class ArgumentException < Exception; end
|
44
|
+
class CommandLine
|
45
|
+
attr_accessor :options
|
46
|
+
def initialize
|
47
|
+
@options = {}
|
48
|
+
@options[:server] = {}
|
49
|
+
@options[:check] = {}
|
50
|
+
@options[:log] = {}
|
51
|
+
|
52
|
+
begin
|
53
|
+
@optparse = OptionParser.new do |opts|
|
54
|
+
opts.banner = "Usage: #{$0} options"
|
55
|
+
#
|
56
|
+
# Health check
|
57
|
+
#
|
58
|
+
@options[:check][:master] = false
|
59
|
+
opts.on( '--check:master', 'Master health check') do
|
60
|
+
@options[:check][:master] = true
|
61
|
+
end
|
62
|
+
|
63
|
+
@options[:check][:slave] = false
|
64
|
+
opts.on( '--check:slave', 'Slave health check') do |host|
|
65
|
+
@options[:check][:slave] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
@options[:check][:allow_overlapping] = false
|
69
|
+
opts.on( '--check:allow-overlapping', "Allow overlapping health checks (default: #{@options[:check][:allow_overlapping]})") do
|
70
|
+
@options[:check][:allow_overlapping] = true
|
71
|
+
end
|
72
|
+
|
73
|
+
@options[:check][:interval] = '10s'
|
74
|
+
opts.on( '--check:interval INTERVAL', "Check health every INTERVAL (default: #{@options[:check][:interval]})") do |interval|
|
75
|
+
@options[:check][:interval] = interval.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
@options[:check][:delay] = '0s'
|
79
|
+
opts.on( '--check:delay DELAY', "Delay health checks for INTERVAL (default: #{@options[:check][:delay]})") do |delay|
|
80
|
+
@options[:check][:delay] = interval.to_s
|
81
|
+
end
|
82
|
+
|
83
|
+
@options[:check][:dsn] ||= "DBI:Mysql:mysql:localhost"
|
84
|
+
opts.on( '--check:dsn DSN', "MySQL DSN (default: #{@options[:check][:dsn]})") do |dsn|
|
85
|
+
@options[:check][:dsn] = dsn.to_s
|
86
|
+
end
|
87
|
+
|
88
|
+
@options[:check][:username] ||= "root"
|
89
|
+
opts.on( '--check:username USERNAME', "MySQL Username (default: #{@options[:check][:username]})") do |username|
|
90
|
+
@options[:check][:username] = username.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
@options[:check][:password] ||= ""
|
94
|
+
opts.on( '--check:password PASSWORD', "MySQL Password (default: #{@options[:check][:password]})") do |password|
|
95
|
+
@options[:check][:password] = interval.to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
# Server
|
99
|
+
@options[:server][:listen] = '0.0.0.0'
|
100
|
+
opts.on( '-l', '--server:listen ADDR', "Server listen address (default: #{@options[:server][:listen]})") do |addr|
|
101
|
+
@options[:server][:addr] = host.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
@options[:server][:port] = 3305
|
105
|
+
opts.on( '-p', '--server:port PORT', "Server listen port (default: #{@options[:server][:port]})") do |port|
|
106
|
+
@options[:server][:port] = port.to_i
|
107
|
+
end
|
108
|
+
|
109
|
+
@options[:server][:daemonize] = false
|
110
|
+
opts.on( '-d', '--server:daemonize', "Daemonize the process (default: #{@options[:server][:daemonize]})") do
|
111
|
+
@options[:server][:daemonize] = true
|
112
|
+
end
|
113
|
+
|
114
|
+
@options[:server][:pid_file] = false
|
115
|
+
opts.on('-P', '--server:pid-file PID-FILE', "Pid-File to save the process id (default: #{@options[:server][:pid_file]})") do |pid_file|
|
116
|
+
@options[:server][:pid_file] = pid_file
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
#
|
121
|
+
# Logging
|
122
|
+
#
|
123
|
+
|
124
|
+
@options[:log][:level] = Logger::INFO
|
125
|
+
opts.on( '--log:level LEVEL', 'Logging level (default: INFO)' ) do|level|
|
126
|
+
@options[:log][:level] = Logger.const_get level.upcase
|
127
|
+
end
|
128
|
+
|
129
|
+
@options[:log][:file] = STDERR
|
130
|
+
opts.on( '--log:file FILE', 'Write logs to FILE (default: STDERR)' ) do|file|
|
131
|
+
@options[:log][:file] = File.open(file, File::WRONLY | File::APPEND | File::CREAT)
|
132
|
+
end
|
133
|
+
|
134
|
+
@options[:log][:age] = 7
|
135
|
+
opts.on( '--log:age DAYS', "Rotate logs after DAYS pass (default: #{@options[:log][:age]})" ) do|days|
|
136
|
+
@options[:log][:age] = days.to_i
|
137
|
+
end
|
138
|
+
|
139
|
+
@options[:log][:size] = 1024*1024*10
|
140
|
+
opts.on( '--log:size SIZE', "Rotate logs after the grow past SIZE bytes (default: #{@options[:log][:size]})" ) do |size|
|
141
|
+
@options[:log][:size] = size.to_i
|
142
|
+
end
|
143
|
+
end
|
144
|
+
@optparse.parse!
|
145
|
+
|
146
|
+
raise ArgumentException.new("No action specified") if @options[:check][:master] == false && @options[:check][:slave] == false
|
147
|
+
@log = Logger.new(@options[:log][:file], @options[:log][:age], @options[:log][:size])
|
148
|
+
@log.level = @options[:log][:level]
|
149
|
+
|
150
|
+
daemonize if @options[:server][:daemonize]
|
151
|
+
write_pid_file if @options[:server][:pid_file]
|
152
|
+
|
153
|
+
MysqlHealth.log = @log
|
154
|
+
MysqlHealth.health = Health.new(@options[:check])
|
155
|
+
|
156
|
+
rescue ArgumentException => e
|
157
|
+
puts e.message
|
158
|
+
puts @optparse
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def daemonize
|
164
|
+
# Become a daemon
|
165
|
+
if RUBY_VERSION < "1.9"
|
166
|
+
exit if fork
|
167
|
+
Process.setsid
|
168
|
+
exit if fork
|
169
|
+
Dir.chdir "/"
|
170
|
+
STDIN.reopen "/dev/null"
|
171
|
+
STDOUT.reopen "/dev/null", "a"
|
172
|
+
STDERR.reopen "/dev/null", "a"
|
173
|
+
else
|
174
|
+
Process.daemon
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def write_pid_file
|
179
|
+
@log.debug("writing pid file #{@options[:server][:pid_file]}")
|
180
|
+
File.open(@options[:server][:pid_file], 'w') do |f|
|
181
|
+
f.write(Process.pid)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def execute
|
186
|
+
begin
|
187
|
+
::EM.run do
|
188
|
+
::EM.start_server @options[:server][:listen], @options[:server][:port], Server
|
189
|
+
end
|
190
|
+
rescue ArgumentException => e
|
191
|
+
@log.fatal(e.message)
|
192
|
+
rescue Interrupt => e
|
193
|
+
@log.info("exiting...")
|
194
|
+
rescue Exception => e
|
195
|
+
@log.fatal(e.message + e.backtrace.join("\n"))
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
#
|
2
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
3
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
4
|
+
#
|
5
|
+
# This file is part of mysql_health.
|
6
|
+
#
|
7
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# mysql_health is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
require 'dbi'
|
21
|
+
require 'mysql'
|
22
|
+
require 'json'
|
23
|
+
require 'rufus-scheduler'
|
24
|
+
|
25
|
+
module MysqlHealth
|
26
|
+
class Health
|
27
|
+
@master_status = nil
|
28
|
+
@slave_status = nil
|
29
|
+
@scheduler = nil
|
30
|
+
@options = nil
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
@options = options
|
34
|
+
|
35
|
+
@mutex = Mutex.new
|
36
|
+
@scheduler = Rufus::Scheduler.start_new
|
37
|
+
def @scheduler.handle_exception(job, e)
|
38
|
+
MysqlHealth.log.error "job #{job.job_id} caught #{e.class} exception '#{e}' #{e.backtrace.join("\n")}"
|
39
|
+
end
|
40
|
+
|
41
|
+
if options[:master]
|
42
|
+
master_status = {}
|
43
|
+
master_status[:status] = 503
|
44
|
+
master_status[:content] = "Health of master not yet determined\n"
|
45
|
+
self.master_status=(master_status)
|
46
|
+
@scheduler.every options[:interval], :allow_overlapping => options[:allow_overlapping], :first_in => options[:delay] do
|
47
|
+
check_master
|
48
|
+
end
|
49
|
+
else
|
50
|
+
master_status = {}
|
51
|
+
master_status[:status] = '501 Not Enabled'
|
52
|
+
master_status[:content] = "Health of master not enabled\n"
|
53
|
+
self.master_status=(master_status)
|
54
|
+
end
|
55
|
+
|
56
|
+
if options[:slave]
|
57
|
+
slave_status = {}
|
58
|
+
slave_status[:status] = '501 Not Enabled'
|
59
|
+
slave_status[:content] = "Health of slave not yet determined\n"
|
60
|
+
self.slave_status=(slave_status)
|
61
|
+
@scheduler.every options[:interval], :allow_overlapping => options[:allow_overlapping], :first_in => options[:delay] do
|
62
|
+
check_slave
|
63
|
+
end
|
64
|
+
else
|
65
|
+
slave_status = {}
|
66
|
+
slave_status[:status] = '501 Not Enabled'
|
67
|
+
slave_status[:content] = "Health of slave not enabled\n"
|
68
|
+
self.slave_status=(slave_status)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def master_status=(response)
|
73
|
+
@mutex.synchronize do
|
74
|
+
MysqlHealth.log.info("master status: #{response[:status]}")
|
75
|
+
@master_status = response
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def master_status
|
80
|
+
master_status = nil
|
81
|
+
@mutex.synchronize do
|
82
|
+
master_status = @master_status
|
83
|
+
end
|
84
|
+
return master_status
|
85
|
+
end
|
86
|
+
|
87
|
+
def slave_status=(response)
|
88
|
+
@mutex.synchronize do
|
89
|
+
MysqlHealth.log.info("slave status: #{response[:status]}")
|
90
|
+
@slave_status = response
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def slave_status
|
95
|
+
slave_status = nil
|
96
|
+
@mutex.synchronize do
|
97
|
+
slave_status = @slave_status
|
98
|
+
end
|
99
|
+
return slave_status
|
100
|
+
end
|
101
|
+
|
102
|
+
def read_only?(dbh)
|
103
|
+
variables = dbh.select_all("SHOW VARIABLES WHERE Variable_name = 'read_only' AND Value = 'ON'")
|
104
|
+
return (variables.length == 1)
|
105
|
+
end
|
106
|
+
|
107
|
+
def check_master
|
108
|
+
MysqlHealth.log.debug("check_master")
|
109
|
+
|
110
|
+
# connect to the MySQL server
|
111
|
+
dbh = DBI.connect(@options[:dsn], @options[:username], @options[:password])
|
112
|
+
|
113
|
+
response = {}
|
114
|
+
response[:content_type] = 'text/plain'
|
115
|
+
|
116
|
+
status = {}
|
117
|
+
dbh.select_all('SHOW STATUS') do |row|
|
118
|
+
status[row[0].downcase.to_sym] = row[1]
|
119
|
+
end
|
120
|
+
mysqladmin_status = "Uptime: %s Threads: %s Questions: %s Slow queries: %s Opens: %s Flush tables: %s Open tables: %s Queries per second avg: %.3f\n" %
|
121
|
+
[ status[:uptime], status[:threads_running], status[:questions], status[:slow_queries], status[:opened_tables], status[:flush_commands], status[:open_tables], status[:queries].to_i/status[:uptime].to_i]
|
122
|
+
if status.length > 0
|
123
|
+
if read_only?(dbh)
|
124
|
+
response[:status] = '503 Service Read Only'
|
125
|
+
response[:content] = mysqladmin_status
|
126
|
+
else
|
127
|
+
response[:status] = '200 OK'
|
128
|
+
response[:content] = mysqladmin_status
|
129
|
+
end
|
130
|
+
else
|
131
|
+
response[:status] = '503 Service Unavailable'
|
132
|
+
response[:content] = mysqladmin_status
|
133
|
+
end
|
134
|
+
self.master_status=(response)
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_slave
|
138
|
+
MysqlHealth.log.debug("check_slave")
|
139
|
+
|
140
|
+
# connect to the MySQL server
|
141
|
+
dbh = DBI.connect(@options[:dsn], @options[:username], @options[:password])
|
142
|
+
|
143
|
+
response = {}
|
144
|
+
response[:content_type] = 'text/plain'
|
145
|
+
|
146
|
+
show_slave_status = []
|
147
|
+
status = {}
|
148
|
+
dbh.select_all('SHOW SLAVE STATUS') do |row|
|
149
|
+
status[row[0].downcase.to_sym] = row[1]
|
150
|
+
show_slave_status << "#{row[0]}: #{row[1]}"
|
151
|
+
end
|
152
|
+
|
153
|
+
if status.length > 0
|
154
|
+
seconds_behind_master = status[:seconds_behind_master]
|
155
|
+
|
156
|
+
# We return a "203 Non-Authoritative Information" when replication is shot. We don't want to reduce site performance, but still want to track that something is awry.
|
157
|
+
if seconds_behind_master.eql?('NULL')
|
158
|
+
response[:status] = '203 Slave Stopped'
|
159
|
+
response[:content] = status.to_json
|
160
|
+
response[:content_type] = 'application/json'
|
161
|
+
elsif seconds_behind_master.to_i > 60*30
|
162
|
+
response[:status] = '203 Slave Behind'
|
163
|
+
response[:content] = status.to_json
|
164
|
+
response[:content_type] = 'application/json'
|
165
|
+
elsif read_only?(dbh)
|
166
|
+
response[:status] = '200 OK ' + seconds_behind_master + ' Seconds Behind Master'
|
167
|
+
response[:content] = status.to_json
|
168
|
+
response[:content_type] = 'application/json'
|
169
|
+
else
|
170
|
+
response[:status] = '503 Service Unavailable'
|
171
|
+
response[:content] = status.to_json
|
172
|
+
response[:content_type] = 'application/json'
|
173
|
+
end
|
174
|
+
else
|
175
|
+
response[:status] = '503 Slave Not Configured'
|
176
|
+
response[:content] = show_slave_status.join("\n")
|
177
|
+
end
|
178
|
+
self.slave_status=(response)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
3
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
4
|
+
#
|
5
|
+
# This file is part of mysql_health.
|
6
|
+
#
|
7
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# mysql_health is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
require 'json'
|
21
|
+
require 'eventmachine'
|
22
|
+
require 'eventmachine_httpserver'
|
23
|
+
require 'evma_httpserver/response'
|
24
|
+
require 'forwardable'
|
25
|
+
|
26
|
+
module MysqlHealth
|
27
|
+
class Server < ::EM::Connection
|
28
|
+
include ::EM::HttpServer
|
29
|
+
|
30
|
+
def post_init
|
31
|
+
super
|
32
|
+
no_environment_strings
|
33
|
+
end
|
34
|
+
|
35
|
+
def http_response(data)
|
36
|
+
MysqlHealth.log.debug("http_response")
|
37
|
+
response = EventMachine::DelegatedHttpResponse.new(self)
|
38
|
+
if data.instance_of?(OpenStruct)
|
39
|
+
puts data.table.inspect
|
40
|
+
puts data.inspect
|
41
|
+
end
|
42
|
+
|
43
|
+
if data.nil?
|
44
|
+
response.status = '500 Server Error'
|
45
|
+
response.content = "Empty call to http_response\n"
|
46
|
+
else
|
47
|
+
data.each_pair do |k,v|
|
48
|
+
MysqlHealth.log.debug("#{k}=#{v}")
|
49
|
+
if k == :content_type
|
50
|
+
response.send(k, v)
|
51
|
+
else
|
52
|
+
response.send("#{k}=".to_sym, v)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
response
|
57
|
+
end
|
58
|
+
|
59
|
+
def process_http_request
|
60
|
+
response = nil
|
61
|
+
begin
|
62
|
+
case @http_path_info
|
63
|
+
when '/master_status'
|
64
|
+
response = http_response(MysqlHealth.health.master_status)
|
65
|
+
when '/slave_status'
|
66
|
+
response = http_response(MysqlHealth.health.slave_status)
|
67
|
+
else
|
68
|
+
response = http_response({:status => '501 Not Implemented'})
|
69
|
+
end
|
70
|
+
rescue Exception => e
|
71
|
+
response = http_response({:status => '500 Server Error', :content => e.message + "\n" + e.backtrace.join("\n")})
|
72
|
+
end
|
73
|
+
response.send_response
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface
|
3
|
+
# Copyright (C) 2012 Erik Osterman <e@osterman.com>
|
4
|
+
#
|
5
|
+
# This file is part of mysql_health.
|
6
|
+
#
|
7
|
+
# mysql_health is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# mysql_health is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with mysql_health. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
module MysqlHealth
|
21
|
+
VERSION = "0.5.0"
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mysql_health/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Erik Osterman"]
|
6
|
+
gem.email = ["e@osterman.com"]
|
7
|
+
gem.summary = %q{A service for monitoring MySQL and exposing its health through an HTTP interface.}
|
8
|
+
gem.description = %q{A service for monitoring MySQL and exposing its health through an HTTP interface for use with TCP load balancers (like haproxy) that support out-of-band health checks using HTTP.}
|
9
|
+
gem.homepage = "https://github.com/osterman/mysql_health"
|
10
|
+
gem.files = `git ls-files`.split($\)
|
11
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.name = "mysql_health"
|
14
|
+
gem.license = 'GPLv3'
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = MysqlHealth::VERSION
|
17
|
+
gem.add_runtime_dependency('rufus-scheduler', '>= 2.0.17')
|
18
|
+
gem.add_runtime_dependency('eventmachine', '>= 1.0.0.beta.4')
|
19
|
+
gem.add_runtime_dependency('eventmachine_httpserver', '>= 0.2.1')
|
20
|
+
gem.add_runtime_dependency('json', '>= 1.5.3')
|
21
|
+
gem.add_runtime_dependency('dbi', '>= 0.4.5')
|
22
|
+
gem.add_runtime_dependency('mysql', '>= 2.8.1')
|
23
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
#
|
3
|
+
# mysql_health - a service for monitoring MySQL and exposing its health through an HTTP interface.
|
4
|
+
#
|
5
|
+
# chkconfig: 345 99 99
|
6
|
+
# description: mysql_health
|
7
|
+
# processname: mysql_health
|
8
|
+
|
9
|
+
|
10
|
+
# Source function library.
|
11
|
+
. /etc/rc.d/init.d/functions
|
12
|
+
|
13
|
+
MYSQL_HEALTH_NICE="-n 19"
|
14
|
+
MYSQL_HEALTH_BIN="/usr/bin/mysql_health"
|
15
|
+
MYSQL_HEALTH_LOG="/var/log/mysql_health.log"
|
16
|
+
MYSQL_HEALTH_PID_FILE="/var/run/mysql_health.pid"
|
17
|
+
MYSQL_HEALTH_HOME="/tmp"
|
18
|
+
MYSQL_HEALTH_ARGS=""
|
19
|
+
|
20
|
+
[ -f /etc/sysconfig/mysql_health ] && . /etc/sysconfig/mysql_health
|
21
|
+
|
22
|
+
[ -x /usr/bin/mysql_health ] || exit 0
|
23
|
+
|
24
|
+
|
25
|
+
start_mysql_health()
|
26
|
+
{
|
27
|
+
cd $MYSQL_HEALTH_HOME
|
28
|
+
echo -n $"Starting mysql_health"
|
29
|
+
nice $MYSQL_HEALTH_NICE $MYSQL_HEALTH_BIN --server:pid-file="$MYSQL_HEALTH_PID_FILE" --log:file "$MYSQL_HEALTH_LOG" --server:daemonize $MYSQL_HEALTH_ARGS
|
30
|
+
result=$?
|
31
|
+
if [ "$result" -eq 0 ]; then
|
32
|
+
sleep 0.5
|
33
|
+
status -p "$MYSQL_HEALTH_PID_FILE" >/dev/null
|
34
|
+
result=$?
|
35
|
+
if [ "$result" -eq 0 ]; then
|
36
|
+
success
|
37
|
+
else
|
38
|
+
failure
|
39
|
+
fi
|
40
|
+
else
|
41
|
+
failure
|
42
|
+
fi
|
43
|
+
echo
|
44
|
+
return $result
|
45
|
+
}
|
46
|
+
|
47
|
+
stop_mysql_health()
|
48
|
+
{
|
49
|
+
echo -n $"Stopping mysql_health"
|
50
|
+
|
51
|
+
if [ -f "$MYSQL_HEALTH_PID_FILE" ]; then
|
52
|
+
kill $(cat "$MYSQL_HEALTH_PID_FILE") 2>/dev/null
|
53
|
+
result=$?
|
54
|
+
else
|
55
|
+
result=1
|
56
|
+
fi
|
57
|
+
|
58
|
+
if [ "$result" -eq 0 ]; then
|
59
|
+
result=1
|
60
|
+
for i in {1..60}; do
|
61
|
+
if [ -f "$MYSQL_HEALTH_PID_FILE" ]; then
|
62
|
+
echo -n "."
|
63
|
+
sleep 1
|
64
|
+
else
|
65
|
+
result=0
|
66
|
+
break
|
67
|
+
fi
|
68
|
+
done
|
69
|
+
fi
|
70
|
+
|
71
|
+
if [ "$result" -eq 0 ];then
|
72
|
+
success
|
73
|
+
else
|
74
|
+
failure
|
75
|
+
fi
|
76
|
+
echo
|
77
|
+
return $result
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
status_mysql_health() {
|
82
|
+
echo -n "mysql_health"
|
83
|
+
if [ -f "$MYSQL_HEALTH_PID_FILE" ]; then
|
84
|
+
status -p "$MYSQL_HEALTH_PID_FILE"
|
85
|
+
else
|
86
|
+
echo " is not running"
|
87
|
+
fi
|
88
|
+
}
|
89
|
+
|
90
|
+
status=0
|
91
|
+
# See how we were called.
|
92
|
+
case "$1" in
|
93
|
+
start)
|
94
|
+
start_mysql_health
|
95
|
+
status=$?
|
96
|
+
;;
|
97
|
+
stop)
|
98
|
+
stop_mysql_health
|
99
|
+
status=$?
|
100
|
+
;;
|
101
|
+
restart|reload)
|
102
|
+
stop_mysql_health
|
103
|
+
sleep 1
|
104
|
+
start_mysql_health
|
105
|
+
status=$?
|
106
|
+
;;
|
107
|
+
status)
|
108
|
+
status_mysql_health
|
109
|
+
status=$?
|
110
|
+
;;
|
111
|
+
*)
|
112
|
+
echo $"Usage: $0 {start|stop|restart|reload}"
|
113
|
+
status=1
|
114
|
+
esac
|
115
|
+
|
116
|
+
exit $status
|
117
|
+
exit 0
|
@@ -0,0 +1,73 @@
|
|
1
|
+
%define ruby_sitelib %(ruby -rrbconfig -e "puts Config::CONFIG['sitelibdir']")
|
2
|
+
%define gemdir %(ruby -rubygems -e 'puts Gem::dir' 2>/dev/null)
|
3
|
+
%define gemname mysql_health
|
4
|
+
%define gemversion 0.3.0
|
5
|
+
%define geminstdir %{gemdir}/gems/%{gemname}-%{gemversion}
|
6
|
+
%define gemfile %{gemname}-%{gemversion}.gem
|
7
|
+
%define gemsummary %(ruby -rrubygems -e 'puts YAML.load(`gem specification %{gemfile}`).summary')
|
8
|
+
%define gemdesc %(ruby -rrubygems -e 'puts YAML.load(`gem specification %{gemfile}`).description')
|
9
|
+
%define gemhomepage %(ruby -rrubygems -e 'puts YAML.load(`gem specification %{gemfile}`).homepage')
|
10
|
+
%define gemlicense %(ruby -rrubygems -e 'puts YAML.load(`gem specification %{gemfile}`).license || "Unknown"')
|
11
|
+
%define gemdeps %(ruby -rrubygems -e 'puts YAML.load(`gem specification %{gemfile}`.chomp).dependencies.map { |d| "rubygem(%s) %s" % [d.name, d.requirement] }.sort.join(", ")')
|
12
|
+
%define gemrelease %(date +"%%Y%%m%%d%%H%%M%%S")
|
13
|
+
|
14
|
+
Summary: %{gemsummary}
|
15
|
+
# The version is repeated in the name so as to allow multiple versions of the gem to be installed on the system.
|
16
|
+
Name: rubygem-%{gemname}-%{gemversion}
|
17
|
+
Version: %{gemversion}
|
18
|
+
Release: %{gemrelease}%{?dist}
|
19
|
+
Group: Development/Languages
|
20
|
+
License: %{gemlicense}
|
21
|
+
URL: %{gemhomepage}
|
22
|
+
Source0: http://rubygems.org/gems/%{gemname}-%{version}.gem
|
23
|
+
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
24
|
+
Requires: rubygems
|
25
|
+
|
26
|
+
Requires: %{gemdeps}
|
27
|
+
|
28
|
+
BuildRequires: rubygems
|
29
|
+
BuildRequires: rubygem(bundler)
|
30
|
+
BuildArch: noarch
|
31
|
+
Provides: rubygem(%{gemname}) = %{version}
|
32
|
+
|
33
|
+
%description
|
34
|
+
%{gemdesc}
|
35
|
+
|
36
|
+
%prep
|
37
|
+
|
38
|
+
%build
|
39
|
+
|
40
|
+
%install
|
41
|
+
rm -rf %{buildroot}
|
42
|
+
install --directory 0755 %{buildroot}%{gemdir}
|
43
|
+
gem install --local --install-dir %{buildroot}%{gemdir} \
|
44
|
+
--force --rdoc %{SOURCE0}
|
45
|
+
install --directory 0755 %{buildroot}/%{_bindir}
|
46
|
+
mv %{buildroot}%{gemdir}/bin/%{gemname} %{buildroot}/%{_bindir}
|
47
|
+
find %{buildroot}%{geminstdir}/bin -type f | xargs chmod a+x
|
48
|
+
|
49
|
+
install --directory --mode 0755 %{buildroot}%{_sysconfdir}/%{gemname}
|
50
|
+
install --directory --mode 0755 %{buildroot}%{_initrddir}
|
51
|
+
install --mode 755 %{buildroot}%{geminstdir}/redhat/%{gemname}.initrc %{buildroot}%{_initrddir}/%{gemname}
|
52
|
+
|
53
|
+
install --directory --mode 0755 %{buildroot}%{_sysconfdir}/sysconfig
|
54
|
+
cat<<__EOF__>%{buildroot}/%{_sysconfdir}/sysconfig/%{gemname}
|
55
|
+
__EOF__
|
56
|
+
|
57
|
+
%clean
|
58
|
+
rm -rf %{buildroot}
|
59
|
+
|
60
|
+
%files
|
61
|
+
%defattr(-, root, root, -)
|
62
|
+
%{_bindir}/%{gemname}
|
63
|
+
%{gemdir}/gems/%{gemname}-%{version}/
|
64
|
+
%doc %{gemdir}/doc/%{gemname}-%{version}
|
65
|
+
%{gemdir}/cache/%{gemname}-%{version}.gem
|
66
|
+
%{gemdir}/specifications/%{gemname}-%{version}.gemspec
|
67
|
+
%{_initrddir}/%{gemname}
|
68
|
+
%{_sysconfdir}/%{gemname}/
|
69
|
+
%{_sysconfdir}/sysconfig/%{gemname}
|
70
|
+
|
71
|
+
%changelog
|
72
|
+
* Sun Jul 29 2012 Erik Osterman <e@osterman.com>
|
73
|
+
- Initial package
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mysql_health
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 5
|
8
|
+
- 0
|
9
|
+
version: 0.5.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Erik Osterman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-07-29 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rufus-scheduler
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 0
|
30
|
+
- 17
|
31
|
+
version: 2.0.17
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: eventmachine
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 0
|
44
|
+
- 0
|
45
|
+
- beta
|
46
|
+
- 4
|
47
|
+
version: 1.0.0.beta.4
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: eventmachine_httpserver
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
- 2
|
60
|
+
- 1
|
61
|
+
version: 0.2.1
|
62
|
+
type: :runtime
|
63
|
+
version_requirements: *id003
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
name: json
|
66
|
+
prerelease: false
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 1
|
73
|
+
- 5
|
74
|
+
- 3
|
75
|
+
version: 1.5.3
|
76
|
+
type: :runtime
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: dbi
|
80
|
+
prerelease: false
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
- 4
|
88
|
+
- 5
|
89
|
+
version: 0.4.5
|
90
|
+
type: :runtime
|
91
|
+
version_requirements: *id005
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: mysql
|
94
|
+
prerelease: false
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
segments:
|
100
|
+
- 2
|
101
|
+
- 8
|
102
|
+
- 1
|
103
|
+
version: 2.8.1
|
104
|
+
type: :runtime
|
105
|
+
version_requirements: *id006
|
106
|
+
description: A service for monitoring MySQL and exposing its health through an HTTP interface for use with TCP load balancers (like haproxy) that support out-of-band health checks using HTTP.
|
107
|
+
email:
|
108
|
+
- e@osterman.com
|
109
|
+
executables:
|
110
|
+
- mysql_health
|
111
|
+
extensions: []
|
112
|
+
|
113
|
+
extra_rdoc_files: []
|
114
|
+
|
115
|
+
files:
|
116
|
+
- .gitignore
|
117
|
+
- Gemfile
|
118
|
+
- LICENSE
|
119
|
+
- README.md
|
120
|
+
- Rakefile
|
121
|
+
- bin/mysql_health
|
122
|
+
- lib/mysql_health.rb
|
123
|
+
- lib/mysql_health/command_line.rb
|
124
|
+
- lib/mysql_health/health.rb
|
125
|
+
- lib/mysql_health/server.rb
|
126
|
+
- lib/mysql_health/version.rb
|
127
|
+
- mysql_health.gemspec
|
128
|
+
- redhat/mysql_health.initrc
|
129
|
+
- redhat/rubygem-mysql_health.spec
|
130
|
+
has_rdoc: true
|
131
|
+
homepage: https://github.com/osterman/mysql_health
|
132
|
+
licenses:
|
133
|
+
- GPLv3
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
segments:
|
144
|
+
- 0
|
145
|
+
version: "0"
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
segments:
|
151
|
+
- 0
|
152
|
+
version: "0"
|
153
|
+
requirements: []
|
154
|
+
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 1.3.6
|
157
|
+
signing_key:
|
158
|
+
specification_version: 3
|
159
|
+
summary: A service for monitoring MySQL and exposing its health through an HTTP interface.
|
160
|
+
test_files: []
|
161
|
+
|