aboutsummaryrefslogtreecommitdiffstats
path: root/net-mgmt
diff options
context:
space:
mode:
authorfeld <feld@FreeBSD.org>2016-08-18 02:13:36 +0800
committerfeld <feld@FreeBSD.org>2016-08-18 02:13:36 +0800
commite7945fd9783ebf2d8616865de026ba772e4b87a0 (patch)
treea0c007ed0f8921b02dd5183f139c98ffcf04fc1b /net-mgmt
parent8922c6879ec2c5d93ef0d438cd1d32c36f016eb1 (diff)
downloadfreebsd-ports-gnome-e7945fd9783ebf2d8616865de026ba772e4b87a0.tar.gz
freebsd-ports-gnome-e7945fd9783ebf2d8616865de026ba772e4b87a0.tar.zst
freebsd-ports-gnome-e7945fd9783ebf2d8616865de026ba772e4b87a0.zip
net-mgmt/rancid3: Add experimental support for Comware devices
Comware support has been added via a port option. It is not enabled by default as upstream has not been able to validate the reliability of this enhancement. PR: 209361
Diffstat (limited to 'net-mgmt')
-rw-r--r--net-mgmt/rancid3/Makefile16
-rw-r--r--net-mgmt/rancid3/files/cmw.pm525
-rw-r--r--net-mgmt/rancid3/files/cmwlogin1043
-rw-r--r--net-mgmt/rancid3/files/comware_types.conf56
-rw-r--r--net-mgmt/rancid3/pkg-plist2
5 files changed, 1641 insertions, 1 deletions
diff --git a/net-mgmt/rancid3/Makefile b/net-mgmt/rancid3/Makefile
index 000b0bb2040f..dcefa2c6ca02 100644
--- a/net-mgmt/rancid3/Makefile
+++ b/net-mgmt/rancid3/Makefile
@@ -34,15 +34,22 @@ NEWERCONFIG_FILES1= etc/rancid.conf
NEWERCONFIG_FILES2= etc/lg.conf
NEWCONFIG_FILES= rancid.conf lg.conf
-OPTIONS_DEFINE= SVN
+OPTIONS_DEFINE= COMWARE SVN
OPTIONS_DEFAULT=
+COMWARE_DESC= Add experimental support for Comware devices
SVN_DESC= Use Subversion instead of CVS
CONFLICTS= rancid-[0-9]*
.include <bsd.port.options.mk>
+.if ${PORT_OPTIONS:MCOMWARE}
+PLIST_SUB+= COMWARE=""
+.else
+PLIST_SUB+= COMWARE="@comment "
+.endif
+
.if ${PORT_OPTIONS:MSVN}
CONFIGURE_ARGS+= --with-svn
BUILD_DEPENDS+= svn:devel/subversion
@@ -57,6 +64,9 @@ pre-everything::
@ ${ECHO} "Make sure your rancid repository is quiet before upgrading; disable rancid cron jobs"
post-patch:
+.if ${PORT_OPTIONS:MCOMWARE}
+ ${CAT} ${FILESDIR}/comware_types.conf >> ${WRKSRC}/etc/rancid.types.base
+.endif
${REINPLACE_CMD} "s|par.1|rancid-par.1|" ${WRKSRC}/man/Makefile.in
${MV} ${WRKSRC}/man/par.1 ${WRKSRC}/man/rancid-par.1
${REINPLACE_CMD} "s|3des|3des-cbc|g" ${WRKSRC}/bin/*.in ${WRKSRC}/cloginrc.sample \
@@ -65,6 +75,10 @@ post-patch:
post-install:
${LN} -s ${PREFIX}/libexec/${PORTNAME}/rancid-cvs ${STAGEDIR}${PREFIX}/bin
${LN} -s ${PREFIX}/libexec/${PORTNAME}/rancid-run ${STAGEDIR}${PREFIX}/bin
+.if ${PORT_OPTIONS:MCOMWARE}
+ ${INSTALL_DATA} ${FILESDIR}/cmw.pm ${STAGEDIR}/${PREFIX}/lib/rancid
+ ${INSTALL_SCRIPT} ${FILESDIR}/cmwlogin ${STAGEDIR}/${PREFIX}/libexec/rancid
+.endif
.for file in ${NEWCONFIG_FILES}
@ if [ -f ${PREFIX}/etc/${PORTNAME}/${file} ] ; then \
${ECHO} "WARNING: *** new ${file} file is installed as ${PREFIX}/rancid/${file}.sample"; \
diff --git a/net-mgmt/rancid3/files/cmw.pm b/net-mgmt/rancid3/files/cmw.pm
new file mode 100644
index 000000000000..fac13cb975ce
--- /dev/null
+++ b/net-mgmt/rancid3/files/cmw.pm
@@ -0,0 +1,525 @@
+package cmw;
+##
+## $Id: cmw.pm.in 3000 2015-01-06 18:47:49Z heas $
+##
+## rancid 3.1.99
+## Copyright (c) 1997-2015 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
+#
+# cmw.pm - Comware (Huawei/H3C/3com/HP) rancid procedures
+#
+# https://sites.google.com/site/jrbinks/code/rancid/cmwrancid
+
+use 5.010;
+use strict 'vars';
+use warnings;
+no warnings 'uninitialized';
+require(Exporter);
+our @ISA = qw(Exporter);
+
+use rancid 3.1.99;
+
+our $login;
+
+@ISA = qw(Exporter rancid main);
+#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);
+
+# XXX
+#our @EXPORT = qw(iproutesort iprouteval);
+
+# load-time initialization
+sub import {
+ 0;
+}
+
+# post-open(collection file) initialization
+sub init {
+ $login = "cmwlogin";
+ # add content lines and separators
+ ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
+
+ 0;
+}
+
+# main loop of input of device output
+sub inloop {
+ my($INPUT, $OUTPUT) = @_;
+ my($cmd, $rval);
+
+TOP: while(<$INPUT>) {
+ tr/\015//d;
+ if (/[\]>#]\a?\s*quit/) {
+ #if (/[>#]\s?exit$/) {
+ $clean_run = 1;
+ last;
+ }
+ if (/^Error:/) {
+ print STDOUT ("$host $login error: $_");
+ print STDERR ("$host $login error: $_") if ($debug);
+ $clean_run = 0;
+ last;
+ }
+ while (/[\]>#]\a?\s*($cmds_regexp)\s*$/) {
+ $cmd = $1;
+
+ if (!defined($prompt)) {
+ # Extract the prompt: look for something not [ or < at the start
+ # of the line, until either ] or > or # is reached:
+ $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}}($INPUT, $OUTPUT, $cmd);
+ delete($commands{$cmd});
+ if ($rval == -1) {
+ $clean_run = 0;
+ last TOP;
+ }
+ }
+ }
+}
+
+# dummy function
+sub DoNothing {print STDOUT;}
+
+# This is a sort routine that will sort on the
+# ip route when the ip route is anywhere in
+# the strings.
+sub iproutesort {
+ my(%lines) = @_;
+ my($i) = 0;
+ my(@sorted_lines);
+ foreach my $iproute (sort sortbyiproute keys %lines) {
+ $sorted_lines[$i] = $lines{$iproute};
+ $i++;
+ }
+ @sorted_lines;
+}
+
+## XXX Re-evaluate based on new routines, and consider IPv6:
+# These two routines will sort based upon IP route
+sub iprouteval {
+ my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$#);
+ $a[4] + ($a[3] + 256 * ($a[2] + 256 * ($a[1] + 256 * $a[0])));
+}
+
+sub sortbyiproute {
+ &iprouteval($a) <=> &iprouteval($b);
+}
+
+# Clean up lines on input, particularly ANSI characters as a result
+# of us not being able to turn off per-session terminal paging
+sub filter_lines {
+ my ($l) = (@_);
+
+ #s/^\033\[42D +\033\[42D(.+)$/$1/;
+ #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;
+ $l =~ s/.*\[37D(.*)/$1/g; # MA5600
+ # Probably not needed:
+ $l =~ s/\s*---- More ----\s*//;
+ $l =~ s/^ //; # Comware7
+ $l =~ s/Synchronization is finished.//g;
+ return $l;
+}
+
+# Some commands are not supported on some models or versions
+# of code.
+# Remove the associated error messages, and rancid will ensure that
+# these are not treated as "missed" commands
+sub command_not_valid {
+ my ($l) = (@_);
+
+ if ( $l =~
+ /% Too many parameters found at '\^' position/ ||
+ /% Unrecognized command found at '\^' position/ ||
+ /% Incomplete command found at '\^' position./ ||
+ /(% )?Wrong parameter found at '\^' position/ ||
+ /% Wrong device .+/
+ ) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+# Some commands are not authorized under the current
+# user's permissions
+sub command_not_auth {
+ my ($l) = (@_);
+
+ if ( $l =~
+ /Permission denied\./
+ ) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+# Some output lines are always skipped
+sub skip_pattern {
+ my ($l) = (@_);
+
+ if ( $l =~
+ /^\s+\^$/ ||
+ /^$/
+ ) {
+ return(1);
+ } else {
+ return(0);
+ }
+}
+
+sub DisplayFib {
+
+ my($INPUT, $OUTPUT, $cmd) = @_;
+ my($dest, $nexthop, $flag, $outint, $label);
+ print STDERR " In DisplayFib: $_" if ($debug);
+
+ chomp;
+
+ # Display the command we're processing in the output:
+ ProcessHistory("FIB","","","!\n! '$cmd':\n!\n");
+
+ while (<$INPUT>) {
+ tr/\015//d;
+ last if(/^\s*$prompt/);
+ chomp;
+ $_ = filter_lines($_);
+
+ return(1) if command_not_valid($_);
+ return(-1) if command_not_auth($_);
+ next if skip_pattern($_);
+
+ next if /^Destination count: \d+ FIB entry count: \d+/;
+
+ # Chop out some detail that changes over time (Comware 3):
+ s/(\s+)TimeStamp\s+/$1/; # TimeStamp column heading
+
+ ProcessHistory("FIB","","","! $_\n");
+
+ if ( m,Destination/Mask, ) {
+ while (<$INPUT>) {
+ tr/\015//d;
+ last if(/^\s*$prompt/);
+ chomp;
+ $_ = filter_lines($_);
+
+ # Chop out some detail that changes over time (Comware 3):
+ s/(\s+)t\[\d+\]\s+/$1/; # TimeStamp data
+
+ # "display fib" on comware7 shows host entries for things
+ # learned via arp too. For a distribution router, that's all
+ # the devices on subnets routed by it!
+ # If we filter out all "UH" entries that are NOT InLoop, we
+ # get acceptable output.
+ #
+ # So we want to keep:
+ #
+ # 0.0.0.0/32 127.0.0.1 UH InLoop0 Null
+ #
+ # but reject:
+ #
+ # 130.159.44.161/32 130.159.44.161 UH Vlan44 Null
+ #
+ # However I've a feeling that this is a problematic
+ # solution, and some object to the notion that rancid
+ # should be representing such potentially dynamic data in
+ # the first place, which is why we created the
+ # $display_fib flag for rancid 2, and in rancid 3 one
+ # can modify the command table in rancid.types.conf
+
+ ($dest, $nexthop, $flag, $outint, $label) = split;
+ next if ( $flag eq 'UH' && $outint !~ /InLoop/ );
+ #ProcessHistory("FIB", "cmw::iproutesort", "$dest", "! $_\n");
+ ProcessHistory("FIB", "ipsort", "$dest", "! $_\n");
+ }
+
+ ProcessHistory("FIB", "", "", "!\n");
+
+ # return here to ensure that we don't keep swallowing the
+ # next command's output by returning to the surrounding
+ # while loop
+ return(0);
+ }
+ }
+ return(0);
+}
+
+sub DisplayIPRoutes {
+ my($INPUT, $OUTPUT, $cmd) = @_;
+ my($key,$line,$spaces);
+ print STDERR " In DisplayIPRoutes: $_" if ($debug);
+
+ chomp;
+
+ # Display the command we're processing in the output:
+ ProcessHistory("IPR","","","!\n! '$cmd':\n!\n");
+
+ while (<$INPUT>) {
+ tr/\015//d;
+ last if(/^\s*$prompt/);
+ chomp;
+
+ $_ = filter_lines($_);
+ return(1) if command_not_valid($_);
+ return(-1) if command_not_auth($_);
+ next if skip_pattern($_);
+
+ ProcessHistory("IPR","","","! $_\n");
+
+ if ( m,Destination/Mask, ) {
+ my $lastkey = "";
+ my $lastspaces = "";
+ while (<$INPUT>) {
+ tr/\015//d;
+ last if(/^\s*$prompt/);
+ chomp;
+ $_ = filter_lines($_);
+
+ # If the key is blank, indicating multiple nexthops for
+ # a particular route, then we use the previous one
+ if ( m/^\s+(.+)/ ) {
+ $key = $lastkey;
+ $line = $key . $lastspaces . $1;
+ #ProcessHistory("IPR", "cmw::iproutesort", "$key", "! $line\n");
+ ProcessHistory("IPR", "ipsort", "$key", "! $line\n");
+ }
+ if ( m/^(\S+)(\s+).+/ ) {
+ $key = $1;
+ $line = $_;
+ $spaces = $2;
+ #ProcessHistory("IPR", "cmw::iproutesort", "$key", "! $line\n");
+ ProcessHistory("IPR", "ipsort", "$key", "! $line\n");
+
+ # Remember these, we may need them on the next pass
+ $lastkey = $key;
+ $lastspaces = $spaces;
+ }
+ }
+
+ ProcessHistory("IPR", "", "", "!\n");
+
+ # return here to ensure that we don't keep swallowing the
+ # next command's output by returning to the surrounding
+ # while loop
+ return(0);
+ }
+ }
+ return(0);
+}
+
+## This routine processes general output of "display" commands
+sub CommentOutput {
+ my($INPUT, $OUTPUT, $cmd) = @_;
+ print STDERR " In CommentOutput: $_" if ($debug);
+
+ chomp;
+
+ # Display the command we're processing in the output:
+ ProcessHistory("COMMENTS", "", "", "!\n! '$cmd':\n!\n");
+
+ while (<$INPUT>) {
+ tr/\015//d;
+
+ # If we find the prompt, we're done
+ # Ordinarily this matches from the start of the line, however
+ # we've seen circumstances at least in Comware7 where the
+ # prompt is preceded by whitespace, like so:
+ # ^M^M ^M<router>display boot-loader^M
+ last if(/^\s*$prompt/);
+ chomp;
+
+ # filter out some junk
+ $_ = filter_lines($_);
+ return(1) if command_not_valid($_);
+ return(-1) if command_not_auth($_);
+ next if skip_pattern($_);
+
+ # Now we skip or modify some lines from various commands to
+ # remove irrelevant content, or to avoid insignificant diffs
+
+ # 'display local-user':
+ s/\s+Current AccessNum:.+$//;
+
+ # 'display version':
+ next if (/^(Uptime is \d|.+ [Uu]ptime is \d).+$/);
+ # No longer necessary since skipping the whole Uptime line:
+ # Mangle these lines:
+ #s/(.*)[Uu]ptime.*.weeks.*.days*.*hours*.*minutes*(.*)/$1 $2/;
+ #s/(.*)[Uu]ptime.*days*.*hours*.*minutes*(.*)/$1 $2/;
+
+ # MSRs display a 'last reboot' time, but sometimes the seconds
+ # vary by one or two (presumably internal rounding), so simply make
+ # the last digit a fixed '0'. It would probably be safer to make
+ # the last two digits a fixed '00'.
+ # (Thx Alexander Belokopytov)
+ s/(^Last reboot.+)\d$/${1}0/;
+
+ # 'dir ' commands
+ if ( $cmd =~ /^dir / ) {
+ # First field is just an index number, chop it out
+ s/^\s+\d+\s+(.+)/ $1/;
+ # Remove filenames that are updated frequently
+ next if (
+ /logfile\.log$/ ||
+ /lauth\.dat$/ ||
+ /ifindex\.dat$/ ||
+ /startup\.mdb$/ ||
+ /private-data\.txt$/ ||
+ /.+ KB total \(.+ KB free/ ||
+ /.+ KB total \(.+ KB free/ ||
+ /\.trash/
+ );
+ }
+
+ # 'display ospf brief'/'display ospf'
+ if ( $cmd =~ 'display ospf( brief)?' ) {
+ #next if (/^(Ospf is not enabled yet|Info: OSPF routing process is not enabled|The feature OSPF has not been enabled.).+$/);
+ next if (/^\s+SPF (Computation|Scheduled|calculation) Count:.+$/i);
+ }
+
+ if ( $cmd eq 'display power' ) {
+ next if (/^(\s+Input Power).+$/);
+ }
+
+ if ( $cmd eq 'display poe powersupply' ) {
+ next if (/^(PSE Total Power Consumption|PSE Available Power|PSE Peak Value|PSE Average Value).+$/);
+ }
+
+ if ( $cmd eq 'display ntp-service status' ) {
+ next unless m/(Clock status|Clock stratum|Reference clock ID)/i;
+ }
+
+ if ( $cmd eq 'display transceiver interface' ) {
+ s/^(\S+ transceiver information:).+$/$1/; # filter random garbage
+ s/^Error: The transceiver is absent.$/ No transceiver present./;
+ s/^Error: The combo port is inactive.$/ Inactive combo port./;
+ }
+
+ # Add the processed lines to the output buffer:
+ ProcessHistory("COMMENTS","","","! $_\n");
+ }
+
+ # Add a blank comment line to the output buffer
+ ProcessHistory("COMMENTS", "", "", "!\n");
+ return(0);
+}
+
+## This routine processes a "display current"
+sub DisplayCurrent {
+ my($INPUT, $OUTPUT, $cmd) = @_;
+ print STDERR " In DisplayCurrent: $_" if ($debug);
+
+
+ while (<$INPUT>) {
+ tr/\015//d;
+ last if(/^\s*$prompt/);
+ chomp;
+
+ $_ = filter_lines($_);
+ return(1) if command_not_valid($_);
+ return(-1) if command_not_auth($_);
+ next if skip_pattern($_);
+
+ return(0) if ($found_end);
+
+ # Filter out some sensitive data:
+ if ( $filter_commstr &&
+ /^ ?(snmp-agent (target-host.+securityname|usm-user|community (read|write)) )(\S+)/
+ ) {
+ ProcessHistory("","","","! $1<removed>$'\n");
+ next;
+ }
+ if ( $filter_pwds >= 1 &&
+ /^ ?(password (?:simple|cipher|hash) )(\S+)/ ||
+ /^ ?(super password( role level-\d)( level \d)? (cipher|simple|hash) )(\S+)/ ||
+ /^ ?(set authentication password (cipher|simple|hash) )(\S+)/ ||
+ /^ ?(key (?:authentication|accounting) )(\S+)/
+ ) {
+ ProcessHistory("","","","! $1<removed>$'\n");
+ next;
+ }
+
+ # filter ssh public keys of devices connected to from this device
+ if (/^ ?(public-key-code begin)/ &&
+ $filter_pwds >= 2) {
+ ProcessHistory("","","","!$1\n");
+ ProcessHistory("","","","! <removed>\n");
+ while (<$INPUT>) {
+ tr/\015//d;
+ next if /^$/;
+ next if /^\s+[[:xdigit:]]$/;
+ if (/(^ public-key-code end)/) {
+ ProcessHistory("","","","!$1\n");
+ last;
+ }
+ }
+ next;
+ }
+
+ # Filter mac addresses dynamically added to config
+ next if (/^ ?mac-address security.+$/);
+
+ ProcessHistory("", "", "", "$_\n");
+
+ # end of config
+ if (/^return/) {
+ $found_end = 1;
+ return(0);
+ }
+ }
+ return(0);
+}
+
+1;
+
+__END__
+
+
diff --git a/net-mgmt/rancid3/files/cmwlogin b/net-mgmt/rancid3/files/cmwlogin
new file mode 100644
index 000000000000..b54e9c54a5db
--- /dev/null
+++ b/net-mgmt/rancid3/files/cmwlogin
@@ -0,0 +1,1043 @@
+#! /usr/local/bin/expect --
+##
+## $Id: cmwlogin 3022 2015-01-13 20:00:00Z heas $
+##
+## rancid 3.1.99
+## Copyright (c) 1997-2015 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.
+#
+# cwlogin - Comware login
+#
+# Most options are intuitive for logging into a Comware device.
+# The default is to enable (thus -noenable). Some folks have
+# setup tacacs to have a user login at priv-lvl = 3 (enabled)
+# so the -autoenable flag was added for this case (don't go through
+# the process of enabling and the prompt will be the "#" prompt.
+# The default username password is the same as the vty password.
+#
+
+# Usage line
+set 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\] \[-P platform\] \
+\[-y ssh_cypher_type\] router \[router...\]\n"
+
+# env(CLOGIN) may contain:
+# x == do not set xterm banner or name
+
+# Password file
+set password_file $env(HOME)/.cloginrc
+# Default is to login to the router
+set do_command 0
+set do_script 0
+# The default is to automatically enable
+set 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 1
+set do_enapasswd 1
+# Save config, if prompted
+set do_saveconfig 0
+# Sometimes routers take awhile to answer (the default is 10 sec)
+set timeoutdflt 45
+# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
+set send_human {.2 .1 .4 .2 1}
+
+# 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
+}
+if {[info exists env(CLOGINRC)]} {
+ set password_file $env(CLOGINRC)
+}
+
+# Process the command line
+for {set i 0} {$i < $argc} {incr 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 "rancid 3.1.99\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
+ # Platform
+ } -P* {
+ if {! [regexp .\[P\](.+) $arg ignore platform]} {
+ incr i
+ set plat [lindex $argv $P]
+ }
+ # 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 argument! $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 cares
+proc 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-pie
+proc 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 -nocase [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 script
+proc 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 successfully
+proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } {
+ global command 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 methcmd 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 methcmd suffix port
+ set cmd $sshcmd
+ 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: $cmd 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;
+ }
+
+ # handle escaped ;s in commands, and ;; and ^;
+ regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
+ regsub {^;} $esccommand "\u002;" command
+ set sep "\\1\u001"
+ regsub -all {([^\\])\;} $command "$sep" esccommand
+ set sep "\u001"
+ set commands [split $esccommand $sep]
+ set num_commands [llength $commands]
+ set rshfail 0
+ for {set i 0} {$i < $num_commands && !$rshfail} { incr 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 {
+ send_user "\nError: unknown connection method: $prog\n"
+ 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 "^<-+ More -+>\[^\n\r]*" {
+ # ASA will use the pager for long banners
+ send " ";
+ exp_continue
+ }
+ -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";
+ 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 "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" {
+ send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
+ 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 "Press the <tab> key \[^\r\n]+\[\r\n]+" {
+ 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
+ }
+ -re "\[^\r\n]*\[\r\n]+" { exp_continue; }
+ }
+ }
+
+ set in_proc 0
+ return 0
+}
+
+# Enable
+proc do_enable { enauser enapasswd } {
+ global do_saveconfig in_proc
+ global prompt u_prompt e_prompt enacmd
+ set in_proc 1
+
+ send "$enacmd\r"
+ expect {
+ -re "$u_prompt" { send -- "$enauser\r"; exp_continue}
+ -re "$e_prompt" { send -- "$enapasswd\r"; exp_continue}
+ ">" { set prompt ">" }
+ "#" { set prompt "#" }
+ "(enable)" { set prompt "> \\(enable\\) " }
+ "% Invalid input" {
+ send_user "\nError: Unrecognized command, check your enable command\n";
+ return 1
+ }
+ -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
+ }
+ "% Password is not set." { # Comware
+ send_user "\nError: No '$enacmd' password set for devide\n"
+ return 1
+ }
+ "% Authenticate failed." { # Comware
+ send_user "\nError: Check your enable password for '$enacmd'\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 exitcmd
+ set in_proc 1
+
+ # If the prompt is (enable), then we are on a switch and the
+ # command is "set length 0"; otherwise its "terminal length 0".
+ # skip if its an extreme (since the pager can not be disabled on a
+ # per-vty basis).
+ if { [string compare "extreme" "$platform"] } {
+ # match cisco config mode prompts too, such as router(config-if)#,
+ # but catalyst does not change in this fashion.
+ regsub -all {^(.{1,11}).*([#>])$} $prompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
+ } else {
+ set reprompt $prompt
+ }
+
+ # this is the only way i see to get rid of more prompts in o/p..grrrrr
+ log_user 0
+
+ # handle escaped ;s in commands, and ;; and ^;
+ regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
+ regsub {^;} $esccommand "\u002;" command
+ set sep "\\1\u001"
+ regsub -all {([^\\]);} $command "$sep" esccommand
+ set sep "\u001"
+ set commands [split $esccommand $sep]
+ set num_commands [llength $commands]
+ # the pager can not be turned off on the PIX, so we have to look
+ # for the "More" prompt. the extreme is equally obnoxious in pre-12.3 XOS,
+ # with a global switch in the config.
+ for {set i 0} {$i < $num_commands} { incr i} {
+ if { [lindex $commands $i] == "\u002" } {
+ send -- "\r"
+ } else {
+ 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 "^--More--\[\r\n]+" { # specific match c1900 pager
+ send " "
+ exp_continue
+ }
+ -re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)"
+ exp_continue
+ }
+ -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" {
+ send " "
+ # bloody ^[[2K after " "
+ expect {
+ -re "^\[^\r\n]*\r" {}
+ }
+ exp_continue
+ }
+ -re "^ *--More--\[^\n\r]*" {
+ send " "
+ exp_continue }
+ -re "^<-+ More -+>\[^\n\r]*" {
+ send_user -- "$expect_out(buffer)"
+ send " "
+ exp_continue }
+ -re "^ {0,2}-+ More .*-+.*\[^\n\r]*" {
+ # Comware 3/5 pager prompt
+ sleep 0.1
+ send " "
+ exp_continue }
+ -re "^---- More ----\[^\n\r]*" {
+ # Comware 7 pager prompt
+ sleep 0.1
+ send " "
+ exp_continue }
+ }
+ }
+ log_user 1
+
+ if { [string compare "extreme" "$platform"] } {
+ send -h "exit\r"
+ } else {
+ send -h "quit\r"
+ }
+ expect {
+ -re "^\[^\n\r *]*$reprompt" {
+ # the Cisco CE and Jnx ERX
+ # return to non-enabled mode
+ # on exit in enabled mode.
+ send -h "$exitcmd\r"
+ exp_continue;
+ }
+ "The system has unsaved changes" { # Force10 SFTOS
+ if {$do_saveconfig} {
+ catch {send "y\r"}
+ } else {
+ catch {send "n\r"}
+ }
+ exp_continue
+ }
+ "Would you like to save them now" { # Force10
+ if {$do_saveconfig} {
+ catch {send "y\r"}
+ } else {
+ catch {send "n\r"}
+ }
+ exp_continue
+ }
+ -re "(Profile|Configuration) changes have occurred.*" {
+ # Cisco CSS
+ if {$do_saveconfig} {
+ catch {send "y\r"}
+ } else {
+ catch {send "n\r"}
+ }
+ exp_continue
+ }
+ "Do you wish to save your configuration changes" {
+ if {$do_saveconfig} {
+ catch {send "y\r"}
+ } else {
+ catch {send "n\r"}
+ }
+ exp_continue
+ }
+ -re "\[\n\r]+" { exp_continue }
+ -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_file
+set in_proc 0
+set exitval 0
+set prompt_match ""
+foreach router [lrange $argv $i end] {
+ set router [string tolower $router]
+ # attempt at platform switching.
+ set platform ""
+ send_user -- "$router\n"
+
+ # Figure out the platform
+ if {[info exists plat]} {
+ # command line platform
+ set platform $plat
+ } else {
+ set plat [find platform $router]
+ if { "$plat" == "" } { set platform "" }
+ }
+
+ # Based on the platform, set some other defaults
+ # These can get overridden by other explicit settings
+ # Note that MA5600 wants enacmd "enable"
+ # Note that later versions of Coomware want cyphertype "aes128-cbc"
+ if { [string compare "cmw" "$platform"] } {
+ set enacmd "super"
+ set exitcmd "quit"
+ set cyphertype "3des"
+ } else {
+ set exitcmd "exit"
+ }
+
+ # device timeout
+ set timeout [find timeout $router]
+ if { [llength $timeout] == 0 } {
+ set timeout $timeoutdflt
+ }
+
+ # Default prompt.
+ set prompt [join [find prompt $router] ""]
+ if { [llength $prompt] == 0 } {
+ set prompt "(>\a?|#| \\(enable\\))"
+ }
+
+ # look for autoenable option in .cloginrc & cmd-line
+ set ae [find autoenable $router]
+ if { "$ae" == "1" || $avautoenable } {
+ set autoenable 1
+ } else {
+ set autoenable 0
+ }
+ # look for enable options in .cloginrc & cmd-line
+ if { $avenable == 0 } {
+ set enable 0
+ } else {
+ set ne [find noenable $router]
+ if { "$ne" == "1" || "$autoenable" == "1" } {
+ set enable 0
+ } else {
+ set enable 1
+ }
+ }
+
+ # 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 enable command
+ set enacmd [join [find enablecmd $router] ""]
+ if { "$enacmd" == "" } { set enacmd "enable" }
+
+ # Figure out prompts
+ set u_prompt [find userprompt $router]
+ if { "$u_prompt" == "" } {
+ set u_prompt "(\[Uu]sername|Login|login|user name|User|User name):"
+ } 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 "3des" }
+ }
+
+ # Figure out connection method
+ set cmethod [find method $router]
+ if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
+
+ # Figure out the SSH executable name
+ set sshcmd [join [lindex [find sshcmd $router] 0] ""]
+ 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.
+ if { [regexp -- "(#| \\(enable\\))" $prompt_match junk] == 1 } {
+ set enable 0
+ }
+
+ # Disable smart and interactive before send other commands
+ # (MA5600 and similar only?)
+ # Also disable log junk being sent to terminal
+ if { [string compare "cmw" "$platform"] } {
+ send -h "undo smart\r"
+ expect -re $prompt {}
+ send -h "undo interactive\r"
+ expect -re $prompt {}
+ send -h "undo terminal monitor\r"
+ expect -re $prompt {}
+ }
+
+ 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"
+ regsub -all {^(\^*)(.*)} $prompt {\2} reprompt
+ expect {
+ -re "\[\r\n]+" { exp_continue; }
+ -re "^(.+\[:.])1 ($reprompt)" { # 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 "^.+$reprompt" { set junk $expect_out(0,string);
+ regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt;
+ }
+ }
+ if { $do_command || $do_script } {
+ if { [string compare "extreme" "$platform"] } {
+ # If the prompt is (enable), then we are on a switch and the
+ # command is "set length 0"; otherwise its "terminal length 0".
+ if [regexp -- ".*> .*enable" "$prompt"] {
+ send "set length 0\r"
+ expect -re $prompt {}
+ send "set width 132\r"
+ expect -re $prompt {}
+ send "set logging session disable\r"
+ } else {
+ send "terminal length 0\r"
+ expect -re $prompt {}
+ send "terminal width 132\r"
+ }
+ expect -re $prompt {}
+ } elseif { [string compare "cmw" "$platform"] } {
+ # Turn session paging off
+ # Comware 5/7 only.
+ # Comware 3 models have a screen-length command that only works on
+ # a vty basis
+ send -h "screen-length disable\r"
+ expect -re $prompt {}
+ } else {
+ send "disable clipaging\r"
+ expect -re $prompt {}
+ }
+ }
+ if { $do_command } {
+ if {[run_commands $prompt $command]} {
+ incr exitval
+ continue
+ }
+ } elseif { $do_script } {
+ source $sfile
+ catch {close};
+ } else {
+ label $router
+ log_user 1
+ interact
+ }
+
+ # End of for each router
+ catch {wait};
+ sleep 0.3
+}
+exit $exitval
diff --git a/net-mgmt/rancid3/files/comware_types.conf b/net-mgmt/rancid3/files/comware_types.conf
new file mode 100644
index 000000000000..a58349020f00
--- /dev/null
+++ b/net-mgmt/rancid3/files/comware_types.conf
@@ -0,0 +1,56 @@
+# Comware devices
+cmw;script;rancid -t cmw
+cmw;login;cmwlogin
+cmw;module;cmw
+cmw;inloop;cmw::inloop
+cmw;command;cmw::CommentOutput;display version
+cmw;command;cmw::CommentOutput;display boot-loader
+cmw;command;cmw::CommentOutput;display startup
+cmw;command;cmw::CommentOutput;dir /all
+cmw;command;cmw::CommentOutput;dir /all unit2>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot2#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit3>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot3#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit4>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot4#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit5>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot5#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit6>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot6#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit7>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot7#flash:/
+cmw;command;cmw::CommentOutput;dir /all unit8>flash:/
+cmw;command;cmw::CommentOutput;dir /all slot8#flash:/
+# Commands relating to the hardware:
+cmw;command;cmw::CommentOutput;display device
+cmw;command;cmw::CommentOutput;display device manuinfo
+cmw;command;cmw::CommentOutput;display fan
+cmw;command;cmw::CommentOutput;display power
+cmw;command;cmw::CommentOutput;display poe powersupply
+cmw;command;cmw::CommentOutput;display poe temperature-protection
+cmw;command;cmw::CommentOutput;display transceiver interface
+# Commands relating to authentication:
+cmw;command;cmw::CommentOutput;display cluster
+cmw;command;cmw::CommentOutput;display domain
+cmw;command;cmw::CommentOutput;display local-user
+cmw;command;cmw::CommentOutput;display password-control
+cmw;command;cmw::CommentOutput;display password-control super
+cmw;command;cmw::CommentOutput;display ssh server status
+# Commands relating to system state:
+cmw;command;cmw::CommentOutput;display irf
+cmw;command;cmw::CommentOutput;display xrn-fabric
+cmw;command;cmw::CommentOutput;display ftm topology-database
+cmw;command;cmw::DisplayFib;display fib
+cmw;command;cmw::DisplayIPRoutes;display ip routing-table
+cmw;command;cmw::CommentOutput;display ospf
+cmw;command;cmw::CommentOutput;display ospf brief
+cmw;command;cmw::CommentOutput;display vlan all
+cmw;command;cmw::CommentOutput;display lacp sys
+cmw;command;cmw::CommentOutput;display link-aggregation summary
+cmw;command;cmw::CommentOutput;display link-aggregation verbose
+cmw;command;cmw::CommentOutput;display loopback-detection
+cmw;command;cmw::CommentOutput;display mirror all
+cmw;command;cmw::CommentOutput;display ntp-service status
+cmw;command;cmw::CommentOutput;display stp root
+# And the system config itself:
+cmw;command;cmw::DisplayCurrent;display current-configuration
diff --git a/net-mgmt/rancid3/pkg-plist b/net-mgmt/rancid3/pkg-plist
index bcc86a6907dd..1996d9d0856f 100644
--- a/net-mgmt/rancid3/pkg-plist
+++ b/net-mgmt/rancid3/pkg-plist
@@ -3,6 +3,7 @@ bin/rancid-run
lib/rancid/acos.pm
lib/rancid/arbor.pm
lib/rancid/ciscowlc.pm
+%%COMWARE%%lib/rancid/cmw.pm
lib/rancid/dell.pm
lib/rancid/eos.pm
lib/rancid/foundry.pm
@@ -30,6 +31,7 @@ libexec/rancid/clogin
libexec/rancid/complogin
libexec/rancid/control_rancid
libexec/rancid/cssrancid
+%%COMWARE%%libexec/rancid/cmwlogin
libexec/rancid/dllogin
libexec/rancid/elogin
libexec/rancid/erancid