#!/usr/bin/perl # ## Filename: /usr/local/etc/switchtftpbackup.pl # # Purpose: This Perl script will backup via TFTP the switch configurations of mutiple # switches and appliances to central TFTP server for disaster recovery and # documentation purposes. # # Author: Michael McNamara (http://blog.michaelfmcnamara.com) # # Credits: Stuart Kendrick (http://www.skendric.com) # I've taken a lot of ideas (and some code) from Stuart's many scripts. Without # Stuart's help it would have taken me much longer to develop some of the scripts # we've come to rely on today. # # Date: May 6, 2003 # # License: # Copyright (C) 2002 Michael McNamara (mfm@michaelfmcnamara.com) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see # # # Changes: # # May 04, 2011 (M.McNamara) added support for HP C-Class GbE2c and legacy P-Class GbE2 # thanks to Karol Perkowski for his code addition # Dec 28, 2010 (M.McNamara) added additional code to support ERS4500 being slow TFTP transfer # Dec 27, 2010 (M.McNamara) updated CISCO-PRODUCTS-MIB to cover ciscoCBS3120 blade # Dec 20, 2010 (M.McNamara) updated ASCII routine with OID s5AgSysAsciiConfigManualUpload # Aug 31, 2010 (M.McNamara) added routines to handle binary and ASCII data for Avaya ERS switches # also added code to keep 4 archive copies per device # Dec 02, 2009 (M.McNamara) cleaned up code added additional debug routines # Oct 23, 2008 (M.McNamara) added support for Motorola RFS7000 Wireless LAN Switch # Oct 22, 2008 (M.McNamara) added support for ASCII configuration files for Avaya ERS switches # Oct 10, 2008 (M.McNamara) added support for Cisco switches # Jan 22, 2008 (M.McNamara) added support for HP GbE2c (C-Class) switch # Apr 24, 2007 (M.McNamara) added support for WS5100 3.x software # Oct 24, 2006 (M.McNamara) added support for ERS1600 v2.1 release # Sep 29, 2006 (M.McNamara) added support for BayStack 470 PwR 48T # Oct 20, 2005 (M.McNamara) added support for Baystack 5510 24 port also added # Ethernet Routing Switch (formerly Passport) 8600 code # Mar 01, 2005 (M.McNamara) incorporated a sub to check for the presence of the # proper filename on the TFTP server (/tftpboot) thereby # eliminating the first script "readytftpbackup.pl" # Feb 25, 2005 (M.McNamara) added the ability to retry a failed backup # Jan 13, 2004 (M.McNamara) some minor bugs throughout code base # Jan 06, 2004 (M.McNamara) implemented a workaround for the Passport RAPID-CITY MIB # > 3.2 problem, copied OIDs for Passport 1600 into # existing MIB along with required MIBS and added sub # to handle 1600s # Jan 05, 2004 (M.McNamara) issues with SNMP MIB for Passport 8600 v3.3.4 is presenting # problems with the Net-SNMP perl modules and the old MIB # cannot identify the newly added Passport 1600 switches. # Dec 11, 2003 (M.McNamara) resolved issue with Passport 8600 not backing up properly # Sep 17, 2003 (M.McNamara) added code to incorporate all BayStack switches into backup # Oct 1, 2003 (M.McNamara) added code to email status report to notify@acme.org # also added Perl script to weekly crontab # # Notes: # This script relies heavily upon having the correct SNMP MIBS in place. # Without the proper SNMP MIBS may of the SNMP OIDs used below will fail # to resolve and ultimately fail to work properly. You can find a collection # of my SNMP MIBS at the following URL; # # http://www.michaelfmcnamara.com/files/mysnmpmibs.tar.gz # ################################################################################ # Load Modules use strict; use SNMP; use Net::Ping; use Socket; # Declare constants use constant DEBUG => 1; # DEBUG settings use constant RETRIES => 3; # SNMP retries use constant TIMEOUT => 1000000; # SNMP timeout, in microseconds use constant SNMPVER => 1; # SNMP version $SNMP::verbose = DEBUG; $SNMP::use_enums = 1; $SNMP::use_sprint_value = 1; &SNMP::initMib(); &SNMP::loadModules('ALL'); our $community = "public"; our $switchlist = "/usr/local/etc/allswitches"; our @devices; our $snmphost; our $filename; our $sysDescr; our $sysObjectID; our $sysUpTime; our $sysContact; our $sysName; our $sysLocation; our $SERVER = "netmon.mdc.acme.org"; our $sdate = `date`; our $TFTPSERVER = "10.103.24.50"; our $PAUSE = 4; our $LASTUPDATEDDATE = "Wednesday May 04, 2011"; our $MAILTO = "notify\@acme.org"; our $MAILFROM = "notify\@acme.org"; our $MAILSUBJECT = "Network Switch Configuration Backup Report"; ################################################################### ### MAIN PROGRAM ################################################## ################################################################### { # Load list of switches to backup &load_switches; # Prune list of switches before starting backups &prune_switches; # Start the HTML report &start_html_report; # Perform the TFTP backup &call_tftp_backup; # Finish the HTML report &finish_html_report; } ### END MAIN PROGRAM ################################################# ########################################################################## # Subroutine load_switches # # Purpose: load the list of switches from a file into an array ########################################################################## sub load_switches { # Open file for input open(SWITCHLIST, "<$switchlist"); # Walk through data file while () { # Skip blank lines next if (/^\n$/); # Skip comments next if (/^#/); print "DEBUG: adding $_ to our list of devices \n" if (DEBUG); # Remove the CR/LF chomp; push (@devices, $_); } #end while close(SWITCHLIST); return 1; } #end sub load_switches ########################################################################## # Subroutine check_filename # # Purpose: make sure the filename exists with the proper permissions on # the TFTP server which happens to resides on the same host ########################################################################## sub check_filename { # Declare Local Variables my $temp_filename = shift; # Let's archive the last four backups just incase we need to go back # This is a quick and dirty solution since the TFTPSERVER is on the # same server so let's just renmae the files. `/bin/mv -fu /tftpboot/$filename.3 /tftpboot/$filename.4`; `/bin/mv -fu /tftpboot/$filename.2 /tftpboot/$filename.3`; `/bin/mv -fu /tftpboot/$filename.1 /tftpboot/$filename.2`; `/bin/mv -fu /tftpboot/$filename /tftpboot/$filename.1`; print "DEBUG: I'm touching the following file /tftpboot/$temp_filename\n" if (DEBUG); `touch /tftpboot/$temp_filename`; print "DEBUG: I'm chaning the permissions on /tftpboot/$temp_filename\n" if (DEBUG); `chmod og+r+w /tftpboot/$temp_filename`; return 1; } #end sub check_filename; ########################################################################### # Subroutine tftp_filename # # Purpose: compose the TFTP filename from the FQDN of the switch ########################################################################### sub tftp_filename { #Declare Local Variables my $host = shift; my $stemp; # Let's shorten the filenames by removing the parent netmont domains $stemp = $host; $stemp =~ s/\.acme\.org//g; $stemp =~ s/\.otherhospital\.org//g; # Let's convert remaining . to - easier to read the filenames $stemp =~ s/\./-/g; $stemp = $stemp.".cfg"; $stemp = "a/".$stemp; print "DEBUG: the filename for $host will be $stemp\n" if (DEBUG); return $stemp; } #end sub tftp_filename ########################################################################## # Subroutine call_tftp_backup # # Purpose: determine which subroutine we should call and then execute ########################################################################## sub call_tftp_backup { # Loop over all the assets/devices we need to backup foreach $snmphost (@devices) { # Remove the trailing CR/LF $snmphost =~ s/\n//g; #print "DEBUG: starting loop with snmphost = $snmphost\n" if (DEBUG); # Let's determine the filename that we'll use for this host $filename = &tftp_filename($snmphost); # Let's check for basic ICMP and SNMP access before continuing if (&grab_snmpsystem == 99) { next; } print "DEBUG: sysObjectID = $sysObjectID ($snmphost)\n"; # Ethernet Routing Switch 8600 and 1600 Switches if ( ($sysObjectID eq "rcA8610") || ($sysObjectID eq "rcA8606") || ($sysObjectID eq "rcA1648") ) { &passport_tftp_config; # Ethernet Switch 460,470 Ethernet Routing Switch 4500, 5500 series } elsif (($sysObjectID eq "sreg-BayStack5520-48T-PWR") || ($sysObjectID eq "sreg-BayStack5520-24T-PWR") || ($sysObjectID eq "sreg-EthernetRoutingSwitch5530-24TFD") || ($sysObjectID eq "sreg-BayStack5510-48T-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack5510-24T-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack470-48T-PWR-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack470-48T-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack470-24T-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack460-24T-PWR-ethSwitchNMM") ) { &baystack_tftp_config; &baystack_tftp_config_ascii; # Ethernet Routing Switch 4500 } elsif ( ($sysObjectID eq "sreg-ERS-4548GT-PWR") ) { &baystack_tftp_config; # BayStack 450 Switch } elsif ( ($sysObjectID eq "sreg-BayStack450-ethSwitchNMM") || ($sysObjectID eq "sreg-BayStack350-24T-ethSwitchNMM") || #($sysObjectID eq "sreg-ERS-4548GT-PWR") || ($sysObjectID eq "sreg-BPS2000-24T-ethSwitchNMM") ) { &baystack_tftp_config; # HP GbE2 Blade Switches (P-Class) } elsif ($sysObjectID eq "hpProLiant-p-GbE2-InterconnectSwitch") { &hpgbe2_tftp_config; # HP GbE2c Blade Switches (C-Class) } elsif ($sysObjectID eq "hpProLiant-GbE2c-InterconnectSwitch") { &hpgbe2c_tftp_config; # Motorola WS5100 v3.x and RFS7000 v1.2 } elsif ( ($sysObjectID eq "ws") || ($sysObjectID eq "rfs7000") ) { &ws5100_config; # Cisco Catalyst Switches } elsif ( ($sysObjectID eq "catalyst2924XLv") || ($sysObjectID eq "catalyst295024G") || ($sysObjectID eq "catalyst295048G") || ($sysObjectID eq "catalyst37xxStack") || ($sysObjectID eq "catalyst3560E24PD") || ($sysObjectID eq "cisco2651") || ($sysObjectID eq "catalyst6kMsfc2") || ($sysObjectID eq "ciscoCBS3120") || ($sysObjectID eq "catalyst3560G48TS") || ($sysObjectID eq "catalyst3560G48PS") || ($sysObjectID eq "catalyst3560G24PS") || ($sysObjectID eq "catalyst356024PS") || ($sysObjectID eq "catalyst2960G48") || ($sysObjectID eq "catalyst295012") || ($sysObjectID eq "catalyst296024") || ($sysObjectID eq "catalyst2950t24") || ($sysObjectID eq "ciscoAIRAP1210") || ($sysObjectID eq "catalyst2924MXL") ) { # Cisco &cisco_tftp_config; # Unsupported devices } elsif ( ($sysObjectID eq "cevChassisN7Kc7010") || ($sysObjectID eq "cevChassisN5kC5010pBf") || ($sysObjectID eq "ciscoACE4710K9") ) { print "ERROR: $snmphost ~ $sysObjectID is not supported via SNMP skipping...\n"; print SENDMAIL "INFO:$snmphost ~ $sysObjectID is not supported via SNMP skipping...
\n"; } else { print "ERROR: $snmphost ~ $sysObjectID is unknown skipping...\n"; print SENDMAIL "ERROR:$snmphost ~ $sysObjectID is unknown skipping...
\n"; } } #end foreach $snmphost return 1; } #end sub call_tftp_backup ############################################################################ # Subroutine baystack_tftp_config # # Purpose: use SNMP to instruct BayStack switches to TFTP upload their # configuration file to the central TFTP server ############################################################################ sub baystack_tftp_config { # Declare Local Variables my $setresult; my $getresult; # This is a binary file so let's chang the file path my $local_tmp = substr $filename, 2; $filename = "b/".$local_tmp; &check_filename($filename); print "DEBUG: BINARY baystack config **************************\n" if (DEBUG); print "DEBUG: $snmphost filename = $filename\n" if (DEBUG); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['s5AgMyIfLdSvrAddr', 1, "$TFTPSERVER",], ['s5AgMyIfCfgFname', 1, $filename,] ); my $go = new SNMP::VarList( ['s5AgInfoFileAction', 0, 4,] ); # Set TFTP source and destination strings $setresult = $sess->set($vars); if ( $sess->{ErrorStr} ) { print "ERROR: {BayStack} problem setting the TFTP parameters for $snmphost\n"; print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Start TFTP copy $setresult = $sess->set($go); if ( $sess->{ErrorStr} ) { print "ERROR: {BayStack} problem setting the TFTP action bit for $snmphost\n"; print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $getresult = $sess->get('s5AgInfoFileStatus.0'); if ( $sess->{ErrorStr} ) { print "ERROR: problem checking the TFTP result for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Updated December 28, 2010 # The Ethernet Routing Switch 4500 series seems to timeout to SNMP requests immediately # after you initiate the upload command. In order to compensate I've added the additional # code below to test for the "Timeout" and retry the SNMP get command which originally # timed out when first run. I've also increased the PAUSE time from 2 seconds to 4 seconds if ( $sess->{ErrorStr} eq "Timeout" ) { sleep $PAUSE; # Check to see if the TFTP copy completed a second time $getresult = $sess->get('s5AgInfoFileStatus.0'); if ( $sess->{ErrorStr} ) { print "ERROR: problem checking the TFTP result a second time for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } } # If TFTP failed output error message if ($getresult ne "success") { while ($getresult eq "inProgress") { print "DEBUG: config upload status = $getresult (waiting)\n" if (DEBUG); sleep $PAUSE; $getresult = $sess->get('s5AgInfoFileStatus.0'); } #end while } #end if $getresult ne "success" # If the upload command failed let's try again if ($getresult eq "fail") { print "DEBUG: initial command returned $getresult\n" if (DEBUG); print "DEBUG: lets try the upload command again\n" if (DEBUG); # Let's pause here for a few seconds since the previous command failed sleep $PAUSE; # Start TFTP copy $setresult = $sess->set($go); if ( $sess->{ErrorStr} ) { print "ERROR: problem setting the TFTP action bit for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $getresult = $sess->get('s5AgInfoFileStatus.0'); if ( $sess->{ErrorStr} ) { print "ERROR: problem checking the TFTP result for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # If TFTP failed output error message if ($getresult ne "success") { while ($getresult eq "inProgress") { print "DEBUG: config upload status = $getresult (waiting)\n" if (DEBUG); sleep $PAUSE; $getresult = $sess->get('s5AgInfoFileStatus.0'); } #end while } #end if } #end if if ($getresult eq "fail") { print "DEBUG: $snmphost config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } elsif ($getresult eq "success") { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful\n"; } else { print "DEBUG: $snmphost ($sysObjectID) unknown error return = $getresult\n" if (DEBUG); print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *UNKNOWN*
\n"; } #end if print "DEBUG: upload config file results = $getresult\n" if (DEBUG); return 1; } #end sub baystack_tftp_config ############################################################################ # Subroutine passport_tftp_config # # Purpose: use SNMP to instruct Passport 8600 switches to TFTP upload their # configuration file to the central TFTP server ############################################################################ sub passport_tftp_config { my $test; &check_filename($filename); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); #print "DEBUG: we're in the passport tftp sub!\n" if (DEBUG); ### ### The SNMP MIBS below would not work for the Passport 8600 ### I'm not really sure why but I decided to work around the ### problem rather than wasting all my time trying to figure ### out what the problem was/is. The trick was to initiate a ### file copy from /flash/config.cfg to the tftp server. The ### Passport 8600 natively supports copying to/from a tftp ### server (ie. copy 10.103.24.50:/image.bin /flash/image.bin) ### # my $vars = new SNMP::VarList( # ['rcTftpHost', 0, "$TFTPSERVER",], # ['rcTftpFile', 0, $filename,] ); # # my $go = new SNMP::VarList( # ['rcTftpAction', 0, 3,] ); # # # Set TFTP source and destination strings # $test = $sess->set($vars); # if ( $sess->{ErrorStr} ) { # print "DEBUG: after attempting to set the Tftp mibs we bombed!\n"; # print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; # } # # # Start TFTP copy # $test = $sess->set($go); # if ( $sess->{ErrorStr} ) { # print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; # } # # # Pause while the TFTP copy completes # sleep 3; # # # Check to see if the TFTP copy completed # $test = $sess->get('rcTftpResult.0'); # if ( $sess->{ErrorStr} ) { # print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; # } my $vars = new SNMP::VarList( ['rc2kCopyFileSource', 0, "/flash/config.cfg",], ['rc2kCopyFileDestination', 0, "$TFTPSERVER:$filename",] ); my $go = new SNMP::VarList( ['rc2kCopyFileAction', 0, 2,] ); # Set TFTP source and destination strings $test = $sess->set($vars); # Start TFTP copy $test = $sess->set($go); # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $test = $sess->get('rc2kCopyFileResult.0'); # If TFTP failed output error message if ($test ne "success") { while ($test eq "inProgress") { print "DEBUG: config upload status = $test (waiting)\n" if (DEBUG); sleep $PAUSE; $test = $sess->get('s5AgInfoFileStatus.0'); } #end while } #end if if ($test eq "fail") { print "ERROR: $snmphost config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } elsif ($test eq "success") { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful\n"; } #end if print "DEBUG: $snmphost ($sysObjectID) upload config file results = $test\n" if (DEBUG); return 1; } #end sub passport_tftp_config ############################################################################ # Subroutine grab_snmpsystem # # Purpose: use SNMP to identify the type of switch we'll be working with ############################################################################ sub grab_snmpsystem { # Declare Local Variables my @vals; my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['sysDescr', 0], ['sysObjectID', 0], ['sysUpTime', 0], ['sysContact', 0], ['sysName', 0], ['sysLocation', 0] ); print "DEBUG: snmphost = $snmphost and community = $community\n" if (DEBUG); @vals = $sess->get($vars); # retreive SNMP information if ( $sess->{ErrorStr} ) { print "ERROR: retreiving system for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } if ($vals[0] eq "") { print "ERROR: Unable to poll the switch $snmphost. !!!\n"; print SENDMAIL "ERROR:Unable to poll the switch $snmphost. !!!
\n"; return 99; } $sysDescr = $vals[0]; $sysObjectID = $vals[1]; $sysUpTime = $vals[2]; $sysContact = $vals[3]; $sysName = $vals[4]; $sysLocation = $vals[5]; $sysObjectID =~ s/.1.3.6.1.4.1/enterprises/; print "DEBUG: $snmphost sysObjectID=$sysObjectID \n" if (DEBUG); return 1; }; #end sub grab_snmpsystem ######################################## ############################################################################ # Subroutine passport1600_tftp_config # # Purpose: use SNMP to instruct Passport 1600 switches to TFTP upload their # configuration file to the central TFTP server. This sub routine was used # for the ERS 1600 switches which were running 1.x software. When the switch # is running v2.x software is behaves (and responds) to the same SNMP MIBS # as the ERS 8600 switches. # # SNMP OIDs # swL2DevCtrlUpDownloadImageFileName # swL2DevCtrlUpDownloadImageSourceAddr # swL2DevCtrlUpDownloadImage OBJECT-TYPE # other(1), # upload(2), # download(3) # swL2DevCtrlUpDownloadState OBJECT-TYPE # other(1), # in-process(2), # invalid-file(3), # violation(4), # file-not-found(5), # disk-full(6), # complete(7), # time-out(8), # tftp-establish-fail(9) # ############################################################################ sub passport1600_tftp_config { # Declare Local Variables my $test; my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['swL2DevCtrlUpDownloadImageSourceAddr', 0, "$TFTPSERVER",], #['s5AgSysBinaryConfigFilename', 0, $filename,] ); ['swL2DevCtrlUpDownloadImageFileName', 0, $filename,] ); my $go = new SNMP::VarList( ['swL2DevCtrlUpDownloadImage', 0, 2,] ); # Set TFTP source and destination strings $test = $sess->set($vars); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Start TFTP copy $test = $sess->set($go); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Pause while the TFTP copy completes print "DEBUG: Sleeping 3 seconds while TFTP backup occurs... \n" if (DEBUG); sleep $PAUSE; # Check to see if the TFTP copy completed $test = $sess->get('swL2DevCtrlUpDownloadState.0'); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } print "DEBUG: swL2DevCtrlUpDownloadState = $test \n" if (DEBUG); # If TFTP failed output error message if ($test ne "complete") { while ($test eq "in-process") { print "DEBUG: config upload status = $test (waiting)\n" if (DEBUG); sleep $PAUSE; $test = $sess->get('swL2DevCtrlUpDownloadState.0'); print "DEBUG: swL2DevCtrlUpDownloadState = $test \n" if (DEBUG); } }; if (($test ne "complete") & ($test ne "other")) { print "DEBUG: result <> complete and <> other = $test \n" if (DEBUG); print "ERROR: $snmphost config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } elsif ($test eq "complete") { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "$snmphost ($sysObjectID) was successful\n"; } print "DEBUG: $snmphost ($sysObjectID) upload config file results = $test\n" if (DEBUG); return 1; } #end sub passoprt1600_tftp_config ########################################################################### # Subroutine strart_html_report # # Purpose: open handle to SENDMAIL and create HTML message ########################################################################### sub start_html_report { open(SENDMAIL, "| /usr/lib/sendmail $MAILTO") || die; print(SENDMAIL "From: $MAILFROM\nTo: $MAILTO\nSubject: $MAILSUBJECT\n"); print(SENDMAIL "MIME-Version: 1.0\n"); print(SENDMAIL "Content-Type: text/html; charset=us-ascii\n\n"); print SENDMAIL << "EOF"; \

