Finding Policy Information by Client (command-line scripting)
If you're using NetBackup, I highly suggest using OpsCenter for your reporting and troubleshooting needs. It's a great tool and is very rich in its features. I've written a few SQL queries to find and process information I need in the backups, but I've found the SQL report to be somewhat buggy when there's more than one page of output. That being said, there are always things that I want to do that OpsCenter won't do for me, and one of those happens to be finding out the Policy information for a given client, and correllating the data from actual backup times with the backup windows.
Confused already? Let me explain. I have clients that want to know information about their backup schedules. They ask what the backup windows are when they mean to ask "When are the backups running so I can avoid doing [hard-disk-hitting work] during that time?" As a backup administrator, I know that if I give them the backup window information (let's say 2:00AM - 4:00AM), they may start their work sometime after the backup window has ended, though the backup job is still running (3:55AM-4:22AM). So I wanted a simple way to tell the customer that their backup windows start at time W, end at time X, and their backups typically have been starting around time Y and have been lasting for Z amount of time.
Granted, I can use OpsCenter to gather all these values, which requires me to find the client name (probably through the report), then click on the Policy, then look at the Schedules, but I wanted a one-click solution. I also wanted to be able to give that one-click solution to trusted clients, so that I could direct them to a web page, have them log in, see their servers, click on one, then see the data provided.
To gather the data, I use two NetBackup commands that get installed on the media servers and master server: "/usr/openv/netbackup/bin/admincmd/bpimagelist" and "/usr/openv/netbackup/bin/admincmd/bppllist".
The outputs have options of having human-readable format (-U) or data-readable format. I wanted the data-readable format because when looking at the schedules, they can sometimes be unpredictable as far as length goes. There are some other variants in the output that make it harder for scripting, but I don't want to go into the checks that I don't want to do. I also made the decision to do the script in perl since I would be parsing a lot of text lines, in addition to calculating the average times and start times.
Speaking of times, I would need to start with a few basic functions:
# Converts seconds to HH:MM:SS format sub sectotimestring { if ( $_[0] ) { my $secs = $_[0]; my $hours = floor($secs / 3600); my $minutes = floor($secs / 60) % 60; my $seconds = $secs % 60; my $output = sprintf("%02d:%02d:%02d",$hours,$minutes,$seconds); return $output; } } # Calculates average of a list sub avg { return floor(sum(@_)/@_); } # Takes epoch time and converts the date to MM/YY/DD format. sub datetodatestring { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($_[0]); my $output = sprintf("%s/%s/%s",$mon+1,$mday,1900+$year); return $output; }
# Takes epoch time and converts the date to MM/YY/DD HH:MM format. sub datetotimestring { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($_[0]); my $output = sprintf("%s/%s/%s %s:%s",$mon+1,$mday,1900+$year,$hour,$min); return $output; } # Extracts the seconds converted from HH:MM of the epoch time. sub extractsecs { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($_[0]); my $output = $hour * 3600 + $min * 60 + $sec; return $output; } # Converts day of week (0..7) to a readable format sub dowtostring { my @s = split(',',$_[0]); my $output = sprintf("%s %s",$rdths[$s[1]],$days[$s[0]]); return $output; }
sub usage { printf("USAGE: $BASENAME -client <client> [-hours <hours>]\n"); printf("\n"); printf(" <client> - The name of the client you wish to inspect.\n"); printf(" <hours> - How many hours back you want to average.\n"); printf("\n"); } GetOptions("client=s" => \$clientname, "hours=i" => \$phours) or die("Error in arguments"); if ( $clientname eq '' ) { usage(); exit 1; } open(FH,"$bpilcmd -L -client $clientname -hoursago $phours |") || die "Failed to open: $!\n"; while (<FH>) { $line = $_; @sval = split(/\s+/,$line); if ( $#sval > 0 ) { chomp($sval[-1]); } if ($line =~ /^Policy:/ ) { $policyname = $sval[1]; } elsif ( $line =~ /^Client:/ ) { $clientname = $sval[1]; } elsif ( $line =~ /^Sched Label:/ ) { $schedulename = $sval[2]; } elsif ( $line =~ /^Backup Time:/ ) { # "Backup Time: Tue Nov 19 18:29:02 2013 (1384910942)" $starttime = $sval[7]; $starttime =~ s/[()]//g; $starttime = extractsecs($starttime); } elsif ( $line =~ /^Elapsed Time:/ ) { # "Elapsed Time: 68 second(s)" $elapsed = $sval[2]; push(@{$starttimes{$clientname}{$policyname}{$schedulename}},$starttime); push(@{$elapsedtimes{$clientname}{$policyname}{$schedulename}},$elapsed); # printf("$policyname,$clientname,$schedulename,$starttime,$elapsed\n"); # printf("%s,%s,%s,%s,%s\n",$policyname,$clientname,$schedulename,sectotimestring($starttime),sectotimestring($elapsed)); } } close(FH); for my $polkey ( keys %{$starttimes{$clientname}} ) { print $polkey . "\n"; # We're already in starttimes -> clientname -> policy. # We want the average information from # @{$starttimes{$clientname}{$policyname}{$schedulename}} # @{$elapsedtimes{$clientname}{$policyname}{$schedulename}} for my $schedkey (keys %{$starttimes{$clientname}{$polkey}}) { $avgstart{$clientname}{$policyname}{$schedkey} = avg(@{$starttimes{$clientname}{$polkey}{$schedkey}}); $avgelapsed{$clientname}{$policyname}{$schedkey} = avg(@{$elapsedtimes{$clientname}{$polkey}{$schedkey}}); } open(FH,"$bpplcmd $polkey -l |") || die "Failed to open: $!\n"; while (<FH>) { $line = $_; @sval = split(/\s+/,$line); if ( $line =~ /^CLASS / ) { $policyname = $sval[1]; $policies{$policyname}{'name'} = $policyname; } elsif ( $line =~ /^INFO / ) { $policies{$policyname}{'active'} = $sval[11]; } elsif ( $line =~ /^RES / ) { $policies{$policyname}{'residence'} = $sval[1]; } elsif ( $line =~ /^CLIENT /) { push(@clients,$sval[1]); $policies{$policyname}{'clients'} = \@clients; push(@{$clienttopolicymap{$sval[1]}},$policyname); } elsif ( $line =~ /^SCHED / ) { $schedulename = $sval[1]; $schedstruct{'name'} = $schedulename; push(@schedules,$schedulename); $policies{$policyname}{'schedules'} = \@schedules; $policies{$policyname}{'type'} = $sval[2]; } elsif ( $line =~ /^SCHEDCALIDATES / ) { my @sv = @sval[1..$#sval]; $policies{$policyname}{$schedulename}{'schedidates'} = \@sv; } elsif ( $line =~ /^SCHEDCALEDATES / ) { my @sv = @sval[1..$#sval]; $policies{$policyname}{$schedulename}{'schededates'} = \@sv; } elsif ( $line =~ /^SCHEDCALDAYOWEEK / ) { my @sv = @sval[1..$#sval]; $policies{$policyname}{$schedulename}{'scheddayoweek'} = \@sv; } elsif ( $line =~ /^SCHEDWIN / ) { my @sv = @sval[1..$#sval]; $policies{$policyname}{$schedulename}{'schedwin'} = \@sv; } elsif ( $line =~ /^SCHEDRL / ) { $schedrl{$policyname}{$schedulename} = $sval[1]; } } close(FH); } # Print output for my $key ( keys %policies ) { my $name = $policies{$key}{'name'}; my $active = $policies{$key}{'active'}; my $residence = $policies{$key}{'residence'}; my @clients = @{$policies{$key}{'clients'}}; my @schedules = @{$policies{$key}{'schedules'}}; foreach my $i (@schedules) { my $starting = 0; my $ending = 0; my $windowday = 1; my $windowdaytext = ''; my @sid = (); my @sed = (); my @sdd = (); my @sw = (); @sid = @{$policies{$key}{$i}{'schedidates'}} if exists $policies{$key}{$i}{'schedidates'}; @sed = @{$policies{$key}{$i}{'schededates'}} if exists $policies{$key}{$i}{'schededates'}; @sdd = @{$policies{$key}{$i}{'scheddayoweek'}} if exists $policies{$key}{$i}{'scheddayoweek'}; @sw = @{$policies{$key}{$i}{'schedwin'}} if exists $policies{$key}{$i}{'schedwin'}; printf "SCHEDULE: \n"; printf " Name: $name\n"; printf " Active: $active\n"; printf " Residence: $residence\n"; printf " Schedule: $i\n"; printf(" Retention Level: %s\n",$rlevels[$schedrl{$name}{$i}]); foreach my $j (@sid) { # The $yearatleast variable checks if the date is within the past year. if ( $j > $yearatleast ) { printf " Include: " . datetodatestring($j) . "\n"; } } foreach my $j (@sed) { # The $yearatleast variable checks if the date is within the past year. if ( $j > $yearatleast ) { printf " Exclude: " . datetodatestring($j) . "\n"; } } foreach my $j (@sdd) { my @sweek = split(";",$j); printf " Day of Week: $j\n"; } foreach my $j (@sw) { if ( $windowday %2 == 1 ) { $starting = $j; } else { $ending = $j; } if ( $starting != 0 && $ending != 0 && $windowday%2 == 0) { printf(" %s: %s - %s\n",$days[floor($windowday/2)],sectotimestring($starting),sectotimestring($ending)); } $windowday++; } if ( $avgstart{$clientname}{$key}{$i} ) { printf(" Average Start Time: %s\n",sectotimestring($avgstart{$clientname}{$key}{$i})); } if ( $avgelapsed{$clientname}{$key}{$i} ) { printf(" Average Elapsed Time: %s\n",sectotimestring($avgelapsed{$clientname}{$key}{$i})); } printf "\n"; } }
So that's in in a nutshell. I know that almost everything that's here I can get from OpsCenter, but I really wanted a one-click solution to what I wanted and ability to further customize how I see fit (like adding the file sizes; I may just do that). I'll add the download link below so you won't have to copy/paste it so much.
Usage and download information is available here: https://www-secure.symantec.com/connect/downloads/finding-policy-information-client-download
Enterprise Data Services Community Blog is the perfect place to share short, timely insights including product tips, news and other information relevant to the community.