Skip to content

Instantly share code, notes, and snippets.

@aleks-mariusz
Last active August 29, 2015 14:00
Show Gist options
  • Save aleks-mariusz/3cf2c7e808c1728ab376 to your computer and use it in GitHub Desktop.
Save aleks-mariusz/3cf2c7e808c1728ab376 to your computer and use it in GitHub Desktop.
May 17 2005/June 17 2005 - When working for the city of new york, we had a lot of systems we needed a better way to get a handle on their utilization. i threw together this perl script over the course of a week or so to parse the output of sar (on solaris) and create some basic excel spreadsheets for easier consumption of usage statistics.. The …
#!/usr/bin/perl
$| = 1;
BEGIN { unshift(@INC, "/usr/perl5/site_perl/5.005" ); };
use strict;
use Time::ParseDate;
use Spreadsheet::WriteExcel;
unless ($#ARGV == 0) {
my $progName = ($0 =~ m#^.+/([^/]+)$#)[0];
print "Usage: $progName <pathToPerf.SHdirectory>\n\n";
exit 1;
}
my $perfDir = shift;
if (! -d $perfDir) {
print "No such directory: $perfDir\n";
exit 1;
}
my $dataSet;
$dataSet = &loadData($perfDir);
&generateReport($dataSet);
use Data::Dumper; print &Dumper($dataSet);
sub loadData { my $dir = shift;
my %data;
print "Now loading data from $dir (please wait).. ";
foreach my $currType (qw/iostat mpstat netstat vmstat/) {
my $currFile = (glob("$dir/$currType.*"))[0];
next unless -f $currFile;
print "\n\t- processing $currType data.. ";
&loadDataFile(\%data,$currFile,$currType);
}
print "\nDone loading data into memory!\n\n";
return \%data;
}
sub loadDataFile { my ($ds,$file,$type) = @_;
my $setDate; # current run's start date
my @field; # will contain the field/key names
open FILE, $file || die "Cannot open file $1: $!\n";
undef $/; # slurp entire file into one scalar variable
$file = <FILE>; # read file into memory
close FILE;
if ($type eq "iostat") {
my %tempIOSTAT; # hash to hold split io runs by start date
# list of io runs split up per execution
my @dateList = split /\n\n(Sun|Mon|Tue|Wed|Thu|Fri|Sat) /, $file;
# go through each execution
foreach my $currSet (@dateList) {
next if $currSet =~ /^\w{3}$/; # the day of the week is all we have, skip it..
# grab the output of the 'date' command that was saved immediatley before io run started
$setDate = $1 if $currSet =~ /(\w+\s+\d+\s+\d\d:\d\d:\d\d\s+[A-Z]{3}\s+\d{4})/;
# and split/save each "line" from iostat
$tempIOSTAT{$setDate} = [ split /\n\s+extended device statistics/, $currSet ];
} # done going through all explicit dates
# get a list of dates, in chronological order
@dateList = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, &parsedate($_) ] } keys %tempIOSTAT;
# we'll go through each date from beginning to end (may want to filter this)
for (my $i = 0; $i < $#dateList; ++$i) {
# figure out the bounds for this iostat's run
my $baseTS = &parsedate($dateList[$i]);
my $endTS = &parsedate($dateList[$i+1]);
# save the individual "lines" of this iostat
my @iostats = @{$tempIOSTAT{$dateList[$i]}};
shift @iostats; # very first line is blank
shift @iostats; # first "line" is sys average since system boot
# we need to determine how much time was used between "lines"
my $interval = sprintf "%d", ($endTS - $baseTS)/scalar(@iostats);
# go through each "line" of iostat's run
for (my $j = 0; $j <= $#iostats; ++$j) {
# figure which time this run represents
my $currTS = $baseTS + $interval*$j;
# split the "line" up each respective device
my @deviceStats = split /\n/, $iostats[$j];
shift @deviceStats; # first line is blank
# extract the field names
@field = split /\s+/, $deviceStats[0];
shift @deviceStats; # done with that, so lose the header information
# go through each remaining device
foreach my $currStat (@deviceStats) {
# split each stat for this device
my @stat = split /\s+/, $currStat;
# save the device's stat
$ds->{$type}->{$currTS}->{$stat[0]} = {
map {
$field[$_], $stat[$_]
} (1 .. $#field)
};
}
}
}
} elsif ($type eq "mpstat") {
my %tempMPSTAT; # hash to hold split cpu stats by start date
# list of cpu stats split up per execution
my @dateList = split /\n\n(Sun|Mon|Tue|Wed|Thu|Fri|Sat) /, $file;
# go through each execution
foreach my $currSet (@dateList) {
next if $currSet =~ /^\w{3}$/; # the day of the week is all we have, skip it..
# grab the output of the 'date' command that was saved immediatley before io run started
$setDate = $1 if $currSet =~ /(\w+\s+\d+\s+\d\d:\d\d:\d\d\s+[A-Z]{3}\s+\d{4})/;
# and split/save each "line" from mpstat
$tempMPSTAT{$setDate} = [ split /\n(?=CPU)/, $currSet ];
} # done going through all explicit dates
# get a list of dates, in chronological order
@dateList = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, &parsedate($_) ] } keys %tempMPSTAT;
# we'll go through each date from beginning to end (may want to filter this)
for (my $i = 0; $i < $#dateList; ++$i) {
# figure out the bounds for this mpstat's run
my $baseTS = &parsedate($dateList[$i]);
my $endTS = &parsedate($dateList[$i+1]);
# save the individual "lines" of this mpstat
my @mpstats = @{$tempMPSTAT{$dateList[$i]}};
shift @mpstats; # very first line is the date
# we need to determine how much time was used between "lines"
my $interval = sprintf "%d", ($endTS - $baseTS)/scalar(@mpstats);
shift @mpstats; # first "line" is sys average since system boot
# go through each "line" of mpstat's run
for (my $j = 0; $j <= $#mpstats; ++$j) {
# figure which time this run represents
my $currTS = $baseTS + $interval*$j;
# split the "line" up each set of respective cpu statistics
my @cpuStats = split /\n/, $mpstats[$j];
# extract the field names
if ($cpuStats[0] =~ /CPU/) {
@field = split /\s+/, $cpuStats[0];
shift @cpuStats; # done with that, so lose the header information
}
# go through each remaining CPU
foreach my $currCPU (@cpuStats) {
# split each stat for this CPU
my @CPU = split /\s+/, $currCPU;
# save the cpu's stat
$ds->{$type}->{$currTS}->{$CPU[1]} = {
map {
$field[$_], $CPU[$_+1]
} (1 .. $#field)
};
}
}
}
} elsif ($type eq "netstat") {
my %tempNETSTAT; # hash to hold split cpu stats by start date
# list of cpu stats split up per execution
my @dateList = split /\n\n(Sun|Mon|Tue|Wed|Thu|Fri|Sat) /, $file;
# go through each execution
foreach my $currSet (@dateList) {
next if $currSet =~ /^\w{3}$/; # the day of the week is all we have, skip it..
# grab the output of the 'date' command that was saved immediatley before io run started
$setDate = $1 if $currSet =~ /(\w+\s+\d+\s+\d\d:\d\d:\d\d\s+[A-Z]{3}\s+\d{4})/;
# and split/save each "line" from netstat
$tempNETSTAT{$setDate} = [ split /\n(?=\d)/, $currSet ];
} # done going through all explicit dates
# get a list of dates, in chronological order
@dateList = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, &parsedate($_) ] } keys %tempNETSTAT;
# we'll go through each date from beginning to end (may want to filter this)
for (my $i = 0; $i < $#dateList; ++$i) {
# figure out the bounds for this netstat's run
my $baseTS = &parsedate($dateList[$i]);
my $endTS = &parsedate($dateList[$i+1]);
# save the individual "lines" of this netstat
my $fieldInfo = shift @{$tempNETSTAT{$dateList[$i]}}; # contains header information
my @netstats = grep { /^\d/ } @{$tempNETSTAT{$dateList[$i]}};
shift @netstats; # first "line" is sys average since system boot
# we need to determine how much time was used between lines
my $interval = sprintf "%d", ($endTS - $baseTS)/scalar(@netstats);
@field = &parseFieldNames($fieldInfo);
# go through each "line" of netstat's run
for (my $j = 0; $j <= $#netstats; ++$j) {
# figure which time this run represents
my $currTS = $baseTS + $interval*$j;
# split the "line" up each respective net statistic
my @netStat = split /\n/, $netstats[$j];
# go through each net statistics
foreach my $currNS (@netStat) {
# split each stat for this network statistic
my @NS = split /\s+/, $currNS;
# save the network's stat
$ds->{$type}->{$currTS} = {
map {
$field[$_], $NS[$_]
} (0 .. $#field)
};
}
}
}
} elsif ($type eq "vmstat") {
my %tempVMSTAT; # hash to hold split cpu stats by start date
# list of vm stats split up per execution
my @dateList = split /\n\n(Sun|Mon|Tue|Wed|Thu|Fri|Sat) /, $file;
# go through each execution
foreach my $currSet (@dateList) {
next if $currSet =~ /^\w{3}$/; # the day of the week is all we have, skip it..
# grab the output of the 'date' command that was saved immediatley before io run started
$setDate = $1 if $currSet =~ /(\w+\s+\d+\s+\d\d:\d\d:\d\d\s+[A-Z]{3}\s+\d{4})/;
# and split/save each "line" from vmstat
$tempVMSTAT{$setDate} = [ split /\n\s+(?=\d)/, $currSet ];
} # done going through all explicit dates
# get a list of dates, in chronological order
@dateList = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, &parsedate($_) ] } keys %tempVMSTAT;
# we'll go through each date from beginning to end (may want to filter this)
for (my $i = 0; $i < $#dateList; ++$i) {
# figure out the bounds for this vmstat's run
my $baseTS = &parsedate($dateList[$i]);
my $endTS = &parsedate($dateList[$i+1]);
# save the individual "lines" of this vmstat
my $fieldInfo = shift @{$tempVMSTAT{$dateList[$i]}}; # contains header information
my @vmstats = map { (/^([^\n]+)/)[0] } @{$tempVMSTAT{$dateList[$i]}};
shift @vmstats; # first "line" is sys average since system boot
# we need to determine how much time was used between lines
my $interval = sprintf "%d", ($endTS - $baseTS)/scalar(@vmstats);
@field = &parseFieldNames($fieldInfo);
# go through each "line" of vmstat's run
for (my $j = 0; $j <= $#vmstats; ++$j) {
# figure which time this run represents
my $currTS = $baseTS + $interval*$j;
# split the line up each set of statistic
my @vmStat = split /\n/, $vmstats[$j];
# go through each set of statistic
foreach my $currVMS (@vmStat) {
# split each stat for this set
my @VMS = split /\s+/, $currVMS;
# save the stat
$ds->{$type}->{$currTS} = {
map {
$field[$_], $VMS[$_]
} (0 .. $#field)
};
}
}
}
}
}
sub parseFieldNames { my $fieldInfo = shift;
my @fieldNames;
my @fieldHeaderLines = split /\n/, $fieldInfo;
if ($fieldHeaderLines[-2] =~ /\(Total\)/) {
# these are netstat fields
# input ce0 output input (Total) output
# packets errs packets errs colls packets errs packets errs colls
my $intName = "mainInt";
$intName = $1 if $fieldHeaderLines[-2] =~ /^\s+input\s+(\S+)\s+output/;
@fieldNames = ( "$intName-input-packets",
"$intName-input-errs",
"$intName-output-packets",
"$intName-output-errs",
"$intName-colls",
"Total-input-packets",
"Total-input-errs",
"Total-output-packets",
"Total-output-errs",
"Total-colls"
);
} elsif ($fieldHeaderLines[-2] =~ /memory/) {
# these are vmstat fields
# kthr memory page disk faults cpu
# r b w swap free re mf pi po fr de sr m0 m1 m2 m1 in sy cs us sy id
my $diskNames;
$diskNames = $1 if
$fieldHeaderLines[-1] =~
/\s+r\s+b\s+w\s+swap\s+free\s+re\s+mf\s+pi\s+po\s+fr\s+de\s+sr\s+(.+)\s+in\s+sy\s+cs\s+us\s+sy\s+id/;
my @diskNames = map { "disk-$_" } (split /\s+/, $diskNames);
@fieldNames = ( "kthr-r",
"kthr-b",
"kthr-w",
"memory-swap",
"memory-free",
"page-re",
"page-mf",
"page-pi",
"page-po",
"page-fr",
"page-de",
"page-sr",
@diskNames,
"faults-in",
"faults-sy",
"faults-cs",
"cpu-us",
"cpu-sy",
"cpu-id"
);
}
return (@fieldNames);
}
sub generateReport { my $ds = shift;
my ($timeStart,$timeEnd,$workBook,$workSheet,$tsArrayVM,$tsArrayNS,$tsArrayIO,$interval,$fmt);
$/ = "\n";
# do { print "Please enter date/time or timestamp of when you want reporting to begin: ";
# $timeStart = <>; chomp $timeStart;
# print "\n";
# $timeStart = &parsedate($timeStart) if $timeStart !~ /^\d+$/;
#
#} while ($timeStart !~ /^\d+$/);
#do { print "Please enter date/time or timestamp of when you want reporting to stop: ";
# $timeEnd = <>; chomp $timeEnd;
# print "\n";
# $timeEnd = &parsedate($timeEnd) if $timeEnd !~ /^\d+$/;
#} while ($timeEnd !~ /^\d+$/);
($timeStart,$timeEnd) = (0,99999999999999999);
# create a list of arrayrefs for each set of data, for the interest set of times/dates requested
$tsArrayNS = [ sort { $a <=> $b } grep { $_ >= $timeStart && $_ <= $timeEnd } (keys %{$ds->{'netstat'}}) ];
$tsArrayVM = [ sort { $a <=> $b } grep { $_ >= $timeStart && $_ <= $timeEnd } (keys %{$ds->{'vmstat'}}) ];
$tsArrayIO = [ sort { $a <=> $b } grep { $_ >= $timeStart && $_ <= $timeEnd } (keys %{$ds->{'iostat'}}) ];
$workBook = Spreadsheet::WriteExcel->new("perf-cpu.xls");
$fmt = $workBook->add_format();
$fmt->set_rotation(60); $fmt->set_align('center'); $fmt->set_align('bottom'); $fmt->set_border(1);
$workSheet = $workBook->add_worksheet("Processor");
$workSheet->set_column('A:A', 16);
$workSheet->set_column('B:F', 5);
$workSheet->set_row(0, 100);
$workSheet->write_row(
0,
0,
[ "Time",
"CPU Utilization",
"CPU in System Mode",
"CPU in User Mode",
"Interrupt/sec",
"Context Switch/sec"
],
$fmt
);
$fmt = $workBook->add_format(); $fmt->set_align('center');
for (my $i = 0; $i < $#$tsArrayVM; ++$i ) {
# $interval = $tsArrayVM->[$i+1]-$tsArrayVM if $tsArrayVM->[$i+1];
$workSheet->write_row(
$i+1,
0,
[ &formatDate($tsArrayVM->[$i]),
(100 - $ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'cpu-id'})."%",
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'cpu-sy'}."%",
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'cpu-us'}."%",
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'faults-in'},
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'faults-cs'},
],
$fmt
);
}
$workBook->close();
$workBook = Spreadsheet::WriteExcel->new("perf-mem.xls");
$workSheet = $workBook->add_worksheet("Memory System");
$fmt = $workBook->add_format();
$fmt->set_rotation(60); $fmt->set_align('center'); $fmt->set_align('bottom'); $fmt->set_border(1);
$workSheet->set_column('A:A', 16);
$workSheet->set_column('B:D', 5);
$workSheet->set_row(0, 100);
$workSheet->write_row(
0,
0,
[ "Time",
"Page-in rate/sec",
"Page-out rate/sec",
"Paging rate/sec",
],
$fmt
);
$fmt = $workBook->add_format(); $fmt->set_align('center');
for (my $i = 0; $i < $#$tsArrayVM; ++$i ) {
# $interval = $tsArrayVM->[$i+1]-$tsArrayVM if $tsArrayVM->[$i+1];
$workSheet->write_row(
$i+1,
0,
[ &formatDate($tsArrayVM->[$i]),
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'page-pi'},
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'page-po'},
$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'page-pi'}+$ds->{'vmstat'}->{$tsArrayVM->[$i]}->{'page-po'}
],
$fmt
);
}
$workBook->close();
$workBook = Spreadsheet::WriteExcel->new("perf-io.xls");
$workSheet = $workBook->add_worksheet("Disk IO");
$workSheet->set_column('A:A', 16);
$workSheet->set_column('B:C', 10);
$workSheet->set_row(0, 100);
$fmt = $workBook->add_format();
$fmt->set_rotation(60); $fmt->set_align('center'); $fmt->set_align('bottom'); $fmt->set_border(1);
$workSheet->write_row(
0,
0,
[ "Time",
"Disk Read rate/s",
"Disk Write rate/s",
],
$fmt
);
$fmt = $workBook->add_format(); $fmt->set_align('center');
for (my $i = 0; $i < $#$tsArrayIO; ++$i ) {
# $interval = $tsArrayIO->[$i+1]-$tsArrayIO if $tsArrayIO->[$i+1];
$workSheet->write_row(
$i+1,
0,
[ &formatDate($tsArrayIO->[$i]),
$ds->{'iostat'}->{$tsArrayIO->[$i]}->{'md0'}->{'kr/s'},
$ds->{'iostat'}->{$tsArrayIO->[$i]}->{'md0'}->{'kw/s'},
],
$fmt
);
}
$workBook->close();
$workBook = Spreadsheet::WriteExcel->new("perf-network.xls");
$workSheet = $workBook->add_worksheet("Network");
$workSheet->set_column('A:A', 16);
$workSheet->set_column('B:F', 8);
$workSheet->set_row(0, 100);
$fmt = $workBook->add_format();
$fmt->set_rotation(60); $fmt->set_align('center'); $fmt->set_align('bottom'); $fmt->set_border(1);
$workSheet->write_row(
0,
0,
[ "Time",
"Collision rate/min",
"Incoming pkt error/min",
"Incoming pkt rate/min",
"Outgoing pkg error/min",
"Outgoing pkg rate/min"
],
$fmt
);
$fmt = $workBook->add_format(); $fmt->set_align('center');
for (my $i = 0; $i < $#$tsArrayNS; ++$i ) {
$interval = $tsArrayNS->[$i+1]-$tsArrayNS->[$i] if $tsArrayNS->[$i+1];
$workSheet->write_row(
$i+1,
0,
[ &formatDate($tsArrayNS->[$i]),
int(60*$ds->{'netstat'}->{$tsArrayNS->[$i]}->{'Total-colls'}/$interval),
int(60*$ds->{'netstat'}->{$tsArrayNS->[$i]}->{'Total-input-errs'}/$interval),
int(60*$ds->{'netstat'}->{$tsArrayNS->[$i]}->{'Total-input-packets'}/$interval),
int(60*$ds->{'netstat'}->{$tsArrayNS->[$i]}->{'Total-output-errs'}/$interval),
int(60*$ds->{'netstat'}->{$tsArrayNS->[$i]}->{'Total-output-packets'}/$interval),
],
$fmt
);
}
$workBook->close();
}
sub formatDate {
my $timeStamp = shift || return;
my @tempTime = localtime($timeStamp); # format of the moth names
# return a nice string with the appropriately formatted date
return sprintf "%02d/%02d/%02d %02d:%02d:%02d",
$tempTime[4]+1, $tempTime[3], $tempTime[5]-100, $tempTime[2], $tempTime[1], $tempTime[0];
}
#!/usr/bin/perl
$| = 1;
use strict; no strict 'refs'; # discipline is good
use Time::ParseDate;
use Spreadsheet::WriteExcel;
use Spreadsheet::WriteExcel::Utility;
die "Need exactly one argument: file with list of hosts to query!" unless $#ARGV == 0;
my %sar; # will contain sar info on a per host basis
my %mem; # will contain amount of system memory on a per host basis
my %date; # will contain list of valid dates to report fields for
my @host; # read in from a file, list of hosts to log into
my %hostRow; # will hold what row the host ended up being in
my $sarThreshold = 0; # need at least this many hosts with the date to include in the final report
my ($sarTimeFrom,$sarTimeTo) = qw/0 23/; # what times of day to look at values (in 24-hr time)
my ($sarDayFrom,$sarDayTo) = qw/0 6/; # what days of the week to look at values (0 - Sun, 6 - Sat)
my ($row,$col);
my @dataType = qw/CPU-Avg MEM-Avg/;
my @prog = qw/x | \/ - \\/; # progress indicator characters
my $prog = 0; # progress indicator counter
my @timeList = localtime(time);
my $nowYear = $timeList[5]+1900;# the ls output needs to be corrected for the appropriate
my $nowMon = $timeList[4]; #+year, and these values help figure out what the year is
my @monthName = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; # used to populate the hash
# hash of month name to numeric translations (so i can avoid using the 3rd party Time::ParseDate module
my %monthName = map { $monthName[$_], $_ } (0 .. $#monthName);
# will be used as part of the filename
my $filename = "weekly-".&formatDate($monthName[$nowMon]." ".$timeList[3]." ".$nowYear).".xls";
my $workBook = Spreadsheet::WriteExcel->new($filename);
my $fmtPercentage = $workBook->add_format(); $fmtPercentage->set_num_format('0.0%');
my $fmtPercentage2 = $workBook->add_format(); $fmtPercentage2->set_num_format('0.0%'); $fmtPercentage2->set_fg_color('yellow');
my $fmtRotated = $workBook->add_format(); $fmtRotated->set_num_format('dd-mm'); $fmtRotated->set_rotation(90); $fmtRotated->set_align('right'); $fmtRotated->set_align('bottom');
undef $/; # we'll use an undefined field seperator to slurp the entire file into a scalar
# here we'll read in the file and put it into a array
open HOSTLIST, $ARGV[0] || die "Cannot open $ARGV[0]: $!\n"; {
my $hostList = <HOSTLIST>; # defined in 'local' context, doesn't exist afterwards
@host = split /\n/, $hostList; } #+which is my sorry way of saving memory, lol
close HOSTLIST;
print "\nNow logging into hosts & retrieving information.. ";
foreach my $currHost (grep { !/^=/ } @host) {
++$prog; print "\x08"; print $prog[$prog%$#prog+1]; # progress indicator
# log into each host, run
# 1.) prtconf
# a.) grep for memroy
# 3.) bash with sh-style for loop to run on all four-letter files in /var/adm/sa/ that begin with "sa"
# a.) run an ls -ld on them (to get the timestamp)
# b.) run sar -u to get cpu info from that file
# c.) run sar -r to get mem info from that file
# my $sshOutSAR = `ssh $currHost "/etc/swap -s; /usr/sbin/prtconf|grep Memory;bash -c 'for i in /var/adm/sa/sa??; do ls -ld \\\$i; /usr/bin/sar -u -f \\\$i; /usr/bin/sar -r -f \\\$i; done'" 2>/dev/null`;
my $sshOutSAR = `ssh $currHost "/usr/sbin/prtconf|grep Memory;bash -c 'for i in /var/adm/sa/sa??; do ls -ld \\\$i; /usr/bin/sar -u -f \\\$i; /usr/bin/sar -r -f \\\$i; done'" 2>/dev/null`;
# THIS SECTION WORKS ON THE SAR OUTPUT
next unless $sshOutSAR =~ /Average/; # there is nothing to do with this host if no sar (no average reported)
$mem{$currHost} = $1 if $sshOutSAR =~ /Memory\s+size:\s+(\d+)\s+M/; # save amount of memory on system
# $sshOutSAR =~ s/^[^\n]+\n//; #+and remove from input
# for each log file entry up (which consists of the ls, the sar -u and the sar -r)
foreach my $currLog (split /\n-rw/, $sshOutSAR) {
next unless $currLog =~ /Average/; # this is an empty log file if no Average string appears
# break up current log commands between the ls, the sar -u, and the sar -r
my ($logFile,$sarCPU,$sarMEM) = split(/\nSunOS/,$currLog);
next unless $logFile; # if we have no ls, then we have no log file
# retreiving the timestamp on the log file (from the ls)
$logFile =~ /\d+\s(\S+\s+\d+)\s+(\S+)\s\/var\/adm/;
my ($currDay,$currYear) = ($1,$2); # saving the Month N,
if ($currYear =~ /\d+:\d+/) { # check if the ls command included a time instead of a year, this
$currYear = $nowYear; #+means it was less than 6 months ago, so we correct it:
# but if today's month is less than a half year, then any months from the latter twelve
#+months of a year, obvious refer to the last year, here we're correcting for that too
--$currYear if $nowMon < 6 && ($monthName{($currDay =~ /^(\S+)\s/)[0]}) > 5;
}
# we are interested in only running this on certain days.. here we get the day of the week for this date
my $dayOfWeek = (localtime(&parsedate($currDay." ".$currYear)))[6];
next if $dayOfWeek < $sarDayFrom || $dayOfWeek > $sarDayTo; #+ and skip if it isn't in our range
my ( # since the utilization reports are the amount unused, we start with large values
$sarCPUpeak,$sarMEMpeak,$sarSWAPpeak,
$sarCPUavg,$sarMEMavg,$sarSWAPavg,
$avgCounter,$avgSum
) = ( 999,99999999,99999999999,0,0,0,0,0 );
# fetch the average idle cpu time
$avgCounter = 0; $avgSum = 0;
foreach my $currSARcpu (split /\n/, $sarCPU) { # look for the smallest idle
next unless $currSARcpu =~ /^(\d+):\d+:\d+\s+\d+\s+\d+\s+\d+\s+(\d+)/;
next unless $1 >= $sarTimeFrom && $1 <= $sarTimeTo; # only interested in certain times of day
++$avgCounter; $avgSum += $2;
$sarCPUpeak = $2 if $2 < $sarCPUpeak;
}
$sarCPUavg = ($avgSum/$avgCounter) unless $avgCounter == 0; # calculate average
# fetch the average free memory
$avgCounter = 0; $avgSum = 0;
foreach my $currSARmem (split /\n/, $sarMEM) { # look for the smallest free-mem
next unless $currSARmem =~ /^(\d+):\d+:\d+\s+(\d+)\s+/;
next unless $1 >= $sarTimeFrom && $1 <= $sarTimeTo; # only interested in certain times of day
++$avgCounter; $avgSum += $2;
$sarMEMpeak = $2 if $2 < $sarMEMpeak;
}
$sarMEMavg = ($avgSum/$avgCounter) unless $avgCounter == 0; # calculate average
++$date{$currDay. " ". $currYear}; # retain this date for when we print out the column headers
# save the avg/peak cpu/mem values based on date, in this elaborate data structure
$sar{$currHost}->{$currDay. " ". $currYear} = {
'CPU' => {
'Avg' => $sarCPUavg,
},
'MEM' => {
'Avg' => $sarMEMavg,
}
};
} # done going through each log file
} # done going through all hosts
#print "\x08 \n\nCut and Paste this into Excel\n";
print "\x08Done\n\nNow creating Excel speadsheet (\"$filename\")... ";
# debugging purposes only
#use Data::Dumper;
#print &Dumper(\@host);
#print &Dumper(\%sar);
#print &Dumper(\%fs);
# get a list of times, in chronological order
@timeList = reverse ( (
map {
$_->[0]
} sort {
$b->[1] <=> $a->[1] || $b->[2] <=> $a->[2] || $b->[3] <=> $a->[3] # reverse chron order
} map { # Scwartzian transform sort technique
[ $_, ($_ =~ /(\d{4}$)/)[0], $monthName{($_ =~ /^(\w+)\s/)[0]}, ($_ =~ /(\d+)\s\d{4}/)[0] ]
} grep {
$date{$_} > $sarThreshold
} keys %date
)[0..6]
);
# go through each stat (currently only CPU and MEM)
foreach my $currStatFull (@dataType) {
my ($currStat,$currType) = split /-/, $currStatFull;
# will be populated with location of the hosts
%hostRow = ( );
# create the work sheet, set some widths for the rows
my $workSheet = $workBook->add_worksheet($currStat);
$workSheet->set_column('A:A',16);
$workSheet->set_column('B:H', undef, $fmtPercentage);
$workSheet->set_column('I:I',128);
$workSheet->set_row('A:A', 72, undef);
($row,$col) = (0,1);
# draw out the timestamps
map { $workSheet->write($row,$col++, &formatDate2($_), $fmtRotated) } @timeList;
# go through all entries, including hosts as well as groups
foreach my $currHost (@host) {
# if we're not a group, and we have no stats, what's to print? let's skip past this one
next if $currHost !~ /^=/ && $sar{$currHost}->{$timeList[0]}->{$currStat}->{$currType} eq "";
# if this is a group..
if ($currHost =~ /^=/) {
my $group = $currHost; $group =~ s/^=\s*//;
my @groupHost = grep { $sar{$_}->{$timeList[0]}->{$currStat}->{$currType} } map { s/\s+//; $_ } split (/\s+/, $group);
next unless scalar(@groupHost);
} # we want to make sure that its members has data as well
# if we got this far, we deserve to advance a row, and save the host's row location
++$row; $col = 0; $hostRow{$currHost} = $row;
# if this is not a group (it's a host)
if ($currHost !~ /^=/) {
$workSheet->write($row,$col++,$currHost);
map {
$workSheet->write($row,$col++,&{"format".$currStat}($sar{$currHost}->{$_}->{$currStat}->{$currType}, $mem{$currHost}))
} @timeList;
}
} # done going through all possible hosts and groups
# go through only the groups
foreach my $currGroup (grep { /^=/ } @host) {
# get the previously saved locations
($row,$col) = ($hostRow{$currGroup},0);
# don't ask me why, but changing $currGroup seems to reflect the change back in @hosts.. is this a bug or what?
my $group = $currGroup; $group =~ s/^=\s*//;
# get a list of hosts, but only ones with data in them
my @groupHost = grep { $sar{$_}->{$timeList[0]}->{$currStat}->{$currType} } map { s/\s+//; $_ } split (/\s+/, $group);
next unless scalar(@groupHost); # if there's no hosts found with data, we don't want to print this line
# go through each date available and print out the formula to calculate the average for all the hosts
map { ++$col;
$workSheet->write(
$row,
$col,
( scalar(@groupHost) ? "=AVERAGE(".join(",",map { &xl_rowcol_to_cell($hostRow{$_},$col) } @groupHost).")" : "" ),
$fmtPercentage2
);
} @timeList;
# record the hosts that were used in this line's calculations
$workSheet->write($row,++$col,join(",",@groupHost));
}
}
print "Done!\n";
# take the Mon N Year, and conver it to the YYYYMMDD format
sub formatDate { local($_) = $_[0];
my ($mon,$day,$yr) = split /\s+/;
sprintf "%04d%02d%02d", $yr, $monthName{$mon}+1, $day;
}
# take the Mon N Year format and convert it to the MM-DD-YYYY format that is compatible with excel
sub formatDate2 { local($_) = $_[0];
my ($mon,$day,$yr) = split /\s+/;
sprintf "$day-$mon";
}
# take the idle time, and figure out the inverse, create a percentage val
sub formatCPU { local($_) = $_[0]; sprintf "%.5f", ((100-$_)/100); }
# take the free memory, convert it to the same unit as the free memory amount, and create a percentage val
sub formatMEM { my ($freeMem,$totalMem) = @_; sprintf "%.5f", (1-($freeMem * 8192 / 1024 / 1024)/$totalMem); }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment