Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Batch Router

by crackotter (Beadle)
on Jul 30, 2003 at 19:18 UTC ( [id://279335]=CUFP: print w/replies, xml ) Need Help??

Hi Monks, I work in a job where me, and my coworkers, need to log into many Cisco routers and switches all day long. Sometimes we need to do he same thing on differnt groups of Cisco devices. I ussualy write tiny scripts for my own use, but it was my first time to write one for site wide use. Here is the resultant code, which my coworkers have used for a week, and love. It is very primitive so any suggestions or comments would be apprecaited. I have included the coe for the script and after that, an example config file.

Note: I realzie that the Telnet function requires some work to cover the many differnt log in/enable style that Cisco routers support. I made it work ,just enough, for my enviroment.


batch_router.pl - the script itself
#!/usr/bin/perl use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); use Net::Telnet::Cisco; use FileHandle; use Term::ReadKey; use Config::Tiny; $|=1; #! #!<VERSION> my $version="0.07"; my $version_date="07/22/03"; #!</VERSION> #! #Get command line options my $ln_group=""; my $ln_router=""; my $user_name=""; my $password=""; my $input_file=""; my $output_file=""; my $flag_help=0; my $flag_version=0; my $command=""; GetOptions( 'r=s'=>\$ln_router, 'router=s'=>\$ln_router, 'g=s'=>\$ln_group, 'group=s'=>\$ln_group, 'u=s'=>\$user_name, 'username=s'=>\$user_name, 'p=s'=>\$password, 'password=s'=>\$password, 'i=s'=>\$input_file, 'input=s'=>\$input_file, 'o=s'=>\$output_file, 'output=s'=>\$output_file, 'c=s'=>\$command, 'command=s'=>\$command, 'h'=>\$flag_help, 'help'=>\$flag_help, 'v'=>\$flag_version, 'version'=>\$flag_version, ); #Validate command line options #my @commands=(); #if ($command ne "") #{ # $commands[0]="first array"; # push (@commands,$command); # my $line=""; # foreach $line (@commands) # { # print $line . "\n"; # } #} #exit 0; if ($flag_help) { #List Options print STDOUT "Batch Router - the batch router configurator\n"; print STDOUT "\n"; print STDOUT "List of options and their functions:\n"; print STDOUT "-r --router \t Specify a single router name from the + config file, overides -g/--group.\n"; print STDOUT "-g --group \t Specify a single group of routers from + the config file.\n"; print STDOUT "-u --username \t Specifies username to use for TACAC +S logins. If not supplied then script will prompt for name. Cannot be + blank\n"; print STDOUT "-p --password \t Specifies password to use for TACAC +S logins. If not supplied then script will prompt for password. Canno +t be blank\n"; print STDOUT "-c --command \t Specifies single command to run. Com +mand must be surround with double qoutes, if there are spaces.\n"; print STDOUT "-i --input \t Specifies name and path to file which +has the commands that will be ran on each router. If not supplied the +n script will prompt for path/name. Cannot be blank\n"; print STDOUT "-o --output \t Specifies name and path where to send + output from routers. If not supplied output will be sent to STDOUT\n +"; print STDOUT "-h --help \t This help screen. Displays a list of op +tions.\n"; print STDOUT "-v --version \t Display version information for this + script.\n"; exit 0; } if ($flag_version) { #Show Version print STDOUT "Batch Router - the batch router configurator\n"; print STDOUT "Version number $version\n"; print STDOUT "Latest revision on $version_date\n"; exit 0; } #Load Config file my %routers=(); my $conf = Config::Tiny->read('batch_router.cnf'); my $conf_routers = $conf->{routers}; # Hash ref with routers informati +on my $conf_groups = $conf->{groups}; # Hash ref with group information my $validation=0; if ($ln_router ne "") { #Check for Single Host #Validate router with config file $validation = &Check_routers($conf_routers, $ln_router); if (!$validation) { print STDOUT "Router \'$ln_router\' is not in routers list.\n" +; exit 1; } $routers{$ln_router}=$conf_routers->{$ln_router}; } elsif ($ln_group ne "") { #Check for group of hosts #Validate group with config file $validation = &Check_groups($conf_groups, $ln_group); if (!$validation) { print STDOUT "Group \'$ln_group\' is not in group list.\n"; exit 1; } #Pull each router name from $conf_group and put into a list my @router_list=&Group_to_routers($conf_groups->{$ln_group}); #Validate each router name in group my $line = ""; foreach $line (@router_list) { #Reset Validation to false $validation=0; $validation = &Check_routers($conf_routers, $line); if (!$validation) { print STDOUT "Router \'$line\' in group \'$ln_group\' is n +ot in routers list.\n"; exit 1; } $routers{$line}=$conf_routers->{$line}; } } else { #Make sure single or group of hosts selected print STDOUT "You must select a router or group to run this script +.\n"; exit 0; } #Prompt for user name if not provided if ($user_name eq "") { print STDOUT "Please enter your user name: "; ReadMode('normal'); $user_name = ReadLine(0); chomp $user_name; ReadMode('restore'); if ($user_name eq "") { print STDOUT "User name cannot be blank.\n"; exit 1; } } #prompt for password if not provided if ($password eq "") { print STDOUT "Please enter your password: "; ReadMode('noecho'); $password = ReadLine(0); chomp $password; ReadMode('restore'); if ($password eq "") { print STDOUT "Password cannot be blank.\n"; exit 1; } print "\n"; } if ($input_file eq "" && $command eq "") { print STDOUT "Please enter \'input' filename, with path if needed: + "; ReadMode('normal'); $input_file = ReadLine(0); chomp $input_file; ReadMode('restore'); if ($input_file eq "") { print STDOUT "Input filename can not be blank name cannot be b +lank.\n"; exit 1; } } #Get list of commands from Input file or from the command line my @commands=(); if ($command ne "") { push (@commands,$command); } elsif ($input_file ne "") { print STDOUT "*Reading in commands from Input File..."; @commands= &Open_input_file($input_file); #Check for returned error if (scalar(@commands) <= 0) { print STDOUT "\nInput file '$input_file' does not exist, is un +readable, or is empty.\n"; exit 1; } print STDOUT "Done\n"; } #Set $out_fh(refernce to output file handle) to STDOUT or output file print STDOUT "*Setting up output file, if any..."; my $out_FH = &Set_output_FH($output_file); if (!$out_FH) { print STDOUT "\nOutput file '$output_file' can not be created or o +pened.\n"; exit 1; } print STDOUT "Done\n"; print STDOUT "*Logging into Routers.\n"; if ($output_file eq "") {print "\n";} my $host=""; my %Result=(); foreach $host (keys %routers) { print STDOUT "*Logging into router $host($routers{$host}).\n"; if ($output_file ne "") { print $out_FH "Router $host($routers{$host}).\n"; } %Result= &Router($user_name,$password, $routers{$host}, @commands) +; my $line = ""; foreach $line (@commands) { print $out_FH "-" . $line . "\n"; my $output_list=""; foreach $output_list ($Result{$line}) { my $output_line=""; foreach $output_line (@$output_list) { chomp ($output_line); print $out_FH "\t" . $output_line . "\n"; } } } print $out_FH "\n"; print $out_FH "--------------------------------------------------- +-\n"; if ($output_file eq "") {print STDOUT "\n";} } if ($output_file eq "") {print STDOUT "\n";} print STDOUT "*Closing output file, if any..."; if (!$output_file eq "") { close ($out_FH); } print STDOUT "Done\n"; ## Start of subfunctions sub Check_routers { #Check router Hash reference for router string my ($conf_routers, $ln_router)=@_; if (!defined($conf_routers->{$ln_router})) { return 0; } return 1; } sub Check_groups { #Check group Hash refernce for group string my ($conf_groups, $ln_group)=@_; if (!defined($conf_groups->{$ln_group})) { return 0; } return 1; } sub Group_to_routers { #Convert group router string into list of router names my ($group)=@_; chomp $group; $group =~ s/[ \[\]]//g; my @router_list = split/,/,$group; return @router_list; } sub Open_input_file { #Open config file ($input_file)=@_; my @list = (); #Check if file exists and size > 0, else return error if (-s $input_file) { #Open Input file, else return Error unless (open IP_FH, "<$input_file") { close IP_FH; return (); } @list = <IP_FH>; chomp(@list); } else { close IP_FH; return (); } close IP_FH; return @list; } sub Set_output_FH { #Set output file handle, default to STDOUT ($output_file)=@_; my $out_FH=""; if ($output_file eq "") { $out_FH = \*STDOUT; } else { unless (open OUT_FH,">$output_file") { return 0; } $out_FH=\*OUT_FH; } return $out_FH; } sub Router { #Log into single router. Return command output in hash (with neste +d list) my ($user_name,$password, $host, @commands)=@_; my $t = Net::Telnet::Cisco->new(Host => $host, Dump_log=> "telnetlog$host.txt"); #Log in With Username and Password $t->login($user_name,$password); my @level = $t->cmd("show privilege"); #Check if at level 15, if not then enable if (!($level[0] =~ /15/)) { # if user level not 15 then enable $t->enable($password); } my $command=""; $t->cmd("terminal length 0"); my %Result=(); foreach $command (@commands) { @{$Result{$command}} = $t->cmd($command); } $t->close; return %Result; }

batch_router.cnf - the Config file
## Router Identification [routers] any-core_12016 = 192.168.1.1 any-edge_12012 = 192.168.1.2 ##Group of routers [groups] network = [any-core_12016, any-edge_12012]

Replies are listed 'Best First'.
Re: Batch Router
by krisahoch (Deacon) on Aug 07, 2003 at 21:10 UTC

    crackotter,

    First and foremost, let me congradulate you on your name. It gives me a distrubing mental impression that I do not want to think to long about

    Now that the is over, if there would be one thing that I would do here, it would be to write a 'Print' function. If you need to send everything to STDOUT, then you can lessen your keystrokes by doing something like ...

    sub Print{ my $string = shift(); print STDOUT "$string\n"; }
    Then you could change all of those instances of...
    print STDOUT "Batch Router - the batch router configurator\n"; print STDOUT "\n"; print STDOUT "List of options and their functions:\n";
    to
    Print "Batch Router - the batch router configurator"; Print(); #Empty line Print "List of options and their functions:";

    This is merely a suggestion.

    Kristofer Hoch

    Damn it Jim, I'm a doctor not a physician

      Wouldn't it be saner to simply use print until the need arises to print to anything but STDOUT?

      print "Batch Router - the batch router configurator\n"; ... print $out_FH "----------------------------------------------------\n" +;

        That would be my thought, but I don't know what crackotter is trying to do/circumvent. As such, I assumed that he had a good reason for doing what he did. Personally, I'd do exactly what you're saying, but that's just me.

        Kristofer Hoch

        The dog isn't supposed to stretch like that -- Overheard spouse talking to the daughter

      Awesome suggestion, which I will implement into the next version. Thank You
Re: Batch Router
by graff (Chancellor) on Aug 11, 2003 at 04:51 UTC
    The first comment I would make is "if it works, don't fix it". The next comment would be that if you find you do need to fix it at some point, or need to write something else of a similar nature, doing it with fewer lines will generally be better. I'm not pushing for a crypto/obfu/golf style -- just use a few more of the syntactic options that perl offers for the sake of clear brevity; e.g.:
    # instead of this: if ($user_name eq "") { print STDOUT "User name cannot be blank.\n"; exit 1; } # do this die "User name cannot be blank.\n" unless ( $user_name ); # (numerous examples of that sort) #instead of this: my $out_FH=""; if ($output_file eq "") { $out_FH = \*STDOUT; } else { unless (open OUT_FH,">$output_file") { return 0; } $out_FH=\*OUT_FH; } return $out_FH; # do this: my $out_FH = ( $output_file eq "" ) ? \*STDOUT : ( open( OUT_FH, ">$output_file" )) ? \*OUT_FH : undef; return $out_FH; # and similarly, instead of this: if (!defined($conf_groups->{$ln_group})) { return 0; } return 1; # do this: return ( defined( $conf_groups->{$ln_group} ));
    You could reduce your "Open_input_file" a lot by simplifying the error checking: don't bother with "-s", just read the file into @list (grepping off unwanted lines (blanks/comments) as needed) chomp @list, and return it, and have the caller check for an empty list.

    You could also put some of the option strings into a hash (user_name, password, input_file, etc) -- since you prompt for these when they aren't stated on the command line, it will be easier to loop through the hash elements to do the "if(empty){prompt,read,check}" stuff, rather than coding a separate block to do this for each parameter; e.g.:

    for my $opt ( qw/user_name password input_file/ ) { next if ( $opthash{$opt} ); print "You need to specify a $opt: " if ( $opt eq "password" ) { ReadMode( "noecho" ); else { ReadMode( "normal" ); } $opthash{$opt} = ReadLine(0); ReadMode( "restore" ); die "You really should have typed something for $opt\n" unless ( $opthash{$opt} ); } # this input mechanism only needs to be written once
    Finally, when you have a long usage message to print, this sort of "here-document" layout is easiest:
    my $Usage = <<ENDUSE; This is a great program and I'd like to fill up your console with pithy lines of information about all the stuff it can do. blah blah blah blah ENDUSE
      Another great idea, One of the things I need to work on is reducing my code bloat. This piece of work is accutly quite short for me (bloat wise). I will redo the script with the ideas you offered. Thanx

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://279335]
Approved by ybiC
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (3)
As of 2024-04-19 01:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found