Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

(code)) Cisco Pass Mass - IOS (deprecated by node 123464)

by ybiC (Prior)
on Sep 15, 2000 at 16:46 UTC ( [id://32661]=sourcecode: print w/replies, xml ) Need Help??
Category: Networking Code
Author/Contact Info ybiC
Description: ## deprecated by "(code) Poor Man's TACACS - automate CatOS and IOS password resets with Net::Telnet::Cisco and Net::SNMP" ##

Automate mass config changes (password updates, etc.) for large number of Cisco routers &/or switches.

Comments or critiques are very much welcomed.

Update: Feburary 26, 2001
Rearranged using functions for easier maintenance and greater legibility.
Added function that prompts for devices type (CatOS vs. IOS).

Update: October 27-29, 2000
Added:
    Term::Readky for no-echo pw prompts + confirm of new.
    Tie::IxHash for ordered hashes to update passwords.
    Localtime timestamp to $logfile for each device.
    Console preview of target devices list.
    Too many more ToDos.
Moved:
    Regex's into first tmp loop, to eliminate 2nd $tempfile.
    Console+log prints to subs HEREdocs (improve legibility).

Update: Thanks to ar0n, chromatic, merlyn, Fastolfe, tilly and Joshua Keroes for suggestions and examples that, hopefully, steered me away from Cargo Cult Code and Running With Scissors.   {grin}

#!/usr/bin/perl -w

# ciscoconf.pl
# pod at tail

use strict;
use Net::Telnet::Cisco;
use Term::ReadKey;
use Tie::IxHash;
use Time::localtime;
use vars qw(
    $target   @targets
    $timeout  @timeout
    $oldpass  $newpass $confpass
    $olden    $newen   $confen
    $command  @commands
    $setpass  @setpass
    $writemem @writemem
    $section  $yn
    $devtype
    );

my $conf    = 'ciscoconf.conf';
my $tmp     = 'ciscoconf.tmp';
my $log     = 'ciscoconf.log';
my $errmode = 'return';                  # so bad command won't kill s
+cript
my $timeout = '30';                      # default is none in $conf
my @regex   = (                          # strip these strings from $l
+og
    '^Enter configuration commands, one per line.*$',
    '^\s+$',                             # blank lines. Should be last
+ regex line
    );

&CONFEXT();
&WARNTEXT();
&STARTCLEAN();
&OLDPASSLIST();
&NEWPASSLIST();
&CATORIOS();
&STARTTEXT();


print "  ------------------------------------------\n\n";
print "  The following devices have been configured:\n";
print "    (listed by system prompt)\n";



umask oct 177;             # tempfile owner-only readable. Like "chmod
+ 0600", but no race condition.
for $target (@targets) {
    if (my $cs=Net::Telnet::Cisco->new(
        host    => $target,    errmode   => $errmode,
        timeout => $timeout,   input_log => $tmp,
            )
        )
        {$cs->login('',$oldpass);                   # vty login
        print "      ", $cs->last_prompt;           # show progress w/
+device prompt to console
        if ($cs->enable($olden)) {                  # privilaged mode 
+for config changes
            print " ", $cs->last_prompt;            # show progress w/
+device prompt to console
            foreach $setpass (@setpass) {           # iterate through 
+internal set pass commands
                my @output = $cs -> cmd($setpass);
                }
            foreach $command (@commands) {          # iterate through 
+external config commands
                my @output = $cs -> cmd($command);
                }
            } else {                                # privilaged mode 
+failed
            warn "  Error connecting.  Did you enter a bad password?\n
+";    #. $cs->errmsg;
            }
            print " ", $cs -> last_prompt, "\n";    # show progress w/
+device prompt to console
            $cs->close;                             # exit session
            &LOGREGX();
            }
        else {                                      # telnet connectio
+n failed
            warn "  Error connecting.  Is the device reachable?\n";
            }
    }


&ENDCLEAN();
&ENDTEXT();


######################################################################
+###
sub WARNTEXT {
    print <<EOF;

  = ciscoconf.pl =

  Automate configuration changes for
  multiple Cisco routers and/or switches.

  This program will do *exactly* what you tell it to do.
  If your config entries are horky, then your configs will get horked!

  YOU HAVE BEEN WARNED!

  Ctrl+C at any time to abort,
EOF
    &CONTINUE();
    }
######################################################################
+###
sub OLDPASSLIST {
    print <<EOF;
  ------------------------------------------

  Prompting for existing passwords
  (*not* echoed to screen nor written to disk)

EOF
    tie my %prompts, "Tie::IxHash";
    %prompts = (
        'Enter existing password:'        => 'oldpass',
        'Enter existing enable password:' => 'olden',
        );
    tie my %passwds, "Tie::IxHash";
    for my $prompt (keys %prompts) {
        print "    $prompt ";
        ReadMode('noecho');                   # don't echo to screen.
        chomp(my $input = <STDIN>);
        $passwds{$prompts{$prompt}} = $input;
        ReadMode(0);                          # re-activate screen ech
+o.
        print "\n";
        }
    print "\n";
    $oldpass  = ($passwds{"oldpass"});
    $olden    = ($passwds{"olden"});
    }
######################################################################
+###
sub NEWPASSLIST {
    print "  Change passwords? (y or n) ";
    $yn = 'n';
    chomp($yn = <STDIN>);
    if ($yn eq ('y' or 'Y' or 'yes' or 'YES')) {  # only do this if ch
+anging passwords
        print "  Prompting for new passwords\n";
        print "  (*not* echoed to screen nor written to disk)\n";
        tie my %newprompts, "Tie::IxHash";
        %newprompts = (
            'Enter new password:'             => 'newpass',
            '    Retype password to confirm:' => 'confpass',
            'Enter new enable password:'      => 'newen',
            '    Retype enable to confirm:'   => 'confen',
            );
        tie my %newpasswds, "Tie::IxHash";
        for my $newprompt(keys %newprompts) {
            print "    $newprompt ";
            ReadMode('noecho');                   # don't echo passwor
+d to screen.
            chomp(my $input = <STDIN>);
            $newpasswds{$newprompts{$newprompt}} = $input;
            ReadMode(0);                          # re-activate screen
+ echo.
            print "\n";
            }
        print "\n";
        $newpass  = ($newpasswds{"newpass"});
        $confpass = ($newpasswds{"confpass"});
        $newen    = ($newpasswds{"newen"});
        $confen   = ($newpasswds{"confen"});
        unless (                                  # Confirm new passwo
+rds re-entered identically
            ("$newpass" eq "$confpass")
            and ("$newen" eq "$confen")) {&MISMATCHTEXT();}
        }
    }
######################################################################
+###
sub CATORIOS {
    print "\n  Hit \"c\" if CatOS devices or \"i\" if IOS devices: ";
    chomp(my $devtype = <STDIN>);
    if ($devtype eq ('c'|'C')) {
        $devtype = 'CatOS';
        &SETPASSLISTCATOS();
        } else {
        if ($devtype eq ('i'|'I')) {
            $devtype = 'IOS';
            &SETPASSLISTIOS();
            } else {
                print "    D'ot!  Only two choices - \"i\" or \"c\".  
+Try again...";
                &CATORIOS();
                }
            }
    print "    You selected $devtype.\n\n";
    &CONTINUE();
    }
######################################################################
+###
sub SETPASSLISTIOS {
    @setpass = (
        "conf t",
        "enable pass $newen",
        "line con 0",
        "pass $newpass",

        "login",
        "line aux 0",
        "pass $newpass",
        "login",
        "line vty 0 4",
        "pass $newpass",
        "login",
        "line vty 5 9",
        "pass $newpass",
        "login",
        "line vty 10 15",
        "pass $newpass",
        "login",
        "end",
        "write mem",
        );
    }
######################################################################
+##
sub SETPASSLISTCATOS {
    @setpass = (
        "set enablepass $newen",
        "set pass $newpass",
        );
    }
######################################################################
+##
sub MISMATCHTEXT {
    print "  D'oh!  Password confirmation(s) didn't match!\n";
    print "  Run this program again if you want, and try not to fatfin
+ger it next time!\n\n";
    exit;        # "exit" instead of "die" so no error to console.
    }
######################################################################
+###
sub CONFEXT {
    open (CONF, "<$conf")  or die "  ciscoconf.pl: Error opening $conf
+ for read: $!";
    while (<CONF>) {
        next if /^#/ or /^\s.*$/ or /^!/;  # ignore comment lines
        chomp;
        if (/^\[([^\]]+)\]/) {
            $section = $1;
            next;
            }
        else {
            if ($section =~ /timeout/i) {
                push @timeout,$_;
                }
            if ($section =~ /targets/i) {
                push @targets,$_;
                }
            if ($section =~ /commands/i) {
                push @commands,$_;
                }
            }
        }
    close (CONF)  or die "  ciscoconf.pl: Error closing $conf after re
+ad: $!";
    }
######################################################################
+###
sub STARTCLEAN {                       # so we're not appending to exi
+sting file(s)
    if (-e $tmp and -f _) {
        print "  Unlinking $tmp from prior (aborted) program run.\n";
        &CONTINUE();
        unlink ($tmp) or die "  ciscoconf.pl: Error unlinking $tmp: $!
+"
        }
    if (-e $log and -f _) {
        print "  Unlinking $log from prior program run.\n";
        &CONTINUE();
        unlink ($log) or die "  ciscoconf.pl: Error unlinking $log: $!
+"
        }
    }
######################################################################
+###
sub STARTTEXT {
    print "\n  These devices are targeted for configuration:\n";
    print "    (listed by DNS name or IP address)\n";
    foreach $target (@targets) {
        print "      $target\n"
        }
    &CONTINUE();
    print "\n  These commands will be sent to above devices:\n";
    print "    (Password changes not shown)\n";
    foreach $command (@commands) {
        print "      $command\n"
        }
    print "\n  This is your LAST CHANCE to abort cleanly.\n";
    &CONTINUE();
    }
######################################################################
+###
sub ENDCLEAN {
    if (-e $tmp and -f _) {
        unlink ($tmp) or die "  ciscoconf.pl: Error unlinking $tmp: $!
+"
        }
    }
######################################################################
+###
sub ENDTEXT {
    print "\n  Finished updating device configs.\n";
    print "  Review $log to confirm your commands did what you intende
+d.\n\n";
    }
######################################################################
+###
sub CONTINUE {
    print "  <enter> to continue...\n";
    my $continue = <STDIN>;
    }
######################################################################
+###
sub PCTIME {
    printf LOG "  PC localtime is %d:%d:%d  %d-%d-%d\n",
        localtime -> hour(),
        localtime -> min(),
        localtime -> sec(),
        localtime -> mon()+1,
        localtime -> mday(),
        localtime -> year()+1900,
        ;
    }
######################################################################
+###
sub LOGREGX {
    umask oct 133;                                        # logfile wo
+rld-readable.  Like "chmod 644"
    open (TMP, "<$tmp")    or die "  ciscoconf.pl: Error opening $tmp 
+for read: $!";
    open (LOG, ">> $log")  or die "  ciscoconf.pl: Error opening $log 
+for append: $!";
        &PCTIME();
        print LOG "##########################################\n";
        while (<TMP>) {
            foreach my $regex(@regex) {
                s/$regex//g;
                }
            if ($yn eq ('y' or 'Y' or 'yes' or 'YES')) {  # only do th
+is if changing passwords
                s/($newpass|$newen)/*****/g;              # x-over pas
+swords in $log
                }
            print LOG $_;
            }
        print LOG "\n##########################################\n";
        &PCTIME();
        close (TMP)  or die "  ciscoconf.pl: Error closing $tmp after 
+read: $!";
        close (LOG)  or die "  ciscoconf.pl: Error closing $log after 
+append: $!";
    }
######################################################################
+###

=head1 name

 ciscoconf.pl

=head1 Updated

 February 26, 2001
 September 14, 2000

=head1 Summary

Automate mass config changes (password updates, etc.) for any
number of Cisco routers or switches.  Accepts no command-line switches
+.
Target devices, command list, and comm timeout specified in $conf exte
+rnal
config file.  Presents user with list of target devices and commands, 
+before
proceeding.  Records all sessions to common log file, with password up
+dates
stripped out.  Comments or suggestions for improvement are very much w
+elcomed.

=head1 Usage

 ciscoconf.pl<enter>

=head1 Tested

 with:    Perl 5.00503   Debian 2.2 Espy
 against: Cisco 2514    - IOS 12.0(8)
          Catalyst 2924 - IOS 11.2(8)SA5
 not yet: Catalyst 3500, 2948g, 5000, 6000

=head1 Troubleshooting

 use Data::Dumper;
 open (DUMP, "> ciscoconfext.dump")
     or die "  ciscoconf.pl: Error opening ciscoconfext.dump for write
+: $!";
 print DUMP Dumper($timeout), "\n";
 print DUMP Dumper(@targets), "\n";
 print DUMP Dumper(@commands), "\n";
 close (DUMP)
     or die "  ciscoconf.pl: Error closing ciscoconfext.dump after wri
+te: $!";
 &CONTINUE();

=head1 Todos

 Verbify function names for greater legibility
 Add "waitfor()" to handler interactive password prompt of CatOS
   from Network Programming with Perl p151
 Do "set leng 0" vs. "term leng 0" vi &CATORIOS() instead of in $conf
 Replace "roll-your-own" [ar0n] parser with AppConfig CPAN module  [Bo
+redByPolitics]
 Exclude "end, write mem, copy run start, disa" from $conf parsing
   as they are included in this program
 Cwd to set directory for $conf, $tmp, $log files  [merlyn]
 File::Temp for, um... tempfile                    [tilly]
 Flock temp file (until File::Temp)
 Parse log for errors:
   Failed, error, timeout, expired, Invalid input, Cannot find communi
+ty
 Timeout if wait too long for keyboard input
 Disable kb entry whil proggy running (except for CTRL+C to halt)

=head1 Author

[ybiC]

=head1 Credits

 Examples and suggestions from the following helped a bunch:
 Joshua Keroes, [ar0n], [chromatic], [merlyn],
 [Fastolfe], [tilly], [mirod], and [BoredByPolitics].
 Oh yeah, and some guy named [vroom].
 Although they may not want that to be public knowledge   ;^)

 www.perlmonks.org
 search.cpan.org/search?dist=Net-Telnet-Cisco
 search.cpan.org/search?dist=TermReadKey
 search.cpan.org/search?dist=Tie-IxHash

