#!/usr/bin/perl
# -*- perl -*-

# Copyright (C) 2015 Pirx Developers - https://pirx.dev/
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

=head1 NAME

hq_cpu - Munin wildcard plugin to monitor swap usage.

=head1 APPLICABLE SYSTEMS

Linux systems.

=head1 CONFIGURATION

You may want to configure warning and critical thresholds
in your munin node configuration:

[hq_cpu]
  env.warning 80
  env.critical 90

Default thresholds are 80% for warning and 90% for critical.

=head1 VERSION

  20151126

=head1 MAGIC MARKERS

  #%# family=manual
  #%# capabilities=autoconf

=head1 BUGS

None.

=head1 AUTHOR

Pirx Developers - https://pirx.dev/

=head1 LICENSE

GPLv3

=cut

use strict;
use warnings;
use Munin::Plugin;

sub read_proc_stat;

# Handle autoconf
if(defined($ARGV[0]) and $ARGV[0] eq 'autoconf') {
  print("yes\n");
  exit(0);
}

need_multigraph();

# Get data from /proc
my $stats_fields;
my %cpus;
$cpus{'read_count'} = 0;
while($cpus{'read_count'} le 1) {
  read_proc_stat();
  sleep(1);
}
delete($cpus{'read_count'});

my @data_names;
if($stats_fields == 5) {
  @data_names = ("system", "user", "nice", "iowait", "idle");
}
elsif($stats_fields == 7) {
  @data_names = ("system", "user", "nice", "iowait", "irq", "softirq", "idle");
}
elsif($stats_fields == 8) {
  @data_names = ("system", "user", "nice", "iowait", "irq", "softirq", "steal", "idle");
}
elsif($stats_fields == 9) {
  @data_names = ("system", "user", "nice", "iowait", "irq", "softirq", "steal", "guest", "idle");
}
elsif($stats_fields == 10) {
  @data_names = ("system", "user", "nice", "iowait", "irq", "softirq", "steal", "guest", "guest_nice", "idle");
}
my %data_colors = (
  'system' => "800000",
  'user' => "ff0000",
  'nice' => "ff9500",
  'iowait' => "ffff00",
  'irq' => "0000ff",
  'softirq' => "00aaff",
  'steal' => "00ffff",
  'guest' => "ad9300",
  'guest_nice' => "ad0071",
  'idle' => "00ff00"
);

# set warning and critical thresholds
my $warning = 80;
my $critical = 90;
if(defined($ENV{'warning'})) {
  $warning = $ENV{'warning'};
}
if(defined($ENV{'critical'})) {
  $critical = $ENV{'critical'};
}

# Handle config
if(defined($ARGV[0]) and $ARGV[0] eq 'config') {
  my $graph_order = "";
  foreach my $cpu_num (sort {$a <=> $b} keys %cpus) {
    $graph_order .= " cpu" . $cpu_num;
  }
  print <<EOF;
multigraph cpu_usage_all_cores
graph_title CPU usage - all cores
graph_args --base 1000 --lower-limit 0 --upper-limit 100
graph_order$graph_order
graph_scale no
graph_vlabel %
graph_category cpu

EOF
  foreach my $cpu_num (sort {$a <=> $b} keys %cpus) {
    print <<EOF;
cpu$cpu_num.label CPU core $cpu_num
cpu$cpu_num.type GAUGE
cpu$cpu_num.draw LINE1
cpu$cpu_num.min 0
cpu$cpu_num.max 100
cpu$cpu_num.warning $warning
cpu$cpu_num.critical $critical
EOF
    if($cpu_num == 0) {
      print("cpu" . $cpu_num . ".line " . $warning . ":ffff00:Warning threshold\n");
    }
  }

  foreach my $cpu_num (sort {$a <=> $b} keys %cpus) {
    print <<EOF;

multigraph cpu_usage_core_$cpu_num
graph_title CPU usage - core $cpu_num
graph_args --base 1000 --lower-limit 0 --upper-limit 100
graph_order system user nice iowait irq softirq steal guest guest_nice idle usage
graph_scale no
graph_vlabel %
graph_category cpu

EOF
    foreach my $data (@data_names) {
      print <<EOF;
$data.label $data
$data.type GAUGE
$data.draw AREASTACK
$data.colour $data_colors{$data}
EOF
    }
  print <<EOF;
usage.label total usage
usage.type GAUGE
usage.draw LINE1
usage.colour 000000
usage.line $warning:ffffff:Warning threshold
usage.warning $warning
usage.critical $critical
EOF
  }
  exit(0);
}

print("multigraph cpu_usage_all_cores\n");
foreach my $cpu_num (sort {$a <=> $b} keys %cpus) {
  print("cpu" . $cpu_num . ".value " . $cpus{$cpu_num}{'percentage'} . "\n");
}

foreach my $cpu_num (sort {$a <=> $b} keys %cpus) {
  print("\nmultigraph cpu_usage_core_" . $cpu_num . "\n");
  foreach my $data (@data_names) {
    print($data . ".value " . ($cpus{$cpu_num}{'data'}{$data} / $cpus{$cpu_num}{'data'}{'cpu_total'}) * 100 . "\n");
  }
  print("usage.value " . ($cpus{$cpu_num}{'data'}{'total_usage'} / $cpus{$cpu_num}{'data'}{'cpu_total'}) * 100 . "\n");
}

exit(0);

sub read_proc_stat() {
  my $stat_fd;
  if(!open($stat_fd, "</proc/stat")) {
    print("Error: can't read /proc/stat.\n");
    exit(1);
  }
  STAT_READ_LOOP: while(defined(my $line=<$stat_fd>)) {
    if($line =~ m/^cpu\d+\s/) {
      chomp($line);
      my @cpustat = split(" ", $line);
      $stats_fields = $#cpustat;
      my ($core_num) = $cpustat[0] =~ m/^cpu([0-9]+)/;
      my $user;
      my $nice;
      my $system;
      my $idle;
      my $iowait;
      my $irq;
      my $softirq;
      my $steal;
      my $guest;
      my $guest_nice;
      $user = $cpustat[1] || 0;
      $nice = $cpustat[2] || 0;
      $system = $cpustat[3] || 0;
      $idle = $cpustat[4] || 0;
      $iowait = $cpustat[5] || 0;
      $irq = $cpustat[6] || 0;
      $softirq = $cpustat[7] || 0;
      $steal = $cpustat[8] || 0;
      $guest = $cpustat[9] || 0;
      $guest_nice = $cpustat[10] || 0;
      $user -= $guest;
      $nice -= $guest_nice;
      if(defined($cpus{$core_num})) {
          $cpus{$core_num}{'data'}{'user'} = $user - $cpus{$core_num}{'data'}{'user'};
          $cpus{$core_num}{'data'}{'nice'} = $nice - $cpus{$core_num}{'data'}{'nice'};
          $cpus{$core_num}{'data'}{'system'} = $system - $cpus{$core_num}{'data'}{'system'};
          $cpus{$core_num}{'data'}{'idle'} = $idle - $cpus{$core_num}{'data'}{'idle'};
          $cpus{$core_num}{'data'}{'iowait'} = $iowait - $cpus{$core_num}{'data'}{'iowait'};
          $cpus{$core_num}{'data'}{'irq'} = $irq - $cpus{$core_num}{'data'}{'irq'};
          $cpus{$core_num}{'data'}{'softirq'} = $softirq - $cpus{$core_num}{'data'}{'softirq'};
          $cpus{$core_num}{'data'}{'steal'} = $steal - $cpus{$core_num}{'data'}{'steal'};
          $cpus{$core_num}{'data'}{'guest'} = $guest - $cpus{$core_num}{'data'}{'guest'};
          $cpus{$core_num}{'data'}{'guest_nice'} = $guest_nice - $cpus{$core_num}{'data'}{'guest_nice'};
        }
      else {
        $cpus{$core_num}{'data'} = {
          'user' => $user,
          'nice' => $nice,
          'system' => $system,
          'idle' => $idle,
          'iowait' => $iowait,
          'irq' => $irq,
          'softirq' => $softirq,
          'steal' => $steal,
          'guest' => $guest,
          'guest_nice' => $guest_nice
        };
      }
    $cpus{$core_num}{'data'}{'total_usage'} = 
      $cpus{$core_num}{'data'}{'user'} +
      $cpus{$core_num}{'data'}{'nice'} +
      $cpus{$core_num}{'data'}{'system'} +
      $cpus{$core_num}{'data'}{'iowait'} +
      $cpus{$core_num}{'data'}{'irq'} +
      $cpus{$core_num}{'data'}{'softirq'} +
      $cpus{$core_num}{'data'}{'steal'} +
      $cpus{$core_num}{'data'}{'guest'} +
      $cpus{$core_num}{'data'}{'guest_nice'};
    $cpus{$core_num}{'data'}{'cpu_total'} = $cpus{$core_num}{'data'}{'idle'} + $cpus{$core_num}{'data'}{'total_usage'};
    $cpus{$core_num}{'percentage'} = ($cpus{$core_num}{'data'}{'total_usage'} / $cpus{$core_num}{'data'}{'cpu_total'}) * 100;
    }
  }
  close($stat_fd);
  $cpus{'read_count'}++;
}
