#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 = )); 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 it.\n"; while () { chomp($_); push @items, $_; } close FH; } return @items; } ### Subroutine Section ### #Anonymous code block for scoping %options, @list properly in recursive 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 option. 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 = <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) = < Options: -c ... Command(s) to execute. -s ... 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