1. #!/usr/bin/perl -w
  2. use strict;
  3.  
  4. use Getopt::Std;
  5. use DBI;
  6. use Cache::SharedMemoryCache;
  7. use Getopt::Long;
  8.  
  9.  
  10. use lib "third-party/";
  11.  
  12. use utils qw(%ERRORS);
  13.  
  14. use vars qw/ %opt /;
  15.  
  16. # globals
  17. my $o_help = undef; # Help
  18. my $o_hostname = undef; # Hostname
  19. my $o_username = undef; # Username
  20. my $o_password = undef; # Password
  21. my $o_bufferpool = undef; # Bufferpool Check
  22. my $o_qps = undef; # Queries per second Check
  23. my $o_pagecache = undef; # Page cache Check
  24. my $o_threadcache = undef; # Thread Cache Check
  25. my $o_querycache = undef; # Query Cache Check
  26. my $o_connections = undef; # Percent connections in use
  27. my $o_warn = undef; # Warning value
  28. my $o_crit = undef; # Critical value
  29. my $results = undef; # Results
  30. my $n_status = undef; # Status
  31. my $n_perfdata = undef; # Performance data
  32. my $n_output = undef; # Output
  33. my $cache = undef; # Cache object
  34.  
  35. sub connect {
  36. my($dbh) = DBI->connect ("DBI:mysql:host=$o_hostname",$o_username,$o_password)
  37. or die "Can't connect to database\n";
  38. return $dbh;
  39. }
  40.  
  41. sub check_cache {
  42. my($getvars) = "SHOW GLOBAL VARIABLES";
  43. my($testcache) = $cache->get ( 'version' );
  44. if($testcache){
  45. return 1;
  46. }else{
  47. my($dbh) = &connect;
  48. my($sth) = $dbh->prepare('SHOW GLOBAL VARIABLES');
  49. $sth->execute;
  50. while(my(@settings) = $sth->fetchrow_array())
  51. {
  52. $cache->set ( $settings[0], $settings[1], "10 minutes" )
  53. or die "Cannot populate cache";
  54. }
  55. return 1;
  56. }
  57. }
  58.  
  59. sub get_cached_value {
  60. my($param)=shift;
  61. if( &check_cache ) {
  62. my($cachedval)= $cache->get($param);
  63. return $cachedval;
  64. }else{
  65. die "Could not get value from cache\n";
  66. }
  67. }
  68.  
  69. sub get_live_value {
  70. my($param)=shift;
  71. my($dbh) = &connect;
  72. my($sth) = $dbh->prepare("SHOW GLOBAL STATUS LIKE '$param'");
  73. $sth->execute;
  74. my(@livevals) = $sth->fetchrow_array()
  75. or die "Cannot get value from database";
  76. return $livevals[1];
  77. }
  78.  
  79. sub connection_use {
  80. my($max_connections)=&get_cached_value('max_connections');
  81. my($current_connections)=&get_live_value('threads_connected');
  82. my($percent_used) = ($current_connections/$max_connections)*100;
  83. return sprintf("%.2f", $percent_used);
  84. }
  85.  
  86. sub qcache_hit_ratio {
  87. my($qcache_hits)=&get_live_value('qcache_hits');
  88. #my($com_select)=&get_live_value('com_select');
  89. my($qcache_inserts)=&get_live_value('qcache_inserts');
  90. my($qcache_not_cached)=&get_live_value('qcache_not_cached');
  91. # Original query I created
  92. #my($hit_ratio)=($qcache_hits/($qcache_hits + $com_select))*100;
  93. # Innotop logic
  94. #my($hit_ratio)=(( $qcache_hits||0 )/( ( ($com_select||0) + ($qcache_hits||0) )||1) ) * 100;
  95. # http://www.pythian.com/blogs/437/mysql-memory-usage-profile-script-2 logic
  96. # This was the only one I could get to match the same results I was seeing in innotop so I'm using it.
  97. my($hit_ratio) = ($qcache_hits / ($qcache_hits + $qcache_inserts + $qcache_not_cached)) * 100;
  98. return sprintf("%.2f", $hit_ratio);
  99. }
  100.  
  101. sub tcache_hit_ratio {
  102. my($threads_created)=&get_live_value('threads_created');
  103. my($connections)=&get_live_value('connections');
  104. my($hit_ratio)=100 - (($threads_created / $connections) * 100);
  105. return sprintf("%.2f", $hit_ratio);
  106. }
  107.  
  108. sub innodb_page_cache_usage {
  109. my($innodb_buffer_pool_pages_free)=&get_live_value('innodb_buffer_pool_pages_free');
  110. my($innodb_buffer_pool_pages_total)=&get_live_value('Innodb_buffer_pool_pages_total');
  111. my($page_cache_usage) = $innodb_buffer_pool_pages_free * 100 / $innodb_buffer_pool_pages_total;
  112. return sprintf("%.2f", $page_cache_usage);
  113. }
  114.  
  115. sub innodb_bp_hit_ratio {
  116. my($innodb_buffer_pool_reads)=&get_live_value('innodb_buffer_pool_reads');
  117. my($innodb_buffer_pool_read_requests)=&get_live_value('innodb_buffer_pool_read_requests');
  118. my($hit_ratio) = 100 - ($innodb_buffer_pool_reads * 100 / $innodb_buffer_pool_read_requests);
  119. return sprintf("%.2f", $hit_ratio);
  120. }
  121.  
  122. sub tmp_table_ratio {
  123. my($created_tmp_disk_tables)=&get_live_value('created_tmp_disk_tables');
  124. my($created_tmp_tables)=&get_live_value('created_tmp_tables');
  125. my($ratio) = $created_tmp_disk_tables * 100 / $created_tmp_tables;
  126. return sprintf("%.2f", $ratio);
  127. }
  128.  
  129. sub qps {
  130. # per http://mysqldump.azundris.com/archives/68-Monitoring-MySQL.html
  131. # The following is a more accurate representation of "useful" or application
  132. # queries per second
  133. my($com_select)=&get_live_value('com_select');
  134. my($qcache_hits)=&get_live_value('qcache_hits');
  135. my($com_insert)=&get_live_value('com_insert');
  136. my($com_update)=&get_live_value('com_update');
  137. my($com_delete)=&get_live_value('com_delete');
  138. my($com_replace)=&get_live_value('com_replace');
  139. my($uptime)=&get_live_value('uptime');
  140. my($total_questions) = $com_select + $qcache_hits + $com_insert + $com_update + $com_delete + $com_replace;
  141. my $qps = $total_questions / $uptime;
  142. return sprintf("%.2f", $qps);
  143. }
  144.  
  145. sub print_usage {
  146. print "Usage: $0 -H <host> -U <username> -P <password> -w <warn level> -c <crit level> [-i|-q|-p|-t|-Q]\n"
  147. }
  148.  
  149. sub help {
  150. print "\nMySQL Performance Monitor Script for Nagios\n";
  151. print_usage();
  152. print <<EOT;
  153. -h, --help
  154. print this help message
  155. -H, --hostname=HOST
  156. name or IP address of host to check
  157. -U, --username=USERNAME
  158. username to connect to the database
  159. * MUST have permissions to execute "SHOW GLOBAL VARIABLES" and "SHOW GLOBAL STATUS"
  160. -P, --password=PASSWORD
  161. password of username connecting to the database
  162. -i, --innodbbp
  163. check InnoDB bufferpool hit ratio
  164. - Returns percentage value
  165. -q, --qps
  166. check queries per second
  167. - Returns single value
  168. -p, --innodbpc
  169. check InnoDB page cache usage
  170. - Returns percentage value
  171. -t, --threadcache
  172. check thread cache hit ratio
  173. - Returns percentage value
  174. -Q, --querycache
  175. check query cache hit ratio
  176. - Returns percentage value
  177. -C, --connections
  178. check percentage of max connections in use
  179. - Returns percentage value
  180. -c, --crit
  181. critical value
  182. (<= for options i,t,Q)
  183. (>= for options q,C,p)
  184. -w, --warn
  185. warning value
  186. (<= for options i,t,Q)
  187. (>= for options q,C,p)
  188.  
  189. **********************************************************************************
  190. For options [i,p,t,Q], warning and critical are lowest values acceptable i.e.
  191. If bufferpool hit ratio falls BELOW warn/crit value, alarms will be raised
  192. For option [q], warning and critical are maximum values acceptable i.e.
  193. If queries per second goes ABOVE warn/crit value, alarms will be raised
  194. **********************************************************************************
  195. EOT
  196. }
  197.  
  198. sub check_options {
  199. Getopt::Long::Configure ("bundling");
  200. GetOptions (
  201. 'h' => \$o_help, 'help' => \$o_help,
  202. 'H:s' => \$o_hostname, 'hostname:s' => \$o_hostname,
  203. 'U:s' => \$o_username, 'username:s' => \$o_username,
  204. 'P:s' => \$o_password, 'password:s' => \$o_password,
  205. 'i' => \$o_bufferpool, 'innodbbp' => \$o_bufferpool,
  206. 'q' => \$o_qps, 'qps' => \$o_qps,
  207. 'p' => \$o_pagecache, 'innodbpc' => \$o_pagecache,
  208. 't' => \$o_threadcache, 'threadcache' => \$o_threadcache,
  209. 'Q' => \$o_querycache, 'querycache' => \$o_querycache,
  210. 'C' => \$o_connections, 'connections' => \$o_connections,
  211. 'c:i' => \$o_crit, 'crit:i' => \$o_crit,
  212. 'w:i' => \$o_warn, 'warn:i' => \$o_warn
  213. );
  214. if (defined ($o_help) ) { help(); exit $ERRORS{"UNKNOWN"}};
  215. if ( !defined($o_hostname) )
  216. { print "No host defined!\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
  217. if ( !defined($o_username) )
  218. { print "No username defined!\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
  219. if ( !defined($o_password) )
  220. { print "No password defined!\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
  221. if ( !defined($o_bufferpool) && !defined($o_qps) && !defined($o_pagecache) && !defined($o_threadcache) && !defined($o_querycache) && !defined($o_connections) )
  222. { print "A monitor must be defined!\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
  223. if ( !defined($o_warn) && !defined($o_crit) )
  224. { print "Warning and Critical levels must be defined!\n";print_usage(); exit $ERRORS{"UNKNOWN"}}
  225. }
  226.  
  227. ## Main ##
  228. check_options();
  229.  
  230. my %cache_options = ('namespace'=>$o_hostname);
  231. $cache = new Cache::SharedMemoryCache(\%cache_options)
  232. or die "Cannot instatiate ShareMemoryCache: $_\n";
  233.  
  234. if (defined($o_bufferpool)) {
  235. if ( $o_crit != 0 && $o_warn < $o_crit )
  236. { print "Warning level should be higher than crit for hit ratios!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  237. $results = innodb_bp_hit_ratio()
  238. or die $ERRORS{"UNKNOWN"};
  239. $n_perfdata=" hit_ratio=$results;$o_warn;$o_crit;0;100";
  240. if ($results <= $o_crit) {
  241. $n_status="CRITICAL";
  242. } elsif ($results <= $o_warn) {
  243. $n_status="WARNING";
  244. } else {
  245. $n_status="OK";
  246. }
  247. $n_output="$n_status: Bufferpool hit ratio is $results% | " . $n_perfdata;
  248.  
  249. }
  250. if (defined($o_qps)) {
  251. if ( $o_crit != 0 && $o_warn > $o_crit )
  252. { print "Warning level should be lower than crit for single values!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  253. $results = qps()
  254. or die $ERRORS{"UNKNOWN"};
  255. $n_perfdata=" qps=$results;$o_warn;$o_crit;;";
  256. if ($results >= $o_crit) {
  257. $n_status="CRITICAL";
  258. } elsif ($results >= $o_warn) {
  259. $n_status="WARNING";
  260. } else {
  261. $n_status="OK";
  262. }
  263. $n_output="$n_status: Queries per second: $results | " . $n_perfdata;
  264.  
  265. }
  266. if (defined($o_connections)) {
  267. if ( $o_crit != 0 && $o_warn > $o_crit )
  268. { print "Warning level should be less than crit!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  269. $results = connection_use()
  270. or die $ERRORS{"UNKNOWN"};
  271. $n_perfdata=" percent_used=$results;$o_warn;$o_crit;0;100";
  272. if ($results >= $o_crit) {
  273. $n_status="CRITICAL";
  274. } elsif ($results >= $o_warn) {
  275. $n_status="WARNING";
  276. } else {
  277. $n_status="OK";
  278. }
  279. $n_output="$n_status: Percentage of Connection used is $results% | " . $n_perfdata;
  280. }
  281. if (defined($o_pagecache)) {
  282. if ( $o_crit != 0 && $o_warn > $o_crit )
  283. { print "Warning level should be lower than crit for hit ratios!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  284. $results = innodb_page_cache_usage()
  285. or die $ERRORS{"UNKNOWN"};
  286. $n_perfdata=" cache_usage=$results;$o_warn;$o_crit;0;100";
  287. if ($results <= $o_crit) {
  288. $n_status="CRITICAL";
  289. } elsif ($results <= $o_warn) {
  290. $n_status="WARNING";
  291. } else {
  292. $n_status="OK";
  293. }
  294. $n_output="$n_status: Page cache usage is $results% | " . $n_perfdata;
  295. }
  296. if (defined($o_threadcache)) {
  297. if ( $o_crit != 0 && $o_warn < $o_crit )
  298. { print "Warning level should be higher than crit for hit ratios!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  299. $results = tcache_hit_ratio()
  300. or die $ERRORS{"UNKNOWN"};
  301. $n_perfdata=" hit_ratio=$results;$o_warn;$o_crit;0;100";
  302. if ($results <= $o_crit) {
  303. $n_status="CRITICAL";
  304. } elsif ($results <= $o_warn) {
  305. $n_status="WARNING";
  306. } else {
  307. $n_status="OK";
  308. }
  309. $n_output="$n_status: Thread cache hit ratio is $results% | " . $n_perfdata;
  310. }
  311. if (defined($o_querycache)) {
  312. if ( $o_crit != 0 && $o_warn < $o_crit )
  313. { print "Warning level should be higher than crit for hit ratios!\n";print_usage(); exit $ERRORS{"UNKNOWN"}};
  314. $results = qcache_hit_ratio()
  315. or die $ERRORS{"UNKNOWN"};
  316. $n_perfdata=" hit_ratio=$results;$o_warn;$o_crit;0;100";
  317. if ($results <= $o_crit) {
  318. $n_status="CRITICAL";
  319. } elsif ($results <= $o_warn) {
  320. $n_status="WARNING";
  321. } else {
  322. $n_status="OK";
  323. }
  324. $n_output="$n_status: Query cache hit ratio is $results% | " . $n_perfdata;
  325. }
  326. print "$n_output \n";
  327. exit $ERRORS{$n_status};
  328.