#!/usr/bin/perl -w # # 200409-10 by gabriel rosenkoetter at JPMC # 20070208 by gabriel rosenkoetter at Radian # # parses the output of both bpmedialist -h ALL and vmquery -a, # produces reports on various classes of media or all media; use # --help for options use strict; use POSIX; use Getopt::Long; my $scratch = 0; my $frozen = 0; my $single_line = 0; my $brief = 0; my $offsite = 0; my $quiet = 0; my $help = 0; my $debug = 0; GetOptions('scratch' => \$scratch, 'frozen' => \$frozen, 'line' => \$single_line, 'brief' => \$brief, 'offsite' => \$offsite, 'quiet' => \$quiet, 'help' => \$help, 'debug' => \$debug); if ($help) { print "Select class(es) of media:\n", "--scratch, -s scratch media (based on `vmpool -listscratch`)\n", "--frozen, -f frozen media\n", "--offsite, -o shows volumes that the volDB thinks are off-site\n", "\n", "None of the above flag shows all volumes; you may specify more\n", "one of the above.\n", "\n", "Output formatting:\n", "--brief, -b brief (single line, should be under 80 columns,\n", " missing some details about individual volumes)\n", "--line, -l single-line listing (all information, will\n", " probably wrap on most terminals)\n", "--quiet, -q quiet (summary format only)\n", "\n", "None of the above shows multi-line, detailed informaton about\n", "each volume with a summary at the end.\n", "\n", "--help, -h display this message\n"; exit 0; } my $sudo = ''; $sudo = "sudo " if ($> != 0); $ENV{PATH} .= ':/usr/openv/netbackup/bin:/usr/openv/netbackup/bin/admincmd:/usr/openv/netbackup/bin/goodies:/usr/openv/volmgr/bin:/usr/openv/volmgr/bin/goodies:/usr/openv/java'; # Most of the information we track goes in anon hashes within this # hash. my %media; chomp(my $scratch_pool = `$sudo vmpool -listscratch | tail -1`); # requires a media ID (string), displays the information about that # tape as requested (or not, if quiet) sub disp_tape { my @tape_states = qw(allocated written expires read); my $tape_state; unless ($quiet) { print "$_[0] $media{$_[0]}{'medtype'} media in pool $media{$_[0]}{'pool'}", ", in slot $media{$_[0]}{'robslot'} of robot ", "$media{$_[0]}{'robtype'} $media{$_[0]}{'robnumber'}"; unless ($brief) { foreach $tape_state (@tape_states) { ($single_line > 0) ? print ", " : print "\n "; # print Dumper $media{$_[0]}; if (defined($media{$_[0]}{$tape_state}) && $media{$_[0]}{$tape_state} > 0) { print "$tape_state ", strftime('%m/%d/%y %T', localtime($media{$_[0]}{$tape_state})); } else { print "never $tape_state"; } } } print "\n"; } } # for counting tapes of a given media type across all robots my %media_types; # oft-reused in various cycles; globals to be lazy about passing to subs my ($mediaid, $medtype, $robtype, $robnumber, $robslot, $pool, $i); # total of relevant class, counting tapes of all media types in a given robot my ($count, %robot); #foreach (`$sudo /usr/openv/netbackup/bin/admincmd/bpmedialist -h ALL -l`) { # Only but that only gets allocated tapes, not scratch tapes, so we # should use vmquery... # ... whose -l is undocumented. Oh, but it matches the headers given # with -w. # # 0 media ID # 1 partner ID # 2 media type (verbal) # 3 barcode # 4 partner barcode # 5 robot control host # 6 robot type (verbal) # 7 robot number # 8 robot slot # 9 side/face (no clue... for optical media?) # 10 volume group # 11 pool (verbal) # 12 pool number # 13 previous pool # 14 number of mounts # 15 max mounts # 16 number of cleanings # 17 create datetime # 18 assigned datetime # 19 first mount datetime # 20 last mount datetime # 21 status # 22 offsite location # 23 offsite sent datetime # 24 offsite return datetime # 25 offsite slot number # 26 offsite session id # 27 version # 28 description # Initial population of %media... print "DEBUG: Entering vmquery loop.\n" if $debug; foreach (`$sudo /usr/openv/volmgr/bin/vmquery -a -l`) { ($mediaid, $medtype, $robtype, $robnumber, $robslot, $pool) = (split())[0,2,6,7,8,11]; $media{$mediaid}{'medtype'} = $medtype; # Switch these if you want to unnecessarily see cleaning tapes in # scratch/frozen output... I've mostly ignored cleaning tapes, as # I'm used to libraries that take care of that. wouldn't be hard to # add in handling for them as a separate class, though. # $media_types{$medtype} = 0; $media_types{$medtype} = 0 unless ($medtype =~ m/_CLN$/); # setting this to 0 repetatively here, but we're not trying to # count anything, just create defined hash values for the media # types that exist in the robot... which media we want to count # depends on what class of media we're after, and that comes below $media{$mediaid}{'robtype'} = $robtype; $media{$mediaid}{'robnumber'} = $robnumber; $media{$mediaid}{'robslot'} = $robslot; $media{$mediaid}{'pool'} = $pool; # These are hard to use; they contain spaces == $FS: # $media->{'created'}, # $media->{'assigned'}, $media->{'first_mount'}, # $media->{'last_mount'}, $media->{'vault'}, # $media->{'vault_sent'}, $media->{'vault_return'}, # $media->{'vault_slot'}, $media->{'vault_session'}) = # (split())[0,6,7,8,11,17,18,19,20,22,23,24,25,26]; } print "DEBUG: Completed vmquery loop.\n" if $debug; # $#[](sort keys %media_types) or whatever gets awkward after a while my @medtypes = sort keys %media_types; # grid summary output; no arguments, just pulls out of the globals, # populated under the appropriate class section below sub disp_summary { print "\n" unless $quiet; print " robot |"; foreach $medtype (@medtypes) { printf " %7s |", $medtype; } print " total\n-------+"; for ($i = 0; $i <= $#medtypes; $i++) { print "---------+"; } print "------\n"; foreach $robnumber (sort keys %robot) { printf "%6s |", $robnumber; $i = 0; foreach $medtype (@medtypes) { printf " %7d |", $robot{$robnumber}{$medtype}; $i += $robot{$robnumber}{$medtype}; } print " $i\n"; } print "-------+"; for ($i = 0; $i <= $#medtypes; $i++) { print "---------+"; } print "------\nloaded |"; my $j = 0; foreach $medtype (@medtypes) { $i = 0; foreach $robnumber (sort keys %robot) { next if ($robnumber eq 'NONE'); $i += $robot{$robnumber}{$medtype}; } printf " %7d |", $i; $j += $i; } print " $j"; print "\n total |"; foreach $medtype (@medtypes) { printf " %7d |", $media_types{$medtype}; } print " $count\n"; } #print "[$scratch_pool]\n"; #print Dumper @medialist; #exit 0; # decimal values for what really should be a hex/binary field in the # output from bpmedialist; unclear why Veritas chose to respresent # *these* flags this way... my %media_states = ( 512 => 'mpx', 128 => 'imported', 64 => 'multi_ret', 8 => 'full', 2 => 'suspended', 1 => 'frozen', 0 => 'active' ); # ... but we still need bpmedialist to get the STATE field, argh! my ($allocated, $written, $expires, $read, $state); # (the rest of populating %media) print "DEBUG: Entering bpmedialist loop.\n" if $debug; #foreach (`$sudo /usr/openv/netbackup/bin/admincmd/bpmedialist -h ALL -l`) { # -h ALL takes FOREVER pa2ibks2 against Dayton servers, but that problem # doesn't exist the other way, which is whack-O. Temporary workaround: #chomp(my $hostname = `uname -n`); # ... and pa2ibks2 is just as bad as oh1ibks2? What? # chomp(my $hostname = `head -1 /usr/openv/netbackup/bp.conf | awk '{ print \$NF }'`); # ... and phlmaster leaves out NDMP tapes, good grief foreach my $hostname (`($sudo bpstulist | awk '{ print \$3 }' ; bpstulist | awk '{ print \$13 }' ) | grep -v '*NULL*' | sort | uniq`) { chomp($hostname); print "DEBUG: bpmedialist for $hostname.\n" if $debug; foreach (`$sudo /usr/openv/netbackup/bin/admincmd/bpmedialist -h $hostname -l`) { ($mediaid, $allocated, $written, $expires, $read, $state) = (split())[0,4,5,6,7,14]; $media{$mediaid}{'allocated'} = $allocated; $media{$mediaid}{'written'} = $written; $media{$mediaid}{'expires'} = $expires; $media{$mediaid}{'read'} = $read; $media{$mediaid}{'state'} = $state; #foreach (sort { $b <=> $a } keys %media_states) # yeah, don't ask... for ($i = 512; $i >= 2; $i /= 2) { if ($state >= $i) { $media{$mediaid}{$media_states{$i}} = 1 if defined($media_states{$i}); $state -= $i; } else { $media{$mediaid}{$media_states{$i}} = 0 if defined($media_states{$i}); } } for ($i = 0; $i <= 1; $i++) { if ($state == $i) { $media{$mediaid}{$media_states{$i}} = 1; } else { $media{$mediaid}{$media_states{$i}} = 0; } } } } print "DEBUG: Completed bpmedialist loop.\n" if $debug; print "DEBUG: Entering tpconfig loop.\n" if $debug; foreach (qx{tpconfig -l}) { if ($_ =~ m/^robot/) { foreach $medtype (@medtypes) { $robot{(split())[1]}{$medtype} = 0; } } } foreach $medtype (@medtypes) { $robot{'NONE'}{$medtype} = 0; } print "DEBUG: Completed tpconfig loop.\n" if $debug; if ($scratch) { print "DEBUG: Entering scratch display.\n" if $debug; # resetting globals $count = 0; foreach $robnumber (sort keys %robot) { foreach $medtype (@medtypes) { $robot{$robnumber}{$medtype} = 0; } } foreach $i (sort keys %media_types) { $media_types{$i} = 0; } print "Available scratch volumes:\n"; foreach $mediaid (sort keys %media) { if ($media{$mediaid}{'pool'} eq $scratch_pool) { disp_tape($mediaid); $count++; $media_types{$media{$mediaid}{'medtype'}} += 1; if ($media{$mediaid}{'robtype'} =~ m/^NONE$/) { # we get away with treating "NONE" as another robot number... # go go loose typing $robot{$media{$mediaid}{'robtype'}}{$media{$mediaid}{'medtype'}} += 1; } else { $robot{$media{$mediaid}{'robnumber'}}{$media{$mediaid}{'medtype'}} += 1; } } } disp_summary(); print "\n" unless ($frozen == 0 && $offsite == 0); print "DEBUG: Completed scratch display.\n" if $debug; } if ($frozen) { print "DEBUG: Entering frozen display.\n" if $debug; # resetting globals $count = 0; foreach $robnumber (sort keys %robot) { foreach $medtype (@medtypes) { $robot{$robnumber}{$medtype} = 0; } } foreach $i (sort keys %media_types) { $media_types{$i} = 0; } print "Frozen volumes:\n"; foreach $mediaid (sort keys %media) { if ($media{$mediaid}{'frozen'}) { disp_tape($mediaid); $count++; $media_types{$media{$mediaid}{'medtype'}} += 1; if ($media{$mediaid}{'robtype'} =~ m/^NONE$/) { # we get away with treating "NONE" as another robot number... # go go loose typing $robot{$media{$mediaid}{'robtype'}}{$media{$mediaid}{'medtype'}} += 1; } else { $robot{$media{$mediaid}{'robnumber'}}{$media{$mediaid}{'medtype'}} += 1; } } } disp_summary(); print "\n" unless ($offsite == 0); print "DEBUG: Completed frozen display.\n" if $debug; } if ($offsite) { $count = 0; foreach $robnumber (sort keys %robot) { foreach $medtype (@medtypes) { $robot{$robnumber}{$medtype} = 0; } } foreach $i (sort keys %media_types) { $media_types{$i} = 0; } print "Off-site volumes:\n"; foreach $mediaid (sort keys %media) { if ($media{$mediaid}{'robtype'} =~ m/^NONE$/) { disp_tape($mediaid); $count++; $robot{$media{$mediaid}{'robtype'}}{$media{$mediaid}{'medtype'}} += 1; } } disp_summary(); } if ($frozen == 0 && $scratch == 0 && $offsite == 0) { # resetting globals $count = 0; foreach $robnumber (sort keys %robot) { foreach $medtype (@medtypes) { $robot{$robnumber}{$medtype} = 0; } } foreach $i (sort keys %media_types) { $media_types{$i} = 0; } print "All volumes:\n"; foreach $mediaid (sort keys %media) { disp_tape($mediaid); $count++; if ($media{$mediaid}{'robtype'} =~ m/^NONE$/) { # we get away with treating "NONE" as another robot number... # go go loose typing $robot{$media{$mediaid}{'robtype'}}{$media{$mediaid}{'medtype'}} += 1; } else { $robot{$media{$mediaid}{'robnumber'}}{$media{$mediaid}{'medtype'}} += 1; } } disp_summary(); }