The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Net::Appliance::Session::Cookbook::Recipe03 - Simple Error Handling

NOTE

This Cookbook was contributed to the Net::Appliance::Session project by Nigel Bowden. Source code from the Cookbook is shipped in the examples folder of this module's distribution.

PROBLEM

You want to perform an opertation using SSH on a number of Cisco IOS devices, but don't want the script to fail just because an operation on one device failed.

SOLUTION

This solution is very similar to recipe 02, but this time we attempt to get information from more than one device.

Again, we assume that the IOS device requires only a username and password to login to the device. At that point, it is assumed that the device is configured to drop a user straight in to enable mode (privilege level 15) when logged in. See recipe 02 for details of how to explicitly drop in to enable mode.

 use strict;
 use warnings;
 
 use Net::Appliance::Session;
 
 # list of devices to contact
 my @ios_device_ip_addresses = ('10.250.249.215', '10.250.249.216');
 
 my $ios_username        = 'cisco';
 my $ios_password        = 'cisco';
 
 # step through each device in turn
 for my $ios_device_ip ( @ios_device_ip_addresses ) {
 
     my @version_info; # array to hold data from device
 
     my $session_obj = Net::Appliance::Session->new(
         Host      => $ios_device_ip,
         Transport => 'SSH',
     );
     
     # start eval block to trap errors in interactive session
     eval {
         # try to login to the ios device, ignoring host check
         $session_obj->connect(
             Name => $ios_username,
             Password => $ios_password,
             SHKC => 0
         );
         
         # get our running config
         @version_info =  $session_obj->cmd('show ver');
         
         # close down our session
         $session_obj->close;
     };
     
     # check if we had an error
     if ($@) {
         print "We had an issue when accessing the device : $ios_device_ip \n";
         print "The reported error was : $@ \n\n";
     }
     else {
         print "Version infor for $ios_device_ip retrieved OK, here it is : \n\n"; 
         print @version_info;
     }
 
 # end of for loop
 }

DISCUSSION

In this recipe, we want to try to contact a number of Cisco IOS devices and pull some information from them. So, we use a simple for loop to step though a list of IP addresses:

 # list of devices to contact
 my @ios_device_ip_addresses = ('10.250.249.215', '10.250.249.216');
 
 my $ios_username        = 'cisco';
 my $ios_password        = 'cisco';
 
 # step through each device in turn
 for my $ios_device_ip ( @ios_device_ip_addresses ) {

By default, Net::Appliance::Session will generate an exception (i.e. die) when it encounters any issues during an interactive session (e.g. Telnet or SSH) with a device.

For instance, if the user credentials are incorrect, or maybe a device is un-reachable, then your script will grind to a halt at the point that it fails. If you are running your script from command line, you will also get a helpful failure message to give you a clue as to what went wrong.

However, if you are using your script to access a number of devices to retrieve some information or perform an operation, then you maybe don't want the script to fail part-way through a series of operations on a number of devices. For instance, if one device is un-reachable, you may want the script to skip this device and move on to the next one.

In order to work around the default behaviour of Net::Appliance::Session, we have to apply a bit of perl trickery to allow the failure to occur, but for our script to detect it, report on it and then continue.

The way that we have to do this is to wrap the interactive segments of our code (i.e. when we are logging in, sending commands etc.) in eval {} statements.

The eval statement allows us to run the code between its braces as a normal piece of perl code, but if an exception is raised (e.g. it dies), we can detect the failure by inspecting the perl builtin $@ variable to see why things went wrong. This is Perl's version of an Exception (try/catch) system as found in other languages.

Here is the code from our recipe that uses the eval statement to trap any errors :

 eval {
     # try to login to the ios device, ignoring host check
     $session_obj->connect(
         Name => $ios_username,
         Password => $ios_password,
         SHKC => 0
     );
         
     # get our running config
     @version_info =  $session_obj->cmd('show ver');
         
     # close down our session
     $session_obj->close;
 };

You can see that we have taken our standard set of commands that we use to login to the device and attempt to get some information, but have wrapped the eval {} statement around them.

Each statement will be executed as normal, except if there is an error condition. If an error occurs, then the execution will halt at that point, and subsequent commands in the braces will not be executed. The flow of the script then switches to the end of the eval braces, giving us the opportunity to find out what may have gone wrong:

 # check if we had an error
 if ($@) {
     print "We had an issue when accessing the device : $ios_device_ip \n";
     print "The reported error was : $@ \n\n";
     
 }
 else {
     print "Version infor for $ios_device_ip retrieved OK, here it is : \n\n"; 
     print @version_info;
 }

The segment of code above shows how we do a simple check to see if there was an error and what happened. By checking whether a value for $@ is defined, we can see if there was an error in the code wrapped in the eval {} wrapper. If an error was detected, we can get a text message about the error by simply printing out $@.

As our script hasn't been halted in its tracks, we can now decide whether or not we continue with the flow of our script. In our particular example, even if we have a failure, we just print out a failure message and then move on to the next device by going around the next iteration of our for loop.

Net::Appliance::Session allows us to get more detailed information about the reason for failure, but we'll look more in to that in our next recipe.

SEE ALSO

Net::Appliance::Session

AUTHOR

Nigel Bowden, with POD formatting by Oliver Gorwits.

COPYRIGHT & LICENSE

Copyright (c) Nigel Bowden 2007. All Rights Reserved.

You may distribute and/or modify this documentation under the same terms as Perl itself.