git.lirion.de

Of git, get, and gud

summaryrefslogtreecommitdiffstats
path: root/nagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt
diff options
context:
space:
mode:
Diffstat (limited to 'nagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt')
-rwxr-xr-xnagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt526
1 files changed, 526 insertions, 0 deletions
diff --git a/nagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt b/nagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt
new file mode 100755
index 0000000..2029c9e
--- /dev/null
+++ b/nagios-plugins-contrib-24.20190301~bpo9+1/check_libvirt/check_libvirt
@@ -0,0 +1,526 @@
+#!/usr/bin/perl -w
+#
+# Nagios plugin to monitor different virtualization solutions using libvirt, e.g. Xen, KVM, Virtual Box.
+#
+# License: GPL
+# Copyright (c) 2011 op5 AB
+# Author: Kostyantyn Hushchyn <op5-users@lists.op5.com>
+#
+# For direct contact with any of the op5 developers send a mail to
+# op5-users@lists.op5.com
+# Discussions are directed to the mailing list op5-users@op5.com,
+# see http://lists.op5.com/mailman/listinfo/op5-users
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+use warnings;
+use vars qw($PROGNAME $VERSION $output $result);
+use Nagios::Plugin;
+use File::Basename;
+use Sys::Virt;
+use XML::Simple;
+
+$PROGNAME = basename($0);
+$VERSION = '0.1.0';
+my $spooldir="/opt/monitor/var/check_libvirt";
+
+if (!-d $spooldir)
+{
+ mkdir($spooldir);
+}
+
+my $np = Nagios::Plugin->new(
+ usage => "Usage: %s -H <hosturl> [ -N <vmname> ]\n"
+ . " [-u <user> -p <pass>]\n"
+ . " -l <command> [ -s <subcommand> ]\n"
+ . " [ -t <timeout> ] [ -w <warn_range> ] [ -c <crit_range> ]\n"
+ . ' [ -V ] [ -h ]',
+ version => $VERSION,
+ plugin => $PROGNAME,
+ shortname => uc($PROGNAME),
+ blurb => 'Plugin for monitoring virtualization solutions via libvirt: KVM/QEMU, VirtualBox, Xen, Microsoft Hyper-V, etc',
+ extra => "Supported commands :\n"
+ . " Host specific :\n"
+ . " * list - shows VM's list and their statuses\n"
+ . " * pool - shows pool info\n"
+ . " + (name) - query particular pool with name (name)\n"
+ . " ^ list pools and their statuses\n"
+ . " * volume - shows volume info\n"
+ . " + (name) - query particular volume in pool with full name (name)\n"
+ . " ^ list volumes and their statuses\n"
+ . " * running - queries VM state\n"
+ . " + (name) - query particular VM state by it's name (name)\n"
+ . " VM specific :\n"
+ . " * cpu - shows cpu usage info\n"
+ . " * mem - shows mem usage info\n"
+ . " * net - shows net info: TX bytes, TX packets, TX errors, TX drops, RX bytes, RX packets, RX errors, RX drops\n"
+ . " * io - shows io info: Read bytes, Read requests, Write bytes, Write requests, Errors\n"
+ . "\n\nCopyright (c) 2011 op5",
+ timeout => 30,
+);
+
+$np->add_arg(
+ spec => 'host|H=s',
+ help => "-H, --host=<hosturl>\n"
+ . " libvirt remote urls. More information can be found here http://libvirt.org/remote.html#Remote_URI_reference\n"
+ . ' and here http://libvirt.org/drivers.html .',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'name|N=s',
+ help => "-N, --name=<vmname>\n"
+ . ' Virtual machine name.',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'username|u=s',
+ help => "-u, --username=<username>\n"
+ . ' Username to connect to Hypervisor with.',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'password|p=s',
+ help => "-p, --password=<password>\n"
+ . ' Password to use with the username.',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'warning|w=s',
+ help => "-w, --warning=THRESHOLD\n"
+ . " Warning threshold. See\n"
+ . " http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT\n"
+ . ' for the threshold format.',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'critical|c=s',
+ help => "-c, --critical=THRESHOLD\n"
+ . " Critical threshold. See\n"
+ . " http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT\n"
+ . ' for the threshold format.',
+ required => 0,
+);
+
+$np->add_arg(
+ spec => 'command|l=s',
+ help => "-l, --command=COMMAND\n"
+ . ' Specify command type (VM Server: LIST, POOL, VOLUME; VM Machine: CPU, MEM, NET, IO)',
+ required => 1,
+);
+
+$np->add_arg(
+ spec => 'subcommand|s=s',
+ help => "-s, --subcommand=SUBCOMMAND\n"
+ . ' Specify subcommand',
+ required => 0,
+);
+
+$np->getopts;
+
+my $host = $np->opts->host;
+my $vmname = $np->opts->name;
+my $username = $np->opts->username;
+my $password = $np->opts->password;
+my $warning = $np->opts->warning;
+my $critical = $np->opts->critical;
+my $command = $np->opts->command;
+my $subcommand = $np->opts->subcommand;
+my %runstates = (Sys::Virt::Domain::STATE_NOSTATE => "running", Sys::Virt::Domain::STATE_RUNNING => "running", Sys::Virt::Domain::STATE_BLOCKED => "running", Sys::Virt::Domain::STATE_PAUSED => "running", Sys::Virt::Domain::STATE_SHUTDOWN => "going down", Sys::Virt::Domain::STATE_SHUTOFF => "down", Sys::Virt::Domain::STATE_CRASHED => "crashed");
+my %poolstates = (Sys::Virt::StoragePool::STATE_INACTIVE => "inactive", Sys::Virt::StoragePool::STATE_BUILDING => "building", Sys::Virt::StoragePool::STATE_RUNNING => "running", Sys::Virt::StoragePool::STATE_DEGRADED => "degraded");
+my %voltypes = (Sys::Virt::StorageVol::TYPE_FILE => "image", Sys::Virt::StorageVol::TYPE_BLOCK => "dev");
+$output = "Unknown ERROR!";
+$result = CRITICAL;
+
+if (defined($critical))
+{
+ $critical = undef if ($critical eq '');
+}
+
+if (defined($warning))
+{
+ $warning = undef if ($warning eq '');
+}
+
+$np->set_thresholds(critical => $critical, warning => $warning);
+
+eval
+{
+ my $con = Sys::Virt->new(address => $host, readonly => 1, auth => (defined($username) && defined($password)),
+ credlist => [
+ Sys::Virt::CRED_AUTHNAME,
+ Sys::Virt::CRED_PASSPHRASE,
+ ],
+ callback =>
+ sub {
+ my $creds = shift;
+
+ foreach my $cred (@{$creds}) {
+ if ($cred->{type} == Sys::Virt::CRED_AUTHNAME) {
+ $cred->{result} = $username;
+ }
+ if ($cred->{type} == Sys::Virt::CRED_PASSPHRASE) {
+ $cred->{result} = $password;
+ }
+ }
+ return 0;
+ }
+ );
+
+ if (!defined($vmname))
+ {
+ if (uc($command) eq "LIST")
+ {
+ my @updoms = $con->list_domains();
+ my $up = @updoms;
+ my $cnt = $con->num_of_defined_domains();
+ my @downdoms = $con->list_defined_domain_names($cnt);
+ $output = "";
+
+ while (my $dom = shift(@downdoms))
+ {
+ $output .= $dom . "(" . $runstates{Sys::Virt::Domain::STATE_SHUTOFF} . "), ";
+ }
+
+ $cnt += $up;
+ while (my $dom = shift(@updoms))
+ {
+ my $domstate = $dom->get_info()->{"state"};
+ if (exists($runstates{$domstate}))
+ {
+ if ($runstates{$domstate} ne "running")
+ {
+ $up--;
+ $output = $dom->get_name() . "(" . $runstates{$domstate} . "), " . $output;
+ }
+ else
+ {
+ $output .= $dom->get_name() . "(running), ";
+ }
+ }
+ else
+ {
+ $up--;
+ $output = $dom->get_name() . "(unknown), " . $output;
+ }
+ }
+
+ chop($output);
+ chop($output);
+ $output = $up . "/" . $cnt . " VMs up: " . $output;
+ $np->add_perfdata(label => "vmcount", value => $up, uom => 'units', threshold => $np->threshold);
+ $result = $np->check_threshold(check => $up);
+ }
+ elsif (uc($command) eq "POOL")
+ {
+ if (!defined($subcommand))
+ {
+ my @pools = $con->list_storage_pools();
+ push(@pools, $con->list_defined_storage_pools());
+
+ $output = "";
+ while (my $pool = shift(@pools))
+ {
+ my $poolinfo = $pool->get_info();
+ if (exists($poolstates{$poolinfo->{"state"}}))
+ {
+ my $value1 = $poolinfo->{"allocation"};
+ my $value2 = simplify_number($value1 / $poolinfo->{"capacity"} * 100);
+ $value1 = simplify_number($value1 / 1024 / 1024);
+ $output .= $pool->get_name() . "(" . $poolstates{$poolinfo->{"state"}} . ")=" . $value1 . "MB(" . $value2 . "%), ";
+ }
+ else
+ {
+ $output .= $pool->get_name() . "(unknown)=unavialable, ";
+ }
+ }
+ chop($output);
+ chop($output);
+ $result = OK;
+ }
+ else
+ {
+ my $pool = $con->get_storage_pool_by_name($subcommand);
+ my $poolinfo = $pool->get_info();
+ my $value1 = $poolinfo->{"allocation"};
+ my $value2 = simplify_number($value1 / $poolinfo->{"capacity"} * 100);
+ $value1 = simplify_number($value1 / 1024 / 1024);
+ $output = $pool->get_name() . "(" . $poolstates{$poolinfo->{"state"}} . ")=" . $value1 . "MB(" . $value2 . "%)";
+ $np->add_perfdata(label => $pool->get_name(), value => $value1, uom => 'MB', threshold => $np->threshold);
+ $result = $np->check_threshold(check => $value1);
+ }
+ }
+ elsif (uc($command) eq "VOLUME")
+ {
+ if (!defined($subcommand))
+ {
+ my @pools = $con->list_storage_pools();
+ push(@pools, $con->list_defined_storage_pools());
+
+ $output = "";
+ while (my $pool = shift(@pools))
+ {
+ my @volumes = $pool->list_volumes();
+ while (my $vol = shift(@volumes))
+ {
+ my $volinfo = $vol->get_info();
+ my $value1 = simplify_number($volinfo->{"allocation"} / 1024 / 1024);
+ my $value2 = simplify_number($volinfo->{"capacity"} / 1024 / 1024);
+ $output .= $vol->get_name() . "(" . $voltypes{$volinfo->{"type"}} . ")=" . $value1 . "MB/" . $value2 . "MB, ";
+ }
+ }
+ chop($output);
+ chop($output);
+ $result = OK;
+ }
+ else
+ {
+ my ($poolname, $volname) = split(/\//, $subcommand, 2);
+ die "Volume name is not defined. Please provide argument in form 'pool/volume'.\n" if (!defined($volname));
+ my $pool = $con->get_storage_pool_by_name($poolname);
+ my $vol = $pool->get_volume_by_name($volname);
+ my $volinfo = $vol->get_info();
+ my $value1 = simplify_number($volinfo->{"allocation"} / 1024 / 1024);
+ my $value2 = simplify_number($volinfo->{"capacity"} / 1024 / 1024);
+ $output = $vol->get_name() . "(" . $voltypes{$volinfo->{"type"}} . ")=" . $value1 . "MB/" . $value2 . "MB";
+ $np->add_perfdata(label => $vol->get_name(), value => $value1, uom => 'MB', threshold => $np->threshold);
+ $result = $np->check_threshold(check => $value1);
+ }
+ }
+ elsif (uc($command) eq "RUNNING")
+ {
+ die "VM name is not defined. Please provide argument in -s command.\n" if (!defined($subcommand));
+ my $dom = $con->get_domain_by_name($subcommand);
+ my $domstate = $dom->get_info()->{"state"};
+ if (exists($runstates{$domstate}))
+ {
+ $result = ($runstates{$domstate} eq "running") ? OK : CRITICAL;
+ $output = $dom->get_name() . " is in " . $runstates{$domstate} . " state\n";
+ }
+ else
+ {
+ $result = CRITICAL;
+ $output = $dom->get_name() . " is in unknown state\n";
+ }
+ }
+ else
+ {
+ $result = CRITICAL;
+ $output = "unknown command '$command' for Host\n";
+ }
+ }
+ else
+ {
+ my $dom = $con->get_domain_by_name($vmname);
+ my $dominfo = $dom->get_info();
+ my $domstate = $dominfo->{"state"};
+ die "VM '$vmname' is " . $runstates{$domstate} . ".\n" if (!exists($runstates{$domstate}) || $runstates{$domstate} ne "running");
+
+ if (uc($command) eq "CPU")
+ {
+ my $vars;
+ my $range = time();
+ ($result, $vars) = process_domstat($np, $spooldir . "/" . $vmname . "_cpu", $range, {cpu_time => $dominfo->{"cpuTime"}});
+ die {msg => ("Skipped, first time of data collection.\n"), code => OK} if (ref($vars) ne "HASH");
+ # cpuTime is in nano seconds
+ my $cpuusage = simplify_number($vars->{"cpu_time"} / 1000000000 * 100);
+ $output = "CPU usage = " . $cpuusage . " %\n";
+ $np->add_perfdata(label => "cpu", value => $cpuusage, threshold => $np->threshold);
+ $result = $np->check_threshold(check => $cpuusage);
+ }
+ elsif (uc($command) eq "MEM")
+ {
+ my $value = simplify_number($dominfo->{"memory"} / 1024);
+ $output = "MEM usage = " . $value . " MB\n";
+ $np->add_perfdata(label => "memory", value => $value, threshold => $np->threshold);
+ $result = $np->check_threshold(check => $value);
+ }
+ elsif (uc($command) eq "NET")
+ {
+ my $vmdesc = XML::Simple->new()->XMLin($dom->get_xml_description());
+ my $ifaces = $vmdesc->{"devices"}->{"interface"};
+ $ifaces = [ $ifaces ] if (ref($ifaces) ne "ARRAY");
+ my $tx_bytes = 0;
+ my $tx_pkts = 0;
+ my $tx_drop = 0;
+ my $tx_errs = 0;
+ my $rx_bytes = 0;
+ my $rx_pkts = 0;
+ my $rx_drop = 0;
+ my $rx_errs = 0;
+
+ while (my $iface = shift(@{$ifaces}))
+ {
+ my $ifacedev = $iface->{"target"}->{"dev"};
+ my $devname = $ifacedev;
+ die {msg => ("Can not access network interfaces. Unsure that VM is running as paravirt guest or PV drivers are installed.\n"), code => CRITICAL} if (!defined($devname));
+ $devname =~ s/\./_/g;
+
+ my $vars;
+ my $range = time();
+ ($result, $vars) = process_domstat($np, $spooldir . "/" . $vmname . "_" . $devname . "_net", $range, $dom->interface_stats($ifacedev));
+ next if (ref($vars) ne "HASH");
+
+ $output = "";
+ $tx_bytes += $vars->{"tx_bytes"};
+ $tx_pkts += $vars->{"tx_packets"};
+ $tx_drop += $vars->{"tx_drop"};
+ $tx_errs += $vars->{"tx_errs"};
+ $rx_bytes += $vars->{"rx_bytes"};
+ $rx_pkts += $vars->{"rx_packets"};
+ $rx_drop += $vars->{"rx_drop"};
+ $rx_errs += $vars->{"rx_errs"};
+ }
+ die {msg => ("Skipped, first time of data collection.\n"), code => OK} if ($output);
+
+ $np->add_perfdata(label => "tx_bytes", value => $tx_bytes, threshold => $np->threshold);
+ $np->add_perfdata(label => "tx_packets", value => $tx_pkts, threshold => $np->threshold);
+ $np->add_perfdata(label => "tx_drop", value => $tx_drop, threshold => $np->threshold);
+ $np->add_perfdata(label => "tx_errs", value => $tx_errs, threshold => $np->threshold);
+ $np->add_perfdata(label => "rx_bytes", value => $rx_bytes, threshold => $np->threshold);
+ $np->add_perfdata(label => "rx_packets", value => $rx_pkts, threshold => $np->threshold);
+ $np->add_perfdata(label => "rx_drop", value => $rx_drop, threshold => $np->threshold);
+ $np->add_perfdata(label => "rx_errs", value => $rx_errs, threshold => $np->threshold);
+
+ $output = "NET TX bytes = " . $tx_bytes . ", TX pkts = " . $tx_pkts . ", TX drops = " . $tx_drop . ", TX errors = " . $tx_errs . ", RX bytes = " . $rx_bytes . ", RX pkts = " . $rx_pkts . ", RX drops = " . $rx_drop . ", RX errors = " . $rx_errs;
+ }
+ elsif (uc($command) eq "IO")
+ {
+ my $vmdesc = XML::Simple->new()->XMLin($dom->get_xml_description());
+ my $blks = $vmdesc->{"devices"}->{"disk"};
+ $blks = [ $blks ] if (ref($blks) ne "ARRAY");
+ my $rd_bytes = 0;
+ my $rd_req = 0;
+ my $wr_bytes = 0;
+ my $wr_req = 0;
+ my $errs = 0;
+
+ while (my $blk = shift(@{$blks}))
+ {
+ my $blkdev = $blk->{"target"}->{"dev"};
+ my $devname = $blkdev;
+ die {msg => ("Can not access disk device. Unsure that VM is running as paravirt guest or PV drivers are installed.\n"), code => CRITICAL} if (!defined($devname));
+ $devname =~ s/\./_/g;
+
+ my $vars;
+ my $range = time();
+ ($result, $vars) = process_domstat($np, $spooldir . "/" . $vmname . "_" . $devname . "_io", $range, $dom->block_stats($blkdev));
+ next if (ref($vars) ne "HASH");
+
+ $output = "";
+ $rd_bytes += $vars->{"rd_bytes"};
+ $rd_req += $vars->{"rd_req"};
+ $wr_bytes += $vars->{"wr_bytes"};
+ $wr_req += $vars->{"wr_req"};
+ $errs += $vars->{"errs"};
+ }
+ die {msg => ("Skipped, first time of data collection.\n"), code => OK} if ($output);
+
+ $np->add_perfdata(label => "rd_bytes", value => $rd_bytes, threshold => $np->threshold);
+ $np->add_perfdata(label => "rd_req", value => $rd_req, threshold => $np->threshold);
+ $np->add_perfdata(label => "wr_bytes", value => $wr_bytes, threshold => $np->threshold);
+ $np->add_perfdata(label => "wr_req", value => $wr_req, threshold => $np->threshold);
+ $np->add_perfdata(label => "errors", value => $errs, threshold => $np->threshold);
+
+ $output = "IO read bytes = " . $rd_bytes . ", read req = " . $rd_req . ", write bytes = " . $wr_bytes . ", write req = " . $wr_req . ", errors = " . $errs;
+ }
+ else
+ {
+ $result = CRITICAL;
+ $output = "unknown command '$command' for VM '$vmname'\n";
+ }
+ }
+};
+
+if ($@)
+{
+ if (uc(ref($@)) eq "HASH")
+ {
+ $output = $@->{msg};
+ $result = $@->{code};
+ }
+ else
+ {
+ $output = $@ . "";
+ $result = CRITICAL;
+ }
+ $output =~ s/libvirt error code: [0-9]+, message: //;
+ $output =~ s/\r\n//;
+}
+
+$np->nagios_exit($result, $output);
+
+sub simplify_number
+{
+ my ($number, $cnt) = @_;
+ $cnt = 2 if (!defined($cnt));
+ return sprintf("%.${cnt}f", "$number");
+}
+
+sub process_domstat
+{
+ my ($np, $datapath, $range, $new_vars) = @_;
+ my $result = OK;
+ my $old_range = 0;
+
+ # Read old info
+ my %old_vars;
+ if (open(OLDSPOOL, "<" . $datapath))
+ {
+ $old_range = <OLDSPOOL>;
+ while (my $line = <OLDSPOOL>)
+ {
+ my @vals = split(/\s/, $line);
+ $old_vars{$vals[0]} = $vals[1];
+ }
+ close(OLDSPOOL);
+ }
+
+ # Save new info
+ open(SPOOL, ">" . $datapath) or die {msg => ("Can not create file " . $datapath . ". Please check permissions, disk space and mount point availability.\n"), code => CRITICAL};
+ print(SPOOL $range . "\n");
+ while (my ($key, $value) = each %{$new_vars})
+ {
+ print(SPOOL $key . " " . $value . "\n")
+ }
+ close(SPOOL);
+
+ return OK if (!scalar keys %old_vars);
+
+ # Compute usage statistic
+ my %vars;
+ $range = $range - $old_range;
+ foreach my $key (keys %{$new_vars})
+ {
+ if (exists($old_vars{$key}))
+ {
+ my $value = simplify_number(($new_vars->{$key} - $old_vars{$key}) / $range);
+ $vars{$key} = $value;
+ return OK if ($value < 0);
+ }
+ else
+ {
+ $result = CRITICAL;
+ }
+ }
+
+ die {msg => ("Can not retreive any value.\n"), code => CRITICAL} if (!scalar keys %vars);
+
+ return ($result, \%vars);
+}