patches to rancid to allow rsh with Cisco gear

Ed Ravin eravin at panix.com
Mon Jun 6 21:59:44 UTC 2005


Attached are some patches and additions to Rancid to allow management
of a Cisco router via rsh (i.e. one-shot "rsh" commands, not interactive
rlogin).  The nice thing about using "rsh" is that you don't need to
supply a password.

For starters, install something like this in your router config:

 ip rcmd remote-host rancid <trusted-IP-address> rancid enable

If necessary, replace "rancid" with the username that Rancid runs
as on your system.

Then, replace your rancid-fe with the one attached.  This version of
rancid-fe includes the ability to configure a clogin command that will
be passed in via the RANCID_CLOGIN environment variable.  A bonus is
that you can add your own device types or update existing device types
by creating a file in /etc or wherever and then defining it in
/etc/rancid.conf with the RANCID_EXECCONF variable.  A sample
"rancidexec.conf" is attached.

Then, apply the attached patch to "rancid".  This tells rancid to use
the value of RANCID_CLOGIN if available instead of calling 'clogin".

Finally, install the attached "rsh.clogin" in rancid's PATH.  This is
a clogin-like program (though it only supports the -t and -c options
so far) that talks to the router via rsh, by breaking up the command list
into individual calls to the "rsh" command.

If these changes are acceptable to the Rancid maintainers, I will write
up full documentation and extend rsh.clogin to support the rest of the
usual options (where applicable).

	-- Ed
-------------- next part --------------
#! /usr/local/bin/perl5
##
## $Id: rancid-fe,v 1.3 2005/06/05 06:19:21 root Exp root $
##
## Copyright (C) 1997-2004 by Terrapin Communications, Inc.
## All rights reserved.
##
## This software may be freely copied, modified and redistributed
## without fee for non-commerical purposes provided that this license
## remains intact and unmodified with any RANCID distribution.
##
## 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.
##
## Except where noted otherwise, rancid was written by and is maintained by
## Henry Kilmer, John Heasley, Andrew Partan, Pete Whiting, and Austin Schutz.
##
#
#  rancid-FE - front-end to rancid/jrancid/etc. for use with par. 
#
my $usage="usage: rancid-fe <router>:<vendor>\n";
#

require 5;

die $usage unless defined($ARGV[0]);

($router, $vendor) = split('\:', $ARGV[0]);

die $usage unless defined($router) and defined($vendor);

# Default dispatch table
# "device"  => "rancid-pgm  [login-pgm [args]]"
my %routertab= (
	"alteon" => "arancid",
	"baynet" => "brancid",
	"cat5" => "cat5rancid",
	"cisco" => "rancid",
	"ciscorsh" => "rancid rsh.clogin",
	"css" => "cssrancid",
	"enterasys" => "rivrancid",
	"erx" => "jerancid",
	"extreme" => "xrancid",
	"ezt3" => "erancid",
	"force10" => "f10rancid",
	"foundry" => "francid",
	"hitachi" => "htrancid",
	"hp" => "hrancid",
	"juniper" => "jrancid",
	"mrtd" => "mrancid",
	"netscaler" => "nsrancid",
	"netscreen" => "nrancid",
	"procket" => "prancid",
	"redback" => "rrancid",
	"riverstone" => "rivrancid",
	"tnt" => "tntrancid",
	"zebra" => "zrancid",
	"hp4000m" => "hp4000m.rancid hp4000m.clogin",
);

my $execconf= $ENV{"RANCID_EXECCONF"};

if (defined($execconf))
{
	open(TABLE, "<$execconf") || die "$0: cannot open file $execconf: $!\n";
	while(<TABLE>)
	{
		chomp;
		next if /^\s*#/;	# skip comments
		next if /^$/;

		my ($routertype, $remainder)= split(' ', $_, 2);
		if (!defined($remainder))
		{
			warn "$0: bad entry in file $execconf line $.: $_\n";
			next;
		}

		$routertab{$routertype}= $remainder;
	}
}

die "$0: unknown router manufacturer for $router: $vendor\n"
	unless exists($routertab{$vendor});

my $vendormatch= $routertab{$vendor};

my ($rancidpgm, $cloginpgm)= split(' ', $vendormatch, 2);
die "$0: bad dispatch table entry for $vendor - no programs found\n"
	unless defined($rancidpgm);

$ENV{"RANCID_CLOGIN"}= $cloginpgm if defined($cloginpgm);

exec("$rancidpgm $router");

die "$0: exec of $rancidpgm failed for router manufacturer $vendor: $!\n";
-------------- next part --------------
--- rancid	2005/06/04 05:51:57	1.1
+++ rancid	2005/06/04 05:53:54
@@ -1,6 +1,6 @@
 #! /usr/local/bin/perl5
 ##
-## $Id: rancid,v 1.1 2005/06/04 05:51:57 root Exp $
+## $Id: rancid,v 1.2 2005/06/04 05:53:45 root Exp root $
 ##
 ## Copyright (C) 1997-2004 by Terrapin Communications, Inc.
 ## All rights reserved.
@@ -35,6 +35,7 @@
 $found_env = 0;
 $found_diag = 0;
 $timeo = 90;			# clogin timeout in seconds
+$clogin_pgm= $ENV{'RANCID_CLOGIN'} || "clogin";
 
 my(%filter_pwds);		# password filtering mode
 
@@ -1628,13 +1629,13 @@
     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);
