#!/usr/bin/perl -w

################################################################################
##
## Author: Bruno Miranda
## E-mail: bruno.miranda@opservices.com.br
##
## Date: 19/06/2012
##
################################################################################

################################################################################
## ChangeLog (Sidney - sidney.souza@opservices.com.br)
################################################################################
##
## 11/12/12 -> Corrigido problema de retorno de dados para o OpMon, onde o
## retorno estava com (,) e deveria ser (;) para que seja gerado o dados de
## capacidade e performance.
##
################################################################################
##
## 02/01/13 -> Corrigido problema no processamento da informacao "usr/user" na
## versao 7.0.2 do mpstat.
##
################################################################################
##
## 28/01/13 -> Corrigido problema no processamento do retorno do comando mpstat
## na versao 7.0.2, em pt-br.
##
################################################################################
################################################################################
##
## 21/03/16 -> Definido o valor default para verbose como zero. O plugin nao
## funcionava no CentOS 7 por causa disso.
##
################################################################################

################################################################################
## ChangeLog (Guilherme - guilherme.fontans@opservices.com.br)
################################################################################
##
## 11/08/16 -> Removido o valor default do verbose, pois afetou o funcionamento
## do plugin em CentOS 5, foi adicionado a opcao default do Nagios, utilizando
## -vv em vez de -v 2.
##
################################################################################


use strict;
use Getopt::Long;
use File::Basename;
use IPC::Open3;
use bignum ( p => -2 );

my $VERSION = 1.3;

################################################################################
##
## Display help
##
################################################################################
sub help {
	print "This plugin uses the command 'mpstat' to check the cpu usage of the machine.\n";
	usage();
	print << 'HELP';
 -A | --all      Displays information for all CPU cores.
 -c | --critical     Critical threshold in %.
 -C | --count    Number of times information is collected.
 -h | --help     Display this help.
 -i | --interval     Time interval between the collection of
		   information.
 -u          Type of information monitored. The following values are
		   available:
		  usr
			 Show the percentage of CPU utilization that occurred
			 while executing at the user level (application).
		  nice
			 Show the percentage of CPU utilization that occurred
			 while executing at the user level with nice priority.
		  sys
			 Show the percentage of CPU utilization that occurred
			 while executing at the system level (kernel). Note that
			 this does not include time spent servicing hardware and
			 software interrupts.
		  iowait
			 Show the percentage of time that the CPU or CPUs were
			 idle during which the system had an outstanding disk
			 I/O request.
		  irq
			 Show the percentage of time spent by the CPU or CPUs to
			 service hardware interrupts.
		  soft
			 Show the percentage of time spent by the CPU or CPUs to
			 service software interrupts.
		  steal
			 Show the percentage of time spent in involuntary wait
			 by the virtual CPU or CPUs while the hypervisor was
			 servicing another virtual processor.
		  guest
			 Show the percentage of time spent by the CPU or CPUs to
			 run a virtual processor.
		  idle
			 Show the percentage of time that the CPU or CPUs were
			 idle and the system did not have an outstanding disk
			 I/O request.
		  sum
			 Returns the sum of user, sys and iowait.

		  NOTE:   On SMP machines a processor that does not have any
			 activity at all is a disabled (offline) processor.
			  If the value idle is used, the logic is reverse
			 and this plugin returns Warning or Critical if the
			 value is lower or equal the set for Warning or
			 Critical.

 -w | --warning    Warning threshold in %.
 -V | --version    Shows the version of this plugin.
 -v | --verbose    Show details for command-line debugging (can repeat up to 2 times).

HELP
}

################################################################################
##
## Display usage
##
################################################################################
sub usage {
	print "Usage:\n";
	print " ", basename($0), " -u <usr|nice|sys|iowait|sum|...> [OPTIONS]...\n";
}

################################################################################
##
## Display version
##
################################################################################
sub version {
	print "Version: ", $VERSION, "\n";
}

