#!/usr/bin/perl -w use strict; use POSIX; use Getopt::Long; use LWP; my $pol_prefix = 'dbcold_'; my $send_email = 0; #my $email_target = 'backupandrecovery2@radian.biz'; my $email_target = 'gabriel.rosenkoetter@radian.biz,rick.verhage@radian.biz'; my $send_opcmsg = 0; my $help = 0; my $debug = 0; my $pp_def = $pol_prefix; my $et_def = $email_target; GetOptions('policy=s' => \$pol_prefix, 'email' => \$send_email, 'recipient=s' => \$email_target, 'openview' => \$send_opcmsg, 'help' => \$help, 'debug' => \$debug); if ($help) { print " --policy, -p ..... Specify policy [prefix] to watch.\n", " [Default: $pp_def]\n", " --email, -e ...... Send email notification.\n", " --recipient, -r .. Specify email recipient.\n", " [Default: $et_def]\n", " --openview, -o ... Use opcmsg to raise an OpenView alert.\n", " --help, -h ....... Display this message.\n\n", " In absence of -e or -o, output is to STDOUT.\n"; exit 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:/opt/OV/bin'; #bpdbjobs -report -all_columns | awk -F, '$3 ~ /^[0-2]$/ && $5 ~ /^dbcold/ \ # { print "jobid " $1 " for " $7 " under policy " $5 " schedule " $6 \ # " still running at `date`." }' # # This would be swell, but the Force is weak with HP-UX's awk(1): # #awk: Input line 104218,4,3,0,,,phlma cannot be longer than 3,000 bytes. # The input line number is 10. # The source line number is 1. # # Well, if we're going to use Perl, might as well just get the full line # parsed, since that's quicker to copy/paste rather than just cherry- # picking what we need. 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'); my @vault_info_labels = ('unknown1', 'unknown2', 'unknown3', 'vltrobot', 'vltname', 'vltprofile', 'vltsid', 'vltejects', 'unknown4', 'unknown5', 'unknown6', 'unknown7'); sub parse_line { chomp; # Yeah, they really include escaped ,s; ditch them # ... only but there are some legit \\, situations on Windows $_ =~ s#\\\\#//#g; # You can also specify multiple STUs for vaults through different # duplication rules, and these are separated by /, / in the -all_columns # output. $_ =~ 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++]; 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++; } return $job; } # third field: 0=queued, 1=active, 2=requeued, 3=done my $limit = "| egrep '^[0-9]*,[0-9]?,[0-2],[0-9]*," . $pol_prefix . "'"; my @bpdbjobs_lines = qx{bpdbjobs -all_columns $limit}; my $date = strftime('%c', localtime()); # %c is "locale appropriate" my $email_body = ''; foreach (reverse @bpdbjobs_lines) { my $job = parse_line($_); my $message = sprintf "NetBackup job ID %s still running on %s for %s under policy %s at %s.\n", $job->{'jobid'}, $job->{master_server}, $job->{client}, $job->{class}, $date; $email_body .= $message; chomp($message); if ($send_opcmsg) { my $command = 'opcmsg severity=major application=Netbackup ' . 'msg_grp=SAN object="Long-running Backup" msg_text="' . $message . '"'; $email_body .= "Notifying OpenView with command:\n $command\n"; # XXX uncomment when OpenView's ready my $output = qx{$command 2>&1}; $email_body .= $output if ($? > 0); } $email_body .= "\n"; } chomp(my $hostname = qx{uname -n}); if ($send_email && length($email_body) > 0) { my $ua = LWP::UserAgent->new; my $req = HTTP::Request->new(POST => "mailto:$email_target"); $req->header(Subject => "Long-running backup jobs for $hostname"); $req->content($email_body); my $res = $ua->request($req); print STDERR $res->status_line, "\n" unless ($res->is_success); } else { print $email_body; } exit 0;