Repository
Munin (contrib)
Last change
2020-03-26
Capabilities
Keywords
Language
Perl

emc_comprehensive

Sadly there is no documentation for this plugin.

#! /usr/bin/perl

use strict;
use warnings;

use IO::File;
use Data::Dumper;
use POSIX qw(strftime);

$ENV{LANG} = "C";

my $spool_fetch_epoch = (shift || 0);

# Don't reply for too old $spool_fetch_epoch)
# Max 7 days
$spool_fetch_epoch = time - 3600 * 24 * 7 if $spool_fetch_epoch < time - 3600 * 24 * 7;

my $max_epoch = ($ENV{MAX_EPOCH} || 0);
my %fieldset;

my @files = sort glob($ENV{FILES} || "*.tsv*");
FILE: for my $file (@files) {
	my $values = {};
	my $nb_epochs = 0; my $first_epoch;

	# Read file
	{
		# Skipping if the file is too old
		# (more that one day older than asked)
		next if file_mtime($file) < $spool_fetch_epoch - (3600 *24);

		print STDERR "opening $file\n" if $ENV{DEBUG};
		my $fh = new IO::File( ($file =~ m/\.gz$/) ? "gunzip < $file |" : $file);
		next unless $fh;

		$_ = <$fh>; chomp;
		my @headers = split(/\t/);

		# Ignore 3 first fields (Object Name, Epoch & Owner Array Name)
		shift @headers; shift @headers; shift @headers;

		my $nb_headers = $#headers;
		LINE: while(<$fh>) {
			chomp;
			my @row = split(/\t/, $_);
			my $object_name = shift @row;
			my $epoch = shift @row;
			my $owner_array_name = shift @row;

			# Ignore if too old
			next if ($epoch <= $spool_fetch_epoch);

			# Don't do too much work : 4h each time is enough
			$first_epoch ||= $epoch;
			next if $epoch > $first_epoch + 60 * 60 * 4;


			# Store Values
			for (my $idx = 0; $idx < $nb_headers; $idx ++) {
				my $value = $row[$idx];
				my $field_name = $headers[$idx];

				# Ignore empty values
				next unless (defined $value && $value ne "");

				# Ignore non numeric values
				next unless $value =~ m/^[0-9.]+$/;

				# Ignore Optimal/NonOptimal values
				next unless ($fieldset{$field_name} || $field_name !~ /[oO]ptimal/);
				$fieldset{$field_name} = 1 unless $fieldset{$field_name};

				if ($ENV{DEBUG}) {
					no warnings;
					print "object_name:$object_name, field_name:$field_name, epoch:$epoch, value:$value\n";
				}


				$values->{$object_name}->{$field_name} .= "$epoch:$value ";
				$nb_epochs ++;
			}
		}
	}

	# Don't emit anything if the file didn't contain any useful value
	next unless $nb_epochs;

	# Restitution MultiGraph
		print <<EOF
multigraph san
graph_title Vue globale
graph_order \
	cpu_sp_a=san.cpu.sp_a \
	cpu_sp_b=san.cpu.sp_b
EOF
		;
	# CPU
	my @object_names = keys %$values;
	gen_multigraph(
		$values,
		"cpu",
		[  grep { /^SP / } @object_names ],
	);

	# DISK
	gen_multigraph(
		$values,
		"disk",
		[  grep { /^Bus \d+/ } @object_names ],
	);

	# Port
	gen_multigraph(
		$values,
		"port",
		[  grep { /^Port / } @object_names ],
		#sub { return $1 if shift =~ m/^(Port \w+)/ },
	);

	# Pool
	gen_multigraph(
		$values,
		"pool",
		[  grep { /^Pool / } @object_names ],
	);

	# LUN Global
		print <<EOF
multigraph san.lun
graph_title Luns Global
graph_order \
	cpu_sp_a=san.cpu.sp_a \
	cpu_sp_b=san.cpu.sp_b
EOF
;
	# LUN Per Host
	my %host_seen;
	my @hosts = grep { $_ ne "" } grep { ! $host_seen{$_}++ }
			map { $1 if /^\w+ \[\d+; (\w+)/ } @object_names;
	for my $host (@hosts) {
		my $host_field = $host;
		$host_field =~ tr/./_/;
		gen_multigraph(
			$values,
			"lun.$host_field",
			[  grep { /^\w+ \[\d+; $host/ } @object_names ],
			sub { return $1 if shift =~ m/^(\w+) / },
			sub { return $1 if shift =~ m/^(\w+) / },
		);
	}

	# Never send more than 1 file
	last;
}

sub gen_multigraph
{
	my ($values, $category, $object_names, $convert_to_label, $convert_to_field) = @_;

	# convert_to_label() reverts to identity if not defined
	$convert_to_label ||= sub { return $_[0]; };
	# convert_to_field() reverts to convert_to_label() if not defined
	$convert_to_field ||= $convert_to_label;

#	print Dumper($object_names);
	# Global Graph
	print <<EOF
multigraph san.$category
graph_title $category Global
graph_order \
	cpu_sp_a=san.cpu.sp_a \
	cpu_sp_b=san.cpu.sp_b
EOF
;

	my @fields = keys %{ $values->{$object_names->[0]} };
	for my $field (@fields) {
		my $graph_name = hash_field_name($field);
		print "multigraph san.$category.$graph_name\n";
		print "graph_title $field\n";

		for my $object_name (@$object_names) {
			my $label = &$convert_to_label($object_name);
			my $field_name = hash_field_name(&$convert_to_field($object_name));
			print "$field_name.label $label\n";
			print "$field_name.info $object_name\n";
			for my $value (split(/ /, $values->{$object_name}->{$field})) {
				print $field_name , ".value " , $value , "\n";
			}
		}
	}
}

sub hash_field_name
{
	my $name = shift;
	$name = lc($name);
	$name =~ s/[^a-z0-9]+/_/g;
	$name =~ s/^_//;
	$name =~ s/_$//;
	return $name;
}

sub trim
{
	my $line = shift;
	$line =~ s/^ +//;
	$line =~ s/ +$//;
	return $line;
}

sub file_mtime
{
	my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat(shift);
	return $mtime;
}

__DATA__

sb


multigraph san.cpu
graph_title Usage CPU
cpu_sp_a.value $epoch:$cpu_sp_a
cpu_sp_b.value $epoch:$cpu_sp_b
multigraph san.resptime
graph_title Response Time
resp_time_a.value $epoch:$resp_time_a
resp_time_b.value $epoch:$resp_time_b
multigraph san.iops
graph_title IOPS
read_io_a.value $epoch:$read_io_a
write_io_a.value $epoch:$write_io_a
read_io_b.value $epoch:$read_io_b
write_io_b.value $epoch:$write_io_b
multigraph san.io
graph_title IO
read_mb_a.value $epoch:$read_mb_a
write_mb_a.value $epoch:$write_mb_a
read_mb_b.value $epoch:$read_mb_b
write_mb_b.value $epoch:$write_mb_b
multigraph san.cache
graph_title Cache
cache_mb_flush_a.value $epoch:$cache_mb_flush_a
write_cache_mb_flush_a.value $epoch:$write_cache_mb_flush_a
cache_mb_flush_b.value $epoch:$cache_mb_flush_b
write_cache_mb_flush_b.value $epoch:$write_cache_mb_flush_b
EOF
;
	}
}