################################################################################
##
## Get arguments
##
################################################################################
sub getOption  {
	my ($opt_crit, $opt_warn, $opt_help, $opt_type, $opt_all_cpus, $opt_interval, $opt_count, $opt_version, $opt_verbose);

	Getopt::Long::Configure('bundling');
	Getopt::Long::Configure('pass_through');
	GetOptions(
		'A|all'        => \$opt_all_cpus,
		'c|critical=f'     => \$opt_crit,
		'C|count=i'    => \$opt_count,
		'h|help'       => \$opt_help,
		'i|interval=f'     => \$opt_interval,
		'u=s'          => \$opt_type,
		'w|warning=f'      => \$opt_warn,
		'V|version'    => \$opt_version,
		'v|verbose+'      => \$opt_verbose
	);

	if(!$opt_verbose){
		$opt_verbose = 0;
	}

	if ($opt_help) {
		help();
		exit(0);
	}

	if (defined($opt_version)){
		version();
		exit(0);
	}

	if (($opt_verbose != 0) && ($opt_verbose != 1) && ($opt_verbose != 2)) {
		help();
		exit(3);
	}

	$opt_interval = 1 if (!defined($opt_interval));
	$opt_count = 1 if (!defined($opt_count));

	if (((!defined($opt_crit)) && (defined($opt_warn))) || ((defined($opt_crit)) && (!defined($opt_warn)))){
		help();
		exit(3);
	}

	if ((!defined($opt_type)) || (($opt_type !~ /^usr$/)
			&& ($opt_type !~ /^nice$/) && ($opt_type !~/^sys$/)
			&& ($opt_type !~ /^iowait$/) && ($opt_type !~ /^irq$/)
			&& ($opt_type !~ /^soft$/) && ($opt_type !~ /^steal$/)
			&& ($opt_type !~ /^guest$/) && ($opt_type !~ /^idle$/)
			&& ($opt_type !~ /^sum$/))) {
		usage();
		exit(3);
	}

	return ($opt_crit, $opt_warn, $opt_help, $opt_type, $opt_all_cpus, $opt_interval, $opt_count, $opt_verbose);
}

################################################################################
##
##   Use the command "mpstat" to get the CPU usage
## of the system.
##
################################################################################
sub getCpuInfo {
	my $opt_type = shift;
	my $opt_all = shift;
	my $opt_interval = shift;
	my $opt_count = shift;
	my $opt_verbose = shift;
	my @args = ( $opt_interval, $opt_count );
	my (%cpuinfo, @ret_command, @temp);
	my %labels;
	my @aux;
	my $pos;
	my($wtr, $rdr, $err);

	if (defined($opt_all)) {
		push(@args,"-P");
		push(@args,"ALL");
		open3($wtr, $rdr, $err, "/usr/bin/mpstat", @args);
	} else {
		open3($wtr, $rdr, $err, "/usr/bin/mpstat", @args);
	}

	while (<$rdr>) { push(@ret_command, $_) };

	print "\nOutput of the command 'mpstat [-P ALL] interval count':\n\n" if ($opt_verbose >= 1);

	foreach (@ret_command) {
		$_ =~ s/AM|PM//;
		if ($_ =~ /nice/) {
			$pos = 0;

			if (defined($labels{0})){
				undef %labels;
			}


			@aux = split(/ +/,$_);
			foreach (@aux) {
				chomp ($_);
				if ($_ =~ /%/){
					$_ =~ s/%//;
					$labels{$_} = $pos;
				}
				$pos++;
			}

		}

		print $_ if ($opt_verbose >= 1);

		if (($_ =~ /average:.*[0-9].*/i) || ($_ =~ /dia:.*[0-9].*/i)){
			$_ =~ s/,/./g;

			@temp = split(/\s+/,$_);
			foreach my $key (keys %labels) {
				$cpuinfo{$temp[1]}{$key}   = $temp[$labels{$key}];
			}
			$cpuinfo{$temp[1]}{sum}   = $cpuinfo{$temp[1]}{usr} + $cpuinfo{$temp[1]}{sys} + $cpuinfo{$temp[1]}{iowait} if (defined($labels{usr}));
			$cpuinfo{$temp[1]}{sum}   = $cpuinfo{$temp[1]}{user} + $cpuinfo{$temp[1]}{sys} + $cpuinfo{$temp[1]}{iowait} if (defined($labels{user}));
		}
	}

	print "\n" if ($opt_verbose >= 1);
	$opt_type = "user"  if (defined($labels{user}));

	#       foreach my $key (sort keys %cpuinfo) {
	#           print "CPU_", $key, " ", $cpuinfo{$key}{sum}, "% ";
	#           print "/ " if (keys(%cpuinfo) > 1);
	#       }

	return ($opt_type, %cpuinfo);
}


