summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTar Committer <tar@ocjtech.us>2000-08-23 20:13:31 +0000
committerTar Committer <tar@ocjtech.us>2000-08-23 20:13:31 +0000
commita505626101e262be2cd5a8c74c44d3616c299519 (patch)
treefaa740207faec239fa2af636b194c924eaf3573f
parentb24aa5051db5d4bf9757efe7df06cb1892898382 (diff)
downloadrancid-a505626101e262be2cd5a8c74c44d3616c299519.tar.gz
rancid-a505626101e262be2cd5a8c74c44d3616c299519.tar.xz
rancid-a505626101e262be2cd5a8c74c44d3616c299519.zip
Imported from rancid-1.5.tar.gz.rancid-1.5
-rw-r--r--CHANGES58
-rw-r--r--README48
-rw-r--r--Todo17
-rwxr-xr-xbin/cat5rancid996
-rwxr-xr-xbin/clogin118
-rwxr-xr-xbin/control_rancid29
-rwxr-xr-xbin/do-diffs19
-rw-r--r--bin/env5
-rwxr-xr-xbin/flogin4
-rwxr-xr-xbin/francid9
-rwxr-xr-xbin/jlogin192
-rwxr-xr-xbin/jrancid104
-rwxr-xr-xbin/rancid116
-rwxr-xr-xbin/rancid-fe2
-rw-r--r--cloginrc.sample34
-rw-r--r--util/README6
-rwxr-xr-xutil/rtrfilter145
-rw-r--r--util/rtrfilter.README14
18 files changed, 1738 insertions, 178 deletions
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 <basedir>
@@ -40,11 +53,14 @@ Quick Installation Guide (an example):
2) mkdir <basedir>/bin
-3) Put the contents of rancid/bin in <basedir>/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 <basedir>/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 <basedir>/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 [<GROUP>]). 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 (<INPUT>) {
+ 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.
+ $_ = <INPUT>; 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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;
+ $_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
+ ProcessHistory("INT","","","!Interface: $ctlr $_");
+ }
+ if (/^(\S+) : show controller:$/) {
+ my($ctlr) = $1;
+ $_ = <INPUT>; 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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;
+ $_ = <INPUT>;
+ /^\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";
+ $_ = <INPUT>;
+ /^\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;
+ $_ = <INPUT>;
+ /^\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);
+ }
+ $_ = <INPUT>; 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 (<INPUT>) {
+ tr/\015//d;
+ last if (/^$prompt/);
+ next if (/^(\s*|\s*$cmd\s*)$/);
+ return(1) if ($type !~ /^72/);
+ /^$/ && next;
+ if (/C7200 Midplane EEPROM:/) {
+ $_ = <INPUT>;
+ /revision\s+(\S+).*revision\s+(\S+)/;
+ ProcessHistory("SLOT","","","!Slot Midplane: hvers $1 rev $2\n");
+ $_ = <INPUT>;
+ /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:/) {
+ $_ = <INPUT>;
+ /revision\s+(\S+).*revision\s+(\S+)/ &&
+ ProcessHistory("SLOT","","","!Slot CPU: hvers $1 rev $2\n");
+ $_ = <INPUT>;
+ /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 (<INPUT>) {
+ 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 (<INPUT>) {
+ 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 <removed>\n") &&
+ next;
+ /^(username .*) password /&&
+ ProcessHistory("USER","","","!$1 password <removed>\n") && next;
+ /^\s*password / &&
+ ProcessHistory("LINE-PASS","","","! password <removed>\n") && next;
+ /^\s*neighbor (\S*) password / &&
+ ProcessHistory("","","","! neighbor $1 password <removed>\n") &&
+ next;
+ /^(ip ftp password) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
+ /^( ip ospf authentication-key) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
+ /^( ip ospf message-digest-key \d+ md5) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
+ /fair-queue individual-limit/ && next;
+ # sort ip explicit-paths.
+ if (/^ip explicit-path name (\S+)/) {
+ my($key) = $1;
+ my($expath) = $_;
+ while (<INPUT>) {
+ 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 (<INPUT>) {
+ 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<removed>\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 <removed>\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 </dev/null > $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 </dev/null |") || die "clogin failed for $host: $!\n";
+ }
+}
+
+ProcessHistory("COMMENTS","keysort","B0","!\n");
+ProcessHistory("COMMENTS","keysort","F0","!\n");
+ProcessHistory("COMMENTS","keysort","G0","!\n");
+TOP: while(<INPUT>) {
+ 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 <<END
-The following routers have not been successfully contacted within the
-last 24 hours.
+The following routers have not been successfully contacted for more
+than $OLDTIME hours.
END
cat $DIR/routers.failed
diff --git a/bin/do-diffs b/bin/do-diffs
index 88cc445..fa4acd2 100755
--- a/bin/do-diffs
+++ b/bin/do-diffs
@@ -21,7 +21,7 @@ fi
for GROUP in $LIST_OF_GROUPS
do
- LOCKFILE=/tmp/.$GROUP.run.lock
+ LOCKFILE=$TMPDIR/.$GROUP.run.lock
(
echo starting: `date`
@@ -31,6 +31,23 @@ do
then
echo hourly config diffs failed: $LOCKFILE exists
/bin/ls -l $LOCKFILE
+
+ # Send email if the lock file is old.
+ if [ "X$LOCKTIME" = "X" ] ; then
+ LOCKTIME=4
+ fi
+ perl -e "\$t = (stat(\"$LOCKFILE\"))[9]; print \"OLD\\n\" if (time() - \$t >= $LOCKTIME*60*60);" > $TMPDIR/.$GROUP.old
+ if [ -s $TMPDIR/.$GROUP.old ]
+ then
+ (
+ cat <<END
+rancid $GROUP hung on `hostname`? Old lockfile still exists:
+`/bin/ls -l $LOCKFILE`
+END
+ ) | Mail -s "rancid hung - $GROUP" rancid-admin-$GROUP
+ fi
+ rm -f $TMPDIR/.$GROUP.old
+
else
/usr/bin/touch $LOCKFILE
control_rancid $GROUP
diff --git a/bin/env b/bin/env
index 236ceca..85aa309 100644
--- a/bin/env
+++ b/bin/env
@@ -12,6 +12,7 @@ TERM=network;export TERM
# use a full path (no sym-links) for BASEDIR. some versions of CVS seemingly
# don't take kindly to sym-links.
#
+TMPDIR=/tmp; export TMPDIR
BASEDIR=$HOME/rancid; export BASEDIR
PATH=$BASEDIR/bin:/usr/local/bin:/usr/ucb:/usr/bin:/bin:/usr/lib:/usr/sbin;export PATH
CVSROOT=$BASEDIR/CVS; export CVSROOT
@@ -20,6 +21,10 @@ CVSROOT=$BASEDIR/CVS; export CVSROOT
# collection from the router(s).
#NOPIPE=YES; export NOPIPE
#
+# How many hours to go by before complaining about routers that
+# can not be reached.
+OLDTIME=4; export OLDTIME
+#
LIST_OF_GROUPS="sl joebobisp"
#
# For each group, define a list of people to receive the diffs.
diff --git a/bin/flogin b/bin/flogin
index b168762..652e38a 100755
--- a/bin/flogin
+++ b/bin/flogin
@@ -262,7 +262,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 {
@@ -270,7 +270,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 {
diff --git a/bin/francid b/bin/francid
index 3050aee..54aae8d 100755
--- a/bin/francid
+++ b/bin/francid
@@ -30,7 +30,7 @@ $file = $opt_f;
$host = $ARGV[0];
$clean_run = 0;
$found_end = 0;
-$timeo = 90; # clogin timeout in seconds
+$timeo = 90; # flogin timeout in seconds
# This routine is used to print out the router configuration
sub ProcessHistory {
@@ -172,6 +172,7 @@ sub ShowChassis {
tr/\015//d;
last if (/^$prompt/);
next if (/ from /);
+ next if (/current temperature/i);
ProcessHistory("CHASSIS","","","! $_");
}
ProcessHistory("CHASSIS","","","!\n");
@@ -327,7 +328,7 @@ if ($file) {
}
}
-while(<INPUT>) {
+TOP: while(<INPUT>) {
tr/\015//d;
if (/\#exit$/) {
$clean_run=1;
@@ -347,13 +348,13 @@ while(<INPUT>) {
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 (<INPUT>) {
+ tr/\015//d;
+ last if(/^$prompt/);
+
+ /Unrecognized command/ && return;
+ /^\s+\^/ && return;
+ /syntax error/ && return;
+ /^JUNOS / && <INPUT> && 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<removed>;/;
s/^(.*\ssecret \")\$9\$.*(\".*)$/#$1<removed>$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(<INPUT>) {
+
+TOP: while(<INPUT>) {
tr/\015//d;
if (/^Error:/) {
s/^.*Error:/Error:/;
@@ -374,13 +450,13 @@ while(<INPUT>) {
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.
+ $_ = <INPUT>; 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 (<INPUT>) {
+ 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 (<INPUT>) {
@@ -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 (<INPUT>) {
@@ -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;
+ $_ = <INPUT>; tr/\015//d; s/ subunit \d,//;
+ ProcessHistory("INT","","","!Interface: $ctlr $_");
+ }
+ if (/^(\S+) : show controller:$/) {
my($ctlr) = $1;
$_ = <INPUT>; 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 (<INPUT>) {
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:/) {
$_ = <INPUT>;
/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 <removed>\n") &&
next;
- /^(username .*) password \d *(\S)\s*(.*)/ &&
+ /^(username .*) password /&&
ProcessHistory("USER","","","!$1 password <removed>\n") && next;
/^\s*password / &&
ProcessHistory("LINE-PASS","","","! password <removed>\n") && next;
/^\s*neighbor (\S*) password / &&
ProcessHistory("","","","! neighbor $1 password <removed>\n") &&
next;
+ /^(ip ftp password) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
+ /^( ip ospf authentication-key) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
+ /^( ip ospf message-digest-key \d+ md5) / &&
+ ProcessHistory("","","","!$1 <removed>\n") && next;
/fair-queue individual-limit/ && next;
+ # sort ip explicit-paths.
+ if (/^ip explicit-path name (\S+)/) {
+ my($key) = $1;
+ my($expath) = $_;
+ while (<INPUT>) {
+ 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 (<INPUT>) {
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(<INPUT>) {
+TOP: while(<INPUT>) {
tr/\015//d;
if (/\#exit$/) {
$clean_run=1;
@@ -888,13 +956,13 @@ while(<INPUT>) {
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 <router name regexp> <username>
# The default user is $USER (i.e.: the user running clogin).
#
+# add userprompt <router name regexp> <username prompt>
+# What the router prints to prompt for the username.
+# Default: "(Username|login):"
+#
# add userpassword <router name regexp> <user password>
# The password for user if different than the password set
# using 'add password'.
#
+# add passprompt <router name regexp> <password prompt>
+# What the router prints to prompt for the password.
+# Default: "\[Pp]assword:"
+#
+# add noenable <router name regexp>
+# equivalent of -noenable on the cmd line to not enable at login.
+#
+# add enableprompt <router name regexp> <enable prompt>
+# What the router prints to prompt for the enable password.
+# Default: "\[Pp]assword:"
+#
# add enauser <router name regexp> <username>
# 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 <router name regexp> <ssh encryption type>
# Default is 3des.
#
+# add identity <router name regexp> <path to ssh identity file>
+# 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 <perl regex> -i <perl regex> -f <regex file> \
+# -u <From address> -s <subject> <rcpts>"
+# expects to read an email message on stdin containing a diff from
+# rancid and emails a filtered copy to <rcpts> with the subject of the
+# original msg or the contents of -s <subject>. 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 <regex file> in the form:
+# # comment
+# x <regex>
+# # comment
+# i <regex>
+# 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 (<FILE>) {
+ 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 (<STDIN>) {
+ 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 (<STDIN>) {
+ # 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 <<USAGE;
+usage: $me [-h] [-i <perl regex>] [-x <perl regex>] [-f <regex file>] [-u <From: address> [-s <subject>] <mail rcpt> [<rcpt> ...]
+ -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