From 88f19a11b63c8637f3cdaefafd14f719c5b433a6 Mon Sep 17 00:00:00 2001 From: Trevor Batley Date: Sat, 7 Sep 2024 21:19:08 +1000 Subject: [PATCH] initial commit of file from CVS for smeserver-zabbix-server on Sat Sep 7 21:19:08 AEST 2024 --- .gitignore | 4 + Makefile | 21 + README.md | 16 +- contriborbase | 1 + createlinks | 75 ++ .../defaults/zabbix-server/DbName | 1 + .../defaults/zabbix-server/DbUser | 1 + .../defaults/zabbix-server/JabberAccount | 1 + .../defaults/zabbix-server/JabberPassword | 1 + .../defaults/zabbix-server/JabberServer | 1 + .../defaults/zabbix-server/JabberTLS | 1 + .../defaults/zabbix-server/NodeID | 1 + .../defaults/zabbix-server/TCPPort | 1 + .../defaults/zabbix-server/WebAccess | 1 + .../defaults/zabbix-server/access | 1 + .../defaults/zabbix-server/status | 1 + .../configuration/defaults/zabbix-server/type | 1 + .../migrate/zabbix-server-database | 38 + .../events/actions/smeserver-zabbix-server | 3 + root/etc/e-smith/events/actions/zabbix-server | 7 + .../etc/e-smith/sql/init105/80zabbix-server | 1 + .../etc/zabbix/web/zabbix.conf.php | 3 + .../var/lib/zabbix/.sendxmpprc | 3 + .../var/lib/zabbix/bin/sendxmpp | 4 + .../template-begin | 21 + .../etc/e-smith/sql/init105/80zabbix-server | 73 ++ .../etc/httpd/conf/httpd.conf/98zabbix-web | 45 + .../conf/httpd.conf/VirtualHosts/60ZabbixSSL | 7 + .../etc/php-fpm.d/www.conf/20zabbix-server | 60 ++ .../templates/etc/sudoers/00zabbixAlias | 1 + .../e-smith/templates/etc/sudoers/30zabbix | 1 + .../etc/zabbix/zabbix.conf.php/00header | 19 + .../etc/zabbix/zabbix.conf.php/10database | 20 + .../etc/zabbix/zabbix.conf.php/20zbxserver | 10 + .../etc/zabbix/zabbix.conf.php/30images | 1 + .../etc/zabbix/zabbix_server.conf/00header | 4 + .../etc/zabbix/zabbix_server.conf/10NodeID | 11 + .../etc/zabbix/zabbix_server.conf/20instances | 35 + .../etc/zabbix/zabbix_server.conf/30listen | 16 + .../etc/zabbix/zabbix_server.conf/40freq | 15 + .../etc/zabbix/zabbix_server.conf/50debug | 9 + .../etc/zabbix/zabbix_server.conf/60pidAndLog | 13 + .../etc/zabbix/zabbix_server.conf/70location | 17 + .../etc/zabbix/zabbix_server.conf/80database | 36 + .../share/zabbix/.user.ini/20zabbix-server | 12 + .../templates/var/lib/zabbix/.sendxmpprc/all | 3 + .../templates/var/lib/zabbix/bin/sendxmpp/all | 6 + .../zabbix-server.service.d/50koozali.conf | 3 + root/usr/share/zabbix/.user.ini | 6 + root/var/lib/zabbix/bin/cert_expire.pl | 139 +++ root/var/lib/zabbix/bin/check_cert.pl | 109 ++ root/var/lib/zabbix/bin/fping | 2 + root/var/lib/zabbix/bin/fping6 | 2 + root/var/lib/zabbix/bin/zbxtg.py | 939 ++++++++++++++++++ root/var/lib/zabbix/bin/zbxtg_group.py | 939 ++++++++++++++++++ .../lib/zabbix/bin/zbxtg_settings.example.py | 68 ++ smeserver-zabbix-server.spec | 241 +++++ 57 files changed, 3069 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 contriborbase create mode 100644 createlinks create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/DbName create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/DbUser create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberAccount create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberPassword create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberServer create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberTLS create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/NodeID create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/TCPPort create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/WebAccess create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/access create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/status create mode 100644 root/etc/e-smith/db/configuration/defaults/zabbix-server/type create mode 100644 root/etc/e-smith/db/configuration/migrate/zabbix-server-database create mode 100644 root/etc/e-smith/events/actions/smeserver-zabbix-server create mode 100644 root/etc/e-smith/events/actions/zabbix-server create mode 100644 root/etc/e-smith/templates.metadata/etc/e-smith/sql/init105/80zabbix-server create mode 100644 root/etc/e-smith/templates.metadata/etc/zabbix/web/zabbix.conf.php create mode 100644 root/etc/e-smith/templates.metadata/var/lib/zabbix/.sendxmpprc create mode 100644 root/etc/e-smith/templates.metadata/var/lib/zabbix/bin/sendxmpp create mode 100644 root/etc/e-smith/templates/etc/backup-data.d/smeserver-zabbix-server.include/template-begin create mode 100644 root/etc/e-smith/templates/etc/e-smith/sql/init105/80zabbix-server create mode 100644 root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/98zabbix-web create mode 100644 root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/60ZabbixSSL create mode 100644 root/etc/e-smith/templates/etc/php-fpm.d/www.conf/20zabbix-server create mode 100644 root/etc/e-smith/templates/etc/sudoers/00zabbixAlias create mode 100644 root/etc/e-smith/templates/etc/sudoers/30zabbix create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/00header create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/10database create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/20zbxserver create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/30images create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/00header create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/10NodeID create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/20instances create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/30listen create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/40freq create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/50debug create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/60pidAndLog create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/70location create mode 100644 root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/80database create mode 100644 root/etc/e-smith/templates/usr/share/zabbix/.user.ini/20zabbix-server create mode 100644 root/etc/e-smith/templates/var/lib/zabbix/.sendxmpprc/all create mode 100644 root/etc/e-smith/templates/var/lib/zabbix/bin/sendxmpp/all create mode 100644 root/usr/lib/systemd/system/zabbix-server.service.d/50koozali.conf create mode 100644 root/usr/share/zabbix/.user.ini create mode 100644 root/var/lib/zabbix/bin/cert_expire.pl create mode 100644 root/var/lib/zabbix/bin/check_cert.pl create mode 100644 root/var/lib/zabbix/bin/fping create mode 100644 root/var/lib/zabbix/bin/fping6 create mode 100644 root/var/lib/zabbix/bin/zbxtg.py create mode 100644 root/var/lib/zabbix/bin/zbxtg_group.py create mode 100644 root/var/lib/zabbix/bin/zbxtg_settings.example.py create mode 100644 smeserver-zabbix-server.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbb3a13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.rpm +*.log +*spec-20* +*.tar.gz diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..18330f8 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# Makefile for source rpm: smeserver-zabbix-server +# $Id: Makefile,v 1.1 2020/12/04 18:33:23 brianr Exp $ +NAME := smeserver-zabbix-server +SPECFILE = $(firstword $(wildcard *.spec)) + +define find-makefile-common +for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done +endef + +MAKEFILE_COMMON := $(shell $(find-makefile-common)) + +ifeq ($(MAKEFILE_COMMON),) +# attept a checkout +define checkout-makefile-common +test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 +endef + +MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) +endif + +include $(MAKEFILE_COMMON) diff --git a/README.md b/README.md index d8bc1b8..4670027 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ -# smeserver-zabbix-server +# smeserver-zabbix-server -SMEServer Koozali developed git repo for smeserver-zabbix-server smecontribs \ No newline at end of file +SMEServer Koozali developed git repo for smeserver-zabbix-server smecontribs + +## Wiki +
https://wiki.koozali.org/ + +## Bugzilla +Show list of outstanding bugs: [here](https://bugs.koozali.org/buglist.cgi?component=smeserver-zabbix-server&product=SME%20Contribs&query_format=advanced&limit=0&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=CONFIRMED) + +## Description + +
*This description has been generated by an LLM AI system and cannot be relied on to be fully correct.* +*Once it has been checked, then this comment will be deleted* +
diff --git a/contriborbase b/contriborbase new file mode 100644 index 0000000..9b7fd51 --- /dev/null +++ b/contriborbase @@ -0,0 +1 @@ +contribs10 diff --git a/createlinks b/createlinks new file mode 100644 index 0000000..c3b7887 --- /dev/null +++ b/createlinks @@ -0,0 +1,75 @@ +#!/usr/bin/perl -w + +use esmith::Build::CreateLinks qw(:all); + +my $event = 'zabbix-server-update'; + +# Templates to expand +templates2events("/etc/httpd/conf/httpd.conf", $event); +templates2events("/etc/zabbix/zabbix_server.conf", qw(zabbix-server-update bootstrap-console-save)); +templates2events("/etc/zabbix/zabbix.conf.php", qw(zabbix-server-update bootstrap-console-save)); + + +# new path with zabbix 4.4.6 +templates2events("/etc/zabbix/web/zabbix.conf.php", qw(zabbix-server-update bootstrap-console-save)); +templates2events("/etc/sudoers", $event); +templates2events("/var/lib/zabbix/bin/sendxmpp", $event); +templates2events("/var/lib/zabbix/.sendxmpprc", $event); +templates2events("/etc/e-smith/sql/init105/80zabbix-server", qw(zabbix-server-update bootstrap-console-save)); +templates2events("/etc/rc.d/init.d/masq", $event); + +# Services to restart +#safe_symlink("restart", "root/etc/e-smith/events/$event/services2adjust/mysql.init"); +safe_symlink("sigusr1", "root/etc/e-smith/events/$event/services2adjust/httpd-e-smith"); +safe_symlink("restart", "root/etc/e-smith/events/$event/services2adjust/zabbix-server"); +safe_symlink("adjust", "root/etc/e-smith/events/$event/services2adjust/masq"); +safe_symlink("/etc/e-smith/events/actions/zabbix-server","root/etc/e-smith/events/$event/50zabbix-server"); + +# PHP header and footer +safe_symlink("/etc/e-smith/templates-default/template-begin-php", "root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/template-begin"); +safe_symlink("/etc/e-smith/templates-default/template-end-php", "root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/template-end"); + +# Bash header +safe_symlink("/etc/e-smith/templates-default/template-begin-shell", "root/etc/e-smith/templates/var/lib/zabbix/bin/sendxmpp/template-begin"); + +# Start and stop links - sysV +#service_link_enhanced("zabbix-server", "S99", "7"); +#service_link_enhanced("zabbix-server", "K10", "6"); +#service_link_enhanced("zabbix-server", "K10", "0"); + +#Systemd start stop +# rpm update action (invoked by yum on install and update +$contrib = "smeserver-zabbix-server"; +safe_symlink("/etc/e-smith/events/$contrib-update", "root/etc/e-smith/events/$contrib-z50-update"); +event_actions("$contrib-update", qw( + systemd-default 88 + systemd-reload 89 + zabbix-server 92 +)); + +event_templates("$contrib-update", qw( + /etc/httpd/conf/httpd.conf + /etc/crontab + /etc/rc.d/init.d/masq + /etc/sudoers + /etc/e-smith/sql/init105/80zabbix-server + /etc/zabbix/zabbix.conf.php + /etc/zabbix/web/zabbix.conf.php + /var/lib/zabbix/bin/sendxmpp + /etc/zabbix/zabbix_server.conf + /usr/share/zabbix/.user.ini + /etc/systemd/system-preset/49-koozali.preset + /etc/opt/remi/php74/php-fpm.d/www.conf + /etc/backup-data.d/smeserver-zabbix-server.include + +)); + +event_services("$contrib-update", qw( + httpd-e-smith sigusr1 + php74-php-fpm reload + masq adjust +)); + +#zabbix server 92 will stop zabbix, restart mariadb105-mysql.init and start zabbix +#that is why those services are not listed in event_service. order matter. +#also this is at the end to be sure that mariadb105 is installed and running diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbName b/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbName new file mode 100644 index 0000000..a2fe07f --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbName @@ -0,0 +1 @@ +zabbixdb diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbUser b/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbUser new file mode 100644 index 0000000..c147932 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/DbUser @@ -0,0 +1 @@ +zabbixuser diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberAccount b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberAccount new file mode 100644 index 0000000..1f5a82f --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberAccount @@ -0,0 +1 @@ +zabbix diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberPassword b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberPassword new file mode 100644 index 0000000..d97c5ea --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberPassword @@ -0,0 +1 @@ +secret diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberServer b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberServer new file mode 100644 index 0000000..2fbb50c --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberServer @@ -0,0 +1 @@ +localhost diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberTLS b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberTLS new file mode 100644 index 0000000..86981e6 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/JabberTLS @@ -0,0 +1 @@ +enabled diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/NodeID b/root/etc/e-smith/db/configuration/defaults/zabbix-server/NodeID new file mode 100644 index 0000000..573541a --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/NodeID @@ -0,0 +1 @@ +0 diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/TCPPort b/root/etc/e-smith/db/configuration/defaults/zabbix-server/TCPPort new file mode 100644 index 0000000..f02a806 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/TCPPort @@ -0,0 +1 @@ +10051 diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/WebAccess b/root/etc/e-smith/db/configuration/defaults/zabbix-server/WebAccess new file mode 100644 index 0000000..4083037 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/WebAccess @@ -0,0 +1 @@ +local diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/access b/root/etc/e-smith/db/configuration/defaults/zabbix-server/access new file mode 100644 index 0000000..3e18ebf --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/access @@ -0,0 +1 @@ +private diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/status b/root/etc/e-smith/db/configuration/defaults/zabbix-server/status new file mode 100644 index 0000000..86981e6 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/status @@ -0,0 +1 @@ +enabled diff --git a/root/etc/e-smith/db/configuration/defaults/zabbix-server/type b/root/etc/e-smith/db/configuration/defaults/zabbix-server/type new file mode 100644 index 0000000..24e1098 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/zabbix-server/type @@ -0,0 +1 @@ +service diff --git a/root/etc/e-smith/db/configuration/migrate/zabbix-server-database b/root/etc/e-smith/db/configuration/migrate/zabbix-server-database new file mode 100644 index 0000000..9161481 --- /dev/null +++ b/root/etc/e-smith/db/configuration/migrate/zabbix-server-database @@ -0,0 +1,38 @@ +{ + my $rec = $DB->get('zabbix-server') + || $DB->new_record('zabbix-server', {type => 'service'}); + my $pw = $rec->prop('DbPassword'); + if (not $pw or length($pw) < 57) + { + use MIME::Base64 qw(encode_base64); + + $pw = "not set due to error"; + if ( open( RANDOM, "/dev/urandom" ) ) + { + my $buf; + # 57 bytes is a full line of Base64 coding, and contains + # 456 bits of randomness - given a perfectly random /dev/random + if ( read( RANDOM, $buf, 57 ) != 57 ) + { + warn("Short read from /dev/random: $!"); + } + else + { + $pw = encode_base64($buf); + chomp $pw; + } + close RANDOM; + } + else + { + warn "Could not open /dev/urandom: $!"; + } + $rec->set_prop('DbPassword', $pw); + } + + my $AdminPass = $rec->prop('AdminPassword') || + $rec->set_prop('AdminPassword', `/usr/bin/openssl rand -base64 15 | /usr/bin/tr -c -d '[:graph:]'`); + + +} + diff --git a/root/etc/e-smith/events/actions/smeserver-zabbix-server b/root/etc/e-smith/events/actions/smeserver-zabbix-server new file mode 100644 index 0000000..583acc3 --- /dev/null +++ b/root/etc/e-smith/events/actions/smeserver-zabbix-server @@ -0,0 +1,3 @@ +systemctl daemon-reload +systemsctl preset-all +systemsctl restart zabbix-server diff --git a/root/etc/e-smith/events/actions/zabbix-server b/root/etc/e-smith/events/actions/zabbix-server new file mode 100644 index 0000000..dee37e5 --- /dev/null +++ b/root/etc/e-smith/events/actions/zabbix-server @@ -0,0 +1,7 @@ +#!/bin/bash +# restart mysql.init +/usr/bin/systemctl stop zabbix-server 1>/dev/null +/usr/bin/systemctl restart mariadb105-mysql.init 1>/dev/null +/usr/bin/systemctl start zabbix-server 1>/dev/null +exit 0 + diff --git a/root/etc/e-smith/templates.metadata/etc/e-smith/sql/init105/80zabbix-server b/root/etc/e-smith/templates.metadata/etc/e-smith/sql/init105/80zabbix-server new file mode 100644 index 0000000..940dcf3 --- /dev/null +++ b/root/etc/e-smith/templates.metadata/etc/e-smith/sql/init105/80zabbix-server @@ -0,0 +1 @@ +PERMS=0750 diff --git a/root/etc/e-smith/templates.metadata/etc/zabbix/web/zabbix.conf.php b/root/etc/e-smith/templates.metadata/etc/zabbix/web/zabbix.conf.php new file mode 100644 index 0000000..fa539ea --- /dev/null +++ b/root/etc/e-smith/templates.metadata/etc/zabbix/web/zabbix.conf.php @@ -0,0 +1,3 @@ +TEMPLATE_PATH="/etc/zabbix/zabbix.conf.php" +OUTPUT_FILENAME="/etc/zabbix/web/zabbix.conf.php" + diff --git a/root/etc/e-smith/templates.metadata/var/lib/zabbix/.sendxmpprc b/root/etc/e-smith/templates.metadata/var/lib/zabbix/.sendxmpprc new file mode 100644 index 0000000..0d67773 --- /dev/null +++ b/root/etc/e-smith/templates.metadata/var/lib/zabbix/.sendxmpprc @@ -0,0 +1,3 @@ +PERMS=0600 +UID="zabbix" +GID="zabbix" diff --git a/root/etc/e-smith/templates.metadata/var/lib/zabbix/bin/sendxmpp b/root/etc/e-smith/templates.metadata/var/lib/zabbix/bin/sendxmpp new file mode 100644 index 0000000..a44f56f --- /dev/null +++ b/root/etc/e-smith/templates.metadata/var/lib/zabbix/bin/sendxmpp @@ -0,0 +1,4 @@ +PERMS=0750 +UID="root" +GID="zabbix" + diff --git a/root/etc/e-smith/templates/etc/backup-data.d/smeserver-zabbix-server.include/template-begin b/root/etc/e-smith/templates/etc/backup-data.d/smeserver-zabbix-server.include/template-begin new file mode 100644 index 0000000..f74560e --- /dev/null +++ b/root/etc/e-smith/templates/etc/backup-data.d/smeserver-zabbix-server.include/template-begin @@ -0,0 +1,21 @@ +#Only non rpm owned files are backupe there +{ +use RPM2; +my $rpm_db = RPM2->open_rpm_db(); + +my @dirs = qw( +/etc/zabbix +/etc/zabbix/zabbix_agentd.conf.d/ +/var/lib/zabbix/bin/ +); + +foreach my $some_dir (@dirs) { + next unless ( -e $some_dir ); + opendir(my $dh, $some_dir) || die "Can't open $some_dir: $!"; + while ( (my $file = readdir $dh) ) { + next if $file =~ /^\.{1,2}$/; + $OUT .= "$some_dir/$file\n" unless $rpm_db->find_by_file("$some_dir/$file"); + } + closedir $dh; +} +} diff --git a/root/etc/e-smith/templates/etc/e-smith/sql/init105/80zabbix-server b/root/etc/e-smith/templates/etc/e-smith/sql/init105/80zabbix-server new file mode 100644 index 0000000..878a84b --- /dev/null +++ b/root/etc/e-smith/templates/etc/e-smith/sql/init105/80zabbix-server @@ -0,0 +1,73 @@ +{ +my $db = ${'zabbix-server'}{'DbName'} || 'zabbixdb'; +my $user = ${'zabbix-server'}{'DbUser'} || 'zabbixuser'; +my $pass = ${'zabbix-server'}{'DbPassword'} || 'secret'; + +my $schema = `rpm -qd zabbix-server-mysql | grep create`; +chomp $schema; +my $curcharset= ( -d "/var/lib/mysql/$db" ) ? `echo 'show variables like "character_set_database";'|mysql $db|grep character_set_database|sed -r 's/^character_set_database\\s*([a-zA-Z0-9_-]+)/\\1/'` : "utf8"; +chomp $curcharset; +my $adminpass= ${'zabbix-server'}{'AdminPassword'} || 'zabbix'; +$hashpass=`/usr/bin/htpasswd -bnBC 10 '' $adminpass | tr -d ':'`; +$hashpass =~ tr/\r\n//d; +$version = `/bin/ls -d /usr/share/doc/zabbix-web*|grep -Eo '[0-9.]+\$'|cut -d. -f1 || echo 4 `; +$modpass=($version > 4)? "update users set passwd='$hashpass' where alias='Admin';": "#$version"; +$OUT .= <<"END"; +#! /bin/sh +if [ -d /var/opt/rh/rh-mariadb105/lib/mysql/$db ]; then + # check if utf8 + if [[ "$curcharset" != "utf8" ]] ;then + echo "ALTER DATABASE $db CHARACTER SET utf8 COLLATE utf8_bin;" |/usr/bin/mysql105 + echo 'ALTER TABLE `$db`.`problem_tag` DROP INDEX `problem_tag_1`, ADD INDEX `problem_tag_1` (`eventid`, `tag` (100), `value`(100));' |/usr/bin/mysql105 + mysql --batch --skip-column-names --execute 'select concat("alter table ",TABLE_SCHEMA,".",TABLE_NAME," CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;") from information_schema.TABLES where TABLE_SCHEMA="$db"' | /usr/bin/mysql105 + fi + for P in \$(rpm -qd zabbix-server-mysql | grep dbpatch | grep mysql); do + /usr/bin/mysql105 $db < \$P + done +else + echo "CREATE DATABASE $db CHARACTER SET utf8 COLLATE utf8_bin;" | /usr/bin/mysql105 + /usr/bin/gunzip < $schema | /usr/bin/mysql105 $db + +fi + +/usr/bin/mysql105 < + SSLRequireSSL on + Options FollowSymLinks + AllowOverride None + #AddType application/x-httpd-php .php + + SetHandler "proxy:unix:/var/run/php-fpm/php74-zabbix-server.sock|fcgi://localhost" + + Require $access + + + + Require all denied + + Require all denied + + + + + Require all denied + + Require all denied + + + +HERE +} +} diff --git a/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/60ZabbixSSL b/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/60ZabbixSSL new file mode 100644 index 0000000..1139ed2 --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/conf/httpd.conf/VirtualHosts/60ZabbixSSL @@ -0,0 +1,7 @@ +{ + + if ($port ne ${modSSL}{'TCPPort'}){ + $OUT = ' RewriteRule ^/zabbix(/.*|$) https://%{HTTP_HOST}/zabbix$1 [L,R]'; + } +} + diff --git a/root/etc/e-smith/templates/etc/php-fpm.d/www.conf/20zabbix-server b/root/etc/e-smith/templates/etc/php-fpm.d/www.conf/20zabbix-server new file mode 100644 index 0000000..6b0cc7d --- /dev/null +++ b/root/etc/e-smith/templates/etc/php-fpm.d/www.conf/20zabbix-server @@ -0,0 +1,60 @@ +{ +if ($PHP_VERSION eq '74'){ + if ((${'zabbix-server'}{status} || 'disabled') eq 'enabled'){ + my $id = 'zabbix-server'; + my $openbasedir = '/usr/share/zabbix:/var/cache/zabbix/:/var/run/php-fpm/:/var/lib/php/zabbix/' . + ':/var/lock/zabbix/:/etc/zabbix/:/usr/share/php/:/usr/share/pear/:/opt/remi/php74/root/usr/share/pear/' . + ':/opt/remi/php74/root/usr/share/php/'; + $disablefunctions = 'system, show_source, symlink, exec, dl, shell_exec, passthru, phpinfo, ' . + 'escapeshellarg, escapeshellcmd'; + +$socket = ( -d "/var/lib/mysql/zabbixdb") ? "/var/lib/mysql/mysql.sock" : "/var/lib/mysql/mariadb105.sock"; + + $OUT .=<<_EOF; + +[php$PHP_VERSION-$id] +user = www +group = www +listen.owner = root +listen.group = www +listen.mode = 0660 +listen = /var/run/php-fpm/php$PHP_VERSION-$id.sock +pm = dynamic +pm.max_children = 15 +pm.start_servers = 3 +pm.min_spare_servers = 3 +pm.max_spare_servers = 4 +pm.max_requests = 1000 +request_terminate_timeout = 30 +php_admin_value[session.save_path] = /var/lib/php/zabbix/session +php_admin_value[opcache.file_cache] = /var/lib/php/zabbix/opcache +php_admin_value[upload_tmp_dir] = /var/lib/php/zabbix/tmp +php_admin_value[error_log] = /var/log/php/zabbix/error.log +slowlog = /var/log/php/zabbix/slow.log +php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f php@{ $DomainName } +php_admin_flag[display_errors] = off +php_admin_flag[log_errors] = on +php_admin_value[error_log] = syslog +php_admin_value[memory_limit] = 256M +php_admin_value[max_execution_time] = 600 +php_admin_value[max_input_time] = 600 +php_admin_value[post_max_size] = 32M +php_admin_value[upload_max_filesize] = 16M +php_admin_value[disable_functions] = $disablefunctions +php_admin_value[open_basedir] = $openbasedir +php_admin_flag[allow_url_fopen] = on +php_admin_flag[file_upload] = off +php_admin_flag[session.cookie_httponly] = on +php_admin_flag[allow_url_include] = off +php_admin_value[session.save_handler] = files +php_admin_value[always_populate_raw_post_data] = -1 +php_value[mysqli.default_socket] = $socket +php_value[mysql.default_socket] = $socket + +_EOF + } + else{ + $OUT .= '; Zabbix Server is disabled'; + } +} +} diff --git a/root/etc/e-smith/templates/etc/sudoers/00zabbixAlias b/root/etc/e-smith/templates/etc/sudoers/00zabbixAlias new file mode 100644 index 0000000..049fba3 --- /dev/null +++ b/root/etc/e-smith/templates/etc/sudoers/00zabbixAlias @@ -0,0 +1 @@ +Cmnd_Alias ZABBIX = /usr/sbin/fping,/usr/sbin/fping6 diff --git a/root/etc/e-smith/templates/etc/sudoers/30zabbix b/root/etc/e-smith/templates/etc/sudoers/30zabbix new file mode 100644 index 0000000..717991f --- /dev/null +++ b/root/etc/e-smith/templates/etc/sudoers/30zabbix @@ -0,0 +1 @@ +zabbix ALL=(root) NOPASSWD: ZABBIX diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/00header b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/00header new file mode 100644 index 0000000..a352e59 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/00header @@ -0,0 +1,19 @@ +/* +** ZABBIX +** Copyright (C) 2000-2005 SIA Zabbix +** +** 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; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** 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., 675 Mass Ave, Cambridge, MA 02139, USA. +**/ + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/10database b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/10database new file mode 100644 index 0000000..1bbda93 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/10database @@ -0,0 +1,20 @@ +global $DB; + +$DB["TYPE"] = "MYSQL"; +$DB["SERVER"] = "localhost"; +$DB["PORT"] = "0"; + +{ + +my $dbname = ${'zabbix-server'}{'DbName'} || 'zabbix'; +my $dbuser = ${'zabbix-server'}{'DbUser'} || 'zabbix'; +my $dbpass = ${'zabbix-server'}{'DbPassword'} || 'secret'; + +$OUT .=<<"HERE"; + +\$DB["DATABASE"] = "$dbname"; +\$DB["USER"] = "$dbuser"; +\$DB["PASSWORD"] = "$dbpass"; +HERE +} + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/20zbxserver b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/20zbxserver new file mode 100644 index 0000000..0829a7e --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/20zbxserver @@ -0,0 +1,10 @@ +{ +my $port = ${'zabbix-server'}{'TCPPort'} || '10051'; + +$OUT .=<<"HERE"; + +\$ZBX_SERVER = "localhost"; +\$ZBX_SERVER_PORT = "$port"; +HERE +} + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/30images b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/30images new file mode 100644 index 0000000..701b458 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix.conf.php/30images @@ -0,0 +1 @@ +$IMAGE_FORMAT_DEFAULT = IMAGE_FORMAT_PNG; diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/00header b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/00header new file mode 100644 index 0000000..916507e --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/00header @@ -0,0 +1,4 @@ +# This is config file for ZABBIX server process +# To get more information about ZABBIX, +# go http://www.zabbix.com + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/10NodeID b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/10NodeID new file mode 100644 index 0000000..b7740ed --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/10NodeID @@ -0,0 +1,11 @@ +############ GENERAL PARAMETERS ################# + +#NodeID dropped since Zabbix 2.40 +{ +# This defines unique NodeID in distributed setup, +# Default value 0 (standalone server) +# This parameter must be between 0 and 999 +#my $nodeID = ${'zabbix-server'}{'NodeID'} || '0'; +#$OUT .= "NodeID=$nodeID\n"; +} + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/20instances b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/20instances new file mode 100644 index 0000000..8f0f6ba --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/20instances @@ -0,0 +1,35 @@ +# Number of pre-forked instances of pollers +# Default value is 5 +# This parameter must be between 0 and 255 +StartPollers=5 + +# Number of pre-forked instances of IPMI pollers +# Default value is 0 +# This parameter must be between 0 and 255 +#StartIPMIPollers=0 + +# Number of pre-forked instances of pollers for unreachable hosts +# Default value is 1 +# This parameter must be between 0 and 255 +#StartPollersUnreachable=1 + +# Number of pre-forked instances of trappers +# Default value is 5 +# This parameter must be between 0 and 255 +StartTrappers=5 + +# Number of pre-forked instances of ICMP pingers +# Default value is 1 +# This parameter must be between 0 and 255 +StartPingers=1 + +# Number of pre-forked instances of discoverers +# Default value is 1 +# This parameter must be between 0 and 255 +StartDiscoverers=1 + +# Number of pre-forked instances of HTTP pollers +# Default value is 1 +# This parameter must be between 0 and 255 +#StartHTTPPollers=1 + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/30listen b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/30listen new file mode 100644 index 0000000..d580160 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/30listen @@ -0,0 +1,16 @@ +# Listen port for trapper. Default port number is 10051. This parameter +# must be between 1024 and 32767 +{ +my $port = ${'zabbix-server'}{'TCPPort'} || '10051'; +$OUT .= "ListenPort=$port\n"; +} + +# Source IP address for outgouing connections +#SourceIP= + +# Listen interface for trapper. Trapper will listen all network interfaces +# if this parameter is missing. + +#ListenIP=127.0.0.1 + + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/40freq b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/40freq new file mode 100644 index 0000000..5ae7d8d --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/40freq @@ -0,0 +1,15 @@ +# How often ZABBIX will perform housekeeping procedure +# (in hours) +# Default value is 1 hour +# Housekeeping is removing unnecessary information from +# tables history, alert, and alarms +# This parameter must be between 1 and 24 + +#HousekeepingFrequency=1 + +# Uncomment this line to disable housekeeping procedure +#DisableHousekeeping=1 + +# Frequency of ICMP pings (item keys 'icmpping' and 'icmppingsec'). Defauls is 60 seconds. +#PingerFrequency=60 + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/50debug b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/50debug new file mode 100644 index 0000000..32f45d2 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/50debug @@ -0,0 +1,9 @@ +# Specifies debug level +# 0 - debug is not created +# 1 - critical information +# 2 - error information +# 3 - warnings (default) +# 4 - for debugging (produces lots of information) + +DebugLevel=3 + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/60pidAndLog b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/60pidAndLog new file mode 100644 index 0000000..3c88d14 --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/60pidAndLog @@ -0,0 +1,13 @@ +# Name of PID file + +PidFile=/run/zabbix/zabbix_server.pid + +# Name of log file +# If not set, syslog is used + +LogFile=/var/log/zabbix/zabbix_server.log + +# Maximum size of log file in MB. Set to 0 to disable automatic log rotation. +LogFileSize=10 + + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/70location b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/70location new file mode 100644 index 0000000..5984a7a --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/70location @@ -0,0 +1,17 @@ +# Location for custom alert scripts +AlertScriptsPath=/var/lib/zabbix/bin + +# Location of external scripts +ExternalScripts=/var/lib/zabbix/bin + +# Location of fping. Default is /usr/sbin/fping +# Make sure that fping binary has root permissions and SUID flag set +FpingLocation=/var/lib/zabbix/bin/fping + +# Location of fping6. Default is /usr/sbin/fping6 +# Make sure that fping binary has root permissions and SUID flag set +Fping6Location=/var/lib/zabbix/bin/fping6 + +# Temporary directory. Default is /tmp +TmpDir=/var/lib/zabbix/tmp + diff --git a/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/80database b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/80database new file mode 100644 index 0000000..1e33f3d --- /dev/null +++ b/root/etc/e-smith/templates/etc/zabbix/zabbix_server.conf/80database @@ -0,0 +1,36 @@ +# Database host name +# Default is localhost + +DBHost=localhost + +# Database name +# SQLite3 note: path to database file must be provided. DBUser and DBPassword are ignored. +{ +my $dbname = ${'zabbix-server'}{'DbName'} || 'zabbix'; +my $dbuser = ${'zabbix-server'}{'DbUser'} || 'zabbix'; +my $dbpass = ${'zabbix-server'}{'DbPassword'} || 'secret'; + +$OUT .=<<"HERE"; + +DBName=$dbname + +# Database user + +DBUser=$dbuser + +# Database password +# Comment this line if no password used + +DBPassword=$dbpass +HERE + +} + +# Connect to MySQL using Unix socket? + +#DBSocket=/var/lib/mysql/mysql.sock +DBSocket=/var/lib/mysql/{$OUT = ( -d "/var/lib/mysql/zabbixdb") ? "mysql" : "mariadb105"}.sock + +# Enable database cache module +StartDBSyncers=1 + diff --git a/root/etc/e-smith/templates/usr/share/zabbix/.user.ini/20zabbix-server b/root/etc/e-smith/templates/usr/share/zabbix/.user.ini/20zabbix-server new file mode 100644 index 0000000..fc5f22b --- /dev/null +++ b/root/etc/e-smith/templates/usr/share/zabbix/.user.ini/20zabbix-server @@ -0,0 +1,12 @@ +{ +my $tz = ${'TimeZone'} || 'Europe/Paris'; +$OUT =<<"HERE"; +max_execution_time=600 +max_input_time=600 +memory_limit=256M +date.timezone=$tz +post_max_size=32M +always_populate_raw_post_data=-1 +HERE +} + diff --git a/root/etc/e-smith/templates/var/lib/zabbix/.sendxmpprc/all b/root/etc/e-smith/templates/var/lib/zabbix/.sendxmpprc/all new file mode 100644 index 0000000..c72a3d3 --- /dev/null +++ b/root/etc/e-smith/templates/var/lib/zabbix/.sendxmpprc/all @@ -0,0 +1,3 @@ +# Jabber Account for zabbix alerts +{${'zabbix-server'}{'JabberAccount'}}@{${'zabbix-server'}{'JabberServer'}} {${'zabbix-server'}{'JabberPassword'}} + diff --git a/root/etc/e-smith/templates/var/lib/zabbix/bin/sendxmpp/all b/root/etc/e-smith/templates/var/lib/zabbix/bin/sendxmpp/all new file mode 100644 index 0000000..e5cfbca --- /dev/null +++ b/root/etc/e-smith/templates/var/lib/zabbix/bin/sendxmpp/all @@ -0,0 +1,6 @@ + +echo "$3" | \ + sendxmpp -r zabbix -f /var/lib/zabbix/.sendxmpprc \ + {(${'zabbix-server'}{'JabberTLS'} || 'enabled') eq 'disabled' ? '':'-t \\';} + -s "$2" "$1" + diff --git a/root/usr/lib/systemd/system/zabbix-server.service.d/50koozali.conf b/root/usr/lib/systemd/system/zabbix-server.service.d/50koozali.conf new file mode 100644 index 0000000..87a9404 --- /dev/null +++ b/root/usr/lib/systemd/system/zabbix-server.service.d/50koozali.conf @@ -0,0 +1,3 @@ +[Install] + WantedBy=sme-server.target + diff --git a/root/usr/share/zabbix/.user.ini b/root/usr/share/zabbix/.user.ini new file mode 100644 index 0000000..b0a6b90 --- /dev/null +++ b/root/usr/share/zabbix/.user.ini @@ -0,0 +1,6 @@ +max_execution_time=600 +max_input_time=600 +memory_limit=256M +date.timezone=$tz +post_max_size=32M +always_populate_raw_post_data=-1 diff --git a/root/var/lib/zabbix/bin/cert_expire.pl b/root/var/lib/zabbix/bin/cert_expire.pl new file mode 100644 index 0000000..443ced6 --- /dev/null +++ b/root/var/lib/zabbix/bin/cert_expire.pl @@ -0,0 +1,139 @@ +#!/usr/bin/perl -w +# Check peer certificate validity for Zabbix +# Require perl module : IO::Socket, Net::SSLeay, Date::Parse +# Require unix programs : openssl, echo, sendmail +# +# Based on sslexpire from Emmanuel Lacour +# +# This file 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; either version 2, or (at your option) any +# later version. +# +# This file is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this file; see the file COPYING. If not, write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301, USA. +# + + +use strict; +use IO::Socket; +use Net::SSLeay; +use Getopt::Long; +use Date::Parse; + +Net::SSLeay::SSLeay_add_ssl_algorithms(); +Net::SSLeay::randomize(); + +# Default values +my $opensslpath = "/usr/bin/openssl"; +my $host = '127.0.0.1'; +my $port = '443'; + +my %opts; +GetOptions (\%opts, + 'host|h=s', + 'port|p=s', + 'help', +); + +if ($opts{'host'}) { + $host = $opts{'host'}; +} +if ($opts{'port'}){ + $port = $opts{'port'}; +} + +if ($opts{'help'}) { + &usage; +} + +# Print program usage +sub usage { + print "Usage: sslexpire [OPTION]... +-h, --host=HOST check this host +-p, --port=TCPPORT check this port on the previous host + --help print this help, then exit +"; + exit; +} + +# This will return the expiration date +sub getExpire { + + my ($l_host,$l_port) = @_; + my ($l_expdate,$l_comment); + + # Connect to $l_host:$l_port + my $socket = IO::Socket::INET->new( + Proto => "tcp", + PeerAddr => $l_host, + PeerPort => $l_port + ); + # If we connected successfully + if ($socket) { + # Intiate ssl + my $l_ctx = Net::SSLeay::CTX_new(); + my $l_ssl = Net::SSLeay::new($l_ctx); + + Net::SSLeay::set_fd($l_ssl, fileno($socket)); + my $res = Net::SSLeay::connect($l_ssl); + + # Get peer certificate + my $l_x509 = Net::SSLeay::get_peer_certificate($l_ssl); + if ($l_x509) { + my $l_string = Net::SSLeay::PEM_get_string_X509($l_x509); + # Get the expiration date, using openssl + $l_expdate = `echo "$l_string" | $opensslpath x509 -enddate -noout 2>&1`; + $l_expdate =~ s/.*=//; + chomp($l_expdate); + } + else { + $l_expdate = 1; + } + + # Close and cleanup + Net::SSLeay::free($l_ssl); + Net::SSLeay::CTX_free($l_ctx); + close $socket; + } + else { + $l_expdate = 1; + } + return $l_expdate; +} + + +# Print remaining days before expiration +sub report { + # Convert date into epoch using date command + my ($l_expdate) = @_; + + if ($l_expdate ne "1") { + # The current date + my $l_today = time; + my $l_epochdate = str2time($l_expdate); + + # Calculate diff between expiration date and today + my $l_diff = ($l_epochdate - $l_today)/(3600*24); + + # Report if needed + printf "%.0f\n", $l_diff; + } + else { + print "Unable to read certificate!\n"; + exit (1); + } +} + +# Get expiration date +my $expdate = getExpire($host,$port); + +# Report +report("$expdate"); diff --git a/root/var/lib/zabbix/bin/check_cert.pl b/root/var/lib/zabbix/bin/check_cert.pl new file mode 100644 index 0000000..4592adf --- /dev/null +++ b/root/var/lib/zabbix/bin/check_cert.pl @@ -0,0 +1,109 @@ +#!/usr/bin/perl + +=head1 SYNOPSIS + + check_ssl_certificate.pl + --url,-u URL + --sni,-s HOSTNAME SNI servername (SSL vhost) that will be requested during SSL handshake. + This tells the server which certificate to return. + Default to the host passed with --url + +=cut + +use strict; +use warnings; +use IO::Socket::SSL; +use LWP::UserAgent; +use URI::URL; +use DateTime::Format::ISO8601; +use Getopt::Long qw/:config auto_help/; +use Pod::Usage; +use JSON qw(to_json); + +use constant TIMEOUT => 10; + +my ($url, $sni, $status, @san); + +sub ssl_opts { + my ($sni, $expiration_date_ref, $status_ref, $san_ref) = @_; + return ( + 'verify_hostname' => 0, + 'SSL_ca_file' => '/etc/pki/tls/certs/ca-bundle.crt', + 'SSL_hostname' => $sni, + 'SSL_verifycn_name' => $sni, + 'SSL_verify_scheme' => 'http', + 'SSL_verify_callback' => sub { + my (undef, $ctx_store) = @_; + # Get the error message from openssl verification + $$status_ref = Net::SSLeay::X509_verify_cert_error_string(Net::SSLeay::X509_STORE_CTX_get_error($ctx_store)); + # Get the raw cert, to extract the expiration + my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($ctx_store); + $$expiration_date_ref = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notAfter($cert)); + # Get Alt names so we can check later if the hostname match + @$san_ref = Net::SSLeay::X509_get_subjectAltNames($cert); + # Keep only odd elements. Even ones contains subject types which we're not interested in + @$san_ref = @$san_ref[grep $_ % 2, 0..scalar(@$san_ref)]; + # Always return success + return 1; + } + ) +} + +sub https_get { + my ($url, $sni, $expiration_date_ref, $status_ref, $san_ref) = @_; + + my $ua = LWP::UserAgent->new(); + $ua->timeout(TIMEOUT); + $ua->ssl_opts( ssl_opts($sni, $expiration_date_ref, $status_ref, $san_ref) ); + my $request = HTTP::Request->new('GET', $url); + $request->header(Host => $sni); + my $response = $ua->simple_request($request); + return $response; +} + +sub wildcard_match { + my ($cn, $host) = @_; + my $match = 0; + return 0 if $cn !~ m/^\*\.(.*)$/; + my $cn_dom = $1; + my $host_dom = ($sni =~ m/^[^\.]+\.(.*)$/)[0]; + return ($cn_dom eq $host_dom); +} + +GetOptions ("url|u=s" => \$url, + "sni|s=s" => \$sni) or pod2usage(1); +if (@ARGV) { + print "This script takes no arguments...\n"; + pod2usage(1); +} +pod2usage(1) if (!$url); + +my $expiration_date; +my $uri = URI->new($url); +die "Only https urls are supported\n" unless $uri->scheme eq 'https'; +$sni ||= $uri->host; +my $response = https_get($url, $sni, \$expiration_date, \$status, \@san); + +my $out = { + code => $response->code, + status => $response->message, + days_left => undef, + cert_cn => undef, + issuer => undef +}; + +if ($response->code != 500) { # Even a 404 is good enough, as far as cert validation goes... + my $now = DateTime->now; + $expiration_date = DateTime::Format::ISO8601->parse_datetime( $expiration_date ); + + $out->{issuer} = $response->headers->{'client-ssl-cert-issuer'}; + $out->{cert_cn} = ($response->headers->{'client-ssl-cert-subject'} =~ m/CN=(.*)$/)[0]; + $status = "no common name" if !$out->{cert_cn}; + $out->{status} = ($status eq 'ok' and !grep { $sni eq $_ } @san and !wildcard_match($out->{cert_cn},$sni)) ? + $out->{status} = "hostname mismatch ($sni doesn't match any of " . join(" ", @san) . ")" : + $status; + $out->{days_left} = ($expiration_date < $now) ? -1 * $expiration_date->delta_days($now)->delta_days : + $expiration_date->delta_days($now)->delta_days +} + +print to_json($out, { pretty => 1 }); diff --git a/root/var/lib/zabbix/bin/fping b/root/var/lib/zabbix/bin/fping new file mode 100644 index 0000000..2173537 --- /dev/null +++ b/root/var/lib/zabbix/bin/fping @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/sudo /usr/sbin/fping "$@" diff --git a/root/var/lib/zabbix/bin/fping6 b/root/var/lib/zabbix/bin/fping6 new file mode 100644 index 0000000..38b1d6d --- /dev/null +++ b/root/var/lib/zabbix/bin/fping6 @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/sudo /usr/sbin/fping6 "$@" diff --git a/root/var/lib/zabbix/bin/zbxtg.py b/root/var/lib/zabbix/bin/zbxtg.py new file mode 100644 index 0000000..cd5626c --- /dev/null +++ b/root/var/lib/zabbix/bin/zbxtg.py @@ -0,0 +1,939 @@ +#!/usr/bin/env python +# coding: utf-8 + +import sys +import os +import time +import random +import string +import requests +import json +import re +import stat +import hashlib +import subprocess +#import sqlite3 +from os.path import dirname +import zbxtg_settings + + +class Cache: + def __init__(self, database): + self.database = database + + def create_db(self, database): + pass + + +class TelegramAPI: + tg_url_bot_general = "https://api.telegram.org/bot" + + def http_get(self, url): + answer = requests.get(url, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def __init__(self, key): + self.debug = False + self.key = key + self.proxies = {} + self.type = "private" # 'private' for private chats or 'group' for group chats + self.markdown = False + self.html = False + self.disable_web_page_preview = False + self.disable_notification = False + self.reply_to_message_id = 0 + self.tmp_dir = None + self.tmp_uids = None + self.location = {"latitude": None, "longitude": None} + self.update_offset = 0 + self.image_buttons = False + self.result = None + self.ok = None + self.error = None + self.get_updates_from_file = False + + def get_me(self): + url = self.tg_url_bot_general + self.key + "/getMe" + me = self.http_get(url) + return me + + def get_updates(self): + url = self.tg_url_bot_general + self.key + "/getUpdates" + params = {"offset": self.update_offset} + if self.debug: + print_message(url) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + if self.get_updates_from_file: + print_message("Getting updated from file getUpdates.txt") + self.result = json.loads("".join(file_read("getUpdates.txt"))) + if self.debug: + print_message("Content of /getUpdates:") + print_message(json.dumps(self.result)) + self.ok_update() + return self.result + + def send_message(self, to, message): + url = self.tg_url_bot_general + self.key + "/sendMessage" + message = "\n".join(message) + params = {"chat_id": to, "text": message, "disable_web_page_preview": self.disable_web_page_preview, + "disable_notification": self.disable_notification} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + if self.markdown or self.html: + parse_mode = "HTML" + if self.markdown: + parse_mode = "Markdown" + params["parse_mode"] = parse_mode + if self.debug: + print_message("Trying to /sendMessage:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + if answer.status_code == 414: + self.result = {"ok": False, "description": "414 URI Too Long"} + else: + self.result = answer.json() + self.ok_update() + return self.result + + def update_message(self, to, message_id, message): + url = self.tg_url_bot_general + self.key + "/editMessageText" + message = "\n".join(message) + params = {"chat_id": to, "message_id": message_id, "text": message, + "disable_web_page_preview": self.disable_web_page_preview, + "disable_notification": self.disable_notification} + if self.markdown or self.html: + parse_mode = "HTML" + if self.markdown: + parse_mode = "Markdown" + params["parse_mode"] = parse_mode + if self.debug: + print_message("Trying to /editMessageText:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def send_photo(self, to, message, path): + url = self.tg_url_bot_general + self.key + "/sendPhoto" + message = "\n".join(message) + if self.image_buttons: + reply_markup = json.dumps({"inline_keyboard": [[ + {"text": "R", "callback_data": "graph_refresh"}, + {"text": "1h", "callback_data": "graph_period_3600"}, + {"text": "3h", "callback_data": "graph_period_10800"}, + {"text": "6h", "callback_data": "graph_period_21600"}, + {"text": "12h", "callback_data": "graph_period_43200"}, + {"text": "24h", "callback_data": "graph_period_86400"}, + ], ]}) + else: + reply_markup = json.dumps({}) + params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification, + "reply_markup": reply_markup} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + files = {"photo": open(path, 'rb')} + if self.debug: + print_message("Trying to /sendPhoto:") + print_message(url) + print_message(params) + print_message("files: " + str(files)) + answer = requests.post(url, params=params, files=files, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def send_txt(self, to, text, text_name=None): + path = self.tmp_dir + "/" + "zbxtg_txt_" + url = self.tg_url_bot_general + self.key + "/sendDocument" + text = "\n".join(text) + if not text_name: + path += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10)) + else: + path += text_name + path += ".txt" + file_write(path, text) + params = {"chat_id": to, "caption": path.split("/")[-1], "disable_notification": self.disable_notification} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + files = {"document": open(path, 'rb')} + if self.debug: + print_message("Trying to /sendDocument:") + print_message(url) + print_message(params) + print_message("files: " + str(files)) + answer = requests.post(url, params=params, files=files, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def get_uid(self, name): + uid = 0 + if self.debug: + print_message("Getting uid from /getUpdates...") + updates = self.get_updates() + for m in updates["result"]: + if "message" in m: + chat = m["message"]["chat"] + elif "edited_message" in m: + chat = m["edited_message"]["chat"] + else: + continue + if chat["type"] == self.type == "private": + if "username" in chat: + if chat["username"] == name: + uid = chat["id"] + if (chat["type"] == "group" or chat["type"] == "supergroup") and self.type == "group": + if "title" in chat: + if sys.version_info[0] < 3: + if chat["title"] == name.decode("utf-8"): + uid = chat["id"] + else: + if chat["title"] == name: + uid = chat["id"] + return uid + + def error_need_to_contact(self, to): + if self.type == "private": + print_message("User '{0}' needs to send some text bot in private".format(to)) + if self.type == "group": + print_message("You need start a conversation with your bot first in '{0}' group chat, type '/start@{1}'" + .format(to, self.get_me()["result"]["username"])) + + def update_cache_uid(self, name, uid, message="Add new string to cache file"): + cache_string = "{0};{1};{2}\n".format(name, self.type, str(uid).rstrip()) + # FIXME + if self.debug: + print_message("{0}: {1}".format(message, cache_string)) + with open(self.tmp_uids, "a") as cache_file_uids: + cache_file_uids.write(cache_string) + return True + + def get_uid_from_cache(self, name): + if self.debug: + print_message("Trying to read cached uid for {0}, {1}, from {2}".format(name, self.type, self.tmp_uids)) + uid = 0 + if os.path.isfile(self.tmp_uids): + with open(self.tmp_uids, 'r') as cache_file_uids: + cache_uids_old = cache_file_uids.readlines() + for u in cache_uids_old: + u_splitted = u.split(";") + if name == u_splitted[0] and self.type == u_splitted[1]: + uid = u_splitted[2] + return uid + + def send_location(self, to, coordinates): + url = self.tg_url_bot_general + self.key + "/sendLocation" + params = {"chat_id": to, "disable_notification": self.disable_notification, + "latitude": coordinates["latitude"], "longitude": coordinates["longitude"]} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + if self.debug: + print_message("Trying to /sendLocation:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def answer_callback_query(self, callback_query_id, text=None): + url = self.tg_url_bot_general + self.key + "/answerCallbackQuery" + if not text: + params = {"callback_query_id": callback_query_id} + else: + params = {"callback_query_id": callback_query_id, "text": text} + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def ok_update(self): + self.ok = self.result["ok"] + if self.ok: + self.error = None + else: + self.error = self.result["description"] + print_message(self.error) + return True + + +def markdown_fix(message, offset, emoji=False): + offset = int(offset) + if emoji: # https://github.com/ableev/Zabbix-in-Telegram/issues/152 + offset -= 2 + message = "\n".join(message) + message = message[:offset] + message[offset+1:] + message = message.split("\n") + return message + + +class ZabbixWeb: + def __init__(self, server, username, password): + self.debug = False + self.server = server + self.username = username + self.password = password + self.proxies = {} + self.verify = True + self.cookie = None + self.basic_auth_user = None + self.basic_auth_pass = None + self.tmp_dir = None + + def login(self): + if not self.verify: + requests.packages.urllib3.disable_warnings() + + data_api = {"name": self.username, "password": self.password, "enter": "Sign in"} + answer = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify, + auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) + cookie = answer.cookies + if len(answer.history) > 1 and answer.history[0].status_code == 302: + print_message("probably the server in your config file has not full URL (for example " + "'{0}' instead of '{1}')".format(self.server, self.server + "/zabbix")) + if not cookie: + print_message("authorization has failed, url: {0}".format(self.server + "/")) + cookie = None + + self.cookie = cookie + + def graph_get(self, itemid, period, title, width, height, version=3): + file_img = self.tmp_dir + "/{0}.png".format("".join(itemid)) + + title = requests.utils.quote(title) + + colors = { + 0: "00CC00", + 1: "CC0000", + 2: "0000CC", + 3: "CCCC00", + 4: "00CCCC", + 5: "CC00CC", + } + + drawtype = 5 + if len(itemid) > 1: + drawtype = 2 + + zbx_img_url_itemids = [] + for i in range(0, len(itemid)): + itemid_url = "&items[{0}][itemid]={1}&items[{0}][sortorder]={0}&" \ + "items[{0}][drawtype]={3}&items[{0}][color]={2}".format(i, itemid[i], colors[i], drawtype) + zbx_img_url_itemids.append(itemid_url) + + zbx_img_url = self.server + "/chart3.php?" + if version < 4: + zbx_img_url += "period={0}".format(period) + else: + zbx_img_url += "from=now-{0}&to=now".format(period) + zbx_img_url += "&name={0}&width={1}&height={2}&graphtype=0&legend=1".format(title, width, height) + zbx_img_url += "".join(zbx_img_url_itemids) + + if self.debug: + print_message(zbx_img_url) + answer = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify, + auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) + status_code = answer.status_code + if status_code == 404: + print_message("can't get image from '{0}'".format(zbx_img_url)) + return False + res_img = answer.content + file_bwrite(file_img, res_img) + return file_img + + def api_test(self): + headers = {'Content-type': 'application/json'} + api_data = json.dumps({"jsonrpc": "2.0", "method": "user.login", "params": + {"user": self.username, "password": self.password}, "id": 1}) + api_url = self.server + "/api_jsonrpc.php" + api = requests.post(api_url, data=api_data, proxies=self.proxies, headers=headers) + return api.text + + +def print_message(message): + message = str(message) + "\n" + filename = sys.argv[0].split("/")[-1] + sys.stderr.write(filename + ": " + message) + + +def list_cut(elements, symbols_limit): + symbols_count = symbols_count_now = 0 + elements_new = [] + element_last_list = [] + for e in elements: + symbols_count_now = symbols_count + len(e) + if symbols_count_now > symbols_limit: + limit_idx = symbols_limit - symbols_count + e_list = list(e) + for idx, ee in enumerate(e_list): + if idx < limit_idx: + element_last_list.append(ee) + else: + break + break + else: + symbols_count = symbols_count_now + 1 + elements_new.append(e) + if symbols_count_now < symbols_limit: + return elements, False + else: + element_last = "".join(element_last_list) + elements_new.append(element_last) + return elements_new, True + + +class Maps: + # https://developers.google.com/maps/documentation/geocoding/intro + def __init__(self): + self.key = None + self.proxies = {} + + def get_coordinates_by_address(self, address): + coordinates = {"latitude": 0, "longitude": 0} + url_api = "https://maps.googleapis.com/maps/api/geocode/json?key={0}&address={1}".format(self.key, address) + url = url_api + answer = requests.get(url, proxies=self.proxies) + result = answer.json() + try: + coordinates_dict = result["results"][0]["geometry"]["location"] + except: + if "error_message" in result: + print_message("[" + result["status"] + "]: " + result["error_message"]) + return coordinates + coordinates = {"latitude": coordinates_dict["lat"], "longitude": coordinates_dict["lng"]} + return coordinates + + +def file_write(filename, text): + with open(filename, "w") as fd: + fd.write(str(text)) + return True + + +def file_bwrite(filename, data): + with open(filename, "wb") as fd: + fd.write(data) + return True + + +def file_read(filename): + with open(filename, "r") as fd: + text = fd.readlines() + return text + + +def file_append(filename, text): + with open(filename, "a") as fd: + fd.write(str(text)) + return True + + +def external_image_get(url, tmp_dir, timeout=6): + image_hash = hashlib.md5() + image_hash.update(url.encode()) + file_img = tmp_dir + "/external_{0}.png".format(image_hash.hexdigest()) + try: + answer = requests.get(url, timeout=timeout, allow_redirects=True) + except requests.exceptions.ReadTimeout as ex: + print_message("Can't get external image from '{0}': timeout".format(url)) + return False + status_code = answer.status_code + if status_code == 404: + print_message("Can't get external image from '{0}': HTTP 404 error".format(url)) + return False + answer_image = answer.content + file_bwrite(file_img, answer_image) + return file_img + + +def age2sec(age_str): + age_sec = 0 + age_regex = "([0-9]+d)?\s?([0-9]+h)?\s?([0-9]+m)?" + age_pattern = re.compile(age_regex) + intervals = age_pattern.match(age_str).groups() + for i in intervals: + if i: + metric = i[-1] + if metric == "d": + age_sec += int(i[0:-1])*86400 + if metric == "h": + age_sec += int(i[0:-1])*3600 + if metric == "m": + age_sec += int(i[0:-1])*60 + return age_sec + + +def main(): + + tmp_dir = zbxtg_settings.zbx_tg_tmp_dir + if tmp_dir == "/tmp/" + zbxtg_settings.zbx_tg_prefix: + print_message("WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!") + print_message("https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings") + + tmp_cookie = tmp_dir + "/cookie.py.txt" + tmp_uids = tmp_dir + "/uids.txt" + tmp_need_update = False # do we need to update cache file with uids or not + + rnd = random.randint(0, 999) + ts = time.time() + hash_ts = str(ts) + "." + str(rnd) + + log_file = "/dev/null" + + args = sys.argv + + settings = { + "zbxtg_itemid": "0", # itemid for graph + "zbxtg_title": None, # title for graph + "zbxtg_image_period": None, + "zbxtg_image_age": "3600", + "zbxtg_image_width": "900", + "zbxtg_image_height": "200", + "tg_method_image": False, # if True - default send images, False - send text + "tg_chat": False, # send message to chat or in private + "tg_group": False, # send message to chat or in private + "is_debug": False, + "is_channel": False, + "disable_web_page_preview": False, + "location": None, # address + "lat": 0, # latitude + "lon": 0, # longitude + "is_single_message": False, + "markdown": False, + "html": False, + "signature": None, + "signature_disable": False, + "graph_buttons": False, + "extimg": None, + "to": None, + "to_group": None, + "forked": False, + } + + url_github = "https://github.com/ableev/Zabbix-in-Telegram" + url_wiki_base = "https://github.com/ableev/Zabbix-in-Telegram/wiki" + url_tg_group = "https://t.me/ZbxTg" + url_tg_channel = "https://t.me/Zabbix_in_Telegram" + + settings_description = { + "itemid": {"name": "zbxtg_itemid", "type": "list", + "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs"}, + "title": {"name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs"}, + "graphs_period": {"name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs"}, + "graphs_age": {"name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs"}, + "graphs_width": {"name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs"}, + "graphs_height": {"name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs"}, + "graphs": {"name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs"}, + "chat": {"name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'", + "url": "How-to-send-message-to-the-group-chat"}, + "group": {"name": "tg_group", "type": "bool", "help": "sends message to a group", + "url": "How-to-send-message-to-the-group-chat"}, + "debug": {"name": "is_debug", "type": "bool", "help": "enables 'debug'", + "url": "How-to-test-script-in-command-line"}, + "channel": {"name": "is_channel", "type": "bool", "help": "sends message to a channel", + "url": "Channel-support"}, + "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool", + "help": "disable web page preview", "url": "Disable-web-page-preview"}, + "location": {"name": "location", "type": "str", "help": "address of location", "url": "Location"}, + "lat": {"name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location"}, + "lon": {"name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location"}, + "single_message": {"name": "is_single_message", "type": "bool", "help": "do not split message and graph", + "url": "Why-am-I-getting-two-messages-instead-of-one"}, + "markdown": {"name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, + "html": {"name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, + "signature": {"name": "signature", "type": "str", + "help": "bot's signature", "url": "Bot-signature"}, + "signature_disable": {"name": "signature_disable", "type": "bool", + "help": "enables/disables bot's signature", "url": "Bot-signature"}, + "graph_buttons": {"name": "graph_buttons", "type": "bool", + "help": "activates buttons under graph, could be using in ZbxTgDaemon", + "url": "Interactive-bot"}, + "external_image": {"name": "extimg", "type": "str", + "help": "should be url; attaches external image from different source", + "url": "External-image-as-graph"}, + "to": {"name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", + "url": "Custom-to-and-to_group"}, + "to_group": {"name": "to_group", "type": "str", + "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group"}, + "forked": {"name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": ""}, + } + + if len(args) < 4: + do_not_exit = False + if "--features" in args: + print(("List of available settings, see {0}/Settings\n---".format(url_wiki_base))) + for sett, proprt in list(settings_description.items()): + print(("{0}: {1}\ndoc: {2}/{3}\n--".format(sett, proprt["help"], url_wiki_base, proprt["url"]))) + + elif "--show-settings" in args: + do_not_exit = True + print_message("Settings: " + str(json.dumps(settings, indent=2))) + + else: + print(("Hi. You should provide at least three arguments.\n" + "zbxtg.py [TO] [SUBJECT] [BODY]\n\n" + "1. Read main page and/or wiki: {0} + {1}\n" + "2. Public Telegram group (discussion): {2}\n" + "3. Public Telegram channel: {3}\n" + "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev" + .format(url_github, url_wiki_base, url_tg_group, url_tg_channel))) + if not do_not_exit: + sys.exit(0) + + + zbx_to = args[1] + zbx_subject = args[2] + zbx_body = args[3] + + tg = TelegramAPI(key=zbxtg_settings.tg_key) + + tg.tmp_dir = tmp_dir + tg.tmp_uids = tmp_uids + + if zbxtg_settings.proxy_to_tg: + proxy_to_tg = zbxtg_settings.proxy_to_tg + if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"): + proxy_to_tg = "https://" + proxy_to_tg + tg.proxies = { + "https": "{0}".format(proxy_to_tg), + } + + zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user, + password=zbxtg_settings.zbx_api_pass) + + zbx.tmp_dir = tmp_dir + + # workaround for Zabbix 4.x + zbx_version = 3 + + try: + zbx_version = zbxtg_settings.zbx_server_version + except: + pass + + if zbxtg_settings.proxy_to_zbx: + zbx.proxies = { + "http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx), + "https": "https://{0}/".format(zbxtg_settings.proxy_to_zbx) + } + + # https://github.com/ableev/Zabbix-in-Telegram/issues/55 + try: + if zbxtg_settings.zbx_basic_auth: + zbx.basic_auth_user = zbxtg_settings.zbx_basic_auth_user + zbx.basic_auth_pass = zbxtg_settings.zbx_basic_auth_pass + except: + pass + + try: + zbx_api_verify = zbxtg_settings.zbx_api_verify + zbx.verify = zbx_api_verify + except: + pass + + map = Maps() + # api key to resolve address to coordinates via google api + try: + if zbxtg_settings.google_maps_api_key: + map.key = zbxtg_settings.google_maps_api_key + if zbxtg_settings.proxy_to_tg: + map.proxies = { + "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg), + "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg) + } + except: + pass + + zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines() + zbxtg_body_text = [] + + for line in zbxtg_body: + if line.find(zbxtg_settings.zbx_tg_prefix) > -1: + setting = re.split("[\s:=]+", line, maxsplit=1) + key = setting[0].replace(zbxtg_settings.zbx_tg_prefix + ";", "") + if key not in settings_description: + if "--debug" in args: + print_message("[ERROR] There is no '{0}' method, use --features to get help".format(key)) + continue + if settings_description[key]["type"] == "list": + value = setting[1].split(",") + elif len(setting) > 1 and len(setting[1]) > 0: + value = setting[1] + elif settings_description[key]["type"] == "bool": + value = True + else: + value = settings[settings_description[key]["name"]] + if key in settings_description: + settings[settings_description[key]["name"]] = value + else: + zbxtg_body_text.append(line) + + tg_method_image = bool(settings["tg_method_image"]) + tg_chat = bool(settings["tg_chat"]) + tg_group = bool(settings["tg_group"]) + is_debug = bool(settings["is_debug"]) + is_channel = bool(settings["is_channel"]) + disable_web_page_preview = bool(settings["disable_web_page_preview"]) + is_single_message = bool(settings["is_single_message"]) + + # experimental way to send message to the group https://github.com/ableev/Zabbix-in-Telegram/issues/15 + if args[0].split("/")[-1] == "zbxtg_group.py" or "--group" in args or tg_chat or tg_group: + tg_chat = True + tg_group = True + tg.type = "group" + + if "--debug" in args or is_debug: + is_debug = True + tg.debug = True + zbx.debug = True + print_message(tg.get_me()) + print_message("Cache file with uids: " + tg.tmp_uids) + log_file = tmp_dir + ".debug." + hash_ts + ".log" + #print_message(log_file) + + if "--markdown" in args or settings["markdown"]: + tg.markdown = True + + if "--html" in args or settings["html"]: + tg.html = True + + if "--channel" in args or is_channel: + tg.type = "channel" + + if "--disable_web_page_preview" in args or disable_web_page_preview: + if is_debug: + print_message("'disable_web_page_preview' option has been enabled") + tg.disable_web_page_preview = True + + if "--graph_buttons" in args or settings["graph_buttons"]: + tg.image_buttons = True + + if "--forked" in args: + settings["forked"] = True + + if "--tg-key" in args: + tg.key = args[args.index("--tg-key") + 1] + + location_coordinates = {"latitude": None, "longitude": None} + if settings["lat"] > 0 and settings["lat"] > 0: + location_coordinates = {"latitude": settings["lat"], "longitude": settings["lon"]} + tg.location = location_coordinates + else: + if settings["location"]: + location_coordinates = map.get_coordinates_by_address(settings["location"]) + if location_coordinates: + settings["lat"] = location_coordinates["latitude"] + settings["lon"] = location_coordinates["longitude"] + tg.location = location_coordinates + + if not os.path.isdir(tmp_dir): + if is_debug: + print_message("Tmp dir doesn't exist, creating new one...") + try: + os.makedirs(tmp_dir) + open(tg.tmp_uids, "a").close() + os.chmod(tmp_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + os.chmod(tg.tmp_uids, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + except: + tmp_dir = "/tmp" + if is_debug: + print_message("Using {0} as a temporary dir".format(tmp_dir)) + + done_all_work_in_the_fork = False + # issue75 + + to_types = ["to", "to_group", "to_channel"] + to_types_to_telegram = {"to": "private", "to_group": "group", "to_channel": "channel"} + multiple_to = {} + for i in to_types: + multiple_to[i]=[] + + for t in to_types: + try: + if settings[t] and not settings["forked"]: + # zbx_to = settings["to"] + multiple_to[t] = re.split(",", settings[t]) + except KeyError: + pass + + # example: + # {'to_channel': [], 'to': ['usr1', 'usr2', 'usr3'], 'to_group': []} + + if (sum([len(v) for k, v in list(multiple_to.items())])) == 1: + # if we have only one recipient, we don't need fork to send message, just re-write "to" vaiable + tmp_max = 0 + for t in to_types: + if len(multiple_to[t]) > tmp_max: + tmp_max = len(multiple_to[t]) + tg.type = to_types_to_telegram[t] + zbx_to = multiple_to[t][0] + else: + for t in to_types: + for i in multiple_to[t]: + args_new = list(args) + args_new[1] = i + if t == "to_group": + args_new.append("--group") + args_new.append("--forked") + args_new.insert(0, sys.executable) + if is_debug: + print_message("Fork for custom recipient ({1}), new args: {0}".format(args_new, + to_types_to_telegram[t])) + subprocess.call(args_new) + done_all_work_in_the_fork = True + + if done_all_work_in_the_fork: + sys.exit(0) + + uid = None + + if tg.type == "channel": + uid = zbx_to + if tg.type == "private": + zbx_to = zbx_to.replace("@", "") + + if zbx_to.isdigit(): + uid = zbx_to + + if not uid: + uid = tg.get_uid_from_cache(zbx_to) + + if not uid: + uid = tg.get_uid(zbx_to) + if uid: + tmp_need_update = True + if not uid: + tg.error_need_to_contact(zbx_to) + sys.exit(1) + + if tmp_need_update: + tg.update_cache_uid(zbx_to, str(uid).rstrip()) + + if is_debug: + print_message("Telegram uid of {0} '{1}': {2}".format(tg.type, zbx_to, uid)) + + # add signature, turned off by default, you can turn it on in config + try: + if "--signature" in args or settings["signature"] or zbxtg_settings.zbx_tg_signature\ + and not "--signature_disable" in args and not settings["signature_disable"]: + if "--signature" in args: + settings["signature"] = args[args.index("--signature") + 1] + if not settings["signature"]: + settings["signature"] = zbxtg_settings.zbx_server + zbxtg_body_text.append("--") + zbxtg_body_text.append(settings["signature"]) + except: + pass + + # replace text with emojis + internal_using_emoji = False # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152 + if hasattr(zbxtg_settings, "emoji_map"): + zbxtg_body_text_emoji_support = [] + for l in zbxtg_body_text: + l_new = l + for k, v in list(zbxtg_settings.emoji_map.items()): + l_new = l_new.replace("{{" + k + "}}", v) + zbxtg_body_text_emoji_support.append(l_new) + if len("".join(zbxtg_body_text)) - len("".join(zbxtg_body_text_emoji_support)): + internal_using_emoji = True + zbxtg_body_text = zbxtg_body_text_emoji_support + + if not is_single_message: + tg.send_message(uid, zbxtg_body_text) + if not tg.ok: + # first case – if group has been migrated to a supergroup, we need to update chat_id of that group + if tg.error.find("migrated") > -1 and tg.error.find("supergroup") > -1: + migrate_to_chat_id = tg.result["parameters"]["migrate_to_chat_id"] + tg.update_cache_uid(zbx_to, migrate_to_chat_id, message="Group chat is migrated to supergroup, " + "updating cache file") + uid = migrate_to_chat_id + tg.send_message(uid, zbxtg_body_text) + + # another case if markdown is enabled and we got parse error, try to remove "bad" symbols from message + if tg.markdown and tg.error.find("Can't find end of the entity starting at byte offset") > -1: + markdown_warning = "Original message has been fixed due to {0}. " \ + "Please, fix the markdown, it's slowing down messages sending."\ + .format(url_wiki_base + "/" + settings_description["markdown"]["url"]) + markdown_fix_attempts = 0 + while not tg.ok and markdown_fix_attempts != 3: + offset = re.search("Can't find end of the entity starting at byte offset ([0-9]+)", tg.error).group(1) + zbxtg_body_text = markdown_fix(zbxtg_body_text, offset, emoji=internal_using_emoji) + \ + ["\n"] + [markdown_warning] + tg.disable_web_page_preview = True + tg.send_message(uid, zbxtg_body_text) + markdown_fix_attempts += 1 + if tg.ok: + print_message(markdown_warning) + + if is_debug: + print((tg.result)) + + if settings["zbxtg_image_age"]: + age_sec = age2sec(settings["zbxtg_image_age"]) + if age_sec > 0 and age_sec > 3600: + settings["zbxtg_image_period"] = age_sec + + message_id = 0 + if tg_method_image: + zbx.login() + if not zbx.cookie: + text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\ + "unable to send graphs check manually" + tg.send_message(uid, [text_warn]) + print_message(text_warn) + else: + if not settings["extimg"]: + zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"], + settings["zbxtg_title"], settings["zbxtg_image_width"], + settings["zbxtg_image_height"], version=zbx_version) + else: + zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir) + zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200) + if tg.ok: + message_id = tg.result["result"]["message_id"] + tg.reply_to_message_id = message_id + if not zbxtg_file_img: + text_warn = "Can't get graph image, check script manually, see logs, or disable graphs" + tg.send_message(uid, [text_warn]) + print_message(text_warn) + else: + if not is_single_message: + zbxtg_body_text = "" + else: + if is_modified: + text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\ + "the message has been cut to 200 symbols, "\ + "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\ + "#issuecomment-166895044" + print_message(text_warn) + if not is_single_message: + tg.disable_notification = True + tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img) + if tg.ok: + settings["zbxtg_body_text"] = zbxtg_body_text + os.remove(zbxtg_file_img) + else: + if tg.error.find("PHOTO_INVALID_DIMENSIONS") > -1: + if not tg.disable_web_page_preview: + tg.disable_web_page_preview = True + text_warn = "Zabbix user couldn't get graph (probably has no rights to get data from host), " \ + "check script manually, see {0}".format(url_wiki_base + "/" + + settings_description["graphs"]["url"]) + tg.send_message(uid, [text_warn]) + print_message(text_warn) + if tg.location and location_coordinates["latitude"] and location_coordinates["longitude"]: + tg.reply_to_message_id = message_id + tg.disable_notification = True + tg.send_location(to=uid, coordinates=location_coordinates) + + if "--show-settings" in args: + print_message("Settings: " + str(json.dumps(settings, indent=2))) + +if __name__ == "__main__": + main() diff --git a/root/var/lib/zabbix/bin/zbxtg_group.py b/root/var/lib/zabbix/bin/zbxtg_group.py new file mode 100644 index 0000000..cd5626c --- /dev/null +++ b/root/var/lib/zabbix/bin/zbxtg_group.py @@ -0,0 +1,939 @@ +#!/usr/bin/env python +# coding: utf-8 + +import sys +import os +import time +import random +import string +import requests +import json +import re +import stat +import hashlib +import subprocess +#import sqlite3 +from os.path import dirname +import zbxtg_settings + + +class Cache: + def __init__(self, database): + self.database = database + + def create_db(self, database): + pass + + +class TelegramAPI: + tg_url_bot_general = "https://api.telegram.org/bot" + + def http_get(self, url): + answer = requests.get(url, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def __init__(self, key): + self.debug = False + self.key = key + self.proxies = {} + self.type = "private" # 'private' for private chats or 'group' for group chats + self.markdown = False + self.html = False + self.disable_web_page_preview = False + self.disable_notification = False + self.reply_to_message_id = 0 + self.tmp_dir = None + self.tmp_uids = None + self.location = {"latitude": None, "longitude": None} + self.update_offset = 0 + self.image_buttons = False + self.result = None + self.ok = None + self.error = None + self.get_updates_from_file = False + + def get_me(self): + url = self.tg_url_bot_general + self.key + "/getMe" + me = self.http_get(url) + return me + + def get_updates(self): + url = self.tg_url_bot_general + self.key + "/getUpdates" + params = {"offset": self.update_offset} + if self.debug: + print_message(url) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + if self.get_updates_from_file: + print_message("Getting updated from file getUpdates.txt") + self.result = json.loads("".join(file_read("getUpdates.txt"))) + if self.debug: + print_message("Content of /getUpdates:") + print_message(json.dumps(self.result)) + self.ok_update() + return self.result + + def send_message(self, to, message): + url = self.tg_url_bot_general + self.key + "/sendMessage" + message = "\n".join(message) + params = {"chat_id": to, "text": message, "disable_web_page_preview": self.disable_web_page_preview, + "disable_notification": self.disable_notification} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + if self.markdown or self.html: + parse_mode = "HTML" + if self.markdown: + parse_mode = "Markdown" + params["parse_mode"] = parse_mode + if self.debug: + print_message("Trying to /sendMessage:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + if answer.status_code == 414: + self.result = {"ok": False, "description": "414 URI Too Long"} + else: + self.result = answer.json() + self.ok_update() + return self.result + + def update_message(self, to, message_id, message): + url = self.tg_url_bot_general + self.key + "/editMessageText" + message = "\n".join(message) + params = {"chat_id": to, "message_id": message_id, "text": message, + "disable_web_page_preview": self.disable_web_page_preview, + "disable_notification": self.disable_notification} + if self.markdown or self.html: + parse_mode = "HTML" + if self.markdown: + parse_mode = "Markdown" + params["parse_mode"] = parse_mode + if self.debug: + print_message("Trying to /editMessageText:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def send_photo(self, to, message, path): + url = self.tg_url_bot_general + self.key + "/sendPhoto" + message = "\n".join(message) + if self.image_buttons: + reply_markup = json.dumps({"inline_keyboard": [[ + {"text": "R", "callback_data": "graph_refresh"}, + {"text": "1h", "callback_data": "graph_period_3600"}, + {"text": "3h", "callback_data": "graph_period_10800"}, + {"text": "6h", "callback_data": "graph_period_21600"}, + {"text": "12h", "callback_data": "graph_period_43200"}, + {"text": "24h", "callback_data": "graph_period_86400"}, + ], ]}) + else: + reply_markup = json.dumps({}) + params = {"chat_id": to, "caption": message, "disable_notification": self.disable_notification, + "reply_markup": reply_markup} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + files = {"photo": open(path, 'rb')} + if self.debug: + print_message("Trying to /sendPhoto:") + print_message(url) + print_message(params) + print_message("files: " + str(files)) + answer = requests.post(url, params=params, files=files, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def send_txt(self, to, text, text_name=None): + path = self.tmp_dir + "/" + "zbxtg_txt_" + url = self.tg_url_bot_general + self.key + "/sendDocument" + text = "\n".join(text) + if not text_name: + path += "".join(random.choice(string.ascii_lowercase + string.digits) for _ in range(10)) + else: + path += text_name + path += ".txt" + file_write(path, text) + params = {"chat_id": to, "caption": path.split("/")[-1], "disable_notification": self.disable_notification} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + files = {"document": open(path, 'rb')} + if self.debug: + print_message("Trying to /sendDocument:") + print_message(url) + print_message(params) + print_message("files: " + str(files)) + answer = requests.post(url, params=params, files=files, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def get_uid(self, name): + uid = 0 + if self.debug: + print_message("Getting uid from /getUpdates...") + updates = self.get_updates() + for m in updates["result"]: + if "message" in m: + chat = m["message"]["chat"] + elif "edited_message" in m: + chat = m["edited_message"]["chat"] + else: + continue + if chat["type"] == self.type == "private": + if "username" in chat: + if chat["username"] == name: + uid = chat["id"] + if (chat["type"] == "group" or chat["type"] == "supergroup") and self.type == "group": + if "title" in chat: + if sys.version_info[0] < 3: + if chat["title"] == name.decode("utf-8"): + uid = chat["id"] + else: + if chat["title"] == name: + uid = chat["id"] + return uid + + def error_need_to_contact(self, to): + if self.type == "private": + print_message("User '{0}' needs to send some text bot in private".format(to)) + if self.type == "group": + print_message("You need start a conversation with your bot first in '{0}' group chat, type '/start@{1}'" + .format(to, self.get_me()["result"]["username"])) + + def update_cache_uid(self, name, uid, message="Add new string to cache file"): + cache_string = "{0};{1};{2}\n".format(name, self.type, str(uid).rstrip()) + # FIXME + if self.debug: + print_message("{0}: {1}".format(message, cache_string)) + with open(self.tmp_uids, "a") as cache_file_uids: + cache_file_uids.write(cache_string) + return True + + def get_uid_from_cache(self, name): + if self.debug: + print_message("Trying to read cached uid for {0}, {1}, from {2}".format(name, self.type, self.tmp_uids)) + uid = 0 + if os.path.isfile(self.tmp_uids): + with open(self.tmp_uids, 'r') as cache_file_uids: + cache_uids_old = cache_file_uids.readlines() + for u in cache_uids_old: + u_splitted = u.split(";") + if name == u_splitted[0] and self.type == u_splitted[1]: + uid = u_splitted[2] + return uid + + def send_location(self, to, coordinates): + url = self.tg_url_bot_general + self.key + "/sendLocation" + params = {"chat_id": to, "disable_notification": self.disable_notification, + "latitude": coordinates["latitude"], "longitude": coordinates["longitude"]} + if self.reply_to_message_id: + params["reply_to_message_id"] = self.reply_to_message_id + if self.debug: + print_message("Trying to /sendLocation:") + print_message(url) + print_message("post params: " + str(params)) + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def answer_callback_query(self, callback_query_id, text=None): + url = self.tg_url_bot_general + self.key + "/answerCallbackQuery" + if not text: + params = {"callback_query_id": callback_query_id} + else: + params = {"callback_query_id": callback_query_id, "text": text} + answer = requests.post(url, params=params, proxies=self.proxies) + self.result = answer.json() + self.ok_update() + return self.result + + def ok_update(self): + self.ok = self.result["ok"] + if self.ok: + self.error = None + else: + self.error = self.result["description"] + print_message(self.error) + return True + + +def markdown_fix(message, offset, emoji=False): + offset = int(offset) + if emoji: # https://github.com/ableev/Zabbix-in-Telegram/issues/152 + offset -= 2 + message = "\n".join(message) + message = message[:offset] + message[offset+1:] + message = message.split("\n") + return message + + +class ZabbixWeb: + def __init__(self, server, username, password): + self.debug = False + self.server = server + self.username = username + self.password = password + self.proxies = {} + self.verify = True + self.cookie = None + self.basic_auth_user = None + self.basic_auth_pass = None + self.tmp_dir = None + + def login(self): + if not self.verify: + requests.packages.urllib3.disable_warnings() + + data_api = {"name": self.username, "password": self.password, "enter": "Sign in"} + answer = requests.post(self.server + "/", data=data_api, proxies=self.proxies, verify=self.verify, + auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) + cookie = answer.cookies + if len(answer.history) > 1 and answer.history[0].status_code == 302: + print_message("probably the server in your config file has not full URL (for example " + "'{0}' instead of '{1}')".format(self.server, self.server + "/zabbix")) + if not cookie: + print_message("authorization has failed, url: {0}".format(self.server + "/")) + cookie = None + + self.cookie = cookie + + def graph_get(self, itemid, period, title, width, height, version=3): + file_img = self.tmp_dir + "/{0}.png".format("".join(itemid)) + + title = requests.utils.quote(title) + + colors = { + 0: "00CC00", + 1: "CC0000", + 2: "0000CC", + 3: "CCCC00", + 4: "00CCCC", + 5: "CC00CC", + } + + drawtype = 5 + if len(itemid) > 1: + drawtype = 2 + + zbx_img_url_itemids = [] + for i in range(0, len(itemid)): + itemid_url = "&items[{0}][itemid]={1}&items[{0}][sortorder]={0}&" \ + "items[{0}][drawtype]={3}&items[{0}][color]={2}".format(i, itemid[i], colors[i], drawtype) + zbx_img_url_itemids.append(itemid_url) + + zbx_img_url = self.server + "/chart3.php?" + if version < 4: + zbx_img_url += "period={0}".format(period) + else: + zbx_img_url += "from=now-{0}&to=now".format(period) + zbx_img_url += "&name={0}&width={1}&height={2}&graphtype=0&legend=1".format(title, width, height) + zbx_img_url += "".join(zbx_img_url_itemids) + + if self.debug: + print_message(zbx_img_url) + answer = requests.get(zbx_img_url, cookies=self.cookie, proxies=self.proxies, verify=self.verify, + auth=requests.auth.HTTPBasicAuth(self.basic_auth_user, self.basic_auth_pass)) + status_code = answer.status_code + if status_code == 404: + print_message("can't get image from '{0}'".format(zbx_img_url)) + return False + res_img = answer.content + file_bwrite(file_img, res_img) + return file_img + + def api_test(self): + headers = {'Content-type': 'application/json'} + api_data = json.dumps({"jsonrpc": "2.0", "method": "user.login", "params": + {"user": self.username, "password": self.password}, "id": 1}) + api_url = self.server + "/api_jsonrpc.php" + api = requests.post(api_url, data=api_data, proxies=self.proxies, headers=headers) + return api.text + + +def print_message(message): + message = str(message) + "\n" + filename = sys.argv[0].split("/")[-1] + sys.stderr.write(filename + ": " + message) + + +def list_cut(elements, symbols_limit): + symbols_count = symbols_count_now = 0 + elements_new = [] + element_last_list = [] + for e in elements: + symbols_count_now = symbols_count + len(e) + if symbols_count_now > symbols_limit: + limit_idx = symbols_limit - symbols_count + e_list = list(e) + for idx, ee in enumerate(e_list): + if idx < limit_idx: + element_last_list.append(ee) + else: + break + break + else: + symbols_count = symbols_count_now + 1 + elements_new.append(e) + if symbols_count_now < symbols_limit: + return elements, False + else: + element_last = "".join(element_last_list) + elements_new.append(element_last) + return elements_new, True + + +class Maps: + # https://developers.google.com/maps/documentation/geocoding/intro + def __init__(self): + self.key = None + self.proxies = {} + + def get_coordinates_by_address(self, address): + coordinates = {"latitude": 0, "longitude": 0} + url_api = "https://maps.googleapis.com/maps/api/geocode/json?key={0}&address={1}".format(self.key, address) + url = url_api + answer = requests.get(url, proxies=self.proxies) + result = answer.json() + try: + coordinates_dict = result["results"][0]["geometry"]["location"] + except: + if "error_message" in result: + print_message("[" + result["status"] + "]: " + result["error_message"]) + return coordinates + coordinates = {"latitude": coordinates_dict["lat"], "longitude": coordinates_dict["lng"]} + return coordinates + + +def file_write(filename, text): + with open(filename, "w") as fd: + fd.write(str(text)) + return True + + +def file_bwrite(filename, data): + with open(filename, "wb") as fd: + fd.write(data) + return True + + +def file_read(filename): + with open(filename, "r") as fd: + text = fd.readlines() + return text + + +def file_append(filename, text): + with open(filename, "a") as fd: + fd.write(str(text)) + return True + + +def external_image_get(url, tmp_dir, timeout=6): + image_hash = hashlib.md5() + image_hash.update(url.encode()) + file_img = tmp_dir + "/external_{0}.png".format(image_hash.hexdigest()) + try: + answer = requests.get(url, timeout=timeout, allow_redirects=True) + except requests.exceptions.ReadTimeout as ex: + print_message("Can't get external image from '{0}': timeout".format(url)) + return False + status_code = answer.status_code + if status_code == 404: + print_message("Can't get external image from '{0}': HTTP 404 error".format(url)) + return False + answer_image = answer.content + file_bwrite(file_img, answer_image) + return file_img + + +def age2sec(age_str): + age_sec = 0 + age_regex = "([0-9]+d)?\s?([0-9]+h)?\s?([0-9]+m)?" + age_pattern = re.compile(age_regex) + intervals = age_pattern.match(age_str).groups() + for i in intervals: + if i: + metric = i[-1] + if metric == "d": + age_sec += int(i[0:-1])*86400 + if metric == "h": + age_sec += int(i[0:-1])*3600 + if metric == "m": + age_sec += int(i[0:-1])*60 + return age_sec + + +def main(): + + tmp_dir = zbxtg_settings.zbx_tg_tmp_dir + if tmp_dir == "/tmp/" + zbxtg_settings.zbx_tg_prefix: + print_message("WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!") + print_message("https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings") + + tmp_cookie = tmp_dir + "/cookie.py.txt" + tmp_uids = tmp_dir + "/uids.txt" + tmp_need_update = False # do we need to update cache file with uids or not + + rnd = random.randint(0, 999) + ts = time.time() + hash_ts = str(ts) + "." + str(rnd) + + log_file = "/dev/null" + + args = sys.argv + + settings = { + "zbxtg_itemid": "0", # itemid for graph + "zbxtg_title": None, # title for graph + "zbxtg_image_period": None, + "zbxtg_image_age": "3600", + "zbxtg_image_width": "900", + "zbxtg_image_height": "200", + "tg_method_image": False, # if True - default send images, False - send text + "tg_chat": False, # send message to chat or in private + "tg_group": False, # send message to chat or in private + "is_debug": False, + "is_channel": False, + "disable_web_page_preview": False, + "location": None, # address + "lat": 0, # latitude + "lon": 0, # longitude + "is_single_message": False, + "markdown": False, + "html": False, + "signature": None, + "signature_disable": False, + "graph_buttons": False, + "extimg": None, + "to": None, + "to_group": None, + "forked": False, + } + + url_github = "https://github.com/ableev/Zabbix-in-Telegram" + url_wiki_base = "https://github.com/ableev/Zabbix-in-Telegram/wiki" + url_tg_group = "https://t.me/ZbxTg" + url_tg_channel = "https://t.me/Zabbix_in_Telegram" + + settings_description = { + "itemid": {"name": "zbxtg_itemid", "type": "list", + "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs"}, + "title": {"name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs"}, + "graphs_period": {"name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs"}, + "graphs_age": {"name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs"}, + "graphs_width": {"name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs"}, + "graphs_height": {"name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs"}, + "graphs": {"name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs"}, + "chat": {"name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'", + "url": "How-to-send-message-to-the-group-chat"}, + "group": {"name": "tg_group", "type": "bool", "help": "sends message to a group", + "url": "How-to-send-message-to-the-group-chat"}, + "debug": {"name": "is_debug", "type": "bool", "help": "enables 'debug'", + "url": "How-to-test-script-in-command-line"}, + "channel": {"name": "is_channel", "type": "bool", "help": "sends message to a channel", + "url": "Channel-support"}, + "disable_web_page_preview": {"name": "disable_web_page_preview", "type": "bool", + "help": "disable web page preview", "url": "Disable-web-page-preview"}, + "location": {"name": "location", "type": "str", "help": "address of location", "url": "Location"}, + "lat": {"name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location"}, + "lon": {"name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location"}, + "single_message": {"name": "is_single_message", "type": "bool", "help": "do not split message and graph", + "url": "Why-am-I-getting-two-messages-instead-of-one"}, + "markdown": {"name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, + "html": {"name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML"}, + "signature": {"name": "signature", "type": "str", + "help": "bot's signature", "url": "Bot-signature"}, + "signature_disable": {"name": "signature_disable", "type": "bool", + "help": "enables/disables bot's signature", "url": "Bot-signature"}, + "graph_buttons": {"name": "graph_buttons", "type": "bool", + "help": "activates buttons under graph, could be using in ZbxTgDaemon", + "url": "Interactive-bot"}, + "external_image": {"name": "extimg", "type": "str", + "help": "should be url; attaches external image from different source", + "url": "External-image-as-graph"}, + "to": {"name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", + "url": "Custom-to-and-to_group"}, + "to_group": {"name": "to_group", "type": "str", + "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group"}, + "forked": {"name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": ""}, + } + + if len(args) < 4: + do_not_exit = False + if "--features" in args: + print(("List of available settings, see {0}/Settings\n---".format(url_wiki_base))) + for sett, proprt in list(settings_description.items()): + print(("{0}: {1}\ndoc: {2}/{3}\n--".format(sett, proprt["help"], url_wiki_base, proprt["url"]))) + + elif "--show-settings" in args: + do_not_exit = True + print_message("Settings: " + str(json.dumps(settings, indent=2))) + + else: + print(("Hi. You should provide at least three arguments.\n" + "zbxtg.py [TO] [SUBJECT] [BODY]\n\n" + "1. Read main page and/or wiki: {0} + {1}\n" + "2. Public Telegram group (discussion): {2}\n" + "3. Public Telegram channel: {3}\n" + "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev" + .format(url_github, url_wiki_base, url_tg_group, url_tg_channel))) + if not do_not_exit: + sys.exit(0) + + + zbx_to = args[1] + zbx_subject = args[2] + zbx_body = args[3] + + tg = TelegramAPI(key=zbxtg_settings.tg_key) + + tg.tmp_dir = tmp_dir + tg.tmp_uids = tmp_uids + + if zbxtg_settings.proxy_to_tg: + proxy_to_tg = zbxtg_settings.proxy_to_tg + if not proxy_to_tg.find("http") and not proxy_to_tg.find("socks"): + proxy_to_tg = "https://" + proxy_to_tg + tg.proxies = { + "https": "{0}".format(proxy_to_tg), + } + + zbx = ZabbixWeb(server=zbxtg_settings.zbx_server, username=zbxtg_settings.zbx_api_user, + password=zbxtg_settings.zbx_api_pass) + + zbx.tmp_dir = tmp_dir + + # workaround for Zabbix 4.x + zbx_version = 3 + + try: + zbx_version = zbxtg_settings.zbx_server_version + except: + pass + + if zbxtg_settings.proxy_to_zbx: + zbx.proxies = { + "http": "http://{0}/".format(zbxtg_settings.proxy_to_zbx), + "https": "https://{0}/".format(zbxtg_settings.proxy_to_zbx) + } + + # https://github.com/ableev/Zabbix-in-Telegram/issues/55 + try: + if zbxtg_settings.zbx_basic_auth: + zbx.basic_auth_user = zbxtg_settings.zbx_basic_auth_user + zbx.basic_auth_pass = zbxtg_settings.zbx_basic_auth_pass + except: + pass + + try: + zbx_api_verify = zbxtg_settings.zbx_api_verify + zbx.verify = zbx_api_verify + except: + pass + + map = Maps() + # api key to resolve address to coordinates via google api + try: + if zbxtg_settings.google_maps_api_key: + map.key = zbxtg_settings.google_maps_api_key + if zbxtg_settings.proxy_to_tg: + map.proxies = { + "http": "http://{0}/".format(zbxtg_settings.proxy_to_tg), + "https": "https://{0}/".format(zbxtg_settings.proxy_to_tg) + } + except: + pass + + zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines() + zbxtg_body_text = [] + + for line in zbxtg_body: + if line.find(zbxtg_settings.zbx_tg_prefix) > -1: + setting = re.split("[\s:=]+", line, maxsplit=1) + key = setting[0].replace(zbxtg_settings.zbx_tg_prefix + ";", "") + if key not in settings_description: + if "--debug" in args: + print_message("[ERROR] There is no '{0}' method, use --features to get help".format(key)) + continue + if settings_description[key]["type"] == "list": + value = setting[1].split(",") + elif len(setting) > 1 and len(setting[1]) > 0: + value = setting[1] + elif settings_description[key]["type"] == "bool": + value = True + else: + value = settings[settings_description[key]["name"]] + if key in settings_description: + settings[settings_description[key]["name"]] = value + else: + zbxtg_body_text.append(line) + + tg_method_image = bool(settings["tg_method_image"]) + tg_chat = bool(settings["tg_chat"]) + tg_group = bool(settings["tg_group"]) + is_debug = bool(settings["is_debug"]) + is_channel = bool(settings["is_channel"]) + disable_web_page_preview = bool(settings["disable_web_page_preview"]) + is_single_message = bool(settings["is_single_message"]) + + # experimental way to send message to the group https://github.com/ableev/Zabbix-in-Telegram/issues/15 + if args[0].split("/")[-1] == "zbxtg_group.py" or "--group" in args or tg_chat or tg_group: + tg_chat = True + tg_group = True + tg.type = "group" + + if "--debug" in args or is_debug: + is_debug = True + tg.debug = True + zbx.debug = True + print_message(tg.get_me()) + print_message("Cache file with uids: " + tg.tmp_uids) + log_file = tmp_dir + ".debug." + hash_ts + ".log" + #print_message(log_file) + + if "--markdown" in args or settings["markdown"]: + tg.markdown = True + + if "--html" in args or settings["html"]: + tg.html = True + + if "--channel" in args or is_channel: + tg.type = "channel" + + if "--disable_web_page_preview" in args or disable_web_page_preview: + if is_debug: + print_message("'disable_web_page_preview' option has been enabled") + tg.disable_web_page_preview = True + + if "--graph_buttons" in args or settings["graph_buttons"]: + tg.image_buttons = True + + if "--forked" in args: + settings["forked"] = True + + if "--tg-key" in args: + tg.key = args[args.index("--tg-key") + 1] + + location_coordinates = {"latitude": None, "longitude": None} + if settings["lat"] > 0 and settings["lat"] > 0: + location_coordinates = {"latitude": settings["lat"], "longitude": settings["lon"]} + tg.location = location_coordinates + else: + if settings["location"]: + location_coordinates = map.get_coordinates_by_address(settings["location"]) + if location_coordinates: + settings["lat"] = location_coordinates["latitude"] + settings["lon"] = location_coordinates["longitude"] + tg.location = location_coordinates + + if not os.path.isdir(tmp_dir): + if is_debug: + print_message("Tmp dir doesn't exist, creating new one...") + try: + os.makedirs(tmp_dir) + open(tg.tmp_uids, "a").close() + os.chmod(tmp_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + os.chmod(tg.tmp_uids, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + except: + tmp_dir = "/tmp" + if is_debug: + print_message("Using {0} as a temporary dir".format(tmp_dir)) + + done_all_work_in_the_fork = False + # issue75 + + to_types = ["to", "to_group", "to_channel"] + to_types_to_telegram = {"to": "private", "to_group": "group", "to_channel": "channel"} + multiple_to = {} + for i in to_types: + multiple_to[i]=[] + + for t in to_types: + try: + if settings[t] and not settings["forked"]: + # zbx_to = settings["to"] + multiple_to[t] = re.split(",", settings[t]) + except KeyError: + pass + + # example: + # {'to_channel': [], 'to': ['usr1', 'usr2', 'usr3'], 'to_group': []} + + if (sum([len(v) for k, v in list(multiple_to.items())])) == 1: + # if we have only one recipient, we don't need fork to send message, just re-write "to" vaiable + tmp_max = 0 + for t in to_types: + if len(multiple_to[t]) > tmp_max: + tmp_max = len(multiple_to[t]) + tg.type = to_types_to_telegram[t] + zbx_to = multiple_to[t][0] + else: + for t in to_types: + for i in multiple_to[t]: + args_new = list(args) + args_new[1] = i + if t == "to_group": + args_new.append("--group") + args_new.append("--forked") + args_new.insert(0, sys.executable) + if is_debug: + print_message("Fork for custom recipient ({1}), new args: {0}".format(args_new, + to_types_to_telegram[t])) + subprocess.call(args_new) + done_all_work_in_the_fork = True + + if done_all_work_in_the_fork: + sys.exit(0) + + uid = None + + if tg.type == "channel": + uid = zbx_to + if tg.type == "private": + zbx_to = zbx_to.replace("@", "") + + if zbx_to.isdigit(): + uid = zbx_to + + if not uid: + uid = tg.get_uid_from_cache(zbx_to) + + if not uid: + uid = tg.get_uid(zbx_to) + if uid: + tmp_need_update = True + if not uid: + tg.error_need_to_contact(zbx_to) + sys.exit(1) + + if tmp_need_update: + tg.update_cache_uid(zbx_to, str(uid).rstrip()) + + if is_debug: + print_message("Telegram uid of {0} '{1}': {2}".format(tg.type, zbx_to, uid)) + + # add signature, turned off by default, you can turn it on in config + try: + if "--signature" in args or settings["signature"] or zbxtg_settings.zbx_tg_signature\ + and not "--signature_disable" in args and not settings["signature_disable"]: + if "--signature" in args: + settings["signature"] = args[args.index("--signature") + 1] + if not settings["signature"]: + settings["signature"] = zbxtg_settings.zbx_server + zbxtg_body_text.append("--") + zbxtg_body_text.append(settings["signature"]) + except: + pass + + # replace text with emojis + internal_using_emoji = False # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152 + if hasattr(zbxtg_settings, "emoji_map"): + zbxtg_body_text_emoji_support = [] + for l in zbxtg_body_text: + l_new = l + for k, v in list(zbxtg_settings.emoji_map.items()): + l_new = l_new.replace("{{" + k + "}}", v) + zbxtg_body_text_emoji_support.append(l_new) + if len("".join(zbxtg_body_text)) - len("".join(zbxtg_body_text_emoji_support)): + internal_using_emoji = True + zbxtg_body_text = zbxtg_body_text_emoji_support + + if not is_single_message: + tg.send_message(uid, zbxtg_body_text) + if not tg.ok: + # first case – if group has been migrated to a supergroup, we need to update chat_id of that group + if tg.error.find("migrated") > -1 and tg.error.find("supergroup") > -1: + migrate_to_chat_id = tg.result["parameters"]["migrate_to_chat_id"] + tg.update_cache_uid(zbx_to, migrate_to_chat_id, message="Group chat is migrated to supergroup, " + "updating cache file") + uid = migrate_to_chat_id + tg.send_message(uid, zbxtg_body_text) + + # another case if markdown is enabled and we got parse error, try to remove "bad" symbols from message + if tg.markdown and tg.error.find("Can't find end of the entity starting at byte offset") > -1: + markdown_warning = "Original message has been fixed due to {0}. " \ + "Please, fix the markdown, it's slowing down messages sending."\ + .format(url_wiki_base + "/" + settings_description["markdown"]["url"]) + markdown_fix_attempts = 0 + while not tg.ok and markdown_fix_attempts != 3: + offset = re.search("Can't find end of the entity starting at byte offset ([0-9]+)", tg.error).group(1) + zbxtg_body_text = markdown_fix(zbxtg_body_text, offset, emoji=internal_using_emoji) + \ + ["\n"] + [markdown_warning] + tg.disable_web_page_preview = True + tg.send_message(uid, zbxtg_body_text) + markdown_fix_attempts += 1 + if tg.ok: + print_message(markdown_warning) + + if is_debug: + print((tg.result)) + + if settings["zbxtg_image_age"]: + age_sec = age2sec(settings["zbxtg_image_age"]) + if age_sec > 0 and age_sec > 3600: + settings["zbxtg_image_period"] = age_sec + + message_id = 0 + if tg_method_image: + zbx.login() + if not zbx.cookie: + text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\ + "unable to send graphs check manually" + tg.send_message(uid, [text_warn]) + print_message(text_warn) + else: + if not settings["extimg"]: + zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"], + settings["zbxtg_title"], settings["zbxtg_image_width"], + settings["zbxtg_image_height"], version=zbx_version) + else: + zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir) + zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200) + if tg.ok: + message_id = tg.result["result"]["message_id"] + tg.reply_to_message_id = message_id + if not zbxtg_file_img: + text_warn = "Can't get graph image, check script manually, see logs, or disable graphs" + tg.send_message(uid, [text_warn]) + print_message(text_warn) + else: + if not is_single_message: + zbxtg_body_text = "" + else: + if is_modified: + text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\ + "the message has been cut to 200 symbols, "\ + "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\ + "#issuecomment-166895044" + print_message(text_warn) + if not is_single_message: + tg.disable_notification = True + tg.send_photo(uid, zbxtg_body_text, zbxtg_file_img) + if tg.ok: + settings["zbxtg_body_text"] = zbxtg_body_text + os.remove(zbxtg_file_img) + else: + if tg.error.find("PHOTO_INVALID_DIMENSIONS") > -1: + if not tg.disable_web_page_preview: + tg.disable_web_page_preview = True + text_warn = "Zabbix user couldn't get graph (probably has no rights to get data from host), " \ + "check script manually, see {0}".format(url_wiki_base + "/" + + settings_description["graphs"]["url"]) + tg.send_message(uid, [text_warn]) + print_message(text_warn) + if tg.location and location_coordinates["latitude"] and location_coordinates["longitude"]: + tg.reply_to_message_id = message_id + tg.disable_notification = True + tg.send_location(to=uid, coordinates=location_coordinates) + + if "--show-settings" in args: + print_message("Settings: " + str(json.dumps(settings, indent=2))) + +if __name__ == "__main__": + main() diff --git a/root/var/lib/zabbix/bin/zbxtg_settings.example.py b/root/var/lib/zabbix/bin/zbxtg_settings.example.py new file mode 100644 index 0000000..4b9e656 --- /dev/null +++ b/root/var/lib/zabbix/bin/zbxtg_settings.example.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +tg_key = "XYZ" # telegram bot api key + +zbx_tg_prefix = "zbxtg" # variable for separating text from script info +zbx_tg_tmp_dir = "/var/tmp/" + zbx_tg_prefix # directory for saving caches, uids, cookies, etc. +zbx_tg_signature = False + +zbx_tg_update_messages = True +zbx_tg_matches = { + "problem": "PROBLEM: ", + "ok": "OK: " +} + +zbx_server = "http://127.0.0.1/zabbix/" # zabbix server full url +zbx_api_user = "api" +zbx_api_pass = "api" +zbx_api_verify = True # True - do not ignore self signed certificates, False - ignore + +#zbx_server_version = 2 # for Zabbix 2.x version +zbx_server_version = 3 # for Zabbix 3.x version, by default, not everyone updated to 4.x yet +#zbx_server_version = 4 # for Zabbix 4.x version, default will be changed in the future with this + +zbx_basic_auth = False +zbx_basic_auth_user = "zabbix" +zbx_basic_auth_pass = "zabbix" + +proxy_to_zbx = None +proxy_to_tg = None + +# proxy_to_zbx = "http://proxy.local:3128" +# proxy_to_tg = "https://proxy.local:3128" + +# proxy_to_tg = "socks5://user1:password2@hostname:port" # socks5 with username and password +# proxy_to_tg = "socks5://hostname:port" # socks5 without username and password +# proxy_to_tg = "socks5h://hostname:port" # hostname resolution on SOCKS proxy. + # This helps when internet provider alter DNS queries. + # Found here: https://stackoverflow.com/a/43266186/957508 + +google_maps_api_key = None # get your key, see https://developers.google.com/maps/documentation/geocoding/intro + +zbx_tg_daemon_enabled = False +zbx_tg_daemon_enabled_ids = [6931850, ] +zbx_tg_daemon_enabled_users = ["ableev", ] +zbx_tg_daemon_enabled_chats = ["Zabbix in Telegram Script", ] + +zbx_db_host = "localhost" +zbx_db_database = "zabbix" +zbx_db_user = "zbxtg" +zbx_db_password = "zbxtg" + + +emoji_map = { + "Disaster": "đŸ”Ĩ", + "High": "🛑", + "Average": "❗", + "Warning": "⚠ī¸", + "Information": "ℹī¸", + "Not classified": "🔘", + "OK": "✅", + "PROBLEM": "❗", + "info": "ℹī¸", + "WARNING": "⚠ī¸", + "DISASTER": "❌", + "bomb": "đŸ’Ŗ", + "fire": "đŸ”Ĩ", + "hankey": "💩", +} diff --git a/smeserver-zabbix-server.spec b/smeserver-zabbix-server.spec new file mode 100644 index 0000000..baf2fad --- /dev/null +++ b/smeserver-zabbix-server.spec @@ -0,0 +1,241 @@ +# $Id: smeserver-zabbix-server.spec,v 1.20 2022/12/11 07:12:36 jpp Exp $ +# Authority: vip-ire +# Name: Daniel Berteaud + +%define name smeserver-zabbix-server +%define version 0.1 +%define release 32 +Summary: sme server integration of zabbix server and web front-end +Name: %{name} +Version: %{version} +Release: %{release}%{?dist} +License: GNU GPL version 2 +URL: http://www.zabbix.com/ +Group: SMEserver/addon +Source: %{name}-%{version}.tar.xz + + +BuildArchitectures: noarch +BuildRequires: e-smith-devtools +BuildRoot: /var/tmp/%{name}-%{version} +Requires: e-smith-release >= 10.0 +Requires: e-smith-apache >= 2.6.0-19 +Requires: smeserver-php >= 3.0.0-43 +Requires: fping +Requires: zabbix-server-mysql >= 4.4.6 +Requires: zabbix-web-mysql >= 4.4.6 +Requires: zabbix-web >= 4.4.6 +Requires: sendxmpp +Requires: smeserver-remoteuseraccess +Requires: smeserver-mariadb105 +# for telegram bot +Requires: python2-pysocks python-requests python2-requests-oauthlib +Obsoletes: zabbix-server +Conflicts: smeserver-zabbix-proxy +AutoReqProv: no + +%description +smserver integration of zabbix server and web front-end. +Zabbix is an entreprise-class open source distributed monitoring +solution + +%package z50 +%define provscl 5.0.30-1.el7 +#5.0.30-1 +Summary: SME Server integration of zabbix server 5.0 and web front-end using Remi SCLO +Group: Applications/Internet +#common +BuildArchitectures: noarch +BuildRequires: e-smith-devtools +Requires: e-smith-release >= 10.0 +Requires: e-smith-apache >= 2.6.0-19 +Requires: smeserver-php >= 3.0.0-43 +Requires: fping +Requires: zabbix-server-mysql >= 5.0.0 +#Requires: zabbix-web-mysql-scl >= 5.0.0 +#Requires: zabbix-web >= 5.0.0 +Requires: sendxmpp +Requires: smeserver-remoteuseraccess +Requires: smeserver-mariadb105 +# specific +#Provides: rh-php72-php-fpm rh-php72-php-mbstring rh-php72 rh-php72-php-mysqlnd rh-php72-php-gd rh-php72-php-xml rh-php72-php-ldap rh-php72-php-bcmath +Provides: zabbix-web-database-scl-php74 = %{provscl} +Provides: zabbix-web-deps-scl = %{provscl} +Provides: zabbix-web-deps-scl-php73 = %{provscl} +Requires: zabbix-web = %{provscl} +Requires: php74-php-mysqlnd php74 php74-php-gd php74-php-bcmath php74-php-mbstring php74-php-xml php74-php-ldap php74-php-fpm php74-php-mysqlnd + +%description z50 +SME Server integration of zabbix server 5.0 and web front-end using Remi SCLO. +Zabbix is an entreprise-class open source distributed monitoring +solution + +%changelog +* Sat Sep 07 2024 cvs2git.sh aka Brian Read 0.1-32.sme +- Roll up patches and move to git repo [SME: 12338] + +* Sat Sep 07 2024 BogusDateBot +- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday, + by assuming the date is correct and changing the weekday. + +* Sat Dec 10 2022 Jean-Philippe Pialasse 0.1-31.sme +- original package build for Zabbix 5.0 using Remi SCLO [SME: 11748] + support for LTS 5.0 EOL May 31, 2025 + needs mariadb105. + manual migration from mariadb55 to mariadb105 needed for existing installs + +* Mon Aug 01 2022 Jean-Philippe Pialasse 0.1-30.sme +- update to httpd 2.4 access syntax [SME: 12068] + thanks to Vasarhelyi Zsolt +- add to core backup [SME: 12031] + non rpm owned files in /etc/zabbix, /etc/zabbix/zabbix_agentd.conf.d/ + and /var/lib/zabbix/bin/ + +* Tue Nov 09 2021 Jean-Philippe Pialasse 0.1-29.sme +- set random password to Admin i fuser exists and password is zabbix [SME: 11749] + +* Mon Nov 08 2021 Jean-Philippe Pialasse 0.1-28.sme +- add check cert scripts and telegram [SME: 10802] + no template provided to use with + +* Sun Nov 07 2021 Jean-Philippe Pialasse 0.1-27.sme +- fix init sql, typo and reload deamon [SME: 11232] + +* Sun Nov 07 2021 Jean-Philippe Pialasse 0.1-26.sme +- fix session and log issue [SME: 11232] + fix mysql-init not launched + +* Mon Nov 01 2021 BogusDateBot +- Eliminated rpmbuild "bogus date" warnings due to inconsistent weekday, + by assuming the date is correct and changing the weekday. + Tue Mar 02 2009 --> Tue Feb 24 2009 or Mon Mar 02 2009 or Tue Mar 03 2009 or .... + Wed Feb 07 2016 --> Wed Feb 03 2016 or Sun Feb 07 2016 or Wed Feb 10 2016 or .... + +* Mon Nov 01 2021 Brian Read 0.1-25.sme +- Switch-to-specific-php-fpm [SME: 11232] + +* Mon Nov 01 2021 Brian Read 0.1-24.sme +- Remove post and postun command in spec file [SME: 11232] + +* Thu Dec 10 2020 Brian Read 0.1-23.sme +- Add in post instructions to spec for SQL db creation [SME: 11232] + +* Tue Dec 08 2020 Brian Read 0.1-22.sme +- Add expand zabbix-server.conf and .user.ini to creatlinks update event [SME:11232] + +* Tue Dec 08 2020 Brian Read 0.1-21.sme +- Template-user-dot-ini-and-override-service-file-change-pid-in-conf-file [SME: 11232] + +* Mon Dec 07 2020 Brian Read 0.1-20.sme +- Import to SME10 tree [SME: 11232] +- Update createlinks for systemd +- Update httpd.conf for php-fpw +- Add .user.ini for php-admin-flags that where in httpd.conf + +* Sun May 10 2020 Jean-Philipe Pialasse 0.1-19.sme +- adapt for zabbix 4.4.6 [SME: 10944] + +* Thu Sep 05 2019 Jean-Philipe Pialasse 0.1-17.sme +- remove deprecated option preventing from starting service [SME: 10458] + +* Wed Mar 29 2017 Jean-Philipe Pialasse 0.1-16.sme +- fix sql init not finding default sql dump to import [SME: 9569] +- NodeID support droped as per zabbix 2.4.0 and higher +- requires smeserver-php-scl as Zabbix-server 3.2.4 requires php 5.4 or higher + +* Mon Jun 13 2016 Jean-Philipe Pialasse 0.1-15.sme +- finitial import [SME: 9569] +- fix php requirement +- fix add path to new db + +* Sun Feb 07 2016 stephane de Labrusse 0.1-14.sme + Wed Feb 07 2016 --> Wed Feb 03 2016 or Sun Feb 07 2016 or Wed Feb 10 2016 or .... +- New roll for sme9 + +* Mon Apr 29 2013 Daniel B. 0.1-13 +- Increase PHP mem limit to 128M + +* Mon Mar 8 2010 Daniel B. 0.1-12 +- Use global TimeZone + +* Wed Dec 2 2009 Daniel B. 0.1-11 +- Support several mysql DB patches + +* Tue Mar 03 2009 Daniel B. 0.1-10 +- Add smeserver-remoteuseraccess as a dependencie (sudoers template problem) + +* Mon Mar 02 2009 Daniel B. 0.1-9 +- specify path to .sendxmpprc file in the script sendxmpp + +* Mon Mar 02 2009 Daniel B. 0.1-8 +- move .sendxmpprc template to the correct directory + +* Mon Mar 02 2009 Daniel B. 0.1-7 +- Move jabber account informations to xmpprc + +* Mon Mar 02 2009 Daniel B. 0.1-6 +- Adjust service masq during zabbix-server-update event +- Enable DB cache module with StartDBSyncers directive + +* Sun Mar 01 2009 Daniel B. 0.1-5 +- Fix permissions on /var/lib/zabbix/tmp + +* Tue Feb 17 2009 Daniel B. 0.1-4 +- rename event zabbix-update to zabbix-server-update + +* Wed Feb 11 2009 Daniel B. 0.1-3 +- disable web access (usefull for distributed monitoring) +- Use stronger password for mysql database +- Use /var/lib/zabbix/bin as default location for scripts +- Use /var/lib/zabbix/tmp for temp dir + +* Fri Feb 06 2009 Daniel B. 0.1-2 +- Link template-begin-shell to template-begin for sendxmpp script + +* Fri Feb 06 2009 Daniel B. 0.1-1 +- templatize sendxmpp as zabbix user doesn't have access to SME db +- Add JabberTLS option in the db + +* Mon Feb 02 2009 Daniel B. 0.1-0 +- initial release + +%prep +%setup + +%build +perl ./createlinks +%{__mkdir_p} root/var/lib/zabbix/tmp +#%{__mkdir_p} root/var/log/zabbix - conflict with zabbix-server-mysql +%{__mkdir_p} root/var/log/php/zabbix +%{__mkdir_p} root/var/lib/php/zabbix/{tmp,wsdlcache,opcache,session} + + +%install +rm -rf $RPM_BUILD_ROOT +(cd root ; find . -depth -print | cpio -dump $RPM_BUILD_ROOT) +rm -f %{name}-%{version}-filelist +/sbin/e-smith/genfilelist $RPM_BUILD_ROOT \ + --file /var/lib/zabbix/bin/fping 'attr(0750,root,zabbix)' \ + --file /var/lib/zabbix/bin/fping6 'attr(0750,root,zabbix)' \ + --dir /var/lib/zabbix/tmp 'attr(0750,zabbix,zabbix)' \ + --dir /var/log/php/zabbix 'attr(0770,www,www)' \ + --dir /var/lib/php/zabbix 'attr(0755,root,www)' \ + --dir /var/lib/php/zabbix/tmp 'attr(0770,root,www)' \ + --dir /var/lib/php/zabbix/opcache 'attr(0770,root,www)' \ + --dir /var/lib/php/zabbix/session 'attr(0770,root,www)' \ + |grep -v '.pyc'|grep -v '.pyo' \ + > %{name}-%{version}-filelist + +%files -f %{name}-%{version}-filelist +%defattr(-,root,root) + +%files z50 -f %{name}-%{version}-filelist +%defattr(-,root,root) + +%clean +rm -rf $RPM_BUILD_ROOT + +%post + +%postun +