From ed2ff78a1ebafbf6bd965b195960627cbee3d767 Mon Sep 17 00:00:00 2001 From: Jan Philipp Timme <jan.philipp@timme.it> Date: Tue, 7 Jan 2020 11:58:44 +0100 Subject: [PATCH] Add more vendor files for later use --- checkmk/checkmk-files/apache_status | 182 +++++++ checkmk/checkmk-files/check_mk_agent.freebsd | 1 + checkmk/checkmk-files/mk_apt | 65 +++ checkmk/checkmk-files/mk_haproxy.freebsd | 2 +- checkmk/checkmk-files/mk_logins | 29 ++ checkmk/checkmk-files/mk_postgres | 520 +++++++++++++++++++ checkmk/checkmk-files/mk_zypper | 50 ++ checkmk/checkmk-files/netstat.linux | 31 ++ checkmk/checkmk-files/nginx_status | 153 ++++++ checkmk/checkmk-files/smart | 179 +++++++ 10 files changed, 1211 insertions(+), 1 deletion(-) create mode 100644 checkmk/checkmk-files/apache_status create mode 100644 checkmk/checkmk-files/mk_apt create mode 100644 checkmk/checkmk-files/mk_logins create mode 100644 checkmk/checkmk-files/mk_postgres create mode 100644 checkmk/checkmk-files/mk_zypper create mode 100644 checkmk/checkmk-files/netstat.linux create mode 100644 checkmk/checkmk-files/nginx_status create mode 100644 checkmk/checkmk-files/smart diff --git a/checkmk/checkmk-files/apache_status b/checkmk/checkmk-files/apache_status new file mode 100644 index 0000000..ad11fc9 --- /dev/null +++ b/checkmk/checkmk-files/apache_status @@ -0,0 +1,182 @@ +#!/usr/bin/python +# -*- encoding: utf-8; py-indent-offset: 4 -*- +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# Check_MK-Agent-Plugin - Apache Server Status +# +# Fetches the server-status page from detected or configured apache +# processes to gather status information about this apache process. +# +# To make this agent plugin work you have to load the status_module +# into your apache process. It is also needed to enable the "server-status" +# handler below the URL "/server-status". +# +# By default this plugin tries to detect all locally running apache processes +# and to monitor them. If this is not good for your environment you might +# create an apache_status.cfg file in MK_CONFDIR and populate the servers +# list to prevent executing the detection mechanism. +# +# It is also possible to override or extend the ssl_ports variable to make the +# check contact other ports than 443 with HTTPS requests. +import os +import re +import socket +import sys +import urllib2 + + +config_dir = os.getenv("MK_CONFDIR", "/etc/check_mk") +config_file = config_dir + "/apache_status.conf" + +if not os.path.exists(config_file): + config_file = config_dir + "/apache_status.cfg" + +# We have to deal with socket timeouts. Python > 2.6 +# supports timeout parameter for the urllib2.urlopen method +# but we are on a python 2.5 system here which seem to use the +# default socket timeout. We are local here so set it to 1 second. +socket.setdefaulttimeout(5.0) + +# None or list of (proto, ipaddress, port) tuples. +# proto is 'http' or 'https' +servers = None +ssl_ports = [443] + + +if os.path.exists(config_file): + execfile(config_file) + + +def try_detect_servers(): + results = [] + + for line in os.popen('netstat -tlnp 2>/dev/null').readlines(): + parts = line.split() + # Skip lines with wrong format + if len(parts) < 7 or '/' not in parts[6]: + continue + + pid, proc = parts[6].split('/', 1) + to_replace = re.compile('^.*/') + proc = to_replace.sub('', proc) + + procs = [ + 'apache2', + 'httpd', + 'httpd-prefork', + 'httpd2-prefork', + 'httpd2-worker', + 'httpd.worker', + 'fcgi-pm', + ] + # the pid/proc field length is limited to 19 chars. Thus in case of + # long PIDs, the process names are stripped of by that length. + # Workaround this problem here + procs = [ p[:19 - len(pid) - 1] for p in procs ] + + # Skip unwanted processes + if proc not in procs: + continue + + address, port = parts[3].rsplit(':', 1) + port = int(port) + + # Use localhost when listening globally + if address == '0.0.0.0': + address = '127.0.0.1' + elif address == '::': + address = '[::1]' + elif ':' in address: + address = '[%s]' % address + + # Switch protocol if port is SSL port. In case you use SSL on another + # port you would have to change/extend the ssl_port list + if port in ssl_ports: + proto = 'https' + else: + proto = 'http' + + results.append((proto, address, port)) + + return results + + +if servers is None: + servers = try_detect_servers() + + +if not servers: + sys.exit(0) + + +sys.stdout.write('<<<apache_status>>>\n') +for server in servers: + if isinstance(server, tuple): + proto, address, port = server + page = 'server-status' + else: + proto = server['protocol'] + address = server['address'] + port = server['port'] + page = server.get('page', 'server-status') + + portspec = port and ":%d" % port or "" + + try: + url = '%s://%s%s/%s?auto' % (proto, address, portspec, page) + is_local = address in ("127.0.0.1", "[::1]", "localhost") + # Try to fetch the status page for each server + try: + request = urllib2.Request(url, headers={"Accept" : "text/plain"}) + if is_local and proto == 'https': + import ssl + no_cert_context = ssl.create_default_context() + no_cert_context.check_hostname = False + no_cert_context.verify_mode = ssl.CERT_NONE + fd = urllib2.urlopen(url, context=no_cert_context) + else: + fd = urllib2.urlopen(request) + except urllib2.URLError, e: + if 'unknown protocol' in str(e): + # HACK: workaround misconfigurations where port 443 is used for + # serving non ssl secured http + url = 'http://%s%s/server-status?auto' % (address, portspec) + fd = urllib2.urlopen(url) + else: + raise + + for line in fd.read().split('\n'): + if not line.strip(): + continue + if line.lstrip()[0] == '<': + # Seems to be html output. Skip this server. + break + + sys.stdout.write("%s %s %s\n" % (address, port, line)) + except urllib2.HTTPError, e: + sys.stderr.write('HTTP-Error (%s%s): %s %s\n' % (address, portspec, e.code, e)) + + except Exception, e: + sys.stderr.write('Exception (%s%s): %s\n' % (address, portspec, e)) diff --git a/checkmk/checkmk-files/check_mk_agent.freebsd b/checkmk/checkmk-files/check_mk_agent.freebsd index f75ed90..c1b3601 100644 --- a/checkmk/checkmk-files/check_mk_agent.freebsd +++ b/checkmk/checkmk-files/check_mk_agent.freebsd @@ -442,3 +442,4 @@ then echo done fi + diff --git a/checkmk/checkmk-files/mk_apt b/checkmk/checkmk-files/mk_apt new file mode 100644 index 0000000..31f930b --- /dev/null +++ b/checkmk/checkmk-files/mk_apt @@ -0,0 +1,65 @@ +#!/bin/bash +# Check for APT updates (Debian, Ubuntu) +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# TODO: +# Einstellungen: +# - upgrade oder dist-upgrade +# - vorher ein update machen +# Bakery: +# - Bakelet anlegen +# - Async-Zeit einstellbar machen und das Ding immer async laufen lassen +# Check programmieren: +# * Schwellwerte auf Anzahlen +# * Regexen auf Pakete, die zu CRIT/WARN führen +# - Graph malen mit zwei Kurven + +# This variable can either be "upgrade" or "dist-upgrade" +UPGRADE=upgrade +DO_UPDATE=yes + + +function check_apt_update { + if [ "$DO_UPDATE" = yes ] ; then + # NOTE: Even with -qq, apt-get update can output several lines to + # stderr, e.g.: + # + # W: There is no public key available for the following key IDs: + # 1397BC53640DB551 + apt-get update -qq 2> /dev/null + fi + apt-get -o 'Debug::NoLocking=true' -o 'APT::Get::Show-User-Simulation-Note=false' -s -qq "$UPGRADE" | grep -v '^Conf' +} + + +if type apt-get > /dev/null ; then + echo '<<<apt:sep(0)>>>' + out=$(check_apt_update) + if [ -z "$out" ]; then + echo "No updates pending for installation" + else + echo "$out" + fi +fi diff --git a/checkmk/checkmk-files/mk_haproxy.freebsd b/checkmk/checkmk-files/mk_haproxy.freebsd index dd8f6c6..7c7a0fe 100644 --- a/checkmk/checkmk-files/mk_haproxy.freebsd +++ b/checkmk/checkmk-files/mk_haproxy.freebsd @@ -1,4 +1,4 @@ -if [ -r /var/run/haproxy.sock ]; then +if [ -r /var/run/haproxy.stat ]; then echo "<<<haproxy:sep(44)>>>" echo "show stat" | socat - UNIX-CONNECT:/var/run/haproxy.sock fi diff --git a/checkmk/checkmk-files/mk_logins b/checkmk/checkmk-files/mk_logins new file mode 100644 index 0000000..b4b8b64 --- /dev/null +++ b/checkmk/checkmk-files/mk_logins @@ -0,0 +1,29 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +if type who >/dev/null; then + echo "<<<logins>>>" + who | wc -l +fi diff --git a/checkmk/checkmk-files/mk_postgres b/checkmk/checkmk-files/mk_postgres new file mode 100644 index 0000000..47fef50 --- /dev/null +++ b/checkmk/checkmk-files/mk_postgres @@ -0,0 +1,520 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2015 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + + +# TODO postgres_connections output format + + +# .--common funcs--------------------------------------------------------. +# | __ | +# | ___ ___ _ __ ___ _ __ ___ ___ _ __ / _|_ _ _ __ ___ ___ | +# | / __/ _ \| '_ ` _ \| '_ ` _ \ / _ \| '_ \ | |_| | | | '_ \ / __/ __| | +# || (_| (_) | | | | | | | | | | | (_) | | | || _| |_| | | | | (__\__ \ | +# | \___\___/|_| |_| |_|_| |_| |_|\___/|_| |_||_| \__,_|_| |_|\___|___/ | +# | | +# '----------------------------------------------------------------------' + + +function compare_version_greater_equal() { + local GREATER_ONE + GREATER_ONE=$(echo "$1 $2" | awk '{if ($1 >= $2) print $1; else print $2}') + if [ "$GREATER_ONE" == "$1" ] ; then + return 0 + else + return 1 + fi +} + + +#. +# .--section funcs-------------------------------------------------------. +# | _ _ __ | +# | ___ ___ ___| |_(_) ___ _ __ / _|_ _ _ __ ___ ___ | +# | / __|/ _ \/ __| __| |/ _ \| '_ \ | |_| | | | '_ \ / __/ __| | +# | \__ \ __/ (__| |_| | (_) | | | | | _| |_| | | | | (__\__ \ | +# | |___/\___|\___|\__|_|\___/|_| |_| |_| \__,_|_| |_|\___|___/ | +# | | +# '----------------------------------------------------------------------' + + +function postgres_instances() { + echo '<<<postgres_instances>>>' + # If we have no instances we take db id (pqsql/postgres) because + # ps output may be unreadable + # In case of instances ps output shows them readable + if [ ! -z "${1}" ]; then + echo "[[[${1}]]]" + fi + + # shellcheck disable=SC2009 + # The pgrep command would be different for older distros. Newer distros + # need the -a option, but older ones need the -f option. The first idea + # was to use: + # pgrep -af bin/postgres 2>/dev/null || pgrep -lf bin/postgres + # but SLES 11 returns the exit code 0 for a wrong command line. + ps -eo pid,command | grep bin/postgres | grep -vE '^[ ]*[0-9]+ grep ' +} + + +function postgres_sessions() { + local OUTPUT + + # In postgresql 9.2 the column state was introduced, which has to be queried to + # find the state of the sessions. The column state can be NULL. + CONDITION="$ROW = $IDLE" + OUTPUT="$(echo "\echo '<<<postgres_sessions>>>${INSTANCE_SECTION}' + SELECT $CONDITION, count(*) FROM pg_stat_activity WHERE $ROW IS NOT NULL GROUP BY ($CONDITION);" |\ + su - "$DBUSER" -c "$export_PGPASSFILE $psql -X --variable ON_ERROR_STOP=1 ${EXTRA_ARGS} -A -t -F' '" 2>/dev/null)" + + echo "$OUTPUT" + # line with number of idle sessions is sometimes missing on Postgres 8.x. This can lead + # to an altogether empty section and thus the check disappearing. + echo "$OUTPUT" | grep -q '^t ' || echo "t 0" +} + + +function postgres_simple_queries() { + # Querytime + # Supports versions >= 8.3, > 9.1 + local QUERYTIME_QUERY + if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then + QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, state AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds, + pid, regexp_replace(query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND (state NOT LIKE 'idle%' OR state IS NULL)) ORDER BY query_start, pid DESC;" + else + QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, '' AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds, + procpid as pid, regexp_replace(current_query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND current_query NOT LIKE '<IDLE>%') ORDER BY query_start, procpid DESC;" + fi + + # Number of current connections per database + # We need to output the databases, too. + # This query does not report databases without an active query + local CONNECTIONS_QUERY + + # Here the order of the columns did not match with what the server side expects, + # with the result, that there was never any active connection shown in the + # web-gui. + CONDITION="$ROW <> $IDLE" + CONNECTIONS_QUERY="SELECT d.datname, COUNT(datid) AS current, + (SELECT setting AS mc FROM pg_settings WHERE name = 'max_connections') AS mc + FROM pg_database d + LEFT JOIN pg_stat_activity s ON (s.datid = d.oid) WHERE $CONDITION + GROUP BY 1 + ORDER BY datname;" + + echo "\pset footer off + \echo '<<<postgres_stat_database:sep(59)>>>${INSTANCE_SECTION}' + SELECT datid, datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit, tup_returned, tup_fetched, tup_inserted, tup_updated, tup_deleted, pg_database_size(datname) AS datsize FROM pg_stat_database; + + \echo '<<<postgres_locks:sep(59)>>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + SELECT datname, granted, mode FROM pg_locks l RIGHT JOIN pg_database d ON (d.oid=l.database) WHERE d.datallowconn; + + \echo '<<<postgres_query_duration:sep(59)>>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + $QUERYTIME_QUERY + + \echo '<<<postgres_connections:sep(59)>>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + $CONNECTIONS_QUERY" \ + | su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';'" +} + + +function postgres_stats() { + # Contains last vacuum time and analyze time + local LASTVACUUM="SELECT current_database() AS datname, nspname AS sname, relname AS tname, + CASE WHEN v IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS vtime, + CASE WHEN g IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS atime + FROM (SELECT nspname, relname, GREATEST(pg_stat_get_last_vacuum_time(c.oid), pg_stat_get_last_autovacuum_time(c.oid)) AS v, + GREATEST(pg_stat_get_last_analyze_time(c.oid), pg_stat_get_last_autoanalyze_time(c.oid)) AS g + FROM pg_class c, pg_namespace n + WHERE relkind = 'r' AND n.oid = c.relnamespace AND n.nspname <> 'information_schema' + ORDER BY 3) AS foo;" + + local FIRST= + local QUERY="\pset footer off + BEGIN; + SET statement_timeout=30000; + COMMIT; + + \echo '<<<postgres_stats:sep(59)>>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]'" + + for db in $DATABASES ; do + QUERY="$QUERY + \c $db + $LASTVACUUM + " + if [ -z $FIRST ] ; then + FIRST=false + QUERY="$QUERY + \pset tuples_only on + " + fi + done + echo "$QUERY" | su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';'" | grep -v -e 'COMMIT$' -e 'SET$' -e 'BEGIN$' +} + + +function postgres_version() { + # Postgres version an connection time + echo -e "<<<postgres_version:sep(1)>>>${INSTANCE_SECTION}" + (TIMEFORMAT='%3R'; time echo "SELECT version() AS v" |\ + su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -t -A -F';'; echo -e '<<<postgres_conn_time>>>${INSTANCE_SECTION}'") 2>&1 +} + + +function postgres_bloat() { + # Bloat index and tables + # Supports versions <9.0, >=9.0 + # This huge query has been gratefully taken from Greg Sabino Mullane's check_postgres.pl + local BLOAT_QUERY + if compare_version_greater_equal "$POSTGRES_VERSION" "9.0" ; then + BLOAT_QUERY="SELECT + current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta, + ROUND(CASE WHEN sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE (sml.relpages-otta::numeric)/sml.relpages END,3) AS tbloat, + CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages, + CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes, + CASE WHEN relpages < otta THEN 0 ELSE (bs*(relpages-otta))::bigint END AS wastedsize, + iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta, + ROUND(CASE WHEN ipages=0 OR ipages<=iotta THEN 0.0 ELSE (ipages-iotta::numeric)/ipages END,3) AS ibloat, + CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages, + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, + CASE WHEN ipages < iotta THEN 0 ELSE (bs*(ipages-iotta))::bigint END AS wastedisize, + CASE WHEN relpages < otta THEN + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END + ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint) + ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END + END AS totalwastedbytes + FROM ( + SELECT + nn.nspname AS schemaname, + cc.relname AS tablename, + COALESCE(cc.reltuples,0) AS reltuples, + COALESCE(cc.relpages,0) AS relpages, + COALESCE(bs,0) AS bs, + COALESCE(CEIL((cc.reltuples*((datahdr+ma- + (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta, + COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, + COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols + FROM + pg_class cc + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema' + LEFT JOIN + ( + SELECT + ma,bs,foo.nspname,foo.relname, + (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, + (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 + FROM ( + SELECT + ns.nspname, tbl.relname, hdr, ma, bs, + SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth, + MAX(coalesce(null_frac,0)) AS maxfracsum, + hdr+( + SELECT 1+count(*)/8 + FROM pg_stats s2 + WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname + ) AS nullhdr + FROM pg_attribute att + JOIN pg_class tbl ON att.attrelid = tbl.oid + JOIN pg_namespace ns ON ns.oid = tbl.relnamespace + LEFT JOIN pg_stats s ON s.schemaname=ns.nspname + AND s.tablename = tbl.relname + AND s.inherited=false + AND s.attname=att.attname, + ( + SELECT + (SELECT current_setting('block_size')::numeric) AS bs, + CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\[0-9]+.[0-9]+#\%' for '#') + IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr, + CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma + FROM (SELECT version() AS v) AS foo + ) AS constants + WHERE att.attnum > 0 AND tbl.relkind='r' + GROUP BY 1,2,3,4,5 + ) AS foo + ) AS rs + ON cc.relname = rs.relname AND nn.nspname = rs.nspname + LEFT JOIN pg_index i ON indrelid = cc.oid + LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid + ) AS sml + WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;" + else + BLOAT_QUERY="SELECT + current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta, + ROUND(CASE WHEN sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE (sml.relpages-otta::numeric)/sml.relpages END,3) AS tbloat, + CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages, + CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes, + CASE WHEN relpages < otta THEN '0 bytes'::text ELSE (bs*(relpages-otta))::bigint || ' bytes' END AS wastedsize, + iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta, + ROUND(CASE WHEN ipages=0 OR ipages<=iotta THEN 0.0 ELSE (ipages-iotta::numeric)/ipages END,3) AS ibloat, + CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages, + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, + CASE WHEN ipages < iotta THEN '0 bytes' ELSE (bs*(ipages-iotta))::bigint || ' bytes' END AS wastedisize, + CASE WHEN relpages < otta THEN + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END + ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint) + ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END + END AS totalwastedbytes + FROM ( + SELECT + nn.nspname AS schemaname, + cc.relname AS tablename, + COALESCE(cc.reltuples,0) AS reltuples, + COALESCE(cc.relpages,0) AS relpages, + COALESCE(bs,0) AS bs, + COALESCE(CEIL((cc.reltuples*((datahdr+ma- + (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta, + COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, + COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols + FROM + pg_class cc + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema' + LEFT JOIN + ( + SELECT + ma,bs,foo.nspname,foo.relname, + (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, + (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 + FROM ( + SELECT + ns.nspname, tbl.relname, hdr, ma, bs, + SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth, + MAX(coalesce(null_frac,0)) AS maxfracsum, + hdr+( + SELECT 1+count(*)/8 + FROM pg_stats s2 + WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname + ) AS nullhdr + FROM pg_attribute att + JOIN pg_class tbl ON att.attrelid = tbl.oid + JOIN pg_namespace ns ON ns.oid = tbl.relnamespace + LEFT JOIN pg_stats s ON s.schemaname=ns.nspname + AND s.tablename = tbl.relname + AND s.attname=att.attname, + ( + SELECT + (SELECT current_setting('block_size')::numeric) AS bs, + CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\"[0-9]+.[0-9]+#\"%' for '#') + IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr, + CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma + FROM (SELECT version() AS v) AS foo + ) AS constants + WHERE att.attnum > 0 AND tbl.relkind='r' + GROUP BY 1,2,3,4,5 + ) AS foo + ) AS rs + ON cc.relname = rs.relname AND nn.nspname = rs.nspname + LEFT JOIN pg_index i ON indrelid = cc.oid + LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid + ) AS sml + WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;" + fi + + local FIRST= + local QUERY="\pset footer off + \echo '<<<postgres_bloat:sep(59)>>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]'" + + for db in $DATABASES ; do + QUERY="$QUERY + \c $db + $BLOAT_QUERY + " + if [ -z $FIRST ] ; then + FIRST=false + QUERY="$QUERY + \pset tuples_only on + " + fi + done + echo "$QUERY" | su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';'" +} + + +#. +# .--main----------------------------------------------------------------. +# | _ | +# | _ __ ___ __ _(_)_ __ | +# | | '_ ` _ \ / _` | | '_ \ | +# | | | | | | | (_| | | | | | | +# | |_| |_| |_|\__,_|_|_| |_| | +# | | +# '----------------------------------------------------------------------' + + +### postgres.cfg ## +# DBUSER=OS_USER_NAME +# INSTANCE=/home/postgres/db1.env:USER_NAME:/PATH/TO/.pgpass +# INSTANCE=/home/postgres/db2.env:USER_NAME:/PATH/TO/.pgpass + +# TODO @dba USERNAME in .pgpass ? +# INSTANCE=/home/postgres/db2.env:/PATH/TO/.pgpass + + +function postgres_main() { + if [ -z "$DBUSER" ] || [ -z "$PGDATABASE" ] ; then + exit 0 + fi + + EXTRA_ARGS="" + if [ ! -z "$PGUSER" ]; then + EXTRA_ARGS=$EXTRA_ARGS" -U $PGUSER" + fi + if [ ! -z "$PGDATABASE" ]; then + EXTRA_ARGS=$EXTRA_ARGS" -d $PGDATABASE" + fi + if [ ! -z "$PGPORT" ]; then + EXTRA_ARGS=$EXTRA_ARGS" -p $PGPORT" + fi + + if [ ! -z "$PGPASSFILE" ]; then + export_PGPASSFILE="export PGPASSFILE=$PGPASSFILE; " + fi + + DATABASES="$(echo "SELECT datname FROM pg_database WHERE datistemplate = false;" |\ + su - "$DBUSER" -c "$export_PGPASSFILE $psql -X ${EXTRA_ARGS} -t -A -F';'")" + # shellcheck disable=SC2001 + # DATABASES contains one database per line. To print each database in the psql terminal + # we have to prefix each line with '\echo '. ${varialbe//search/replace} is not + # sufficient in this case. + ECHO_DATABASES="$(echo "$DATABASES" | sed 's/^/\\echo /')" + + postgres_check_server_version + postgres_sessions + postgres_simple_queries + postgres_stats + postgres_version + postgres_bloat +} + + +function postgres_check_server_version() { + # Query the server version from the server. Originally the version of the + # client tools was taken, but what really matters is the version of the server. + # Terminate the script if the server can not be queried. + # We want $(su ...) to be + # shellcheck disable=SC2046 + set -- $(su - "$DBUSER" -c "$psql -A -t -X ${EXTRA_ARGS} -c 'SHOW server_version;'" | tr '.' ' ') + test $# -eq 0 && exit 0 + POSTGRES_VERSION="$1.$2" + postgres_set_condition_vars +} + + +function postgres_set_condition_vars() { + if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then + ROW=state + IDLE="'idle'" + else + ROW=current_query + IDLE="'<IDLE>'" + fi +} + + +MK_CONFFILE=$MK_CONFDIR/postgres.cfg +if [ -e "$MK_CONFFILE" ]; then + + postgres_instances + + DBUSER=$(grep DBUSER "$MK_CONFFILE" | sed 's/.*=//g') + + # read -r: we do not want to interpret backslashes before spaces and line feeds + while read -r line; do + case $line in + INSTANCE*) + instance=$line + ;; + *) + instance= + ;; + esac + + if [ ! -z "$instance" ]; then + # Too complicated for ${variable//search/replace} + # shellcheck disable=SC2001 + instance_path=$(echo "$instance" | sed 's/.*=\(.*\):.*:.*$/\1/g') + instance_name=$(echo "$instance_path" | sed -e 's/.*\/\(.*\)/\1/g' -e 's/\.env$//g') + if [ ! -z "$instance_name" ]; then + INSTANCE_SECTION="\n[[[$instance_name]]]" + else + INSTANCE_SECTION="" + fi + + psql="/$DBUSER/$(grep "^export PGVERSION=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g')/bin/psql" + + # Too complicated for ${variable//search/replace} + # shellcheck disable=SC2001 + PGUSER=$(echo "$instance" | sed 's/.*=.*:\(.*\):.*$/\1/g') + # Too complicated for ${variable//search/replace} + # shellcheck disable=SC2001 + PGPASSFILE="$(echo "$instance" | sed 's/.*=.*:.*:\(.*\)$/\1/g')" + PGDATABASE=$(grep "^export PGDATABASE=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g') + PGPORT=$(grep "^export PGPORT=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g') + + # Fallback + if [ ! -f "$psql" ]; then + psql="$(grep "^export PGHOME=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g')/psql" + fi + + postgres_main + + fi + done < "$MK_CONFFILE" + +else + + if id pgsql >/dev/null 2>&1; then + DBUSER=pgsql + elif id postgres >/dev/null 2>&1; then + DBUSER=postgres + else + exit 0 + fi + + PGDATABASE=postgres + INSTANCE_SECTION="" + psql="psql" + + postgres_instances "$DBUSER" + postgres_main + +fi diff --git a/checkmk/checkmk-files/mk_zypper b/checkmk/checkmk-files/mk_zypper new file mode 100644 index 0000000..a12d09b --- /dev/null +++ b/checkmk/checkmk-files/mk_zypper @@ -0,0 +1,50 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +if type zypper > /dev/null ; then + echo '<<<zypper:sep(124)>>>' + if grep -q '^VERSION = 10' < /etc/SuSE-release + then + ZYPPER='waitmax 10 zypper --no-gpg-checks --non-interactive --terse' + REFRESH=`$ZYPPER refresh 2>&1` + if [ "$REFRESH" ] + then + echo "ERROR: $REFRESH" + else + { $ZYPPER pchk || [ $? = 100 -o $? = 101 ] && $ZYPPER lu ; } \ + | egrep '(patches needed|\|)' | egrep -v '^(#|Repository |Catalog )' + fi + else + ZYPPER='waitmax 10 zypper --no-gpg-checks --non-interactive --quiet' + REFRESH=`$ZYPPER refresh 2>&1` + if [ "$REFRESH" ] + then + echo "ERROR: $REFRESH" + else + { { $ZYPPER pchk || [ $? = 100 -o $? = 101 ] && $ZYPPER lp ; } ; $ZYPPER ll ; } \ + | egrep '(patches needed|\|)' | egrep -v '^(#|Repository)' + fi + fi +fi diff --git a/checkmk/checkmk-files/netstat.linux b/checkmk/checkmk-files/netstat.linux new file mode 100644 index 0000000..55a27ae --- /dev/null +++ b/checkmk/checkmk-files/netstat.linux @@ -0,0 +1,31 @@ +#!/bin/sh +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# This is not part of the standard agent since it can take very +# long to run if your TCP/UDP table is large. Netstat seems to +# have an execution time complexity of at least O(n^2) on Linux. + +echo '<<<netstat>>>' +netstat -ntua | egrep '^(tcp|udp)' | sed -e 's/LISTEN/LISTENING/g' diff --git a/checkmk/checkmk-files/nginx_status b/checkmk/checkmk-files/nginx_status new file mode 100644 index 0000000..8eb52fc --- /dev/null +++ b/checkmk/checkmk-files/nginx_status @@ -0,0 +1,153 @@ +#!/usr/bin/python +# -*- encoding: utf-8; py-indent-offset: 4 -*- +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# Check_MK-Agent-Plugin - Nginx Server Status +# +# Fetches the stub nginx_status page from detected or configured nginx +# processes to gather status information about this process. +# +# Take a look at the check man page for details on how to configure this +# plugin and check. +# +# By default this plugin tries to detect all locally running processes +# and to monitor them. If this is not good for your environment you might +# create an nginx_status.cfg file in MK_CONFDIR and populate the servers +# list to prevent executing the detection mechanism. + +import os +import re +import sys +import urllib2 + +# tell urllib2 not to honour "http(s)_proxy" env variables +urllib2.getproxies = lambda: {} + +config_dir = os.getenv("MK_CONFDIR", "/etc/check_mk") +config_file = config_dir + "/nginx_status.cfg" + +# None or list of (proto, ipaddress, port) tuples. +# proto is 'http' or 'https' +servers = None +ssl_ports = [ + 443, +] + +if os.path.exists(config_file): + execfile(config_file) + + +def try_detect_servers(): + pids = [] + results = [] + for netstat_line in os.popen('netstat -tlnp 2>/dev/null').readlines(): + parts = netstat_line.split() + # Skip lines with wrong format + if len(parts) < 7 or '/' not in parts[6]: + continue + + pid, proc = parts[6].split('/', 1) + to_replace = re.compile('^.*/') + proc = to_replace.sub('', proc) + + procs = ['nginx', 'nginx:', 'nginx.conf'] + # the pid/proc field length is limited to 19 chars. Thus in case of + # long PIDs, the process names are stripped of by that length. + # Workaround this problem here + procs = [p[:19 - len(pid) - 1] for p in procs] + + # Skip unwanted processes + if proc not in procs: + continue + + # Add only the first found port of a single server process + if pid in pids: + continue + pids.append(pid) + + server_proto = 'http' + server_address, server_port = parts[3].rsplit(':', 1) + server_port = int(server_port) + + # Use localhost when listening globally + if server_address == '0.0.0.0': + server_address = '127.0.0.1' + elif server_address == '::': + server_address = '::1' + + # Switch protocol if port is SSL port. In case you use SSL on another + # port you would have to change/extend the ssl_port list + if server_port in ssl_ports: + server_proto = 'https' + + results.append((server_proto, server_address, server_port)) + + return results + + +if servers is None: + servers = try_detect_servers() + +if not servers: + sys.exit(0) + +sys.stdout.write('<<<nginx_status>>>\n') +for server in servers: + if isinstance(server, tuple): + proto, address, port = server + page = 'nginx_status' + else: + proto = server['protocol'] + address = server['address'] + port = server['port'] + page = server.get('page', 'nginx_status') + + try: + url = '%s://%s:%s/%s' % (proto, address, port, page) + # Try to fetch the status page for each server + try: + request = urllib2.Request(url, headers={"Accept": "text/plain"}) + fd = urllib2.urlopen(request) + except urllib2.URLError, e: + if 'SSL23_GET_SERVER_HELLO:unknown protocol' in str(e): + # HACK: workaround misconfigurations where port 443 is used for + # serving non ssl secured http + url = 'http://%s:%s/%s' % (address, port, page) + fd = urllib2.urlopen(url) + else: + raise + + for line in fd.read().split('\n'): + if not line.strip(): + continue + if line.lstrip()[0] == '<': + # seems to be html output. Skip this server. + break + sys.stdout.write("%s %s %s\n" % (address, port, line)) + except urllib2.HTTPError, e: + sys.stderr.write('HTTP-Error (%s:%d): %s %s\n' % (address, port, e.code, e)) + + except Exception, e: + sys.stderr.write('Exception (%s:%d): %s\n' % (address, port, e)) diff --git a/checkmk/checkmk-files/smart b/checkmk/checkmk-files/smart new file mode 100644 index 0000000..6e28c91 --- /dev/null +++ b/checkmk/checkmk-files/smart @@ -0,0 +1,179 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk 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 in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + + + + +# This will be called on LSI based raidcontrollers and accesses +# the SMART data of SATA disks attached to a SAS Raid HBA via +# SCSI protocol interface. +megaraid_info() +{ + #PDINFO=$(MegaCli -PDlist -a0) + if [ -z "$1" ]; then + PDINFO=$(megacli -PDlist -a0 -NoLog) + else + PDINFO=$($1 -PDlist -a0 -NoLog) + fi + + echo "$PDINFO" | \ + while read line ; do + case "$line" in + # FIRST LINE + "Enclosure Device ID"*) #Enclosure Device ID: 252 + ENC=$( echo "$line" | awk '{print $4}') + unset SLOT LOG_DEV_ID VEND MODEL + ;; + "Slot Number"*) #Slot Number: 7 + SLOT=$( echo "$line" | awk '{print $3}') + ;; + # Identify the logical device ID. smartctl needs it to access the disk. + "Device Id"*) #Device Id: 19 + LOG_DEV_ID=$( echo "$line" | awk '{print $3}') + ;; + "PD Type"*) #PD Type: SATA + VEND=$( echo "$line" | awk '{print $3}') + ;; + # This is the last value, generate output here + "Inquiry Data"*) + #Inquiry Data: WD-WCC1T1035197WDC WD20EZRX-00DC0B0 80.00A80 + # $4 seems to be better for some vendors... wont be possible to get this perfect. + MODEL=$( echo "$line" | awk '{print $3}') + + # /dev/sdc ATA SAMSUNG_SSD_830 5 Reallocated_Sector_Ct 0x0033 100 100 010 Pre-fail Always - + smartctl -d megaraid,${LOG_DEV_ID} -v 9,raw48 -A /dev/sg0 | \ + grep Always | egrep -v '^190(.*)Temperature(.*)' | \ + sed "s|^|Enc${ENC}/Slot${SLOT} $VEND $MODEL |" + ;; + esac + done +} + + +# Only handle always updated values, add device path and vendor/model +if which smartctl > /dev/null 2>&1 ; then + # + # if the 3ware-utility is found + # get the serials for all disks on the controller + # + if which tw_cli > /dev/null 2>&1 ; then + # support for only one controller at the moment + TWAC=$(tw_cli show | awk 'NR < 4 { next } { print $1 }' | head -n 1) + + # - add a trailing zero to handle case of unused slot + # trailing zeros are part of the device links in /dev/disk/by-id/... anyway + # - only the last 9 chars seem to be relevant + # (hopefully all this doesn't change with new kernels...) + eval `tw_cli /$TWAC show drivestatus | grep -E '^p[0-9]' | awk '{print $1 " " $7 "0"}' | while read twaminor serial ; do + twaminor=${twaminor#p} + serial=${serial:(-9)} + serial=AMCC_${serial}00000000000 + echo "$serial=$twaminor" + done` + else: + echo "tw_cli not found" >&2 + fi + + echo '<<<smart>>>' + SEEN= + for D in /dev/disk/by-id/{scsi,ata}-*; do + [ "$D" != "${D%scsi-\*}" ] && continue + [ "$D" != "${D%ata-\*}" ] && continue + [ "$D" != "${D%-part*}" ] && continue + N=$(readlink $D) + N=${N##*/} + if [ -r /sys/block/$N/device/vendor ]; then + VEND=$(tr -d ' ' < /sys/block/$N/device/vendor) + else + # 2012-01-25 Stefan Kaerst CDJ - in case $N does not exist + VEND=ATA + fi + if [ -r /sys/block/$N/device/model ]; then + MODEL=$(sed -e 's/ /_/g' -e 's/_*$//g' < /sys/block/$N/device/model) + else + MODEL=$(smartctl -a $D | grep -i "device model" | sed -e "s/.*:[ ]*//g" -e "s/\ /_/g") + fi + # Excluded disk models for SAN arrays or certain RAID luns that are also not usable.. + if [ "$MODEL" = "iSCSI_Disk" -o "$MODEL" = "LOGICAL_VOLUME" ]; then + continue + fi + + # Avoid duplicate entries for same device + if [ "${SEEN//.$N./}" != "$SEEN" ] ; then + continue + fi + SEEN="$SEEN.$N." + + # strip device name for final output + DNAME=${D#/dev/disk/by-id/scsi-} + DNAME=${DNAME#/dev/disk/by-id/ata-} + # 2012-01-25 Stefan Kaerst CDJ - special option in case vendor is AMCC + CMD= + if [ "$VEND" == "AMCC" -a -n "$TWAC" ]; then + DNAME=${DNAME#1} + [ -z "${!DNAME}" ] && continue + CMD="smartctl -d 3ware,${!DNAME} -v 9,raw48 -A /dev/twa0" + # create nice device name including model + MODEL=$(tw_cli /$TWAC/p${!DNAME} show model | head -n 1 | awk -F= '{ print $2 }') + MODEL=${MODEL## } + MODEL=${MODEL// /-} + DNAME=${DNAME#AMCC_} + DNAME="AMCC_${MODEL}_${DNAME%000000000000}" + elif [ "$VEND" != "ATA" ] ; then + TEMP= + # create temperature output as expected by checks/smart + # this is a hack, TODO: change checks/smart to support SCSI-disks + eval `smartctl -d scsi -i -A $D | while read a b c d e ; do + [ "$a" == Serial ] && echo SN=$c + [ "$a" == Current -a "$b" == Drive -a "$c" == Temperature: ] && echo TEMP=$d + done` + [ -n "$TEMP" ] && CMD="echo 194 Temperature_Celsius 0x0000 000 000 000 Old_age Always - $TEMP (0 0 0 0)" + DNAME="${VEND}_${MODEL}_${SN}" + else + CMD="smartctl -d ata -v 9,raw48 -A $D" + fi + + [ -n "$CMD" ] && $CMD | grep Always | egrep -v '^190(.*)Temperature(.*)' | sed "s|^|$DNAME $VEND $MODEL |" + done 2>/dev/null + + + # Call MegaRaid submodule if conditions are met + if type MegaCli >/dev/null 2>&1; then + MegaCli_bin="MegaCli" + elif type MegaCli64 >/dev/null 2>&1; then + MegaCli_bin="MegaCli64" + elif type megacli >/dev/null 2>&1; then + MegaCli_bin="megacli" + else + MegaCli_bin="unknown" + fi + + if [ "$MegaCli_bin" != "unknown" ]; then + megaraid_info "$MegaCli_bin" + fi +else + echo "ERROR: smartctl not found" >&2 +fi + -- GitLab