freebsd-ports/net-mgmt/rancid3/files/cmw.pm.in
Olli Hauer 2f31299487 - add missing fix for rancid version
s/3.1.99/@VERSION@/

PR:		215832
2017-01-06 18:48:49 +00:00

525 lines
16 KiB
Perl

package cmw;
##
## $Id: cmw.pm.in 3000 2015-01-06 18:47:49Z heas $
##
## rancid @VERSION@
## Copyright (c) 1997-2015 by Terrapin Communications, Inc.
## All rights reserved.
##
## This code is derived from software contributed to and maintained by
## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
## Pete Whiting, Austin Schutz, and Andrew Fort.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions
## are met:
## 1. Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in the
## documentation and/or other materials provided with the distribution.
## 3. All advertising materials mentioning features or use of this software
## must display the following acknowledgement:
## This product includes software developed by Terrapin Communications,
## Inc. and its contributors for RANCID.
## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
## contributors may be used to endorse or promote products derived from
## this software without specific prior written permission.
## 5. It is requested that non-binding fixes and modifications be contributed
## back to Terrapin Communications, Inc.
##
## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
## POSSIBILITY OF SUCH DAMAGE.
#
# RANCID - Really Awesome New Cisco confIg Differ
#
# cmw.pm - Comware (Huawei/H3C/3com/HP) rancid procedures
#
# https://sites.google.com/site/jrbinks/code/rancid/cmwrancid
use 5.010;
use strict 'vars';
use warnings;
no warnings 'uninitialized';
require(Exporter);
our @ISA = qw(Exporter);
use rancid @VERSION@;
our $login;
@ISA = qw(Exporter rancid main);
#XXX @Exporter::EXPORT = qw($VERSION @commandtable %commands @commands);
# XXX
#our @EXPORT = qw(iproutesort iprouteval);
# load-time initialization
sub import {
0;
}
# post-open(collection file) initialization
sub init {
$login = "cmwlogin";
# add content lines and separators
ProcessHistory("","","","!RANCID-CONTENT-TYPE: $devtype\n!\n");
0;
}
# main loop of input of device output
sub inloop {
my($INPUT, $OUTPUT) = @_;
my($cmd, $rval);
TOP: while(<$INPUT>) {
tr/\015//d;
if (/[\]>#]\a?\s*quit/) {
#if (/[>#]\s?exit$/) {
$clean_run = 1;
last;
}
if (/^Error:/) {
print STDOUT ("$host $login error: $_");
print STDERR ("$host $login error: $_") if ($debug);
$clean_run = 0;
last;
}
while (/[\]>#]\a?\s*($cmds_regexp)\s*$/) {
$cmd = $1;
if (!defined($prompt)) {
# Extract the prompt: look for something not [ or < at the start
# of the line, until either ] or > or # is reached:
$prompt = ($_ =~ /^([^\]>#]+[\]>]\a?)/)[0];
$prompt =~ s/([][}{)(\\])/\\$1/g;
print STDERR ("PROMPT MATCH: $prompt\n") if ($debug);
}
print STDERR ("HIT COMMAND:$_") if ($debug);
if (! defined($commands{$cmd})) {
print STDERR "$host: found unexpected command - \"$cmd\"\n";
$clean_run = 0;
last TOP;
}
$rval = &{$commands{$cmd}}($INPUT, $OUTPUT, $cmd);
delete($commands{$cmd});
if ($rval == -1) {
$clean_run = 0;
last TOP;
}
}
}
}
# dummy function
sub DoNothing {print STDOUT;}
# This is a sort routine that will sort on the
# ip route when the ip route is anywhere in
# the strings.
sub iproutesort {
my(%lines) = @_;
my($i) = 0;
my(@sorted_lines);
foreach my $iproute (sort sortbyiproute keys %lines) {
$sorted_lines[$i] = $lines{$iproute};
$i++;
}
@sorted_lines;
}
## XXX Re-evaluate based on new routines, and consider IPv6:
# These two routines will sort based upon IP route
sub iprouteval {
my(@a) = ($_[0] =~ m#^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$#);
$a[4] + ($a[3] + 256 * ($a[2] + 256 * ($a[1] + 256 * $a[0])));
}
sub sortbyiproute {
&iprouteval($a) <=> &iprouteval($b);
}
# Clean up lines on input, particularly ANSI characters as a result
# of us not being able to turn off per-session terminal paging
sub filter_lines {
my ($l) = (@_);
#s/^\033\[42D +\033\[42D(.+)$/$1/;
#s/\033\133\064\062\104\s*\033\133\064\062\104//g;
$l =~ s/\033\133\064\062\104\s+\033\133\064\062\104//g;
$l =~ s/\033\133\061\066\104\s+\033\133\061\066\104//g;
$l =~ s/\033\133\064\062\104//g;
$l =~ s/\033\133\061\062\104//g;
$l =~ s/.*\[37D(.*)/$1/g; # MA5600
# Probably not needed:
$l =~ s/\s*---- More ----\s*//;
$l =~ s/^ //; # Comware7
$l =~ s/Synchronization is finished.//g;
return $l;
}
# Some commands are not supported on some models or versions
# of code.
# Remove the associated error messages, and rancid will ensure that
# these are not treated as "missed" commands
sub command_not_valid {
my ($l) = (@_);
if ( $l =~
/% Too many parameters found at '\^' position/ ||
/% Unrecognized command found at '\^' position/ ||
/% Incomplete command found at '\^' position./ ||
/(% )?Wrong parameter found at '\^' position/ ||
/% Wrong device .+/
) {
return(1);
} else {
return(0);
}
}
# Some commands are not authorized under the current
# user's permissions
sub command_not_auth {
my ($l) = (@_);
if ( $l =~
/Permission denied\./
) {
return(1);
} else {
return(0);
}
}
# Some output lines are always skipped
sub skip_pattern {
my ($l) = (@_);
if ( $l =~
/^\s+\^$/ ||
/^$/
) {
return(1);
} else {
return(0);
}
}
sub DisplayFib {
my($INPUT, $OUTPUT, $cmd) = @_;
my($dest, $nexthop, $flag, $outint, $label);
print STDERR " In DisplayFib: $_" if ($debug);
chomp;
# Display the command we're processing in the output:
ProcessHistory("FIB","","","!\n! '$cmd':\n!\n");
while (<$INPUT>) {
tr/\015//d;
last if(/^\s*$prompt/);
chomp;
$_ = filter_lines($_);
return(1) if command_not_valid($_);
return(-1) if command_not_auth($_);
next if skip_pattern($_);
next if /^Destination count: \d+ FIB entry count: \d+/;
# Chop out some detail that changes over time (Comware 3):
s/(\s+)TimeStamp\s+/$1/; # TimeStamp column heading
ProcessHistory("FIB","","","! $_\n");
if ( m,Destination/Mask, ) {
while (<$INPUT>) {
tr/\015//d;
last if(/^\s*$prompt/);
chomp;
$_ = filter_lines($_);
# Chop out some detail that changes over time (Comware 3):
s/(\s+)t\[\d+\]\s+/$1/; # TimeStamp data
# "display fib" on comware7 shows host entries for things
# learned via arp too. For a distribution router, that's all
# the devices on subnets routed by it!
# If we filter out all "UH" entries that are NOT InLoop, we
# get acceptable output.
#
# So we want to keep:
#
# 0.0.0.0/32 127.0.0.1 UH InLoop0 Null
#
# but reject:
#
# 130.159.44.161/32 130.159.44.161 UH Vlan44 Null
#
# However I've a feeling that this is a problematic
# solution, and some object to the notion that rancid
# should be representing such potentially dynamic data in
# the first place, which is why we created the
# $display_fib flag for rancid 2, and in rancid 3 one
# can modify the command table in rancid.types.conf
($dest, $nexthop, $flag, $outint, $label) = split;
next if ( $flag eq 'UH' && $outint !~ /InLoop/ );
#ProcessHistory("FIB", "cmw::iproutesort", "$dest", "! $_\n");
ProcessHistory("FIB", "ipsort", "$dest", "! $_\n");
}
ProcessHistory("FIB", "", "", "!\n");
# return here to ensure that we don't keep swallowing the
# next command's output by returning to the surrounding
# while loop
return(0);
}
}
return(0);
}
sub DisplayIPRoutes {
my($INPUT, $OUTPUT, $cmd) = @_;
my($key,$line,$spaces);
print STDERR " In DisplayIPRoutes: $_" if ($debug);
chomp;
# Display the command we're processing in the output:
ProcessHistory("IPR","","","!\n! '$cmd':\n!\n");
while (<$INPUT>) {
tr/\015//d;
last if(/^\s*$prompt/);
chomp;
$_ = filter_lines($_);
return(1) if command_not_valid($_);
return(-1) if command_not_auth($_);
next if skip_pattern($_);
ProcessHistory("IPR","","","! $_\n");
if ( m,Destination/Mask, ) {
my $lastkey = "";
my $lastspaces = "";
while (<$INPUT>) {
tr/\015//d;
last if(/^\s*$prompt/);
chomp;
$_ = filter_lines($_);
# If the key is blank, indicating multiple nexthops for
# a particular route, then we use the previous one
if ( m/^\s+(.+)/ ) {
$key = $lastkey;
$line = $key . $lastspaces . $1;
#ProcessHistory("IPR", "cmw::iproutesort", "$key", "! $line\n");
ProcessHistory("IPR", "ipsort", "$key", "! $line\n");
}
if ( m/^(\S+)(\s+).+/ ) {
$key = $1;
$line = $_;
$spaces = $2;
#ProcessHistory("IPR", "cmw::iproutesort", "$key", "! $line\n");
ProcessHistory("IPR", "ipsort", "$key", "! $line\n");
# Remember these, we may need them on the next pass
$lastkey = $key;
$lastspaces = $spaces;
}
}
ProcessHistory("IPR", "", "", "!\n");
# return here to ensure that we don't keep swallowing the
# next command's output by returning to the surrounding
# while loop
return(0);
}
}
return(0);
}
## This routine processes general output of "display" commands
sub CommentOutput {
my($INPUT, $OUTPUT, $cmd) = @_;
print STDERR " In CommentOutput: $_" if ($debug);
chomp;
# Display the command we're processing in the output:
ProcessHistory("COMMENTS", "", "", "!\n! '$cmd':\n!\n");
while (<$INPUT>) {
tr/\015//d;
# If we find the prompt, we're done
# Ordinarily this matches from the start of the line, however
# we've seen circumstances at least in Comware7 where the
# prompt is preceded by whitespace, like so:
# ^M^M ^M<router>display boot-loader^M
last if(/^\s*$prompt/);
chomp;
# filter out some junk
$_ = filter_lines($_);
return(1) if command_not_valid($_);
return(-1) if command_not_auth($_);
next if skip_pattern($_);
# Now we skip or modify some lines from various commands to
# remove irrelevant content, or to avoid insignificant diffs
# 'display local-user':
s/\s+Current AccessNum:.+$//;
# 'display version':
next if (/^(Uptime is \d|.+ [Uu]ptime is \d).+$/);
# No longer necessary since skipping the whole Uptime line:
# Mangle these lines:
#s/(.*)[Uu]ptime.*.weeks.*.days*.*hours*.*minutes*(.*)/$1 $2/;
#s/(.*)[Uu]ptime.*days*.*hours*.*minutes*(.*)/$1 $2/;
# MSRs display a 'last reboot' time, but sometimes the seconds
# vary by one or two (presumably internal rounding), so simply make
# the last digit a fixed '0'. It would probably be safer to make
# the last two digits a fixed '00'.
# (Thx Alexander Belokopytov)
s/(^Last reboot.+)\d$/${1}0/;
# 'dir ' commands
if ( $cmd =~ /^dir / ) {
# First field is just an index number, chop it out
s/^\s+\d+\s+(.+)/ $1/;
# Remove filenames that are updated frequently
next if (
/logfile\.log$/ ||
/lauth\.dat$/ ||
/ifindex\.dat$/ ||
/startup\.mdb$/ ||
/private-data\.txt$/ ||
/.+ KB total \(.+ KB free/ ||
/.+ KB total \(.+ KB free/ ||
/\.trash/
);
}
# 'display ospf brief'/'display ospf'
if ( $cmd =~ 'display ospf( brief)?' ) {
#next if (/^(Ospf is not enabled yet|Info: OSPF routing process is not enabled|The feature OSPF has not been enabled.).+$/);
next if (/^\s+SPF (Computation|Scheduled|calculation) Count:.+$/i);
}
if ( $cmd eq 'display power' ) {
next if (/^(\s+Input Power).+$/);
}
if ( $cmd eq 'display poe powersupply' ) {
next if (/^(PSE Total Power Consumption|PSE Available Power|PSE Peak Value|PSE Average Value).+$/);
}
if ( $cmd eq 'display ntp-service status' ) {
next unless m/(Clock status|Clock stratum|Reference clock ID)/i;
}
if ( $cmd eq 'display transceiver interface' ) {
s/^(\S+ transceiver information:).+$/$1/; # filter random garbage
s/^Error: The transceiver is absent.$/ No transceiver present./;
s/^Error: The combo port is inactive.$/ Inactive combo port./;
}
# Add the processed lines to the output buffer:
ProcessHistory("COMMENTS","","","! $_\n");
}
# Add a blank comment line to the output buffer
ProcessHistory("COMMENTS", "", "", "!\n");
return(0);
}
## This routine processes a "display current"
sub DisplayCurrent {
my($INPUT, $OUTPUT, $cmd) = @_;
print STDERR " In DisplayCurrent: $_" if ($debug);
while (<$INPUT>) {
tr/\015//d;
last if(/^\s*$prompt/);
chomp;
$_ = filter_lines($_);
return(1) if command_not_valid($_);
return(-1) if command_not_auth($_);
next if skip_pattern($_);
return(0) if ($found_end);
# Filter out some sensitive data:
if ( $filter_commstr &&
/^ ?(snmp-agent (target-host.+securityname|usm-user|community (read|write)) )(\S+)/
) {
ProcessHistory("","","","! $1<removed>$'\n");
next;
}
if ( $filter_pwds >= 1 &&
/^ ?(password (?:simple|cipher|hash) )(\S+)/ ||
/^ ?(super password( role level-\d)( level \d)? (cipher|simple|hash) )(\S+)/ ||
/^ ?(set authentication password (cipher|simple|hash) )(\S+)/ ||
/^ ?(key (?:authentication|accounting) )(\S+)/
) {
ProcessHistory("","","","! $1<removed>$'\n");
next;
}
# filter ssh public keys of devices connected to from this device
if (/^ ?(public-key-code begin)/ &&
$filter_pwds >= 2) {
ProcessHistory("","","","!$1\n");
ProcessHistory("","","","! <removed>\n");
while (<$INPUT>) {
tr/\015//d;
next if /^$/;
next if /^\s+[[:xdigit:]]$/;
if (/(^ public-key-code end)/) {
ProcessHistory("","","","!$1\n");
last;
}
}
next;
}
# Filter mac addresses dynamically added to config
next if (/^ ?mac-address security.+$/);
ProcessHistory("", "", "", "$_\n");
# end of config
if (/^return/) {
$found_end = 1;
return(0);
}
}
return(0);
}
1;
__END__