-
Notifications
You must be signed in to change notification settings - Fork 8
/
check_mysql_replication.php
executable file
·205 lines (179 loc) · 8.07 KB
/
check_mysql_replication.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/php
<?php
/*****************************************************************************************
* check_mysql_replication.php - checks that MySQL replication is *actually* running
* USAGE: check_mysql_replication.php [--help] <master name>
*
*****************************************************************************************
* Copyright 2011 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
*
*****************************************************************************************
* CONFIGURATION:
* Edit the arrays below, following the example, for your server settings.
* Port numbers are included in case you run multiple instances of MySQL.
*
*****************************************************************************************
* CAVEAT -
* This checks the master binary log file and position, and then checks that on all
* configured slaves in order. If a transaction is pushed from the master to the slave
* while this script is running, it may return CRITICAL. Set your soft and hard state
* limits accordingly.
*****************************************************************************************
* PERMISSIONS ON MYSQL:
* The user this script connects to MySQL as runs the following commands, and needs the
* following privileges:
* SHOW PROCESSLIST -> Process priv (Master)
* SHOW MASTER STATUS -> Replication Client priv (Master)
* SHOW SLAVE STATUS -> Replication Client priv (Slave)
* i.e. GRANT REPLICATION CLIENT, PROCESS ON *.* TO 'nagios'@'hostname.domain.com' IDENTIFIED BY 'password';
*****************************************************************************************
* RESULT:
* This returns OK (0) or CRITICAL (2). Either the master and slave(s) are in sync or they aren't.
* No thresholds, no warning.
* This returns UNKNOWN (3) if it can't connect to one of the servers, if configuration is
* wrong/missing, or if the specified master name isn't configured.
*
*****************************************************************************************
* The authoritative version of this script lives at:
* <https://github.com/jantman/nagios-scripts>
*
* Please submit bug/feature requests or questions using
* the issue tracker there. Feedback, and patches (preferred
* as a GitHub pull request, but emailed diffs are also
* accepted) are strongly encouraged.
*
* Licensed under GNU GPLv3 - see the LICENSE file in the git repository.
****************************************************************************************/
//
// BEGIN CONFIGURATION
//
$servers = array();
define("MYSQL_DEFAULT_PORT", 3306);
define("MAX_BYTES_DIFF", 5000);
// EXAMPLE ARRAY:
/*
$servers['mastername'] = array('hostname' => 'foo', 'user' => 'username', 'password' => 'mypass', 'port' => 3306, 'slaves' => array());
$servers['mastername']['slaves']['slaveOne'] = array('hostname' => 'slaveOne', 'user' => 'username', 'password' => 'mypass', 'port' => 3306);
$servers['mastername']['slaves']['slaveTwo'] = array('hostname' => 'slaveTwo', 'user' => 'username', 'password' => 'mypass', 'port' => 3306);
*/
//
// END CONFIGURATION
//
// define exit code constants
define("OK", 0);
define("WARNING", 1);
define("CRITICAL", 2);
define("UNKNOWN", 3);
// check args
if(! isset($argv[1]) || $argv[1] == "-h" || $argv[1] == "--help")
{
echo "USAGE: check_mysql_replication.php [--help] <master name>\n";
exit(UNKNOWN);
}
// get master name from args
$masterName = trim($argv[1]);
// make sure master name is configured
if(! isset($servers[$masterName]))
{
echo "UNKNOWN: master name '$masterName' not defined in check_mysql_replication.php configuration. Exiting (3).\n";
exit(UNKNOWN);
}
$master = $servers[$masterName];
$slaves = $master['slaves'];
// try to connect to master
if($master['port'] != MYSQL_DEFAULT_PORT) { $foo = $master['hostname'].':'.$master['port'];} else { $foo = $master['hostname'];}
$masterConn = mysql_connect($foo, $master['user'], $master['password']);
if(! $masterConn)
{
echo "UNKNOWN: Unable to connect to MySQL Master server ".$foo." as ".$master['user'].".\n";
exit(UNKNOWN);
}
// check processlist for 'Binlog Dump', make an array of all hosts (slaves) binlog dump procs are running for
$BINLOG_DUMP_SLAVES = array();
$query = "SHOW PROCESSLIST;";
$result = mysql_query($query);
if(! $result){ echo "UNKNOWN: Error in MySQL query to master: $query\n"; exit(UNKNOWN);}
while($row = mysql_fetch_assoc($result))
{
if($row['Command'] != "Binlog Dump"){ continue; }
$foo = substr($row['Host'], 0, strpos($row['Host'], ":"));
$BINLOG_DUMP_SLAVES[] = $foo;
}
// check that each configured slave has a Binlog Dump process running
$foo = "";
foreach($slaves as $name => $arr)
{
if(! in_array($arr['hostname'], $BINLOG_DUMP_SLAVES))
{
$foo .= $arr['hostname'].", ";
}
}
if($foo != "")
{
echo "CRITICAL: No Binlog Dump process on Master for slaves: ".trim($foo, ", ")."\n";
exit(CRITICAL);
}
// find the current binlog file and position
// WARNING - TODO - this assumes we only do one DB, and 'SHOW MASTER STATUS' will only return one row!
$query = "SHOW MASTER STATUS;";
$result = mysql_query($query);
if(! $result){ echo "UNKNOWN: Error in MySQL query to master: $query\n"; exit(UNKNOWN);}
$row = mysql_fetch_assoc($result);
$MASTER_LOG_FILE = $row['File'];
$MASTER_LOG_POS = $row['Position'];
// now we have to loop through the defined slaves and check file and position...
$okSlaves = 0;
$badSlaves = 0;
$str = "";
foreach($slaves as $name => $arr)
{
$foo = check_slave_file_pos($arr, $MASTER_LOG_FILE, $MASTER_LOG_POS);
// $foo is an array like slave_name => array('result' => boolean, 'log_pos' => int, 'log_file' => string, 'bytes_diff' => int)
if($foo['result'] == true){ $okSlaves++;} else { $badSlaves++;}
//$str .= $name."=".$foo['log_file'].":".$foo['log_pos'].', '.($foo['bytes_diff'] == 0 ? 'ok' : 'off by '.$foo['bytes_diff'].'B (> '.MAX_BYTES_DIFF.')').'; ';
$str .= $name."=".$foo['log_file'].":".$foo['log_pos'].', off by '.$foo['bytes_diff'].'B ('.($foo['result'] == true ? '<' : '>').' '.MAX_BYTES_DIFF.'); ';
}
$str = "master=".$MASTER_LOG_FILE.":".$MASTER_LOG_POS." ".$str;
$str = trim($str, '; ');
if($badSlaves > 0)
{
echo "CRITICAL: MySQL replication to $badSlaves of ".($okSlaves + $badSlaves)." slaves broken ($str).\n";
exit(CRITICAL);
}
echo "OK: MySQL replication to $okSlaves of ".($okSlaves + $badSlaves)." slaves up-to-date ($str).\n";
exit(0);
mysql_close($masterConn);
/**
* Connects to a slave and compares the slave and master binary log file names and positions
*
* @param $slaveArr array the array for this slave from the main $servers configuration array
* @param $MASTER_LOG_FILE string the name of the master binary log file
* @param $MASTER_LOG_POS the position in the binary log on the master
*
* @return boolean true if same file and position, false otherwise
*/
function check_slave_file_pos($slaveArr, $MASTER_LOG_FILE, $MASTER_LOG_POS)
{
// try to connect to the slave
if(isset($slaveArr['ip'])){ $hostname = $slaveArr['ip'];} else { $hostname = $slaveArr['hostname'];}
if($slaveArr['port'] != MYSQL_DEFAULT_PORT) { $foo = $hostname.':'.$slaveArr['port'];} else { $foo = $hostname;}
$conn = mysql_connect($foo, $slaveArr['user'], $slaveArr['password']);
if(! $conn)
{
echo "UNKNOWN: Unable to connect to MySQL slave server ".$foo." as ".$slaveArr['user'].".\n";
exit(UNKNOWN);
}
// find the current binlog file and position
// WARNING - TODO - this assumes we only do one DB, and 'SHOW SLAVE STATUS' will only return one row!
$query = "SHOW SLAVE STATUS;";
$result = mysql_query($query);
if(! $result){ echo "UNKNOWN: Error in MySQL query to slave $foo: $query\n"; exit(UNKNOWN);}
$row = mysql_fetch_assoc($result);
mysql_close($conn);
$result = true;
if($row['Master_Log_File'] != $MASTER_LOG_FILE) { $result = false; }
$bytes_diff = abs($MASTER_LOG_POS - $row['Read_Master_Log_Pos']);
if($bytes_diff > MAX_BYTES_DIFF){ $result = false;}
return array('result' => $result, 'log_pos' => $row['Read_Master_Log_Pos'], 'log_file' => $row['Master_Log_File'], 'bytes_diff' => $bytes_diff);
}
?>