Net::Appliance::Session::Cookbook::Recipe06

Net::Appliance::Session::Cookbook::Recipe06 is a Perl module for storing device configurations in files.
Download

Net::Appliance::Session::Cookbook::Recipe06 Ranking & Summary

Advertisement

  • Rating:
  • License:
  • Perl Artistic License
  • Price:
  • FREE
  • Publisher Name:
  • Nigel Bowden
  • Publisher web site:
  • http://search.cpan.org/~oliver/Net-Appliance-Session-0.21/lib/Net/Appliance/Session/Cookbook/Recipe01.pod

Net::Appliance::Session::Cookbook::Recipe06 Tags


Net::Appliance::Session::Cookbook::Recipe06 Description

Net::Appliance::Session::Cookbook::Recipe06 is a Perl module for storing device configurations in files. Net::Appliance::Session::Cookbook::Recipe06 is a Perl module for storing device configurations in files.NOTEThis 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.PROBLEMYou want to telnet or SSH to all of your Cisco IOS devices and pull off their configurations so that you can build up a historic record of configuration files across your network.SOLUTIONWell, if this is a cookbook, then this particular section is a 6 course banquet. This is a huge script compared to the previous examples we have looked at.It brings together all of the areas we have looked at previously, but I have pulled in the services of a number of other Perl modules to try to provide a usable utility that could perhaps be used on your network to gather your Cisco device configurations.All of the credentials that are required to access your devices are now stored in a CSV file, so devices can be added or removed as required. As different devices may have differing access requirements (e.g. transports, username/passwords required etc.) each device is configured with its own set of access credentials. CSV files (comma seperated values) are easy to update using many spreadsheet applications, making it easy to maintain the device credential data for this script.Also, the script uses an external intialization file (.ini) to configure the operation of the script (e.g. where configuration files will be dumped), rather than having to hard-code values in the body of the script itself.The script comprises two files:Recipe_06.plThe file below, in this Cookbook.setup.iniThe initialization file that must be in the same directory as Recipe_06.pl.device_credentials.csvThe CSV file whose location is configured in setup.ini.The main body of the script should be relatively easy to follow, as it is very similar to previous examples. Where new functions are introduced (e.g. reading and parsing a CSV file), then those functions have (in the main) been separated off in to additional subroutines.Here is the full solution: use strict; use warnings; use Carp; use Net::Appliance::Session; use Config::INI::Simple; use Text::CSV_XS; use File::Basename; # create Config::INI::Simple object and read in our ini file data my $ini_filename = dirname($0) . "/setup.ini"; # where we can find ini file my %ini_data = parse_ini_file($ini_filename); # get the device credentials for our devices my @device_data = parse_data_file($ini_data{device_csv_file}); # step through each device and try to get and store our configs DEVICE: for my $device_ref (@device_data) { my $device_name = $device_ref->{device_name} || $device_ref->{device_ip}; # set up some logging my $debug_log = "$ini_data{debug_dir}/$device_name.debug.log"; my $error_log = "$ini_data{error_dir}/$device_name.error.log"; # create our config file name my $file_timestamp = file_timestamp(); my $running_config_file = "$ini_data{repository_dir}/$device_name.$file_timestamp.conf"; # create our Net::Appliance::Session with the transport for this device my $session_obj = Net::Appliance::Session->new( Host => $device_ref->{device_ip}, Transport => $device_ref->{transport}, ); # send the debug for this session to a device-specific file $session_obj->input_log($debug_log); my @running_config; # generate the required fields for the priv_array subroutine my @priv_array = priv_array($device_ref); # tell our session object we don't need enable password if none supplied unless ($priv_array) { $session_obj->do_privileged_mode(0); } # do our interactive (Telnet/SSH) stuff... eval { # try to login to the ios device, ignoring host check $session_obj->connect( connect_hash($device_ref), SHKC => 0 ); if ( $priv_array ) { # if we need to use some enable credentials, supply them $session_obj->begin_privileged( @priv_array ); } # get our running config @running_config = $session_obj->cmd('show running'); }; # did we get an error ? if ($@) { # log error to file and move on to next device log_error( error_report($@, $device_name), $error_log ); next DEVICE; } # chop out the extra info top and bottom of the config @running_config = @running_config; # dump the config to a file open(CONFIG , " > $running_config_file") or warn("Unable to open config file for : $device_name : $!"); print CONFIG @running_config; close CONFIG; # close down our session $session_obj->close; } ##################################### # Subroutines ##################################### sub parse_ini_file { # parse our ini file to get the parameters we need in to # some convenient variables my $ini_filename = shift or croak("No ini file name passed"); my $config_obj = Config::INI::Simple->new(); $config_obj->read($ini_filename) or die("Cannot open ini file : $ini_filename (reason: $!)"); my %ini_data; # variable to use as data hash to hold all ini file data # set up some variables for later use $ini_data{error_dir} = $config_obj->{Logs}->{error_dir}; $ini_data{debug_dir} = $config_obj->{Logs}->{debug_dir}; $ini_data{device_csv_file} = $config_obj->{CSV}->{device_csv_file}; $ini_data{repository_dir} = $config_obj->{Repository}->{repository_dir}; $ini_data{timestamp_format} = $config_obj->{Repository}->{timestamp_format}; return %ini_data; } sub parse_data_file { # parse the CSV data file we are using to hold our device # credential data my $device_csv_file = shift or croak("No csv file named passed"); # create our csv object ready to parse in the data from our csv file my $csv_obj = Text::CSV_XS->new(); #read in our csv file open my $csv_fh, "< $device_csv_file" or croak("Cannot open device csv file : $device_csv_file (reason: $!)"); # take off the top row that has the field names my $top_row = $csv_obj->getline($csv_fh); my @device_data; # take each entry in the CSV file and massage it into a complex # data structure while (my $data_row = $csv_obj->getline($csv_fh)) { my $hash_ref; map { ($hash_ref->{$_} = shift @$data_row) } @$top_row; push(@device_data, $hash_ref); } close $csv_fh; return @device_data; } sub connect_hash { # depending on the combination of credentials supplied, determine # the combination of login username/password to use my $device_ref = shift or croak("No device credentials ref passed !"); # decide which set of credentials we have if ( exists($device_ref->{username}) && exists($device_ref->{password}) ) { # username & password supplied return ( Name => $device_ref->{username}, Password => $device_ref->{password} ); } elsif ( exists($device_ref->{password}) ) { # password only supplied return ( Password => $device_ref->{password} ); } else { croak("Invalid or missing credentials to log in to this device : " . $device_ref->{device_name} ); } } sub priv_array { # depending on the combination of credentials supplied, determine # the combination of enable username/password to use my $device_ref = shift or croak("No device credentials ref passed !"); # decide which set of priv credentials we have if ( $device_ref->{enable_username} && $device_ref->{enable_password} ) { # username & password supplied return( $device_ref->{enable_username}, $device_ref->{enable_password} ); } elsif ( $device_ref->{enable_password} ) { # password only supplied return( $device_ref->{enable_password} ); } elsif ( $device_ref->{enable_username} ) { # username only supplied - error ! croak( "Invalid enable login credentials provided (only username provided !" ); } else { # no enble pwd required (assume drop straight in to enable mode) return 0; } } sub error_report { # standard subroutine used to extract failure info when # interactive session fails my $err = shift or croak("No err !"); my $device_name = shift or croak("No device name !"); my $report; # holder for report message to return to caller if ( UNIVERSAL::isa($err, 'Net::Appliance::Session::Exception') ) { # fault description from Net::Appliance::Session $report = "We had an error during our Telnet/SSH session to device : $device_name n"; $report .= $err->message . " n"; # message from Net::Telnet $report .= "Net::Telnet message : " . $err->errmsg . "n"; # last line of output from your appliance $report .= "Last line of output from device : " . $err->lastline . "nn"; } elsif (UNIVERSAL::isa($err, 'Net::Appliance::Session::Error') ) { # fault description from Net::Appliance::Session $report = "We had an issue during program execution to device : $device_name n"; $report .= $err->message . " n"; } else { # we had some other error that wasn't a deliberately created exception $report = "We had an issue when accessing the device : $device_name n"; $report .= "The reported error was : $err n"; } return $report; } sub log_error { # log an error message to a file my $error_message = shift; my $file_name = shift; open(ERR , " > $file_name") or carp("Unable to error file : $file_name : $!"); print ERR $error_message; close ERR; } sub file_timestamp { # create a timestamp to add to the conf files created my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); if ($ini_data{timestamp_format} eq 'uk') { # UK format return sprintf( "d-d-M-dd", $mday, ($mon + 1), ($year + 1900), $hour, $min ); } else { # US format return sprintf( "d-d-M-dd", ($mon + 1), $mday, ($year + 1900), $hour, $min ); } }Requirements:· Perl Requirements: · Perl


Net::Appliance::Session::Cookbook::Recipe06 Related Software