472 lines
15 KiB
Perl
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);
|