centreon-plugins/connectors/vmware/centreon_esxd

472 lines
15 KiB
Perl

#!/usr/bin/perl -w
BEGIN {
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
$ENV{ESX_SYSLOGD_LOAD} = 0;
eval 'require Unix::Syslog;';
if (!$@) {
$ENV{ESX_SYSLOGD_LOAD} = 1;
require Unix::Syslog;
Unix::Syslog->import(qw(:subs :macros));
}
}
use strict;
use VMware::VIRuntime;
use VMware::VILib;
use IO::Socket;
use File::Basename;
use IO::Select;
use POSIX ":sys_wait_h";
use Data::Dumper;
our $module_date_parse_loaded = 0;
eval 'require DateTime::Format::ISO8601';
if (!$@) {
$module_date_parse_loaded = 1;
require DateTime::Format::ISO8601;
}
use vars qw($libpath $port %vsphere_server $TIMEOUT_VSPHERE $TIMEOUT $TIMEOUT_KILL $REFRESH_KEEPER_SESSION);
use vars qw($LOG $log_mode $log_crit $log_facility);
use vars qw($openlog_option $syslog_err_priority $syslog_info_priority);
use constant {
LOG_ESXD_ERROR => 1,
LOG_ESXD_INFO => 2
};
require '/etc/centreon/centreon_esxd.pm';
require $libpath . '/esxd-common.pm';
require $libpath . '/command-cpuhost.pm';
require $libpath . '/command-cpuvm.pm';
require $libpath . '/command-countvmhost.pm';
require $libpath . '/command-datastoreio.pm';
require $libpath . '/command-datastoreshost.pm';
require $libpath . '/command-datastoresvm.pm';
require $libpath . '/command-datastoreusage.pm';
require $libpath . '/command-getmap.pm';
require $libpath . '/command-healthhost.pm';
require $libpath . '/command-listdatastore.pm';
require $libpath . '/command-listhost.pm';
require $libpath . '/command-listnichost.pm';
require $libpath . '/command-maintenancehost.pm';
require $libpath . '/command-memhost.pm';
require $libpath . '/command-memvm.pm';
require $libpath . '/command-nethost.pm';
require $libpath . '/command-snapshotvm.pm';
require $libpath . '/command-statushost.pm';
require $libpath . '/command-swaphost.pm';
require $libpath . '/command-toolsvm.pm';
our $VERSION = "1.3";
our $session_id;
our %sockets = ();
our %child_proc;
our %return_child;
our $vsphere_connected = 0;
our $last_time_vsphere;
our $keeper_session_time;
our $last_time_check;
our $perfmanager_view;
our %perfcounter_cache;
our %perfcounter_cache_reverse;
our $perfcounter_refreshrate = 20;
our $perfcounter_speriod = -1;
our $stop = 0;
our $counter_request_id = 0;
our $child_vpshere_pid;
our $read_select;
our $session1;
our $counter = 0;
our $global_id;
our $whoaim; # to know which vsphere to connect
our %filenos;
our $openlog_option;
our $syslog_err_priority;
our $syslog_info_priority;
if ($ENV{ESX_SYSLOGD_LOAD} == 1) {
require $libpath . '/esxd-syslog.pm';
}
our %ERRORS = ( "OK" => 0, "WARNING" => 1, "CRITICAL" => 2, "UNKNOWN" => 3, "PENDING" => 4);
our %MYERRORS = (0 => "OK", 1 => "WARNING", 3 => "CRITICAL", 7 => "UNKNOWN");
our %MYERRORS_MASK = ("CRITICAL" => 3, "WARNING" => 1, "UNKNOWN" => 7, "OK" => 0);
our %checks_descr = (
"healthhost" => {'arg' => \&healthhost_check_args, 'compute' => \&healthhost_compute_args, 'exec' => \&healthhost_do},
"datastoreusage" => {'arg' => \&datastoreusage_check_args, 'compute' => \&datastoreusage_compute_args, 'exec' => \&datastoreusage_do},
"datastoreio" => {'arg' => \&datastoreio_check_args, 'compute' => \&datastoreio_compute_args, 'exec' => \&datastoreio_do},
"maintenancehost" => {'arg' => \&maintenancehost_check_args, 'compute' => \&maintenancehost_compute_args, 'exec' => \&maintenancehost_do},
"statushost" => {'arg' => \&statushost_check_args, 'compute' => \&statushost_compute_args, 'exec' => \&statushost_do},
"cpuhost" => {'arg' => \&cpuhost_check_args, 'compute' => \&cpuhost_compute_args, 'exec' => \&cpuhost_do},
"nethost" => {'arg' => \&nethost_check_args, 'compute' => \&nethost_compute_args, 'exec' => \&nethost_do},
"datastoreshost" => {'arg' => \&datastoreshost_check_args, 'compute' => \&datastoreshost_compute_args, 'exec' => \&datastoreshost_do},
"memhost" => {'arg' => \&memhost_check_args, 'compute' => \&memhost_compute_args, 'exec' => \&memhost_do},
"swaphost" => {'arg' => \&swaphost_check_args, 'compute' => \&swaphost_compute_args, 'exec' => \&swaphost_do},
"countvmhost" => {'arg' => \&countvmhost_check_args, 'compute' => \&countvmhost_compute_args, 'exec' => \&countvmhost_do},
"cpuvm" => {'arg' => \&cpuvm_check_args, 'compute' => \&cpuvm_compute_args, 'exec' => \&cpuvm_do},
"toolsvm" => {'arg' => \&toolsvm_check_args, 'compute' => \&toolsvm_compute_args, 'exec' => \&toolsvm_do},
"snapshotvm" => {'arg' => \&snapshotvm_check_args, 'compute' => \&snapshotvm_compute_args, 'exec' => \&snapshotvm_do},
"datastoresvm" => {'arg' => \&datastoresvm_check_args, 'compute' => \&datastoresvm_compute_args, 'exec' => \&datastoresvm_do},
"memvm" => {'arg' => \&memvm_check_args, 'compute' => \&memvm_compute_args, 'exec' => \&memvm_do},
"listhost" => {'arg' => \&listhost_check_args, 'compute' => \&listhost_compute_args, 'exec' => \&listhost_do},
"listdatastore" => {'arg' => \&listdatastore_check_args, 'compute' => \&listdatastore_compute_args, 'exec' => \&listdatastore_do},
"listnichost" => {'arg' => \&listnichost_check_args, 'compute' => \&listnichost_compute_args, 'exec' => \&listnichost_do},
"getmap" => {'arg' => \&getmap_check_args, 'compute' => \&getmap_compute_args, 'exec' => \&getmap_do}
);
sub catch_zap_term {
writeLogFile(LOG_ESXD_INFO, "$$ Receiving order to stop...\n");
$stop = 1;
}
sub REAPER {
my $child_pid;
while (($child_pid = waitpid(-1, &WNOHANG)) > 0) {
$return_child{$child_pid} = {'status' => 1, 'rtime' => time()};
}
$SIG{CHLD} = \&REAPER;
}
sub verify_child {
my $progress = 0;
my $handle_writer_pipe = ${$vsphere_server{$whoaim}->{'writer_one'}};
# Verify process
foreach (keys %child_proc) {
# Check ctime
if (time() - $child_proc{$_}->{'ctime'} > $TIMEOUT) {
my $handle = ${$child_proc{$_}->{'reading'}};
print $handle_writer_pipe "$_|-1|Timeout Process.\n";
kill('INT', $child_proc{$_}->{'pid'});
$read_select->remove($handle);
close $handle;
delete $child_proc{$_};
} else {
$progress++;
}
}
# Clean old hash CHILD (security)
foreach (keys %return_child) {
if (time() - $return_child{$_}->{'rtime'} > 600) {
writeLogFile(LOG_ESXD_INFO, "Clean Old return_child list = " . $_ . "\n");
delete $return_child{$_};
}
}
return $progress;
}
sub vsphere_handler {
my $timeout_process;
my $handle_reader_pipe = ${$vsphere_server{$whoaim}->{'reader_two'}};
my $fileno_reader = fileno($handle_reader_pipe);
my $handle_writer_pipe = ${$vsphere_server{$whoaim}->{'writer_one'}};
$read_select = new IO::Select();
$read_select->add($handle_reader_pipe);
while (1) {
my $progress = verify_child();
#####
# Manage ending
#####
if ($stop && $timeout_process > $TIMEOUT_KILL) {
writeLogFile(LOG_ESXD_ERROR, "'$whoaim' Kill child not gently.\n");
foreach (keys %child_proc) {
kill('INT', $child_proc{$_}->{'pid'});
}
$progress = 0;
}
if ($stop && !$progress) {
if ($vsphere_connected) {
eval {
$session1->logout();
};
}
print $handle_writer_pipe "STOPPED|$whoaim\n";
exit (0);
}
###
# Manage vpshere connection
###
if (defined($last_time_vsphere) && defined($last_time_check) && $last_time_vsphere < $last_time_check) {
writeLogFile(LOG_ESXD_ERROR, "'$whoaim' Deconnect\n");
$vsphere_connected = 0;
eval {
$session1->logout();
};
}
if ($vsphere_connected == 0) {
if (!connect_vsphere($vsphere_server{$whoaim}->{'url'}, $vsphere_server{$whoaim}->{'username'}, $vsphere_server{$whoaim}->{'password'})) {
writeLogFile(LOG_ESXD_INFO, "'$whoaim' Vsphere connection ok\n");
writeLogFile(LOG_ESXD_INFO, "'$whoaim' Create perf counters cache in progress\n");
if (!cache_perf_counters()) {
$last_time_vsphere = time();
$keeper_session_time = time();
$vsphere_connected = 1;
writeLogFile(LOG_ESXD_INFO, "'$whoaim' Create perf counters cache done\n");
}
}
}
###
# Manage session time
###
if (defined($keeper_session_time) && (time() - $keeper_session_time) > ($REFRESH_KEEPER_SESSION * 60)) {
my $stime;
eval {
$stime = $session1->get_service_instance()->CurrentTime();
$keeper_session_time = time();
};
if ($@) {
writeLogFile(LOG_ESXD_ERROR, "$@");
writeLogFile(LOG_ESXD_ERROR, "'$whoaim' Ask a new connection\n");
# Ask a new connection
$last_time_check = time();
} else {
writeLogFile(LOG_ESXD_INFO, "'$whoaim' Get current time = " . Data::Dumper::Dumper($stime));
}
}
my $data_element;
my @rh_set;
if ($vsphere_connected == 0) {
sleep(5);
}
if ($stop == 0) {
@rh_set = $read_select->can_read(30);
} else {
sleep(1);
$timeout_process++;
@rh_set = $read_select->can_read(0);
}
foreach my $rh (@rh_set) {
if (fileno($rh) == $fileno_reader && !$stop) {
$data_element = <$rh>;
chomp $data_element;
if ($data_element =~ /^STOP$/) {
$stop = 1;
$timeout_process = 0;
next;
}
my ($id) = split(/\|/, $data_element);
if ($vsphere_connected) {
writeLogFile(LOG_ESXD_INFO, "vpshere '$whoaim' handler asking: $data_element\n");
$child_proc{$id} = {'ctime' => time()};
my $reader;
my $writer;
pipe($reader, $writer);
$writer->autoflush(1);
$read_select->add($reader);
$child_proc{$id}->{'reading'} = \*$reader;
$child_proc{$id}->{'pid'} = fork;
if (!$child_proc{$id}->{'pid'}) {
# Child
close $reader;
open STDOUT, '>&', $writer;
# Can't print on stdout
$log_mode = 1 if ($log_mode == 0);
my ($id, $name, @args) = split /\|/, $data_element;
$global_id = $id;
$checks_descr{$name}->{'exec'}($checks_descr{$name}->{'compute'}(@args));
exit(0);
} else {
# Parent
close $writer;
}
} else {
print $handle_writer_pipe "$id|-1|Vsphere connection error.\n";
}
} else {
# Read pipe
my $output = <$rh>;
$read_select->remove($rh);
close $rh;
$output =~ s/^(.*?)\|//;
my $lid = $1;
if ($output =~ /^-1/) {
$last_time_check = $child_proc{$lid}->{'ctime'};
}
chomp $output;
print $handle_writer_pipe "$lid|$output\n";
delete $return_child{$child_proc{$lid}->{'pid'}};
delete $child_proc{$lid};
}
}
}
}
$SIG{TERM} = \&catch_zap_term;
$SIG{CHLD} = \&REAPER;
if ($log_mode == 1) {
open my $centesx_fh, '>>', $LOG;
open STDOUT, '>&', $centesx_fh;
open STDERR, '>&', $centesx_fh;
}
if ($log_mode == 2) {
openlog($0, $openlog_option, $log_facility);
}
my $server = IO::Socket::INET->new( Proto => "tcp",
LocalPort => $port,
Listen => SOMAXCONN,
Reuse => 1);
if (!$server) {
writeLogFile(LOG_ESXD_ERROR, "Can't setup server: $!\n");
exit(1);
}
##
# Create childs
##
foreach (keys %vsphere_server) {
my ($reader_pipe_one, $writer_pipe_one);
my ($reader_pipe_two, $writer_pipe_two);
$whoaim = $_;
pipe($reader_pipe_one, $writer_pipe_one);
pipe($reader_pipe_two, $writer_pipe_two);
$writer_pipe_one->autoflush(1);
$writer_pipe_two->autoflush(1);
$vsphere_server{$whoaim}->{'reader_one'} = \*$reader_pipe_one;
$vsphere_server{$whoaim}->{'writer_one'} = \*$writer_pipe_one;
$vsphere_server{$whoaim}->{'reader_two'} = \*$reader_pipe_two;
$vsphere_server{$whoaim}->{'writer_two'} = \*$writer_pipe_two;
$child_vpshere_pid = fork();
if (!$child_vpshere_pid) {
close $vsphere_server{$whoaim}->{'reader_one'};
close $vsphere_server{$whoaim}->{'writer_two'};
vsphere_handler();
exit(0);
}
$vsphere_server{$whoaim}->{'running'} = 1;
close $vsphere_server{$whoaim}->{'writer_one'};
close $vsphere_server{$whoaim}->{'reader_two'};
}
$read_select = new IO::Select();
$read_select->add($server);
foreach (keys %vsphere_server) {
$filenos{fileno(${$vsphere_server{$_}->{'reader_one'}})} = 1;
$read_select->add(${$vsphere_server{$_}->{'reader_one'}});
}
my $socket_fileno = fileno($server);
writeLogFile(LOG_ESXD_INFO, "[Server accepting clients]\n");
while (1) {
my @rh_set = $read_select->can_read(15);
if ($stop == 1) {
foreach (keys %vsphere_server) {
writeLogFile(LOG_ESXD_INFO, "Send STOP command to '$_' child.\n");
my $writer_handle = $vsphere_server{$_}->{'writer_two'};
print $writer_handle "STOP\n";
}
$stop = 2;
}
foreach my $rh (@rh_set) {
my $current_fileno = fileno($rh);
if (!$stop && $current_fileno == $socket_fileno) {
my $client;
# Connect to accept
$client = $rh->accept();
$client->autoflush(1);
$counter++;
$sockets{fileno($client)} = {"obj" => \$client, "ctime" => time(), "counter" => $counter};
$read_select->add($client);
next;
} elsif (defined($filenos{$current_fileno})) {
# Return to read
my $data_element = <$rh>;
chomp $data_element;
if ($data_element =~ /^STOPPED/) {
# We have to wait all childs
my ($name, $which_one) = split(/\|/, $data_element);
writeLogFile(LOG_ESXD_INFO, "Thread vsphere '$which_one' has stopped\n");
$vsphere_server{$which_one}->{'running'} = 0;
my $to_stop_or_not = 1;
foreach (keys %vsphere_server) {
$to_stop_or_not = 0 if ($vsphere_server{$_}->{'running'} == 1);
}
if ($to_stop_or_not == 1) {
# We quit
writeLogFile(LOG_ESXD_INFO, "Quit main process\n");
exit(0);
}
next;
}
my @results = split(/\|/, $data_element);
my ($id, $counter) = split(/\./, $results[0]);
if (!defined($sockets{$id}) || $counter != $sockets{$id}->{'counter'}) {
writeLogFile(LOG_ESXD_INFO, "Too much time to get response.\n");
next;
}
writeLogFile(LOG_ESXD_INFO, "response = $data_element\n");
$data_element =~ s/^.*?\|//;
${$sockets{$id}->{'obj'}}->send($data_element . "\n");
$read_select->remove(${$sockets{$id}->{"obj"}});
close ${$sockets{$id}->{"obj"}};
delete $sockets{$id};
} else {
# Socket
my $line = <$rh>;
if (defined($line) && $line ne "") {
chomp $line;
my ($name, $vsphere_name, @args) = split /\|/, $line;
if ($name eq 'stats') {
stats_info($rh, $current_fileno, \@args);
next;
}
if (!defined($checks_descr{$name})) {
response_client1($rh, $current_fileno, "3|Unknown method name '$name'\n");
next;
}
if ($checks_descr{$name}->{'arg'}(@args)) {
response_client1($rh, $current_fileno, "3|Params error '$name'\n");
next;
}
$vsphere_name = 'default' if (!defined($vsphere_name) || $vsphere_name eq '');
if (!defined($vsphere_server{$vsphere_name})) {
response_client1($rh, $current_fileno, "3|Vsphere name unknown\n");
next;
}
my $tmp_handle = ${$vsphere_server{$vsphere_name}->{'writer_two'}};
print $tmp_handle $current_fileno . "." . $sockets{$current_fileno}->{'counter'} . "|$name|" . join('|', @args) . "\n";
} else {
response_client1($rh, $current_fileno, "3|Need arguments\n");
}
}
}
# Verify socket
foreach (keys %sockets) {
if (time() - $sockets{$_}->{'ctime'} > $TIMEOUT) {
writeLogFile(LOG_ESXD_INFO, "Timeout returns.\n");
${$sockets{$_}->{'obj'}}->send("3|TIMEOUT\n");
$read_select->remove(${$sockets{$_}->{"obj"}});
close ${$sockets{$_}->{"obj"}};
delete $sockets{$_};
}
}
}
exit(0);