=head1 Example $conf

 !! Program prompts user for $newpass $newen $newRO !!
 !! Comment lines start with "!" or "#" or " " !!
 !! Everything else except for [Sections] targets and timeout !!
 !!    will be sent directly to router/switch as a command. !!

 ! 
[Timeout]
!! Seconds to wait for device response !!
!! Set short as practical for quickest program run !!
!! ~20 for LAN device, more for WAN device !!
!! Low-end routers may take 15-20 seconds to "write mem"
!!   not counting communications latency
!! Default of 30 seconds if none specified here.
20

 ! 
[Targets]
!! one device name/address per line !!
!! fqdn !!
!! unqualified_device_name !!
!! device_IP_address !!
10.0.0.1
10.254.254.254

 ! 
[Commands]
sho clo
!! CatOS !!
set leng 0
!! IOS !!
term leng 0
conf t
no service password-encryption
logging buffered errors
logging console errors
logging console
! logging trap errors
! logging syslog_server
! ip name-server dns_server_address
! ip name-server another_dns_server_address
! ip domain-name domain_name
no snmp-server community private
no snmp-server community secret
!snmp-server host some_host contact some_body new_RO
! ntp server ntp_server
clock summer-time CST recurring
no service udp-small-servers
no service tcp-small-servers
no banner login
!! start banner with ^ and end with ^C !!
banner login ^login banner^C
no banner motd
banner motd ^motd banner^C
!! Do Not UnComment The Last Four Lines !!
!! They Are Included In The Program Itself !!
!! Run Here You Fall Down Go Boom !!
!! end !!
!! write mem !!
!! disa !!
!! sho clo !!

=cut
Replies are listed 'Best First'.
Re: Cisco Pass Mass (IOS)
by mr_mischief (Monsignor) on Oct 24, 2001 at 01:17 UTC
    This is a handy project. Another monk is working on a project to do similar things with Cisco using SNMP management. His project, called Pancho is relased on Freshmeat and is rated pretty well. I know it works, because we've used it for mass router config updates before. Perhaps you two would have some insights to share which could be helpful to both of you.

    MrM

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://32661]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (5)
As of 2024-03-28 16:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found