################################################################################
##
##   Defines the status returned by the plugin
## (OK, WARNING or CRITICAL).
##
################################################################################
sub defineStatus {
	my $opt_type = shift;
	my $opt_crit = shift;
	my $opt_warn = shift;
	my %cpuinfo = @_;

	if ((defined($opt_crit)) && (defined($opt_warn))) {
		if ($opt_type =~ /idle/) {
			foreach my $key (keys %cpuinfo){
				if ($cpuinfo{$key}{$opt_type} <= $opt_crit) {
					return "CRITICAL";
				} elsif ($cpuinfo{$key}{$opt_type} <= $opt_warn){
					return "WARNING";
				}
			}
		} else {
			foreach my $key (keys %cpuinfo){
				if ($cpuinfo{$key}{$opt_type} >= $opt_crit) {
					return "CRITICAL";
				} elsif ($cpuinfo{$key}{$opt_type} >= $opt_warn){
					return "WARNING";
				}
			}
		}
	}
	return "OK";
}

################################################################################
###
###   Returns the information collected to OpMon.
###
################################################################################
sub displayInfo {
	my $opt_type = shift;
	my $opt_crit = shift;
	my $opt_warn = shift;
	my $status = shift;
	my %cpuinfo = @_;
	my $label = $opt_type;

	$label = "Utilization" if ($opt_type eq "sum");

	$opt_warn=0 if (!defined($opt_warn));
	$opt_crit=0 if (!defined($opt_crit));

	print $status, ": ", ucfirst($label), " ";

	foreach my $key (sort keys %cpuinfo) {
		print "CPU_", $key, " ", $cpuinfo{$key}{$opt_type}, "% ";
		print "/ " if (keys(%cpuinfo) > 1);
	}

	print "| ";

	foreach my $key (sort keys %cpuinfo) {
		print "CPU_", $key,"_", ucfirst($label), "=", $cpuinfo{$key}{$opt_type},"%;$opt_warn;$opt_crit;0;100 ";
	}

	if ($opt_type eq "sum") {
		foreach my $key (sort keys %cpuinfo) {
			print "CPU_", $key, "_USR=", $cpuinfo{$key}{usr},"%;0;0;0;100 " if (defined($cpuinfo{$key}{usr}));
			print "CPU_", $key, "_USR=", $cpuinfo{$key}{user},"%;0;0;0;100 " if (defined($cpuinfo{$key}{user}));
			print "CPU_", $key, "_SYS=", $cpuinfo{$key}{sys},"%;0;0;0;100 ";
			print "CPU_", $key, "_IOWAIT=", $cpuinfo{$key}{iowait},"%;0;0;0;100 ";
			print "CPU_", $key, "_IDLE=", $cpuinfo{$key}{idle},"%;0;0;0;100 ";
		}
	}

	print "\n";
}

################################################################################
##
##  Display verbose informations.
##
################################################################################
sub verbose {
	my $opt_verbose = shift;
	my %cpuinfo = @_;
	my($wtr, $rdr, $err);

	if ($opt_verbose == 2) {
		print "\nCPU/Hash ID   ";
		print "  ", $_ foreach (sort keys %{$cpuinfo{'all'}});
		print "\n";
		print "----------------------------------------------------------------------------\n";
		foreach my $key (sort keys %cpuinfo) {
			print "   ", $key, " \t-\t";
			foreach my $key2 (sort keys %{$cpuinfo{$key}}) {
				print $cpuinfo{$key}{$key2}, "% ";
			}
			print "\n";
		}
		print "\n";
	}
}

################################################################################
###
###   Checks if the command 'mpstat' is available.
###
################################################################################
sub checkDependencies{
	if (! -e "/usr/bin/mpstat") {
		print "This plugin depends on the package \"sysstat\" to work.\n";
		exit(3);
	}
}

sub main {
	checkDependencies();
	my ($opt_crit, $opt_warn, $opt_help, $opt_type, $opt_all_cpu, $opt_interval, $opt_count, $opt_verbose) =  getOption();
	my %cpuinfo;
	($opt_type, %cpuinfo) = getCpuInfo($opt_type, $opt_all_cpu, $opt_interval, $opt_count, $opt_verbose);
	my $status = defineStatus($opt_type, $opt_crit, $opt_warn, %cpuinfo);

	verbose($opt_verbose, %cpuinfo) if ($opt_verbose == 2);
	displayInfo($opt_type, $opt_crit, $opt_warn, $status, %cpuinfo);

	if ($status =~ /WARNING/) {
		exit (1);
	} elsif ($status =~ /CRITICAL/) {
		exit (2);
	}
	exit(0);
} &main
