From a505626101e262be2cd5a8c74c44d3616c299519 Mon Sep 17 00:00:00 2001 From: Tar Committer Date: Wed, 23 Aug 2000 20:13:31 +0000 Subject: Imported from rancid-1.5.tar.gz. --- CHANGES | 58 +++ README | 48 ++- Todo | 17 +- bin/cat5rancid | 996 ++++++++++++++++++++++++++++++++++++++++++++++++++ bin/clogin | 118 ++++-- bin/control_rancid | 29 +- bin/do-diffs | 19 +- bin/env | 5 + bin/flogin | 4 +- bin/francid | 9 +- bin/jlogin | 192 ++++++---- bin/jrancid | 104 +++++- bin/rancid | 116 ++++-- bin/rancid-fe | 2 + cloginrc.sample | 34 ++ util/README | 6 + util/rtrfilter | 145 ++++++++ util/rtrfilter.README | 14 + 18 files changed, 1738 insertions(+), 178 deletions(-) create mode 100755 bin/cat5rancid create mode 100644 util/README create mode 100755 util/rtrfilter create mode 100644 util/rtrfilter.README diff --git a/CHANGES b/CHANGES index abe3961..9721d52 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,61 @@ +1.5 + sync command-line option between clogin / jlogin. jlogin modified: + -x passphrase -> -r passphrase + -> -x command-file + -e encrypt type -> -y ssh_chyper_type + + add .cloginrc noenable directive to set the cmd-line -noenable + option. + + add .cloginrc userprompt, passprompt, and enableprompt directives + to adjust expected cisco router prompts in clogin. + + see README for info on web interface to rancid CVS repository. + + Try to detect hung rancid jobs & send email. + + Add (partial) support for the cat5s. Still need more work + on the show output, but it does grab the config. + + Skip the juniper's kernel version, memory, and fsck output + of "show system boot-messages" - fsck output changes every + time you boot and the version & memory is better found + elsewhere. + + Delete the cisco ospf authentication and ftp passwords. + + Get both (juniper) show chassis ssb and scb but only process + them once. + + Get info from show diag for 2600s as well. + + Catch juniper "command is not valid on the olive" errors. + + Catch juniper config mismatch between versions of JUNOS. + + Catch close on EOF in clogin/jlogin/flogin. + + Don't expand RCS keywords in config files. + + Catch the case were rancid gets and error and leaves an + empty new config file. + + Make the time to elapse before complaining about unreachable + routers be configurable instead of fixed at 24 hrs. The + default is now 4 hours. + + Add -x passphrase to jlogin. + + You can now set a ssh identity file in .cloginrc for use + with jlogin. + + Handle more errors in jrancid. Also handle changes for + JUNOS 4.0 and collect "show system boot-messages" output. + + rancid now recognizes cisco 12016s and more types of 7200s. + It also looks for WARNING messages in show version. + And sort ip explicit-paths. + 1.4 sort cisco route-maps diff --git a/README b/README index e595c2c..d273776 100644 --- a/README +++ b/README @@ -4,17 +4,20 @@ maintain CVS controlled copies of router configs. The following is the packing list for Rancid: README - This file. -env - Enviromental settings needed. +CHANGES - List of changes to Rancid. +Todo - partial Todo list of what needs to be done. +env - Enviromental settings needed. do-diffs - Script designed to be run from cron calling control_rancid. control_rancid - Builds router list, calls rancid on each router and - handles cvs routines. -rancid-fe - chooses between rancid/francid/jrancid/rrancid. + handles cvs routines. +rancid-fe - chooses between rancid/francid/jrancid/rrancid/cat5rancid. rancid - Runs commands on cisco routers and processes the output - the meat. francid - Runs commands on foundry switches and processes the output. jrancid - Runs commands on juniper routers and processes the output. rrancid - Runs commands on redback routers and processes the output. +cat5rancid -Runs commands on cisco cat5 switches and processes the output. clogin - Expect script that logs into routers either interactively, - runs a set of commands or runs another expect script. + runs a set of commands or runs another expect script. flogin - Expect script that logs into foundry switches. once foundry cleans up their bloody UI, clogin should do the job. jlogin - Expect script that logs into juniper routers similarly to @@ -22,17 +25,27 @@ jlogin - Expect script that logs into juniper routers similarly to -c and -s options. .cloginrc - TCL commands to set passwords, usernames etc. used by clogin and jlogin. -par - Parallel processing of commands - any commands. +par - Parallel processing of commands - any commands. +rename - Perl script to rename files. +create_cvs -Creates all of the CVS and config directories. +util - utilities / contribs rancid will also need to have the following packages: cvs - code revision system available from prep.ai.mit.edu:/pub/gnu gnudiff - gnudiff provides the -u option. if you do not have gnudiff, - you will have to modify control_rancid to use 'diff -c' or some - such. see examples. + you will have to modify control_rancid to use 'diff -c' or some + such. see examples. perl5 - perl version 5 or greater available from www.cpan.org expect - http://expect.nist.gov/ tcl - required by expect. +Bill Fenner has a cgi script for interacting with CVS repositories via +a web interface. This provides a great way to view rancid diffs and +full configs, especially for those unfamiliar with cvs. The package is +not included here, but can be found here: + + http://www.freebsd.org/~fenner/cvsweb/cvsweb-1.0.tar.gz + Quick Installation Guide (an example): 1) mkdir @@ -40,11 +53,14 @@ Quick Installation Guide (an example): 2) mkdir /bin -3) Put the contents of rancid/bin in /bin. modify the location of - perl and expect in each of clogin, par, rancid, rancid-fe, jlogin, - jrancid, and rename if necessary. Make sure that you are using perl - version 5 and not perl version 4. There are also 3 calls to perl in - control_rancid. +3) Put the contents of rancid/bin in /bin. + + Modify the location of perl and expect in each of cat5rancid, + clogin, flogin, francid, jlogin, jrancid, par, rancid, rancid-fe, + rename, and rrancid if necessary. There are also 3 calls to + perl in control_rancid and one in do-diffs. + + Make sure that you are using perl version 5 and not perl version 4. 4) Modify /bin/env. @@ -80,10 +96,10 @@ Quick Installation Guide (an example): 9) For each "group", modify the router.db file in the group directory. The file is of the form "router:mfg:state" where "router" is - the name (we use FQDN) of the router, mfg is the manufacturer from the - set of (cisco|foundry|juniper|redback), and "state" is either up or - down. Each router listed as "up" will have the configuration - grabbed. + the name (we use FQDN) of the router, mfg is the manufacturer + from the set of (cisco|foundry|juniper|redback|cat5), and "state" + is either up or down. Each router listed as "up" will have the + configuration grabbed. 10) Put do-diffs in cron to be called however often you want it to run for each group (do-diffs []). eg: diff --git a/Todo b/Todo index 72d8547..4afb294 100644 --- a/Todo +++ b/Todo @@ -1,8 +1,23 @@ - detect 'same' vty configs - ignoring length/width/passwd is a start, but need more - add .cloginrc var to say to use ssh or telnet +- merge clogin and jlogin into one. possible? - rancid needs to treat the 3600s like the 7Ks and 12Ks... - Also, need to allow 12012s, and force 700s to not be treated like 7Ks. -- change rancid to work on a file - flogin (for foundry) needs more testing and should be integrated with clogin when foundry fixes their UI. +- check clogin. does it handle multiple addrs for 1 fqdn correctly? + > guelah [444] clogin gw6.partan.com + > gw6.partan.com + > spawn telnet gw6.partan.com + > Trying 198.6.255.57... + > telnet: connect to address 198.6.255.57: Host is unreachable + > telnet: connect to address 198.6.255.61: Host is unreachable + > telnet: connect to address 198.6.255.65: Host is unreachable + > Connected to gw6.partan.com (198.6.255.194). + > Escape character is '^]'. + > + > + > User Access Verification + > + > Username: ^Cguelah [445] diff --git a/bin/cat5rancid b/bin/cat5rancid new file mode 100755 index 0000000..0055cc8 --- /dev/null +++ b/bin/cat5rancid @@ -0,0 +1,996 @@ +#!/usr/local/bin/perl +## +## +## Copyright (C) 1997 by Henry Kilmer. +## All rights reserved. +## +## This software may be freely copied, modified and redistributed without +## fee for non-commerical purposes provided that this copyright notice is +## preserved intact on all copies and modified copies. +## +## There is no warranty or other guarantee of fitness of this software. +## It is provided solely "as is". The author(s) disclaim(s) all +## responsibility and liability with respect to this software's usage +## or its effect upon hardware, computer systems, other software, or +## anything else. +## +## +# +# RANCID - Really Awesome New Cisco confIg Differ +# +# usage: rancid [-d] [-l] [-f filename | $host] +# +use Getopt::Std; +getopts('dflm'); +$log = $opt_l; +$debug = $opt_d; +$file = $opt_f; +$host = $ARGV[0]; +$clean_run = 0; +$found_end = 0; +$timeo = 90; # clogin timeout in seconds + +# This routine is used to print out the router configuration +sub ProcessHistory { + my($new_hist_tag,$new_command,$command_string,@string)=(@_); + if((($new_hist_tag ne $hist_tag) || ($new_command ne $command)) + && defined %history) { + print eval "$command \%history"; + undef %history; + } + if (($new_hist_tag) && ($new_command) && ($command_string)) { + if ($history{$command_string}) { + $history{$command_string} = "$history{$command_string}@string"; + } else { + $history{$command_string} = "@string"; + } + } elsif (($new_hist_tag) && ($new_command)) { + $history{++$#history} = "@string"; + } else { + print "@string"; + } + $hist_tag = $new_hist_tag; + $command = $new_command; + 1; +} + +sub numerically { $a <=> $b; } + +# This is a sort routing that will sort numerically on the +# keys of a hash as if it were a normal array. +sub keynsort { + local(%lines)=@_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort numerically keys(%lines)) { + $sorted_lines[$i] = $lines{$key}; + $i++; + } + @sorted_lines; +} + +# This is a sort routing that will sort on the +# keys of a hash as if it were a normal array. +sub keysort { + local(%lines)=@_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort keys(%lines)) { + $sorted_lines[$i] = $lines{$key}; + $i++; + } + @sorted_lines; +} + +# This is a sort routing that will sort on the +# values of a hash as if it were a normal array. +sub valsort{ + local(%lines)=@_; + local($i) = 0; + local(@sorted_lines); + foreach $key (sort values %lines) { + $sorted_lines[$i] = $key; + $i++; + } + @sorted_lines; +} + +# This is a numerical sort routing (ascending). +sub numsort { + local(%lines)=@_; + local($i) = 0; + local(@sorted_lines); + foreach $num (sort {$a <=> $b} keys %lines) { + $sorted_lines[$i] = $lines{$num}; + $i++; + } + @sorted_lines; +} + +# This is a sort routine that will sort on the +# ip address when the ip address is anywhere in +# the strings. +sub ipsort { + local(%lines)=@_; + local($i) = 0; + local(@sorted_lines); + foreach $addr (sort sortbyipaddr keys %lines) { + $sorted_lines[$i] = $lines{$addr}; + $i++; + } + @sorted_lines; +} + +# These two routines will sort based upon IP addresses +sub ipaddrval { + my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)$#); + $a[3]+256*($a[2]+256*($a[1]+256*$a[0])); +} +sub sortbyipaddr { + &ipaddrval($a) <=> &ipaddrval($b); +} + +# This routine parses "show version" +sub ShowVersion { + print STDERR " In ShowVersion: $_" if ($debug); + + while () { + tr/\015//d; + last if(/^$prompt/); + next if(/^(\s*|\s*$cmd\s*)$/); + if (/^Slave in slot (\d+) is running/) { + $slave = " Slave:"; + next; + } + /^IOS .* Software \(([A-Za-z-0-9]*)\), .*Version\s+(.*)$/ && + ProcessHistory("COMMENTS","keysort","F1", + "!Image:$slave Software: $1, $2\n") && next; + /^([A-Za-z-0-9_]*) Synced to mainline version: (.*)$/ && + ProcessHistory("COMMENTS","keysort","F2", + "!Image:$slave $1 Synced to mainline version: $2\n") && next; + /^Compiled (.*)$/ && + ProcessHistory("COMMENTS","keysort","F3", + "!Image:$slave Compiled: $1\n") && next; + /^ROM: (System )?Bootstrap.*(Version.*)$/ && + ProcessHistory("COMMENTS","keysort","G1", + "!ROM Bootstrap: $2\n") && next; + /^ROM: \d+ Bootstrap .*(Version.*)$/ && + ProcessHistory("COMMENTS","keysort","G2", + "!ROM Image: Bootstrap $1\n!\n") && next; + /^ROM: .*(Version.*)$/ && + ProcessHistory("COMMENTS","keysort","G3","!ROM Image: $1\n") && next; + /^BOOTFLASH: .*(Version.*)$/ && + ProcessHistory("COMMENTS","keysort","G4","!BOOTFLASH: $1\n") && next; + /^System image file is "([^\"]*)", booted via (\S*)/ && +# removed the booted source due to +# CSCdk28131: cycling info in 'sh ver' +# ProcessHistory("COMMENTS","keysort","F4","!Image: booted via $2, $1\n") && + ProcessHistory("COMMENTS","keysort","F4","!Image: booted $1\n") && + next; + /^System image file is "([^\"]*)"$/ && + ProcessHistory("COMMENTS","keysort","F5","!Image: $1\n") && next; + if (/(\S+)\s+\((\S+)\)\s+processor.*with (\S+K) bytes/) { + my($proc) = $1; + my($cpu) = $2; + my($mem) = $3; + if ( $1 eq "CSC") { + $type = "AGS"; + } elsif ( $1 eq "CSC4") { + $type = "AGS+"; + } elsif ( $1 eq "2511" || $1 eq "2524" || $1 eq "AS2511-RJ") { + $type = "2500"; + } elsif ( $1 eq "3620" || $1 eq "3640") { + $type = "3600"; + } elsif ( $1 eq "RSP7000") { + $type = "7500"; + } elsif ( $1 =~ /RSP\d/) { + $type = "7500"; + } elsif ( $1 eq "RP1") { + $type = "7000"; + } elsif ( $1 eq "RP") { + $type = "7000"; + } elsif ( $1 =~ /720[246]/) { + $type = "7200"; + } elsif ($1 =~ /1200[48]\/GRP/ || $1 =~ /1201[26]\/GRP/) { + $type = "12000"; + } else { + $type = $1; + } + print STDERR "TYPE = $type\n" if ($debug); + ProcessHistory("COMMENTS","keysort","A1", + "!Chassis type:$slave $proc - a $type router\n"); + ProcessHistory("COMMENTS","keysort","B1", + "!Memory:$slave main $mem\n"); + ProcessHistory("COMMENTS","keysort","A3","!CPU:$slave $cpu\n"); + next; + } + if (/(\S+) Silicon\s*Switch Processor/) { + if (!defined($C0)) { + $C0=1; ProcessHistory("COMMENTS","keysort","C0","!\n"); + } + ProcessHistory("COMMENTS","keysort","C2","!SSP: $1\n"); + $ssp = 1; + $sspmem = $1; + next; + } + /^(\d+K) bytes of multibus/ && + ProcessHistory("COMMENTS","keysort","B2", + "!Memory: multibus $1\n") && next; + /^(\d+K) bytes of non-volatile/ && + ProcessHistory("COMMENTS","keysort","B3", + "!Memory: nvram $1\n") && next; + /^(\d+K) bytes of flash memory/ && + ProcessHistory("COMMENTS","keysort","B5","!Memory: flash $1\n") && + next; + /^(\d+K) bytes of .*flash partition/ && + ProcessHistory("COMMENTS","keysort","B6", + "!Memory: flash partition $1\n") && next; + /^(\d+K) bytes of Flash internal/ && + ProcessHistory("COMMENTS","keysort","B4", + "!Memory: bootflash $1\n") && next; + if(/^(\d+K) bytes of (Flash|ATA)?.*PCMCIA .*slot ?(\d)/i) { + ProcessHistory("COMMENTS","keysort","B7", + "!Memory: pcmcia $2 slot$3 $1\n"); + next; + } + if(/^WARNING/) { + if (!defined($I0)) { + $I0=1; + ProcessHistory("COMMENTS","keysort","I0","!\n"); + } + ProcessHistory("COMMENTS","keysort","I1","! $_"); + # The line after the WARNING is what to do about it. + $_ = ; tr/\015//d; + ProcessHistory("COMMENTS","keysort","I1","! $_"); + } + if (/^Configuration register is (.*)$/) { + $config_register=$1; + next; + } + } + return(0); +} + +# This routine parses "show install active" +sub ShowInstallActive { + print STDERR " In ShowInstallActive: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if /^\s*\^\s*$/; + return(1) if /Invalid input detected/; + return(1) if /Unknown command/; + ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next; + } + return(0); +} + +# This routine parses "show env all" +sub ShowEnv { + # Skip if this is not a 7500, 7200, or 7000. + print STDERR " In ShowEnv: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^7/); + if (!defined($E0)) { + $E0=1; + ProcessHistory("COMMENTS","keysort","E0","!\n"); + } + if (/^Arbiter type (\d), backplane type (\S+)/) { + if (!defined($C0)) { + $C0=1; ProcessHistory("COMMENTS","keysort","C0","!\n"); + } + ProcessHistory("COMMENTS","keysort","C1", + "!Enviromental Arbiter Type: $1\n"); + ProcessHistory("COMMENTS","keysort","A2", + "!Chassis type: $2 backplane\n"); + next; + } + /^\s*(Power .*)/ && + ProcessHistory("COMMENTS","keysort","E1","!Power: $1\n") && next; + /^\s*(Lower Power .*)/i && + ProcessHistory("COMMENTS","keysort","E2","!Power: $1\n") && next; + } + ProcessHistory("COMMENTS","","","!\n"); + return(0); +} + +# This routine parses "show gsr chassis-info" for the gsr +# This will create arrarys for hw info. +sub ShowGSR { + # Skip if this is not a 1200n. + print STDERR " In ShowGSR: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^120/); + /^$/ && next; + /^\s+Chassis: type (\S+) Fab Ver: (\S+)/ && + ProcessHistory("COMMENTS","keysort","D0","!\n") && + ProcessHistory("COMMENTS","keysort","D1", + "!GSR Chassis type: $1 Fab Ver: $2\n") && + next; + /^\s+Chassis S\/N: (.*)$/ && + ProcessHistory("COMMENTS","keysort","D2", + "!GSR Chassis S/N: $1\n") && + next; + /^\s+PCA: (\S+)\s*rev: (\S+)\s*dev: \S+\s*HW ver: (\S+)$/ && + ProcessHistory("COMMENTS","keysort","D3", + "!GSR Backplane PCA: $1, rev $2, ver $3\n") && + next; + /^\s+Backplane S\/N: (\S+)$/ && + ProcessHistory("COMMENTS","keysort","D4", + "!GSR Backplane S/N: $1\n") && + next; + } + ProcessHistory("COMMENTS","","","!\n"); + return(0); +} + +# This routine parses "show boot" +sub ShowBoot { + # Pick up boot variables if 7000/7200/7500/12000; + # otherwise pick up bootflash. + print STDERR " In ShowBoot: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if /^\s*\^\s*$/; + return(1) if /Ambiguous command/i; + return(1) if /Invalid input detected/; + return(1) if /Unknown command/; + return(1) if /(Open device \S+ failed|Error opening \S+:)/; + next if /CONFGEN variable/; + if (!defined($H0)) { + $H0=1; ProcessHistory("COMMENTS","keysort","H0","!\n"); + } + if ($type !~ /^(120|7)/) { + ProcessHistory("COMMENTS","keysort","H2","!BootFlash: $_"); + } elsif (/variable/) { + ProcessHistory("COMMENTS","keysort","H1","!Variable: $_"); + } + } + ProcessHistory("COMMENTS","","","!\n"); + return(0); +} + +# This routine parses "show flash" +sub ShowFlash { + # skip if this is 7000, 7200, 7500, or 12000. + print STDERR " In ShowFlash: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type =~ /^(120|7)/); + return(1) if /^\s*\^\s*$/; + return(1) if /Invalid input detected/; + ProcessHistory("FLASH","","","!Flash: $_"); + } + ProcessHistory("","","","!\n"); + return; +} + +# This routine parses "dir /all ((disk|slot)N|bootflash):" +sub DirSlotN { + # Skip if this is not a 3600, 7000, 7200, 7500, or 12000. + print STDERR " In DirSlotN: $_" if ($debug); + + my($dev) = (/\s([^\s]+):/); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^(120|7|36)/); + return(1) if /^\s*\^\s*$/; + return(1) if /Invalid input detected/; + return(1) if /\%Error: No such file or directory/; + return(1) if /No space information available/; + return(-1) if /\%Error calling/; + return(-1) if /: device being squeezed/; # Flash is busy + return(1) if /(Open device \S+ failed|Error opening \S+:)/; + ProcessHistory("FLASH","","","!Flash: $dev: $_"); + } + ProcessHistory("","","","!\n"); + return(0); +} + +# This routine parses "show controllers" +sub ShowContAll { + # Skip if this is a 70[01]0, 7500, or 12000. + print STDERR " In ShowContAll: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type =~ /^(120|7[05])/); + if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; } + /^(BRI unit \d)/ && + ProcessHistory("INT","","","!Interface: $1\n") && next; + /^LANCE unit \d, NIM/ && + ProcessHistory("INT","","","!Interface: $_") && next; + /^(LANCE unit \d)/ && + ProcessHistory("INT","","","!Interface: $1\n") && next; + /(Media Type is \S+),/ && + ProcessHistory("INT","","","!\t$1\n"); + if (/(M\dT:) show controller:$/) { + my($ctlr) = $1; + $_ = ; tr/\015//d; s/ subunit \d,//; + ProcessHistory("INT","","","!Interface: $ctlr $_"); + } + if (/^(\S+) : show controller:$/) { + my($ctlr) = $1; + $_ = ; tr/\015//d; s/ subunit \d,//; + ProcessHistory("INT","","","!Interface: $ctlr: $_"); + } + /^(HD unit \d), idb/ && + ProcessHistory("INT","","","!Interface: $1\n") && next; + /^HD unit \d, NIM/ && + ProcessHistory("INT","","","!Interface: $_") && next; + /^buffer size \d+ HD unit \d, (.*)/ && + ProcessHistory("INT","","","!\t$1\n") && next; + /^AM79970 / && ProcessHistory("INT","","","!Interface: $_") && next; + /^buffer size \d+ (Universal Serial: .*)/ && + ProcessHistory("INT","","","!\t$1\n") && next; + /^Hardware is (.*)/ && + ProcessHistory("INT","","","!Interface: $INT$1\n") && next; + /^(QUICC Serial unit \d),/ && + ProcessHistory("INT","","","!$1\n") && next; + /^QUICC Ethernet .*/ && + ProcessHistory("INT","","","!$_") && next; + /^DTE .*\.$/ && + ProcessHistory("INT","","","!\t$_") && next; + /^(cable type :.*),/ && + ProcessHistory("INT","","","!\t$1\n") && next; + /^(.* cable.*), received clockrate \d+$/ && + ProcessHistory("INT","","","!\t$1\n") && next; + /^.* cable.*$/ && + ProcessHistory("INT","","","!\t$_") && next; + } + return(0); +} + +# This routine parses "show controllers cbus" +# Some of this is printed out in ShowDiagbus. +sub ShowContCbus { + # Skip if this is not a 7000 or 7500. + print STDERR " In ShowContCbus: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^7[05]0/); + if (/^\s*slot(\d+): ([^,]+), hw (\S+), sw (\S+), ccb/) { + $slot = $1; + $board{$slot} = $2; + $hwver{$slot} = $3; + $hwucode{$slot} = $4; + } elsif (/^\s*(\S+) (\d+), hardware version (\S+), microcode version (\S+)/) { + $slot = $2; + $board{$slot} = $1; + $hwver{$slot} = $3; + $hwucode{$slot} = $4; + } elsif (/(Microcode .*)/) { + $ucode{$slot} = $1; + } elsif (/(software loaded .*)/) { + $ucode{$slot} = $1; + } elsif (/(\d+) Kbytes of main memory, (\d+) Kbytes cache memory/) { + $hwmemd{$slot} = $1; + $hwmemc{$slot} = $2; + } elsif (/byte buffers/) { + chop; + s/^\s*//; + $hwbuf{$slot} = $_; + } elsif (/Interface (\d+) - (\S+ \S+),/) { + $interface = $1; + ProcessHistory("HW","","", + "!\n!Int $interface: in slot $slot, named $2\n"); next; + } elsif (/(\d+) buffer RX queue threshold, (\d+) buffer TX queue limit, buffer size (\d+)/) { + ProcessHistory("HW","","","!Int $interface: rxq $1, txq $2, bufsize $3\n"); + next; + } + } + return(0); +} + +# This routine parses "show diagbus" +# This will create arrarys for hw info. +sub ShowDiagbus { + # Skip if this is not a 7000, 70[01]0, or 7500. + print STDERR " In ShowDiagbus: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^7[05]/); + if (/^\s*Slot (\d+):/i) { + $slot = $1; + next; + } elsif (/^\s*Slot (\d+) \(virtual\):/i) { + $slot = $1; + next; + } elsif (/^\s*(.*Processor.*|.*controller|.*Chassis Interface), HW rev (\S+), board revision (\S+)/i) { + $board = $1; + $hwver = $2; + $boardrev = $3; + if ($board =~ /Processor/) { + if ($board =~ /7000 Route\/Switch/) { + $board = "RSP7000"; + } elsif ($board =~ /Route\/Switch Processor (\d)/) { + $board = "RSP$1"; + } elsif ($board =~ /Route/) { + $board = "RP"; + } elsif ($board =~ /Silicon Switch/) { + $board = "SSP"; + } elsif ($board =~ /Switch/) { + $board = "SP"; + $board = "SSP $sspmem" if $ssp; + } elsif ($board =~ /ATM/) { + $board = "AIP"; + } + } elsif ($board =~ /(.*) controller/i) { + $board = $1; + } + # hwucode{$slot} defined in ShowContCbus + if (defined $hwucode{$slot}) { + ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev ucode $hwucode{$slot}\n"); + } else { + ProcessHistory("SLOT","","","!\n!Slot $slot/$board: hvers $hwver rev $boardrev\n"); + } + # These are also from the ShowContCbus + ProcessHistory("SLOT","","","!Slot $slot/$board: $ucode{$slot}\n") if (defined $ucode{$slot}); + ProcessHistory("SLOT","","","!Slot $slot/$board: memd $hwmemd{$slot}, cache $hwmemc{$slot}\n") + if ((defined $hwmemd{$slot}) && (defined $hwmemc{$slot})); + ProcessHistory("SLOT","","","!Slot $slot/$board: $hwbuf{$slot}\n") if (defined $hwbuf{$slot}); + next; + } + /Serial number: (\S+)\s*Part number: (\S+)/ && + ProcessHistory("SLOT","","", + "!Slot $slot/$board: part $2, serial $1\n") && + next; + /^\s*Controller Memory Size: (.*)$/ && + ProcessHistory("SLOT","","","!Slot $slot/$board: $1\n") && + next; + if (/PA Bay (\d) Information/) { + $pano = $1; + if ("PA" =~ /$board/) { + ($s,$c) = split(/\//,$board); + $board = "$s/$c/PA $pano"; + } else { + $board =~ s/\/PA \d//; + $board = "$board/PA $pano"; + } + next; + } + /\s+(.*) PA, (\d) ports?, (\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot/$board: type $3, $2 ports\n") && + next; + /\s+(.*) PA( \(\S+\))?, (\d) ports?/ && + ProcessHistory("SLOT","","","!Slot $slot/$board: type $1$2, $3 ports\n") && + next; + /^\s*HW rev (\S+), Board revision (\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot/$board: hvers $1 rev $2\n") && + next; + /Serial number: (\S+)\s*Part number: (\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot/$board: part $2, serial $1\n") && next; + } + return(0); +} + +# This routine parses "show diag" for the gsr, 7200, 3600, 2600. +# This will create arrarys for hw info. +sub ShowDiag { + # Skip if this is not a 12000. + print STDERR " In ShowDiag: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^(120|720|36|26)/); + /^$/ && next; + if (!defined($showdiags)) {$showdiags=1; ProcessHistory("SLOT","","","!\n");} + s/Port Packet Over SONET/POS/; + if (/^\s*SLOT (\d+)\s+\(.*\): (.*)/) { + $slot = $1; + ProcessHistory("SLOT","","","!Slot $slot: $2\n"); + $board = "RP" if (/Route Processor/); + $board = "CLK" if (/Clock Scheduler Card/); + next; + } + if (/^\s+PCA:\s+(.*)/){ + local($part) = $1; + $_ = ; + /^\s+HW version (\S+)\s+S\/N (\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot/PCA: part $part, serial $2\n") && + ProcessHistory("SLOT","","","!Slot $slot/PCA: hvers $1\n"); + next; + } + + if (/^\s+MBUS: .*\)\s+(.*)/) { + local($tmp) = "!Slot $slot/MBUS: part $1"; + $_ = ; + /^\s+HW version (\S+)\s+S\/N (\S+)/ && + ProcessHistory("SLOT","","","$tmp, serial $2\n") && + ProcessHistory("SLOT","","","!Slot $slot/MBUS: hvers $1\n"); + next; + } + if (/^\s+MBUS Agent Software version (.*)/) { + local($sw) = $1; + local($tail) = "!\n" if ($board =~ /(CLK|RP)/); + ProcessHistory("SLOT","","","!Slot $slot/MBUS: software $sw\n$tail"); + next; + } + if (/^\s+DRAM size: (\d+)/) { + local($dram) = $1 / 1048576; + $_ = ; + /^\s+FrFab SDRAM size: (\d+)/ && + ProcessHistory("SLOT","","","!Slot $slot/MBUS: $dram Mbytes DRAM, " + . $1 / 1024 . " Kbytes SDRAM\n!\n"); + next; + } + # 7200 and 3600 stuff + if (/^(Slot)\s+(\d+):/ || /^\s+(WIC|VIC) Slot (\d):/) { + if ($1 eq "WIC") { + $WIC = "/$2"; + } elsif ($1 eq "VIC") { + $WIC = "/$2"; + } else { + $slot = $2; + undef($WIC); + } + $_ = ; tr/\015//d; + + # clean up hideous 7200 format to look more like 7500 output + s/Fast-ethernet on C7200 I\/O card/FE-IO/; + s/ with MII or RJ45/-TX/; + s/Fast-ethernet /100Base/; s/[)(]//g; + + /\s+(.*) port adapter,?\s+(\d+)\s+/i && + ProcessHistory("SLOT","","","!Slot $slot: type $1, $2 ports\n"); + # I/O controller with no interfaces + /\s+(.*)\s+port adapter\s*$/i && + ProcessHistory("SLOT","","","!Slot $slot: type $1, 0 ports\n"); + /\s+(.*)\s+daughter card(.*)$/ && + ProcessHistory("SLOT","","","!Slot $slot$WIC: type $1$2\n"); + /\s+(FT1)$/ && + ProcessHistory("SLOT","","","!Slot $slot$WIC: type $1\n"); + next; + } + /revision\s+(\S+).*revision\s+(\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot$WIC: hvers $1 rev $2\n") && + next; + /number\s+(\S+)\s+Part number\s+(\S+)/ && + ProcessHistory("SLOT","","","!Slot $slot$WIC: part $2, serial $1\n!\n") && + next; +} + return(0); +} + +# This routine parses "show c7200" for the 7200 +# This will create arrarys for hw info. +sub ShowC7200 { + # Skip if this is not a 7200. + print STDERR " In ShowC7200: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if ($type !~ /^72/); + /^$/ && next; + if (/C7200 Midplane EEPROM:/) { + $_ = ; + /revision\s+(\S+).*revision\s+(\S+)/; + ProcessHistory("SLOT","","","!Slot Midplane: hvers $1 rev $2\n"); + $_ = ; + /number\s+(\S+)\s+Part number\s+(\S+)/; + ProcessHistory("SLOT","","","!Slot Midplane: part $2, serial $1\n!\n"); + next; + } + if (/C720\d(VXR)? CPU EEPROM:/) { + $_ = ; + /revision\s+(\S+).*revision\s+(\S+)/ && + ProcessHistory("SLOT","","","!Slot CPU: hvers $1 rev $2\n"); + $_ = ; + /number\s+(\S+)\s+Part number\s+(\S+)/ && + ProcessHistory("SLOT","","","!Slot CPU: part $2, serial $1\n!\n"); + next; + } + } + return(0); +} + +# This routine processes a "write term" +sub WriteTerm { + print STDERR " In WriteTerm: $_" if ($debug); + + while () { + tr/\015//d; + last if(/^$prompt/); + /Non-Volatile memory is in use/ && return(-1); # NvRAM is locked + # skip the crap + if (/^(\.\.+$|##+$|Building configuration...)/i) { + while () { + tr/\015//d; + next if (/^Current configuration:/i); + next if (/^(\.+|[%!].*|\s*)$/); + next if (/^ip add.*ipv4:/); # band-aid for 3620 12.0S + last; + } + ProcessHistory("","","","!\n"); + tr/\015//d; + } + # some versions have other crap mixed in with the bits in the + # block above + /^! (Last configuration|NVRAM config last)/ && next; + + # Dog gone Cool matches to process the rest of the config + /^#time: / && next; # kill time: + /^tftp-server flash / && next; # kill any tftp remains + /^ntp clock-period / && next; # kill ntp clock-period + /^ length / && next; # kill length on serial lines + /^ width / && next; # kill width on serial lines + /^enable password / && + ProcessHistory("ENABLE","","","!enable password \n") && + next; + /^(username .*) password /&& + ProcessHistory("USER","","","!$1 password \n") && next; + /^\s*password / && + ProcessHistory("LINE-PASS","","","! password \n") && next; + /^\s*neighbor (\S*) password / && + ProcessHistory("","","","! neighbor $1 password \n") && + next; + /^(ip ftp password) / && + ProcessHistory("","","","!$1 \n") && next; + /^( ip ospf authentication-key) / && + ProcessHistory("","","","!$1 \n") && next; + /^( ip ospf message-digest-key \d+ md5) / && + ProcessHistory("","","","!$1 \n") && next; + /fair-queue individual-limit/ && next; + # sort ip explicit-paths. + if (/^ip explicit-path name (\S+)/) { + my($key) = $1; + my($expath) = $_; + while () { + tr/\015//d; + last if (/^$prompt/); + last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/); + if (/^ip explicit-path name (\S+)/) { + ProcessHistory("EXPATH","keysort","$key","$expath"); + $key = $1; + $expath = $_; + } else { + $expath .= $_; + } + } + ProcessHistory("EXPATH","keysort","$key","$expath"); + } + # sort route-maps + if (/^route-map (\S+)/) { + my($key) = $1; + my($routemap) = $_; + while () { + tr/\015//d; + last if (/^$prompt/ || ! /^(route-map |[ !])/); + if (/^route-map (\S+)/) { + ProcessHistory("ROUTEMAP","keysort","$key","$routemap"); + $key = $1; + $routemap = $_; + } else { + $routemap .= $_; + } + } + ProcessHistory("ROUTEMAP","keysort","$key","$routemap"); + } + # filter out any RCS/CVS tags to avoid confusing local CVS storage + s/\$(Revision|Id):/ $1:/; + # order access-lists + /^access-list\s+(\d\d?)\s+(\S+)\s+(\S+)/ && + ProcessHistory("ACL $1 $2","ipsort","$3","$_") && next; + # order extended access-lists + /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+host\s+(\S+)/ && + ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next; + /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+(\d\S+)/ && + ProcessHistory("EACL $1 $2","ipsort","$3","$_") && next; + /^access-list\s+(\d\d\d)\s+(\S+)\s+ip\s+any/ && + ProcessHistory("EACL $1 $2","ipsort","0.0.0.0","$_") && next; + # order arp lists + /^arp\s+(\d+\.\d+\.\d+\.\d+)\s+/ && + ProcessHistory("ARP","ipsort","$1","$_") && next; + /^ip prefix-list\s+(\S+)\s+seq\s+(\d+)\s+(permit|deny)\s+(\d\S+)(\/.*)$/ && + ProcessHistory("PACL $1 $3","ipsort","$4","ip prefix-list $1 $3 $4$5\n") + && next; + # order logging statements + /^logging (\d+\.\d+\.\d+\.\d+)/ && + ProcessHistory("LOGGING","ipsort","$1","$_") && next; + # order name-server statements + /^ip name-server (\d+\.\d+\.\d+\.\d+)/ && + ProcessHistory("NAMESERVER","ipsort","$1","$_") && next; + # order snmp-server host statements + /^snmp-server host (\d+\.\d+\.\d+\.\d+)/ && + ProcessHistory("SNMPSERVERHOST","ipsort","$1","$_") && next; + /^snmp-server community / && + ProcessHistory("SNMPSERVERCOMM","keysort","$_","$_") && next; + # order tacacs server statements + /^(tacacs-server key )/ && + ProcessHistory("","","","!$1\n") && next; + /^tacacs-server host (\d+\.\d+\.\d+\.\d+)/ && + ProcessHistory("TAC","ipsort","$1","$_") && next; + # order clns host statements + /^clns host \S+ (\S+)/ && + ProcessHistory("CLNS","keysort","$1","$_") && next; + # order alias statements + /^alias / && ProcessHistory("ALIAS","keysort","$_","$_") && next; + # delete ntp auth password + /^(ntp authentication-key \d+ md5) / && + ProcessHistory("","","","!$1 \n") && next; + # order ntp peers/servers + if (/^ntp (server|peer) (\d+)\.(\d+)\.(\d+)\.(\d+)/) { + $sortkey = sprintf("$1 %03d%03d%03d%03d",$2,$3,$4,$5); + ProcessHistory("NTP","keysort",$sortkey,"$_"); + next; + } + # order ip host line statements + /^ip host line(\d+)/ && + ProcessHistory("IPHOST","numsort","$1","$_") && next; + # order ip nat source static statements + /^ip nat (\S+) source static (\S+)/ && + ProcessHistory("IP NAT $1","ipsort","$2","$_") && next; + # order atm map-list statements + /^\s+ip\s+(\d+\.\d+\.\d+\.\d+)\s+atm-vc/ && + ProcessHistory("ATM map-list","ipsort","$1","$_") && next; + # order ip rcmd lines + /^ip rcmd/ && ProcessHistory("RCMD","keysort","$_","$_") && next; + + ProcessHistory("","","","$_"); + # end of config + if (/^end$/) { + $found_end = 1; + return(1); + } + } + return(0); +} + +# dummy function +sub DoNothing {print STDOUT;} + +# Main +%commands=( + 'show version' => "ShowVersion", + 'show install active' => "ShowInstallActive", + 'show env all' => "ShowEnv", + 'show gsr chassis' => "ShowGSR", + 'show boot' => "ShowBoot", + 'show bootvar' => "ShowBoot", + 'show flash' => "ShowFlash", + 'dir /all bootflash:' => "DirSlotN", + 'dir /all slot0:' => "DirSlotN", + 'dir /all disk0:' => "DirSlotN", + 'dir /all slot1:' => "DirSlotN", + 'dir /all disk1:' => "DirSlotN", + 'show controllers' => "ShowContAll", + 'show controllers cbus' => "ShowContCbus", + 'show diagbus' => "ShowDiagbus", + 'show diag' => "ShowDiag", + 'show c7200' => "ShowC7200", + 'write term' => "WriteTerm" +); +# keys() doesnt return things in the order entered and the order of the +# cmds is important (show version first and write term last). pita +@commands=( + "show version", + "show install active", + "show env all", + "show gsr chassis", + "show boot", + "show bootvar", + "show flash", + "dir /all bootflash:", + "dir /all slot0:", + "dir /all disk0:", + "dir /all slot1:", + "dir /all disk1:", + "show controllers", + "show controllers cbus", + "show diagbus", + "show diag", + "show c7200", + "write term" +); +$cisco_cmds=join(";",@commands); +$cmds_regexp=join("|",@commands); + +open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n"; +select(OUTPUT); +# make OUTPUT unbuffered if debugging +if ($debug) { $| = 1; } + +if ($file) { + print STDERR "opening file $host\n" if ($debug); + print STDOUT "opening file $host\n" if ($log); + open(INPUT,"<$host") || die "open failed for $host: $!\n"; +} else { + print STDERR "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug); + print STDOUT "executing clogin -t $timeo -c\"$cisco_cmds\" $host\n" if ($log); + if (defined($ENV{NOPIPE})) { + system "clogin -t $timeo -c \"$cisco_cmds\" $host $host.raw 2>&1" || die "clogin failed for $host: $!\n"; + open(INPUT, "< $host.raw") || die "clogin failed for $host: $!\n"; + } else { + open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host ) { + tr/\015//d; + if (/> \(enable\) exit$/) { + $clean_run=1; + last; + } + if (/Error:/) { + s/^.*Error:/Error:/; + print STDOUT ("$host clogin error: $_"); + print STDERR ("$host clogin error: $_") if ($debug); + $clean_run=0; + last; + } + while (/> \(enable\)\s*($cmds_regexp)\s*$/) { + $cmd = $1; +# if (!defined($prompt)) {$prompt = ($_ =~ /^([^>]+> .enable.)/)[0]; } + if (!defined($prompt)) {$prompt = ($_ =~ /^([^>]+>)/)[0]; } + print STDERR ("HIT COMMAND:$_") if ($debug); + if (! defined($commands{$cmd})) { + print STDERR "found unexpected command - \"$cmd\"\n"; + $clean_run = 0; + last TOP; + } + $rval = &{$commands{$cmd}}; + delete($commands{$cmd}); + if ($rval == -1) { + $clean_run = 0; + last TOP; + } + } +} +print STDOUT "Done $logincmd: $_\n" if ($log); +# Flush History +ProcessHistory("","","",""); +# Cleanup +close(INPUT); +close(OUTPUT); + +if (defined($ENV{NOPIPE})) { + unlink("$host.raw") if (! $debug); +} + +# check for completeness +if (scalar(%commands) || !$clean_run || !$found_end) { + if (scalar(%commands)) { + printf(STDOUT "missed cmd(s): %s\n", join(',', keys(%commands))); + printf(STDERR "missed cmd(s): %s\n", join(',', keys(%commands))) if ($debug); + } + if (!$clean_run || !$found_end) { + print STDOUT "End of run not found\n"; + print STDERR "End of run not found\n" if ($debug); + system("/usr/bin/tail -1 $host.new"); + } + unlink "$host.new" if (! $debug); +} diff --git a/bin/clogin b/bin/clogin index cad94cd..1b345be 100755 --- a/bin/clogin +++ b/bin/clogin @@ -27,10 +27,11 @@ # # Usage line -set usage "Usage: $argv0 \[-u user\] \[-p user-password\] \[-v vty-password\] \ -\[-w enable-username\] \[-e enable-password\] \[-noenable\] \ -\[-f cloginrc-file\] \[-y ssh_cypher_type\] \[-c command\] \[-s script-file\] \ -\[-x command-file\] \[-autoenable\] \[-t timeout\] router \[router...\]\n" +set usage "Usage: $argv0 \[-autoenable\] \[-noenable\] \[-c command\] \ +\[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \ +\[-s script-file\] \[-t timeout\] \[-u username\] \ +\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \ +\[-y ssh_cypher_type\] router \[router...\]\n" # env(CLOGIN) may contain: # x == do not set xterm banner or name @@ -125,7 +126,7 @@ for {set i 0} {$i < $argc} {incr i} { exit 1 } set do_script 1 - # cypher type + # 'ssh -c' cypher type } -y* - -Y* { if {! [ regexp .\[eE\](.+) $arg ignore cypher]} { @@ -139,10 +140,14 @@ for {set i 0} {$i < $argc} {incr i} { incr i set password_file [ lindex $argv $i ] } + # Timeout } -t* - -T* { - incr i - set timeout [ lindex $argv $i ] + if {! [ regexp .\[tT\](.+) $arg ignore timeout]} { + incr i + set timeout [ lindex $argv $i ] + } + # Command file } -x* - -X { if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} { @@ -228,23 +233,28 @@ proc find {var router} { # that a "bad guy" could just as easy put such code in the clogin # script, so I will leave .cloginrc as just an extention of that script proc source_password_file { } { - global env password_file read_password_file - if { [info exists read_password_file] } { return } - if { [info exists password_file] == 0 } { + global env password_file read_password_file + if { [info exists read_password_file] } { return } + if { [info exists password_file] == 0 } { set password_file $env(HOME)/.cloginrc - } - set read_password_file 1 - file stat $password_file fileinfo - if { [expr ($fileinfo(mode) & 007)] != 0000 } { - send_user "Error: $password_file must not be world readable/writable\n" - exit 1 - } - source $password_file + } + if { ! [file exists $password_file] } { + send_user "Error: password file ($password_file) does not exist\n" + exit 1 + } + set read_password_file 1 + file stat $password_file fileinfo + if { [expr ($fileinfo(mode) & 007)] != 0000 } { + send_user "Error: $password_file must not be world readable/writable\n" + exit 1 + } + source $password_file } # Log into the router. proc login { router user userpswd passwd enapasswd prompt cyphertype } { global spawn_id in_proc do_command do_script + global u_prompt p_prompt e_prompt set in_proc 1 set tryssh 1 @@ -259,7 +269,7 @@ proc login { router user userpswd passwd enapasswd prompt cyphertype } { expect_after { timeout { send_user "\nError: TIMEOUT reached\n" - close; wait + catch {close}; wait if { $in_proc} { return 1 } else { @@ -267,7 +277,7 @@ proc login { router user userpswd passwd enapasswd prompt cyphertype } { } } eof { send_user "\nError: EOF received\n" - close; wait + catch {close}; wait if { $in_proc} { return 1 } else { @@ -317,18 +327,18 @@ proc login { router user userpswd passwd enapasswd prompt cyphertype } { send "no\r" send_user "Error: The host key for $router has changed. update the known_hosts file accordingly.\n" return 1 } - -re "(Username|login):" { send "$user\r" + -re "$u_prompt" { send "$user\r" expect { eof { send_user "Error: Couldn't login\n"; wait; return 1 } - -re "\[Pp]assword:" { send "$userpswd\r" } + -re "$p_prompt" { send "$userpswd\r" } "$prompt" { set in_proc 0; return 0 } } exp_continue } - "\[Pp]assword:" { send "$passwd\r" + -re "$p_prompt" { send "$passwd\r" expect { eof { send_user "Error: Couldn't login\n"; wait; return 1 } - "Password:" { send "$enapasswd\r" } + -re "$e_prompt" { send "$enapasswd\r" } "$prompt" { set in_proc 0; return 0 } } exp_continue @@ -336,7 +346,7 @@ proc login { router user userpswd passwd enapasswd prompt cyphertype } { "$prompt" { } denied { send_user "Error: Check your passwd for $router\n" if { $do_command || $do_script } { - send "quit" + send "exit\r" wait return 1 } else { @@ -352,20 +362,22 @@ proc login { router user userpswd passwd enapasswd prompt cyphertype } { # Enable proc do_enable { enauser enapasswd } { global prompt in_proc + global u_prompt e_prompt set in_proc 1 send "enable\r" expect { - "Username:" { send "$enauser\r"; exp_continue} - "Password:" { send "$enapasswd\r"; exp_continue} - "#" { } + -re "$u_prompt" { send "$enauser\r"; exp_continue} + -re "$e_prompt" { send "$enapasswd\r"; exp_continue} + "#" { set prompt "#" } + "(enable)" { set prompt "> (enable) " } denied { send_user "Error: Check your Enable passwd\n"; return 1} "% Bad passwords" { send_user "Error: Check your Enable passwd\n" return 1 } } - # Set the prompt variable so script files don't need to know what it is. - set prompt "#" + # We set the prompt variable (above) so script files don't need + # to know what it is. set in_proc 0 return 0 } @@ -375,27 +387,36 @@ proc run_commands { prompt command } { global in_proc set in_proc 1 - send "term length 0\r" + # If the prompt is (enable), then we are on a switch and the + # command is "set length 0"; otherwise its "term length 0". + if [ string compare "> (enable) " "$prompt" ] { + send "term length 0\r" + } else { + send "set length 0\r" + } + expect $prompt {} + regsub -all "\[)(]" $prompt {\\&} reprompt + # Is this a multi-command? if [ string match "*\;*" "$command" ] { set commands [split $command \;] set num_commands [llength $commands] for {set i 0} {$i < $num_commands} { incr i} { - send "[subst [lindex $commands $i]]\r" + send "[subst -nocommands [lindex $commands $i]]\r" expect { - -re "^\[^\n\r]*$prompt." { exp_continue } - -re "^\[^\n\r *]*$prompt" {} + -re "^\[^\n\r]*$reprompt." { exp_continue } + -re "^\[^\n\r *]*$reprompt" {} -re "\[\n\r]" { exp_continue } } } } else { - send "[subst $command]\r" + send "[subst -nocommands $command]\r" expect { - -re "^\[^\n\r]*$prompt." { exp_continue } - -re "^\[^\n\r *]*$prompt" {} + -re "^\[^\n\r]*$reprompt." { exp_continue } + -re "^\[^\n\r *]*$reprompt" {} -re "\[\n\r]" { exp_continue } } } @@ -434,6 +455,11 @@ foreach router [lrange $argv $i end] { } } + # look for noenable option in .cloginrc + if { [find noenable $router] != "" } { + set enable 0 + } + # Figure out passwords if { $do_passwd || $do_enapasswd } { set pswd [find password $router] @@ -476,7 +502,15 @@ foreach router [lrange $argv $i end] { if { "$enauser" == "" } { set enauser $ruser } } - # Figure out cypher tpye + # Figure out prompts + set u_prompt [find userprompt $router] + if { "$u_prompt" == "" } { set u_prompt "(Username|login):" } + set p_prompt [find passprompt $router] + if { "$p_prompt" == "" } { set p_prompt "\[Pp]assword:" } + set e_prompt [find enableprompt $router] + if { "$e_prompt" == "" } { set e_prompt "\[Pp]assword:" } + + # Figure out cypher type if {[info exists cypher]} { # command line cypher type set cyphertype $cypher @@ -503,7 +537,13 @@ foreach router [lrange $argv $i end] { continue } } elseif { $do_script } { - send "term length 0\r" + # If the prompt is (enable), then we are on a switch and the + # command is "set length 0"; otherwise its "term length 0". + if [ string compare "> (enable) " "$prompt" ] { + send "term length 0\r" + } else { + send "set length 0\r" + } expect $prompt {} source $sfile close diff --git a/bin/control_rancid b/bin/control_rancid index 0728e7e..2c367d6 100755 --- a/bin/control_rancid +++ b/bin/control_rancid @@ -112,7 +112,7 @@ then router=$1 touch $router - cvs add $router + cvs add -ko $router cvs commit -m 'new router' $router echo "Added $router" done @@ -172,9 +172,10 @@ do IFS=$OFS router=$1; mfg=$2 - if [ ! -f $router.new ] + if [ ! -s $router.new ] then echo "$router:$mfg" >> $DIR/routers.up.missed + rm -f $router.new fi done @@ -191,6 +192,15 @@ do done echo +# Make sure that all of the new configs are not empty. +for config in *.new +do + if [ ! -s $config ] + then + rm -f $config + fi +done + # Now that we have the new configs, rename them to their proper # name. rename 's/.new$//' *.new @@ -224,18 +234,21 @@ Precedence: bulk EMAIL fi -# If any machines have not been reached within 24 hours, mail -# out a list of them. +# If any machines have not been reached within the last $OLDTIME +# hours, mail out a list of them. cd $DIR/configs rm -f $DIR/routers.failed -perl -F: -ane '{$t = (stat($F[0]))[9]; print `ls -ld $F[0]` - if (time() - $t >= 86400);}' $DIR/routers.up | sort -u > $DIR/routers.failed +if [ "X$OLDTIME" = "X" ] ; then + OLDTIME=24 +fi +perl -F: -ane "{\$t = (stat(\$F[0]))[9]; print \`ls -ld \$F[0]\` + if (time() - \$t >= $OLDTIME*60*60);}" $DIR/routers.up | sort -u > $DIR/routers.failed if [ -s $DIR/routers.failed ] then ( cat <= $LOCKTIME*60*60);" > $TMPDIR/.$GROUP.old + if [ -s $TMPDIR/.$GROUP.old ] + then + ( + cat <) { +TOP: while() { tr/\015//d; if (/\#exit$/) { $clean_run=1; @@ -347,13 +348,13 @@ while() { if (! defined($commands{$cmd})) { print STDERR "found unexpected command - \"$cmd\"\n"; $clean_run = 0; - last; + last TOP; } $rval = &{$commands{$cmd}}; delete($commands{$cmd}); if ($rval == -1) { $clean_run = 0; - last; + last TOP; } } } diff --git a/bin/jlogin b/bin/jlogin index 485db7f..a21188c 100755 --- a/bin/jlogin +++ b/bin/jlogin @@ -7,7 +7,7 @@ ## This software may be freely copied, modified and redistributed without ## fee for non-commerical purposes provided that this copyright notice is ## preserved intact on all copies and modified copies. -## +## ## There is no warranty or other guarantee of fitness of this software. ## It is provided solely "as is". The author(s) disclaim(s) all ## responsibility and liability with respect to this software's usage @@ -23,8 +23,9 @@ # # Usage line -set usage "Usage: $argv0 \[-e encryption_type\] \[-u username\] \[-p user-password\]\ -\[-f cloginrc-file\] \[-c command\] \[-s script-file\]\ +set usage "Usage: $argv0 \[-c command\] \[-f cloginrc-file\] \ +\[-p user-password\] \[-r passphrase\] \[-s script-file\] \ +\[-u username\] \[-t timeout\] \[-x command-file\] \[-y ssh_cypher_type\] \ router \[router...\]\n" # env(CLOGIN) may contain the following chars: @@ -40,31 +41,41 @@ set enable 1 # The default is to look in the password file to find the passwords. This # tracks if we receive them on the command line. set do_passwd 1 +# No passphrase by default +set passphrase "" # Find the user in the ENV, or use the unix userid. if {[ info exists env(CISCO_USER) ] } { set default_user $env(CISCO_USER) } else { - # This uses "id" which I think is portable. At least it has existed - # (without options) on all machines/OSes I've been on recently - + # This uses "id" which I think is portable. At least it has existed + # (without options) on all machines/OSes I've been on recently - # unlike whoami or id -nu. regexp {\(([^)]*)} [exec id] junk default_user } # Sometimes routers take awhile to answer (the default is 10 sec) -set timeout 45 +set timeout 120 # Process the command line for {set i 0} {$i < $argc} {incr i} { set arg [lindex $argv $i] switch -glob -- $arg { - # Username - -u* - - -U* { - if {! [ regexp .\[uU\](.+) $arg ignore user]} { + # Command to run. + -c* - + -C* { + if {! [ regexp .\[cC\](.+) $arg ignore command]} { incr i - set username [ lindex $argv $i ] + set command [ lindex $argv $i ] + } + set do_command 1 + # alternate cloginrc file + } -f* - + -F* { + if {! [ regexp .\[fF\](.+) $arg ignore password_file]} { + incr i + set password_file [ lindex $argv $i ] } # user Password } -p* - @@ -74,14 +85,13 @@ for {set i 0} {$i < $argc} {incr i} { set userpswd [ lindex $argv $i ] } set do_passwd 0 - # Command to run. - } -c* - - -C* { - if {! [ regexp .\[cC\](.+) $arg ignore command]} { + # passphrase + } -r* - + -R* { + if {! [ regexp .\[rR\](.+) $arg ignore passphrase]} { incr i - set command [ lindex $argv $i ] + set passphrase [ lindex $argv $i ] } - set do_command 1 # Expect script to run. } -s* - -S* { @@ -94,19 +104,38 @@ for {set i 0} {$i < $argc} {incr i} { exit 1 } set do_script 1 - # encryption type - } -e* - - -E* { - if {! [ regexp .\[eE\](.+) $arg ignore encrypt]} { + # Timeout + } -t* - + -T* { + if {! [ regexp .\[tT\](.+) $arg ignore timeout]} { incr i - set encrypt [ lindex $argv $i ] + set timeout [ lindex $argv $i ] } - # alternate cloginrc file - } -f* - - -F* { - if {! [ regexp .\[fF\](.+) $arg ignore password_file]} { + # Username + } -u* - + -U* { + if {! [ regexp .\[uU\](.+) $arg ignore user]} { incr i - set password_file [ lindex $argv $i ] + set username [ lindex $argv $i ] + } + # command file + } -x* - + -X* { + if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} { + incr i + set cmd_file [ lindex $argv $i ] + } + set cmd_fd [open $cmd_file r] + set cmd_text [read $cmd_fd] + close $cmd_fd + set command [join [split $cmd_text \n] \;] + set do_command 1 + # 'ssh -c' cypher type + } -y* - + -Y* { + if {! [ regexp .\[yY\](.+) $arg ignore cypher]} { + incr i + set cypher [ lindex $argv $i ] } } -* { send_user "Error: Unknown argument! $arg\n" @@ -158,9 +187,9 @@ proc label { host } { proc add {var args} { global $var ;lappend $var $args } proc find {var router} { - source_password_file - upvar $var list - if { [info exists list] } { + source_password_file + upvar $var list + if { [info exists list] } { foreach line $list { if { [string match [lindex $line 0] $router ] } { return [lrange $line 1 end] @@ -174,31 +203,45 @@ proc find {var router} { # it is sourced, the user better know what to put in there, as it # could install more than just password info... I will assume however, # that a "bad guy" could just as easy put such code in the clogin -# script, so I will leave .cloginrc as just an extention of that script +# script, so I will leave .cloginrc as just an extention of that script proc source_password_file { } { - global env password_file read_password_file - if { [info exists read_password_file] } { return } - if { [info exists password_file] == 0 } { + global env password_file read_password_file + if { [info exists read_password_file] } { return } + if { [info exists password_file] == 0 } { set password_file $env(HOME)/.cloginrc - } - set read_password_file 1 - file stat $password_file fileinfo - if { [expr ($fileinfo(mode) & 007)] != 0000 } { - send_user "Error: $password_file must not be world readable/writable\n" - exit 1 - } - source $password_file + } + if { ! [file exists $password_file] } { + send_user "Error: password file ($password_file) does not exist\n" + exit 1 + } + + set read_password_file 1 + file stat $password_file fileinfo + if { [expr ($fileinfo(mode) & 007)] != 0000 } { + send_user "Error: $password_file must not be world readable/writable\n" + exit 1 + } + source $password_file } # Log into the router. -proc login { router user passwd prompt encrypttype} { - global spawn_id in_proc do_command do_script +proc login { router user passwd prompt cyphertype identfile} { + global spawn_id in_proc do_command do_script passphrase set in_proc 1 - # ssh to the router & try to login. - if [ catch {spawn ssh -c $encrypttype -x -l $user $router} reason ] { - send_user "Error: failed to ssh: $reason\n" - exit 1 + # ssh to the router & try to login with or without an identfile. + # We use two calls to spawn since spawn does not seem to parse + # spaces correctly. + if {$identfile != ""} { + if [ catch {spawn ssh -c $cyphertype -x -l $user -i $identfile $router} reason ] { + send_user "Error: failed to ssh: $reason\n" + exit 1 + } + } else { + if [ catch {spawn ssh -c $cyphertype -x -l $user $router} reason ] { + send_user "Error: failed to ssh: $reason\n" + exit 1 + } } sleep 0.3 @@ -206,7 +249,7 @@ proc login { router user passwd prompt encrypttype} { expect_after { timeout { send_user "\nError: TIMEOUT reached\n" - close; wait + catch {close}; wait if { $in_proc} { return 1 } else { @@ -214,7 +257,7 @@ proc login { router user passwd prompt encrypttype} { } } eof { send_user "\nError: EOF received\n" - close; wait + catch {close}; wait if { $in_proc} { return 1 } else { @@ -231,10 +274,10 @@ proc login { router user passwd prompt encrypttype} { # then it will just send the passwd. expect { eof { send_user "Error: Couldn't login\n"; wait; return 1 } - "Connection refused" { + "Connection refused" { expect eof send_user "Error: Connection Refused\n"; wait; return 1 - } "Unknown host\r\n" { + } "Unknown host\r\n" { expect eof send_user "Error: Unknown host\n"; wait; return 1 } "Host is unreachable" { @@ -244,6 +287,10 @@ proc login { router user passwd prompt encrypttype} { expect eof send_user "Error: Unknown host\n"; wait; return 1 } + -re "Enter passphrase for RSA key '\[^'\]*': " { + send_user "\nKey has passphrase!\n" + send "$passphrase\r" + exp_continue } -re "Host key not found .* \(yes\/no\)\?" { send "yes\r" send_user "Host $router added to the list of known hosts.\n" @@ -253,7 +300,7 @@ proc login { router user passwd prompt encrypttype} { send_user "Error: The host key for $router has changed. update the known_hosts file accordingly.\n" return 1 } -re "(Username|^login):" { send "$user\r" - expect { + expect { eof { send_user "Error: Couldn't login\n"; wait; return 1 } -re "\[Pp]assword:" { send "$passwd\r" } "$prompt" { set in_proc 0; return 0 } @@ -261,15 +308,15 @@ proc login { router user passwd prompt encrypttype} { exp_continue } "\[Pp]assword:" { send "$passwd\r" - expect { + expect { eof { send_user "Error: Couldn't login\n"; wait; return 1 } "$prompt" { set in_proc 0; return 0 } } exp_continue } "$prompt" { } - denied { send_user "Error: Check your passwd for $router\n" - if { $do_command || $do_script } { + denied { send_user "Error: Check your passwd for $router\n" + if { $do_command || $do_script } { send "quit" wait return 1 @@ -339,7 +386,7 @@ foreach router [lrange $argv $i end] { # if { [llength $pswd] == 0 } { # send_user "Error - no password for $router in $password_file.\n" # continue -# } +# } # if { $do_enapasswd && !$autoenable && [llength $pswd] < 2 } { # send_user "Error - no enable password for $router in $password_file." # continue @@ -348,13 +395,13 @@ foreach router [lrange $argv $i end] { # } # Figure out username - if {[info exists username]} { + if {[info exists username]} { # command line username set loginname $username } else { - set loginname [find user $router] + set loginname [find user $router] if { "$loginname" == "" } { set loginname $default_user } - } + } # Figure out loginname's password (if different from the vty password) if {[info exists userpswd]} { @@ -363,18 +410,25 @@ foreach router [lrange $argv $i end] { } else { set passwd [lindex [find password $loginname@$router] 0] if { "$passwd" == "" } { set passwd [lindex [find password $router] 0] } - } + } - # Figure out encryption tpye - if {[info exists encrypt]} { - # command line encryption type - set encrypttype $encrypt + # figure out identity file to use + set identfile "" + if {[info exists identity]} { + set identfile [lindex [find identity $router] 0] + } + + # Figure out ssh cypher type + if {[info exists cypher]} { + # command line ssh cypher type + set cyphertype $cypher } else { - set encrypttype "3des" - } + set cyphertype [find cyphertype $router] + if { "$cyphertype" == "" } { set cyphertype "3des" } + } # Login to the router - if {[login $router $loginname $passwd $prompt $encrypttype]} { + if {[login $router $loginname $passwd $prompt $cyphertype $identfile]} { continue } @@ -390,7 +444,7 @@ foreach router [lrange $argv $i end] { source $sfile close } else { - label $router + label $router log_user 1 interact } diff --git a/bin/jrancid b/bin/jrancid index c879737..efc8465 100755 --- a/bin/jrancid +++ b/bin/jrancid @@ -146,6 +146,11 @@ sub ShowChassisClocks { last if(/^$prompt/); /error: the chassis subsystem is not running/ && return; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; ProcessHistory("","","","# $_"); } return; @@ -163,7 +168,11 @@ sub ShowChassisEnvironment { /error: the chassis subsystem is not running/ && return; /Couldn\'t initiate connection/ && return; - / backplane temperature/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; + / backplane temperature/ && next; /(\s*Power supply.*), temperature/ && ProcessHistory("","","","# $1\n") && next; /(\s*.+) +\d+ degrees C.*$/ && @@ -184,6 +193,11 @@ sub ShowChassisFirmware { last if(/^$prompt/); /error: the chassis subsystem is not running/ && return; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; ProcessHistory("","","","# $_"); } return; @@ -200,9 +214,14 @@ sub ShowChassisFpcDetail { last if(/^$prompt/); /error: the chassis subsystem is not running/ && return; - / Temperature:/ && next; - / Start time:/ && next; - / Uptime:/ && next; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; + / Temperature/ && next; + / Start time/ && next; + / Uptime/ && next; ProcessHistory("","","","# $_"); } return; @@ -219,12 +238,18 @@ sub ShowChassisHardware { last if(/^$prompt/); /error: the chassis subsystem is not running/ && return; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; ProcessHistory("","","","# $_"); } return; } # This routine parses "show chassis routing-engine" +# Most output is ignored. sub ShowChassisRoutingEngine { print STDERR " In ShowChassisRoutingEngine: $_" if ($debug); @@ -234,14 +259,26 @@ sub ShowChassisRoutingEngine { tr/\015//d; last if(/^$prompt/); + /error: the chassis subsystem is not running/ && return; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; /^Routing Engine status:/ && ProcessHistory("","","","# $_") && next; - / DRAM:/ && ProcessHistory("","","","# $_") && next; + / Slot / && ProcessHistory("","","","# $_") && next; + / Current state/ && ProcessHistory("","","","# $_") && next; + / Election priority/ && ProcessHistory("","","","# $_") && next; + / DRAM/ && ProcessHistory("","","","# $_") && next; + / Serial ID/ && ProcessHistory("","","","# $_") && next; /^\s*$/ && ProcessHistory("","","","# $_") && next; } return; } -# This routine parses "show chassis scb" +# This routine parses "show chassis scb", "show chassis ssb", and +# "show chassis feb". +# Only do this routine once. sub ShowChassisSCB { print STDERR " In ShowChassisSCB: $_" if ($debug); @@ -251,20 +288,50 @@ sub ShowChassisSCB { tr/\015//d; last if(/^$prompt/); + return if ($ShowChassisSCB); /error: the chassis subsystem is not running/ && return; - / Temperature:/ && next; - / utilization:/ && next; - /time:/ && next; + /Couldn\'t initiate connection/ && return; + /Unrecognized command/ && return; + /command is not valid/ && return; + /^\s+\^/ && return; + /syntax error/ && return; + / Temperature/ && next; + / utilization/ && next; + / Start time/ && next; + / Uptime/ && next; / (IP|MLPS) routes:/ && next; / used:/ && next; ProcessHistory("","","","# $_"); } + $ShowChassisSCB = 1; + return; +} + +# This routine parses "show system boot-messages" +sub ShowSystemBootMessages { + print STDERR " In ShowSystemBootMessages: $_" if ($debug); + + s/^[a-z]+@//; + ProcessHistory("","","","# $_"); + while () { + tr/\015//d; + last if(/^$prompt/); + + /Unrecognized command/ && return; + /^\s+\^/ && return; + /syntax error/ && return; + /^JUNOS / && && next; + /^real memory / && next; + /^avail memory / && next; + /^\/dev\// && next; + ProcessHistory("","","","# $_"); + } return; } # This routine parses "show version" sub ShowVersion { - print STDERR " In ShowVersionAndBlame: $_" if ($debug); + print STDERR " In ShowVersion: $_" if ($debug); s/^[a-z]+@//; ProcessHistory("","","","# $_"); @@ -272,7 +339,7 @@ sub ShowVersion { tr/\015//d; last if(/^$prompt/); - /^Juniper Networks is:/ && ProcessHistory("","","","\n$_") && return; + /^Juniper Networks is:/ && ProcessHistory("","","","# \n# $_") && next; ProcessHistory("","","","# $_"); } return; @@ -290,8 +357,10 @@ sub ShowConfiguration { next if (/^\s*$/); /^database header mismatch: / && return(-1); + /^version .*;\d+$/ && return(-1); s/(\s*authentication-key ).*$/#$1;/; s/^(.*\ssecret \")\$9\$.*(\".*)$/#$1$2/; + s/ # SECRET-DATA$//; ProcessHistory("","","","$_"); } return; @@ -313,6 +382,9 @@ sub DoNothing {print STDOUT;} "show chassis hardware" => "ShowChassisHardware", "show chassis routing-engine" => "ShowChassisRoutingEngine", "show chassis scb" => "ShowChassisSCB", + "show chassis ssb" => "ShowChassisSCB", + "show chassis feb" => "ShowChassisSCB", + "show system boot-messages" => "ShowSystemBootMessages", "show version" => "ShowVersion", "show configuration" => "ShowConfiguration" ); @@ -324,6 +396,9 @@ sub DoNothing {print STDOUT;} "show chassis hardware", "show chassis routing-engine", "show chassis scb", + "show chassis ssb", + "show chassis feb", + "show system boot-messages", "show version", "show configuration" ); @@ -352,7 +427,8 @@ if ($file) { } } -while() { + +TOP: while() { tr/\015//d; if (/^Error:/) { s/^.*Error:/Error:/; @@ -374,13 +450,13 @@ while() { if (! defined($commands{$cmd})) { print STDERR "found unexpected command - \"$cmd\"\n"; $clean_run = 0; - last; + last TOP; } $rval = &{$commands{$cmd}}; delete($commands{$cmd}); if ($rval == -1) { $clean_run = 0; - last; + last TOP; } } if (/>\s*quit/) { diff --git a/bin/rancid b/bin/rancid index cd0cb8c..f283c7f 100755 --- a/bin/rancid +++ b/bin/rancid @@ -177,6 +177,10 @@ sub ShowVersion { $type = "AGS"; } elsif ( $1 eq "CSC4") { $type = "AGS+"; + } elsif ( $1 eq "2511" || $1 eq "2524" || $1 eq "AS2511-RJ") { + $type = "2500"; + } elsif ( $1 eq "3620" || $1 eq "3640") { + $type = "3600"; } elsif ( $1 eq "RSP7000") { $type = "7500"; } elsif ( $1 =~ /RSP\d/) { @@ -185,9 +189,9 @@ sub ShowVersion { $type = "7000"; } elsif ( $1 eq "RP") { $type = "7000"; - } elsif ( $1 eq "7202" || $1 eq "7204" || $1 eq "7206") { + } elsif ( $1 =~ /720[246]/) { $type = "7200"; - } elsif ($1 eq "12004/GRP" || $1 eq "12008/GRP" || $1 eq "12012/GRP") { + } elsif ($1 =~ /1200[48]\/GRP/ || $1 =~ /1201[26]\/GRP/) { $type = "12000"; } else { $type = $1; @@ -229,6 +233,16 @@ sub ShowVersion { "!Memory: pcmcia $2 slot$3 $1\n"); next; } + if(/^WARNING/) { + if (!defined($I0)) { + $I0=1; + ProcessHistory("COMMENTS","keysort","I0","!\n"); + } + ProcessHistory("COMMENTS","keysort","I1","! $_"); + # The line after the WARNING is what to do about it. + $_ = ; tr/\015//d; + ProcessHistory("COMMENTS","keysort","I1","! $_"); + } if (/^Configuration register is (.*)$/) { $config_register=$1; next; @@ -237,9 +251,24 @@ sub ShowVersion { return(0); } +# This routine parses "show install active" +sub ShowInstallActive { + print STDERR " In ShowInstallActive: $_" if ($debug); + + while () { + tr/\015//d; + last if (/^$prompt/); + next if (/^(\s*|\s*$cmd\s*)$/); + return(1) if /^\s*\^\s*$/; + return(1) if /Invalid input detected/; + ProcessHistory("COMMENTS","keysort","F5","!Image: $_") && next; + } + return(0); +} + # This routine parses "show env all" sub ShowEnv { - # Skip if this is not a 7500 or 7000. + # Skip if this is not a 7500, 7200, or 7000. print STDERR " In ShowEnv: $_" if ($debug); while () { @@ -306,7 +335,8 @@ sub ShowGSR { # This routine parses "show boot" sub ShowBoot { - # Pick up boot variables if 7000/7500/12000; otherwise pick up bootflash. + # Pick up boot variables if 7000/7200/7500/12000; + # otherwise pick up bootflash. print STDERR " In ShowBoot: $_" if ($debug); while () { @@ -321,7 +351,7 @@ sub ShowBoot { if (!defined($H0)) { $H0=1; ProcessHistory("COMMENTS","keysort","H0","!\n"); } - if ($type !~ /^(1200|7)/) { + if ($type !~ /^(120|7)/) { ProcessHistory("COMMENTS","keysort","H2","!BootFlash: $_"); } elsif (/variable/) { ProcessHistory("COMMENTS","keysort","H1","!Variable: $_"); @@ -340,7 +370,7 @@ sub ShowFlash { tr/\015//d; last if (/^$prompt/); next if (/^(\s*|\s*$cmd\s*)$/); - return(1) if ($type =~ /^(1200|7.0)/); + return(1) if ($type =~ /^(120|7)/); return(1) if /^\s*\^\s*$/; return(1) if /Invalid input detected/; ProcessHistory("FLASH","","","!Flash: $_"); @@ -360,9 +390,10 @@ sub DirSlotN { tr/\015//d; last if (/^$prompt/); next if (/^(\s*|\s*$cmd\s*)$/); - return(1) if ($type !~ /^(1200|7.0|36.0)/); + return(1) if ($type !~ /^(120|7|36)/); return(1) if /^\s*\^\s*$/; return(1) if /Invalid input detected/; + return(1) if /\%Error: No such file or directory/; return(1) if /No space information available/; return(-1) if /\%Error calling/; return(-1) if /: device being squeezed/; # Flash is busy @@ -382,7 +413,7 @@ sub ShowContAll { tr/\015//d; last if (/^$prompt/); next if (/^(\s*|\s*$cmd\s*)$/); - return(1) if ($type =~ /^(1200|7[05])/); + return(1) if ($type =~ /^(120|7[05])/); if (/^Interface ([^ \n(]*)/) { $INT = "$1, "; next; } /^(BRI unit \d)/ && ProcessHistory("INT","","","!Interface: $1\n") && next; @@ -393,10 +424,15 @@ sub ShowContAll { /(Media Type is \S+),/ && ProcessHistory("INT","","","!\t$1\n"); if (/(M\dT:) show controller:$/) { + my($ctlr) = $1; + $_ = ; tr/\015//d; s/ subunit \d,//; + ProcessHistory("INT","","","!Interface: $ctlr $_"); + } + if (/^(\S+) : show controller:$/) { my($ctlr) = $1; $_ = ; tr/\015//d; s/ subunit \d,//; - ProcessHistory("INT","","","!Interface: $ctlr $_"); - } + ProcessHistory("INT","","","!Interface: $ctlr: $_"); + } /^(HD unit \d), idb/ && ProcessHistory("INT","","","!Interface: $1\n") && next; /^HD unit \d, NIM/ && @@ -553,17 +589,17 @@ sub ShowDiagbus { return(0); } -# This routine parses "show diags" for the gsr, 7200, 3600 +# This routine parses "show diag" for the gsr, 7200, 3600, 2600. # This will create arrarys for hw info. -sub ShowDiags { +sub ShowDiag { # Skip if this is not a 12000. - print STDERR " In ShowDiags: $_" if ($debug); + print STDERR " In ShowDiag: $_" if ($debug); while () { tr/\015//d; last if (/^$prompt/); next if (/^(\s*|\s*$cmd\s*)$/); - return(1) if ($type !~ /^(1200|720|36.0)/); + return(1) if ($type !~ /^(120|720|36|26)/); /^$/ && next; if (!defined($showdiags)) {$showdiags=1; ProcessHistory("SLOT","","","!\n");} s/Port Packet Over SONET/POS/; @@ -606,9 +642,11 @@ sub ShowDiags { next; } # 7200 and 3600 stuff - if (/^(Slot) (\d+):/ || /^\s+(WIC) Slot (\d):/) { + if (/^(Slot)\s+(\d+):/ || /^\s+(WIC|VIC) Slot (\d):/) { if ($1 eq "WIC") { $WIC = "/$2"; + } elsif ($1 eq "VIC") { + $WIC = "/$2"; } else { $slot = $2; undef($WIC); @@ -620,8 +658,11 @@ sub ShowDiags { s/ with MII or RJ45/-TX/; s/Fast-ethernet /100Base/; s/[)(]//g; - /\s+(.*)\s+port adapter?,\s+(\d+)\s+/ && + /\s+(.*) port adapter,?\s+(\d+)\s+/i && ProcessHistory("SLOT","","","!Slot $slot: type $1, $2 ports\n"); + # I/O controller with no interfaces + /\s+(.*)\s+port adapter\s*$/i && + ProcessHistory("SLOT","","","!Slot $slot: type $1, 0 ports\n"); /\s+(.*)\s+daughter card(.*)$/ && ProcessHistory("SLOT","","","!Slot $slot$WIC: type $1$2\n"); /\s+(FT1)$/ && @@ -659,7 +700,7 @@ sub ShowC7200 { ProcessHistory("SLOT","","","!Slot Midplane: part $2, serial $1\n!\n"); next; } - if (/C7200 CPU EEPROM:/) { + if (/C720\d(VXR)? CPU EEPROM:/) { $_ = ; /revision\s+(\S+).*revision\s+(\S+)/ && ProcessHistory("SLOT","","","!Slot CPU: hvers $1 rev $2\n"); @@ -700,25 +741,50 @@ sub WriteTerm { /^ntp clock-period / && next; # kill ntp clock-period /^ length / && next; # kill length on serial lines /^ width / && next; # kill width on serial lines + /^ clockrate / && next; # kill clockrate on serial interfaces /^enable password / && ProcessHistory("ENABLE","","","!enable password \n") && next; - /^(username .*) password \d *(\S)\s*(.*)/ && + /^(username .*) password /&& ProcessHistory("USER","","","!$1 password \n") && next; /^\s*password / && ProcessHistory("LINE-PASS","","","! password \n") && next; /^\s*neighbor (\S*) password / && ProcessHistory("","","","! neighbor $1 password \n") && next; + /^(ip ftp password) / && + ProcessHistory("","","","!$1 \n") && next; + /^( ip ospf authentication-key) / && + ProcessHistory("","","","!$1 \n") && next; + /^( ip ospf message-digest-key \d+ md5) / && + ProcessHistory("","","","!$1 \n") && next; /fair-queue individual-limit/ && next; + # sort ip explicit-paths. + if (/^ip explicit-path name (\S+)/) { + my($key) = $1; + my($expath) = $_; + while () { + tr/\015//d; + last if (/^$prompt/); + last if (/^$prompt/ || ! /^(ip explicit-path name |[ !])/); + if (/^ip explicit-path name (\S+)/) { + ProcessHistory("EXPATH","keysort","$key","$expath"); + $key = $1; + $expath = $_; + } else { + $expath .= $_; + } + } + ProcessHistory("EXPATH","keysort","$key","$expath"); + } # sort route-maps - if (/^route-map ([^ ]+)/) { + if (/^route-map (\S+)/) { my($key) = $1; my($routemap) = $_; while () { tr/\015//d; last if (/^$prompt/ || ! /^(route-map |[ !])/); - if (/^route-map ([^ ]+)/) { + if (/^route-map (\S+)/) { ProcessHistory("ROUTEMAP","keysort","$key","$routemap"); $key = $1; $routemap = $_; @@ -804,6 +870,7 @@ sub DoNothing {print STDOUT;} # Main %commands=( 'show version' => "ShowVersion", + 'show install active' => "ShowInstallActive", 'show env all' => "ShowEnv", 'show gsr chassis' => "ShowGSR", 'show boot' => "ShowBoot", @@ -817,7 +884,7 @@ sub DoNothing {print STDOUT;} 'show controllers' => "ShowContAll", 'show controllers cbus' => "ShowContCbus", 'show diagbus' => "ShowDiagbus", - 'show diag' => "ShowDiags", + 'show diag' => "ShowDiag", 'show c7200' => "ShowC7200", 'write term' => "WriteTerm" ); @@ -825,6 +892,7 @@ sub DoNothing {print STDOUT;} # cmds is important (show version first and write term last). pita @commands=( "show version", + "show install active", "show env all", "show gsr chassis", "show boot", @@ -868,7 +936,7 @@ if ($file) { ProcessHistory("COMMENTS","keysort","B0","!\n"); ProcessHistory("COMMENTS","keysort","F0","!\n"); ProcessHistory("COMMENTS","keysort","G0","!\n"); -while() { +TOP: while() { tr/\015//d; if (/\#exit$/) { $clean_run=1; @@ -888,13 +956,13 @@ while() { if (! defined($commands{$cmd})) { print STDERR "found unexpected command - \"$cmd\"\n"; $clean_run = 0; - last; + last TOP; } $rval = &{$commands{$cmd}}; delete($commands{$cmd}); if ($rval == -1) { $clean_run = 0; - last; + last TOP; } } } diff --git a/bin/rancid-fe b/bin/rancid-fe index 8dab8a5..f85d235 100755 --- a/bin/rancid-fe +++ b/bin/rancid-fe @@ -27,6 +27,8 @@ require 5; if ($vendor =~ /^cisco$/i) { exec('rancid', $router); +} elsif ($vendor =~ /^cat5$/i) { + exec('cat5rancid', $router); } elsif ($vendor =~ /^juniper$/i) { exec('jrancid', $router); } elsif ($vendor =~ /^foundry$/i) { diff --git a/cloginrc.sample b/cloginrc.sample index 6b8f133..b8368a2 100644 --- a/cloginrc.sample +++ b/cloginrc.sample @@ -2,10 +2,15 @@ # currently clogin supports a number of add directives: # password # user +# userprompt # userpassword +# passprompt +# noenable # enauser +# enableprompt # autoenable # cyphertype +# identity # # Details on each of these follows. # @@ -14,10 +19,25 @@ # add user # The default user is $USER (i.e.: the user running clogin). # +# add userprompt +# What the router prints to prompt for the username. +# Default: "(Username|login):" +# # add userpassword # The password for user if different than the password set # using 'add password'. # +# add passprompt +# What the router prints to prompt for the password. +# Default: "\[Pp]assword:" +# +# add noenable +# equivalent of -noenable on the cmd line to not enable at login. +# +# add enableprompt +# What the router prints to prompt for the enable password. +# Default: "\[Pp]assword:" +# # add enauser # This is only needed if enable asks for a username and this # username is different from what user is set to. @@ -28,6 +48,11 @@ # add cyphertype # Default is 3des. # +# add identity +# Default is your default ssh identity. +# +# +# Note: The first match for a hostname takes precedence. #add password sl-bb*-dc cow24 #add password sl-gw*-dc geeks @@ -57,8 +82,17 @@ add password *.custy.net {vector} {victor} add user *.custz.net shirley add password *.custz.net {jive} {surely} +# the route-server's do provide enable access. cmdline -noenable equivalent. +add noenable route-server* 1 + # all our routers, ie: everything else add password * {clearance} {clarence} # set ssh encryption type, dflt: 3des add cyphertype * {3des} + +# set the username prompt to "router login:" +#add userprompt * router login: + +# ssh identity for a juniper; used with jlogin +add identity my.juniper $env(HOME)/.ssh/juniper diff --git a/util/README b/util/README new file mode 100644 index 0000000..5a4005c --- /dev/null +++ b/util/README @@ -0,0 +1,6 @@ +rancid/util includes some utilities that either don't seem to belong +in rancid/bin (ie: not part of the core pkg), contributed sources, or +sources included for convenience. + +README - This file. +rtrfilter - mail filter for diffs diff --git a/util/rtrfilter b/util/rtrfilter new file mode 100755 index 0000000..d57f565 --- /dev/null +++ b/util/rtrfilter @@ -0,0 +1,145 @@ +#!/usr/local/bin/perl +## +# rtrtfilter - "| rtrfilter -x -i -f \ +# -u -s " +# expects to read an email message on stdin containing a diff from +# rancid and emails a filtered copy to with the subject of the +# original msg or the contents of -s . the perl regex(es) specified +# via -x or -i (exclusive and inclusive, respectively) are applied to the +# router names (ie: files) from the "Index:" of the diff o/p. alternatively, +# the regex's may be specified in -f in the form: +# # comment +# x +# # comment +# i +# do not include /'s in the regex's. +# eg: +# #i inc1 +# i a0[12]\. +# i a0[34]\. +# # comment +# x router\.db +# x ^r0[0-9] +# #i foo +# +# exclusion takes precedence and defaults to nothing. inclusion defaults to +# everything. +# +# this program require the Mail::Mailer module which can be found on CPAN. +## +BEGIN { +$me = $0; +$me =~ s/.*\/(\S+)$/$1/; +} + +require 'newgetopt.pl'; +use Mail::Mailer; + +# process command line options +$newgetopt'ignorecase=0; +$newgetopt'autoabbrev=1; +$result = &NGetOpt('h','x=s@','i=s@','f=s','s=s'); +&usage($result) if (defined($opt_h) || $result == 0); + +if ($#ARGV < 0) { + usage; +} +my($rcpts) = join(',', @ARGV); + +# if specified, read the regex file and append to @opt_i / @opt_x +if (defined($opt_f)) { + open(FILE, "< $opt_f") || die "Cant open the regex file $opt_f: $!"; + + while () { + next if (! /^(i|x)\s+(.*$)/); + #/(i|x)\s+(.*)$/; + if ($1 eq "i" ) { + push(@opt_i, $2); + } else { + push(@opt_x, $2); + } + } + close(FILE); +} + +# read the header, grok the subject line +my($subject, $from); +while () { + last if (/^$/); + if (s/^from: //i) { + chomp; + $from = $_; + } + if (s/^subject: //i) { + chomp; + $subject = $_; + } +} +if (defined($opt_s)) { $subject = $opt_s;} +if (defined($opt_u)) { $from = $opt_u;} + +# filter the remainder of the mail. save mail in memory to avoid empty msgs +my(@mail); +my($skip) = 1; +while () { + # look for /^Index: ", the filtering key + if (/^Index: (.*)$/) { + # strip the directory before passing to filter() + my($line) = ($1 =~ /.*\/([^\/\s]*)$/); + $skip = filter($line); + } + + next if ($skip); + + push(@mail, $_); +} + +# send mail, if any +if ($#mail < 0) { exit; } +$mailer = new Mail::Mailer 'sendmail', ('-t'); +$headers{From} = $from; +$headers{"Reply-To"} = $from; +$headers{"Errors-To"} = $from; +$headers{Subject} = $subject; +$headers{To} = $rcpts; +$headers{Precedence} = "bulk"; + +$mailer->open(\%headers); +print $mailer @mail; +$mailer->close; + +exit; + +# filter $line inclusive/exclusive (0 / 1) +sub filter { + my($line) = shift; + + # exclusion + if (defined(@opt_x)) { + foreach $regex (@opt_x) { + if ($line =~ /$regex/) { return(1); } + } + } + + # inclusion / default inclusion + if (! @opt_i) { return(0); } + foreach $regex (@opt_i) { + if ($line =~ /$regex/) { return(0); } + } + + # inclusion regex specified, but fall through + return(1); +} + +sub usage { + print STDERR <] [-x ] [-f ] [-u [-s ] [ ...] + -h prints this message + -f file containing perl regex matching router names (mind the cwd()) + -i perl regex matching router names (inclusive) + -u From: address + -s mail subject + -x perl regex matching router names (exclusive) +USAGE + exit $_; +} diff --git a/util/rtrfilter.README b/util/rtrfilter.README new file mode 100644 index 0000000..51ca6e9 --- /dev/null +++ b/util/rtrfilter.README @@ -0,0 +1,14 @@ +rtrfilter can be user to filter rancid diffs to avoid sending unwanted +diffs to certain recipient(s) or diffs which those recipient(s) should +not see without the need to create a separate group(s). + +/etc/aliases eg: + +rancid-foo: engineering, + customer-ops-foo +# +# only send diffs for the SJC routers (aka. *.sjc.shrubbery.net) +customer-ops-foo: "| /home/rancid/util/rtrfilter -i '\.sjc\.' -s 'sjc router diffs' customer-ops" +# +customer-ops: jimbob, + sally -- cgit