centreon-plugins/connectors/vmware/centreonesxd.pm

583 lines
23 KiB
Perl

#!/usr/bin/perl -w
package centreon::script::centreonesxd;
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;
use centreon::script;
use centreon::esxd::common;
BEGIN {
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0;
}
use base qw(centreon::script);
use vars qw(%centreonesxd_config);
my $VERSION = "1.5.3";
my %handlers = (TERM => {}, HUP => {}, CHLD => {});
my @load_modules = ('centreon::esxd::cmdcountvmhost',
'centreon::esxd::cmdcpuhost',
'centreon::esxd::cmdcpuvm',
'centreon::esxd::cmddatastoreio',
'centreon::esxd::cmddatastoreiops',
'centreon::esxd::cmddatastoreshost',
'centreon::esxd::cmddatastoresnapshots',
'centreon::esxd::cmddatastoresvm',
'centreon::esxd::cmddatastoreusage',
'centreon::esxd::cmdgetmap',
'centreon::esxd::cmdhealthhost',
'centreon::esxd::cmdlimitvm',
'centreon::esxd::cmdlistdatastore',
'centreon::esxd::cmdlisthost',
'centreon::esxd::cmdlistnichost',
'centreon::esxd::cmdmemhost',
'centreon::esxd::cmdmaintenancehost',
'centreon::esxd::cmdmemvm',
'centreon::esxd::cmdnethost',
'centreon::esxd::cmdsnapshotvm',
'centreon::esxd::cmdstatushost',
'centreon::esxd::cmdswaphost',
'centreon::esxd::cmdswapvm',
'centreon::esxd::cmdthinprovisioningvm',
'centreon::esxd::cmdtoolsvm',
'centreon::esxd::cmduptimehost'
);
sub new {
my $class = shift;
my $self = $class->SUPER::new("centreonesxd",
centreon_db_conn => 0,
centstorage_db_conn => 0,
noconfig => 1
);
bless $self, $class;
$self->add_options(
"config-extra=s" => \$self->{opt_extra},
);
%{$self->{centreonesxd_default_config}} =
(
credstore_use => 0,
credstore_file => '/root/.vmware/credstore/vicredentials.xml',
timeout_vsphere => 60,
timeout => 60,
timeout_kill => 30,
refresh_keeper_session => 15,
port => 5700,
datastore_state_error => 'UNKNOWN',
vm_state_error => 'UNKNOWN',
host_state_error => 'UNKNOWN',
vsphere_server => {
#'default' => {'url' => 'https://XXXXXX/sdk',
# 'username' => 'XXXXX',
# 'password' => 'XXXXX'},
#'testvc' => {'url' => 'https://XXXXXX/sdk',
# 'username' => 'XXXXX',
# 'password' => 'XXXXXX'}
}
);
$self->{session_id} = undef;
$self->{sockets} = {};
$self->{child_proc} = {};
$self->{return_child} = {};
$self->{vsphere_connected} = 0;
$self->{last_time_vsphere} = undef;
$self->{keeper_session_time} = undef;
$self->{last_time_check} = undef;
$self->{perfmanager_view} = undef;
$self->{perfcounter_cache} = {};
$self->{perfcounter_cache_reverse} = {};
$self->{perfcounter_refreshrate} = 20;
$self->{perfcounter_speriod} = -1;
$self->{stop} = 0;
$self->{counter_request_id} = 0;
$self->{child_vpshere_pid} = undef;
$self->{read_select} = undef;
$self->{session1} = undef;
$self->{counter} = 0;
$self->{global_id} = undef;
$self->{whoaim} = undef; # to know which vsphere to connect
$self->{separatorin} = '~';
$self->{filenos} = {};
$self->{module_date_parse_loaded} = 0;
$self->{modules_registry} = {};
return $self;
}
sub init {
my $self = shift;
$self->SUPER::init();
# redefine to avoid out when we try modules
$SIG{__DIE__} = undef;
if (!defined($self->{opt_extra})) {
$self->{opt_extra} = "/etc/centreon/centreon_esxd.pm";
}
if (-f $self->{opt_extra}) {
require $self->{opt_extra};
} else {
$self->{logger}->writeLogInfo("Can't find extra config file $self->{opt_extra}");
}
$self->{centreonesxd_config} = {%{$self->{centreonesxd_default_config}}, %centreonesxd_config};
##### Load modules
$self->load_module(@load_modules);
##### credstore check #####
if (defined($self->{centreonesxd_config}->{credstore_use}) && defined($self->{centreonesxd_config}->{credstore_file}) &&
$self->{centreonesxd_config}->{credstore_use} == 1 && -e "$self->{centreonesxd_config}->{credstore_file}") {
eval 'require VMware::VICredStore';
if ($@) {
$self->{logger}->writeLogError("Could not load module VMware::VICredStore");
exit(1);
}
require VMware::VICredStore;
if (VMware::VICredStore::init(filename => $self->{centreonesxd_config}->{credstore_file}) == 0) {
$self->{logger}->writeLogError("Credstore init failed: $@");
exit(1);
}
###
# Get password
###
foreach (keys %{$self->{centreonesxd_config}->{vsphere_server}}) {
my $lpassword = VMware::VICredStore::get_password(server => $_, username => $self->{centreonesxd_config}->{vsphere_server}->{$_}->{username});
if (!defined($lpassword)) {
$self->{logger}->writeLogError("Can't get password for couple host='" . $_ . "', username='" . $self->{centreonesxd_config}->{vsphere_server}->{$_}->{username} . "' : $@");
exit(1);
}
$self->{centreonesxd_config}->{vsphere_server}->{$_}->{password} = $lpassword;
}
}
eval 'require Date::Parse';
if (!$@) {
$self->{module_date_parse_loaded} = 1;
require Date::Parse;
}
$self->set_signal_handlers;
}
sub set_signal_handlers {
my $self = shift;
$SIG{TERM} = \&class_handle_TERM;
$handlers{TERM}->{$self} = sub { $self->handle_TERM() };
$SIG{HUP} = \&class_handle_HUP;
$handlers{HUP}->{$self} = sub { $self->handle_HUP() };
$SIG{CHLD} = \&class_handle_CHLD;
$handlers{CHLD}->{$self} = sub { $self->handle_CHLD() };
}
sub class_handle_TERM {
foreach (keys %{$handlers{TERM}}) {
&{$handlers{TERM}->{$_}}();
}
}
sub class_handle_HUP {
foreach (keys %{$handlers{HUP}}) {
&{$handlers{HUP}->{$_}}();
}
}
sub class_handle_CHLD {
foreach (keys %{$handlers{CHLD}}) {
&{$handlers{CHLD}->{$_}}();
}
}
sub handle_TERM {
my $self = shift;
$self->{logger}->writeLogInfo("$$ Receiving order to stop...");
$self->{stop} = 1;
}
sub handle_HUP {
my $self = shift;
$self->{logger}->writeLogInfo("$$ Receiving order to reload...");
# TODO
}
sub handle_CHLD {
my $self = shift;
my $child_pid;
while (($child_pid = waitpid(-1, &WNOHANG)) > 0) {
$self->{return_child}{$child_pid} = {status => 1, rtime => time()};
}
$SIG{CHLD} = \&class_handle_CHLD;
}
sub print_response {
my $self = shift;
print $self->{global_id} . "|" . $_[0];
}
sub load_module {
my $self = shift;
for (@_) {
(my $file = "$_.pm") =~ s{::}{/}g;
require $file;
my $obj = $_->new($self->{logger}, $self);
$self->{modules_registry}->{$obj->getCommandName()} = $obj;
}
}
sub verify_child {
my $self = shift;
my $progress = 0;
my $handle_writer_pipe = ${$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_one}};
# Verify process
foreach (keys %{$self->{child_proc}}) {
# Check ctime
if (time() - $self->{child_proc}->{$_}->{ctime} > $self->{centreonesxd_config}->{timeout}) {
my $handle = ${$self->{child_proc}->{$_}->{reading}};
print $handle_writer_pipe "$_|-1|Timeout Process.\n";
kill('INT', $self->{child_proc}->{$_}->{pid});
$self->{read_select}->remove($handle);
close $handle;
delete $self->{child_proc}->{$_};
} else {
$progress++;
}
}
# Clean old hash CHILD (security)
foreach (keys %{$self->{return_child}}) {
if (time() - $self->{return_child}->{$_}->{rtime} > 600) {
$self->{logger}->writeLogInfo("Clean Old return_child list = " . $_);
delete $self->{return_child}->{$_};
}
}
return $progress;
}
sub vsphere_handler {
my $self = shift;
my $timeout_process;
my $handle_reader_pipe = ${$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{reader_two}};
my $fileno_reader = fileno($handle_reader_pipe);
my $handle_writer_pipe = ${$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_one}};
$self->{read_select} = new IO::Select();
$self->{read_select}->add($handle_reader_pipe);
while (1) {
my $progress = $self->verify_child();
#####
# Manage ending
#####
if ($self->{stop} && $timeout_process > $self->{centreonesxd_config}->{timeout_kill}) {
$self->{logger}->writeLogError("'" . $self->{whoaim} . "' Kill child not gently.");
foreach (keys %{$self->{child_proc}}) {
kill('INT', $self->{child_proc}->{$_}->{pid});
}
$progress = 0;
}
if ($self->{stop} && !$progress) {
if ($self->{vsphere_connected}) {
eval {
$self->{session1}->logout();
};
}
print $handle_writer_pipe "STOPPED|$self->{whoaim}\n";
exit (0);
}
###
# Manage vpshere connection
###
if (defined($self->{last_time_vsphere}) && defined($self->{last_time_check}) && $self->{last_time_vsphere} < $self->{last_time_check}) {
$self->{logger}->writeLogError("'" . $self->{whoaim} . "' Disconnect");
$self->{vsphere_connected} = 0;
eval {
$self->{session1}->logout();
};
}
if ($self->{vsphere_connected} == 0) {
if (!centreon::esxd::common::connect_vsphere($self->{logger},
$self->{whoaim},
$self->{centreonesxd_config}->{timeout_vsphere},
\$self->{session1},
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{url},
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{username},
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{password})) {
$self->{logger}->writeLogInfo("'" . $self->{whoaim} . "' Vsphere connection ok");
$self->{logger}->writeLogInfo("'" . $self->{whoaim} . "' Create perf counters cache in progress");
if (!centreon::esxd::common::cache_perf_counters($self)) {
$self->{last_time_vsphere} = time();
$self->{keeper_session_time} = time();
$self->{vsphere_connected} = 1;
$self->{logger}->writeLogInfo("'" . $self->{whoaim} . "' Create perf counters cache done");
}
}
}
###
# Manage session time
###
if (defined($self->{keeper_session_time}) && (time() - $self->{keeper_session_time}) > ($self->{centreonesxd_config}->{refresh_keeper_session} * 60)) {
my $stime;
eval {
$stime = $self->{session1}->get_service_instance()->CurrentTime();
$self->{keeper_session_time} = time();
};
if ($@) {
$self->{logger}->writeLogError("$@");
$self->{logger}->writeLogError("'" . $self->{whoaim} . "' Ask a new connection");
# Ask a new connection
$self->{last_time_check} = time();
} else {
$self->{logger}->writeLogInfo("'" . $self->{whoaim} . "' Get current time = " . Data::Dumper::Dumper($stime));
}
}
my $data_element;
my @rh_set;
if ($self->{vsphere_connected} == 0) {
sleep(5);
}
if ($self->{stop} == 0) {
@rh_set = $self->{read_select}->can_read(30);
} else {
sleep(1);
$timeout_process++;
@rh_set = $self->{read_select}->can_read(0);
}
foreach my $rh (@rh_set) {
if (fileno($rh) == $fileno_reader && !$self->{stop}) {
$data_element = <$rh>;
chomp $data_element;
if ($data_element =~ /^STOP$/) {
$self->{stop} = 1;
$timeout_process = 0;
next;
}
my ($id) = split(/\Q$self->{separatorin}\E/, $data_element);
if ($self->{vsphere_connected}) {
$self->{logger}->writeLogInfo("vpshere '" . $self->{whoaim} . "' handler asking: $data_element");
$self->{child_proc}->{$id} = {ctime => time()};
my $reader;
my $writer;
pipe($reader, $writer);
$writer->autoflush(1);
$self->{read_select}->add($reader);
$self->{child_proc}->{$id}->{reading} = \*$reader;
$self->{child_proc}->{$id}->{pid} = fork;
if (!$self->{child_proc}->{$id}->{pid}) {
# Child
close $reader;
open STDOUT, '>&', $writer;
# Can't print on stdout
$self->{logger}->{log_mode} = 1 if ($self->{logger}->{log_mode} == 0);
my ($id, $name, @args) = split /\Q$self->{separatorin}\E/, $data_element;
$self->{global_id} = $id;
$self->{modules_registry}->{$name}->initArgs(@args);
$self->{modules_registry}->{$name}->run();
exit(0);
} else {
# Parent
close $writer;
}
} else {
print $handle_writer_pipe "$id|-1|Vsphere connection error.\n";
}
} else {
# Read pipe
my $output = <$rh>;
$self->{read_select}->remove($rh);
close $rh;
$output =~ s/^(.*?)\|//;
my $lid = $1;
if ($output =~ /^-1/) {
$self->{last_time_check} = $self->{child_proc}->{$lid}->{ctime};
}
chomp $output;
print $handle_writer_pipe "$lid|$output\n";
delete $self->{return_child}->{$self->{child_proc}->{$lid}->{pid}};
delete $self->{child_proc}->{$lid};
}
}
}
}
sub run {
my $self = shift;
$self->SUPER::run();
$self->{logger}->redirect_output();
$self->{logger}->writeLogDebug("centreonesxd launched....");
$self->{logger}->writeLogDebug("PID: $$");
my $server = IO::Socket::INET->new( Proto => "tcp",
LocalPort => $self->{centreonesxd_config}->{port},
Listen => SOMAXCONN,
Reuse => 1);
if (!$server) {
$self->{logger}->writeLogError("Can't setup server: $!");
exit(1);
}
##
# Create childs
##
foreach (keys %{$self->{centreonesxd_config}->{vsphere_server}}) {
my ($reader_pipe_one, $writer_pipe_one);
my ($reader_pipe_two, $writer_pipe_two);
$self->{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);
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{reader_one} = \*$reader_pipe_one;
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_one} = \*$writer_pipe_one;
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{reader_two} = \*$reader_pipe_two;
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_two} = \*$writer_pipe_two;
$self->{child_vpshere_pid} = fork();
if (!$self->{child_vpshere_pid}) {
close $self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{reader_one};
close $self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_two};
$self->vsphere_handler();
exit(0);
}
$self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{running} = 1;
close $self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{writer_one};
close $self->{centreonesxd_config}->{vsphere_server}->{$self->{whoaim}}->{reader_two};
}
$self->{read_select} = new IO::Select();
$self->{read_select}->add($server);
foreach (keys %{$self->{centreonesxd_config}->{vsphere_server}}) {
$self->{filenos}->{fileno(${$self->{centreonesxd_config}->{vsphere_server}->{$_}->{reader_one}})} = 1;
$self->{read_select}->add(${$self->{centreonesxd_config}->{vsphere_server}->{$_}->{reader_one}});
}
my $socket_fileno = fileno($server);
$self->{logger}->writeLogInfo("[Server accepting clients]");
while (1) {
my @rh_set = $self->{read_select}->can_read(15);
if ($self->{stop} == 1) {
# No childs
if (scalar(keys %{$self->{centreonesxd_config}->{vsphere_server}}) == 0) {
$self->{logger}->writeLogInfo("Quit main process");
exit(0);
}
foreach (keys %{$self->{centreonesxd_config}->{vsphere_server}}) {
$self->{logger}->writeLogInfo("Send STOP command to '$_' child.");
my $writer_handle = $self->{centreonesxd_config}->{vsphere_server}->{$_}->{writer_two};
print $writer_handle "STOP\n";
}
$self->{stop} = 2;
}
foreach my $rh (@rh_set) {
my $current_fileno = fileno($rh);
if (!$self->{stop} && $current_fileno == $socket_fileno) {
my $client;
# Connect to accept
$client = $rh->accept();
$client->autoflush(1);
$self->{counter}++;
$self->{sockets}->{fileno($client)} = {obj => \$client, ctime => time(), counter => $self->{counter}};
$self->{read_select}->add($client);
next;
} elsif (defined($self->{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);
$self->{logger}->writeLogInfo("Thread vsphere '$which_one' has stopped");
$self->{centreonesxd_config}->{vsphere_server}->{$which_one}->{running} = 0;
my $to_stop_or_not = 1;
foreach (keys %{$self->{centreonesxd_config}->{vsphere_server}}) {
$to_stop_or_not = 0 if ($self->{centreonesxd_config}->{vsphere_server}->{$_}->{running} == 1);
}
if ($to_stop_or_not == 1) {
# We quit
$self->{logger}->writeLogInfo("Quit main process");
exit(0);
}
next;
}
my @results = split(/\|/, $data_element);
my ($id, $counter) = split(/\./, $results[0]);
if (!defined($self->{sockets}->{$id}) || $counter != $self->{sockets}->{$id}->{counter}) {
$self->{logger}->writeLogInfo("Too much time to get response.");
next;
}
$self->{logger}->writeLogInfo("response = $data_element");
$data_element =~ s/^.*?\|//;
centreon::esxd::common::response_client2($self, $id, $data_element . "\n");
} else {
# Socket
my $line = <$rh>;
if (defined($line) && $line ne "") {
chomp $line;
my ($name, $vsphere_name, @args) = split /\Q$self->{separatorin}\E/, $line;
if ($name eq 'stats') {
centreon::esxd::common::stats_info($self, $rh, $current_fileno, \@args);
next;
}
if (!defined($self->{modules_registry}->{$name})) {
centreon::esxd::common::response_client1($self, $rh, $current_fileno, "3|Unknown method name '$name'\n");
next;
}
if ($self->{modules_registry}->{$name}->checkArgs(@args)) {
centreon::esxd::common::response_client1($self, $rh, $current_fileno, "3|Params error '$name'\n");
next;
}
$vsphere_name = 'default' if (!defined($vsphere_name) || $vsphere_name eq '');
if (!defined($self->{centreonesxd_config}->{vsphere_server}->{$vsphere_name})) {
centreon::esxd::common::response_client1($self, $rh, $current_fileno, "3|Vsphere name unknown\n");
next;
}
my $tmp_handle = ${$self->{centreonesxd_config}->{vsphere_server}->{$vsphere_name}->{writer_two}};
print $tmp_handle $current_fileno . "." . $self->{sockets}->{$current_fileno}->{counter} . $self->{separatorin} . $name . $self->{separatorin} . join($self->{separatorin}, @args) . "\n";
} else {
centreon::esxd::common::response_client1($self, $rh, $current_fileno, "3|Need arguments\n");
}
}
}
# Verify socket
foreach (keys %{$self->{sockets}}) {
if (time() - $self->{sockets}->{$_}->{ctime} > $self->{centreonesxd_config}->{timeout}) {
$self->{logger}->writeLogInfo("Timeout returns.");
centreon::esxd::common::response_client2($self, $_, "3|TIMEOUT\n");
}
}
}
}
1;
__END__