#!/usr/bin/perl -w # from: http://www.backupcentral.com/cgi-bin/netbackup-fom?_highlightWords=bpdbjobs&file=194 # modified a bunch by gr for clean presentation (like double-clicking # the job in the GUI) use strict; use POSIX; use Data::Dumper; use Text::Wrap; my @job_info_labels = ('jobid', 'jobtype', 'state', 'status', 'class', 'sched', 'client', 'server', 'start', 'elapsed', 'end', 'stunit', 'try', 'operation', 'kbytes', 'files', 'path_last_written', 'percent', 'jobpid', 'owner', 'subtype', 'classtype', 'schedtype', 'priority', 'group', 'master_server', 'retention_units', 'retention_period', 'compression', 'kbyteslastwritten', 'fileslastwritten', 'filelistcount'); my @job_try_labels_1 = ('trypid', 'trystunit', 'tryserver', 'trystarted', 'tryelapsed', 'tryended', 'trystatus', 'trystatusdescription', 'trystatuscount'); my @job_try_labels_2 = ('trykbyteswritten','tryfileswritten'); my @vault_info_labels = ('unknown1', 'unknown2', 'unknown3', 'vltrobot', 'vltname', 'vltprofile', 'vltsid', 'vltejects', 'unknown4', 'unknown5', 'unknown6', 'unknown7'); # Jacked from the (TOTALLY UNDOCUMENTED SO I CAN'T USE IT WITHOUT # LOTS OF PAIN) NBU::Job. my %op_codes = ( -1 => '---', 25 => 'Waiting', 2 => 'Connecting', 26 => 'Connecting', 0 => ' ? ', 27 => 'Mounting', 29 => 'Positioning', 3 => 'Writing', 35 => 'Writing', 5 => 'Duplicating', ); # The rest are just from bpdbjobs(1M) (operation isn't discussed # there, jerks). my %jt_codes = ( 0 => 'Backup', 1 => 'Archive', 2 => 'Restore', 3 => 'Verify', 4 => 'Duplication', 5 => 'Import', 6 => 'DB Backup', 7 => 'Vault', 8 => 'Label', 9 => 'Erase', ); my %state_codes = ( 0 => 'Queued', 1 => 'Active', 2 => 'Requeued', 3 => 'Done', ); my ($job, @columns, $idx, @files, @trylist, $trydata, @trystatuslines, @jobs); my $quiet = 0; # suppress all status messages my $verbose = 0; # permit the noisy "writing blah" NBU 6.5 messages through if ($#ARGV >= 0) { while (@ARGV) { $_ = shift @ARGV; $quiet = 1 if ($_ =~ /^-q/); $verbose = 1 if ($_ =~ /^-v/); } } while(<>) { chomp; # Yeah, they really include escaped ,s; ditch them # ... only but there are some legit \\, situations on Windows # $_ =~ s/\\,/BACKSLASH,/g; # ... and what's more, "\074" when passed through echo(1) gets you # "<", because echo does octal expansion; see down in the foreach # (@{$trydata->{'trystatuslines'}}) loop for where this gets cleaned # back up $_ =~ s/\\\\/BACKSLASH/g; # You can also specify multiple STUs for vaults through different # duplication rules, and there are separated by /, / in the -all_columns # output. (Could I make this stuff up? No.) $_ =~ s/, ([^,])/; $1/g; $_ =~ s/\\[,;]//g; undef($job); @columns = split(','); $idx = 0; foreach my $label (@job_info_labels) { $job->{$label} = $columns[$idx]; $idx++; } @files = (); if ($job->{'filelistcount'} > 0) { @files = @columns[$idx..$idx+$job->{'filelistcount'}-1]; } $job->{'files'} = [@files]; $idx += $job->{'filelistcount'} ; $job->{'trycount'} = $columns[$idx]; # Now get the try data. Within the try data # is a list of try status lines. @trylist = (); $idx++; for (1..$job->{'trycount'}) { undef($trydata); foreach my $trylabel ( @job_try_labels_1 ) { $trydata->{$trylabel} = $columns[$idx++]; if ($trylabel eq 'trystunit') { # Vault duplication jobs have, without warning, multiple # STUs here until ($columns[$idx + 1] =~ m/^[0-9]+$/) { $trydata->{$trylabel} .= "; $columns[$idx++]"; } } } @trystatuslines = @columns[$idx..$idx+$trydata->{'trystatuscount'}-1]; $trydata->{'trystatuslines'} = [@trystatuslines]; $idx += $trydata->{'trystatuscount'}; foreach my $trylabel ( @job_try_labels_2 ) { $trydata->{$trylabel} = $columns[$idx]; $idx++; } push @trylist, $trydata; } $job->{'trylist'} = [@trylist]; foreach my $vltlabel (@vault_info_labels) { $job->{$vltlabel} = $columns[$idx]; $idx++; } push @jobs, $job; } #print Dumper(@jobs); my @powers = qw(KB MB GB TB PB); my ($hours, $minutes, $seconds, $base, $power, $actual_write_time); foreach $job (@jobs) { $hours = ($job->{'elapsed'} - ($job->{'elapsed'} % (60 * 60))) / (60 * 60); $minutes = (($job->{'elapsed'} - ($job->{'elapsed'} % 60)) / 60) - ($hours * 60); $seconds = $job->{'elapsed'} - ($hours * 60 * 60) - ($minutes * 60); print "--------------\n"; print "JobID: $job->{'jobid'} Type: ", ($job->{'jobtype'} =~ m/[0-9]/) ? $jt_codes{$job->{'jobtype'}} : 'UNKNOWN'; if (($job->{'state'} =~ m/[0-9]/) && $job->{'state'} == 3) { # done print " Status code: $job->{'status'}"; } elsif (($job->{'state'} =~ m/[0-9]/) && ($job->{'state'} == 1 && defined($op_codes{$job->{'operation'}}))) { # active print " Operation: $op_codes{$job->{'operation'}}"; } else { # requeued or queued print " State: ", defined($state_codes{$job->{'state'}}) ? $state_codes{$job->{'state'}} : $job->{'state'}; } print "\n"; print "Policy: ", (length($job->{'class'}) > 0) ? $job->{'class'} : "(none)", " Schedule: ", (length($job->{'sched'}) > 0) ? $job->{'sched'} : "(none)", "\n"; print "Vault: ", (length($job->{'vltname'}) > 0) ? $job->{'vltname'} : "UNKNOWN", " Profile: ", (length($job->{'vltprofile'}) > 0) ? $job->{'vltprofile'} : "UNKNOWN", " SID: ", (length($job->{'vltsid'}) > 0) ? $job->{'vltsid'} : "UNKNOWN", "\n Robot: ", (length($job->{'vltrobot'}) > 0) ? $job->{'vltrobot'} : "UNKNOWN", " Ejects: ", (length($job->{'vltejects'}) > 0) ? $job->{'vltejects'} : '0', "\n" if (($job->{'jobtype'} =~ m/[0-9]/) && ($job->{'jobtype'} == 7)); print "Client: ", (length($job->{'client'}) > 0) ? $job->{'client'} : "(none)", " Server: ", (length($job->{'server'}) > 0) ? $job->{'server'} : "(none)", " Storage unit: ", (length($job->{'stunit'}) > 0) ? $job->{'stunit'} : "(none)", "\n"; print "Runtime: ", strftime('%m/%d/%y %T', localtime($job->{'start'})), " - ", ((length($job->{'end'}) > 0) && $job->{'end'} > 0) ? strftime('%m/%d/%y %T', localtime($job->{'end'})) : "(active)"; printf " (%02d:%02d:%02d)", $hours, $minutes, $seconds; print " - $job->{'percent'}\% complete\n"; if (defined($job->{'filelistcount'}) && ($job->{'filelistcount'} =~ m/^[0-9]+$/)) { if ($job->{'filelistcount'} > 0) { print "$job->{'filelistcount'} path", ($job->{'filelistcount'} > 1) ? "s" : "", " to write:\n"; my $view_limit = 3; my $file_disp = ''; if ($job->{'filelistcount'} > ($view_limit + 2)) { my $start_slot = 0; if (length($job->{'path_last_written'}) > 0) { for ($idx = 0; $idx <= ($job->{'filelistcount'} - 1); $idx++) { next if (${$job->{'files'}}[$idx] =~ m/^\/$/); if ($job->{'path_last_written'} =~ m/^${$job->{'files'}}[$idx]/) { $start_slot = $idx; } } $start_slot -= $view_limit - ($job->{'filelistcount'} - $start_slot) if (($job->{'filelistcount'} - $start_slot) < $view_limit); } print " [$start_slot], " if ($start_slot > 0); for ($idx = $start_slot; $idx <= $start_slot + $view_limit - 1; $idx++) { # print "${$job->{'files'}}[$idx], "; $file_disp = "${$job->{'files'}}[$idx], "; $file_disp =~ s/BACKSLASH/\\/g; print $file_disp; } print "[" . ($job->{'filelistcount'} - $start_slot - $view_limit) . "]\n"; } else { print " "; for ($idx = 0; $idx < ($job->{'filelistcount'} - 1); $idx++) { $file_disp = "${$job->{'files'}}[$idx], "; $file_disp =~ s/BACKSLASH/\\/g; print $file_disp; } $file_disp = "${$job->{'files'}}[$job->{'filelistcount'} - 1]\n"; $file_disp =~ s/BACKSLASH/\\/g; print $file_disp; } print "Last path written:\n $job->{'path_last_written'}\n" if (defined($job->{'path_last_written'}) && (length($job->{'path_last_written'}) > 0)); } else { print "0 paths to write.\n"; } } for ($idx = 1; $idx <= $job->{'trycount'}; $idx++) { print "\nTry $idx:\n"; $trydata = $job->{'trylist'}[$idx - 1]; print "PID: $trydata->{'trypid'} " if defined($trydata->{'trypid'}); print "Status: $trydata->{'trystatus'} " if defined($trydata->{'trystatus'}); print "\n ($trydata->{'trystatusdescription'})" if defined($trydata->{'trystatusdescription'}); print "\n" if (defined($trydata->{'tryserver'}) || defined($trydata->{'trystunit'})); print "Server: $trydata->{'tryserver'} " if defined($trydata->{'tryserver'}); print "Storage unit: $trydata->{'trystunit'} " if defined($trydata->{'trystunit'}); $trydata = ${$job->{'trylist'}}[$idx - 1]; $hours = ($trydata->{'tryelapsed'} - ($trydata->{'tryelapsed'} % (60 * 60))) / (60 * 60); $minutes = (($trydata->{'tryelapsed'} - ($trydata->{'tryelapsed'} % 60)) / 60) - ($hours * 60); $seconds = $trydata->{'tryelapsed'} - ($hours * 60 * 60) - ($minutes * 60); print "\nRuntime: ", (defined($trydata->{'trystarted'}) && $trydata->{'trystarted'} > 0) ? strftime('%m/%d/%y %T', localtime($trydata->{'trystarted'})) : "(not started)", " - ", (defined($trydata->{'tryended'}) && $trydata->{'tryended'} > 0) ? strftime('%m/%d/%y %T', localtime($trydata->{'tryended'})) : "(not ended)"; printf " (%02d:%02d:%02d)\n", $hours, $minutes, $seconds; if (defined($trydata->{'trykbyteswritten'}) && ($trydata->{'trykbyteswritten'} > 0)) { if (defined($trydata->{'tryfileswritten'})) { $base = $trydata->{'trykbyteswritten'}; $power = 0; while ($base > 1024) { $base /= 1024; $power++; } printf "%.2f %s written in %d files", $base, $powers[$power], $trydata->{'tryfileswritten'}; } if (defined($trydata->{'tryelapsed'}) && ($trydata->{'trykbyteswritten'} > 0) && ($trydata->{'tryelapsed'} > 0)) { $base = $trydata->{'trykbyteswritten'} / $trydata->{'tryelapsed'}; $power = 0; while ($base > 1024) { $base /= 1024; $power++; } printf " at %.2f %s/sec", $base, $powers[$power]; $base = $trydata->{'trykbyteswritten'} / ($trydata->{'tryelapsed'} / (60 * 60)); $power = 0; while ($base > 1024) { $base /= 1024; $power++; } printf " (%.2f %s/hour)", $base, $powers[$power]; } print "\n"; } undef $actual_write_time; print "Status messages:\n" unless ($quiet); foreach (@{$trydata->{'trystatuslines'}}) { unless ($quiet) { $_ =~ s,BACKSLASH,\\,g; my $tryline = wrap(" ", " ", $_); if ($verbose) { print "$tryline\n"; } else { print "$tryline\n" unless ($tryline =~ m/[0-9]* .. written|number of files written|writing to path/); } } if (m/end writing; write time:/) { $actual_write_time = (split())[7]; ($hours, $minutes, $seconds) = split(':', $actual_write_time); $actual_write_time = ($hours * 60 * 60) + ($minutes * 60) + $seconds; } } if (defined($trydata->{'trykbyteswritten'}) && defined($actual_write_time) && $actual_write_time > 0) { $base = $trydata->{'trykbyteswritten'} / $actual_write_time; $power = 0; while ($base > 1024) { $base /= 1024; $power++; } printf "Actual speed (based on completed write time): %.2f %s/sec", $base, $powers[$power]; $base = $trydata->{'trykbyteswritten'} / ($actual_write_time / (60 * 60)); $power = 0; while ($base > 1024) { $base /= 1024; $power++; } printf " (%.2f %s/hour)\n", $base, $powers[$power]; } } } exit(0);