#!/usr/bin/perl ############################################################################# # # Cisco Configuration Repository v1.0 # # Author: Ermelindo Mauriello (ermmau AT yahoo.it) # (C) 2006 # # All source code, binaries, documentation, information, and other files # contained in this distribution are provided AS IS with NO WARRANTY OF # ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY, AND FITNESS # FOR A PARTICULAR PURPOSE. # # This software is distribuited under the GNU GENERAL PUBLIC LICENSE # The license is available at http://www.gnu.org/copyleft/gpl.html # # Description: # # Manage a repository of routers/switches configurations with versioning # Requires working CVS, TFTPServer and CVS Web interface # Schedule the process into your crontab, at each running, it gets # the routers configuration, compare with the last release of the repository # and sends and e-mail to alert of the changes. # # The config file format is: # ROUTERNAME:TELNET USER:TELNET PASSWORD:ENABLE PASSWORD:RELEASE # # where: # # TELNET USER = * if no login is required (OLD AAA) # ENABLE PASSWORD = * if loginname is level 15 # RELEASE = 12 for IOS 12 # 11 for IOS 11 # 0 for CAT OS switches # 99 if copy is not permitted and have to use show running-config # ############################################################################### ######## NEEDED PERL MODULES ################################### use Net::Telnet::Cisco; use Cvs; use File::Temp qw/ tempfile tempdir /; use File::Copy; use MIME::Lite; use threads; use threads::shared; use Thread::Pool; use Thread::Semaphore; ################################################################ ########################## PARAMETERS ########################## my $backup_host = "IP of the host running CCR and TFTP"; my $backup_path = "SUBDIR INTO FTP ROOT WHERE TO DOWNLOAD CFGS"; my $tftproot = "TFTP ROOT"; my $cvsrepository = "CVS REPOSITORY"; my $cvsurl="URL to WEB interface for CVS"; my $contact="Administrative contact to send e-mails"; my $logopath="FULL PATH TO LOGO IMAGE"; my $logoname="LOGO IMAGE NAME"; my $configfile="FULL PATH TO CONFIG FILE"; my $GREP="/bin/grep"; my $CVSROOT="/var/lib/cvs" ############################################################### sub worker($$$$$) { my $host=shift; my $username=shift; my $password=shift; my $enable=shift; my $release=shift; my $session = Net::Telnet::Cisco->new(Host => $host ); if ($username eq '*' ) { $session->login('', $password); } else { $session->login($username, $password) || die "Unable to access $host with username $username";; } if (!($enable eq '*') && !$session->enable($enable) ) { warn "Can't enable: " . $session->errmsg; next; } if ($release == 12) { unlink ($tftproot.$backup_path.$host,0); open (FH,">".$tftproot.$backup_path.$host) || die "Unable to create tftp file\n"; close(FH); chmod (0222,$tftproot.$backup_path.$host); my @output=$session->cmd("copy running-config tftp:\n$backup_host\n$backup_path/$host\n\n"); } elsif ( $release == 11 ) { unlink ($tftproot.$backup_path.$host,0); open (FH,">".$tftproot.$backup_path.$host) || die "Unable to create tftp file\n"; close(FH); chmod (0222,$tftproot.$backup_path.$host); my @output=$session->cmd("copy running-config tftp:\n\n$backup_host\n$backup_path/$host\n\n"); } elsif ( $release == 99 ) { unlink ($tftproot.$backup_path.$host,0); open (FH,">".$tftproot.$backup_path.$host) || die "Unable to create tftp file\n"; my @output=$session->cmd("terminal length 0"); @output=$session->cmd("show running-config"); print FH @output; close(FH); chmod (0222,$tftproot.$backup_path.$host); } elsif ( $release == 0 ) { unlink ($tftproot.$backup_path.$host,0); open (FH,">".$tftproot.$backup_path.$host) || die "Unable to create tftp file\n"; close(FH); chmod (0222,$tftproot.$backup_path.$host); my @output=$session->cmd("copy config tftp\n$backup_host\n$backup_path/$host\ny\n"); } else { warn "Router Release ".$release." not supported, skipping $host\n"; } } foreach $a ($GREP,$configfile,$logopath,$tftproot,$tftproot.$backup_path) { if ( ! -e $a ) { die "Unable to find $a\nCheck your configuration\n"; } } my %routers; ### Load Config File open (FH,"<$configfile") || die "Unable to open config file\n"; while () { if (/^#/) {next;} if (/^([a-zZ-Z0-9\.]+):(.+):(.+):(.+):([0-9]+)$/) { $routers{$1}{'username'}=$2; $routers{$1}{'password'}=$3; $routers{$1}{'enable'}=$4; $routers{$1}{'release'}=$5; } } close (FH); ### Get the configuration via TFTP my $pool = Thread::Pool->new( { workers => 5, do => \&worker, optimize => 'cpu'} ); foreach $host (keys %routers) { $pool->job( $host, $routers{$host}{'username'}, $routers{$host}{'password'}, $routers{$host}{'enable'}, $routers{$host}{'release'} ); } $pool->shutdown; ### Open CFG repository and mark files for update my $tempdir = tempdir( CLEANUP => 1 ); chdir($tempdir); my @modified; my $cvs=new Cvs ($tempdir,cvsroot=>$CVSROOT) or die $Cvs::ERROR; $cvs->checkout($cvsrepository); foreach $host (keys %routers) { if ( -e $tftproot.$backup_path.$host) { my $new=0; if ( ! -e $tempdir."/".$host) { $new=1; } system ("$GREP -v \"^ntp clock-period\" $tftproot$backup_path$host | $GREP -v \"^#time:\" > $tempdir/$host\n"); my $status = $cvs->status($host); if($status->is_modified()) { print "File $host modified\n"; push @modified,$host; } undef $status; } } ### Update repository foreach $host (@modified){ print "Commit $host\n"; $cvs->commit($host); } $cvs->release(); $cvs->logout(); File::Temp::cleanup(); ### Send message with changes my $msg="
 
Cisco Configuration Repository has detected a configuration change for the following hosts"; if ($#modified>=0) { foreach $host (@modified){ $msg.="
  • $host"; } $msg.="
    You can check changes on the CVS repository using the lynk provided.
    "; my $mime_msg = MIME::Lite->new( From => "cisco-configuration-repository\@nagios", To => $contact, Subject => "Cisco Configuration Repository - Configuration changed warning", Type => 'multipart/mixed' ); $mime_msg->attach ( Type => "text/html", Data => $msg) ; $mime_msg->attach ( Type => 'image/gif', Path => $logopath, Filename => $logoname, Disposition => 'inline' ); $mime_msg->send(); }