shroh has asked for the wisdom of the Perl Monks concerning the following question:

Hi Experts, I have a requirement to print total number of failed items out of total number of elements in the array. I am running a command on each element of an array, for some elements the command may fail for some reason. I am handling the errors in a subroutine. Finally i want to print out in the log file for the number of unsuccessful executions out of total and successful executions out of total. I am not able to build that logic. Any help is highly appreciated. Below is the code.
#!C:\Perl\bin\perl.exe use strict; use warnings; #use diagnostics; # # This script is created to put the servers # in unplanned outage as part of the tasks that # are received to stop the monitoring on the servers # due to some maintenance activity on the servers. # Author : ROHIT SHARMA (INYROHS) #my $path = 'E:/scripts/OutageNodes/'; #require $path.'omwNodeDetails.pm'; &main(); sub main { my $path = 'E:/scripts/OutageNodes/'; require $path.'omwNodeDetails.pm'; #my ($mode,@nodes); printLog("**************Script Start*************"); my $mode=get_mode(); my @nodes=get_node(); if( $mode eq 'enable'){ enable_unplanned_outage(@nodes); } elsif($mode eq 'disable'){ disable_unplanned_outage(@nodes); } else { printLog("Error invalid Mode $mode"); } # clear serverlist printLog ("Truncating serverlist"); open( SRV,'>',$path.'serverlist.txt') or die "Can't open SRV '$path.serverlist': $!"; close SRV; printLog("**************Script Stop*************"); } sub get_mode { # expect values 'enable|disable' my $maintMode = lc $ARGV[0]; chomp($maintMode); if ($maintMode ne 'enable' && $maintMode ne 'disable'){ $maintMode="error"; } return $maintMode; } sub get_node { #my ($sPath,$sFile,$sInFile,$sText, @sContent,@serverlist,$hostname,$f +qdn); my $sPath='e:/scripts/OutageNodes/'; my $sInFile=$sPath.'serverlist.txt'; my @sContent=(); my $sText; ##checks if the file exists and pulls the info out if (-e "$sInFile"){ open(INFILE, "<$sInFile"); while (<INFILE>){ chomp; $sText="$_"; push(@sContent, $sText); } + close INFILE; } else{ &printLog("Error Cannot open $sInFile"); } # check serverlist had entries if (@sContent == 0){ printLog("ERROR No nodes found in serverlist.txt"); printLog("**************Script Stop*************"); exit; } #get FQDN************ my $hostname; my $fqdn; my @serverlist; for my $NODE (@sContent){ ($hostname) = split /\./, $NODE; $fqdn = getNodeAttributes($hostname,'PrimaryNodeName'); printLog($fqdn); printLog($hostname); if(length($fqdn) < 1) { printLog("No value returned from WMI, node ($NODE) doesn't ex +ists in OMW."); next; } push(@serverlist,$fqdn); } return(@serverlist); } sub enable_unplanned_outage { #my ($FQDN,$cmd); my @nodelist=@_[0..$#_]; foreach my $FQDN (@nodelist) { my $cmd = "ovownodeutil.cmd -outage_node -unplanned -disable_he +artbeat -delete_msgs -node_name $FQDN -on "; my $output =`$cmd`; printLog ("===================\n Putting the server $FQDN into outage."); `$cmd`; printLog($output); } } sub disable_unplanned_outage { #my ($FQDN, $mode, $cmd, $cmdresopcmona, @sContent,$output); my @sContent = @_[0..$#_]; ####When the servers is brough out of maintenance the agent has to be +recycled to reset all the monitors. foreach my $FQDN (@sContent) { my $cmd = "ovownodeutil.cmd -outage_node -unplanned -disable_h +eartbeat -delete_msgs -node_name $FQDN -off "; printLog("Node $FQDN >>> off"); my $cmdresopcmona = "ovdeploy -cmd \"ovc -restart opcmona\" -host +$FQDN"; my $output=`$cmd`; #printLog($cmd); `$cmd`; printLog($output); printLog($cmdresopcmona); `$cmdresopcmona`; } } sub processError { my ($error, $text) = @_; if ($error != 0) { $i++; } } sub printLog { #my ($sPath, $logFile, $sOutFile, $sText, $date_time, $SEC, $MIN, $ +HOUR, $DAY, $MON, $YEAR,$now,$logLine,$now); #get date/time my($SEC, $MIN, $HOUR, $DAY, $MON, $YEAR) = (localtime) [0..6]; my $date_time = $YEAR + 1900 . $MON + 1 . $DAY ; #get log file parameters my $sPath = "e:\\scripts\\OutageNodes\\"; #revi +ew - change to a standard location my $logFile=$sPath."maintenanceMode_$date_time.log"; my ($logLine) = @_; my $now = sprintf "%02d:%02d:%02d",(localtime)[2,1,0]; open (LOG,'>>',$logFile) or die "Can't open LOG '$logFile': $! +\n"; print LOG "$now $logLine\n"; close LOG; }

Replies are listed 'Best First'.
Re: Error Handling the Perl script
by kcott (Archbishop) on Aug 12, 2015 at 19:07 UTC

    G'day shroh,

    Firstly, do yourself (and all subsequent maintainers) a favour by removing all the extraneous whitespace your code is littered with and pick (and stick to) some consistent indentation scheme. perlstyle - Perl Style Guide may help you with this.

    "I am running a command on each element of an array, ..."

    I'm guessing this is @nodelist; although, I'm not entirely sure. You are running a command for every element of that array. Below, I've just referred to @command_array: I'll leave you to modify as appropriate.

    "I am handling the errors in a subroutine."

    I see "sub processError {...}" but you don't call it anywhere. I'll assume this is what you're referring to.

    Near the top of your script, add:

    my @command_array; my $command_error_count = 0;

    Limit the scope of these with an anonymous block if necessary.

    In "sub processError {...}", increment $command_error_count.

    When processing is finished, you can then access (for your log entry):

    • Total commands: scalar @command_array
    • Successful commands: @command_array - $command_error_count
    • Unsuccessful commands: $command_error_count

    — Ken

Re: Error Handling the Perl script
by Laurent_R (Canon) on Aug 12, 2015 at 18:57 UTC
    Hi,

    you have at least one error on line 235: the $i variable hasn't been declared.

    Otherwise, I would suggest that you remove most of these silly vertical spaces, this makes the code harder to read. In general, I want to maximize the quantity of code lines that I can see on a screen page, because I need less navigating through the screen pages and it is easier to see what's going on. I usually put a blank line between two subroutine definitions or, within one sub, between blocks or pieces of code doing different things.

    I would add that that you also need to be much more consistent with your code indentation: that also helps a lot understanding what the code is doing.

Re: Error Handling the Perl script
by poj (Abbot) on Aug 12, 2015 at 21:06 UTC

    Use a hash to store the output from the commands. Then process them all in a loop. I'm assuming the word 'error' or similar appear somewhere in the output for those commands that failed. Alternative use a word that means OK and reverse the logic. Here is your script refactored. Note the log file is only opened once at the start of the script rather than for each entry.

    poj
      Hi Poj, I am only looking to capture the error where it says that "No Value returned from WMI, node doesnt exists in OMW" in the get_node subroutine. Any how i want to capture the total number of this error an print it finally in the log file.
      #!C:\Perl\bin\perl.exe use strict; use warnings; use Data::Dump 'pp'; #use diagnostics; # This script is created to put the servers # in unplanned outage as part of the tasks that # are received to stop the monitoring on the servers # due to some maintenance activity on the servers. # Author : ROHIT SHARMA (INYROHS) main(); sub main { my $result = {}; # holds result of commands my %count=( 'error' => 0, 'OK' => 0, ); my $path = 'E:/scripts/OutageNodes/'; require $path.'omwNodeDetails.pm'; open_log($path.'maintenanceMode_'); my $mode = get_mode($ARGV[0]); if ($mode eq 'error'){ print_log("ERROR Invalid Mode '$ARGV[0]'"); ++$count{'error'}; ##I dont want to capture this error. } else { my $nodelist = get_node($path.'serverlist.txt'); if (@$nodelist == 0){ print_log("ERROR No nodes found in serverlist.txt"); ++$count{'error'}; ## I dont want to capture this error; } else { if ( $mode eq 'enable'){ $result = enable_unplanned_outage($nodelist); } if ($mode eq 'disable'){ $result = disable_unplanned_outage($nodelist); } # count errors from results process_error(\%count,$result); # clear serverlist if ($count{'error'} == 0){ print_log("Truncating serverlist"); open SRV,'>',$path.'serverlist.txt' or die "Can't open SRV '$path.serverlist': $!"; close SRV; } } } close_log(); print "Completed Errors = $count{'error'} OK = $count{'OK'}\n"; # Data::Dump pretty print pp $result; } # expect values 'enable|disable' sub get_mode { my $maintMode = lc shift; if ($maintMode ne 'enable' && $maintMode ne 'disable'){ $maintMode = "error"; } return $maintMode; } sub get_node { my ($infile) = @_; my @nodelist = (); ##checks if the file exists and pulls the info out if (-e $infile){ open INFILE, '<', $infile or die "Could not open $infile : $!"; print_log("Scanning $infile"); while (my $node = <INFILE>){ chomp($node); my ($hostname) = split /\./, $node; my $fqdn = getNodeAttributes($hostname,'PrimaryNodeName'); if (length($fqdn) < 1) { print_log("No value returned from WMI, node ($node) doesn't ex +ists in OMW."); ++$count{'error'}; ## I want to capture these errors for each +element of the array. Want to see how many servers out of total are g +iving this error. } else { print_log("$node => $hostname => $fqdn"); push @nodelist,$fqdn; } } close INFILE; } else { print_log("ERROR Cannot open $infile"); } return \@nodelist; } sub enable_unplanned_outage { my ($nodelist) = @_; my %result = (); foreach my $node (@$nodelist) { print_log ("===================\n Putting the server $node into outage."); my $cmd = "ovownodeutil.cmd -outage_node -unplanned -disable_heart +beat -delete_msgs -node_name $node -on "; print_log($cmd); my $output = `$cmd`; print_log($output); $result{$node}{'ovownodeutil'} = $output; } return \%result; } # When the servers is brought out of maintenance # the agent has to be recycled to reset all the monitors. sub disable_unplanned_outage { my ($nodelist) = @_; my %result=(); foreach my $node (@$nodelist) { print_log("Node $node >>> off"); my $cmd = "ovownodeutil.cmd -outage_node -unplanned -disable_heart +beat -delete_msgs -node_name $node -off "; print_log($cmd); my $output = `$cmd`; print_log($output); $result{$node}{'ovownodeutil'} = $output; my $cmdresopcmona = "ovdeploy -cmd \"ovc -restart opcmona\" -host +$node"; print_log($cmdresopcmona); $output = `$cmdresopcmona`; print_log($output); $result{$node}{'ovdeploy'} = $output; } return \%result; } # check results for error sub process_error { my ($count,$result) = @_; for my $node (sort keys %$result){ for my $cmd (sort keys %{$result->{$node}}){ my $text = $result->{$node}{$cmd}; if ( $text =~ /error/i){ ++$count->{'error'}; } else { ++$count->{'OK'}; } } } } sub print_log { my ($logLine) = @_; my $now = sprintf "%02d:%02d:%02d",(localtime)[2,1,0]; print LOG "$now $logLine\n"; } sub open_log { my ($filename) = @_; my ($day,$month,$year) = (localtime) [3..5]; my $date = sprintf "%04d%02d%02d",$year + 1900 , $month + 1 , $day ; my $logfile = $filename.$date.'.log'; open LOG,'>>',$logfile or die "Can't open LOG '$logfile': $!\n"; print LOG "\n--- START ---\n"; } sub close_log { print LOG "=== END ===\n"; close LOG; }

        Create an errorlist the same as nodelist

        sub get_node { my ($infile) = @_; my @nodelist = (); my @errorlist = (); ##checks if the file exists and pulls the info out if (-e $infile){ open INFILE, '<', $infile or die "Could not open $infile : $!"; print_log("Scanning $infile"); while (my $node = <INFILE>){ chomp($node); my ($hostname) = split /\./, $node; my $fqdn = getNodeAttributes($hostname,'PrimaryNodeName'); if (length($fqdn) < 1) { print_log("No value returned from WMI, node ($node) doesn't ex +ists in OMW."); push @errorlist,$node; } else { print_log("$node => $hostname => $fqdn"); push @nodelist,$fqdn; } } close INFILE; } else { print_log("ERROR Cannot open $infile"); } return (\@nodelist,\@errorlist); }
        And return the value to main
        my ($nodelist,$errorlist) = get_node($path.'serverlist.txt');
        and add line after clearing server list
        print_log("There were ".scalar @$errorlist." errors in serverlist");

        delete the process_error code.

        poj