rancid暂时用北电的刀片交换机用alteon的alogin脚本测试,登录不了。h3c的脚本从google group上下载一份下来先。出处:https://sites.google.com/site/jrbinks/code/rancid/h3ch3crancid点击(此处)折叠或打开#! /usr/bin/perl5#### $Id: $#### @PACKAGE@ @VERSION@## Copyright (c) 1997-2009 by Terrapin Communications, Inc.## All rights reserved.#### This code is derived from software contributed to and maintained by## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,## Pete Whiting, Austin Schutz, and Andrew Fort.#### Redistribution and use in source and binary forms, with or without## modification, are permitted provided that the following conditions## are met:## 1. Redistributions of source code must retain the above copyright## notice, this list of conditions and the following disclaimer.## 2. Redistributions in binary form must reproduce the above copyright## notice, this list of conditions and the following disclaimer in the## documentation and/or other materials provided with the distribution.## 3. All advertising materials mentioning features or use of this software## must display the following acknowledgement:## This product includes software developed by Terrapin Communications,## Inc. and its contributors for RANCID.## 4. Neither the name of Terrapin Communications, Inc. nor the names of its## contributors may be used to endorse or promote products derived from## this software without specific prior written permission.## 5. It is requested that non-binding fixes and modifications be contributed## back to Terrapin Communications, Inc.#### THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE## POSSIBILITY OF SUCH DAMAGE.## RANCID - Really Awesome New Cisco confIg Differ### h3crancid## h3clogin/h3crancid covers the following product ranges:## * 3Com SuperStack 4 (post-joint venture with Huawei)# * H3C# * HP Networking ('A' & some 'E' portfolio, post 2010 3Com acquitision)## They may also work with some Huawei equipment.## https://sites.google.com/site/jrbinks/code/rancid/h3c### Usage: h3crancid [-dV] [-l] [-f filename | hostname]## Notable changes from standard *rancid programs:## * abstracted to path to the 'tail' utility# * altered "cisco_cmds" to be "device_cmds"# * define and use $logincmd## TODO:## It may be useful to pull common subroutines like the sorting ones into# a library for use by all the *rancid programs.############################################################################## END-USER TWEAKS# The login program to use. If no path is given, $PATH is searched:#my $logincmd = "h3clogin99";my $logincmd = "h3clogin";#my $logincmd = "/usr/local/libexec/h3clogin";## Enable display of the FIB:my $display_fib = 1;## Enable display of the routing table:# (no-op)#my $display_iproutes = 1;# END OF END-USER TWEAKS#############################################################################use Getopt::Std;getopts('dflV');if ($opt_V) { print "@PACKAGE@ @VERSION@\n"; exit(0);}$log = $opt_l;$debug = $opt_d;$file = $opt_f;$host = $ARGV[0];$clean_run = 0;$found_end = 0;#$timeo = 90; # login command timeout in seconds$timeo = 20; # login command timeout in secondsmy $TAIL = "/usr/bin/tail";my(@commandtable, %commands, @commands);# command listsmy($aclsort) = ("ipsort"); # ACL sorting modemy($filter_commstr); # SNMP community string filteringmy($filter_pwds); # password filtering mode# This routine is used to print out the router configurationsub 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 routine 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 routine 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 routine 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 routine (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 addressessub 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);}sub filter_lines { my ($l) = (@_);# print STDERR " In filterlines: $l\n" if ($debug); # Filter out some ANSI crud as a result of us not being able to turn # off per-session terminal paging: # h3c: #s/^\033\[42D +\033\[42D(.+)$/$1/; # hwlogin+mods: #s/\033\133\064\062\104\s*\033\133\064\062\104//g; $l =~ s/\033\133\064\062\104\s+\033\133\064\062\104//g; $l =~ s/\033\133\061\066\104\s+\033\133\061\066\104//g; $l =~ s/\033\133\064\062\104//g; $l =~ s/\033\133\061\062\104//g; # Probably not needed: $l =~ s/\s*---- More ----\s*//;# print STDERR "out: $l\n" if ($debug); return $l;}sub DisplayFib { ## We should probably guarantee that the FIB is sorted properly print STDERR " In DisplayFib: $_" if ($debug); return(1) if $display_fib == 0; chomp; # Display the command we're processing in the output: s/^[\[]\a?\s?(.*)/\'$1\':/g; ProcessHistory("COMMENTS","keysort","A0","! $_\n!\n"); while () { tr/\015//d; last if(/^$prompt/); chomp; # skip blank lines return(1) if /^\s+\^$/; $_ = filter_lines($_); # make this a subroutine: return(1) if /% Too many parameters found at '\^' position/; return(1) if /% Unrecognized command found at '\^' position/; return(1) if /% Wrong parameter found at '\^' position/; return(1) if /% Wrong device .+/; # Chop out some detail that changes over time: s/(\s+)TimeStamp\s+/$1/; s/(\s+)t\[\d+\]\s+/$1/; ProcessHistory("COMMENTS","ipsort","A0","! $_\n"); #ProcessHistory("COMMENTS","keysort","A0","! $_\n"); } ProcessHistory("COMMENTS","keysort","A0","! \n"); return(0);}# This routine processes general output of "display" commandssub CommentOutput { print STDERR " In CommentOutput: $_" if ($debug); chomp; # Display the command we're processing in the output: s/^[\[].*?[\]>]\a?\s?(.*)/\'$1\':/g; ProcessHistory("","","","! $_\n!\n"); # eat the header line# $junk = ; # now just copy it mostly verbatim to the history file while () { tr/\015//d; # If we find the prompt, we're done #last if(/^.*>/); last if(/^$prompt/); chomp; # skip blank lines return(1) if /^\s+\^$/; $_ = filter_lines($_); # Filter out some ANSI crud as a result of us not being able to turn # off per-session terminal paging: # h3c: #s/^\033\[42D +\033\[42D(.+)$/$1/; # hwlogin+mods: #s/\033\133\064\062\104\s*\033\133\064\062\104//g;# s/\033\133\064\062\104\s+\033\133\064\062\104//g;# s/\033\133\061\066\104\s+\033\133\061\066\104//g; #s/\033\133\064\062\104//g; # Probably not needed:# s/\s*---- More ----\s*//; # make this a subroutine: # Some commands are not supported on some models or versions # of code. These lines simply remove the associated error # messages: return(1) if /% Too many parameters found at '\^' position/; return(1) if /% Unrecognized command found at '\^' position/; return(1) if /% Wrong parameter found at '\^' position/; return(1) if /% Wrong device .+/; # Parts of the output of some commands changes over time, but we don't # want these changes to trigger a rancid notification, so we # filter them out: # This is part of one line, just cut it out: s/\s+Current AccessNum:.+$//; if (/^(PSE Total Power Consumption|PSE Available Power|PSE Peak Value|PSE Average Value).+$/) { next; } s/(.*)uptime.*days*.*hours*.*minutes*(.*)/$1 $2/; ProcessHistory("","","","! $_\n"); } ProcessHistory("","","","! \n"); return(0);}## This routine processes a "display current"sub DisplayCurrent { print STDERR " In DisplayCurrent: $_" if ($debug); while () { tr/\015//d; last if(/^$prompt/); $_ = filter_lines($_); # Filter out some ANSI crud as a result of us not being able to turn # off per-session terminal paging: # h3c: #s/^\033\[42D +\033\[42D(.+)$/$1/; # hwlogin+mods: #s/\033\133\064\062\104\s*\033\133\064\062\104//g;# s/\033\133\064\062\104\s+\033\133\064\062\104//g;# s/\033\133\061\066\104\s+\033\133\061\066\104//g;# s/\033\133\064\062\104//g; # Probably not needed:# s/\s*---- More ----\s*//; # Filter out some sensitive data: if (/^( ?snmp-agent community (read|write) )(\S+)/ && $filter_commstr == 0) { ProcessHistory("","","","!$1$'"); next; } ProcessHistory("","","","$_"); # end of config if (/^return/) { $found_end = 1; return(0); } } return(0);}# dummy functionsub DoNothing {print STDOUT;}# Main## Not all commands are supported on all models and code versions## Not all of these should necessarily be included@commandtable = ( {'display version' => 'CommentOutput'}, {'display boot-loader' => 'CommentOutput'}, {'dir /all unit1>flash:/' => 'CommentOutput'}, {'dir /all unit2>flash:/' => 'CommentOutput'}, {'dir /all unit3>flash:/' => 'CommentOutput'}, {'dir /all unit4>flash:/' => 'CommentOutput'}, {'dir /all unit5>flash:/' => 'CommentOutput'}, {'dir /all unit6>flash:/' => 'CommentOutput'}, {'dir /all unit7>flash:/' => 'CommentOutput'}, {'dir /all unit8>flash:/' => 'CommentOutput'}, {'display device' => 'CommentOutput'}, {'display device manuinfo' => 'CommentOutput'}, {'display irf' => 'CommentOutput'}, {'display xrn-fabric' => 'CommentOutput'}, {'display ftm topology-database' => 'CommentOutput'}, {'display fan' => 'CommentOutput'}, {'display power' => 'CommentOutput'}, {'display poe powersupply' => 'CommentOutput'}, {'display poe temperature-protection' => 'CommentOutput'}, {'display cluster' => 'CommentOutput'}, {'display domain' => 'CommentOutput'}, {'display local-user' => 'CommentOutput'}, {'display password-control' => 'CommentOutput'}, {'display password-control super' => 'CommentOutput'}, {'display ssh server status' => 'CommentOutput'}, {'display fib' => 'DisplayFib'}, {'display vlan all' => 'CommentOutput'}, {'display ip routing-table' => 'CommentOutput'}, {'display lacp sys' => 'CommentOutput'}, {'display link-aggregation summary' => 'CommentOutput'}, {'display link-aggregation verbose' => 'CommentOutput'}, {'display mirror all' => 'CommentOutput'}, {'display current-configuration' => 'DisplayCurrent'},);# Use an array to preserve the order of the commands and a hash for mapping# commands to the subroutine and track commands that have been completed.@commands = map(keys(%$_), @commandtable);%commands = map(%$_, @commandtable);$device_cmds=join(";",@commands);$cmds_regexp=join("|",@commands);if (length($host) == 0) { if ($file) { print(STDERR "Too few arguments: file name required\n"); exit(1); } else { print(STDERR "Too few arguments: host name required\n"); exit(1); }}open(OUTPUT,">$host.new") || die "Can't open $host.new for writing: $!\n";select(OUTPUT);# make OUTPUT unbuffered if debuggingif ($debug) { $| = 1; }if ($file) { print STDERR "opening file $host\n" if ($debug); print STDOUT "opening file $host\n" if ($log); open(INPUT,"} else { print STDERR "executing $logincmd -t $timeo -c\"$device_cmds\" $host\n" if ($debug); print STDOUT "executing $logincmd -t $timeo -c\"$device_cmds\" $host\n" if ($log); if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) {# system "$logincmd -noenable -t $timeo -c \"$device_cmds\" $host $host.raw 2>&1" || die "$logincmd failed for $host: $!\n";# system "$logincmd -t $timeo -c \"$device_cmds\" $host $host.raw 2>&1" || die "$logincmd failed for $host: $!\n"; system "$logincmd -t $timeo -c \"$device_cmds\" $host $host.raw 2>&1" || die "$logincmd failed for $host: $!\n"; open(INPUT, " } else {# open(INPUT,"$logincmd -noenable -t $timeo -c \"$device_cmds\" $host open(INPUT,"$logincmd -t $timeo -c \"$device_cmds\" $host }}# determine ACL sorting modeif ($ENV{"ACLSORT"} =~ /no/i) { $aclsort = "";}# determine community string filtering modeif (defined($ENV{"NOCOMMSTR"}) && ($ENV{"NOCOMMSTR"} =~ /yes/i || $ENV{"NOCOMMSTR"} =~ /^$/)) { $filter_commstr = 1;} else { $filter_commstr = 0;}# determine password filtering modeif ($ENV{"FILTER_PWDS"} =~ /no/i) { $filter_pwds = 0;} elsif ($ENV{"FILTER_PWDS"} =~ /all/i) { $filter_pwds = 2;} else { $filter_pwds = 1;}ProcessHistory("","","","!RANCID-CONTENT-TYPE: h3c\n!\n");#ProcessHistory("COMMENTS","keysort","B0","!\n");#ProcessHistory("COMMENTS","keysort","D0","!\n");#ProcessHistory("COMMENTS","keysort","F0","!\n");#ProcessHistory("COMMENTS","keysort","G0","!\n");TOP: while() { tr/\015//d;# h3c:# Look for the command at the end of the output# if (/\#exit$/) {# if (/\#quit$/) {# h3c: if (/[\]>]\a?\s*quit/) {# if (/^[\[]\a?\s?quit/) { $clean_run=1; last; } if (/^Error:/) { print STDOUT ("$host $logincmd error: $_"); print STDERR ("$host $logincmd error: $_") if ($debug); $clean_run=0; last; }# while (/#\s*($cmds_regexp)\s*$/) {# h3c: while (/[\]>]\a?\s*($cmds_regexp)\s*$/) {# while (/^[\[]\a?\s*($cmds_regexp)\s*$/) { $cmd = $1; if (!defined($prompt)) { # h3c: # Extract the prompt: look for something not [ or # of the line, until either ] or > is reached: #$prompt = ($_ =~ /^([^#]+#)/)[0]; #$prompt =~ s/([][}{)(\\])/\\$1/g; #$prompt = ($_ =~ /^([^\]>]+[\]>]\007?)/)[0]; $prompt = ($_ =~ /^([^\]>]+[\]>]\a?)/)[0]; $prompt =~ s/([][}{)(\\])/\\$1/g; print STDERR ("PROMPT MATCH: $prompt\n") if ($debug); } print STDERR ("HIT COMMAND:$_") if ($debug); if (! defined($commands{$cmd})) { print STDERR "$host: 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 HistoryProcessHistory("","","","");# Cleanupclose(INPUT);close(OUTPUT);if (defined($ENV{NOPIPE}) && $ENV{NOPIPE} =~ /^YES/i) { unlink("$host.raw") if (! $debug);}printf(STDOUT "$host: clean_run=$clean_run found_end=$found_end\n") if ($debug);# check for completenessif (scalar(%commands) || !$clean_run || !$found_end) { if (scalar(%commands)) { printf(STDOUT "$host: missed cmd(s): %s\n", join(',', keys(%commands))); printf(STDERR "$host: missed cmd(s): %s\n", join(',h3clogin点击(此处)折叠或打开#! /usr/local/bin/expect --#### $Id: $#### @PACKAGE@ @VERSION@## Copyright (c) 1997-2009 by Terrapin Communications, Inc.## All rights reserved.#### This code is derived from software contributed to and maintained by## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,## Pete Whiting, Austin Schutz, and Andrew Fort.#### Redistribution and use in source and binary forms, with or without## modification, are permitted provided that the following conditions## are met:## 1. Redistributions of source code must retain the above copyright## notice, this list of conditions and the following disclaimer.## 2. Redistributions in binary form must reproduce the above copyright## notice, this list of conditions and the following disclaimer in the## documentation and/or other materials provided with the distribution.## 3. All advertising materials mentioning features or use of this software## must display the following acknowledgement:## This product includes software developed by Terrapin Communications,## Inc. and its contributors for RANCID.## 4. Neither the name of Terrapin Communications, Inc. nor the names of its## contributors may be used to endorse or promote products derived from## this software without specific prior written permission.## 5. It is requested that non-binding fixes and modifications be contributed## back to Terrapin Communications, Inc.#### THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE## POSSIBILITY OF SUCH DAMAGE.## The expect login scripts were based on Erik Sherk's gwtn, by permission.## h3clogin - H3C login## h3clogin covers the following product ranges:## * 3Com SuperStack 4 (post-joint venture with Huawei)# * H3C# * HP Networking ('A' & some 'E' portfolio, post 2010 3Com acquitision)## It may also work with some Huawei equipment.## Set to 1 to enable some debugging, or pass "-d" on command lineexp_internal 0# Usage lineset usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \\[-r passphrase\] \[-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# Password fileset password_file $env(HOME)/.cloginrc# Default is to login to the routerset do_command 0set do_script 0# The default is to automatically enableset avenable 1# The default is that you login non-enabled (tacacs can have you login already# enabled)set avautoenable 0# 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 1set do_enapasswd 1set do_saveconfig 0# Sometimes routers take awhile to answer (the default is 10 sec)set timeoutdflt 45#set send_human {.1 .3 .7 .05 2}# H3C: some models don't like "3des"; specify "aes128-cbc" in .cloginrcset default_cyphertype "3des"# H3C: command to elevate priviges (aka "enable") is differentset enacmd "super"# H3C: command to exit from deviceset exitcmd "quit"# Find the user in the ENV, or use the unix userid.if {[ info exists env(CISCO_USER) ] } { set default_user $env(CISCO_USER)} elseif {[ info exists env(USER) ]} { set default_user $env(USER)} elseif {[ info exists env(LOGNAME) ]} { set default_user $env(LOGNAME)} 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 - # unlike whoami or id -nu. if [ catch {exec id} reason ] { send_error "\nError: could not exec id: $reason\n" exit 1 } regexp {\(([^)]*)} "$reason" junk default_user}# Process the command linefor {set i 0} {$i set arg [lindex $argv $i] switch -glob -- $arg { # Expect debug mode -d* { exp_internal 1 # Username } -u* { if {! [ regexp .\[uU\](.+) $arg ignore user]} { incr i set username [ lindex $argv $i ] } # VTY Password } -p* { if {! [ regexp .\[pP\](.+) $arg ignore userpasswd]} { incr i set userpasswd [ lindex $argv $i ] } set do_passwd 0 # ssh passphrase } -r* { if {! [ regexp .\[rR\](.+) $arg ignore passphrase]} { incr i set vapassphrase [ lindex $argv $i ] } # VTY Password } -v* { if {! [ regexp .\[vV\](.+) $arg ignore passwd]} { incr i set passwd [ lindex $argv $i ] } set do_passwd 0 # Version string } -V* { send_user "@PACKAGE@ @VERSION@\n" exit 0 # Enable Username } -w* { if {! [ regexp .\[wW\](.+) $arg ignore enauser]} { incr i set enausername [ lindex $argv $i ] } # Environment variable to pass to -s scripts } -E* { if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} { set E$varname $varvalue } else { send_user "\nError: invalid format for -E in $arg\n" exit 1 } # Enable Password } -e* { if {! [ regexp .\[e\](.+) $arg ignore enapasswd]} { incr i set enapasswd [ lindex $argv $i ] } set do_enapasswd 0 # Command to run. } -c* { if {! [ regexp .\[cC\](.+) $arg ignore command]} { incr i set command [ lindex $argv $i ] } set do_command 1 # Expect script to run. } -s* { if {! [ regexp .\[sS\](.+) $arg ignore sfile]} { incr i set sfile [ lindex $argv $i ] } if { ! [ file readable $sfile ] } { send_user "\nError: Can't read $sfile\n" exit 1 } set do_script 1 # save config on exit } -S* { set do_saveconfig 1 # 'ssh -c' cypher type } -y* { if {! [ regexp .\[eE\](.+) $arg ignore cypher]} { incr i set cypher [ lindex $argv $i ] } # alternate cloginrc file } -f* { if {! [ regexp .\[fF\](.+) $arg ignore password_file]} { incr i set password_file [ lindex $argv $i ] } # Timeout } -t* { if {! [ regexp .\[tT\](.+) $arg ignore timeout]} { incr i set timeoutdflt [ lindex $argv $i ] } # Command file } -x* { if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} { incr i set cmd_file [ lindex $argv $i ] } if [ catch {set cmd_fd [open $cmd_file r]} reason ] { send_user "\nError: $reason\n" exit 1 } set cmd_text [read $cmd_fd] close $cmd_fd set command [join [split $cmd_text \n] \;] set do_command 1 # Do we enable? } -noenable { set avenable 0 # Does tacacs automatically enable us? } -autoenable { set avautoenable 1 set avenable 0 } -* { send_user "\nError: Unknown $arg\n" send_user $usage exit 1 } default { break } }}# Process routers...no routers listed is an error.if { $i == $argc } { send_user "\nError: $usage"}# Only be quiet if we are running a script (it can log its output# on its own)if { $do_script } { log_user 0} else { log_user 1}## Done configuration/variable setting. Now run with it...## Sets Xterm title if interactive...if its an xterm and the user caresproc label { host } { global env # if CLOGIN has an 'x' in it, don't set the xterm name/banner if [info exists env(CLOGIN)] { if {[string first "x" $env(CLOGIN)] != -1} { return } } # take host from ENV(TERM) if [info exists env(TERM)] { if [regexp \^(xterm|vs) $env(TERM) ignore ] { send_user "\033]1;[lindex [split $host "."] 0]\a" send_user "\033]2;$host\a" } }}# This is a helper function to make the password file easier to# maintain. Using this the password file has the form:# add password sl* pete cow# add password at* steve# add password * hanky-pieproc add {var args} { global int_$var ; lappend int_$var $args}proc include {args} { global env regsub -all "(^{|}$)" $args {} args if { [ regexp "^/" $args ignore ] == 0 } { set args $env(HOME)/$args } source_password_file $args}proc find {var router} { upvar int_$var list if { [info exists list] } { foreach line $list { if { [string match [lindex $line 0] $router ] } { return [lrange $line 1 end] } } } return {}}# Loads the password file. Note that as this file is tcl, and that# 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 scriptproc source_password_file { password_file } { global env if { ! [file exists $password_file] } { send_user "\nError: password file ($password_file) does not exist\n" exit 1 } file stat $password_file fileinfo if { [expr ($fileinfo(mode) & 007)] != 0000 } { send_user "\nError: $password_file must not be world readable/writable\n" exit 1 } if [ catch {source $password_file} reason ] { send_user "\nError: $reason\n" exit 1 }}# Log into the router.# returns: 0 on success, 1 on failure, -1 if rsh was used successfullyproc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } { global spawn_id in_proc do_command do_script platform passphrase global prompt prompt_match u_prompt p_prompt e_prompt sshcmd set in_proc 1 set uprompt_seen 0 # try each of the connection methods in $cmethod until one is successful set progs [llength $cmethod] foreach prog [lrange $cmethod 0 end] { incr progs -1 if [string match "telnet*" $prog] { regexp {telnet(:([^[:space:]]+))*} $prog command suffix port if {"$port" == ""} { set retval [ catch {spawn telnet $router} reason ] } else { set retval [ catch {spawn telnet $router $port} reason ] } if { $retval } { send_user "\nError: telnet failed: $reason\n" return 1 } } elseif [string match "ssh*" $prog] { # ssh to the router & try to login with or without an identfile. regexp {ssh(:([^[:space:]]+))*} $prog command suffix port set cmd [join [lindex $sshcmd 0] " "] if {"$port" != ""} { set cmd "$cmd -p $port" } if {"$identfile" != ""} { set cmd "$cmd -i $identfile" } set retval [ catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason ] if { $retval } { send_user "\nError: $sshcmd failed: $reason\n" return 1 } } elseif ![string compare $prog "rsh"] { if { ! $do_command } { if { [llength $cmethod] == 1 } { send_user "\nError: rsh is an invalid method for -x and " send_user "interactive logins\n" } if { $progs == 0 } { return 1 } continue; } set commands [split $command \;] set num_commands [llength $commands] set rshfail 0 for {set i 0} {$i log_user 0 set retval [ catch {spawn rsh $user@$router [lindex $commands $i] } reason ] if { $retval } { send_user "\nError: rsh failed: $reason\n" log_user 1; return 1 } send_user "$router# [lindex $commands $i]\n" # rcmd does not get a pager and no prompts, so we just have to # look for failures & lines. expect { "Connection refused" { catch {close}; catch {wait}; send_user "\nError: Connection\ Refused ($prog): $router\n" set rshfail 1 } -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { catch {close}; catch {wait}; send_user "\nError: Connection\ closed ($prog): $router\n" set rshfail 1 } "Host is unreachable" { catch {close}; catch {wait}; send_user "\nError: Host Unreachable:\ $router\n" set rshfail 1 } "No address associated with" { catch {close}; catch {wait}; send_user "\nError: Unknown host\ $router\n" set rshfail 1 } -re "\b+" { exp_continue } -re "\[\n\r]+" { send_user -- "$expect_out(buffer)" exp_continue } timeout { catch {close}; catch {wait}; send_user "\nError: TIMEOUT reached\n" set rshfail 1 } eof { catch {close}; catch {wait}; } } log_user 1 } if { $rshfail } { if { !$progs } { return 1 } else { continue } } # fake the end of the session for rancid. send_user "$router# exit\n" # return rsh "success" return -1 } else { puts "\nError: unknown connection method: $prog" return 1 } sleep 0.3 # This helps cleanup each expect clause. expect_after { timeout { send_user "\nError: TIMEOUT reached\n" catch {close}; catch {wait}; if { $in_proc} { return 1 } else { continue } } eof { send_user "\nError: EOF received\n" catch {close}; catch {wait}; if { $in_proc} { return 1 } else { continue } } } # Here we get a little tricky. There are several possibilities: # the router can ask for a username and passwd and then # talk to the TACACS server to authenticate you, or if the # TACACS server is not working, then it will use the enable # passwd. Or, the router might not have TACACS turned on, # then it will just send the passwd. # if telnet fails with connection refused, try ssh expect { -re "(Connection refused|Secure connection \[^\n\r]+ refused)" { catch {close}; catch {wait}; if !$progs { send_user "\nError: Connection Refused ($prog): $router\n" return 1 } } -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { catch {close}; catch {wait}; if !$progs { send_user "\nError: Connection closed ($prog): $router\n" return 1 } } eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 } -nocase "unknown host\r" { send_user "\nError: Unknown host $router\n"; wait; return 1 catch {close}; catch {wait}; return 1 } "Host is unreachable" { send_user "\nError: Host Unreachable: $router\n"; catch {close}; catch {wait}; return 1 } "No address associated with name" { send_user "\nError: Unknown host $router\n"; catch {close}; catch {wait}; return 1 } -re "(Host key not found |The authenticity of host .* be established).*\(yes\/no\)\?" { send "yes\r" send_user "\nHost $router added to the list of known hosts.\n" exp_continue } -re "HOST IDENTIFICATION HAS CHANGED.* \(yes\/no\)\?" { send "no\r" send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" catch {close}; catch {wait}; return 1 } -re "Offending key for .* \(yes\/no\)\?" { send "no\r" send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" catch {close}; catch {wait}; return 1 } -re "(denied|Sorry)" { send_user "\nError: Check your passwd for $router\n" catch {close}; catch {wait}; return 1 } "Login failed" { send_user "\nError: Check your passwd for $router\n" catch {close}; catch {wait}; return 1 } -re "% (Bad passwords|Authentication failed)" { send_user "\nError: Check your passwd for $router\n" catch {close}; catch {wait}; return 1 } "Press any key to continue." { # send_user "Pressing the ANY key\n" send "\r" exp_continue } -re "Enter Selection: " { # Catalyst 1900s have some lame menu. Enter # K to reach a command-line. send "K\r" exp_continue; } -re "Last login:" { exp_continue; } -re "@\[^\r\n]+ $p_prompt" { # ssh pwd prompt sleep 1 send "$userpswd\r" exp_continue } -re "Enter passphrase.*: " { # sleep briefly to allow time for stty -echo sleep .3 send -- "$passphrase\r" exp_continue } -re "$u_prompt" { send -- "$user\r" set uprompt_seen 1 exp_continue } -re "$p_prompt" { sleep 1 if {$uprompt_seen == 1} { send -- "$userpswd\r" } else { send -- "$passwd\r" } exp_continue } -re "$prompt" { set prompt_match $expect_out(0,string); break; } "Login invalid" { send_user "\nError: Invalid login: $router\n"; catch {close}; catch {wait}; return 1 } } } set in_proc 0 return 0}# Enableproc do_enable { enauser enapasswd } { global prompt in_proc global prompt u_prompt e_prompt global enacmd set in_proc 1 send -h "$enacmd\r" expect { -re "$u_prompt" { send -h -- "$enauser\r"; exp_continue} -re "$e_prompt" { send -h -- "$enapasswd\r"; exp_continue} -re ">" { set prompt ">" } "% Password is not set." { send_user "\nError: No 'super' password set for device\n" return 1 } "% Authenticate failed." { send_user "\nError: Check your enable password for 'super'\n" return 1 }# "#" { set prompt "#" }# "(enable)" { set prompt "> \\(enable\\) " } #-re "(denied|Sorry|Incorrect)" { # # % Access denied - from local auth and poss. others # send_user "\nError: Check your Enable passwd\n"; # return 1 # } #"% Error in authentication" { # send_user "\nError: Check your Enable passwd\n" # return 1 # } #"% Bad passwords" { # send_user "\nError: Check your Enable passwd\n" # return 1 # } } # We set the prompt variable (above) so script files don't need # to know what it is. set in_proc 0 return 0}# Run commands given on the command line.proc run_commands { prompt command } { global do_saveconfig in_proc platform global exitcmd set in_proc 1 # clogin has this beast: #regsub -all {^(.{1,11}).*([#>])$} $prompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt # escape any parens in the prompt, such as "(enable)" regsub -all {[][)(]} $prompt {\\&} reprompt expect { -re $reprompt {} -re "\[\n\r]+" { exp_continue } } # this is the only way i see to get rid of more prompts in o/p..grrrrr log_user 0 set commands [split $command \;] set num_commands [llength $commands] # The pager can not be turned off on some 3Com/H3C, so we have to look # for the "More" prompt. for {set i 0} {$i $num_commands} { incr i} { send -- "[subst -nocommands [lindex $commands $i]]\r" expect { -re "\b+" { exp_continue } -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)" } -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)" exp_continue } -re "\[\n\r]+" { send_user -- "$expect_out(buffer)" exp_continue } -re "^ ---- More ----.*\[^\n\r]*" { # H3C pager prompt sleep 0.1 send " " exp_continue } } } log_user 1 send -h "$exitcmd\r" # H3C: the exit command leaves the switch completely; it does not # prompt to save if the config has been modified expect {# -re "^\[^\n\r *]*$reprompt" {# # H3C products# # return to non-enabled mode# # on exit in enabled mode.# send -h "$exitcmd\r"# exp_continue;# }# TODO: we will need to do this too:# "Do you wish to save your configuration changes" {# send -h "n\r"# exp_continue# } -re "\[\n\r]+" { exp_continue }# hwlogin+mod: -re "\[^\n\r *]Note:" { return 0 } timeout { catch {close}; catch {wait}; return 0 } eof { return 0 } } set in_proc 0}## For each router... (this is main loop)#source_password_file $password_fileset in_proc 0set exitval 0set prompt_match ""set enable 0foreach router [lrange $argv $i end] { set router [string tolower $router] # attempt at platform switching. set platform "" send_user -- "$router\n" # device timeout set timeout [find timeout $router] if { [llength $timeout] == 0 } { set timeout $timeoutdflt } # Default prompt. #set prompt "(>|#| \\(enable\\))" set prompt ">\a?" # look for noenable option in .cloginrc if { [find noenable $router] == "1" } { set enable 0 } # Figure out passwords if { $do_passwd || $do_enapasswd } { set pswd [find password $router] if { [llength $pswd] == 0 } { send_user "\nError: no password for $router in $password_file.\n" continue } if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] 2 } { send_user "\nError: no enable password for $router in $password_file.\n" continue } set passwd [join [lindex $pswd 0] ""] set enapasswd [join [lindex $pswd 1] ""] } else { set passwd $userpasswd set enapasswd $enapasswd } # Figure out username if {[info exists username]} { # command line username set ruser $username } else { set ruser [join [find user $router] ""] if { "$ruser" == "" } { set ruser $default_user } } # Figure out username's password (if different from the vty password) if {[info exists userpasswd]} { # command line username set userpswd $userpasswd } else { set userpswd [join [find userpassword $router] ""] if { "$userpswd" == "" } { set userpswd $passwd } } # Figure out enable username if {[info exists enausername]} { # command line enausername set enauser $enausername } else { set enauser [join [find enauser $router] ""] if { "$enauser" == "" } { set enauser $ruser } } # Figure out prompts set u_prompt [find userprompt $router] if { "$u_prompt" == "" } { set u_prompt "(Username|Login|login|user name|User):" } else { set u_prompt [join [lindex $u_prompt 0] ""] } set p_prompt [find passprompt $router] if { "$p_prompt" == "" } { set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):" } else { set p_prompt [join [lindex $p_prompt 0] ""] } set e_prompt [find enableprompt $router] if { "$e_prompt" == "" } { set e_prompt "\[Pp]assword:" } else { set e_prompt [join [lindex $e_prompt 0] ""] } # Figure out identity file to use set identfile [join [lindex [find identity $router] 0] ""] # Figure out passphrase to use if {[info exists avpassphrase]} { set passphrase $avpassphrase } else { set passphrase [join [lindex [find passphrase $router] 0] ""] } if { ! [string length "$passphrase"]} { set passphrase $passwd } # Figure out cypher type if {[info exists cypher]} { # command line cypher type set cyphertype $cypher } else { set cyphertype [find cyphertype $router] if { "$cyphertype" == "" } { set cyphertype "$default_cyphertype" } } # Figure out connection method set cmethod [find method $router] if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } # Figure out the SSH executable name set sshcmd [find sshcmd $router] if { "$sshcmd" == "" } { set sshcmd {ssh} } # Login to the router if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} { incr exitval # if login failed or rsh was unsuccessful, move on to the next device continue } # Figure out the prompt.# H3C: this is what we used to have, now should be obsolete:# # Since autoenable is off by default, if we have it defined, it# # was done on the command line. If it is not specifically set on the# # command line, check the password file.# if $avautoenable {# set autoenable 1# set enable 0## hwlogin:# #set prompt "(#| \\(enable\\))"# set prompt ">\a?"# } else {# set ae [find autoenable $router]# if { "$ae" == "1" } {# set autoenable 1# set enable 0## hwlogin:# set prompt ">\a?"# } else {# set autoenable 0# set enable $avenable# set prompt ">\a?"# }# }## # look for noenable option in .cloginrc# if { [find noenable $router] == "1" } {# set enable 0# }## clogin has:## # look for noenable option in .cloginrc## if | [find noenable $router] != "" | {## set enable 0## } # !! H3C does not appear to have a different prompt between lower # privilege and higher privilege users, so the following test is # not applicable# if { [regexp -- "(#| \\(enable\\))" $prompt_match junk] == 1 } {# set enable 0# } else { if { $avenable == 0 } { set enable 0 } else { set ne [find noenable $router] set ae [find autoenable $router] if { "$ne" == "1" || "$ae" == "1" || $avautoenable } { set enable 0 } else { set enable 1 } }# } if { $enable } { if {[do_enable $enauser $enapasswd]} { if { $do_command || $do_script } { incr exitval catch {close}; catch {wait}; continue } } } # we are logged in, now figure out the full prompt send "\r" expect { -re "\[\r\n]+" { exp_continue; }# -re "^(.+\[:.])1 ($prompt)" { # stoopid extreme cmd-line numbers and# # prompt based on state of config changes,# # which may have an * at the beginning.# set junk $expect_out(1,string)# regsub -all "^\\\* " $expect_out(1,string) {} junk# regsub -all "\[\]\[\(\)]" $junk {\\&} junk;# set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)";# set platform "extreme"# } -re "^.+$prompt" { set junk $expect_out(0,string); regsub -all "\[\]\[\(\)]" $junk {\\&} prompt; } } # H3C: Disable log junk being sent to terminal: must be done before # $enacmd is run. It would be nice for this to be setable in .cloginrc send -h "undo terminal monitor\r" expect -re $prompt {} # Turn session paging off # Comware 5 only. # Comware 3 models have a screen-length command that only works on # a vty basis # clogin does this only within the do_script clause below, but I can't # see why you wouldn 12-23 23:53