va-zabbix/zabbix/zabbix-syslog/zabbix_syslog_lkp_host.pl

274 lines
7.3 KiB
Perl

#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/lib";
use Data::Dumper;
use Config::General;
use ZabbixAPI;
use English '-no_match_vars';
use MIME::Base64 qw(encode_base64);
use IO::Socket::INET;
use Storable qw(lock_store lock_retrieve);
our $VERSION = 4.0;
my $CACHE_TIMEOUT = 600;
my $CACHE_DIR = '/tmp/zabbix_syslog_cache_n';
die "No argumets required anymore since script version 3.0\n" if @ARGV > 0;
my $conf;
$conf = eval {Config::General->new('/usr/local/etc/zabbix_syslog.cfg')};
if ($@) {
eval {$conf = Config::General->new('/etc/zabbix/zabbix_syslog.cfg')};
if ($@) {die "Please check that config file is available as /usr/local/etc/zabbix_syslog.cfg or /etc/zabbix/zabbix_syslog.cfg\n";}
}
my %Config = $conf->getall;
#Authenticate yourself
my $url = $Config{'url'} || die "URL is missing in zabbix_syslog.cfg\n";
my $user = $Config{'user'} || die "API user is missing in zabbix_syslog.cfg\n";
my $password = $Config{'password'} || die "API user password is missing in zabbix_syslog.cfg\n";
my $server = $Config{'server'} || die "server hostname is missing in zabbix_syslog.cfg\n";
my $zbx;
my $debug = $Config{'debug'};
my ( $authID, $response, $json );
#IP regex patter part
my $ipv4_octet = q/(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
#rsyslog omprog loop
#http://www.rsyslog.com/doc/master/configuration/modules/omprog.html
while (defined(my $message = <>)) {
chomp($message);
#get ip from message
my $ip;
if ( $message =~ / \[ ((?:$ipv4_octet[.]){3}${ipv4_octet}) \]/msx ) {
$ip = $1;
}
else {
warn "No IP in square brackets found in '$message', cannot continue\n";
next;
}
my $hostname = ${retrieve_from_store($ip)}->{'hostname'};
if ( !defined $hostname ) {
my $result;
$zbx = ZabbixAPI->new( { api_url => $url, username => $user, password => $password } );
$zbx->login();
my @hosts_found;
my $hostid;
my @hostinterfaces;
eval {@hostinterfaces=hostinterface_get($ip)};
if($@){
warn "Failed to retrieve any host interface with IP = $ip. Unable to bind message to item, skipping\n";
next;
}
foreach my $host (@hostinterfaces) {
$hostid = $host->{'hostid'};
if ( grep { /$hostid/msx } @hosts_found ) {
next;
}#check if $hostid already is in array then skip(next)
else { push @hosts_found, $hostid; }
#now get hostname
if ( get_zbx_trapper_syslogid_by_hostid($hostid) ) {
my $result = host_get($hostid);
#return hostname if possible
if ( $result->{'host'} ) {
if ( $result->{'proxy_hostid'} == 0 ) #check if host monitored directly or via proxy
{
#lease $server as is
}
else {
#assume that rsyslogd and zabbix_proxy are on the same server
$server = 'localhost';
}
$hostname = $result->{'host'};
}
last;
}
}
$zbx->logout();
store_message( $ip, $hostname );
}
zabbix_send( $server, $hostname, 'syslog', $message );
}
#______SUBS
sub hostinterface_get {
my $ip = shift;
my $params = {
output => [ 'ip', 'hostid' ],
filter => { ip => $ip, }
};
my $result = $zbx->do('hostinterface.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful (not empty array in result)
if ( !@{ $result } ) {
$zbx->logout();
die "hostinterface.get failed\n";
}
return @{ $result };
}
sub get_zbx_trapper_syslogid_by_hostid {
my $hostid = shift;
my $params = {
output => ['itemid'],
hostids => $hostid,
search => {
'key_' => 'syslog',
type => 2, #type => 2 is zabbix_trapper
status => 0,
},
limit => 1,
};
my $result = $zbx->do('item.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful
if ( !@{ $result } ) {
warn "item.get failed\n";
}
#return itemid of syslog key (trapper type)
return ${ $result }[0]->{itemid};
}
sub host_get {
my $hostid = shift;
my $params = {
hostids => [$hostid],
output => [ 'host', 'proxy_hostid', 'status' ],
filter => { status => 0, }, # only use hosts enabled
limit => 1,
};
my $result = $zbx->do('host.get',$params);
if ( $debug > 0 ) { print Dumper $result; }
# Check if response was successful
if ( !$result ) {
$zbx->logout();
die "host.get failed\n";
}
return ${ $result }[0]; #return result
}
sub zabbix_send {
my $zabbixserver = shift;
my $hostname = shift;
my $item = shift;
my $data = shift;
my $SOCK_TIMEOUT = 10;
my $SOCK_RECV_LENGTH = 1024;
my $result;
my $request =
sprintf
"<req>\n<host>%s</host>\n<key>%s</key>\n<data>%s</data>\n</req>\n",
encode_base64($hostname), encode_base64($item), encode_base64($data);
my $packet = "ZBXD\1" . pack('V', length($request)) . "\0\0\0\0" . $request;
my $sock = IO::Socket::INET->new(
PeerAddr => $zabbixserver,
PeerPort => '10051',
Proto => 'tcp',
Timeout => $SOCK_TIMEOUT
);
die "Could not create socket: $ERRNO\n" unless $sock;
$sock->send($packet);
my @handles = IO::Select->new($sock)->can_read($SOCK_TIMEOUT);
if ( $debug > 0 ) { print "host - $hostname, item - $item, data - $data\n"; }
if ( scalar(@handles) > 0 ) {
$sock->recv( $result, $SOCK_RECV_LENGTH );
if ( $debug > 0 ) {
print "answer from zabbix server $zabbixserver: $result\n";
}
}
else {
if ( $debug > 0 ) { print "no answer from zabbix server\n"; }
}
$sock->close();
return;
}
#helpers
sub store_message {
my $ip = shift;
my $hostname = shift;
my $storage_file = $CACHE_DIR;
my ( $stored, $to_store );
$to_store->{$ip} = {
hostname => $hostname,
created => time()
};
if ( -f $storage_file ) {
$stored = lock_retrieve $storage_file;
lock_store { %{$stored}, %{$to_store} }, $storage_file;
}
else {
#first time file creation, apply proper file permissions and store only single event
lock_store $to_store, $storage_file;
chmod 0666, $storage_file;
}
}
sub retrieve_from_store {
my $ip = shift;
my $storage_file = $CACHE_DIR;
my $stored;
my $message_to_retrieve;
if ( -f $storage_file ) {
$stored = lock_retrieve $storage_file;
#remove expired from cache
if (defined($stored->{$ip})){
if (time() - $stored->{$ip}->{created} > $CACHE_TIMEOUT){
delete $stored->{$ip};
lock_store $stored, $storage_file;
}
else {
$message_to_retrieve = $stored->{$ip};
}
}
}
return \$message_to_retrieve;
}