#!/usr/local/bin/perl -w
# -*- perl -*-

# Cricket: a configuration, polling and data display wrapper for RRD files
#
#    Copyright (C) 1998-2002 Jeff R. Allen and WebTV Networks, Inc.
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

BEGIN {
    my $programdir = (($0 =~ m:^(.*/):)[0] || "./") . "..";
    eval "require '$programdir/cricket-conf.pl'";
    eval "require '/usr/local/etc/cricket-conf.pl'"
                    unless $Common::global::gInstallRoot;
    $Common::global::gInstallRoot ||= $programdir;
}

use lib "$Common::global::gInstallRoot/lib";
use strict;

use snmpUtils;

use Common::Options;
use Common::Log;

# Default the log level high enough to ignore warnings, unless they
# ask for them with "./listInterfaces -loglevel warn". This is so
# that when we get warnings for trying to talk to extended mibs, the
# user does not freak out.

unshift @ARGV, "-logLevel", "error";

my(%suppress) =();
my($bytes) = 0;
my($usage) = 0;
my($redback) = 0;
my($cisco) = 0;
my($useifnamekey) = 0;
my($useifdesckey) = 0;
my($usealias) = 0;
my($usedescr) = 0;
my($units) = "bits";
my($showspeed) = 0;
Common::Options::commonOptions(
    "speed!", \$showspeed,
    "ifname!", \$useifnamekey,
    "ifdesc!", \$useifdesckey,
    "alias!", \$usealias,
    "descript!", \$usedescr,
    "redback!", \$redback,
    "cisco!", \$cisco,
    "bytes|b!", \$bytes,
    "help|usage|h|?!", \$usage,
    "suppress=s@", \@main::suppress
    );

if ($cisco && $redback) {
    $usage = 1;
} elsif ($cisco) {
    $showspeed = 1;
    $useifdesckey = 1;
    $usealias = 1;
    $useifnamekey = 0;
    $usedescr = 0;
} elsif ($redback) {
    $showspeed = 1;
    $useifnamekey = 1;
    $usedescr = 1;
    $useifdesckey = 0;
    $usealias = 0;
} else {
    $useifdesckey = 1 if ! $useifnamekey;
    $usealias = 1 if ! $usedescr;
}

if ($#ARGV+1 < 1 || $#ARGV+1 > 2 || $usage) {
    print STDERR "Usage: $0 [options] hostname [community]\n";
    print STDERR "\tcommunity will default to public if you don't give it.\n\n";
    print STDERR "Options:\n";
    print STDERR "  --help or --usage or -? show this message\n";
    print STDERR "  --speed \tinclude interface speed in output (platform-dependent)\n";
    print STDERR "  --ifdesc \tuse ifDescr as interface key (default)\n";
    print STDERR "  --ifname \tuse ifName as interface key instead of ifDescr\n";
    print STDERR "  --alias \tuse ifAlias as interface description (default)\n";
    print STDERR "  --descr \tuse ifDescr as interface description\n";
    print STDERR "  --bytes \tuse units of bytes (default bits)\n";
    print STDERR "  --cisco \tsame as --speed --ifdesc --alias \n";
    print STDERR "  --redback \tsame as --speed --ifname --descr \n";
    print STDERR "  --suppress [pattern] \tignore/skip interfaces matching pattern\n";
    print STDERR "  \t\tMultiple --suppress entries may be used.\n\n";
    print STDERR "Examples:\n";
    print STDERR "  $0 --cisco --suppress BVI --suppress Loop mycisco mySNMP\n";;
    print STDERR "Generate interfaces config for a Cisco, including speed information \n";
    print STDERR "per interface but skipping loopback and BVI (bridge) interfaces \n";
    exit 1;
}

$units = "bytes" if $bytes;
my($router) = $ARGV[0];
my($community) = "public";
$community = $ARGV[1] unless (!defined($ARGV[1]));
my($snmp) = "$community\@$router";
print "target --default--\n";
print "    router = $router\n";

print "\n";

my($ifDescr) = '1.3.6.1.2.1.2.2.1.2';
my($ifName) = '1.3.6.1.2.1.31.1.1.1.1';
my($ifAlias) = '1.3.6.1.2.1.31.1.1.1.18';

my($interface_key) = '.' . $ifDescr;
$interface_key = '.' . $ifName if $useifnamekey;
my($interface_name) = $ifAlias;
$interface_name = $ifDescr if $usedescr;

## print("# Read with ifName key $interface_key\n") if $useifnamekey;
my($outputme) = 0;
my($test)= "";
my($row);
foreach $row (snmpUtils::walk($snmp, $interface_key)) {
    my($oid, $value) = split(/:/, $row, 2);

    $oid =~ s/$interface_key//;

    my($stat) = snmpUtils::get($snmp, "1.3.6.1.2.1.2.2.1.7.$oid");
    my($connector) = snmpUtils::get($snmp, "1.3.6.1.2.1.31.1.1.1.17.$oid");
    my($desc) = snmpUtils::get($snmp, "$interface_name.$oid");
    my($ifSpeed) = snmpUtils::get($snmp, "1.3.6.1.2.1.2.2.1.5.$oid");

    # Sanitize $desc value because current Redback SNMP implementation
    # as of 11/2000 returns corrupted garbage in ifDescr sometimes.
    $desc =~ tr/!-~/ /cs; # convert all non-printable ASCIIs to a space

    # second fetched value is oper status -- use it to decide which
    # targets to print

    $outputme = ($stat == 1);

    # suppress array is list of strings to be pattern-matched to
    # decide whether we ignore this interface.
    TEST: foreach $test (@main::suppress) {
        if ($outputme && $value =~ m/$test/) {
            $outputme = 0;
            last TEST;
        }
    }

    if ($outputme) {
        my($target) = $value;
        $target =~ s/[\/\s:]/\_/g;

        print "target $target\n";
        output("interface-name", $value);
        output("short-desc", $desc);
        if ($showspeed && $ifSpeed) {
            output("if-speed", $ifSpeed);
            output("fmt-speed", fmispeed($ifSpeed, $units));
        }

        # experience shows that subinterfaces (like those
        # representing the endpoints of PVCs riding on a
        # frame relay link) need a special config.

        if (defined($connector) && $connector == 2) {
            output("target-type", "sub-interface");
        }
        print "\n";
    }
}

sub output {
    my($name, $value) = @_;
    my($quote) = '';

    if (defined($value)) {
        # quote empty (or white-space only) lines, or lines which
        # will have embedded spaces.
        $quote = '"' if ($value =~ /^\s*$/ || $value =~ /\s/);
        print "\t$name    =    $quote$value$quote\n";
    }
}

sub fmispeed {
    my($number, $units) = @_;
    my @short;

    if ($units eq "bytes") {
        @short = ("Bytes/sec","kBytes/sec","MBytes/sec","GBytes/sec");
    } else {
        @short = ("bits/sec","kbits/sec","Mbits/sec","Gbits/sec");
    }

    my $digits = length("".$number);
    my $divm = 0;
    while ($digits - $divm*3 > 3) { $divm++; }
    my $divnum = $number/10**($divm*3);
    if ($divnum == int $divnum) {
        return sprintf("%1d %s", $divnum, $short[$divm]);
    }
    return sprintf("%1.3f %s", $divnum, $short[$divm]);
}
