From ce9e8dce54debf6cc4bd05ad3bbacbb57105b0dd Mon Sep 17 00:00:00 2001 From: Thomas Constans Date: Mon, 3 Mar 2025 14:17:03 +0100 Subject: [PATCH] initial commit --- Conf/userparameter_backup_age.conf | 3 + Conf/userparameter_certificates.conf | 2 + Conf/userparameter_diskstats.conf | 13 +++ Conf/userparameter_filelist.conf | 1 + Conf/userparameter_mountpoint.conf | 2 + Conf/userparameter_mysql.conf | 21 ++++ Conf/userparameter_postfix.conf | 2 + Conf/userparameter_processlist.conf | 1 + Conf/userparameter_smart.conf | 7 ++ README.md | 19 ++++ Scripts/check_ssl_cert.sh | 68 ++++++++++++ Scripts/lld_backup_status_file.py | 13 +++ Scripts/lld_certlist.py | 19 ++++ Scripts/lld_filelist.py | 15 +++ Scripts/lld_php_fpm_url.py | 25 +++++ Scripts/lld_smart_disks.pl | 158 +++++++++++++++++++++++++++ 16 files changed, 369 insertions(+) create mode 100644 Conf/userparameter_backup_age.conf create mode 100644 Conf/userparameter_certificates.conf create mode 100644 Conf/userparameter_diskstats.conf create mode 100644 Conf/userparameter_filelist.conf create mode 100644 Conf/userparameter_mountpoint.conf create mode 100644 Conf/userparameter_mysql.conf create mode 100644 Conf/userparameter_postfix.conf create mode 100644 Conf/userparameter_processlist.conf create mode 100644 Conf/userparameter_smart.conf create mode 100644 README.md create mode 100755 Scripts/check_ssl_cert.sh create mode 100755 Scripts/lld_backup_status_file.py create mode 100755 Scripts/lld_certlist.py create mode 100755 Scripts/lld_filelist.py create mode 100755 Scripts/lld_php_fpm_url.py create mode 100755 Scripts/lld_smart_disks.pl diff --git a/Conf/userparameter_backup_age.conf b/Conf/userparameter_backup_age.conf new file mode 100644 index 0000000..90b4871 --- /dev/null +++ b/Conf/userparameter_backup_age.conf @@ -0,0 +1,3 @@ +UserParameter=status_file.discovery,{{ zabbix_script_dir }}/lld_backup_status_file.py +UserParameter=check_age[*],sudo {{ zabbix_script_dir }}/check_age_quiet.py "$1" + diff --git a/Conf/userparameter_certificates.conf b/Conf/userparameter_certificates.conf new file mode 100644 index 0000000..fc3e573 --- /dev/null +++ b/Conf/userparameter_certificates.conf @@ -0,0 +1,2 @@ +UserParameter=certificate.discovery,{{ zabbix_script_dir }}/lld_certlist.py + diff --git a/Conf/userparameter_diskstats.conf b/Conf/userparameter_diskstats.conf new file mode 100644 index 0000000..5528ce5 --- /dev/null +++ b/Conf/userparameter_diskstats.conf @@ -0,0 +1,13 @@ +UserParameter=custom.vfs.discover_disks,{{ zabbix_script_dir }}/lld-disks.py + +UserParameter=custom.vfs.dev.read.ops[*],awk '{print $$1}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.read.merged[*],awk '{print $$2}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.read.sectors[*],awk '{print $$3}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.read.ms[*],awk '{print $$4}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.write.ops[*],awk '{print $$5}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.write.merged[*],awk '{print $$6}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.write.sectors[*],awk '{print $$7}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.write.ms[*],awk '{print $$8}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.io.active[*],awk '{print $$9}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.io.ms[*],awk '{print $$10}' /sys/class/block/$1/stat +UserParameter=custom.vfs.dev.weight.io.ms[*],awk '{print $$11}' /sys/class/block/$1/stat diff --git a/Conf/userparameter_filelist.conf b/Conf/userparameter_filelist.conf new file mode 100644 index 0000000..79c0849 --- /dev/null +++ b/Conf/userparameter_filelist.conf @@ -0,0 +1 @@ +UserParameter=lld_file_list.discovery[*],{{ zabbix_script_dir }}/lld_filelist.py "$1" diff --git a/Conf/userparameter_mountpoint.conf b/Conf/userparameter_mountpoint.conf new file mode 100644 index 0000000..45e85b0 --- /dev/null +++ b/Conf/userparameter_mountpoint.conf @@ -0,0 +1,2 @@ +UserParameter=check_mountpoint[*],grep -q "$1" /proc/mounts ; echo $? + diff --git a/Conf/userparameter_mysql.conf b/Conf/userparameter_mysql.conf new file mode 100644 index 0000000..9c59bf3 --- /dev/null +++ b/Conf/userparameter_mysql.conf @@ -0,0 +1,21 @@ +# For all the following commands HOME should be set to the directory that has .my.cnf file with password information. + +# Flexible parameter to grab global variables. On the frontend side, use keys like mysql.status[Com_insert]. +# Key syntax is mysql.status[variable]. +UserParameter=mysql.status[*],echo "show global status where Variable_name='$1';" | HOME={{ zabbix_home_dir }} mysql -N | awk '{print $$2}' +UserParameter=mysql.status_on[*],echo "show global status where Variable_name='$1';" | HOME={{ zabbix_home_dir }} mysql -N | awk '{print $$2}'| sed 's/ON/1/' | sed 's/OFF/0/' + +# Flexible parameter to determine database or table size. On the frontend side, use keys like mysql.size[zabbix,history,data]. +# Key syntax is mysql.size[,,]. +# Database may be a database name or "all". Default is "all". +# Table may be a table name or "all". Default is "all". +# Type may be "data", "index", "free" or "both". Both is a sum of data and index. Default is "both". +# Database is mandatory if a table is specified. Type may be specified always. +# Returns value in bytes. +# 'sum' on data_length or index_length alone needed when we are getting this information for whole database instead of a single table +UserParameter=mysql.size[*],bash -c 'echo "select sum($(case "$3" in both|"") echo "data_length+index_length";; data|index) echo "$3_length";; free) echo "data_free";; esac)) from information_schema.tables$([[ "$1" = "all" || ! "$1" ]] || echo " where table_schema=\"$1\"")$([[ "$2" = "all" || ! "$2" ]] || echo "and table_name=\"$2\"");" | HOME={{ zabbix_home_dir }} mysql -N' + +UserParameter=mysql.ping,HOME={{ zabbix_home_dir }} mysqladmin ping | grep -c alive +UserParameter=mysql.version,mysql -V + + diff --git a/Conf/userparameter_postfix.conf b/Conf/userparameter_postfix.conf new file mode 100644 index 0000000..8d49dc9 --- /dev/null +++ b/Conf/userparameter_postfix.conf @@ -0,0 +1,2 @@ +UserParameter=postfix.queue,mailq | grep -v "Mail queue is empty" | egrep -c '^[0-9A-F]{10}' + diff --git a/Conf/userparameter_processlist.conf b/Conf/userparameter_processlist.conf new file mode 100644 index 0000000..da03ff6 --- /dev/null +++ b/Conf/userparameter_processlist.conf @@ -0,0 +1 @@ +UserParameter=processlist.discovery[*],{{ zabbix_script_dir }}/lld_filelist.py {{ zabbix_process_list_file }} diff --git a/Conf/userparameter_smart.conf b/Conf/userparameter_smart.conf new file mode 100644 index 0000000..1728f83 --- /dev/null +++ b/Conf/userparameter_smart.conf @@ -0,0 +1,7 @@ +UserParameter=uHDD[*], sudo smartctl -A $1| grep -i "$2"| tail -1| cut -c 88-|cut -f1 -d' ' +UserParameter=uHDD.model.[*],sudo smartctl -i $1 |grep -i "Device Model"| cut -f2 -d: |tr -d " " +UserParameter=uHDD.sn.[*],sudo smartctl -i $1 |grep -i "Serial Number"| cut -f2 -d: |tr -d " " +UserParameter=uHDD.health.[*],sudo smartctl -H $1 |grep -i "test"| cut -f2 -d: |tr -d " " || true +UserParameter=uHDD.errorlog.[*],sudo smartctl -l error $1 |grep -i "ATA Error Count"| cut -f2 -d: |tr -d " " +### Discovery +UserParameter=uHDD.discovery,sudo {{ zabbix_script_dir }}/smartctl-disks-discovery.pl \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7519d8d --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Zabbix scripts + +Custom discovery and check scripts used by zabbix + +Scripts/* should go in /etc/zabbix/scripts/ + +Conf/* should go in /etc/zabbix/zabbix_agentd.d/ + + - Scripts/check_ssl_cert.sh + - Scripts/lld_backup_status_file.py - list all .status files in /var/run - + - Scripts/lld_certlist.py - list all certificates from /etc/letsencrypt/renewal/*.conf + - Scripts/lld_filelist.py - list all files listed in arg1 + - Scripts/lld_php_fpm_url.py - get url of phpfpm status from apache config (⚠️ RH only ) + - Scripts/lld_smart_disks.pl - get smart list disk + + +## Issues + +are all these scripts really used ? \ No newline at end of file diff --git a/Scripts/check_ssl_cert.sh b/Scripts/check_ssl_cert.sh new file mode 100755 index 0000000..74ddbb7 --- /dev/null +++ b/Scripts/check_ssl_cert.sh @@ -0,0 +1,68 @@ +#! /bin/sh +#------------------------------------------------------------ +# zext_ssl_cert.sh +# Script checks for number of days until certificate expires or the issuing authority +# depending on switch passed on command line. +# +#Based on script from aperto.fr (http://aperto.fr/cms/en/blog/15-blog-en/15-ssl-certificate-expiration-monitoring-with-zabbix.html) +#with additions by racooper@tamu.edu +#------------------------------------------------------------ + +DEBUG=0 +if [ $DEBUG -gt 0 ] +then + exec 2>>/tmp/my.log + set -x +fi + +f=$1 +host=$2 +port=$3 +sni=$4 +proto=$5 + +if [ -z "$sni" ] +then + servername=$host +else + servername=$sni +fi + +if [ -n "$proto" ] +then + starttls="-starttls $proto" +fi + +case $f in +-d) +end_date=`openssl s_client -servername $servername -host $host -port $port -showcerts $starttls -prexit /dev/null | + sed -n '/BEGIN CERTIFICATE/,/END CERT/p' | + openssl x509 -text 2>/dev/null | + sed -n 's/ *Not After : *//p'` + +if [ -n "$end_date" ] +then + end_date_seconds=`date '+%s' --date "$end_date"` + now_seconds=`date '+%s'` + echo "($end_date_seconds-$now_seconds)/24/3600" | bc +fi +;; + +-i) +issue_dn=`openssl s_client -servername $servername -host $host -port $port -showcerts $starttls -prexit /dev/null | + sed -n '/BEGIN CERTIFICATE/,/END CERT/p' | + openssl x509 -text 2>/dev/null | + sed -n 's/ *Issuer: *//p'` + +if [ -n "$issue_dn" ] +then + issuer=`echo $issue_dn | sed -n 's/.*CN=*//p'` + echo $issuer +fi +;; +*) +echo "usage: $0 [-i|-d] hostname port sni" +echo " -i Show Issuer" +echo " -d Show valid days remaining" +;; +esac diff --git a/Scripts/lld_backup_status_file.py b/Scripts/lld_backup_status_file.py new file mode 100755 index 0000000..5941af9 --- /dev/null +++ b/Scripts/lld_backup_status_file.py @@ -0,0 +1,13 @@ +#jinja2:variable_start_string:'[%',variable_end_string:'%]',comment_start_string:'<%',comment_end_string:'%>' +#! [% discovered_interpreter_python %] +import glob +import json + +if __name__ == "__main__": + # Iterate over all block devices, but ignore them if they are in the + # skippable set + skippable = () + data=[] + files=(file for file in glob.glob( "/var/run/*.status" )) + data = [{"{#STATUSFILE}": file} for file in files] + print(json.dumps({"data": data}, indent=4)) diff --git a/Scripts/lld_certlist.py b/Scripts/lld_certlist.py new file mode 100755 index 0000000..9ed9996 --- /dev/null +++ b/Scripts/lld_certlist.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +import json +import os +import re +import glob + +import sys + +if __name__ == "__main__": + data=[] + filelist = glob.glob( '/etc/letsencrypt/renewal/*.conf' ) + cert=[] + for line in filelist: + cert.append( os.path.basename(os.path.splitext(line)[0]) ) + print( os.path.splitext(line)[0] ) + + data = [{"{#CERT}": line.strip()} for line in set(cert)] + print(json.dumps({"data": data}, indent=4)) + diff --git a/Scripts/lld_filelist.py b/Scripts/lld_filelist.py new file mode 100755 index 0000000..ffa1bf1 --- /dev/null +++ b/Scripts/lld_filelist.py @@ -0,0 +1,15 @@ +#! {{ discovered_interpreter_python }} +import glob +import json +from sys import argv,stderr +import sys + +if __name__ == "__main__": + data=[] + try: + file = open( sys.argv[1] ) + except: + sys.exit(1) + + data = [{"{#FILE}": line.strip()} for line in file.readlines()] + print(json.dumps({"data": data}, indent=4)) diff --git a/Scripts/lld_php_fpm_url.py b/Scripts/lld_php_fpm_url.py new file mode 100755 index 0000000..cbc6be4 --- /dev/null +++ b/Scripts/lld_php_fpm_url.py @@ -0,0 +1,25 @@ +#jinja2:variable_start_string:'[%',variable_end_string:'%]',comment_start_string:'<%',comment_end_string:'%>' +#! [% discovered_interpreter_python %] +import json +import os +import re +import glob + +dir="/etc/httpd/conf.d/" + +phpfpm_status_url="ncstatus" + +# we get all url from apache config files +# we know that name of site == name of file +if __name__ == "__main__": + filelist = glob.glob( dir + '*.conf' ) + result=[] + for file in filelist: + poolname=os.path.basename( file ) + poolname=re.sub( '\.conf$', '', poolname ) + for line in open(file): + res=re.search( phpfpm_status_url, line) + if res: + url=re.sub( r'\.conf$', '', os.path.basename(file) ) + result.append({ "{#POOLNAME}": poolname, "{#POOLURL}": 'https://' +url +"/"+ phpfpm_status_url}) + print(json.dumps({"data": result}, indent=4)) diff --git a/Scripts/lld_smart_disks.pl b/Scripts/lld_smart_disks.pl new file mode 100755 index 0000000..997ee1c --- /dev/null +++ b/Scripts/lld_smart_disks.pl @@ -0,0 +1,158 @@ +#jinja2:variable_start_string:'[%',variable_end_string:'%]',comment_start_string:'<%',comment_end_string:'%>' +#!/usr/bin/perl +use warnings; +use strict; + +#must be run as root +my $VERSION = 0.9; + +#add path if needed into $smartctl_cmd +my $smartctl_cmd = "smartctl"; +my @input_disks; +my @global_serials; +my @smart_disks; + +if ( $^O eq 'darwin' ) { # if MAC OSX + + while ( glob('/dev/disk*') ) { + if ( $_ =~ /\/(disk+[0-9])$/ ) { push @input_disks, $1; } + } +} +else { + for (`$smartctl_cmd --scan-open`) { + + #my $testline = "# /dev/sdc -d usbjmicron # /dev/sdc [USB JMicron], ATA device open" ; + #for ($testline) { + #splitting lines like "/dev/sda -d scsi # /dev/sda, SCSI device" + #"/dev/sda [SAT] -d sat [ATA] (opened)" # in debian 6 and smartctl 5.4 + #"/dev/sda -d sat # /dev/sda [SAT], ATA device" # in debian 8 and smartctl 6.4 + #"/dev/bus/0 -d megaraid,01" for megaraid + #"# /dev/sdc -d usbjmicron # /dev/sdc [USB JMicron], ATA device open " + + my ($disk_name) = $_ =~ /(\/(.+?))\s/; + my ($disk_args) = $_ =~ /(-d [A-Za-z0-9,\+]+)/; + + if ( $disk_name and $disk_args ) { + push @input_disks, + { + disk_name => $disk_name, + disk_args => $disk_args, + subdisk => 0 + }; + } + + } +} + +foreach my $disk (@input_disks) { + + my @output_arr; + if ( @output_arr = get_smart_disks($disk) ) { + push @smart_disks, @output_arr; + } + +} + +json_discovery( \@smart_disks ); + +sub get_smart_disks { + my $disk = shift; + my @disks; + + $disk->{smart_enabled} = 0; + + chomp( $disk->{disk_name} ); + chomp( $disk->{disk_args} ); + + #my $testline = "open failed: Two devices connected, try '-d usbjmicron,[01]'"; + #my $testline = "open device: /dev/sdc [USB JMicron] failed: Two devices connected, try '-d usbjmicron,[01]'"; + #if ($disk->{subdisk} == 1) { + #$testline = "/dev/sdb -d usbjmicron,$disk->{disk_args} # /dev/sdb [USB JMicron], ATA device"; + #} + foreach my $line (`$smartctl_cmd -i $disk->{disk_name} $disk->{disk_args} 2>&1`) { + #foreach my $line ($testline) { + #print $line; + #Some disks have "Number" and some "number", so + if ( $line =~ /^Serial (N|n)umber: +(.+)$/ ) { + + #print "Serial number is ".$2."\n"; + if ( grep /$2/, @global_serials ) { + + #print "disk already exist skipping\n"; + return; + } + else { + push @global_serials, $2; + } + } + elsif ( $line =~ /Permission denied/ ) { + + warn $line; + + } + elsif ( $disk->{subdisk} == 0 and $line =~ /failed: [A-zA-Z]+ devices connected, try '(-d [a-zA-Z0-9]+,)\[([0-9]+)\]'/) { + #check for usbjmicron: "open failed: Two devices connected, try '-d usbjmicron,[01]'" + # or "open device: /dev/sdc [USB JMicron] failed: Two devices connected, try '-d usbjmicron,[01]'" + #not $disk->{subdisk} works as a guard against endless recursion + + foreach ( split //, $2 ) { #splitting [01] + + push @disks, + get_smart_disks( + { + disk_name => $disk->{disk_name}, + disk_args => $1 . $_, + subdisk => 1 + } + ); + + } + return @disks; + + } + elsif ( $line =~ /^SMART.+?: +(.+)$/ ) { + + if ( $1 =~ /Enabled/ ) { + $disk->{smart_enabled} = 1; + } + elsif ( $1 =~ /Unavailable/ ) { + `$smartctl_cmd -i $disk->{disk_name} $disk->{disk_args} 2>&1`; + } + + #if SMART is disabled then try to enable it (also offline tests etc) + elsif ( $1 =~ /Disabled/ ) { + foreach (`smartctl -s on -o on -S on $disk->{disk_name} $disk->{disk_args}`) + { + if (/SMART Enabled/) { $disk->{smart_enabled} = 1; } + } + } + + } + } + push @disks, $disk; + return @disks; + +} + +sub json_discovery { + my $disks = shift; + + my $first = 1; + print "{\n"; + print "\t\"data\":[\n\n"; + + foreach my $disk ( @{$disks} ) { + + print ",\n" if not $first; + $first = 0; + print "\t\t{\n"; + print "\t\t\t\"{#DISKNAME}\":\"".$disk->{disk_name}.q{ }.$disk->{disk_args}."\",\n"; + #print "\t\t\t\"{#DISKCMD}\":\"".$disk->{disk_name}.q{ }.$disk->{disk_args}."\",\n"; + print "\t\t\t\"{#SMART_ENABLED}\":\"".$disk->{smart_enabled}."\"\n"; + print "\t\t}"; + + } + print "\n\t]\n"; + print "}\n"; + +}