cancel
Showing results for 
Search instead for 
Did you mean: 

Finding Policy Information by Client (command-line scripting)

AlanTLR
Level 5

        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;
}
        The functions are pretty self-explanatory.  I need a function to calculate averages, and some functions to convert strings to time/date elements.  The rest of the script isn't very self-explanatory, but I'll summarize what's happening.  Essentially, I'm building a couple of hashed values based on the client and/or policy (I programmed each independently, so the first part isn't as optimized as the second).  First, I'm gathering all of the data from the bpimagelist command for the specified number of previous hours (-hoursago flag), and putting those values into several different hashes: for each policy that has the client, for each of those schedules that are in that policy, I get the start time and elapsed time.   I then calculate the average start time and the average elapsed time for each schedule of each policy and store those into the hash.
 
        In the second half, I grab all of the policy information that's relevant: policy name, schedules, whether they're active or not, its storage residence, what other clients there may be, and all the scheduled backup window information (include dates, exclude dates, etc.).  Once I have all this information mapped in the hash to the client, I can generate the output however I see fit.  I like the similar output that bpllist gives with the -U option, so I've modeled it after that, though I can put it into CSV format if I wanted to generate my own graphs, etc (useful if I grab the size, too, which is easy enough to add to the bpimagelist while loop).
 
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