Here's a simple perl script which I wrote to see how my Aggregates and Volumes on my R200 were balancing out, since I needed to grow a volume, but the plain output of 'df' is useless to tell you which Aggregate actually holds the volume. So you need to use the 'vol status' command to figure this out and some funky parsing, etc.
This script makes my life easier, but it does zero trend analysis or anything like that, it's just an easy to use df-like tool that fits my needs.
#!/usr/local/bin/perl -w # # Version 1.0 - December 12th, 2007 # License: GPLv2
use strict; #no warnings 'closure'; use Getopt::Long; &Getopt::Long::Configure("no_ignore_case");
my $DEBUG = -1;
my $rshcmd = "/bin/rsh"; my $rshargs = ""; my $user = "root";
my $ret = GetOptions( 'D:i' => $DEBUG, 'e=s' => $rshcmd, 'u=s' => $user, );
# Default DEBUG level is 1 if ($DEBUG == 0) { $DEBUG++; }
&usage if (!$ret); &usage if ($#ARGV < 0);
if ($rshcmd =~ m/ssh/) { $rshargs = "-l $user"; }
if (! -e $rshcmd) { print STDERR "Error! Cannot execute $rshcmd, please check your path.\n\n"; &usage; exit; }
foreach my $filer (@ARGV) { my %data = (); my %aggr = ();
my $getaggrdf = 0; my $dfargs = "";
my @rawstatus = &getvolstatus($filer,$rshargs); my @rawdf = &getvoldf($filer,$rshargs,$dfargs); $dfargs = "-A"; my @aggrdf = &getvoldf($filer,$rshargs,$dfargs); %data = &parsestatus(%data,$filer,@rawstatus); %data = &parsedf(%data,$filer,$getaggrdf,@rawdf); $getaggrdf++; %aggr = &parsedf(%data,$filer,$getaggrdf,@aggrdf); &showdata($filer,%data,%aggr); undef %aggr; undef %data; }
exit;
#--------------------------------------------------------------------- sub DBG { my $l = 0;
if ($#_ == 1) { $l = shift @_; } # Indent Debug output for each level we go up. print STDERR " "x$DEBUG, @_ if ($DEBUG >= $l); }
#--------------------------------------------------------------------- sub getvolstatus { my $filer = shift @_; my $rshargs = shift @_;
DBG 1, "getvolstatus($filer,$rshargs)\n";
open(IN,"$rshcmd $rshargs $filer vol status -v|") || die "Error! Cannot $rshcmd $rshargs $filer: $!\n"; my @d = <IN>; close(IN); DBG 5, "Found $#d lines of input.\n"; return @d; }
#--------------------------------------------------------------------- sub getvoldf { my $filer = shift @_; my $rshargs = shift @_; my $dfargs = shift @_;
DBG 1, "getvoldf($filer,$rshargs,$dfargs)\n";
open(IN,"$rshcmd $rshargs $filer df $dfargs|") || die "Error! Cannot $rshcmd $filer: $!\n"; my @d = <IN>; close(IN); DBG 5, "Found $#d lines of input.\n"; return @d; }
#--------------------------------------------------------------------- sub parsestatus {
my $ref = shift @_; my $filer = shift @_;
DBG 1, "parse($filer)\n";
my %d = %$ref; my $vol;
STLOOP: foreach (@_) { chomp; DBG 4, "ST: $_\n";
next STLOOP if (m/Volume State Status\s+Options$/);
if (m/^\s*([\w_]+) online (.*)$/) { $vol = $1; DBG 5, "Volume: $vol\n"; my $opt = $2; $d{$vol}->{AGGR} = "";
# Parse out any remaining options on the line... if ($opt ne "") { DBG 6, "opt: $opt\n"; my @o = split(",",$opt); PAIR: foreach my $pair (@o) { if ($pair =~ m/\w+=\w+/) { my ($opt, $val) = split("=",$pair); DBG 6, "option: $opt = $val.\n"; $d{$vol}{$opt} = $val || ""; } } } next STLOOP; }
if (m/Containing aggregate: (.*)/) { my $aggr = $1; $aggr =~ s/'|\s//g;
$d{$vol}{AGGR} = $aggr; next STLOOP; }
if (m/,$/) { my @o = split(",",$_); foreach my $pair (@o) { my ($opt, $val) = split("=",$pair); $d{$vol}{$opt} = $val; DBG 5, "option: $opt = $val.\n"; } } } return %d; }
#--------------------------------------------------------------------- sub parsedf {
my $ref = shift @_; my $filer = shift @_; my $doaggr = shift @_;
DBG 1, "parsedf($filer,$doaggr)\n";
my %d = %$ref; my $vol; my $aggr;
DFLOOP: foreach (@_) { chomp;
if ($doaggr) { DBG 3, "DFA: $_\n"; next DFLOOP if (m/^Aggregate\s+kbytes\s+/); next DFLOOP if (m@/.snapshot\s+@);
if (m/^(\w+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+%)/) { DBG 3, "Aggr = $1\n"; $d{$1}{ATOTAL} = $2; $d{$1}{AUSED} = $3; $d{$1}{AAVAIL} = $4; $d{$1}{ACAP} = $5; next DFLOOP; } } else { DBG 3, "DF: $_\n"; next DFLOOP if (m/^Filesystem\s+total\s+used\s+avail\s+capacity\s+Mounted on$/); next DFLOOP if (m/^snap reserve/);
if (m@^/vol/(.*)/\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+%)@) { $vol = $1; my $total = $2; my $used = $3; my $avail = $4; my $cap = $5; DBG 2, "Volume: $vol, T: $total, U: $used, A: $avail, C: $cap\n"; $d{$vol}{DFTOTAL} = $total; $d{$vol}{DFUSED} = $used; $d{$vol}{DFAVAIL} = $avail; $d{$vol}{DFCAP} = $cap; next DFLOOP; } } } return %d; }
#--------------------------------------------------------------------- sub showdata {
DBG 2, "showdata()\n"; my $filer = shift @_; my $href = shift @_; my %d = %$href;
my $aref = shift @_; my %a = %$aref;
my %shown;
print "\nFiler: $filer\n\n";
printf "%-8s %-30s %8s %8s %4s\n", "Aggr","Volume", "Used", "Total", "Pcnt"; printf "%-8s %-30s %8s %8s %4s\n", "-"x8,"-"x25,"-"x8,"-"x8,"-"x4;
my $blank = ""; foreach my $vol (sort { $d{$a}{AGGR} cmp $d{$b}{AGGR} || $d{$b}{DFTOTAL} <=> $d{$a}{DFTOTAL} } keys %d) {
# All this to print a seperator between aggregates... if ($d{$vol}{AGGR} ne "$blank" ) { my $aggr = $d{$vol}{AGGR}; if ($a{$aggr}) { printf "\n%-8s %-30s %8s %8s %4s\n", $aggr, "--------", &scale($a{$aggr}{AUSED}), &scale($a{$aggr}{ATOTAL}),$a{$aggr}{ACAP}; } $blank = $d{$vol}{AGGR}; }
# Show the data printf "%-8s %-30s %8s %8s %4s\n", $d{$vol}{AGGR}, $vol, &scale($d{$vol}{DFUSED}), &scale($d{$vol}{DFTOTAL}), $d{$vol}{DFCAP}; } }
#--------------------------------------------------------------------- # Takes Kilobyte input and scales to MB, GB or higher.
sub scale { my $n = shift @_;
my @suffix = qw (KB MB GB TB); my $i = 0;
while($n > 10000) { $n /= (1024); $i++; } return sprintf("%5.1f$suffix[$i]",$n); }
#--------------------------------------------------------------------- sub usage {
print STDERR <<EOF;
netapp-vol-to-aggr [options] <filer> [<filer> ...]
Print a report of the usage of aggregates and the volumes on those aggregates to make it easier to see how to balance disk useage amongst FlexVols.
Requires that the script be run on a host which has rsh (default) or ssh access to the filer to run the 'df' and 'vol status' commands.
NOTE! You will be prompted three times per-filer for SSH passwords if used, sorry.
Options: -D [#] Print out debugging info, higher numbers print more. -e rsh|ssh Use a specific rsh or ssh transport. -u user For ssh specify the username to use, default is 'root'
Author: John Stoffel (john.stoffel@taec.toshiba.com || john@stoffel.org)
EOF }