print ".\n";

my %MONTHS = get_months();
sub convert_to_epoch
{
	# converts "05/12/2011 03:57" to EPOCH
	my ($date, $time) = split(/ /);
	my ($mday, $mon, $year) = split(/\//, $date);
	my ($hour, $min, $sec) = split(/:/, $time);
	$sec ||= 0;

	use Time::Local;
	$mon = $MONTHS{lc($mon)} unless $mon =~ m/\d+/;
	my $epoch = timelocal($sec,$min,$hour,$mday,$mon-1,$year);
	return $epoch;
}

sub get_months {
	return (
		"jan" => 0,
		"fev" => 1,
		"mar" => 2,
		"apr" => 3,
		"may" => 4,
		"jun" => 5,
		"jul" => 6,
		"aug" => 7,
		"sep" => 8,
		"oct" => 9,
		"nov" => 10,
		"dec" => 11,
	);
}

sub trim {
	shift;
	s/^\s+//;
	s/\s+$//;
	return $_;
}

__DATA__

05/12/2011 03:57


	print <<EOF
multigraph san
graph_title Vue globale
graph_order \
	cpu_sp_a=san.cpu.cpu_sp_a \
	cpu_sp_b=san.cpu.cpu_sp_b
multigraph san.cpu
graph_title Utilization (%)
cpu_sp_a.label Utilization (%) for SP A
cpu_sp_b.label Utilization (%) for SP B
cpu_sp_a.value $values->{"SP A"}->{"Utilization (%)"}
cpu_sp_b.value $values->{"SP B"}->{"Utilization (%)"}
multigraph san.bw
graph_title Total Bandwidth (MB/s)
iops_a.label Total Throughput (IO/s) for SP A
iops_b.label Total Throughput (IO/s) for SP B
iops_a.value $values->{"SP A"}->{"Total Throughput (IO/s)"}
iops_b.value $values->{"SP B"}->{"Total Throughput (IO/s)"}
multigraph san.iops
graph_title Total Throughput (IO/s)
iops_a.label Total Throughput (IO/s) for SP A
iops_b.label Total Throughput (IO/s) for SP B
iops_a.value $values->{"SP A"}->{"Total Throughput (IO/s)"}
iops_b.value $values->{"SP B"}->{"Total Throughput (IO/s)"}
EOF
;