#!/usr/bin/perl

###############################################################################
#
# fcstats.pl - Display FC host adapter stats
#
# Copyright (c) by  Oliver Falk, 2012-2013
#                   oliver@linux-kernel.at
#
# Changes are welcome, but please inform me about those!
#
###############################################################################

use strict;
use warnings;

use File::Glob ':glob';
use File::Basename;
use Time::HiRes;
use Time::HiRes qw/tv_interval gettimeofday/;
use Data::Dumper; # debug only
use Getopt::Long;

use bignum qw/hex/;

use constant PATH => '/sys/class/fc_host/host*';

my $secs = 1;
my ($abbr, $in, $out);
my $type = 'm';
my $result = GetOptions(
	"secs|s=i"	=> \$secs,
	"type|t=s"	=> \$type,
);

if($type eq 'm') {
	$in = 'fcp_input_megabytes';
	$out = 'fcp_output_megabytes';
	$abbr = 'MB';
} elsif($type eq 'w') {
	$in = 'rx_words';
	$out = 'tx_words';
	$abbr = 'words';
} elsif($type eq 'f') {
	$in = 'rx_frames';
	$out = 'tx_frames';
	$abbr = 'frames';
} else {
	die "Type '$type' unknown (possible values: 'm' (Megabyte), 'w' (Words), 'f' (Frames)";
}

my @hba = bsd_glob(PATH);

die "No FC adapters found. /sys mounted!?" unless @hba;

my $t0 = [gettimeofday];
my $hbas;
while(1) {
	my $elapsed = tv_interval ($t0);
	print "\033[2J";    #clear the screen
	print "\033[0;0H"; #jump to 0,0
	foreach(@hba) {
		my $hbaname = basename($_);

		open(FN, "$_/node_name");
		$hbas->{$hbaname}->{node_name} = <FN>;
		chomp($hbas->{$hbaname}->{node_name});
		close(FN);

		open(FH, "$_/port_state");
		$hbas->{$hbaname}->{portstate} = <FH>;
		chomp($hbas->{$hbaname}->{portstate});
		close(FH);
		
		unless($hbas->{$hbaname}->{portstate} eq 'Linkdown') {
			open(FH, "$_/statistics/$in");
			$hbas->{$hbaname}->{rx} = <FH>;
			chomp($hbas->{$hbaname}->{rx});
			$hbas->{$hbaname}->{rx} = hex($hbas->{$hbaname}->{rx});
			close(FH);
			$hbas->{$hbaname}->{diff_rx} = $hbas->{$hbaname}->{rx} - ($hbas->{$hbaname}->{last_rx}||$hbas->{$hbaname}->{rx});
			$hbas->{$hbaname}->{last_rx} = $hbas->{$hbaname}->{rx};
			$hbas->{$hbaname}->{rx_ps} = $hbas->{$hbaname}->{diff_rx} / $elapsed;

			open(FH, "$_/statistics/$out");
			$hbas->{$hbaname}->{tx} = <FH>;
			chomp($hbas->{$hbaname}->{tx});
			$hbas->{$hbaname}->{tx} = hex($hbas->{$hbaname}->{tx});
			close(FH);
			$hbas->{$hbaname}->{diff_tx} = $hbas->{$hbaname}->{tx} - ($hbas->{$hbaname}->{last_tx}||$hbas->{$hbaname}->{tx});
			$hbas->{$hbaname}->{last_tx} = $hbas->{$hbaname}->{tx};
			$hbas->{$hbaname}->{tx_ps} = $hbas->{$hbaname}->{diff_tx} / $elapsed;

			printf("%-6s [%s]: RX: %10d $abbr (%7.2f $abbr/s), TX: %10d $abbr (%7.2f $abbr/s)\n",
				$hbaname, $hbas->{$hbaname}->{node_name}, $hbas->{$hbaname}->{rx},
				$hbas->{$hbaname}->{rx_ps}, $hbas->{$hbaname}->{tx}, $hbas->{$hbaname}->{tx_ps});
		} else {
			printf("%-6s [%s]: Link down\n", $hbaname, $hbas->{$hbaname}->{node_name});
		}
	}
	# print Dumper($hbas); # debug only
	exit 0 unless $secs;
	$t0 = [gettimeofday];
	print "Update interval: $secs seconds\n";
	sleep $secs;
}

1;

__END__

=head1 NAME

fcstats.pl

=head1 SYNOPSIS

  perl fcstats.pl

=head1 DESCRIPTION

Display FC host adapter stats

=head2 EXPORT

None.

=head1 AUTHOR

Oliver Falk, E<lt>oliver@linux-kernel.at<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2012-2013 by Oliver Falk

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.

=cut