This is a script for reading config info from a bunch of routers (for archiving, autiting, whatever)

The configuration for the script itself is read from a INI-style config file (example below). Each router is represented by a section with its name or IP as the section header. The "info" attribute is mandatory (originally intended as title information for a to-be-generated report). Then there has to be a section "dump" (which means if you have a router with a hostname of "dump", you have to refer to it by its IP address).

Each entry in the "dump" section results in a command sent to the router(s), and the output of this command is written to a file.

The output is placed in the directory named by "dir" in the top INI section. Each router gets its own subdirectory, and if set "debug", there will be a subdirectory "debug" - so you should not use "debug" as hostname, either :-)

The "enable" password is needed, because on Cisco devices, the terminal size command is privileged.

Initially, I used Net::Telnet::Cisco, and later adapted it for SSH. After trying with SSH modules directly, I settled with Control::CLI. It took me a while noticing that they use different ways of to specify prompts, before getting it to work.

Of course it is bad to have passwords stored as plaintext, but at least they are kept separate from the script itself. It runs under Windows, but should work under other systems, too.

Additionally, I uploaded it to a github gist, just in case someone wants to tinker.

use strict; use warnings; use Data::Dumper; use Config::Tiny; use Path::Tiny 'path'; binmode STDOUT, ':encoding(cp437)'; use Control::CLI; my $ini = Config::Tiny->read( 'ciscodump.ini' ); my $dumpdir = path($ini->{_}{dir}); my $debugdir = $dumpdir->child('debug'); my $debug = $ini->{_}{debug}; $Data::Dumper::Useqq = 1; sub dbg { print @_ if $debug; @_; } dbg Dumper $ini; my @k = grep { $_ !~ /^(_|dump)$/ } keys %$ini; print Dumper \@k if $debug; my %dumpcommands = %{$ini->{dump}}; my $prompt = '(?m:^\W?(?!Device#)[\w\/\d.:-]+[>#])'; my %cs_opts; $cs_opts{Prompt} = $prompt; $cs_opts{Errmode} = 'return'; $cs_opts{Output_record_separator} = "\r"; for my $host (@k) { my $user = $ini->{$host}{user} // $ini->{_}{user}; my $pass = $ini->{$host}{pass} // $ini->{_}{pass}; my $enable = $ini->{$host}{enable} // $ini->{_}{enable}; my $info = $ini->{$host}{info} // $ini->{_}{info}; my $method = $ini->{$host}{use} // 'SSH'; print "$host\t- $info\n"; $debug = $ini->{_}{debug} // $ini->{$host}{debug}; $debugdir->mkpath if $debug; $cs_opts{Dump_Log} = $debugdir->child($host . '.log')->stringify i +f $debug; $cs_opts{Timeout} = $ini->{$host}{timeout} // 10; my $hostdir = $dumpdir->child($host); $hostdir->mkpath; my $cc = Control::CLI->new( %cs_opts, Use => $method ); my %login_opts = ( Username => $user, Password => $pass, ); if ($method =~ /^TELNET$/i) { if ($cc->connect(Host => $host)) { dbg "TELNET connection established\n"; if ($cc->login(%login_opts)) { dbg "logged in\n"; } else { warn 'login failed: ' . $cc->errmsg(); } } else { warn 'connection could not be established: ' . $cc->er +rmsg(); next; } } else { # SSH if ($cc->connect(Host => $host, %login_opts)) { dbg "SSH connection established\n"; } else { warn 'connection could not be established: ' . $cc->er +rmsg(); next; } } dbg Dumper $cc->read(Blocking => 1, Timeout => 5); dbg "enable>\n"; dbg Dumper $cc->cmd(command => 'enable', prompt => 'Password:' +); # dbg "waiting>\n"; # my @wf = $cc->waitfor(Match => 'Password:'); # dbg Dumper \@wf; dbg "sending>\n"; dbg Dumper $cc->cmd($enable); dbg "after>\n"; my $out = $cc->cmd('term len 0'); dbg Dumper $out; dbg Dumper $cc->cmd('term page 0') if grep { /invalid/i } $out +; for my $prefix (keys %dumpcommands) { my $cmd = $dumpcommands{$prefix}; dbg "command: '$cmd'\n"; my $out = $cc->cmd($cmd); dbg Dumper \$out; $hostdir->child($prefix . '.txt')->spew_raw($out); } $cc->disconnect; };
And here is the INI file example
user=admin pass=confidential enable=top_secret dir=C:\Users\Administrator\Desktop\ciscodump [dump] inventory=show inventory version=show version running-config=show running-config startup-config=show startup-config [] info=location with different parameters user=otheruser pass=public timeout=30 [] info=somewhere else, but "central" parameters [] info=ancient router use=TELNET [] debug=yes info=lets see...