ACME Network Infrastructure Switch Backup Report
\n Date : $sdate
Server: $SERVER

This is an automated message concerning the status of the automated switch configuration backups for all Nortel Ethernet Switch 450/460/470/BPS/4548/5510/5520/5530 and Ethernet Routing Switches 8600 and 1600.\n This message is being generated from the CentOS 5 Linux server running the Perl Script switchtftpbackup.pl from /usr/local/etc.

We are now also backing up the Cisco Catalyst Switches from Riddle Hospital.

Procedures for recovering the switch configurations can be found at this location
\\\\mdcvtech\\Infrastructure\\Documentation\\Restoring an Ethernet Switch Configuration.doc

Perl Script last updated on $LASTUPDATEDDATE \n

The following is a list of the switches and their backup status. \n

EOF return 1; } #end sub start_html_report ############################################################################ # Subroutine finish_html_report # # Purpose: use SNMP to instruct Passport 1600 switches to TFTP upload their # configuration file to the central TFTP server ############################################################################ sub finish_html_report { print SENDMAIL < Notes: this message is now being sent in HTML format.


\n
\n EOF close(SENDMAIL); return 1; } #end sub finish_html_report ############################################################################ # Subroutine hpgbe2_tftp_config # # Purpose: use SNMP to instruct HP GbE2 Switch Blades to TFTP upload their # configuration file to the central TFTP server. This is specifically for # the HP GbE2 (p-Class) interconnects. These SNMP MIB OIDs do not seem to # work for the HP GbE2c (c-Class) interconnects. ############################################################################ sub hpgbe2_tftp_config { # Local Variables my $setresult; &check_filename($filename); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['agTftpServer', 0, "$TFTPSERVER",], ['agTftpCfgFileName', 0, "$filename",] ); my $go = new SNMP::VarList( ['agTftpAction', 0, 4,] ); # Set TFTP source and destination strings my $test = $sess->set($vars); # Start TFTP copy $test = $sess->set($go); # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $test = $sess->get('agTftpLastActionStatus.0'); # If TFTP failed output error message if ($test =~ /Success/) { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful\n"; } else { print "ERROR: $snmphost ($sysObjectID) config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } #end if print "DEBUG: upload config file results = $test\n" if (DEBUG); return 0; } # end sub hpgbe2_tftp_config ############################################################################ # Subroutine hpgbe2c_tftp_config # # Purpose: use SNMP to instruct HP GbE2 Switch Blades to TFTP upload their # configuration file to the central TFTP server. This is specifically for # the HP GbE2 (p-Class) interconnects. These SNMP MIB OIDs do not seem to # work for the HP GbE2c (c-Class) interconnects. ############################################################################ sub hpgbe2c_tftp_config { # Local Variables my $setresult; &check_filename($filename); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['agTransferServer', 0, "$TFTPSERVER",], ['agTransferCfgFileName', 0, "$filename",] ); my $go = new SNMP::VarList( ['agTransferAction', 0, 4,] ); # Set TFTP source and destination strings my $test = $sess->set($vars); # Start TFTP copy $test = $sess->set($go); # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $test = $sess->get('agTransferLastActionStatus.0'); # If TFTP failed output error message if ($test =~ /Success/) { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful\n"; } else { print "ERROR: $snmphost ($sysObjectID) config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } #end if print "DEBUG: upload config file results = $test\n" if (DEBUG); return 0; } # end sub hpgbe2_tftp_config ############################################################################ # Subroutine ws5100_config # # Purpose: copy system:running-config to TFTP server for backup purposes. # This is for the WS5100 v3.x or RFS7000 v1.x or later Motorola (formerly # Symbol) Wireless LAN switches. This code will not work with the WS5000 # or WS5100 running 2.x software. ############################################################################ sub ws5100_config { # Local Variables my $setresult; &check_filename($filename); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['wsInfraFileManageSrc', 0, "system:running-config",], ['wsInfraFileManageDest', 0, "tftp://$TFTPSERVER/$filename",] ); my $go = new SNMP::VarList( ['wsInfraFileManageStart', 0, 1,] ); # Set TFTP source and destination strings my $test = $sess->set($vars); # Start TFTP copy $test = $sess->set($go); # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $test = $sess->get('wsInfraFileManageStatus.0'); # If TFTP failed output error message if ($test =~ /success/) { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful\n"; } else { print "ERROR: $snmphost ($sysObjectID) config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } #end if print "DEBUG: upload config file results = $test\n" if (DEBUG); return 0; } #end sub ws5100_config ############################################################################ # Subroutine cisco_tftp_config # # Purpose: use SNMP to instruct Cisco switches to TFTP upload their running # configuration to the central TFTP server. These SNMP OIDs do not appear # to work with the Cisco Nexus 7010 or 5010 switches. # # snmpset -v1 -c$COMMUNITY $HOST ccCopyProtocol.101 i 1 # snmpset -v1 -c$COMMUNITY $HOST ccCopySourceFileType.101 i 4 # snmpset -v1 -c$COMMUNITY $HOST ccCopyDestFileType.101 i 1 # snmpset -v1 -c$COMMUNITY $HOST ccCopyServerAddress.101 a "10.103.24.250" # snmpset -v1 -c$COMMUNITY $HOST ccCopyFileName.101 s "sw-train1-2900-24-rmh.cfg" # snmpset -v1 -c$COMMUNITY $HOST ccCopyEntryRowStatus.101 i 1 # sleep 5 # snmpget -v1 -c$COMMUNITY $HOST ccCopyState.101 # snmpset -v1 -c$COMMUNITY $HOST ccCopyEntryRowStatus.101 i 6 # ############################################################################ sub cisco_tftp_config { # Local Variables my $test; my $go; my $copyresult; &check_filename($filename); my $snmpsession = rand(100); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['ccCopyProtocol', 101, 1,], ['ccCopySourceFileType', 101, 4,], ['ccCopyDestFileType', 101, 1,], ['ccCopyServerAddress', 101, "$TFTPSERVER",], ['ccCopyFileName', 101, $filename,] ); # ['ccCopyEntryRowStatus', 101, 1,] # ); my $go = new SNMP::VarList( ['ccCopyEntryRowStatus', 101, 1,] ); my $vars1 = new SNMP::VarList( ['ccCopyEntryRowStatus', 101, 6,] ); # Set TFTP source and destination strings print "DEBUG: $snmphost about to set SNMP values\n" if (DEBUG); my $test = $sess->set($vars); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Start TFTP copy $test = $sess->set($go); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Check to see if the TFTP copy completed print "DEBUG: $snmphost checking ccCopyState before pausing\n" if (DEBUG); $copyresult = $sess->get('ccCopyState.101'); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } print "DEBUG: ccCopyState = $copyresult \n" if (DEBUG); # Pause while the TFTP copy completes print "DEBUG: Sleeping 3 seconds while TFTP backup occurs... \n" if (DEBUG); sleep $PAUSE; # Check to see if the TFTP copy completed print "DEBUG: $snmphost checking ccCopyState after pausing\n" if (DEBUG); $copyresult = $sess->get('ccCopyState.101'); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } print "DEBUG: ccCopyState = $copyresult \n" if (DEBUG); # If TFTP failed output error message if ($copyresult ne "successful") { while ($copyresult eq "running") { print "DEBUG: config upload status = $copyresult (waiting)\n" if (DEBUG); sleep $PAUSE; $copyresult = $sess->get('ccCopyState.101'); print "DEBUG: ccCopyState = $copyresult \n" if (DEBUG); } }; if (($copyresult ne "successful") & ($copyresult ne "other")) { print "DEBUG: result <> complete and <> other = $copyresult \n" if (DEBUG); print "ERROR: $snmphost ($sysObjectID) config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config upload *FAILED*!
\n"; } elsif ($copyresult eq "successful") { print SENDMAIL "$snmphost ($sysObjectID) was successful
\n"; print "$snmphost ($sysObjectID) was successful\n"; } # Clean up the SNMP session print "DEBUG: $snmphost destroying SNMP session\n" if (DEBUG); $test = $sess->set($vars1); if ( $sess->{ErrorStr} ) { print "DEBUG: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } print "DEBUG: upload config file results = $copyresult\n" if (DEBUG); return 1; } #end sub cisco_tftp_config ############################################################################ # Subroutine baystack_tftp_config_ascii # # Purpose: use SNMP to instruct BayStack switches to TFTP upload their # ASCII configuration file to the central TFTP server ############################################################################ sub baystack_tftp_config_ascii { #s5AgSysTftpServerAddress #s5AgSysAsciiConfigFilename #s5AgSysAsciiConfigManualUpload (NOT IN THE MIBS) USE 1.3.6.1.4.1.45.1.6.4.4.19.0 # snmpset -v2c -cpublic 10.101.255.254 1.3.6.1.4.1.45.1.6.4.4.19.0 i 4 # # Updated December 23, 2010 - SNMP OID NOW IN MIBS DOES NOT WORK FOR 4500 # s5AgSysAsciiConfigManualUpload OBJECT-TYPE # SYNTAX INTEGER { # passed(1), # inProgress(2), # failed(3), # uploadNow(4), # uploadToUsb(5) # } # Declare Local Variables my $setresult; $filename = &tftp_filename($snmphost); &check_filename($filename); print "DEBUG: ASCII config upload subroutine starting **********\n" if (DEBUG); my $sess = new SNMP::Session ( DestHost => $snmphost, Community => $community, Version => SNMPVER ); my $vars = new SNMP::VarList( ['s5AgSysTftpServerAddress', 0, "$TFTPSERVER",], ['s5AgSysAsciiConfigFilename', 0, $filename,] ); my $go = new SNMP::VarList( ['.1.3.6.1.4.1.45.1.6.4.4.19', 0, 4, 'INTEGER'] ); # Set TFTP source and destination strings $setresult = $sess->set($vars); if ( $sess->{ErrorStr} ) { print "ERROR: {BayStack} problem setting the TFTP parameters (TFTP IP, FILENAME) for $snmphost\n"; print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Start TFTP copy $setresult = $sess->set($go); if ( $sess->{ErrorStr} ) { print "ERROR: {BayStack} problem setting the TFTP action bit for $snmphost\n"; print "ERROR: {BayStack} sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed # s5AgSysAsciiConfigManualUpload.0 = .1.3.6.1.4.1.45.1.6.4.4.19.0 $setresult = $sess->get('s5AgSysAsciiConfigManualUpload.0'); if ( $sess->{ErrorStr} ) { print "ERROR: problem checking the TFTP result for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # If TFTP failed output error message if ($setresult ne "passed") { # passed(1) while ($setresult eq "inProgress") { # inProgress(2) print "DEBUG: config upload status = $setresult (waiting)\n" if (DEBUG); sleep $PAUSE; $setresult = $sess->get('s5AgSysAsciiConfigManualUpload.0'); } #end while } #end if $test ne "success" # If the upload command failed let's try again if ($setresult eq "failed") { # failed(3) print "DEBUG: initial command returned $setresult\n" if (DEBUG); print "DEBUG: lets try the upload command again\n" if (DEBUG); # Let's pause here for a few seconds since the previous command failed sleep $PAUSE; # Start TFTP copy $setresult = $sess->set($go); if ( $sess->{ErrorStr} ) { print "ERROR: problem setting the TFTP action bit for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # Pause while the TFTP copy completes sleep $PAUSE; # Check to see if the TFTP copy completed $setresult = $sess->get('s5AgSysAsciiConfigManualUpload.0'); if ( $sess->{ErrorStr} ) { print "ERROR: problem checking the TFTP result for $snmphost\n"; print "ERROR: sess->{ErrorStr} = $sess->{ErrorStr}\n"; } # If TFTP failed output error message if ($setresult ne "passed") { # passed(1) while ($setresult eq "inProgress") { # inProgress(2) print "DEBUG: config upload status = $setresult (waiting)\n" if (DEBUG); sleep $PAUSE; $setresult = $sess->get('s5AgSysAsciiConfigManualUpload.0'); } #end while } #end if } #end if if ($setresult eq "failed") { # failed(3) print "DEBUG: $snmphost setresult = $setresult\n"; print "DEBUG: $snmphost config upload *FAILED*!\n"; print SENDMAIL "ERROR:$snmphost ($sysObjectID) config (ASCII) upload *FAILED*!
\n"; } elsif ($setresult eq "passed") { # passed(1) print SENDMAIL "$snmphost ($sysObjectID) was successful (ASCII)
\n"; print "DEBUG: $snmphost ($sysObjectID) was successful (ASCII)\n"; } else { print "DEBUG: unknown error return = $setresult (ASCII)" if (DEBUG); } #end if print "DEBUG: upload config file results = $setresult (ASCII)\n" if (DEBUG); return 1; } #end sub baystack_tftp_config_ascii ############################################################################ # Subroutine prune_switches # # Purpose: prune list of switches to those that are support by this script ############################################################################ sub prune_switches { # Declare Local Variables my @list; my $packet = Net::Ping->new('icmp'); # Loop across all the assets/devices foreach $snmphost (@devices) { # Remove CR/LF $snmphost =~ s/\n//g; # Let's ping the asset/device to make sure it responds else remove # it from processing so it doesn't slow down the script. # FUTURE - add additional code to report error to user via email. if (!($packet->ping($snmphost))) { print "DEBUG: host $snmphost fails to respond to ICMP ping\n" if (DEBUG); next; } #end if # Let's add it to the list of assets/devices push(@list, $snmphost); #if (&grab_snmpsystem == 99) { # next; #} #end if #foreach my $sysobject (@sysObjectIDs) { # if ($sysObjectID eq $sysobject) { # push(@list, $snmphost); # #print "DEBUG: $snmphost ($sysObjectID) is supported pushing to list...\n" if ($DEBUG); # next; # } #end if #} #end foreach } #end foreach @devices = @list; return 1; } # end sub load_switches ############################################################################