#cccp - Cisco Console Command Parser
#A utility for batch processing of IOS commands.
#Meant to work with Cisco Catalyst Express 500 series switches.
#version 0.003
#Cedric Nelson, January 2008
use WWW::Mechanize;
use Crypt::SSLeay;
#use strict;
#use diagnostics;
my ($realm,
$url,
$default_username,
$default_password,
$flags,
$dirty,
@commands,
@switches,
$username,
$password,
$protocol,
$port,
$mode,
$verbosity);
$realm = 'level_15_access';
$url = '/ios_web_exec/commandset';
$default_username = '';
$default_password = '';
$dirty = 0;
#NOTE: MODE 0 IS EXEC, MODE 1 IS CONFIG
$mode = 0;
$verbosity = 'default';
#Parse invokation options.
if (@ARGV) {
$flags = analyze_options(@ARGV);
foreach my $flag (keys %{$flags}) {
if ($flag !~ /^-([cseupiqh])$/) {
print "Unrecognized option: $flag\n\n";
$dirty = 1;
}
}
if ($dirty == 1) {show_help();}
else {
if (${$flags}{'-h'}) {show_help();}
if (${$flags}{'-q'}) {$verbosity = 'quiet';}
if (${$flags}{'-c'}) {
foreach my $item (@{${$flags}{'-c'}}) {
if (-e $item) {push @commands, parse_files_macro($item);}
else {push @commands, $item;}
}
}
if (${$flags}{'-s'}) {
foreach my $item (@{${$flags}{'-s'}}) {
if (-e $item) {push @switches, parse_files_macro($item);}
else {push @switches, $item;}
}
}
if (${$flags}{'-e'}) {$port = '443';$protocol = 'https';}
else {$port = '80';$protocol = 'http';}
if (${$flags}{'-u'}) {$username = @{${$flags}{'-u'}}[0];}
if (${$flags}{'-p'}) {$password = @{${$flags}{'-p'}}[0];}
unless (${$flags}{'-u'}) {$username = $default_username;}
unless (${$flags}{'-p'}) {$password = $default_password;}
if (${$flags}{'-c'}) {
my @input = parse_commands(@commands);
my $count = @switches;
foreach my $ip (@switches) {
print ">>Output for switch ($ip):\n" if ($count > 1);
foreach my $input (@input) {
my $output = ios_commandset($ip, $port, $realm, $username, $
+password, $input);
print_output($output);
}
}
}
if (${$flags}{'-i'}) {
my $command = '';
my $count = @switches;
print "Entering interactive mode. Type 'quit' to exit.\n";
until ($command =~ /^quit$/i) {
if ($mode == 1) {print "(config)#";}
else {print "#";}
my @command_list = (chomp($command = <STDIN>));
exit if ($command =~ /^quit$/i);
my @input = parse_commands($command);
my $input = shift(@input);
foreach my $ip (@switches) {
print ">>Output for switch ($ip):\n" if ($count > 1);
my $output = ios_commandset($ip, $port, $realm, $username, $
+password, $input);
print_output($output);
}
}
}
}
}
#Display help if there are no options.
else {show_help();}
### Macros Section ###
#Parse Files Macro
#Make an array of items by reading them from file(s)
sub parse_files_macro {
my (@files) = @_;
my (@items);
foreach my $file (@files) {
open FH, ($file) or warn "Can't open $file for parsing. Skipping i
+t.\n";
while (<FH>) {
chomp($_);
push @items, $_;
}
close FH;
}
return @items;
}
### Subroutine Section ###
#Anonymous code block for scoping %options, @list properly in recursiv
+e callbacks.
{
my %options;
my @list;
#Defines relationship of invokation arguments as a hash.
sub analyze_options {
my ($element);
($element, @list) = @_;
#Add new options to the hash.
unless ($options{$element}) {$options{$element} = [];}
#Parse the list of arguments.
while (@list) {
my $item = shift(@list);
#If the element is an option, parse it with remaining arguments.
if ($item =~ /^-/) {analyze_options($item, @list);}
#If the element is an argument, it belongs to the preceding opti
+on.
else {push @{$options{$element}}, $item;}
}
#Return a reference to your hash of options.
return \%options;
} #End analyze_options
} #End anonymous code block.
#Formats individual commands into an IOS Commandset script
#Note: This subroutine could use some optimizing
sub parse_commands {
my (@commands) = @_;
my @command_sets;
my @command_list;
##NOTE: MODE 0 IS EXEC, MODE 1 IS CONFIG
##NOTE: You can't enter config from exec mode (configure terminal)
## But you can enter exec from config mode (exit only, not end)
## Because of this one-way dependancy, command sets are seperated
## into different modes and executed seperately but in-sequence.
my $header_exec = <<END;
! COMMANDSET VERSION="1.0"
! OPTIONS BEGIN
! ACTION_ON_FAILURE="0" MODE="0"
! OPTIONS END
END
my $header_config = <<END;
! COMMANDSET VERSION="1.0"
! OPTIONS BEGIN
! ACTION_ON_FAILURE="0" MODE="1"
! OPTIONS END
END
my $footer = <<END;
! END
! COMMANDSET END
END
##Parse commands, and seperate into sections of exec, and config mod
+es
while (@commands) {
my $command = shift(@commands);
if ($command =~ /^con(\w+)? t(\w+)?/) {
#If the commandset doesn't start with a config term command
#Finish the current set and start a new one under mode 1.
if (@command_list) {
my $formatted_commands = join("\n", @command_list) . "\n";
my $command_set;
if ($mode == 0) {$command_set = $header_exec . $formatted_comm
+ands . $footer;}
else {$command_set = $header_config . $formatted_commands . $f
+ooter;}
push @command_sets, $command_set;
@command_list = ();
$mode = 1;
}
else {
$command = '!';
push @command_list, $command;$mode = 1;
}
}
#If the command is 'end', finish the current set and start a new o
+ne under mode 0.
elsif ($command =~ /^end$/) {
push @command_list, $command;
my $formatted_commands = join("\n", @command_list) . "\n";
my $command_set;
if ($mode == 0) {$command_set = $header_exec . $formatted_comman
+ds . $footer;}
else {$command_set = $header_config . $formatted_commands . $foo
+ter;}
push @command_sets, $command_set;
@command_list = ();
$mode = 0;
}
#The command is added to the commandset for the current mode.
else {push @command_list, $command;}
}
#Add header & footer for the last section
if (@command_list) {
my $formatted_commands = join("\n", @command_list) . "\n";
my $command_set;
if ($mode == 0) {$command_set = $header_exec . $formatted_commands
+ . $footer;}
else {$command_set = $header_config . $formatted_commands . $foote
+r;}
push @command_sets, $command_set;
}
##Return list of formatted sections, to be executed seperately but i
+n sequence
return @command_sets;
} #End parse_commands
sub print_output {
my ($output) = @_;
if ($verbosity eq 'quiet') {
my $error = $1 if ($output =~ /PARSE_ERROR="(\d)"/);
if ($error == 0) {print "Success\n";}
else {print "[[FAIL]] - Parse Error $error\n";}
}
else {print "$output\n";}
} #End parse_output
#Passes formatted IOS Commandset script to an http server, and returns
+ output
sub ios_commandset {
my ($ip, $port, $realm, $username, $password, $content) = @_;
my $mecha;
my $uri = "$protocol://$ip$url";
$mecha = WWW::Mechanize->new(autocheck => 1);
$mecha->timeout(10);
$mecha->credentials("$ip:$port", $realm, $username, $password);
#$mecha->post($uri, Content => $content);
#Catch WWW::Mechanize errors.
eval{$mecha->post($uri, Content => $content);};
if ($@) {
if (${$flags}{'-e'}) {
$uri = "http://$ip$url";
$mecha->credentials("$ip:80", $realm, $username, $password);
$mecha->post($uri, Content => $content);
}
else {
$uri = "https://$ip$url";
$mecha->credentials("$ip:443", $realm, $username, $password);
$mecha->post($uri, Content => $content);
}
}
return $mecha->content();
} #End ios_commandset
sub show_help {
my ($error) = @_;
if ($error) {print "$error\n\n";}
my ($help) = <<END;
cccp - Cisco Console Command Parser
Version 0.003
Cedric Nelson, January 2008
A utility for batch processing of IOS commands.
Usage: cccp <options>
Options:
-c <cmd | file>... Command(s) to execute.
-s <sw | file>... Execute commands on the specified switch
+(es).
-e Establish encrypted connection to switch
+(es).
-u <...> Username for connection to switch(es).
-p <...> Password for connection to switch(es).
-i Use interactive mode.
-q Quiet verbosity (Succcess/Fail)
-h Display help.
END
print "$help\n";
} #End show_help
|