#!/usr/bin/perl
# nagios: -epn
#####################################################
#
# Proxmox VE cluster monitoring tool
#
#####################################################
#
# Requires Net::Proxmox::VE librairy:
# git://github.com/dpiquet/proxmox-ve-api-perl.git
#
# License Information:
# 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 3 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, see .
#
# Authors & contributors
# Damien PIQUET damien.piquet@iutbeziers.fr || piqudam@gmail.com
# Alexey Dvoryanchikov github.com/dvoryanchikov
#
use strict;
# use warnings;
use Net::Proxmox::VE;
use IO::Socket::SSL;
use Getopt::Long;
use Switch;
my $configurationFile = './pve-monitor.conf';
my $pluginVersion = '1.07';
my %status = (
'UNDEF' => -1,
'OK' => 0,
'WARNING' => 1,
'CRITICAL' => 2,
'UNKNOWN' => 3,
);
my %rstatus = reverse %status;
my %arguments = (
'nodes' => undef,
'storages' => undef,
'openvz' => undef,
'qemu' => undef,
'pools' => undef,
'qdisk' => undef,
'conf' => undef,
'show_help' => undef,
'show_version' => undef,
'timeout' => 5,
'debug' => undef,
'singlenode' => undef,
);
sub usage {
print "Usage: $0 [--nodes] [--storages] [--qemu] [--openvz] [--pools] [--perfdata] [--html] --conf \n";
print "\n";
print " --nodes\n";
print " Check the state of the cluster's members\n";
print " --storages\n";
print " Check the state of the cluster's storages\n";
print " --qemu\n";
print " Check the state of the cluster's Qemu virtual machines\n";
print " --openvz\n";
print " Check the state of the cluster's OpenVZ virtual machines\n";
print " [DEPRECATED] We keep it for pve-monitor < 1.07 back compat\n";
print " --containers\n";
print " Check the state of the cluster's containers (both openvz and lxc)\n";
print " --pools\n";
print " Check the state of the cluster's virtual machines and/or storages in defined pools\n";
print " --qdisk\n";
print " Check the state of the cluster's quorum disk\n";
print " --singlenode\n";
print " Consider there is no cluster, just a single node\n";
print " --perfdata\n";
print " Print nagios performance data for graphs (PNP4Nagios supported check_multi style) \n";
print " --html\n";
print " Replace linebreaks with
in output\n";
print " --debug\n";
print " Get more log output\n";
}
sub is_number {
($_[0] =~ m/^[0-9]+$/) ? return 1 : return 0;
}
GetOptions ("nodes" => \$arguments{nodes},
"storages" => \$arguments{storages},
"openvz" => \$arguments{openvz},
"containers" => \$arguments{openvz},
"qemu" => \$arguments{qemu},
"pools" => \$arguments{pools},
"qdisk" => \$arguments{qdisk},
"singlenode" => \$arguments{singlenode},
"perfdata" => \$arguments{perfdata},
"html" => \$arguments{html},
"conf=s" => \$arguments{conf},
'version|V' => \$arguments{show_version},
'help|h' => \$arguments{show_help},
'timeout|t=s' => \$arguments{timeout},
'debug' => \$arguments{debug},
);
print "Starting pve-monitor $pluginVersion\n"
if $arguments{debug};
print "Setting timeout to $arguments{timeout}\n"
if $arguments{debug};
if (defined $arguments{show_version}) {
print "$0 version $pluginVersion\n";
exit $status{OK};
}
if (defined $arguments{show_help}) {
usage();
exit $status{UNKNOWN};
}
if (! defined $arguments{conf}) {
usage();
exit $status{UNKNOWN};
}
# Arrays for objects to monitor
my @monitoredStorages;
my @monitoredNodes;
my @monitoredOpenvz;
my @monitoredQemus;
my @monitoredPools;
my $connected = 0;
my $host = undef;
my $username = undef;
my $password = undef;
my $realm = undef;
my $pve;
my $qdiskStatus = undef;
my %qdisk = (
id => undef,
name => undef,
estranged => undef,
cstate => undef,
status => $status{UNKNOWN},
);
my $readingObject = 0;
# Output option
my $br = "\n";
$br = "
" if (defined $arguments{html});
# Read the configuration file
if (! open FILE, "<", "$arguments{conf}") {
print "$!\n" if $arguments{debug};
print "Cannot load configuration file $arguments{conf} !\n";
exit $status{UNKNOWN};
}
while ( ) {
my $line = $_;
# Skip commented lines (starting with #)
next if $line =~ m/^#/i;
# we got an object definition here !
if ( $line =~ m/([\S]+)\s+([\S]+)\s+\{/i ) {
switch ($1) {
case "node" {
my $name = $2;
my $warnCpu = undef;
my $warnMem = undef;
my $warnDisk = undef;
my $critCpu = undef;
my $critMem = undef;
my $critDisk = undef;
my $nAddr = undef;
my $nPort = 8006;
my $nUser = undef;
my $nPwd = undef;
my $nRealm = 'pam';
my $warnMemAlloc = undef;
my $critMemAlloc = undef;
my $warnCpuAlloc = undef;
my $critCpuAlloc = undef;
$readingObject = 1;
while () {
my $objLine = $_;
next if ( $objLine =~ m/^(\s+)?#/ );
if ( $objLine =~ m/([\S]+)\s+([\S]+)(\s+([\S]+))?/i ) {
switch ($1) {
case "cpu" {
if ((is_number $2)and(is_number $4)) {
$warnCpu = $2;
$critCpu = $4;
}
else {
close(FILE);
print "Invalid CPU declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "cpu_alloc" {
if((is_number $2)and(is_number $4)) {
$warnCpuAlloc = $2;
$critCpuAlloc = $4;
}
else {
close(FILE);
print "Invalid CPU_ALLOC declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "mem" {
if ((is_number $2)and(is_number $4)) {
$warnMem = $2;
$critMem = $4;
}
else {
close(FILE);
print "Invalid MEM declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "disk" {
if ((is_number $2)and(is_number $4)) {
$warnDisk = $2;
$critDisk = $4;
}
else {
close(FILE);
print "Invalid DISK declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "mem_alloc" {
if ((is_number $2)and(is_number $4)) {
$warnMemAlloc = $2;
$critMemAlloc = $4;
}
else {
close(FILE);
print "Invalid MEM_ALLOC declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "address" {
$nAddr = $2;
}
case "port" {
if (is_number $2) {
$nPort = $2;
}
else {
close(FILE);
print "Invalid PORT declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "monitor_account" {
$nUser = $2;
}
case "monitor_password" {
$nPwd = $2;
}
case "realm" {
$nRealm = $2;
}
else {
close(FILE);
print "Invalid token $1 in $name definition !\n";
exit $status{UNKNOWN};
}
}
}
elsif ( $objLine =~ m/\}/i ) {
# check object requirements are met, save it, break
if (! defined $name ) {
close(FILE);
print "Invalid configuration !";
exit $status{UNKNOWN};
}
print "Loaded node $name\n"
if $arguments{debug};
$monitoredNodes[scalar(@monitoredNodes)] = ({
name => $name,
address => $nAddr,
port => $nPort,
username => $nUser,
realm => $nRealm,
password => $nPwd,
warn_cpu => $warnCpu,
warn_cpu_alloc => $warnCpuAlloc,
warn_mem => $warnMem,
warn_mem_alloc => $warnMemAlloc,
warn_disk => $warnDisk,
crit_cpu => $critCpu,
crit_cpu_alloc => $critCpuAlloc,
crit_mem => $critMem,
crit_mem_alloc => $critMemAlloc,
crit_disk => $critDisk,
cpu_status => $status{OK},
mem_status => $status{OK},
disk_status => $status{OK},
mem_alloc_status => $status{OK},
cpu_alloc_status => $status{OK},
alive => 0,
curmem => undef,
curdisk => undef,
curcpu => undef,
status => $status{UNDEF},
uptime => undef,
mem_alloc => 0,
maxmem => undef,
cpu_alloc => undef,
maxcpu => undef,
},
);
$readingObject = 0;
last;
}
else {
print "Invalid line " . chomp($objLine) . " at line $. !\n"
if $arguments{debug};
}
}
}
case "storage" {
my $name = $2;
my $warnDisk = undef;
my $critDisk = undef;
my $node = undef;
$readingObject = 1;
while () {
my $objLine = $_;
next if ( $objLine =~ m/^#/i );
if ( $objLine =~ m/([\S]+)\s+([\S]+)(\s+([\S]+))?/i ) {
switch ($1) {
case "disk" {
if ((is_number $2)and(is_number $4)) {
$warnDisk = $2;
$critDisk = $4;
}
else {
close(FILE);
print "Invalid DISK declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "node" {
$node = $2;
}
else {
close(FILE);
print "Invalid token $1 " .
"in $name definition !\n";
exit $status{UNKNOWN};
}
}
}
elsif ( $objLine =~ m/\}/i ) {
# check object requirements are met, save it, break
if (! defined $name ) {
close(FILE);
print "Invalid configuration !";
exit $status{UNKNOWN};
}
if (! defined $node ) {
close(FILE);
print "Invalid configuration, " .
"missing node in $name storage definition !\n";
exit $status{UNKNOWN};
}
print "Loaded storage $name\n"
if $arguments{debug};
$monitoredStorages[scalar(@monitoredStorages)] = ({
name => $name,
node => $node,
warn_disk => $warnDisk,
crit_disk => $critDisk,
curdisk => undef,
disk_status => $status{OK},
status => $status{UNDEF},
},
);
$readingObject = 0;
last;
}
}
}
case /openvz|lxc|container/ {
my $name = $2;
my $warnCpu = undef;
my $warnMem = undef;
my $warnDisk = undef;
my $critCpu = undef;
my $critMem = undef;
my $critDisk = undef;
$readingObject = 1;
while () {
my $objLine = $_;
next if ( $objLine =~ m/^#/i );
if ( $objLine =~ m/([\S]+)\s+([\S]+)\s+([\S]+)/i ) {
switch ($1) {
case "cpu" {
if ((is_number $2) and (is_number $3)) {
$warnCpu = $2;
$critCpu = $3;
}
else {
close(FILE);
print "Invalid CPU declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "mem" {
if ((is_number $2) and (is_number $3)) {
$warnMem = $2;
$critMem = $3;
}
else {
close(FILE);
print "Invalid MEM declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "disk" {
if ((is_number $2) and (is_number $3)) {
$warnDisk = $2;
$critDisk = $3;
}
else {
close(FILE);
print "Invalid DISK declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
else {
close(FILE);
print "Invalid token $1 " .
"in $name definition !\n";
exit $status{UNKNOWN};
}
}
}
elsif ( $objLine =~ m/\}/i ) {
# check object requirements are met, save it, break
if (! defined $name ) {
close(FILE);
print "Invalid configuration !";
exit $status{UNKNOWN};
}
print "Loaded openvz $name\n"
if $arguments{debug};
$monitoredOpenvz[scalar(@monitoredOpenvz)] = ({
name => $name,
warn_cpu => $warnCpu,
warn_mem => $warnMem,
warn_disk => $warnDisk,
crit_cpu => $critCpu,
crit_mem => $critMem,
crit_disk => $critDisk,
alive => undef,
curmem => undef,
curdisk => undef,
curcpu => undef,
cpu_status => $status{OK},
mem_status => $status{OK},
disk_status => $status{OK},
status => $status{UNDEF},
uptime => undef,
node => undef,
},
);
$readingObject = 0;
last;
}
}
}
case "qemu" {
my $name = $2;
my $warnCpu = undef;
my $warnMem = undef;
my $warnDisk = undef;
my $critCpu = undef;
my $critMem = undef;
my $critDisk = undef;
$readingObject = 1;
while () {
my $objLine = $_;
next if ( $objLine =~ m/^#/i );
if ( $objLine =~ m/([\S]+)\s+([\S]+)\s+([\S]+)/i ) {
switch ($1) {
case "cpu" {
if ((is_number $2)and(is_number $3)) {
$warnCpu = $2;
$critCpu = $3;
}
else {
close(FILE);
print "Invalid CPU declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "mem" {
if ((is_number $2)and(is_number $3)) {
$warnMem = $2;
$critMem = $3;
}
else {
close(FILE);
print "Invalid MEM declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "disk" {
if ((is_number $2)and(is_number $3)) {
$warnDisk = $2;
$critDisk = $3;
}
else {
close(FILE);
print "Invalid DISK declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
else {
close(FILE);
print "Invalid token $1 " .
"in $name definition !\n";
exit $status{UNKNOWN};
}
}
}
elsif ( $objLine =~ m/\}/i ) {
# check object requirements are met, save it, break
if (! defined $name ) {
close(FILE);
print "Invalid configuration !\n";
exit $status{UNKNOWN};
}
print "Loaded qemu $name\n"
if $arguments{debug};
$monitoredQemus[scalar(@monitoredQemus)] = (
{
name => $name,
warn_cpu => $warnCpu,
warn_mem => $warnMem,
warn_disk => $warnDisk,
crit_cpu => $critCpu,
crit_mem => $critMem,
crit_disk => $critDisk,
alive => undef,
curmem => undef,
curdisk => undef,
curcpu => undef,
cpu_status => $status{OK},
mem_status => $status{OK},
disk_status => $status{OK},
status => $status{UNDEF},
uptime => undef,
node => undef,
},
);
$readingObject = 0;
last;
}
}
}
case "pool" {
my $name = $2;
my $warnCpu = undef;
my $warnMem = undef;
my $warnDisk = undef;
my $critCpu = undef;
my $critMem = undef;
my $critDisk = undef;
$readingObject = 1;
while () {
my $objLine = $_;
next if ( $objLine =~ m/^#/i );
if ( $objLine =~ m/([\S]+)\s+([\S]+)\s+([\S]+)/i ) {
switch ($1) {
case "cpu" {
if ((is_number $2)and(is_number $3)) {
$warnCpu = $2;
$critCpu = $3;
}
else {
close(FILE);
print "Invalid CPU declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "mem" {
if ((is_number $2)and(is_number $3)) {
$warnMem = $2;
$critMem = $3;
}
else {
close(FILE);
print "Invalid MEM declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
case "disk" {
if ((is_number $2)and(is_number $3)) {
$warnDisk = $2;
$critDisk = $3;
}
else {
close(FILE);
print "Invalid DISK declaration " .
"in $name definition\n";
exit $status{UNKNOWN};
}
}
else {
close(FILE);
print "Invalid token $1 " .
"in $name definition !\n";
exit $status{UNKNOWN};
}
}
}
elsif ( $objLine =~ m/\}/i ) {
# check object requirements are met, save it, break
if (! defined $name ) {
close(FILE);
print "Invalid configuration !\n";
exit $status{UNKNOWN};
}
print "Loaded pool $name\n"
if $arguments{debug};
$monitoredPools[scalar(@monitoredPools)] = (
{
name => $name,
warn_cpu => $warnCpu,
warn_mem => $warnMem,
warn_disk => $warnDisk,
crit_cpu => $critCpu,
crit_mem => $critMem,
crit_disk => $critDisk,
},
);
$readingObject = 0;
last;
}
}
}
else {
close(FILE);
print "Invalid token $1 " .
"in configuration file $arguments{conf} !\n";
exit $status{UNKNOWN};
}
}
}
}
close(FILE);
if ( $readingObject ) {
print "Invalid configuration ! (Probably missing '}' ) \n";
exit $status{UNKNOWN};
}
for($a = 0; $a < scalar(@monitoredNodes); $a++) {
my $host = $monitoredNodes[$a]->{address} or next;
my $port = $monitoredNodes[$a]->{port} or next;
my $username = $monitoredNodes[$a]->{username} or next;
my $password = $monitoredNodes[$a]->{password} or next;
my $realm = $monitoredNodes[$a]->{realm} or next;
my $isClusterMember = 0;
print "Trying " . $host . "...\n"
if $arguments{debug};
$pve = Net::Proxmox::VE->new(
host => $host,
username => $username,
password => $password,
debug => $arguments{debug},
realm => $realm,
timeout => $arguments{timeout},
ssl_opts => {
SSL_verify_mode => SSL_VERIFY_NONE,
verify_hostname => 0
}
);
next unless $pve->login;
next unless $pve->check_login_ticket;
next unless $pve->api_version_check;
# Here we are connected, quit the loop
print "Successfully connected to " . $host . " !\n"
if $arguments{debug};
# skip cluster status checks if we are not in cluster
if (defined $arguments{singlenode}) {
print "Skipping cluster checks (--singlenode passed to command line)\n"
if $arguments{debug};
$connected = 1;
last;
}
# check if node is quorate, if it's not then
# we are probably on a dead node and data is irrelevant
my $cstatuses = $pve->get('/cluster/status');
foreach my $item( @$cstatuses ) {
switch ($item->{type}) {
case "node" {
# qdisks are also type "node"
if ($item->{qdisk} eq "1") {
print "Found qdisk $item->{name} in cluster\n"
if $arguments{debug};
$qdisk{id} = $item->{id};
$qdisk{name} = $item->{name};
$qdisk{estranged} = $item->{estranged}; # boolean value
$qdisk{cstate} = $item->{state}; # boolean value
}
elsif ($item->{local} eq "1") {
if ($item->{estranged} eq "0" || $item->{estranged} eq "") {
$isClusterMember = 1;
print "Node $item->{ip} is in cluster and seems sane. Using it to query cluster status\n"
if $arguments{debug};
}
else {
print "Node $item->{ip} is estranged ! Skipping it !\n"
if $arguments{debug};
}
}
}
default {
next;
}
}
}
if ($isClusterMember) {
$connected = 1;
last; # we queried a valid cluster member, quit the loop
}
}
if (! $connected ) {
print "Could not connect to any server !";
exit $status{UNKNOWN};
}
if (defined $arguments{qdisk}) {
my $statusStr = '';
if ( defined $qdisk{id} ) {
$qdisk{status} = $status{OK};
if ($qdisk{estranged} eq "1") {
$statusStr .= "Qdisk $qdisk{name} is estranged !\n";
$qdisk{status} += $status{WARNING};
}
if ($qdisk{cstate} eq "0") {
$statusStr .= "Qdisk $qdisk{name} is in invalid status !\n";
$qdisk{status} += $status{WARNING};
}
print "Qdisk $rstatus{$qdisk{status}}\n$statusStr";
exit $qdisk{status};
}
else {
print "No qdisk found in cluster !\n";
exit $status{UNKNOWN};
}
}
# list all ressources of the cluster
my $objects = $pve->get('/cluster/resources');
print "Found " . scalar(@$objects) . " objects:\n"
if $arguments{debug};
# loop the objects to find our pool definitions
if (defined $arguments{pools}) {
foreach my $item( @$objects ) {
next unless (defined $item->{pool});
# loop the pool array to see if that one is monitored
foreach my $mpool( @monitoredPools ) {
next unless ($item->{pool} eq $mpool->{name});
print "Found $mpool->{name} in resource list\n"
if $arguments{debug};
#get pool members
my $pool = $pve->get('/pools/' . $mpool->{name});
my $members = $pool->{members};
#fill monitored pool members not defined in config already
foreach my $member( @$members ) {
switch ($member->{type}) {
case /openvz|lxc/ {
unless (grep $_->{name} eq $member->{name}, @monitoredOpenvz) {
$monitoredOpenvz[scalar(@monitoredOpenvz)] = (
{
name => $member->{name},
warn_cpu => $mpool->{warn_cpu},
warn_mem => $mpool->{warn_mem},
warn_disk => $mpool->{warn_disk},
crit_cpu => $mpool->{crit_cpu},
crit_mem => $mpool->{crit_mem},
crit_disk => $mpool->{crit_disk},
alive => undef,
curmem => undef,
curdisk => undef,
curcpu => undef,
cpu_status => $status{OK},
mem_status => $status{OK},
disk_status => $status{OK},
status => $status{UNDEF},
uptime => undef,
node => undef,
pool => $mpool->{name},
},);
print "Loaded container " . $member->{name} . " from pool " . $mpool->{name} . "\n"
if $arguments{debug};
}
}
case "qemu" {
unless (grep $_->{name} eq $member->{name}, @monitoredQemus) {
$monitoredQemus[scalar(@monitoredQemus)] = (
{
name => $member->{name},
warn_cpu => $mpool->{warn_cpu},
warn_mem => $mpool->{warn_mem},
warn_disk => $mpool->{warn_disk},
crit_cpu => $mpool->{crit_cpu},
crit_mem => $mpool->{crit_mem},
crit_disk => $mpool->{crit_disk},
alive => undef,
curmem => undef,
curdisk => undef,
curcpu => undef,
cpu_status => $status{OK},
mem_status => $status{OK},
disk_status => $status{OK},
status => $status{UNDEF},
uptime => undef,
node => undef,
pool => $mpool->{name},
},);
print "Loaded qemu " . $member->{name} . " from pool " . $mpool->{name} . "\n"
if $arguments{debug};
}
}
case "storage" {
unless (grep $_->{name} eq $member->{storage}, @monitoredStorages) {
$monitoredStorages[scalar(@monitoredStorages)] = ({
name => $member->{storage},
node => $member->{node},
warn_disk => $mpool->{warn_disk},
crit_disk => $mpool->{crit_disk},
curdisk => undef,
disk_status => $status{OK},
status => $status{UNDEF},
pool => $mpool->{name},
},);
print "Loaded storage " . $member->{storage} . " from pool " . $mpool->{name} . "\n"
if $arguments{debug};
}
}
}
}
}
}
}
# loop the objects to compare our definitions with the current state of the cluster
foreach my $item( @$objects ) {
switch ($item->{type}) {
case "node" {
# loop the node array to see if that one is monitored
foreach my $mnode( @monitoredNodes ) {
next unless ($item->{node} eq $mnode->{name});
print "Found $mnode->{name} in resource list\n"
if $arguments{debug};
# if a node is down, many values are not set
if(defined $item->{uptime}) {
$mnode->{status} = $status{OK};
$mnode->{uptime} = $item->{uptime};
$mnode->{maxmem} = $item->{maxmem};
$mnode->{maxcpu} = $item->{maxcpu};
if ($item->{maxmem} > 0) {
my $curMem = $item->{mem} / $item->{maxmem} * 100;
$mnode->{curmem} = sprintf("%.2f", $curMem);
}
if ($item->{maxdisk} > 0) {
my $curDisk = $item->{disk} / $item->{maxdisk} * 100;
$mnode->{curdisk} = sprintf("%.2f", $curDisk);
}
if ($item->{maxcpu} > 0) {
my $curCpu = $item->{cpu} / $item->{maxcpu} * 100;
$mnode->{curcpu} = sprintf("%.2f", $curCpu);
}
}
else {
$mnode->{status} = -1;
$mnode->{uptime} = 0;
$mnode->{curmem} = 0;
$mnode->{curdisk} = 0;
$mnode->{curcpu} = 0;
}
last;
}
}
case "storage" {
foreach my $mstorage( @monitoredStorages ) {
next unless ($item->{storage} eq $mstorage->{name});
next unless ($item->{node} eq $mstorage->{node});
print "Found $mstorage->{name} in resource list\n"
if $arguments{debug};
if (defined $item->{disk} ) {
$mstorage->{status} = $status{OK};
if ($item->{maxdisk} > 0) {
my $curDisk = $item->{disk} / $item->{maxdisk} * 100;
$mstorage->{curdisk} = sprintf("%.2f", $curDisk);
}
}
else {
$mstorage->{status} = -1;
$mstorage->{curdisk} = 0;
}
last;
}
next;
}
case /openvz|lxc/ {
#loop monitored nodes to increase mem_hi_limit
foreach my $mnode( @monitoredNodes ) {
next unless $mnode->{name} eq $item->{node};
if (defined $item->{status}) {
if ($item->{status} eq "running") {
$mnode->{mem_alloc} += $item->{maxmem};
$mnode->{cpu_alloc} += $item->{maxcpu};
}
}
last;
}
foreach my $mopenvz( @monitoredOpenvz ) {
next unless ($item->{name} eq $mopenvz->{name});
print "Found $mopenvz->{name} in resource list\n"
if $arguments{debug};
if (defined $item->{status}) {
$mopenvz->{status} = $status{OK};
$mopenvz->{alive} = $item->{status};
$mopenvz->{uptime} = $item->{uptime};
$mopenvz->{node} = $item->{node};
if ($item->{maxmem} > 0) {
my $curMem = $item->{mem} / $item->{maxmem} * 100;
$mopenvz->{curmem} = sprintf("%.2f", $curMem);
}
if ($item->{maxdisk} > 0) {
my $curDisk = $item->{disk} / $item->{maxdisk} * 100;
$mopenvz->{curdisk} = sprintf("%.2f", $curDisk);
}
if ($item->{maxcpu} > 0) {
my $curCpu = $item->{cpu} / $item->{maxcpu} * 100;
$mopenvz->{curcpu} = sprintf("%.2f", $curCpu);
}
}
else {
$mopenvz->{alive} = "on dead node";
$mopenvz->{uptime} = 0;
$mopenvz->{curmem} = 0;
$mopenvz->{curdisk} = 0;
$mopenvz->{curcpu} = 0;
}
last;
}
next;
}
case "qemu" {
#loop monitored nodes to increase mem_hi_limit
foreach my $mnode( @monitoredNodes ) {
next unless $mnode->{name} eq $item->{node};
if (defined $item->{status}) {
if ($item->{status} eq "running") {
$mnode->{mem_alloc} += $item->{maxmem};
$mnode->{cpu_alloc} += $item->{maxcpu};
}
}
last;
}
foreach my $mqemu( @monitoredQemus ) {
next unless ($item->{name} eq $mqemu->{name});
print "Found $mqemu->{name} in resource list\n"
if $arguments{debug};
if(defined $item->{status}) {
$mqemu->{status} = $status{OK};
$mqemu->{alive} = $item->{status};
$mqemu->{uptime} = $item->{uptime};
$mqemu->{node} = $item->{node};
if ($item->{maxmem} > 0) {
my $maxMem = $item->{mem} / $item->{maxmem} * 100;
$mqemu->{curmem} = sprintf("%.2f", $maxMem);
}
if ($item->{maxdisk}) {
my $curDisk = $item->{disk} / $item->{maxdisk} * 100;
$mqemu->{curdisk} = sprintf("%.2f", $curDisk);
}
if ($item->{maxcpu} > 0) {
my $curCpu = $item->{cpu} / $item->{maxcpu} * 100;
$mqemu->{curcpu} = sprintf("%.2f", $curCpu);
}
}
else {
$mqemu->{alive} = "on dead node";
$mqemu->{uptime} = 0;
$mqemu->{curmem} = 0;
$mqemu->{curdisk} = 0;
$mqemu->{curcpu} = 0;
}
last;
}
next;
}
}
}
# Finally, loop the monitored objects arrays to report situation
my $totalScore = 0;
my $totalPerfData = "|";
if (defined $arguments{nodes}) {
my $statusScore = 0;
my $workingNodes = 0;
my $reportSummary = '';
foreach my $mnode( @monitoredNodes ) {
$statusScore += $mnode->{status};
if ($mnode->{status} ne $status{UNDEF}) {
# compute max memory usage
my $memAlloc = 0;
$memAlloc = sprintf("%.2f", $mnode->{mem_alloc} / $mnode->{maxmem} * 100)
if ($mnode->{maxmem} > 0);
my $cpuAlloc = 0;
$cpuAlloc = sprintf("%.2f", $mnode->{cpu_alloc} / $mnode->{maxcpu} * 100)
if ($mnode->{maxcpu} > 0);
if (defined $mnode->{warn_mem_alloc}) {
$mnode->{mem_alloc_status} = $status{WARNING}
if ($memAlloc > $mnode->{warn_mem_alloc});
}
if (defined $mnode->{crit_mem_alloc}) {
$mnode->{mem_alloc_status} = $status{CRITICAL}
if ($memAlloc > $mnode->{crit_mem_alloc});
}
if (defined $mnode->{warn_cpu_alloc}) {
$mnode->{cpu_alloc_status} = $status{WARNING}
if ($cpuAlloc > $mnode->{warn_cpu_alloc});
}
if (defined $mnode->{crit_cpu_alloc}) {
$mnode->{cpu_alloc_status} = $status{CRITICAL}
if ($cpuAlloc > $mnode->{crit_cpu_alloc});
}
if (defined $mnode->{warn_mem}) {
$mnode->{mem_status} = $status{WARNING}
if ($mnode->{curmem} > $mnode->{warn_mem});
}
if (defined $mnode->{crit_mem}) {
$mnode->{mem_status} = $status{CRITICAL}
if ($mnode->{curmem} > $mnode->{crit_mem});
}
if (defined $mnode->{warn_disk}) {
$mnode->{disk_status} = $status{WARNING}
if ($mnode->{curdisk} > $mnode->{warn_disk});
}
if (defined $mnode->{crit_disk}) {
$mnode->{disk_status} = $status{CRITICAL}
if ($mnode->{curdisk} > $mnode->{crit_disk});
}
if (defined $mnode->{warn_cpu}) {
$mnode->{cpu_status} = $status{WARNING}
if ($mnode->{curcpu} > $mnode->{warn_cpu});
}
if (defined $mnode->{crit_cpu}) {
$mnode->{crit_cpu} = $status{CRITICAL}
if ($mnode->{curcpu} > $mnode->{crit_cpu});
}
my $curNodeStatus = $mnode->{cpu_status} +
$mnode->{mem_status} +
$mnode->{disk_status} +
$mnode->{cpu_alloc_status} +
$mnode->{mem_alloc_status};
$curNodeStatus = $status{CRITICAL}
if ($curNodeStatus >= $status{UNKNOWN});
if ($mnode->{status} ne $status{UNDEF}) {
$reportSummary .=
"$mnode->{name} $rstatus{$curNodeStatus} : " .
"cpu $rstatus{$mnode->{cpu_status}} ($mnode->{curcpu}%), " .
"mem $rstatus{$mnode->{mem_status}} ($mnode->{curmem}%), " .
"disk $rstatus{$mnode->{disk_status}} ($mnode->{curdisk}%) " .
"cpu alloc $rstatus{$mnode->{cpu_alloc_status}} ($cpuAlloc%), " .
"mem alloc $rstatus{$mnode->{mem_alloc_status}} ($memAlloc%), " .
"uptime $mnode->{uptime}" . $br;
$workingNodes++
if $mnode->{status} eq $status{OK};
}
else {
$reportSummary .= "$mnode->{name} $rstatus{$status{CRITICAL}} : ".
"node is out of cluster (dead?)" . $br;
}
$statusScore += $curNodeStatus;
# Do not leave $statusScore at level unknown here
$statusScore++ if $statusScore eq $status{UNKNOWN};
}
else {
$reportSummary .= "$mnode->{name} " .
"is in status $rstatus{$status{UNKNOWN}}" . $br;
}
}
$statusScore = $status{CRITICAL}
if (( $statusScore > $status{UNKNOWN}) or ($statusScore < 0));
print "NODES $rstatus{$statusScore} $workingNodes / " .
scalar(@monitoredNodes) . " working nodes" . $br . $reportSummary;
$totalScore += $statusScore;
}; if (defined $arguments{storages}) {
my $statusScore = 0;
my $workingStorages = 0;
my $reportSummary = '';
my $perfData = '';
foreach my $mstorage( @monitoredStorages ) {
#Add pool name to output
#$mstorage->{name} .= "/" . $mstorage->{pool} if defined $mstorage->{pool};
if ($mstorage->{status} eq -1) {
$statusScore += $status{CRITICAL};
$reportSummary .= "$mstorage->{name} ($mstorage->{node}) " .
"$rstatus{$status{CRITICAL}}: " .
"storage is on a dead node" . $br;
}
elsif ($mstorage->{status} ne $status{UNKNOWN}) {
if (defined $mstorage->{warn_disk}) {
$mstorage->{disk_status} = $status{WARNING}
if $mstorage->{curdisk} > $mstorage->{warn_disk};
}
if (defined $mstorage->{crit_disk}) {
$mstorage->{disk_status} = $status{CRITICAL}
if $mstorage->{curdisk} > $mstorage->{crit_disk};
}
$reportSummary .= "$mstorage->{name} ($mstorage->{node}) " .
"$rstatus{$mstorage->{status}} : " .
"disk $mstorage->{curdisk}%" . $br;
$perfData .= "$mstorage->{name}-$mstorage->{node}::check_pve_storage::" .
"disk=$mstorage->{curdisk}%;$mstorage->{warn_disk};$mstorage->{crit_disk} ";
$workingStorages++;
$statusScore += $mstorage->{disk_status};
$statusScore++ if $statusScore eq $status{UNKNOWN};
}
else {
$reportSummary .= "$mstorage->{name} " .
"is in status $rstatus{$status{UNKNOWN}}" . $br;
}
}
$statusScore = $status{CRITICAL}
if ($statusScore > $status{UNKNOWN});
print "STORAGE $rstatus{$statusScore} $workingStorages / " .
scalar(@monitoredStorages) . " working storages" . $br . $reportSummary;
$totalPerfData .= "STORAGE::check_pve_storages::storages=$workingStorages;;;0;" . scalar(@monitoredStorages) . " " . $perfData;
$totalScore += $statusScore;
}; if (defined $arguments{openvz}) {
my $statusScore = 0;
my $workingVms = 0;
my $reportSummary = '';
my $perfData = '';
foreach my $mopenvz( @monitoredOpenvz ) {
#Add pool name to output
#$mopenvz->{name} .= "/" . $mopenvz->{pool} if defined $mopenvz->{pool};
if ($mopenvz->{status} ne $status{UNDEF}) {
if (defined $mopenvz->{warn_mem}) {
$mopenvz->{mem_status} = $status{WARNING}
if ($mopenvz->{curmem} > $mopenvz->{warn_mem});
}
if (defined $mopenvz->{crit_mem}) {
$mopenvz->{mem_status} = $status{CRITICAL}
if $mopenvz->{curmem} > $mopenvz->{crit_mem};
}
if (defined $mopenvz->{warn_disk}) {
$mopenvz->{disk_status} = $status{WARNING}
if $mopenvz->{curdisk} > $mopenvz->{warn_disk};
}
if (defined $mopenvz->{crit_disk}) {
$mopenvz->{disk_status} = $status{CRITICAL}
if $mopenvz->{curdisk} > $mopenvz->{crit_disk};
}
if (defined $mopenvz->{warn_cpu}) {
$mopenvz->{cpu_status} = $status{WARNING}
if $mopenvz->{curcpu} > $mopenvz->{warn_cpu};
}
if (defined $mopenvz->{crit_cpu}) {
$mopenvz->{cpu_status} = $status{CRITICAL}
if $mopenvz->{curcpu} > $mopenvz->{crit_cpu};
}
if (defined $mopenvz->{alive}) {
if ($mopenvz->{alive} eq "running") {
$mopenvz->{status} = $status{OK};
$workingVms++;
$reportSummary .=
"$mopenvz->{name} ($mopenvz->{node}) " .
"$rstatus{$mopenvz->{status}} : " .
"cpu $rstatus{$mopenvz->{cpu_status}} ($mopenvz->{curcpu}%), " .
"mem $rstatus{$mopenvz->{mem_status}} ($mopenvz->{curmem}%), " .
"disk $rstatus{$mopenvz->{disk_status}} ($mopenvz->{curdisk}%) " .
"uptime $mopenvz->{uptime}" . $br;
$perfData .=
"$mopenvz->{name}::check_pve_openvz::" .
"cpu=$mopenvz->{curcpu}%;$mopenvz->{warn_cpu};$mopenvz->{crit_cpu} " .
"mem=$mopenvz->{curmem}%;$mopenvz->{warn_mem};$mopenvz->{crit_mem} ";
}
else {
$mopenvz->{status} = $status{CRITICAL};
$statusScore += $status{CRITICAL};
$reportSummary .= "$mopenvz->{name} " .
"$rstatus{$mopenvz->{status}} : " .
"VM is $mopenvz->{alive}" . $br;
}
}
$statusScore += $mopenvz->{cpu_status} +
$mopenvz->{mem_status} +
$mopenvz->{disk_status};
$statusScore++ if $statusScore eq $status{UNKNOWN};
}
else {
$reportSummary .= "$mopenvz->{name} " .
"is in status $rstatus{$status{UNKNOWN}}" . $br;
$statusScore += $status{UNKNOWN};
}
}
$statusScore = $status{CRITICAL}
if ($statusScore > $status{UNKNOWN});
print "OPENVZ $rstatus{$statusScore} $workingVms / " .
scalar(@monitoredOpenvz) . " working VMs" . $br . $reportSummary;
$totalPerfData .= "OPENVZ::check_pve_vms::vmcount=$workingVms;;;0;" . scalar(@monitoredOpenvz) . " " . $perfData;
$totalScore += $statusScore;
}; if (defined $arguments{qemu}) {
my $statusScore = 0;
my $workingVms = 0;
my $reportSummary = '';
my $perfData = '';
foreach my $mqemu( @monitoredQemus ) {
#Add pool name to output
#$mqemu->{name} .= "/" . $mqemu->{pool} if defined $mqemu->{pool};
if ($mqemu->{status} ne $status{UNDEF}) {
if (defined $mqemu->{warn_mem}) {
$mqemu->{mem_status} = $status{WARNING}
if $mqemu->{curmem} > $mqemu->{warn_mem};
}
if (defined $mqemu->{crit_mem}) {
$mqemu->{mem_status} = $status{CRITICAL}
if $mqemu->{curmem} > $mqemu->{crit_mem};
}
if (defined $mqemu->{warn_disk}) {
$mqemu->{disk_status} = $status{WARNING}
if $mqemu->{curdisk} > $mqemu->{warn_disk};
}
if (defined $mqemu->{crit_disk}) {
$mqemu->{disk_status} = $status{CRITICAL}
if $mqemu->{curdisk} > $mqemu->{crit_disk};
}
if (defined $mqemu->{warn_cpu}) {
$mqemu->{cpu_status} = $status{WARNING}
if $mqemu->{curcpu} > $mqemu->{warn_cpu};
}
if (defined $mqemu->{crit_cpu}) {
$mqemu->{cpu_status} = $status{CRITICAL}
if $mqemu->{curcpu} > $mqemu->{crit_cpu};
}
if ($mqemu->{alive} eq "running") {
$mqemu->{status} = $status{OK};
$workingVms++;
$reportSummary .=
"$mqemu->{name} ($mqemu->{node}) $rstatus{$mqemu->{status}} : " .
"cpu $rstatus{$mqemu->{cpu_status}} ($mqemu->{curcpu}%), " .
"mem $rstatus{$mqemu->{mem_status}} ($mqemu->{curmem}%), " .
"disk $rstatus{$mqemu->{disk_status}} ($mqemu->{curdisk}%) " .
"uptime $mqemu->{uptime}" . $br;
$perfData .=
"$mqemu->{name}::check_pve_qemu::" .
"cpu=$mqemu->{curcpu}%;$mqemu->{warn_cpu};$mqemu->{crit_cpu} " .
"mem=$mqemu->{curmem}%;$mqemu->{warn_mem};$mqemu->{crit_mem} ";
}
else {
$mqemu->{status} = $status{CRITICAL};
$reportSummary .= "$mqemu->{name} $rstatus{$mqemu->{status}} : " .
"VM is $mqemu->{alive}" . $br;
$statusScore += $status{CRITICAL};
$mqemu->{status} = $status{CRITICAL};
}
$statusScore += $mqemu->{cpu_status} +
$mqemu->{mem_status} +
$mqemu->{disk_status};
$statusScore++ if $statusScore eq $status{UNKNOWN};
}
else {
$reportSummary .= "$mqemu->{name} " .
"is in status $rstatus{$status{UNKNOWN}}" . $br;
$statusScore += $status{UNKNOWN};
}
}
$statusScore = $status{CRITICAL}
if ($statusScore > $status{UNKNOWN});
print "QEMU $rstatus{$statusScore} $workingVms / " .
scalar(@monitoredQemus) . " working VMs" . $br .
$reportSummary;
$totalPerfData .= "QEMU::check_pve_vms::vmcount=$workingVms;;;0;" . scalar(@monitoredQemus) . " " . $perfData;
$totalScore += $statusScore;
}; if (not defined $arguments{qemu} and not defined $arguments{openvz} and not defined $arguments{storages} and not defined $arguments{nodes}) {
usage();
exit $status{UNKNOWN};
}
print $totalPerfData
if (defined $arguments{perfdata} and $totalPerfData ne "|");
exit $totalScore;