+    print STDERR "executing $clogin_pgm -t $timeo -c\"$cisco_cmds\" $host\n" if ($debug);
+    print STDOUT "executing $clogin_pgm -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";
+	system "$clogin_pgm -t $timeo -c \"$cisco_cmds\" $host </dev/null > $host.raw 2>&1" || die "$clogin_pgm failed for $host: $!\n";
+	open(INPUT, "< $host.raw") || die "$clogin_pgm failed for $host: $!\n";
     } else {
-	open(INPUT,"clogin -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "clogin failed for $host: $!\n";
+	open(INPUT,"$clogin_pgm -t $timeo -c \"$cisco_cmds\" $host </dev/null |") || die "$clogin_pgm failed for $host: $!\n";
     }
 }
 
-------------- next part --------------
#!/usr/local/bin/perl5.6.1 -w

# $Id: rsh.clogin,v 1.6 2005/06/06 15:32:53 root Exp root $

#  rsh.login
#  use rsh to execute commands on a device
#  insert pseudoprompts before and after commands
#  put results of commands into one output stream to
#  make rancid happy
#
#  by Ed Ravin <eravin at panix.com>
#  Code available courtesy of PANIX Public Access Networks http://panix.com
#  License is GPL
#
# pseudocode
#   for each hostname
#      for each command in command list
#         print a pseudoprompt
#         set timeout and spawn "rsh device cmd" and collect results
#      insert a final pseudoprompt with "exit" for rancid's benefit

use strict;
use Getopt::Long;

# TODO: support external specification of which rsh command to use
# use Rancid::Login;


my $usage="Usage: $0  [-t timeout] [-c command-list] hostname [...]

Run commands via rsh on router or other device.  Kill the rsh process if
it fails to respond within timeout (default 15 seconds).
";

my %opt;
%opt=(
	'expect-timeout' => 15,
	'command' => "",
);

GetOptions (\%opt, "expect-timeout|t=i",
	"command|c=s", ) || die $usage;

#########
# global variables
use vars qw($ExpectTimeout);
use vars qw($cmdstr @cmdlist);
use vars qw($hostname);

# rsh times out at 75 seconds, so rancid's default 90 second timeout
# is too long.  Set a reasonable maximum.  Note that if connection
# is refused, rsh returns after 30 seconds.
$ExpectTimeout= $opt{'expect-timeout'} > 60 ? 60 : $opt{'expect-timeout'};
$cmdstr= $opt{'command'};
@cmdlist= split(';', $cmdstr);
$hostname= "";
#########

die "\n$0: Missing hostname\n" . $usage unless $ARGV[0];
die "\n$0: missing mandatory \"-c command\" argument\n" if $cmdstr eq "";

my $pseudoprompt= "Device-via-rsh# ";


### MAIN

# load_rancid_config();	# -f option code would go here

foreach $hostname (@ARGV)
{
	# my @rshcmd= find_rancid_config("rshcmd", $hostname);
	# @rshcmd= ("rsh -n") if @rshcmd == 0;

	my @rshcmd= ("rsh -n");

	foreach my $cmd (@cmdlist)
	{
		# call rsh for the command.  if it takes too long, kill it.

		my $resultbuffer= "";

		# we're going to monitor stderr separately so we can distinguish
		# rsh errors (connection refused, etc.) from data
		pipe(RSH2READ, RSH2WRITE) || die "$0: cannot create pipe: $!\n";

        my $kidpid = open(PGM, "-|");
		if ($kidpid == 0)
		{ # child
			# point stderr to the pipe back to Daddy
			close(STDERR); open(STDERR, ">&RSH2WRITE") ||
				die "$0: child: cannot move stderr to pipe: $!\n";
			close(STDIN);  open(STDIN, "</dev/null") ||
				die "$0: child: cannot move stdin to /dev/null: $!\n";
			exec "$rshcmd[0] $hostname $cmd" ||
				die "$0: child: unable to exec $rshcmd[0]: $!\n";
		}
		# Back in parent-land
		close(RSH2WRITE); # the kid is using it

	eval {
		local $SIG{ALRM} = sub { die "TIMEOUT\n" };
		alarm $ExpectTimeout;

		while (<PGM>)
		{
			alarm $ExpectTimeout;
			$resultbuffer .= $_;
		}
		alarm 0;
	};
		if ($@ and ($@ =~ /TIMEOUT/)) {
			print "$0: Timeout exceeded.  Killing errant \"$rshcmd[0]\" process...\n";
			kill 'TERM', $kidpid;
			exit 2; # unclean run
		};

		my $splitexp= '\r\n';
		my @results= split($splitexp, $resultbuffer);

		# trim off the CR characters and any trailing white space
		map { s/
//g; } @results;
		map { s/\s+$/ /; } @results;  # reduce trailing white space to one

		# if stdout is empty, but there's something on stderr, assume rsh
		# errored off and abort
		if (@results == 0) # no stdin? try stderr
		{
			@results= <RSH2READ>;
			if (@results) {
				# trim off the CR characters and any trailing white space
				map { s/
//g; } @results;
				map { s/\s+$/ /; } @results;
				print join("\n", @results), "\n";
				exit 3; # unclean run
			}
		}

		print "\n", $pseudoprompt, $cmd, "\n";
		print join("\n", @results), "\n";

		close(PGM);
		close(RSH2READ);
	}
}
print $pseudoprompt, "\n";
print $pseudoprompt, "exit\n"; # so rancid thinks there's a clean_run
-------------- next part --------------
# Additions to RANCID exec table
# Format is:
# devicename  rancid-pgm-name  clogin-pgm-name [options to clogin-pgm]

ciscorsh rancid rsh.clogin


More information about the Rancid-discuss mailing list