diff options
author | mail_redacted_for_web | 2019-04-17 19:07:19 +0200 |
---|---|---|
committer | mail_redacted_for_web | 2019-04-17 19:07:19 +0200 |
commit | 1e2387474a449452b78520b9ad96a8b4b5e99722 (patch) | |
tree | 836889471eec7d2aac177405068e2a8f1e2b1978 /nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins | |
download | nagios-plugins-contrib-1e2387474a449452b78520b9ad96a8b4b5e99722.tar.bz2 |
initial commit of source fetch
Diffstat (limited to 'nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins')
19 files changed, 4896 insertions, 0 deletions
diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Changelog b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Changelog new file mode 100644 index 0000000..cb12581 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Changelog @@ -0,0 +1,115 @@ +2014-03-20: version 1.1.3 +------------------------- +* Introduced more secure location of PHP script configs to harden a Cacti setup (bug #1295006) + +2014-03-14: version 1.1.2 +------------------------- +* Added Nagios plugin and Cacti template for Amazon RDS +* Added Nagios config template to the documentation +* Added an option to pmp-check-pt-table-checksum to check MAX(ts) of latest checksum +* Added generic Nagios plugin for PT tables +* Extended pmp-check-mysql-processlist with max user connections check +* Zabbix MySQL.running-slave item failed with MySQL 5.6 (bug 1272358) +* ss_get_mysql_stats and MariaDB does not use have_response_time_distribution (bug 1285888) +* Cacti Monitoring plugins and SNMP via TCP (bug 1268552) + +2013-12-30: version 1.1.1 +------------------------- +* Cacti mysql graphs stop working with data input field "server-id" after 1.1 upgrade (bug 1264814) +* Non-integer poller errors for MySQL Query Response Time (bug 1264353) + +2013-12-16: version 1.1 +------------------------- +* Added MySQL template for Zabbix 2.0.x (first release) +* Added FreeBSD support to Nagios plugins, partially rewritten pmp-check-unix-memory (bugs 1249575, 1244081) +* Added new options to ss_get_mysql_stats.php to better support pt-heartbeat (bugs 1253125, 1253130) +* ss_get_mysql_stats.php script was opening multiple connections to the server (bug 1255371) +* sql query for idle_blocker_duraction check in pmp-check-mysql-innodb did not conform sql mode of ONLY_FULL_GROUP_BY (bug #1240417) + +2013-10-02: version 1.0.5 +------------------------- + +* Added mysql-ca option to ss_get_mysql_stats.php (bug 1213857) +* Added user info to the idle_blocker_duration check of pmp-check-mysql-innodb (bug 1215317) +* Extended pmp-check-mysql-processlist with more locking states (bug 1213859) +* ss_get_mysql_stats.php did not work with custom mysql port (bug 1213862) +* ss_get_mysql_stats.php silently failed when a query returns too many rows (bug 1225070) +* Wrong description of percona-cacti-templates deb package (bug 1217782) + +2013-07-22: version 1.0.4 +------------------------- + +* Added Galera/MySQL Monitoring Template for Cacti +* Added "Disk Read/Write Time per IO Request (ms)" graph +* Added "MySQL InnoDB Buffer Pool Efficiency" graph +* Switched ss_get_mysql_stats.php to PHP MySQLi extension and made it working with SSL (bug 1193097) +* Added user info to the max_duration check of pmp-check-mysql-innodb plugin (bug 1185513) +* ss_get_mysql_stats.php default values for '$status' array were null instead of 0 (bug 1070268) +* Introduction of innodb_read_views_memory overrode the InnoDB total memory allocated output in Cacti (bug 1188519) +* ss_get_by_ssh.php parsed MongoDB counters incorrectly when replica is set (bug 1087073) +* Cacti graph "Redis Unsaved Changes" was empty for Redis 2.6 (bug 1110372) +* Comparison of status variables that are strings didn't work with pmp-check-mysql-status (bug 1191305) +* pmp-check-mysql-processlist always showed 0 for "copy to table" counter (bug 1197084) +* percona-nagios-plugins package failed to install on Debian Squeeze when debsums is installed (bug 1194757) + +2013-04-17: version 1.0.3 +------------------------- + +* MySQL 5.6 compatibility for InnoDB graphs (bug 1124292) +* Added performance data to Nagios plugins (bugs 1090145, 1102687) +* Added UTC option to pmp-check-mysql-replication-delay to be compatible with pt-hearbeat 2.1.8+ (bug 1103364) +* Added 1-second granularity to pmp-check-mysql-deadlocks (bug 1154774) +* Added package install/update instructions and other documentation updates (bugs 1139652, 1124200, 1015981) +* Updated documentation with the new Cacti sample images +* Updated "Network Traffic" to be blue and green and to show bits/sec (bug 1132900) +* Extended "MySQL Threads" graph with all kind of threads (bug 1157911) +* Some Cacti single-item graphs were broken due to cacti hexadecimal transformation (bug 1155513) +* Memcached graphs were broken when the wrong arguments for nc command are passed (bug 1155712) +* ss_get_by_ssh.php didn't gather mongodb stats without SSH (bug 1050537) +* ss_get_by_ssh.php didn't timeout commands that hang (bug 1160611) +* pmp-check-file-privs didn't throw the proper error on directory permissions issue (bug 1024001) +* pmp-check-mysql-replication-running reported OK when a slave is in "Connecting" state (bug 1089506) + +Update note: Cacti templates have to be re-imported together with the updating +of ss_get_*.php scripts. Then make sure you rebuilt the poller cache under +Cacti -> System Utilities. Also the following Cacti graphs need to be recreated: MySQL "MySQL +Threads", Linux "Network Traffic". + +2013-02-15: version 1.0.2 +------------------------- + +* Created Debian and RPM packages +* Added "Disk IOPS" graph to Cacti Linux Templates +* Added an option to pmp-check-unix-memory to disable overriding the status based on the largest process in memory (bug 1052368) +* Added '!=' comparison operator to pmp-check-mysql-status (bug 1048627) +* pmp-check-mysql-replication-delay didn't alert if second behing master is NULL (bug 1040528) +* pmp-check-pt-table-checksum reported OK when checksums table does not exist (bug 1114425) +* pmp-check-pt-table-checksum threw "Bad substitution" error on Debian (bug 1071802) +* Minor updates to the documentation (bugs 1021855, 1014814, 1125233) + +2012-06-12: version 1.0.1 +------------------------- + +* Cacti debug logs had a 12-hour timestamp instead of 24-hour (bug 973320). +* Nagios checks didn't remove temporary files on some platforms (bug 977514). +* Nagios plugins didn't use enough XXX in mktemp patterns (bug 2022766). +* Nagios check for deleted files didn't return 0 on success (bug 1009751). +* Nagios check for long-running txns didn't show thread ID (bug 1011625). + +2012-04-02: version 1.0.0 +------------------------- + +* Load average was 15-minute instead of 1-minute (bug 968604). +* MySQL connection errors weren't logged (bug 958782). +* The pmp-check-mysql-deadlocks referred to pt-heartbeat (bug 934255). +* HTTP status was not fetched correctly when $use_ssh was disabled (bug 937017). +* MongoDB didn't support --port2 (bug 937018). +* Percona Server response time graph didn't tolerate <> 14 rows (bug 954118). +* The release tarball had an extra directory (bug 934227). +* pmp-check-mysql-status didn't remove one of its temp files (bug 959425). + +2012-02-16: version 0.9.0 +------------------------- + +* Initial release. Not backwards compatible with Better Cacti Templates. + diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Makefile b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Makefile new file mode 100644 index 0000000..dce78e2 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/Makefile @@ -0,0 +1,13 @@ +#/usr/bin/make -f + +PLUGIN = $(wildcard nagios/bin/*) +MANPAGES = $(shell find nagios/bin -type f -printf '%f\n' | sed 's,$$,.7,') +CLEANFILES = $(MANPAGES) + +include ../common.mk + + +%.7: $(PLUGIN) + pod2man -s 7 $< > $@ + + diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/control b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/control new file mode 100644 index 0000000..7b672b3 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/control @@ -0,0 +1,7 @@ +Watch: http://www.percona.com/downloads/percona-monitoring-plugins/LATEST/source/ >percona-monitoring-plugins-([0-9.]+)\.tar\.gz< +Version: 1.1.8 +Homepage: http://www.percona.com/doc/percona-monitoring-plugins/ +Uploaders: Bernd Zeimetz <bzed@debian.org> +Description: Percona Monitoring Plugins (nagios) + Nagios MySQL Monitoring plugins writting/provided by Percona. +Suggests: percona-toolkit, python2.7, python-pymongo diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/copyright b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/copyright new file mode 100644 index 0000000..c0aecd2 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/copyright @@ -0,0 +1,12 @@ +Copyright 2012-2014 Baron Schwartz, 2012-2014 Percona Inc. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-aws-rds.py b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-aws-rds.py new file mode 100755 index 0000000..73b7660 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-aws-rds.py @@ -0,0 +1,562 @@ +#!/usr/bin/env python +"""Nagios plugin for Amazon RDS monitoring. + +This program is part of $PROJECT_NAME$ +License: GPL License (see COPYING) + +Author Roman Vynar +Copyright 2014-2015 Percona LLC and/or its affiliates +""" + +import datetime +import optparse +import pprint +import sys + +import boto +import boto.rds +import boto.ec2.cloudwatch + +# Nagios status codes +OK = 0 +WARNING = 1 +CRITICAL = 2 +UNKNOWN = 3 + + +class RDS(object): + + """RDS connection class""" + + def __init__(self, region, profile=None, identifier=None): + """Get RDS instance details""" + self.region = region + self.profile = profile + self.identifier = identifier + + if self.region == 'all': + self.regions_list = [reg.name for reg in boto.rds.regions()] + else: + self.regions_list = [self.region] + + self.info = None + if self.identifier: + for reg in self.regions_list: + try: + rds = boto.rds.connect_to_region(reg, profile_name=self.profile) + self.info = rds.get_all_dbinstances(self.identifier) + except (boto.provider.ProfileNotFoundError, boto.exception.BotoServerError) as msg: + debug(msg) + else: + # Exit on the first region and identifier match + self.region = reg + break + + def get_info(self): + """Get RDS instance info""" + if self.info: + return self.info[0] + else: + return None + + def get_list(self): + """Get list of available instances by region(s)""" + result = dict() + for reg in self.regions_list: + try: + rds = boto.rds.connect_to_region(reg, profile_name=self.profile) + result[reg] = rds.get_all_dbinstances() + except (boto.provider.ProfileNotFoundError, boto.exception.BotoServerError) as msg: + debug(msg) + + return result + + def get_metric(self, metric, start_time, end_time, step): + """Get RDS metric from CloudWatch""" + cw_conn = boto.ec2.cloudwatch.connect_to_region(self.region, profile_name=self.profile) + result = cw_conn.get_metric_statistics( + step, + start_time, + end_time, + metric, + 'AWS/RDS', + 'Average', + dimensions={'DBInstanceIdentifier': [self.identifier]} + ) + if result: + if len(result) > 1: + # Get the last point + result = sorted(result, key=lambda k: k['Timestamp']) + result.reverse() + + result = float('%.2f' % result[0]['Average']) + + return result + + +def debug(val): + """Debugging output""" + global options + if options.debug: + print 'DEBUG: %s' % val + + +def main(): + """Main function""" + global options + + short_status = { + OK: 'OK', + WARNING: 'WARN', + CRITICAL: 'CRIT', + UNKNOWN: 'UNK' + } + + # DB instance classes as listed on + # http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.DBInstanceClass.html + db_classes = { + 'db.t1.micro': 0.615, + 'db.m1.small': 1.7, + 'db.m1.medium': 3.75, + 'db.m1.large': 7.5, + 'db.m1.xlarge': 15, + 'db.m4.large': 8, + 'db.m4.xlarge': 16, + 'db.m4.2xlarge': 32, + 'db.m4.4xlarge': 64, + 'db.m4.10xlarge': 160, + 'db.r3.large': 15, + 'db.r3.xlarge': 30.5, + 'db.r3.2xlarge': 61, + 'db.r3.4xlarge': 122, + 'db.r3.8xlarge': 244, + 'db.t2.micro': 1, + 'db.t2.small': 2, + 'db.t2.medium': 4, + 'db.t2.large': 8, + 'db.m3.medium': 3.75, + 'db.m3.large': 7.5, + 'db.m3.xlarge': 15, + 'db.m3.2xlarge': 30, + 'db.m2.xlarge': 17.1, + 'db.m2.2xlarge': 34.2, + 'db.m2.4xlarge': 68.4, + 'db.cr1.8xlarge': 244, + } + + # RDS metrics http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/rds-metricscollected.html + metrics = { + 'status': 'RDS availability', + 'load': 'CPUUtilization', + 'memory': 'FreeableMemory', + 'storage': 'FreeStorageSpace' + } + + units = ('percent', 'GB') + + # Parse options + parser = optparse.OptionParser() + parser.add_option('-l', '--list', help='list of all DB instances', + action='store_true', default=False, dest='db_list') + parser.add_option('-n', '--profile', default=None, + help='AWS profile from ~/.boto or /etc/boto.cfg. Default: None, fallbacks to "[Credentials]".') + parser.add_option('-r', '--region', default='us-east-1', + help='AWS region. Default: us-east-1. If set to "all", we try to detect the instance region ' + 'across all of them, note this will be slower than if you specify the region explicitly.') + parser.add_option('-i', '--ident', help='DB instance identifier') + parser.add_option('-p', '--print', help='print status and other details for a given DB instance', + action='store_true', default=False, dest='printinfo') + parser.add_option('-m', '--metric', help='metric to check: [%s]' % ', '.join(metrics.keys())) + parser.add_option('-w', '--warn', help='warning threshold') + parser.add_option('-c', '--crit', help='critical threshold') + parser.add_option('-u', '--unit', help='unit of thresholds for "storage" and "memory" metrics: [%s].' + 'Default: percent' % ', '.join(units), default='percent') + parser.add_option('-t', '--time', help='time period in minutes to query. Default: 5', + type='int', default=5) + parser.add_option('-a', '--avg', help='time average in minutes to request. Default: 1', + type='int', default=1) + parser.add_option('-f', '--forceunknown', help='force alerts on unknown status. This prevents issues related to ' + 'AWS Cloudwatch throttling limits Default: False', + action='store_true', default=False) + parser.add_option('-d', '--debug', help='enable debug output', + action='store_true', default=False) + options, _ = parser.parse_args() + + if options.debug: + boto.set_stream_logger('boto') + + rds = RDS(region=options.region, profile=options.profile, identifier=options.ident) + + # Check args + if len(sys.argv) == 1: + parser.print_help() + sys.exit() + elif options.db_list: + info = rds.get_list() + print 'List of all DB instances in %s region(s):' % (options.region,) + pprint.pprint(info) + sys.exit() + elif not options.ident: + parser.print_help() + parser.error('DB identifier is not set.') + elif options.printinfo: + info = rds.get_info() + if info: + pprint.pprint(vars(info)) + else: + print 'No DB instance "%s" found on your AWS account and %s region(s).' % (options.ident, options.region) + + sys.exit() + elif not options.metric or options.metric not in metrics.keys(): + parser.print_help() + parser.error('Metric is not set or not valid.') + elif not options.warn and options.metric != 'status': + parser.print_help() + parser.error('Warning threshold is not set.') + elif not options.crit and options.metric != 'status': + parser.print_help() + parser.error('Critical threshold is not set.') + elif options.avg <= 0 and options.metric != 'status': + parser.print_help() + parser.error('Average must be greater than zero.') + elif options.time <= 0 and options.metric != 'status': + parser.print_help() + parser.error('Time must be greater than zero.') + + now = datetime.datetime.utcnow() + status = None + note = '' + perf_data = None + + # RDS Status + if options.metric == 'status': + info = rds.get_info() + if not info: + status = UNKNOWN + note = 'Unable to get RDS instance' + else: + status = OK + try: + version = info.EngineVersion + except: + version = info.engine_version + + note = '%s %s. Status: %s' % (info.engine, version, info.status) + + # RDS Load Average + elif options.metric == 'load': + # Check thresholds + try: + warns = [float(x) for x in options.warn.split(',')] + crits = [float(x) for x in options.crit.split(',')] + fail = len(warns) + len(crits) + except: + fail = 0 + + if fail != 6: + parser.error('Warning and critical thresholds should be 3 comma separated numbers, e.g. 20,15,10') + + loads = [] + fail = False + j = 0 + perf_data = [] + for i in [1, 5, 15]: + if i == 1: + # Some stats are delaying to update on CloudWatch. + # Let's pick a few points for 1-min load avg and get the last point. + points = 5 + else: + points = i + + load = rds.get_metric(metrics[options.metric], now - datetime.timedelta(seconds=points * 60), now, i * 60) + if not load: + status = UNKNOWN + note = 'Unable to get RDS statistics' + perf_data = None + break + + loads.append(str(load)) + perf_data.append('load%s=%s;%s;%s;0;100' % (i, load, warns[j], crits[j])) + + # Compare thresholds + if not fail: + if warns[j] > crits[j]: + parser.error('Parameter inconsistency: warning threshold is greater than critical.') + elif load >= crits[j]: + status = CRITICAL + fail = True + elif load >= warns[j]: + status = WARNING + + j = j + 1 + + if status != UNKNOWN: + if status is None: + status = OK + + note = 'Load average: %s%%' % '%, '.join(loads) + perf_data = ' '.join(perf_data) + + # RDS Free Storage + # RDS Free Memory + elif options.metric in ['storage', 'memory']: + # Check thresholds + try: + warn = float(options.warn) + crit = float(options.crit) + except: + parser.error('Warning and critical thresholds should be integers.') + + if crit > warn: + parser.error('Parameter inconsistency: critical threshold is greater than warning.') + + if options.unit not in units: + parser.print_help() + parser.error('Unit is not valid.') + + info = rds.get_info() + free = rds.get_metric(metrics[options.metric], now - datetime.timedelta(seconds=options.time * 60), + now, options.avg * 60) + if not info or not free: + status = UNKNOWN + note = 'Unable to get RDS details and statistics' + else: + if options.metric == 'storage': + storage = float(info.allocated_storage) + elif options.metric == 'memory': + try: + storage = db_classes[info.instance_class] + except: + print 'Unknown DB instance class "%s"' % info.instance_class + sys.exit(CRITICAL) + + free = '%.2f' % (free / 1024 ** 3) + free_pct = '%.2f' % (float(free) / storage * 100) + if options.unit == 'percent': + val = float(free_pct) + val_max = 100 + elif options.unit == 'GB': + val = float(free) + val_max = storage + + # Compare thresholds + if val <= crit: + status = CRITICAL + elif val <= warn: + status = WARNING + + if status is None: + status = OK + + note = 'Free %s: %s GB (%.0f%%) of %s GB' % (options.metric, free, float(free_pct), storage) + perf_data = 'free_%s=%s;%s;%s;0;%s' % (options.metric, val, warn, crit, val_max) + + # Final output + if status != UNKNOWN and perf_data: + print '%s %s | %s' % (short_status[status], note, perf_data) + elif status == UNKNOWN and not options.forceunknown: + print '%s %s | null' % ('OK', note) + sys.exit(0) + else: + print '%s %s' % (short_status[status], note) + + sys.exit(status) + + +if __name__ == '__main__': + main() + +# ############################################################################ +# Documentation +# ############################################################################ +""" +=pod + +=head1 NAME + +pmp-check-aws-rds.py - Check Amazon RDS metrics. + +=head1 SYNOPSIS + + Usage: pmp-check-aws-rds.py [options] + + Options: + -h, --help show this help message and exit + -l, --list list of all DB instances + -n PROFILE, --profile-name=PROFILE + AWS profile from ~/.boto or /etc/boto.cfg. Default: + None, fallbacks to "[Credentials]". + -r REGION, --region=REGION + AWS region. Default: us-east-1. If set to "all", we + try to detect the instance region across all of them, + note this will be slower than you specify the region. + -i IDENT, --ident=IDENT + DB instance identifier + -p, --print print status and other details for a given DB instance + -m METRIC, --metric=METRIC + metric to check: [status, load, storage, memory] + -w WARN, --warn=WARN warning threshold + -c CRIT, --crit=CRIT critical threshold + -u UNIT, --unit=UNIT unit of thresholds for "storage" and "memory" metrics: + [percent, GB]. Default: percent + -t TIME, --time=TIME time period in minutes to query. Default: 5 + -a AVG, --avg=AVG time average in minutes to request. Default: 1 + -f, --forceunknown force alerts on unknown status. This prevents issues + related to AWS Cloudwatch throttling limits Default: + False + -d, --debug enable debug output + +=head1 REQUIREMENTS + +This plugin is written on Python and utilizes the module C<boto> (Python interface +to Amazon Web Services) to get various RDS metrics from CloudWatch and compare +them against the thresholds. + +* Install the package: C<yum install python-boto> or C<apt-get install python-boto> +* Create a config /etc/boto.cfg or ~nagios/.boto with your AWS API credentials. + See http://code.google.com/p/boto/wiki/BotoConfig + +This plugin that is supposed to be run by Nagios, i.e. under ``nagios`` user, +should have permissions to read the config /etc/boto.cfg or ~nagios/.boto. + +Example: + + [root@centos6 ~]# cat /etc/boto.cfg + [Credentials] + aws_access_key_id = THISISATESTKEY + aws_secret_access_key = thisisatestawssecretaccesskey + +If you do not use this config with other tools such as our Cacti script, +you can secure this file the following way: + + [root@centos6 ~]# chown nagios /etc/boto.cfg + [root@centos6 ~]# chmod 600 /etc/boto.cfg + +=head1 DESCRIPTION + +The plugin provides 4 checks and some options to list and print RDS details: + +* RDS Status +* RDS Load Average +* RDS Free Storage +* RDS Free Memory + +To get the list of all RDS instances under AWS account: + + # ./pmp-check-aws-rds.py -l + +To get the detailed status of RDS instance identified as C<blackbox>: + + # ./pmp-check-aws-rds.py -i blackbox -p + +Nagios check for the overall status. Useful if you want to set the rest +of the checks dependent from this one: + + # ./pmp-check-aws-rds.py -i blackbox -m status + OK mysql 5.1.63. Status: available + +Nagios check for CPU utilization, specify thresholds as percentage of +1-min., 5-min., 15-min. average accordingly: + + # ./pmp-check-aws-rds.py -i blackbox -m load -w 90,85,80 -c 98,95,90 + OK Load average: 18.36%, 18.51%, 15.95% | load1=18.36;90.0;98.0;0;100 load5=18.51;85.0;95.0;0;100 load15=15.95;80.0;90.0;0;100 + +Nagios check for the free memory, specify thresholds as percentage: + + # ./pmp-check-aws-rds.py -i blackbox -m memory -w 5 -c 2 + OK Free memory: 5.90 GB (9%) of 68 GB | free_memory=8.68;5.0;2.0;0;100 + # ./pmp-check-aws-rds.py -i blackbox -m memory -u GB -w 4 -c 2 + OK Free memory: 5.90 GB (9%) of 68 GB | free_memory=5.9;4.0;2.0;0;68 + +Nagios check for the free storage space, specify thresholds as percentage or GB: + + # ./pmp-check-aws-rds.py -i blackbox -m storage -w 10 -c 5 + OK Free storage: 162.55 GB (33%) of 500.0 GB | free_storage=32.51;10.0;5.0;0;100 + # ./pmp-check-aws-rds.py -i blackbox -m storage -u GB -w 10 -c 5 + OK Free storage: 162.55 GB (33%) of 500.0 GB | free_storage=162.55;10.0;5.0;0;500.0 + +By default, the region is set to ``us-east-1``. You can re-define it globally in boto config or +specify with -r option. The following command will list all instances across all regions under your AWS account: + + # ./pmp-check-aws-rds.py -r all -l + +The following command will show the status for the first instance identified as ``blackbox`` in all regions: + + # ./pmp-check-aws-rds.py -r all -i blackbox -p + +Remember, scanning regions are slower operation than specifying it explicitly. + +=head1 CONFIGURATION + +Here is the excerpt of potential Nagios config: + + define host{ + use mysql-host + host_name blackbox + alias blackbox + address blackbox.abcdefgh.us-east-1.rds.amazonaws.com + } + + define servicedependency{ + host_name blackbox + service_description RDS Status + dependent_service_description RDS Load Average, RDS Free Storage, RDS Free Memory + execution_failure_criteria w,c,u,p + notification_failure_criteria w,c,u,p + } + + define service{ + use active-service + host_name blackbox + service_description RDS Status + check_command check_rds!status!0!0 + } + + define service{ + use active-service + host_name blackbox + service_description RDS Load Average + check_command check_rds!load!90,85,80!98,95,90 + } + + define service{ + use active-service + host_name blackbox + service_description RDS Free Storage + check_command check_rds!storage!10!5 + } + + define service{ + use active-service + host_name blackbox + service_description RDS Free Memory + check_command check_rds!memory!5!2 + } + + define command{ + command_name check_rds + command_line $USER1$/pmp-check-aws-rds.py -i $HOSTALIAS$ -m $ARG1$ -w $ARG2$ -c $ARG3$ + } + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2014 Percona LLC and/or its affiliates. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-aws-rds.py $VERSION$ + +=cut + +""" diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-lvm-snapshots b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-lvm-snapshots new file mode 100755 index 0000000..6aa4d11 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-lvm-snapshots @@ -0,0 +1,187 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Print the name and fullness of every LVM snapshot that is open and +# nearly full. The input is the file with 'lvs', and the allowable fullness. +# In many cases lvs will report "File descriptor %d (...) leaked" and we ignore +# this, as it's only a warning that usually happens from a shell. +# ######################################################################## +check_lvm_snapshot_fullness() { + local FILE="$1" + local FULL="$2" + awk -v full="$FULL" ' + $1 != "LV" && $1 != "File" && $6 !~ /[^0-9.]/ && $6 > full { + print $2 "/" $1 "[" $5 "]=" $6 "%" + }' "${FILE}" +} + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + + # Get options + for o; do + case "${o}" in + -w) shift; OPT_WARN="${1}"; shift; ;; + -c) shift; OPT_CRIT="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_WARN=${OPT_WARN:-90} + OPT_CRIT=${OPT_CRIT:-95} + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + local NOTE="OK no full LVM snapshot volumes" + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + + # The lvs command is usually in /usr/sbin. But if it's run as a non-root + # user, it will print out "WARNING: Running as a non-root user. Functionality + # may be unavailable." and exit with success anyway. So we have to detect + # this and make the plugin exit UNKNOWN in that case. If there is a $1 it's + # the output of lvs. + PATH="$PATH:/usr/sbin:/sbin" + if [ -z "$1" ]; then + lvs > "${TEMP}" 2>&1 + else + cat "$1" > "${TEMP}" 2>/dev/null # For testing only + fi + + if grep 'command not found' "${TEMP}" > /dev/null 2>&1; then + NOTE="OK $(cat "${TEMP}")" + elif grep 'WARNING: Running as a non-root user' "${TEMP}" >/dev/null 2>&1; then + NOTE="UNK You must execute lvs with root privileges" + else + local VOLS=$(check_lvm_snapshot_fullness "${TEMP}" "${OPT_CRIT}") + if [ "${VOLS}" ]; then + NOTE="CRIT LVM snapshot volumes over ${OPT_CRIT}% full: ${VOLS}" + else + VOLS=$(check_lvm_snapshot_fullness "${TEMP}" "${OPT_WARN}") + if [ "${VOLS}" ]; then + NOTE="WARN LVM snapshot volumes over ${OPT_WARN}% full: ${VOLS}" + fi + fi + fi + + echo $NOTE +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-lvm-snapshots" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-lvm-snapshots - Alert when LVM snapshots are running out of copy-on-write space. + +=head1 SYNOPSIS + + Usage: pmp-check-lvm-snapshots [OPTIONS] + Options: + -c CRIT Critical threshold; default 95%. + -w WARN Warning threshold; default 90%. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin looks at the output of the 'lvs' command to find LVM snapshot volumes +that are beginning to run out of copy-on-write space. If a snapshot fills up its +copy-on-write space, it will fail. This is also a useful way to detect whether +some process, such as a backup, failed to release a snapshot volume after +finishing with it. + +=head1 PRIVILEGES + +This plugin does not access MySQL. + +This plugin executes the following UNIX commands that may need special privileges: + +=over + +=item * + +lvs + +=back + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-lvm-snapshots $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mongo.py b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mongo.py new file mode 100755 index 0000000..0de0987 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mongo.py @@ -0,0 +1,594 @@ +#!/usr/bin/env python2.7 +"""MongoDB Nagios check script + +This program is part of $PROJECT_NAME$ +License: GPL License (see COPYING) + +Author David Murphy +Copyright 2014-2015 Percona LLC and/or its affiliates +""" + +import sys +import time +import optparse +import os +import stat +import pickle +import traceback +import pprint + +from types import FunctionType +# Not yet implemented +# import DeepDiff + +try: + import pymongo +except ImportError, e: + print e + sys.exit(2) + +# As of pymongo v 1.9 the SON API is part of the BSON package, therefore attempt +# to import from there and fall back to pymongo in cases of older pymongo +if pymongo.version >= "1.9": + import bson.son as son +else: + import pymongo.son as son + + +# Adding special behavior for optparse +class OptionParsingError(RuntimeError): + def __init__(self, msg): + self.msg = msg + + +class ModifiedOptionParser(optparse.OptionParser): + def error(self, msg): + raise OptionParsingError(msg) + +def unicode_truncate(s, length, encoding='utf-8'): + encoded = s.encode(encoding)[:length] + return encoded.decode(encoding, 'ignore') + +def parse_options(args): + funcList = [] + for item_name, item_type in NagiosMongoChecks.__dict__.items(): + if type(item_type) is FunctionType and item_name.startswith("check_") and item_name is not 'check_levels': + funcList.append(item_name) + p = ModifiedOptionParser() + + p.add_option('-H', '--host', action='store', type='string', dest='host', default='127.0.0.1', help='The hostname you want to connect to') + p.add_option('-P', '--port', action='store', type='int', dest='port', default=27017, help='The port mongodb is running on') + p.add_option('-u', '--user', action='store', type='string', dest='user', default=None, help='The username you want to login as') + p.add_option('-p', '--password', action='store', type='string', dest='passwd', default=None, help='The password you want to use for that user') + p.add_option('-W', '--warning', action='store', dest='warning', default=None, help='The warning threshold you want to set') + p.add_option('-C', '--critical', action='store', dest='critical', default=None, help='The critical threshold you want to set') + p.add_option('-A', '--action', action='store', type='choice', dest='action', default='check_connect', + choices=funcList, help="The action you want to take. Valid choices are (%s) Default: %s" % (", ".join(funcList), 'check_connect')) + p.add_option('-s', '--ssl', dest='ssl', default=False, help='Connect using SSL') + p.add_option('-r', '--replicaset', dest='replicaset', default=None, help='Connect to replicaset') + p.add_option('-c', '--collection', action='store', dest='collection', default='foo', help='Specify the collection in check_cannary_test') + p.add_option('-d', '--database', action='store', dest='database', default='tmp', help='Specify the database in check_cannary_test') + p.add_option('-q', '--query', action='store', dest='query', default='{"_id":1}', help='Specify the query in check_cannary_test') + p.add_option('--statusfile', action='store', dest='status_filename', default='status.dat', help='File to current store state data in for delta checks') + p.add_option('--backup-statusfile', action='store', dest='status_filename_backup', default='status_backup.dat', + help='File to previous store state data in for delta checks') + p.add_option('--max-stale', action='store', dest='max_stale', type='int', default=60, help='Age of status file to make new checks (seconds)') + # Add options for output stat file + try: + result = p.parse_args() + except OptionParsingError, e: + if 'no such option' in e.msg: + sys.exit("UNKNOWN - No such options of %s" % e.msg.split(":")[1]) + if 'invalid choice' in e.msg: + error_item = e.msg.split(":")[2].split("'")[1] + sys.exit('UNKNOWN - No such action of %s found!' % error_item) + return result + + +def return_result(result_type, message): + if result_type == "ok": + print "OK - " + message + sys.exit(0) + elif result_type == "critical": + print "CRITICAL - " + message + sys.exit(2) + elif result_type == "warning": + print "WARNING - " + message + sys.exit(1) + else: + print "UNKNOWN - " + message + sys.exit(2) + + +def main(argv): + options, arguments = parse_options(argv) + check(options, options.action) + + +def check(args, check_name): + try: + checksObj = globals()['NagiosMongoChecks'](args) + run_check = getattr(checksObj, check_name) + result_type, message = run_check(args, args.warning, args.critical) + except Exception, e: + raise + print(traceback.extract_tb(sys.exc_info()[-1], 1)) + return_result("critical", str(e)) + return_result(result_type, message) + + +class NagiosMongoChecks: + # need to initialize variables and such still + def __init__(self, args): + # setup inital values from optParse + self.host = '127.0.0.1' + self.port = 27017 + self.user = None + self.password = None + self.warning = None + self.critical = None + self.action = 'check_connect' + self.ssl = False + self.replicaset = None + self.collection = 'foo' + self.database = 'tmp' + self.query = '{"_id":1}' + self.status_filename = 'status.dat' + self.status_filename_backup = 'status_backup.dat' + self.max_stale = 60 + + for option in vars(args): + setattr(self, option, getattr(args, option)) + + # Fix filepaths to be relative + if not self.status_filename.startswith("/") or not self.status_filename.startswith(".."): + self.status_filename_backup = "%s/%s" % (os.curdir, self.status_filename_backup) + self.status_filename = "%s/%s" % (os.curdir, self.status_filename) + + # ammend known intenal values we will need + self.current_status = {} + self.last_status = {} + self.connection = None + self.connection_time = None + self.pyMongoError = None + + self.connect() + + if self.file_age(self.status_filename) <= self.max_stale: + # Save status_file contents status as current_status + self.get_last_status(True) + # Save status_filename_backup contents as last_status + self.get_last_status(False, self.status_filename_backup) + else: + if self.connection is None: + raise pymongo.errors.ConnectionFailure(self.pyMongoError or "No connection Found, did connect fail?") + # Get fresh current_status from server + self.current_status = self.sanatize(self.get_server_status()) + # user last status_filename contents as last_status + self.get_last_status(False, self.status_filename) + # Not yet implemented + # self.compute_deltas() + + # get last status + # check if needs refresh, refresh if needed + # set last/current to self.current_status + pass + + def get_last_status(self, returnAsCurrent, forceFile=None): + # Open file using self.file + try: + file_name = forceFile if forceFile is not None else self.status_filename + fileObject = open(file_name, 'r') + if returnAsCurrent is None or returnAsCurrent is False: + self.last_status = pickle.load(fileObject) + else: + self.current_status = pickle.load(fileObject) + except Exception: + return False + return True + + def get_server_status(self): + try: + data = self.connection['admin'].command(pymongo.son_manipulator.SON([('serverStatus', 1)])) + except: + try: + data = self.connection['admin'].command(son.SON([('serverStatus', 1)])) + except Exception, e: + if type(e).__name__ == "OperationFailure": + sys.exit("UNKNOWN - Not authorized!") + else: + sys.exit("UNKNOWN - Unable to run serverStatus: %s::%s" % (type(e).__name__, unicode_truncate(e.message, 45))) + + if self.current_status is None: + self.current_status = data + + return data + + # figure out how to use this one later + def rotate_files(self): + # 1)this needs to rename self.status_filename to status_filename_backup + # 2) Save current_status to self.status_filename ( new file ) + if self.last_status == {}: + # Build the last status file for future deltas from current data + self.save_file(self.status_filename_backup, self.current_status) + # Set the current status file to empty to set the aging clock + self.save_file(self.status_filename, {}) + sys.exit("UNKNOWN - No status data present, please try again in %s seconds" % self.max_stale) + else: + self.save_file(self.status_filename_backup, self.last_status) + self.save_file(self.status_filename, self.current_status) + + + def save_file(self, filename, contents): + try: + pickle.dump(contents, open(filename, "wb")) + except Exception, e: + sys.exit("UNKNOWN - Error saving stat file %s: %s" % (filename, e.message)) + + # TODO - Fill in all check defaults + def get_default(self, key, level): + + defaults = { + 'check_connections': {'warning': 15000, 'critical': 19000}, + 'check_connect': {'warning': 50, 'critical': 100}, + 'check_queues': {'warning': 30, 'critical': 100}, + 'check_lock_pct': {'warning': 30, 'critical': 50}, + 'check_repl_lag': {'warning': 200, 'critical': 500}, + # 'check_flushing': {'warning':XX, 'critical': XX}, + 'check_total_indexes': {'warning': 100, 'critical': 300}, + 'check_cannary_test': {'warning': 30, 'critical': 50}, + 'check_oplog': {'warning': 36, 'critical': 24}, + 'check_index_ratio': {'warning': .9, 'critical': .8}, + } + try: + return defaults[key][level] + except KeyError: + sys.exit("UNKNOWN - Missing defaults found for %s please use -w and -c" % key) + + # Not yet implemented + # def compute_deltas(self): + # deltas = [] + # for item in DeepDiff(self.last_status, self.current_status)['values_changed']: + # name = item.split(":")[0].split("root")[1].replace("['", "").replace("']", ".")[:-1] + # if 'time' not in item.lower(): + # values = item.split(":")[1] + # print(values) + # old, new = values.split("===>") + # print("%s: %s - %s = %s" % (name, new, old, float(new)-float(old))) + # deltas[name] = float(new) - float(old) + # self.delta_data = deltas + # return True + + def file_age(self, filename): + try: + age = time.time() - os.stat(filename)[stat.ST_CTIME] + except OSError: + age = 999999 + return age + + # TODO - Add meat to this if needed, here for future planning + def sanatize(self, status_output): + return status_output + + def connect(self): + start_time = time.time() + try: + # ssl connection for pymongo > 2.3 + if self.replicaset is None: + con = pymongo.MongoClient(self.host, self.port, ssl=self.ssl, serverSelectionTimeoutMS=2500) + else: + con = pymongo.MongoClient(self.host, self.port, ssl=self.ssl, replicaSet=self.replicaset, serverSelectionTimeoutMS=2500) + if (self.user and self.passwd) and not con['admin'].authenticate(self.user, self.passwd): + sys.exit("CRITICAL - Username and password incorrect") + except Exception, e: + raise + if isinstance(e, pymongo.errors.AutoReconnect) and str(e).find(" is an arbiter") != -1: + # We got a pymongo AutoReconnect exception that tells us we connected to an Arbiter Server + # This means: Arbiter is reachable and can answer requests/votes - this is all we need to know from an arbiter + print "OK - State: 7 (Arbiter)" + sys.exit(0) + con = None + self.pyMongoError = str(e) + if con is not None: + try: + con['admin'].command(pymongo.son_manipulator.SON([('ping', 1)])) + except Exception, e: + sys.exit("UNKNOWN - Unable to run commands, possible auth issue: %s" % e.message) + self.connection_time = round(time.time() - start_time, 2) + version = con.server_info()['version'].split('.') + self.mongo_version = (version[0], version[1], version[2]) + self.connection = con + + def check_levels(self, check_result, warning_level, critical_level, message): + if check_result < warning_level: + return "ok", message + elif check_result > critical_level: + return "critical", message + elif check_result > warning_level and check_result < critical_level: + return "warning", message + else: + return "unknown", "Unable to parse %s into a result" % check_result + + def check_connect(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_connect', 'warning') + critical_level = critical_level or self.get_default('check_connect', 'critical') + con_time = self.connection_time + message = "Connection time %.2f ms" % con_time + return self.check_levels(float(con_time), float(warning_level), float(critical_level), message) + + def check_connections(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_connections', 'warning') + critical_level = critical_level or self.get_default('check_connections', 'critical') + connections = self.current_status['connections'] + connections['total'] = connections['available'] + connections['current'] + used_percent = int((connections['current'] / connections['total']) * 100) + message = "%i%% connections used ( %d of %d )" % (used_percent, connections['current'], connections['total']) + return self.check_levels(float(used_percent), int(warning_level), int(critical_level), message) + + def check_lock_pct(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_lock_pct', 'warning') + critical_level = critical_level or self.get_default('check_lock_pct', 'critical') + if self.mongo_version >= ('2', '7', '0'): + return "ok", "Mongo 3.0 and above do not have lock %" + lockTime = self.current_status['globalLock']['lockTime'] - self.last_status['globalLock']['lockTime'] + totalTime = self.current_status['globalLock']['totalTime'] - self.last_status['globalLock']['totalTime'] + lock_percent = int((lockTime / totalTime) * 100) + message = "%i%% locking found (over 100%% is possible)" % (lock_percent) + return self.check_levels(lock_percent, warning_level, critical_level, message) + + def check_flushing(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_flushing', 'warning') + critical_level = critical_level or self.get_default('check_flushing', 'critical') + flushData = self.current_status['backgroundFlushing'] + if args.average: + flush_time = flushData['average_ms'] + stat_type = "Average" + else: + flush_time = flushData['last_ms'] + stat_type = "Last" + + message = "%s Flush Time: %.2fms" % (stat_type, flush_time) + return self.check_levels(flush_time, warning_level, critical_level, message) + + def check_index_ratio(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_index_ratio', 'warning') + critical_level = critical_level or self.get_default('check_index_ratio', 'critical') + message = None + + indexCounters = self.current_status['indexCounters'] + if 'note' in indexCounters: + ratio = 1.0 + message = "not supported defaulting to 1.0 ratio" + elif self.mongo_version >= ('2', '4', '0'): + ratio = indexCounters['missRatio'] + else: + ratio = indexCounters['btree']['missRatio'] + if message is None: + message = "Miss Ratio: %.2f" % ratio + return self.check_levels(ratio, warning_level, critical_level, message) + + def check_have_primary(self, args, warning_level, critical_level): + replset_status = self.connection['admin'].command("replSetGetStatus") + for member in replset_status['members']: + if member['state'] == 1: + return "ok", "Cluster has primary" + return "critical", "Cluster has no primary!" + + def check_total_indexes(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_total_indexes', 'warning') + critical_level = critical_level or self.get_default('check_total_indexes', 'critical') + index_count = 0 + database_count = 0 + for database in self.connection.database_names(): + if database not in ["admin", "local"]: + database_count += 1 + self.connection[database]['system.indexes'].count() + index_count += self.connection[database]['system.indexes'].count() + message = "Found %d indexes in %d databases" % (index_count, database_count) + return self.check_levels(index_count, warning_level, critical_level, message) + + def check_queues(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_queues', 'warning') + critical_level = critical_level or self.get_default('check_queues', 'critical') + currentQueue = self.current_status['globalLock']['currentQueue'] + currentQueue['total'] = currentQueue['readers'] + currentQueue['writers'] + message = "Queue Sizes: read (%d) write(%d) total (%d)" % (currentQueue['readers'], currentQueue['writers'], currentQueue['total']) + return self.check_levels(currentQueue['total'], warning_level, critical_level, message) + + def check_oplog(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_oplog', 'warning') + critical_level = critical_level or self.get_default('check_oplog', 'critical') + if 'local' not in self.connection.database_names() or 'oplog.rs' not in self.connection['local'].collection_names(): + return "critical", "We do not seem to be in a replset!" + oplog = self.connection['local']['oplog.rs'] + first_ts = oplog.find().sort("$natural", pymongo.ASCENDING).limit(1)[0]['ts'] + last_ts = oplog.find().sort("$natural", pymongo.DESCENDING).limit(1)[0]['ts'] + oplog_range = (last_ts.as_datetime() - first_ts.as_datetime()) + oplog_range_hours = oplog_range.total_seconds() / 60 / 60 + message = "Oplog Time is %d hours" % (oplog_range_hours) + return self.check_levels(int(oplog_range_hours), warning_level, critical_level, message) + + def check_election(self, args, warning_level, critical_level): + replset_status = self.connection['admin'].command("replSetGetStatus") + for member in replset_status['members']: + if member['stateStr'] == "PRIMARY": + #last_primary = member.name + last_primary = member['name'] + for member in replset_status['members']: + if member['stateStr'] == "PRIMARY": + current_primary = member['name'] + message = "Old PRI: %s New PRI: %s" % (last_primary, current_primary) + if current_primary == last_primary: + return "ok", message + else: + return "critical", message + + def is_balanced(self): + chunks = {} + + # Loop through each of the chunks, tallying things up + for chunk in self.connection["config"]["chunks"].find(): + namespace = chunk['ns'] + shard = chunk['shard'] + if namespace not in chunks: + chunks[namespace] = {'shards': {}, 'total': 0} + if shard not in chunks[namespace]['shards']: + chunks[namespace]['shards'][shard] = 0 + chunks[namespace]['shards'][shard] += 1 + chunks[namespace]['total'] += 1 + + shardsCount = self.connection["config"]["shards"].count() + chunksCount = self.connection["config"]["chunks"].count() + + # Different migration thresholds depending on cluster size + # http://docs.mongodb.org/manual/core/sharding-internals/#sharding-migration-thresholds + if chunksCount < 20: + threshold = 2 + elif chunksCount < 80 and chunksCount > 21: + threshold = 4 + else: + threshold = 8 + + # Default to balanced state, any failure will then mark it as False forevermore + isBalanced = True + # Loop through each ns and determine if it's balanced or not + for ns in chunks: + balanced = chunks[ns]['total'] / shardsCount + for shard in chunks[ns]['shards']: + if shard > balanced - threshold and shard < balanced + threshold: + pass + else: + isBalanced = False + + return isBalanced + + def check_balance(self, args, warning_level, critical_level): + if self.is_balanced() is True: + return "ok", "Shards are balanced by chunk counts" + else: + return "critcal", "Shards are not balanced by chunk and need review" + + def check_cannary_test(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_cannary_test', 'warning') + critical_level = critical_level or self.get_default('check_cannary_test', 'critical') + # this does not check for a timeout, we assume NRPE or Nagios will alert on that timeout. + try: + start = time.time() + self.connection[self.database][self.collection].find_one(self.query) + time_range = (time.time() - start).total_seconds + message = "Collection %s.%s query took: %d s" % (self.database, self.collection, time_range) + return self.check_levels(time_range, warning_level, critical_level, message) + except Exception, e: + message = "Collection %s.%s query FAILED: %s" % (self.database, self.collection, e) + return "critical", message + + def check_repl_lag(self, args, warning_level, critical_level): + warning_level = warning_level or self.get_default('check_repl_lag', 'warning') + critical_level = critical_level or self.get_default('check_repl_lag', 'critical') + + # make a write incase the client is not writing, but us an update to avoid wasting space + self.connection['test']['lag_check'].update({"_id":1}, {"_id": 1, "x": 1}) + # get a fresh status for the replset + try: + replset_status = self.connection['admin'].command("replSetGetStatus") + except Exception, e: + return "critical", "Are your running with --replset? - %s" % (e) + + for member in replset_status['members']: + if member['stateStr'] == "PRIMARY": + primary = member + if 'self' in member and member['self'] is True: + hostOptimeDate = member['optimeDate'] + + if primary is not None: + highest_optimeDate = primary['optimeDate'] + highest_name = primary['name'] + else: + # find the most current secondary as there is not primary + highest_optimeDate = time.gmtime(0) + for member in replset_status['members']: + if member['optimeDate'] > highest_optimeDate: + highest_optimeDate = member['optimeDate'] + highest_name = member['name'] + + rep_lag_seconds = (highest_optimeDate - hostOptimeDate).seconds + rep_lag_hours = round(rep_lag_seconds/60/60, 4) + message = "Lagging %s by %.4f hours" % (highest_name, rep_lag_hours) + return self.check_levels(rep_lag_hours, warning_level, critical_level, message) + +# +# main app +# +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) + +# ############################################################################ +# Documentation +# ############################################################################ +""" +=pod + +=head1 NAME + +pmp-check-mongo.py - MongoDB Nagios check script. + +=head1 SYNOPSIS + + Usage: pmp-check-mongo.py [options] + + Options: + -h, --help show this help message and exit + -H HOST, --host=HOST The hostname you want to connect to + -P PORT, --port=PORT The port mongodb is running on + -u USER, --user=USER The username you want to login as + -p PASSWD, --password=PASSWD + The password you want to use for that user + -W WARNING, --warning=WARNING + The warning threshold you want to set + -C CRITICAL, --critical=CRITICAL + The critical threshold you want to set + -A ACTION, --action=ACTION + The action you want to take. Valid choices are + (check_connections, check_election, check_lock_pct, + check_repl_lag, check_flushing, check_total_indexes, + check_balance, check_queues, check_cannary_test, + check_have_primary, check_oplog, check_index_ratio, + check_connect) Default: check_connect + -s SSL, --ssl=SSL Connect using SSL + -r REPLICASET, --replicaset=REPLICASET + Connect to replicaset + -c COLLECTION, --collection=COLLECTION + Specify the collection in check_cannary_test + -d DATABASE, --database=DATABASE + Specify the database in check_cannary_test + -q QUERY, --query=QUERY + Specify the query in check_cannary_test + --statusfile=STATUS_FILENAME + File to current store state data in for delta checks + --backup-statusfile=STATUS_FILENAME_BACKUP + File to previous store state data in for delta checks + --max-stale=MAX_STALE + Age of status file to make new checks (seconds) + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2014 Percona LLC and/or its affiliates. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mongo.py $VERSION$ + +=cut + +""" diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deadlocks b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deadlocks new file mode 100755 index 0000000..0545f06 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deadlocks @@ -0,0 +1,189 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz +# Depends-on: pt-deadlock-logger +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -i) shift; OPT_INTERVAL="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -T) shift; OPT_TABLE="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_WARN=${OPT_WARN:-12} + OPT_CRIT=${OPT_CRIT:-60} + OPT_INTERVAL=${OPT_INTERVAL:-1} + OPT_TABLE="${OPT_TABLE:-percona.deadlocks}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + LEVEL=$(mysql_exec "SELECT COUNT(*) FROM ${OPT_TABLE} WHERE server IN ('${OPT_HOST}', @@hostname) AND ts >= NOW() - INTERVAL ${OPT_INTERVAL}*60 SECOND") + if [ $? = 0 ]; then + NOTE="${LEVEL:-UNKNOWN} deadlocks in last ${OPT_INTERVAL} minutes" + if [ "${LEVEL:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + elif [ "${LEVEL:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE" + else + NOTE="OK $NOTE" + fi + + # Build the common perf data output for graph trending + PERFDATA="deadlocks=${LEVEL:-0};${OPT_WARN};${OPT_CRIT};0;" + NOTE="$NOTE | $PERFDATA" + else + NOTE="UNK could not count deadlocks" + fi + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_USER:+-u"${OPT_USER}"} \ + ${OPT_PASS:+-p"${OPT_PASS}"} ${OPT_SOCK:+-S"${OPT_SOCK}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} -ss -e "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-deadlocks" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-deadlocks - Alert when pt-deadlock-logger has recorded too many recent deadlocks. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-deadlocks [OPTIONS] + Options: + -c CRIT Critical threshold; default 60. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -i INTERVAL Interval over which to count, in minutes; default 1. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -T TABLE The database.table that pt-deadlock-logger uses; default percona.deadlocks. + -w WARN Warning threshold; default 12. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin looks at the table that pt-deadlock-logger (part of Percona +Toolkit) maintains, and when there have been too many recent deadlocks, it +alerts. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> from the C<pt-deadlock-logger> table. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-deadlocks $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deleted-files b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deleted-files new file mode 100755 index 0000000..a23c098 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-deleted-files @@ -0,0 +1,286 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -t) shift; OPT_TMPDIR="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # If any connection option was given, then try to log in to find the server's + # tmpdir. + if [ "${OPT_DEFT}${OPT_HOST}${OPT_USER}${OPT_PASS}${OPT_PORT}${OPT_SOCK}" ]; then + OPT_TMPDIR=$(mysql_exec "SELECT @@tmpdir") || exit $? + elif [ -z "${OPT_TMPDIR}" ]; then + OPT_TMPDIR="${TMPDIR:-/tmp/}" + fi + + # TODO: We could auto-check every running instance, not just one. + local NOTE="OK no deleted files" + local PROC_ID=$(_pidof mysqld | head -n1) + if [ "${PROC_ID}" ]; then + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + if _lsof "${PROC_ID}" > "${TEMP}" ; then + # If lsof exists, but you run it as non-root, you'll get a file with a + # bunch of this stuff: + # mysqld 15287 ... /proc/15287/cwd (readlink: Permission denied) + # We have to detect this and return UNK. + if grep -v -e denied -e COMMAND "${TEMP}" >/dev/null 2>&1; then + local FILES=$(check_deleted_files "${TEMP}" "${OPT_TMPDIR}") + NOTE="open but deleted files: ${FILES}" + if [ "${FILES}" -a -z "${OPT_WARN}" ]; then + NOTE="CRIT $NOTE" + elif [ "${FILES}" ]; then + NOTE="WARN $NOTE" + else + NOTE="OK no deleted files" + fi + else + NOTE="UNK You must execute lsof with root privileges" + fi + else + NOTE="UNK could not list MySQL's open files" + fi + else + NOTE="UNK could not find a mysqld process" + fi + echo $NOTE +} + +# ######################################################################## +# A wrapper around pidof, which might not exist. The first argument is the +# command name to match. +# ######################################################################## +_pidof() { + if ! pidof "${1}" 2>/dev/null; then + ps axo pid,ucomm | awk -v comm="${1}" '$2 == comm { print $1 }' + fi +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# A wrapper around lsof, which might not exist. The first argument is the +# process ID to match. Otherwise, the fallback of listing /proc/pid/fd +# will probably only work on Linux. For BSD, fstat will be used. +# ######################################################################## +_lsof() { + PATH="$PATH:/usr/sbin:/sbin" + if ! lsof -p $1 2>/dev/null; then + if ! /bin/ls -l /proc/$1/fd 2>/dev/null; then + fstat -p $1 2>/dev/null + fi + fi +} + +# ######################################################################## +# Generate a list of file handles that MySQL has open, but which are deleted, +# and are not temp files such as /tmp/ib* files (InnoDB) or /tmp/ML* files +# (binary logging). The first argument is a file containing the output of lsof +# or ls -l for the open files. The second argument is the server's tmpdir. +# ######################################################################## +check_deleted_files() { + awk -v tmpdir="${2}" ' + /\(deleted\)/ { if ( index($0, tmpdir) == 0 ) { + if ( $NF ~ /deleted/ ) { + lf=NF-1; + } + else { + lf=NF; + } + print $lf; + }}' "${1}" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-deleted-files" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-deleted-files - Alert when MySQL's files are deleted. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-deleted-files [OPTIONS] + Options: + -c CRIT Critical threshold; ignored. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -w WARN Warning threshold; changes the alert to WARN instead of CRIT. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin looks at the files that the mysqld process has open, and +warns if any of them are deleted that shouldn't be. This typically happens when +there is a poorly written logrotate script or when a human makes a mistake at +the command line. This can cause several bad effects. If a table has been +deleted, of course, it is a serious matter. Such a file can also potentially +fill up the disk invisibly. If the file is the server's log, it might mean that +logging is effectively broken and any problems the server experiences could be +undiagnosable. + +The plugin accepts the -w and -c options for compatibility with standard Nagios +plugin conventions, but they are not based on a threshold. Instead, the plugin +raises a critical alert by default, and if the -w option is given, it raises a +warning instead, regardless of the option's value. + +This plugin doesn't alert about deleted temporary files, which are not a +problem. By default, this plugin assumes that the server's temporary directory +is either the TMPDIR environment variable, or if that is not set, then /tmp/. +If you specify MySQL authentication options, the value will log into the +specified MySQL instance and look at the C<tmpdir> variable to find the +temporary directory. + +This plugin looks at the first running instance of MySQL, as found in the +system process table, so it will not work on systems that have multiple +instances running. It probably works best on Linux, though it might work on +other operating systems. It relies on either lsof or fstat or the ability to +list the files in the process's /proc/pid/fd directory. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> the system variable C<@@tmpdir>. + +=back + +This plugin executes the following UNIX commands that may need special privileges: + +=over + +=item * + +ps + +=item * + +C<lsof> or C<ls /proc/$pid/fd> (Linux), C<fstat> (BSD) + +=back + +The plugin should be able to find mysqld PID using C<ps> command. + +On BSD, if C<sysctl> option C<security.bsd.see_other_uids> is set to 0, C<ps> +will not return mysqld PID if the plugin run from non-root user. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-deleted-files $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-file-privs b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-file-privs new file mode 100755 index 0000000..fa4df1c --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-file-privs @@ -0,0 +1,289 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -g) shift; OPT_UNIX_GROUP="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -u) shift; OPT_UNIX_USER="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_UNIX_GROUP="${OPT_UNIX_GROUP:-mysql}" + OPT_UNIX_USER="${OPT_UNIX_USER:-mysql}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Set the exit status in case there are any problems. + NOTE="UNK could not determine the datadir location." + + # Set up files to hold one or more data directory locations. + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + local DATADIRS=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' '${DATADIRS}' >/dev/null 2>&1" EXIT + + # If any connection option was given, then try to log in to find the datadir. + if [ "${OPT_DEFT}${OPT_HOST}${OPT_USER}${OPT_PASS}${OPT_PORT}${OPT_SOCK}" ]; then + # If this fails (e.g. we can't log in), then there will be no line in the + # file, and later we won't change the exit code / note away from "UNK". + mysql_exec "SELECT IF(@@datadir LIKE '/%', @@datadir, CONCAT(@@basedir, @@datadir))" >> "${DATADIRS}" + else + # Find all MySQL server instances. + for pid in $(_pidof mysqld); do + ps -p ${pid} -o pid,command | grep "${pid}" >> "${TEMP}" + done + # The ${TEMP} file may now contain lines like the following sample: + # 13822 /usr/sbin/mysqld --defaults-file=/var/lib/mysql/my.cnf \ + # --basedir=/usr --datadir=/var/lib/mysql/data/ \ + # --pid-file=/var/run/mysqld/mysqld.pid \ + # --socket=/var/run/mysqld/mysqld.sock + # Now the task is to read find any reference to a --datadir option. + # We store these into the $DATADIRS temp file. + # TODO: maybe in the future we can detect the user/group under which the + # process runs, and assume that is the right value, rather than defaulting + # to 'mysql'. + while read pid command; do + if echo "${command}" | grep datadir >/dev/null 2>&1; then + # Strip off everything up to and including --datadir= + command="${command##*--datadir=}" + # Strip off any options that follow this, assuming that there's not + # a space followed by a dash in the datadir's path. + echo "${command%% -*}" >> "${DATADIRS}" + fi + done < "${TEMP}" + fi + + WRONG="" + NOTE2="" + > ${TEMP} + while read datadir; do + FILES="$(find "${datadir}" \! -group "${OPT_UNIX_GROUP}" -o \! -user "${OPT_UNIX_USER}" 2>>${TEMP})" + if [ "${FILES}" ]; then + WRONG=1 + NOTE2="${NOTE2:+${NOTE2} }${FILES}" + fi + NOTE="OK all files/directories have correct ownership." + done < "${DATADIRS}" + + if [ -s "${TEMP}" ]; then + NOTE="UNK `cat ${TEMP}`" + elif [ "${WRONG}" ]; then + if [ "${OPT_CRIT}" ]; then + NOTE="CRIT files with wrong ownership: ${NOTE2}" + else + NOTE="WARN files with wrong ownership: ${NOTE2}" + fi + fi + + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# A wrapper around pidof, which might not exist. The first argument is the +# command name to match. +# ######################################################################## +_pidof() { + if ! pidof "${1}" 2>/dev/null; then + ps axo pid,ucomm | awk -v comm="${1}" '$2 == comm { print $1 }' + fi +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-file-privs" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-file-privs - Alert if MySQL data directory privileges are wrong. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-file-privs [OPTIONS] + Options: + -c CRIT Critical threshold; makes a privilege issue critical. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -g GROUP The Unix group who should own the files; default mysql. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -u USER The Unix user who should own the files; default mysql. + -w WARN Warning threshold; ignored. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin checks to make sure that the MySQL data directory, and its +contents, is owned by the correct Unix user and group. If the ownership is +incorrect, then the server might fail due to lack of permission to modify its +data. For example, suppose a system administrator enters a database directory +and creates a file that is owned by root. Now a database administrator issues a +DROP TABLE command, which fails because it is unable to remove the file and thus +the non-empty directory cannot be removed either. + +The plugin accepts the -g and -u options to specify which Unix user and group +should own the data directory and its contents. This is usually the user account +under which MySQL runs, which is mysql by default on most systems. The plugin +assumes that user and group by default, too. + +The plugin accepts the -w and -c options for compatibility with standard Nagios +plugin conventions, but they are not based on a threshold. Instead, the plugin +raises a warning by default, and if the -c option is given, it raises an error +instead, regardless of the option's value. + +By default, this plugin will attempt to detect all running instances of MySQL, +and verify the data directory ownership for each one. It does this purely by +examining the Unix process table with the C<ps> tool. However, in some cases +the process's command line does not list the path to the data directory. If the +tool fails to detect the MySQL server process, or if you wish to limit the check +to a single instance in the event that there are multiple instances on a single +server, then you can specify MySQL authentication options. This will cause the +plugin to skip examining the Unix processlist, log into MySQL, and examine the +datadir variable from SHOW VARIABLES to find the location of the data directory. + +In case an user you are calling this plugin from has no permissions to examine +the datadir the plugin raises an unknown with the explanation. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> the MySQL system variables C<@@datadir> and C<@@basedir>. + +=back + +This plugin executes the following UNIX commands that may need special privileges: + +=over + +=item * + +ps + +=item * + +find C<datadir> + +=back + +The plugin should be able to either get variables from MySQL or find mysqld +PID using C<ps> command. + +On BSD, if C<sysctl> option C<security.bsd.see_other_uids> is set to 0, C<ps> +will not return mysqld PID if the plugin run from non-root user. + +Also an user you run the plugin from should be able to access MySQL datadir +files, so you may want to add it into mysql unix group etc. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-file-privs $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-innodb b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-innodb new file mode 100755 index 0000000..038f564 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-innodb @@ -0,0 +1,368 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -C) shift; OPT_CHEK="${1}"; shift; ;; + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_CHEK="${OPT_CHEK:-idle_blocker_duration}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + NOTE="UNK could not find information about transactions" + + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + + case "${OPT_CHEK}" in + idle_blocker_duration) + OPT_WARN=${OPT_WARN:-60} + OPT_CRIT=${OPT_CRIT:-600} + OUTPUT=$(mysql_exec " + SELECT MAX(COALESCE(p.time, 0)) AS idle_in_trx, + p.id, + CONCAT(p.user, '@', p.host) AS userhost + FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS AS w + INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS b ON b.trx_id = w.blocking_trx_id + INNER JOIN INFORMATION_SCHEMA.INNODB_TRX AS r ON r.trx_id = w.requesting_trx_id + LEFT JOIN INFORMATION_SCHEMA.PROCESSLIST AS p ON p.id = b.trx_mysql_thread_id AND p.command = 'Sleep' + GROUP BY p.id, p.user, p.host + ORDER BY idle_in_trx DESC LIMIT 1" 2>"${TEMP}") + if [ "$?" != 0 ]; then + if grep "Unknown table" "${TEMP}" >/dev/null 2>&1; then + # The I_S tables don't exist. + NOTE="OK The INFORMATION_SCHEMA.INNODB_% tables don't exist." + else + cat "${TEMP}" + fi + elif [ -z "${OUTPUT}" ]; then + OUTPUT=0 + fi + ;; + waiter_count) + OPT_WARN=${OPT_WARN:-10} + OPT_CRIT=${OPT_CRIT:-25} + OUTPUT=$(mysql_exec " + SELECT COUNT(DISTINCT REQUESTING_TRX_ID) AS cnt + FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS AS w" 2>"${TEMP}") + if [ "$?" != 0 ]; then + if grep "Unknown table" "${TEMP}" >/dev/null 2>&1; then + # The I_S tables don't exist. + mysql_exec "SHOW /*!50000 ENGINE*/ INNODB STATUS\G" > "${TEMP}" || exit $? + OUTPUT=$(get_waiter_count "${TEMP}") + else + cat "${TEMP}" + fi + fi + ;; + max_duration) + OPT_WARN=${OPT_WARN:-60} + OPT_CRIT=${OPT_CRIT:-600} + OUTPUT=$(mysql_exec " + SET @@time_zone='SYSTEM'; + SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(t.trx_started), + p.id, + CONCAT(p.user, '@', p.host) + FROM INFORMATION_SCHEMA.INNODB_TRX AS t + JOIN INFORMATION_SCHEMA.PROCESSLIST AS p ON p.id = t.trx_mysql_thread_id + ORDER BY t.trx_started LIMIT 1" 2>"${TEMP}") + if [ "$?" != 0 ]; then + if grep "Unknown table" "${TEMP}" >/dev/null 2>&1; then + # The I_S tables don't exist. + mysql_exec "SHOW /*!50000 ENGINE*/ INNODB STATUS\G" > "${TEMP}" || exit $? + OUTPUT=$(get_longest_trx "${TEMP}") + if [ -z "${OUTPUT}" ]; then + OUTPUT=0 + fi + else + cat "${TEMP}" + fi + elif [ -z "${OUTPUT}" ]; then + OUTPUT=0 + fi + ;; + *) + echo "Unknown value for -C: '${OPT_CHEK}'. Consult the documentation."; + exit 1; + ;; + esac + + # $OUTPUT now contains either an empty string or three words: 1) Age of + # oldest transaction, 2) thread ID of oldest transaction, 3) user info. + if [ -n "${OUTPUT}" ]; then + LEVEL="$(echo ${OUTPUT} | awk '{print $1}')" + INFO="$(echo ${OUTPUT} | awk '{print "(thread "$2" by "$3")"}')" + + case "${OPT_CHEK}" in + idle_blocker_duration) + NOTE="longest blocking idle transaction sleeps for ${LEVEL:-UNKNOWN} seconds" + ;; + waiter_count) + NOTE="${LEVEL:-UNKNOWN} transactions in LOCK WAIT status" + INFO="" + ;; + max_duration) + NOTE="longest transaction active for ${LEVEL:-UNKNOWN} seconds" + ;; + esac + if [ "${LEVEL:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE $INFO" + elif [ "${LEVEL:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE $INFO" + else + NOTE="OK $NOTE" + fi + fi + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Gets max txn time in SHOW INNODB STATUS. File is $1. +# ######################################################################## +get_longest_trx() { + awk ' + BEGIN { + maxtime = 0; + thread = 0; + userinfo = "nobody"; + } + /^TRANSACTIONS$/ { + tseen = 1; + } + { + if ( tseen == 1 && $0 ~ /^---TRANSACTION.*[0-9] sec/ ) { + if ( $2 ~ /,/ ) { + time = $4; + } + else { + time = $5; + } + } + if ( tseen == 1 && $0 ~ /^MySQL thread id/ ) { + if ( time > maxtime ) { + maxtime = time; + thread = substr($4, 1, length($4) - 1); + match($0, /query id [0-9]+ .+/); + userinfo = substr($0, RSTART, RLENGTH); + split(userinfo, a, " "); + userinfo = a[5]"@"a[4]; + } + } + } + END { + print maxtime, thread, userinfo; + } + ' "${1}" +} + +# ######################################################################## +# Counts the number of LOCK WAIT in SHOW INNODB STATUS. File is $1. +# ######################################################################## +get_waiter_count() { + awk ' + BEGIN { + lock_waits = 0; + } + /^TRANSACTIONS$/ { + tseen = 1; + } + { + if ( tseen == 1 && $0 ~ /TRX HAS BEEN WAITING/ ) { + lock_waits++; + } + } + END { + print lock_waits; + } + ' "${1}" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-innodb" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-innodb - Alert on problems inside InnoDB. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-innodb [OPTIONS] + Options: + -C CHECK What to alert on; default idle_blocker_duration. + Other options: waiter_count, max_duration. + -c CRIT Critical threshold; default varies. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -w WARN Warning threshold; default varies. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin alerts on various aspects of InnoDB status in several ways, +depending on the value of the -C option: + +=over + +=item idle_blocker_duration + +This is the default behavior. It alerts when a long-running transaction is +blocking another, and the blocker is idle (Sleep). The threshold is based on +how long the transaction has been idle. Long-running idle transactions that +have acquired locks but not released them are a frequent cause of application +downtime due to lock wait timeouts and rollbacks, especially because +applications are often not designed to handle such errors correctly. The +problem is usually due to another error that causes a transaction not to be +committed, such as performing very long tasks in the application while holding +the transaction open. + +This check examines the INFORMATION_SCHEMA tables included with InnoDB version +1.0 and newer. The default critical level is 600, and warning is 60. If the +tables do not exist, the exit status is OK, with a note that the tables do not +exist. + +=item waiter_count + +Alerts if too many transactions are in LOCK WAIT status. Uses information from +SHOW ENGINE INNODB STATUS if the INFORMATION_SCHEMA tables are not available. +The default critical level is 25, and warning is 10. + +=item max_duration + +Alerts if any transaction is too old. Uses information from SHOW ENGINE INNODB +STATUS if the INFORMATION_SCHEMA tables are not available. The default critical +level is 600, and warning is 60. + +=back + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SHOW ENGINE INNODB STATUS>. + +=item * + +C<SELECT> against the C<INFORMATION_SCHEMA> InnoDB transaction and lock tables. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-innodb $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-pidfile b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-pidfile new file mode 100755 index 0000000..bd6c0e5 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-pidfile @@ -0,0 +1,291 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Set the exit status in case there are any problems. + NOTE="UNK could not determine the PID file location." + + # Set up files to hold one or more PID file locations. + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + local FILES=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' '${FILES}' >/dev/null 2>&1" EXIT + + # If any connection option was given, then try to log in to find the PID + # file. + if [ "${OPT_DEFT}${OPT_HOST}${OPT_USER}${OPT_PASS}${OPT_PORT}${OPT_SOCK}" ]; then + # If this fails (e.g. we can't log in), then there will be no line in the + # file, and later we won't change the exit code / note away from "UNK". + if mysql_exec "SHOW GLOBAL VARIABLES" > "${TEMP}"; then + get_pidfile "${TEMP}" >> "${FILES}" + fi + else + # Find all MySQL server instances. + for pid in $(_pidof mysqld); do + ps -p ${pid} -o pid,command | grep "${pid}" >> "${TEMP}" + done + # The ${TEMP} file may now contain lines like the following sample: + # 13822 /usr/sbin/mysqld --defaults-file=/var/lib/mysql/my.cnf \ + # --basedir=/usr --datadir=/var/lib/mysql/data/ \ + # --pid-file=/var/run/mysqld/mysqld.pid \ + # --socket=/var/run/mysqld/mysqld.sock + # Now the task is to read find any reference to a --pid-file or --pid_file option. + # We store these into the $FILES temp file. + while read pid command; do + if echo "${command}" | grep pid.file >/dev/null 2>&1; then + # Strip off everything up to and including --pid-file= + command="${command##*--pid?file=}" + # Strip off any options that follow this, assuming that there's not + # a space followed by a dash in the pidfile's path. + echo "${command%% -*}" >> "${FILES}" + fi + done < "${TEMP}" + fi + + # TODO: maybe in the future we can also check whether the PID in the file is + # correct. TODO: maybe we should also alert on which PID is missing its + # pidfile. + MISSING="" + NOTE2="" + while read pidfile; do + if [ ! -e "${pidfile}" ]; then + MISSING=1 + NOTE2="${NOTE2:+${NOTE2}; }missing ${pidfile}" + fi + NOTE="OK all PID files exist." + done < "${FILES}" + + if [ "${MISSING}" ]; then + if [ "${OPT_CRIT}" ]; then + NOTE="CRIT ${NOTE2}" + else + NOTE="WARN ${NOTE2}" + fi + fi + + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# A wrapper around pidof, which might not exist. The first argument is the +# command name to match. +# ######################################################################## +_pidof() { + if ! pidof "${1}" 2>/dev/null; then + ps axo pid,ucomm | awk -v comm="${1}" '$2 == comm { print $1 }' + fi +} + +# ######################################################################## +# Unfortunately, MySQL 5.0 doesn't have a system variable @@pid_file, so +# we have to use SHOW VARIABLES and a temp file. In 5.1 and newer we +# could have done it in a single SQL statement: +# SELECT IF(@@pid_file LIKE '/%', @@pid_file, +# CONCAT(@@basedir, @@pid_file))" >> "${FILES}" +# The first argument is the file that contains SHOW VARIABLES. +# ######################################################################## +get_pidfile() { + awk ' + /^pid_file/ { pid_file = $2 } + /^basedir/ { basedir = $2 } + END { + if ( substr(pid_file, 1, 1) != "/" ) { + pid_file = basedir pid_file; + } + print pid_file; + } + ' "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-pidfile" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-pidfile - Alert when the mysqld PID file is missing. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-pidfile [OPTIONS] + Options: + -c CRIT Critical threshold; makes a missing PID file critical. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -w WARN Warning threshold; ignored. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin checks to make sure that the MySQL PID file is not missing. +The PID file contains the process ID of the MySQL server process, and is used by +init scripts to start and stop the server. If it is deleted for some reason, +then it is likely that the init script will not work correctly. The file can be +deleted by poorly written scripts, an accident, or a mistaken attempt to restart +MySQL while it is already running, especially if mysqld is executed directly +instead of using the init script. + +The plugin accepts the -w and -c options for compatibility with standard Nagios +plugin conventions, but they are not based on a threshold. Instead, the plugin +raises a warning by default, and if the -c option is given, it raises an error +instead, regardless of the option. + +By default, this plugin will attempt to detect all running instances of MySQL, +and verify the PID file's existence for each one. It does this purely by +examining the Unix process table with the C<ps> tool. However, in some cases +the process's command line does not list the path to the PID file. If the tool +fails to detect the MySQL server process, or if you wish to limit the check to a +single instance in the event that there are multiple instances on a single +server, then you can specify MySQL authentication options. This will cause the +plugin to skip examining the Unix processlist, log into MySQL, and examine the +pid_file variable from SHOW VARIABLES to find the location of the PID file. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> the system variables C<@@pid_file> and C<@@basedir>. + +=back + +This plugin executes the following UNIX commands that may need special privileges: + +=over + +=item * + +ps + +=back + +The plugin should be able to either get variables from MySQL or find mysqld +PID using C<ps> command. + +On BSD, if C<sysctl> option C<security.bsd.see_other_uids> is set to 0, C<ps> +will not return mysqld PID if the plugin run from non-root user. + +Also an user you run the plugin from should be able to access MySQL PID file +file, so you may want to add it into mysql unix group etc. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-pidfile $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-processlist b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-processlist new file mode 100755 index 0000000..b2985f0 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-processlist @@ -0,0 +1,323 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -C) shift; OPT_CHEK="${1}"; shift; ;; + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_CHEK="${OPT_CHEK:-states_count}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Get processlist into a temp file. + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + + case "${OPT_CHEK}" in + states_count) + OPT_WARN=${OPT_WARN:-16} + OPT_CRIT=${OPT_CRIT:-32} + + # Capture a number of types of states, add some together, take the max, + # and compare to the threshold. + mysql_exec 'SHOW PROCESSLIST\G' > "${TEMP}" + if [ $? = 0 ]; then + UNAUTH=$(count_mysql_processlist "${TEMP}" "User" "unauthenticated user") + LOCKED1=$(count_mysql_processlist "${TEMP}" "State" "Locked") + LOCKED2=$(count_mysql_processlist "${TEMP}" "State" "Waiting for .* lock") + LOCKED3=$(count_mysql_processlist "${TEMP}" "State" "Table lock") + LOCKED4=$(count_mysql_processlist "${TEMP}" "State" "Waiting for table flush") + LOCKED5=$(count_mysql_processlist "${TEMP}" "State" "Waiting for tables") + COPYIN=$(count_mysql_processlist "${TEMP}" "State" ".*opy.* to.* table.*") + STATIS=$(count_mysql_processlist "${TEMP}" "State" "statistics") + LOCKED=$((${LOCKED1:-0} + ${LOCKED2:-0} + ${LOCKED3:-0} + ${LOCKED4:-0} + ${LOCKED5:-0})) + NOTE="${UNAUTH} unauthenticated, ${LOCKED} locked," + NOTE="${NOTE} ${COPYIN} copy to table, ${STATIS} statistics" + MAX="$(max "${UNAUTH:-0}" "${LOCKED:-0}" "${COPYIN:-0}" "${STATIS:-0}")" + if [ "${MAX:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + elif [ "${MAX:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE" + else + NOTE="OK $NOTE" + fi + + # Build the common perf data output for graph trending + PERFDATA="processes=${MAX:-0};${OPT_WARN};${OPT_CRIT};0;" + NOTE="$NOTE | $PERFDATA" + else + NOTE="UNK could not retrieve MySQL processlist" + fi + ;; + max_user_conn) + OPT_WARN=${OPT_WARN:-90} + OPT_CRIT=${OPT_CRIT:-95} + + # Check if @@max_user_connections is set on MySQL + MAX_USER_CONN=$(mysql_exec 'SELECT @@max_user_connections') + if [ $? = 0 ]; then + if [ ${MAX_USER_CONN:-0} -gt 0 ]; then + # Capture a number of connections per user from the processlist, take the max, + # and compare to the threshold. + mysql_exec 'SHOW PROCESSLIST\G' > "${TEMP}" + if [ $? = 0 ]; then + MAX_USER=$(cat ${TEMP}|grep User|awk '{print $2}'|sort|uniq -c|sort -n|tail -1) + CNT=$(echo ${MAX_USER} | awk '{print $1}') + USER=$(echo ${MAX_USER} | awk '{print $2}') + MAX=$(expr ${CNT} \* 100 / ${MAX_USER_CONN}) + NOTE="User with max connections: ${USER} (${CNT}) = ${MAX}%" + if [ "${MAX:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + elif [ "${MAX:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE" + else + NOTE="OK $NOTE" + fi + + # Build the common perf data output for graph trending + PERFDATA="max_user_conn=${MAX:-0};${OPT_WARN};${OPT_CRIT};0;100" + NOTE="$NOTE | $PERFDATA" + else + NOTE="UNK could not retrieve MySQL processlist" + fi + else + NOTE="OK @@max_user_connections is not configured." + fi + else + NOTE="UNK could not retrieve @@max_user_connections" + fi + ;; + *) + echo "Unknown value for -C: '${OPT_CHEK}'. Consult the documentation."; + exit 1; + ;; + esac + + echo $NOTE +} + +# ######################################################################## +# Extract a count from MySQL processlist. The arguments are: +# $1 - file with the processlist. +# $2 - the column to examine. +# $3 - the value to count. +# ######################################################################## +count_mysql_processlist() { + local FILE="${1}" + local COL="${2}" + local MATCH="${3}" + grep -c "^ *${COL}: ${MATCH}" "${FILE}" +} + +# ######################################################################## +# Find the maximum argument, assuming nonnegative integers. +# ######################################################################## +max() { + local MAX=0 + for val; do + if [ "${val:-0}" -gt "${MAX}" ]; then + MAX="${val}" + fi + done + echo "${MAX:-0}" +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-processlist" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-processlist - Alert when MySQL processlist has dangerous patterns. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-processlist [OPTIONS] + Options: + -C CHECK What to alert on; default states_count. + Other options: max_user_conn. + -c CRIT Critical threshold; default varies. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -w WARN Warning threshold; default varies. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin examines MySQL processlist in several ways, +depending on the value of the -C option: + +=over + +=item states_count + +Alerts when there are too many processes in various states. +The list of checks is as follows: + +Unauthenticated users appear when DNS resolution is slow, and can be a warning +sign of DNS performance problems that could cause a sudden denial of service to +the server. + +Locked processes are the signature of MyISAM tables, but can also appear for +other reasons. + +Too many processes copying to various kinds of temporary tables at one time is a +typical symptom of a storm of poorly optimized queries. + +Too many processes in the "statistics" state is a signature of InnoDB +concurrency problems causing query execution plan generation to take too long. + +The thresholds should be given as count. The default critical level is 32, +and warning is 16. + +=item max_user_conn + +Alerts when C<@@max_user_connections> is configured on MySQL and any user reaches +this limit. The output of this check will display the user with maximum +connections consumed, its count and percentage of the actual limit. + +The thresholds should be given as percentage. The default critical level is 95, +and warning is 90. + +=back + +Examples: + + # /usr/lib64/nagios/plugins/pmp-check-mysql-processlist + OK 0 unauthenticated, 0 locked, 0 copy to table, 0 statistics | processes=0;16;32;0; + + # /usr/lib64/nagios/plugins/pmp-check-mysql-processlist -C max_user_conn + OK User with max connections: myappuser (70) = 2% | max_user_conn=2;90;95;0;100 + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SHOW PROCESSLIST;> + +=item * + +C<SELECT @@max_user_connections;> + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-processlist $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-delay b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-delay new file mode 100755 index 0000000..1f02585 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-delay @@ -0,0 +1,280 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + OPT_ENSURE_SBM=0 + MIN_DELAY_SET=0 + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -m) shift; OPT_MIN="${1}"; MIN_DELAY_SET=1; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -s) shift; OPT_SRVID="${1}"; shift; ;; + -T) shift; OPT_TABLE="${1}"; shift; ;; + -u) shift; OPT_UTC=1; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --master-conn) shift; OPT_MASTERCONN="${1}"; shift; ;; + --channel) shift; OPT_CHANNEL="${1}"; shift; ;; + --unconfigured) shift; OPT_REPLNOTSET=1; ;; + --ensure-sbm) shift; OPT_ENSURE_SBM=1; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_WARN=${OPT_WARN:-300} + OPT_CRIT=${OPT_CRIT:-600} + OPT_MIN=${OPT_MIN:-0} + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Get replication delay from a heartbeat table or from SHOW SLAVE STATUS. + get_slave_status $1 + if [ "${OPT_TABLE}" ]; then + if [ -z "${OPT_UTC}" ]; then + NOW_FUNC='UNIX_TIMESTAMP()' + else + NOW_FUNC='UNIX_TIMESTAMP(UTC_TIMESTAMP)' + fi + if [ "${OPT_SRVID}" == "MASTER" ]; then + if [ "${MYSQL_CONN}" = 0 ]; then + OPT_SRVID=$(awk '/Master_Server_Id/{print $2}' "${TEMP_SLAVEDATA}") + fi + fi + SQL="SELECT MAX(${NOW_FUNC} - ROUND(UNIX_TIMESTAMP(ts))) AS delay + FROM ${OPT_TABLE} WHERE (${OPT_SRVID:-0} = 0 OR server_id = ${OPT_SRVID:-0})" + LEVEL=$(mysql_exec "${SQL}") + MYSQL_CONN=$? + else + if [ "${MYSQL_CONN}" = 0 ]; then + LEVEL=$(awk '/Seconds_Behind_Master/{print $2}' "${TEMP_SLAVEDATA}") + fi + fi + + # Check for SQL thread errors + LAST_SLAVE_ERRNO=$(awk '/Last_SQL_Errno/{print $2}' "${TEMP_SLAVEDATA}") + + # Build the common perf data output for graph trending + PERFDATA="replication_delay=${LEVEL:-0};${OPT_WARN};${OPT_CRIT};0;" + + # Test whether the delay is too long. + if [ "$MYSQL_CONN" = 0 ]; then + NOTE="${LEVEL:-0} seconds of replication delay" + if [ "${LEVEL:-""}" = "NULL" ]; then + test ${MIN_DELAY_SET} -eq 1 && \ + test ${LAST_SLAVE_ERRNO} -eq 0 && \ + test ${OPT_ENSURE_SBM} -eq 0 && \ + NOTE="OK NULL seconds of replication delay" || NOTE="UNK replica is stopped" + elif [ -z "${LEVEL}" -a "${OPT_REPLNOTSET}" ]; then + NOTE="UNK This server is not configured as a replica." + # pt-slave-delayed slave + elif [ ${MIN_DELAY_SET} -eq 1 ] && [ "${LEVEL:-0}" -lt "${OPT_MIN}" ]; then + NOTE="CRIT (delayed slave) $NOTE | $PERFDATA" + elif [ "${LEVEL:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE | $PERFDATA" + elif [ "${LEVEL:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE | $PERFDATA" + else + NOTE="OK $NOTE | $PERFDATA" + fi + else + NOTE="UNK could not determine replication delay" + fi + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-replication-delay" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Captures the "SHOW SLAVE STATUS" output into a temp file. +# ######################################################################## +get_slave_status() { + TEMP_SLAVEDATA=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP_SLAVEDATA}' >/dev/null 2>&1" EXIT + if [ -z "$1" ]; then + if [ "${OPT_MASTERCONN}" ]; then + # MariaDB multi-source replication + mysql_exec "SHOW SLAVE '${OPT_MASTERCONN}' STATUS\G" > "${TEMP_SLAVEDATA}" + elif [ "${OPT_CHANNEL}" ]; then + mysql_exec "SHOW SLAVE STATUS FOR CHANNEL '${OPT_CHANNEL}'\G" > "${TEMP_SLAVEDATA}" + else + # Leverage lock-free SHOW SLAVE STATUS if available + mysql_exec "SHOW SLAVE STATUS NONBLOCKING\G" > "${TEMP_SLAVEDATA}" 2>/dev/null || + mysql_exec "SHOW SLAVE STATUS NOLOCK\G" > "${TEMP_SLAVEDATA}" 2>/dev/null || + mysql_exec "SHOW SLAVE STATUS\G" > "${TEMP_SLAVEDATA}" + fi + MYSQL_CONN=$? + else + # This is for testing only. + cat "$1" > "${TEMP_SLAVEDATA}" 2>/dev/null + MYSQL_CONN=0 + fi +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-replication-delay - Alert when MySQL replication becomes delayed. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-replication-delay [OPTIONS] + Options: + -c CRIT Critical threshold; default 600. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -m CRIT Minimal threshold to ensure for delayed slaves; default 0. + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -s SERVERID MySQL server ID of master, if using pt-heartbeat table. If + the parameter is set to "MASTER" the plugin will lookup the + server_id of the master + -T TABLE Heartbeat table used by pt-heartbeat. + -u Use UTC time to count the delay in case pt-heartbeat is run + with --utc option. + -w WARN Warning threshold; default 300. + --master-conn NAME Master connection name for MariaDB multi-source replication. + --channel NAME Master channel name for multi-source replication (MySQL 5.7.6+). + --unconfigured Alert when replica is not configured at all; default no. + --ensure-sbm Disallow Seconds_Behind_Master to be NULL for delayed slaves when -m is used + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin examines whether MySQL replication is delayed too much. By +default it uses SHOW SLAVE STATUS, but the output of the Seconds_behind_master +column from this command is unreliable, so it is better to use pt-heartbeat from +Percona Toolkit instead. Use the -T option to specify which table pt-heartbeat +updates. Use the -s option to specify the master's server_id to compare +against; otherwise the plugin reports the maximum delay from any server. Use +the -s options with the value "MASTER" to have plugin lookup the master's server_id + +If you want to run this check against the delayed slaves, e.g. those running +with pt-slave-delay tool, you may want to use -m option specifying the minimal +delay that should be ongoing, otherwise the plugin will alert critical. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SHOW SLAVE STATUS [NONBLOCKING|NOLOCK]> + +or + +=item * + +C<SELECT> from the C<pt-heartbeat> table. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-replication-delay $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-running b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-running new file mode 100755 index 0000000..27f2f18 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-replication-running @@ -0,0 +1,242 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -d) shift; OPT_DELD=1; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --master-conn) shift; OPT_MASTERCONN="${1}"; shift; ;; + --channel) shift; OPT_CHANNEL="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Get replication status into a temp file. TODO: move this into a subroutine + # and test it. But, if there is a commandline argument left over after + # parsing options, then use that as the file instead. + local TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + if [ -z "$1" ]; then + if [ "${OPT_MASTERCONN}" ]; then + # MariaDB multi-source replication + mysql_exec "SHOW SLAVE '${OPT_MASTERCONN}' STATUS\G" > "${TEMP}" + elif [ "${OPT_CHANNEL}" ]; then + mysql_exec "SHOW SLAVE STATUS FOR CHANNEL '${OPT_CHANNEL}'\G" > "${TEMP}" + else + # Leverage lock-free SHOW SLAVE STATUS if available + mysql_exec "SHOW SLAVE STATUS NONBLOCKING\G" > "${TEMP}" 2>/dev/null || + mysql_exec "SHOW SLAVE STATUS NOLOCK\G" > "${TEMP}" 2>/dev/null || + mysql_exec "SHOW SLAVE STATUS\G" > "${TEMP}" + fi + else + # This is just for testing. /dev/null it. + cat "$1" > "${TEMP}" 2>/dev/null + fi + + if [ $? = 0 ]; then + # SHOW SLAVE STATUS isn't an error if the server isn't a replica. The file + # will be empty if that happens. + if [ -s "${TEMP}" ]; then + NOTE=$(awk '$1 ~ /_Running:|Last_Error:/{print substr($0, 1, 100)}' "${TEMP}") + if grep 'Last_Error: .' "${TEMP}" >/dev/null 2>&1; then + NOTE="CRIT $NOTE" + # pt-slave-delayed slave + elif [ -n "${OPT_DELD}" ] && + grep 'Slave_IO_Running: Yes' "${TEMP}" >/dev/null 2>&1 && + grep 'Slave_SQL_Running: No' "${TEMP}" >/dev/null 2>&1; then + NOTE="OK (delayed slave) $NOTE" + elif egrep "_Running: (No|Connecting)" "${TEMP}" >/dev/null 2>&1; then + if [ "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + else + NOTE="WARN $NOTE" + fi + else + NOTE="OK $NOTE" + fi + elif [ "${OPT_WARN}" ]; then + # Empty file; not a replica, but that's not supposed to happen. + NOTE="WARN This server is not configured as a replica." + else + # Empty file; not a replica. + NOTE="OK This server is not configured as a replica." + fi + else + NOTE="UNK could not determine replication status" + fi + + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-replication-running" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-replication-running - Alert when MySQL replication stops. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-replication-running [OPTIONS] + Options: + -c CRIT Report CRITICAL when replication is stopped with or w/o errors. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -d Useful for slaves delayed by pt-slave-delay. It will not alert + when IO thread is running, SQL one is not and no errors. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -w WARN Report WARNING when SHOW SLAVE STATUS output is empty. + --master-conn NAME Master connection name for MariaDB multi-source replication. + --channel NAME Master channel name for multi-source replication (MySQL 5.7.6+). + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin examines whether replication is running. It is separate from +the check for delay because it is confusing or impossible to handle all of the +combinations of replication errors and delays correctly, and provide an +appropriate type of alert, in a single program. + +By default, this plugin treats it as critical when the either thread stops with +an error, and a warning when threads are stopped with no error. You can provide +critical and warning thresholds with the -c and -w options, for compatibility +with Nagios plugin conventions, but they don't work as thresholds. Instead, if +you specify a critical threshold, this plugin will treat it as critical if +either thread is stopped, with or without an error. + +The warning threshold makes the plugin report a warning when SHOW SLAVE STATUS +produces no output, which means it is not configured as a replica. By default, +this plugin will report that replication is healthy when a server isn't +configured as a replica. + +If you want to run this check against the delayed slaves, e.g. those running +with pt-slave-delay tool, you may want to specify -d option. It will not alert +when Slave_IO_Running is Yes, Slave_SQL_Running is No and there are no errors. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SHOW SLAVE STATUS [NONBLOCKING|NOLOCK]> + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-replication-running $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-status b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-status new file mode 100755 index 0000000..89c2a6a --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-status @@ -0,0 +1,482 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -C) shift; OPT_COMP="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -I) shift; OPT_INCR="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -o) shift; OPT_OPER="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -T) shift; OPT_TRAN="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + -x) shift; OPT_VAR1="${1}"; shift; ;; + -y) shift; OPT_VAR2="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + # Set default option values + OPT_COMP="${OPT_COMP:->=}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + # Validate the options. + OPT_ERR="" + if [ -z "${OPT_CRIT}${OPT_WARN}" ]; then + OPT_ERR="you must specify either -c or -w" + elif [ -z "${OPT_VAR1}" ]; then + OPT_ERR="you must specify -x" + elif [ "${OPT_OPER}" -a -z "${OPT_VAR2}" ]; then + OPT_ERR="you specified -o but not -y" + elif [ "${OPT_VAR2}" -a -z "${OPT_OPER}" ]; then + OPT_ERR="you specified -y but not -o" + elif [ "${OPT_TRAN}" = 'pct' -a -z "${OPT_VAR2}" ]; then + OPT_ERR="you specified -T pct but not -y" + elif [ "${OPT_TRAN}" ]; then + case "${OPT_TRAN}" in + pct|str) + ;; + *) + OPT_ERR="-T must be one of: pct str" + ;; + esac + fi + case "${OPT_COMP}" in + '=='|'!='|'>='|'>'|'<'|'<=') + ;; + *) + OPT_ERR="-C must be one of: == != >= > < <=" + ;; + esac + if [ "${OPT_OPER}" ]; then + case "${OPT_OPER}" in + /|'*'|+|-) + : + ;; + *) + OPT_ERR="-o must be one of: / * + -" + ;; + esac + fi + if [ "${OPT_ERR}" ]; then + echo "Error: $OPT_ERR. Try --help." + exit 1 + fi + + NOTE="UNK could not evaluate the expression." + + # Set up a temporary file + local TEMP1=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + local TEMP2=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP1}' '${TEMP2}' >/dev/null 2>&1" EXIT + + if get_status_variables "${TEMP1}" "${TEMP2}" "${OPT_INCR}"; then + LEVEL=$(compute_result "${TEMP1}" "${OPT_VAR1}" "${OPT_OPER}" "${OPT_VAR2}" "${OPT_TRAN}") + if [ $? = 0 -a -n "${LEVEL}" ]; then + NOTE="${OPT_VAR1}${OPT_OPER:+ ${OPT_OPER}}${OPT_VAR2:+ ${OPT_VAR2}}${OPT_TRAN:+ (${OPT_TRAN})}" + NOTE="${NOTE} = ${LEVEL}" + + # XXX Make sure this line and the "case" don't get separated. + compare_result "${LEVEL}" "${OPT_CRIT}" "${OPT_WARN}" "${OPT_COMP}" "${OPT_TRAN}" + case $? in + $STATE_OK) + NOTE="OK $NOTE" + ;; + $STATE_CRITICAL) + NOTE="CRIT $NOTE" + ;; + $STATE_WARNING) + NOTE="WARN $NOTE" + ;; + esac + + # Build the common perf data output for graph trending + if [ "${OPT_TRAN}" = 'pct' ]; then + PERFDATA_MAX=100 + fi + PERFDATA="${OPT_VAR1}${OPT_OPER}${OPT_VAR2}=${LEVEL};${OPT_WARN};${OPT_CRIT};0;${PERFDATA_MAX}" + NOTE="$NOTE | $PERFDATA" + fi + else + NOTE="UNK could not get MySQL status/variables." + fi + + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Compares the variable to the thresholds. Arguments: VAR CRIT WARN CMP TRAN +# Returns nothing; exits with OK/WARN/CRIT. +# ######################################################################## +compare_result() { + local VAR="${1}" + local CRIT="${2}" + local WARN="${3}" + local CMP="${4}" + local TRAN="${5}" + echo 1 | awk "END { + if ( \"${CRIT}\" != \"\" ) { + if ( \"${TRAN}\" == \"str\" ) { + if ( \"${VAR}\" ${CMP} \"${CRIT:-0}\" ) { + exit $STATE_CRITICAL + } + } else { + if ( ${VAR} ${CMP} ${CRIT:-0} ) { + exit $STATE_CRITICAL + } + } + } + if ( \"${WARN}\" != \"\" ) { + if ( \"${TRAN}\" == \"str\" ) { + if ( \"${VAR}\" ${CMP} \"${WARN:-0}\" ) { + exit $STATE_WARNING + } + } else { + if ( ${VAR} ${CMP} ${WARN:-0} ) { + exit $STATE_WARNING + } + } + } + exit $STATE_OK + }" +} + +# ######################################################################## +# Computes an expression against the file of variables. Returns a float. +# Arguments: TEMP VAR1 OPER VAR2 TRAN +# ######################################################################## +compute_result() { + local TEMP="$1" + local VAR1="$2" + local OPER="$3" + local VAR2="$4" + local TRAN="$5" + if [ "${VAR2}" ]; then + # Extract two variables, apply an operator, and possibly apply a + # transform. + awk -F'\t' " + BEGIN { + got1 = \"Could not find variable ${VAR1}\"; + got2 = \"Could not find variable ${VAR2}\"; + } + \$1 == \"${VAR1}\" { + var1 = \$2; + got1 = \"\"; + } + \$1 == \"${VAR2}\" { + var2 = \$2; + got2 = \"\"; + } + END { + if ( got1 == \"\" && got2 == \"\" ) { + if ( var2 == 0 && \"${OPER}\" == \"/\" ) { + # Divide-by-zero; make the result simply 0 + val = 0; + } + else { + val = var1 ${OPER} var2; + } + if ( \"${TRAN}\" == \"pct\" ) { + val = val * 100; + } + if ( val ~ /\.[0-9]/ ) { + printf \"%.6f\\n\", val; + } + else { + print val; + } + } + else { + print got1, got2 | \"cat 1>&2\"; + exit 1; + } + } + " "${TEMP}" + else + # This is the simplest case. We're just extracting a single variable and + # returning it. + awk -F'\t' -v var1="${VAR1}" ' + BEGIN { + got = 0; + } + $1 == var1 { + val = $2; + got = 1; + } + END { + if ( got == 1 ) { + print val; + } + else { + print "Unknown variable", var1 | "cat 1>&2"; + exit 1; + } + } + ' "${TEMP}" + fi +} + +# ######################################################################## +# Gets status variables. The first argument is the file to store the results. +# Optional second argument is another temp file. +# Optional third argument makes SHOW STATUS incremental/relative, and has the +# added effect of filtering out non-numeric variables. +# ######################################################################## +get_status_variables() { + if [ "$3" ]; then + mysql_exec "SHOW /*!50000 GLOBAL*/ STATUS" > "${2}" || exit 1 + sleep "$3" + # Technically we ought to use another temp file, and check the return + # status of this mysql_exec, but if the first one worked it's likely that + # this one will too. + mysql_exec "SHOW /*!50000 GLOBAL*/ STATUS" | cat "${2}" - \ + | awk -F'\t' ' + /Aborted_clients/ { seen++; } + { + if ( seen > 1 && $2 !~ /[^0-9.]/ ) { + print $1 "\t" $2 - var[$1]; + } + else { + var[$1] = $2; + } + } + ' > "${1}" + else + mysql_exec "SHOW /*!50000 GLOBAL*/ STATUS" > "${1}" || exit 1 + fi + mysql_exec "SHOW /*!40101 GLOBAL*/ VARIABLES" >> "${1}" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-status" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-status - Check MySQL SHOW GLOBAL STATUS output. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-status [OPTIONS] + Options: + -c CRIT Critical threshold. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -C COMPARE Comparison operator to apply to -c and -w. + Possible values: == != >= > < <=. Default >=. + -H HOST MySQL hostname. + -I INCR Make SHOW STATUS incremental over this delay. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -o OPERATOR The operator to apply to -x and -y. + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -T TRANS Transformation to apply before comparing to -c and -w. + Possible values: pct str. + -w WARN Warning threshold. + -x VAR1 Required first status or configuration variable. + -y VAR2 Optional second status or configuration variable. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin captures SHOW GLOBAL STATUS and SHOW GLOBAL VARIABLES from +MySQL and evaluates expressions against them. The general syntax is as follows: + + VAR1 [ OPERATOR VAR2 [ TRANSFORM ] ] + +The result of evaluating this is compared against the -w and -c options as usual +to determine whether to raise a warning or critical alert. + +Note that all of the examples provided below are simply for illustrative +purposes and are not supposed to be recommendations for what to monitor. You +should get advice from a professional if you are not sure what you should be +monitoring. + +For our first example, we will raise a warning if Threads_running is 20 or over, +and a critical alert if it is 40 or over: + + -x Threads_running -w 20 -c 40 + +The threshold is implemented as greater-or-equals by default, not strictly +greater-than, so a value of 20 is a warning and a value of 40 is critical. You +can switch this to less-or-equals or other operators with the -C option, which +accepts the arithmetic comparison operators ==, !=, >, >=, <, and <=. + +You can use any variable that is present in SHOW VARIABLES or SHOW STATUS. If +the variable is not found, there is an error. To warn if Threads_connected +exceeds 80% of max_connections: + + -x Threads_connected -o / -y max_connections -T pct -w 80 + +The -T C<pct> option only works when you specify both -x and -y and implements +percentage transformation. The plugin uses awk to do its +computations and comparisons, so you can use floating-point math; you are not +restricted to integers for comparisons. Floating-point numbers are printed with +six digits of precision. The -o option accepts the arithmetic operators /, *, ++, and -. A division by zero results in zero, not an error. + +If you specify the -I option with an integer argument, the SHOW STATUS values +become incremental instead of absolute. The argument is used as a delay in +seconds, and instead of capturing a single sample of SHOW STATUS and using it +for computations, the plugin captures two samples at the specified interval and +subtracts the second from the first. This lets you evaluate expressions over a +range of time. For example, to warn when there are 10 disk-based temporary +tables per second, over a 5-second sampling period: + + -x Created_tmp_disk_tables -o / -y Uptime -I 5 -w 10 + +That is somewhat contrived, because it could also be written as follows: + + -x Created_tmp_disk_tables -I 5 -w 50 + +The -I option has the side effect of removing any non-numeric SHOW STATUS +variables. Be careful not to set the -I option too large, or Nagios will simply +time the plugin out, usually after about 10 seconds. + +This plugin does not support arbitrarily complex expressions, such as computing +the query cache hit ratio and alerting if it is less than some percentage. If +you are trying to do that, you might be doing it wrong. A dubious example for +the query cache might be to alert if the hit-to-insert ratio falls below 2:1, as +follows: + + -x Qcache_hits -o / -y Qcache_inserts -C '<' -w 2 + +Some people might suggest that the following is a more useful alert for the +query cache: + + -x query_cache_size -c 1 + +To check Percona XtraDB Cluster node status you may want to use the following +alert similar to what its clustercheck does: + + -x wsrep_local_state -C '!=' -w 4 + +To compare string variables use -T C<str> transformation. This is required as +numeric and string comparisons are handled differently. The following example +warns when the slave_exec_mode is IDEMPOTENT: + + -x slave_exec_mode -C '==' -T str -w IDEMPOTENT + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SHOW STATUS>. + +=item * + +C<SHOW VARIABLES>. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-status $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-ts-count b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-ts-count new file mode 100755 index 0000000..06ea3f4 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-mysql-ts-count @@ -0,0 +1,210 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Ben Mildren +# Depends-on: table with ts column, e.g. table written by pt-deadlock-logger +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -i) shift; OPT_INTERVAL="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -t) shift; OPT_TIMESTAMP="${1}"; shift; ;; + -T) shift; OPT_TABLE="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + -x) shift; OPT_TARGET="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_WARN=${OPT_WARN:-12} + OPT_CRIT=${OPT_CRIT:-60} + OPT_INTERVAL=${OPT_INTERVAL:-1} + OPT_TARGET="${OPT_TARGET:-deadlocks}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + if [ "${OPT_TARGET}" == "kills" ]; then + OPT_TABLE="${OPT_TABLE:-percona.kill_log}" + OPT_TIMESTAMP="${OPT_TIMESTAMP:-timestamp}" + elif [ "${OPT_TARGET}" == "fkerrors" ]; then + OPT_TABLE="${OPT_TABLE:-percona.foreign_key_errors}" + OPT_TIMESTAMP="${OPT_TIMESTAMP:-ts}" + else + OPT_TABLE="${OPT_TABLE:-percona.deadlocks}" + OPT_TIMESTAMP="${OPT_TIMESTAMP:-ts}" + fi + + LEVEL=$(mysql_exec "SELECT COUNT(*) FROM ${OPT_TABLE} WHERE ${OPT_TIMESTAMP} >= NOW() - INTERVAL ${OPT_INTERVAL}*60 SECOND") + if [ $? = 0 ]; then + NOTE="${LEVEL:-UNKNOWN} ${OPT_TARGET} in last ${OPT_INTERVAL} minutes" + if [ "${LEVEL:-0}" -gt "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + elif [ "${LEVEL:-0}" -gt "${OPT_WARN}" ]; then + NOTE="WARN $NOTE" + else + NOTE="OK $NOTE" + fi + + # Build the common perf data output for graph trending + PERFDATA="${OPT_TARGET}=${LEVEL:-0};${OPT_WARN};${OPT_CRIT};0;" + NOTE="$NOTE | $PERFDATA" + else + NOTE="UNK could not count ${OPT_TARGET}" + fi + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-mysql-ts-count" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-mysql-ts-count - Generic alert based on pmp-check-mysql-deadlocks to count number of rows written in the last interval. + +=head1 SYNOPSIS + + Usage: pmp-check-mysql-ts-count [OPTIONS] + Options: + -c CRIT Critical threshold; default 60. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -i INTERVAL Interval over which to count, in minutes; default 1. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -t TIMESTAMP The name of the timestamp column to be monitored; default ts. + -T TABLE The database.table to be monitored; default percona.deadlocks. + -w WARN Warning threshold; default 12. + -x TARGET Metric monitored; default deadlocks. + Other options: kills, fkerrors. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin looks at a table and counts the number of rows since the +last interval, and alerts if this exceeds the threshold. This could be the +table referenced by pt-deadlock-logger, pt-kill, pt-fk-error-logger, or a +custom table supplied. +Default behaviour is the same as pmp-check-mysql-deadlocks, can also specify +target to be C<kills> or C<fkerrors> to monitor default tables created by pt-kill +or pt-fk-error-logger respectively, or supply custom metric and table. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> from the supplied table. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-mysql-ts-count $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-pt-table-checksum b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-pt-table-checksum new file mode 100755 index 0000000..f8df9b8 --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-pt-table-checksum @@ -0,0 +1,239 @@ +#!/bin/bash + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + # Get options + for o; do + case "${o}" in + -c) shift; OPT_CRIT="${1}"; shift; ;; + --defaults-file) shift; OPT_DEFT="${1}"; shift; ;; + -H) shift; OPT_HOST="${1}"; shift; ;; + -i) shift; OPT_INTERVAL="${1}"; shift; ;; + -l) shift; OPT_USER="${1}"; shift; ;; + -L) shift; OPT_LOPA="${1}"; shift; ;; + -p) shift; OPT_PASS="${1}"; shift; ;; + -P) shift; OPT_PORT="${1}"; shift; ;; + -S) shift; OPT_SOCK="${1}"; shift; ;; + -T) shift; OPT_TABLE="${1}"; shift; ;; + -w) shift; OPT_WARN="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_INTERVAL=${OPT_INTERVAL:-0} + OPT_TABLE="${OPT_TABLE:-percona.checksums}" + if [ -e '/etc/nagios/mysql.cnf' ]; then + OPT_DEFT="${OPT_DEFT:-/etc/nagios/mysql.cnf}" + fi + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + NOTE="UNK couldn't query the checksum table" + + # Set up a temp file to hold error messages from MySQL. + TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + + # Get the query from the documentation and execute it. + SQL=$(get_magic_query "${0}" checksum_diff_query) + PROBLEMS=$(mysql_exec "${SQL/CHECKSUM_TABLE/${OPT_TABLE}}" 2>"${TEMP}") + if [ $? = 0 ]; then + if [ "${PROBLEMS}" ]; then + NOTE="pt-table-checksum found ${PROBLEMS}" + if [ "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + else + NOTE="WARN $NOTE" + fi + else + NOTE="OK pt-table-checksum found no out-of-sync tables" + if [ "${OPT_INTERVAL}" -gt 0 ]; then + RECENT_CHUNKS=$(mysql_exec "SELECT IF(COALESCE(MAX(ts), NOW()) > NOW() - INTERVAL ${OPT_INTERVAL} DAY, 1, 0) FROM ${OPT_TABLE}") + if [ "${RECENT_CHUNKS}" = 0 ]; then + NOTE="pt-table-checksum was not run over last ${OPT_INTERVAL} days" + if [ "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE" + else + NOTE="WARN $NOTE" + fi + fi + fi + fi + else + if grep "Table '${OPT_TABLE}' doesn't exist" "${TEMP}" >/dev/null 2>&1; then + NOTE="UNK table '${OPT_TABLE}' doesn't exist" + fi + fi + echo $NOTE +} + +# ######################################################################## +# Execute a MySQL command. +# ######################################################################## +mysql_exec() { + mysql ${OPT_DEFT:+--defaults-file="${OPT_DEFT}"} \ + ${OPT_LOPA:+--login-path="${OPT_LOPA}"} \ + ${OPT_HOST:+-h"${OPT_HOST}"} ${OPT_PORT:+-P"${OPT_PORT}"} \ + ${OPT_USER:+-u"${OPT_USER}"} ${OPT_PASS:+-p"${OPT_PASS}"} \ + ${OPT_SOCK:+-S"${OPT_SOCK}"} -ss -e "$1" +} + +# ######################################################################## +# Retrieve a paragraph from the given file, which includes MAGIC_$2 as a +# pattern. +# ######################################################################## +get_magic_query() { + perl -00 -ne"m/MAGIC_$2/ && print" "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-pt-table-checksum" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-pt-table-checksum - Alert when pt-table-checksum finds data differences on a replica. + +=head1 SYNOPSIS + + Usage: pmp-check-pt-table-checksum [OPTIONS] + Options: + -c CRIT Raise a critical error instead of a warning. + --defaults-file FILE Only read mysql options from the given file. + Defaults to /etc/nagios/mysql.cnf if it exists. + -H HOST MySQL hostname. + -l USER MySQL username. + -L LOGIN-PATH Use login-path to access MySQL (with MySQL client 5.6). + -p PASS MySQL password. + -P PORT MySQL port. + -S SOCKET MySQL socket file. + -i INTERVAL Interval over which to ensure pt-table-checksum was run, + in days; default - not to check. + -T TABLE The checksum table; default percona.checksums + -w WARN Warning threshold; ignored. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin examines whether MySQL replication has drifted out of sync +with the master's data, according to checks performed by the pt-table-checksum +tool in Percona Toolkit. It uses the following query to determine whether the +server's data matches its master's: + + SELECT /* MAGIC_checksum_diff_query */ + CONCAT( + COUNT(*), + ' chunks differ in ', + COUNT(DISTINCT CONCAT(db, tbl)), + ' tables, including ', + MIN(CONCAT(db, '.', tbl))) + FROM CHECKSUM_TABLE + WHERE master_cnt <> this_cnt OR master_crc <> this_crc + OR ISNULL(master_crc) <> ISNULL(this_crc) + HAVING COUNT(*) > 0 + +The word CHECKSUM_TABLE is replaced by the value of the -T option. If the table +specified by -T does not exist, unknown is raised. + +Optionally, you can specify an interval in days over which to ensure pt-table-checksum +was run. It is useful in cases when the cron job doing the checksumming suddenly +stopped working. This option will have an effect when no diffs are found and the +checksum table is not empty. + +Alerts are raised at a WARNING level by default, but specifying the -c option +with any value will change this to CRITICAL instead. + +=head1 PRIVILEGES + +This plugin executes the following commands against MySQL: + +=over + +=item * + +C<SELECT> against the specified table. + +=back + +This plugin executes no UNIX commands that may need special privileges. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-pt-table-checksum $VERSION$ + +=cut + +DOCUMENTATION diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-unix-memory b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-unix-memory new file mode 100755 index 0000000..a9a3fdc --- /dev/null +++ b/nagios-plugins-contrib-24.20190301~bpo9+1/percona-nagios-plugins/nagios/bin/pmp-check-unix-memory @@ -0,0 +1,207 @@ +#!/bin/sh + +# ######################################################################## +# This program is part of $PROJECT_NAME$ +# License: GPL License (see COPYING) +# Authors: +# Baron Schwartz, Roman Vynar +# ######################################################################## + +# ######################################################################## +# Redirect STDERR to STDOUT; Nagios doesn't handle STDERR. +# ######################################################################## +exec 2>&1 + +# ######################################################################## +# Set up constants, etc. +# ######################################################################## +STATE_OK=0 +STATE_WARNING=1 +STATE_CRITICAL=2 +STATE_UNKNOWN=3 +STATE_DEPENDENT=4 + +# ######################################################################## +# Run the program. +# ######################################################################## +main() { + + # Get options + for o; do + case "${o}" in + -w) shift; OPT_WARN="${1}"; shift; ;; + -d) shift; ;; # left for backward-compatibility + -c) shift; OPT_CRIT="${1}"; shift; ;; + --version) grep -A2 '^=head1 VERSION' "$0" | tail -n1; exit 0 ;; + --help) perl -00 -ne 'm/^ Usage:/ && print' "$0"; exit 0 ;; + -*) echo "Unknown option ${o}. Try --help."; exit 1; ;; + esac + done + OPT_WARN=${OPT_WARN:-90} + OPT_CRIT=${OPT_CRIT:-95} + if is_not_sourced; then + if [ -n "$1" ]; then + echo "WARN spurious command-line options: $@" + exit 1 + fi + fi + + NOTE="UNK cannot find memory statistics" + TEMP=$(mktemp -t "${0##*/}.XXXXXX") || exit $? + trap "rm -f '${TEMP}' >/dev/null 2>&1" EXIT + if [ -e /proc/meminfo ] && cat /proc/meminfo > "${TEMP}" ; then + USD_PCT=$(get_used_memory_linux "${TEMP}") + elif which sysctl > /dev/null && sysctl -a > "${TEMP}" ; then + USD_PCT=$(get_used_memory_bsd "${TEMP}") + else + echo $NOTE + exit + fi + + NOTE="Memory ${USD_PCT}% used" + if [ "${USD_PCT:-0}" -ge "${OPT_CRIT}" ]; then + NOTE="CRIT $NOTE. $(get_largest_process)" + elif [ "${USD_PCT:-0}" -ge "${OPT_WARN}" ]; then + NOTE="WARN $NOTE. $(get_largest_process)" + else + NOTE="OK $NOTE" + fi + + # Build the common perf data output for graph trending + PERFDATA="memory_used=${USD_PCT:-0};${OPT_WARN};${OPT_CRIT};0;100" + echo "$NOTE | $PERFDATA" +} + +# ######################################################################## +# Find the largest process +# ######################################################################## +get_largest_process () { + ALL_PROCS=$(ps axo pid,%mem,ucomm | sort -k2 -nr) + BIGGEST=$(echo $ALL_PROCS | head -n 1) + BIG_PID=$(echo $BIGGEST | cut -d' ' -f1) + BIG_MEM=$(echo $BIGGEST | cut -d' ' -f2) + BIG_COMMAND=$(echo $BIGGEST | cut -d' ' -f3) + echo "Largest process: ${BIG_COMMAND} (${BIG_PID}) = ${BIG_MEM}%" +} + +# ######################################################################## +# Get the used memory from /proc/meminfo +# Consider MemAvailable if available in the file (starting procps-ng 3.3.10). +# See https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 +# ######################################################################## +get_used_memory_linux() { +awk '/^MemTotal:/ {total = $2;} + /^MemFree:/ {free = $2;} + /^MemAvailable:/ {avail = $2;} + /^Buffers:/ {buffers = $2;} + /^Cached:/ {cached = $2; + if (avail == "") avail = free + buffers + cached; + printf "%d\n", (total - avail) / total * 100;}' "$1" +} +# ######################################################################## +# Get the used memory from sysctl for BSD systems +# ######################################################################## +get_used_memory_bsd() { + awk '/vm.stats.vm.v_cache_count:/ {cache = $2} + /vm.stats.vm.v_inactive_count:/ {inactive = $2} + /vm.stats.vm.v_free_count:/ {free = $2} + /vm.stats.vm.v_page_count:/ { + total = $2; + used = total - inactive - cache - free; + printf "%d\n", used / total * 100}' "$1" +} + +# ######################################################################## +# Determine whether this program is being executed directly, or sourced/included +# from another file. +# ######################################################################## +is_not_sourced() { + [ "${0##*/}" = "pmp-check-unix-memory" ] || [ "${0##*/}" = "bash" -a "$_" = "$0" ] +} + +# ######################################################################## +# Execute the program if it was not included from another file. +# This makes it possible to include without executing, and thus test. +# ######################################################################## +if is_not_sourced; then + OUTPUT=$(main "$@") + EXITSTATUS=$STATE_UNKNOWN + case "${OUTPUT}" in + UNK*) EXITSTATUS=$STATE_UNKNOWN; ;; + OK*) EXITSTATUS=$STATE_OK; ;; + WARN*) EXITSTATUS=$STATE_WARNING; ;; + CRIT*) EXITSTATUS=$STATE_CRITICAL; ;; + esac + echo "${OUTPUT}" + exit $EXITSTATUS +fi + +# ############################################################################ +# Documentation +# ############################################################################ +: <<'DOCUMENTATION' +=pod + +=head1 NAME + +pmp-check-unix-memory - Alert on low memory. + +=head1 SYNOPSIS + + Usage: pmp-check-unix-memory [OPTIONS] + Options: + -c CRIT Critical threshold; default 95%. + -w WARN Warning threshold; default 90%. + --help Print help and exit. + --version Print version and exit. + Options must be given as --option value, not --option=value or -Ovalue. + Use perldoc to read embedded documentation with more details. + +=head1 DESCRIPTION + +This Nagios plugin examines C</proc/meminfo> (Linux) or the output of C<sysctl> (BSD) +to check whether the system is running out of memory and finds the largest +process in memory from C<ps> output. + +The plugin is tested on GNU/Linux and FreeBSD. + +=head1 PRIVILEGES + +This plugin does not access MySQL. + +This plugin executes the following UNIX commands that may need special privileges: + +=over + +=item * + +cat /proc/meminfo (Linux), sysctl (BSD) + +=item * + +ps + +=back + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2012-$CURRENT_YEAR$ Baron Schwartz, 2012-$CURRENT_YEAR$ Percona Inc. +Feedback and improvements are welcome. + +THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, version 2. You should have received a copy of the GNU General +Public License along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + +=head1 VERSION + +$PROJECT_NAME$ pmp-check-unix-memory $VERSION$ + +=cut + +DOCUMENTATION |