#!/usr/bin/perl -w # # groking from: # http://www.backupcentral.com/cgi-bin/netbackup-fom?_highlightWords=bpdbjobs&file=194 # # output's gr; give a jobid and an email address on the CL or we'll # ask for them. grabs just that one bpdbjobs line, dumps the # trystatuslines to a temp file, diffs against the last one, emails # anything new. # # May consider a -q switch to only make noise about problems, but # that'd take some research to clearly define "problem", and if I'm # bothering to use this, I'm probably going to be OCD about things # anyway. # # Should probably be run under nohup. You're responsible for cleaning # up the temp files (filenames will be dumped to stdout, so into # nohup.out) # # Note that with the whole $sudo dance, we have to make sure we reuse # $sudo frequently enough to stay unlocked, and it'd better be # unlocked before you kick off under a nohup. Lame-o. # # take it, use it, don't blame me if it breaks something use strict; use POSIX qw(ctime); use Getopt::Long; my $sudo = ''; $sudo = "sudo " if ($> != 0); my @jobid = (); my @email = (); my @pager_number = (); my $help; my $sleep_period = 90; my $debug = 0; GetOptions('jobid=s' => \@jobid, 'email=s' => \@email, 'pager=i' => \@pager_number, 'help' => \$help, 'debug' => \$debug); @jobid = split(/,/,join(',',@jobid)); @email = split(/,/,join(',',@email)); @pager_number = split(/,/,join(',',@pager_number)); if ($help) { print "--jobid, -j Job ID to monitor.\n", "--email, -e email address to notify.\n", "--pager, -p Alternately, pager to notify (\@airmessage.net will", " be appended).\n", "--help, -h Display this message.\n", "\n", "All options may be specified repeatedly, or be supplied a comma-\n", "separated list of values.\n"; exit 0; } foreach (@pager_number) { push @email, $_ . '@airmessage.net'; } die "Need a recipient (--email or --pager)" if ($#email < 0); die "Need a Job ID (--jobid)" if ($#jobid < 0); if ($debug) { print "Will watch jobs "; foreach (@jobid) { print "$_ "; } print "and inform "; foreach (@email) { print "$_ "; } print "of changes.\n"; } 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 = ('trybyteswritten','tryfileswritten'); # Jacked from the (TOTALLY UNDOCUMENTED SO I CAN'T USE IT WITHOUT # LOTS OF PAIN) NBU::Job. my %op_codes = ( -1 => ' - ', 25 => 'wai', 2 => 'con', 26 => 'con', 0 => 'act', 27 => 'mnt', 29 => 'pos', 3 => 'wri', 35 => 'wri', 5 => 'dup', ); # The rest are just from bpdbjobs(1M) (operation isn't discussed # there, jerks). my %jt_codes = ( 0 => 'b', 1 => 'a', 2 => 'r', 3 => 'V', 4 => 'd', 5 => 'i', 6 => 'c', 7 => 'v', ); my %state_codes = ( 0 => 'que', 1 => 'act', 2 => 'req', 3 => 'don', ); my $bpdbjobs_line; sub parse_line { # XXX so not passing args right... $_ = $bpdbjobs_line; chomp; # Yeah, they really include escaped ,s; ditch them # ... only but there are some legit \\, situations on Windows $_ =~ s#\\\\#//#g; $_ =~ s/\\,//g; my $job; my @columns = split(','); my $idx = 0; foreach my $label (@job_info_labels) { $job->{$label} = $columns[$idx]; $idx++; } my @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. my @trylist = (); $idx++; my ($trydata, @trystatuslines); for (1..$job->{'trycount'}) { undef($trydata); foreach my $trylabel ( @job_try_labels_1 ) { $trydata->{$trylabel} = $columns[$idx]; $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]; return $job; } my (%tmpfile, %mailfile, %tmpfh, %mailfh); foreach (@jobid) { $tmpfile{$_} = "$ENV{HOME}/${_}-status-$$"; open $tmpfh{$_}, ">$tmpfile{$_}" or die "Couldn't write to $tmpfile{$_}"; link $tmpfile{$_}, $tmpfile{$_} . '.last' or die "Couldn't back up $tmpfile{$_}"; print "Opened $tmpfile{$_}.\n" if $debug; $mailfile{$_} = "$ENV{HOME}/${_}-mail-$$"; open $mailfh{$_}, ">$mailfile{$_}" or die "Couldn't write to $mailfile{$_}\n"; print "Opened $mailfile{$_}.\n" if $debug; } my ($job, $id, $idx, $trydata, @trylines, $tryline, @changes, $addr); while (1) { print "Current time is ", ctime(time()) if $debug; # XXX it would be better to use only one bpdbjobs call and egrep foreach $id (@jobid) { undef @changes; $bpdbjobs_line = qx{$sudo /usr/openv/netbackup/bin/admincmd/bpdbjobs -all_columns | grep "^$id"}; print "Read in bpdbjobs line for $id. Got:\n", $bpdbjobs_line if $debug; # $job = parse_line($bpdbjobs_line); # XXX arg passing doesn't work like that... $job = parse_line(); for ($idx = 1; $idx <= $job->{'trycount'}; $idx++) { # Whiskey Tango Foxtrot?!? # from `perldoc -f print`: # Note that if you're storing FILEHANDLES in an array # or other expression, you will have to use a block # returning its value instead: # # print { $files[$i] } "stuff\n"; # print { $OK ? STDOUT : STDERR } "stuff\n"; # # Perl syntax stupidity WILL be the death of me. print {$tmpfh{$id}} "Try $idx:\n"; $trydata = ${$job->{'trylist'}}[$idx - 1]; foreach (@{$trydata->{'trystatuslines'}}) { @trylines = `echo "$_" | fmt -s`; unless ( ($_ =~ m/positioning [A-Z0-9]* to file/) || ($_ =~ m/positioned; position time:/) ) { foreach $tryline (@trylines) { print {$tmpfh{$id}} " "; print {$tmpfh{$id}} " " unless (m,^[0-9]{2}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} - ,); print {$tmpfh{$id}} "$tryline"; } } } } print "Wrote out trystatuslines for $id.\n" if $debug; @changes = qx{diff $tmpfile{$id} ${tmpfile{$id}}.last}; if ($#changes >= 0) { print "Found some new lines.\n" if $debug; foreach (@changes) { print {$mailfh{$id}} $_; } foreach $addr (@email) { print qx{mailx -s "$id update" $addr < $mailfile{$id}}; } close $mailfh{$id}; unlink $mailfile{$id}; open $mailfh{$id}, ">$mailfile{$id}" or die "Couldn't reopen $mailfile{$id}.\n"; print "Reopened $mailfile{$id}.\n" if $debug; } unlink $tmpfile{$id} . '.last'; link $tmpfile{$id}, $tmpfile{$id} . '.last' or die "Couldn't back up $tmpfile{$id}"; close $tmpfh{$id}; unlink $tmpfile{$id}; open $tmpfh{$id}, ">$tmpfile{$id}" or die "Couldn't reopen to $tmpfile{$id}"; print "Reopened $tmpfile{$id}.\n" if $debug; } print "Sleeping $sleep_period before next pass.\n" if $debug; sleep $sleep_period; }