#!/usr/local/bin/perl
#
# Check aruba wireless
#
# $Header: /opt/home/doke/work/nagios/RCS/check_aruba,v 1.73 2015/09/18 17:27:25 doke Exp $


use strict;
use warnings;
no warnings 'redefine';
no warnings 'uninitialized';
use Getopt::Long;
use DBI;
use Time::HiRes qw( usleep );
#use lib "/opt/home/doke/work/perl";
use UD::SNMP qw( $mib2 $aruba );
#use Data::Dumper;


use vars qw( $default_community_file 
    $host $community $aptarget $verbose $help $use_snmpv2c $mib2
    $enterprises $aruba @crits @warns @unknowns @oks $rc $sep
    %snmp_sessions %name2ip 
    @perfdata $uds $dbname $dbuser $dbpasswd_file );

$default_community_file = '/usr/local/nagios/etc/aruba_community.pw';

# our local nagios mysql database
$dbname = "nagios";
$dbuser = "nagios";
$dbpasswd_file = "/usr/local/nagios/etc/nagios.pw";

$host = 'aruba-master';
$community = '';
$aptarget = undef;
$verbose = 0;
$help = 0;

$use_snmpv2c = 1;

$mib2 = '1.3.6.1.2.1';
$enterprises = '1.3.6.1.4.1';
$aruba = "$enterprises.14823";

sub usage {
    my( $rc ) = @_;
    print "Usage: $0 [-vh] -H <host> [-C <community>]
       $0 [-vh] [-M <master>] [-C <community>] -a <ap-name>
    Without an ap-name checks the specified controller.
    With an ap-name checks that ap.
    -H s  controller hostname [$host]
    -C s  snmp community [from $default_community_file]
    -a s  access point name
    -v    verbose
    -h    help
";
    exit $rc;
    }

Getopt::Long::Configure ("bundling");
GetOptions(
    'H=s' => \$host,
    'C=s' => \$community,
    'a=s' => \$aptarget,
    'v+' => \$verbose,
    'h' => \$help,
    );
&usage( 0 ) if ( $help );


sub alarm_handler  { 
    die "alarm timeout in pid $$\n";
    exit -1
    }
$SIG{ALRM} = \&alarm_handler;
alarm( 300 );  # make sure we don't hang nagios


if ( ! $community ) {
    $community = get_passwd( $default_community_file ); 
    if ( ! $community ) {
	print "can't open default community file: $!\n";
	exit 3;
	}
    }

if ( $aptarget ) {
    check_ap( $aptarget );
    }
else {
    check_controller();
    }


$rc = 0;
$sep = '';
if ( $#crits >= 0 ) {
    $rc = 2;
    print "CRITICAL ", join( ", ", @crits );
    $sep = '; ';
    }
if ( $#warns >= 0 ) {
    $rc = 1 if ( $rc == 0 );
    print $sep, "Warning ", join( ", ", @warns );
    $sep = '; ';
    }
if ( $#unknowns >= 0 ) {
    $rc = 3 if ( $rc == 0 );
    print $sep, "Unknown ", join( ", ", @unknowns );
    $sep = '; ';
    }
if ( $rc == 0 || $verbose ) {
    print $sep, "Ok ", join( ", ", @oks );
    }
print( " | ", join( ", ", @perfdata ) ) if ( $#perfdata >= 0 );
print "\n";
exit $rc;


##################


sub check_controller {
    my( $result, @oids, $data, $row, $apname, $nrows );

    $uds = UD::SNMP->new();
    $uds->{verbose} = $verbose;

    my $sysDescr_oid = "$mib2.1.1.0";
    my $sysUpTime_oid = "$mib2.1.3.0";
    my $sysName_oid = "$mib2.1.5.0";
    @oids = ( $sysDescr_oid, $sysUpTime_oid, $sysName_oid );
    $result = $uds->snmp_get( $host, $community, 'system', \@oids );
    my $sysDescr = $result->{ $sysDescr_oid };
    my $sysUpTime = $result->{ $sysUpTime_oid };
    my $sysName = $result->{ $sysName_oid };
    if ( $verbose ) {
	print "sysName $sysName\n";
	print "sysDescr $sysDescr\n";
	print "sysUpTime $sysUpTime\n";
	}
    if ( ! $sysDescr ) {
	push @unknowns, "unable to get sysDescr";
	return;
	}
    elsif ( $sysDescr !~ m/ArubaOS/ ) {
	push @unknowns, "not an Aruba controller: $sysDescr";
	return;
	}
    push @oks, "$sysName";
    push @oks, "up $sysUpTime";


    my $role_oid = "$aruba.2.2.1.1.1.4.0";
    my $serialnum_oid = "$aruba.2.2.1.1.1.12.0";
    my $num_aps_oid = "$aruba.2.2.1.1.3.1.0";
    my $num_assocs_oid = "$aruba.2.2.1.1.3.2.0";
    my $swip_oid = "$aruba.2.2.1.2.1.1.0";   # needed to index sysext tables
    my $temp_oid = "$aruba.2.2.1.2.1.10.0";
    #my $num_users_oid = "$aruba.2.2.1.4.1.1.0";   # not useful, wierd counter

    @oids = ( $role_oid, $serialnum_oid, $num_aps_oid, $num_assocs_oid,
	$swip_oid, $temp_oid );
    $result = $uds->snmp_get( $host, $community, 'summary data', \@oids );
    my $role = $result->{ $role_oid };
    my $serialnum = $result->{ $serialnum_oid };
    my $num_aps = $result->{ $num_aps_oid };
    my $num_assocs = $result->{ $num_assocs_oid };
    my $swip = $result->{ $swip_oid };
    my $temp = $result->{ $temp_oid };

    my @roles = ( 'unknown', 'master', 'local', 'backup-master' );
    $verbose && print "role $role $roles[ $role ]\n";
    push @oks, "role " . $roles[ $role ];

    if ( $verbose ) {
	print "serialnum $serialnum\n";
	print "aps $num_aps\n";
	print "associations $num_assocs\n";
	print "swip $swip\n";
	}
    push @oks, "serialnum $serialnum";
    push @oks, "aps $num_aps";
    push @oks, "associations $num_assocs";

    if ( $temp =~ m/normal/i ) {
	push @oks, $temp;
	}
    elsif ( $temp =~ m/^[\w\d\s!-~]+$/i ) {
	push @warns, $temp;
	}
    else {
	# Weird unprintable characters in 6.3
	# push @warns, $temp;
	}

    my $swver_oid = "$aruba.2.2.1.2.1.19.1.4.$swip";
    my $status_oid = "$aruba.2.2.1.2.1.19.1.5.$swip";
    @oids = ( $swver_oid, $status_oid );
    $result = $uds->snmp_get( $host, $community, 'version', \@oids );
    my $swver = $result->{ $swver_oid };
    my $status = $result->{ $status_oid };

    push @oks, "version $swver" if defined ( $swver );

    if ( defined $status ) { 
	my @statuses = ( 'unknown', 'active', 'inactive' );
	$verbose && print "status $status $statuses[ $status ]\n";
	if ( $status != 1 ) {
	    push @warns, "status " . $statuses[ $status ];
	    }
	else {
	    #push @oks, "status " . $statuses[ $status ];
	    }
	}

    # processor load table
    $data = $uds->snmp_table( $host, $community, 'cpuload', "$aruba.2.2.1.2.1.13", [ 2, 3 ] );
    $nrows = scalar( @$data );
    foreach $row ( 1 .. $nrows - 1 ) {

	my $descr = $data->[2][$row];
	next if ( ! defined $descr );
	my $load = $data->[3][$row];
	$verbose && print "$descr load $load%\n";
	if ( $load > 90 ) {
	    push @crits, "$descr load $load%";
	    }
	elsif ( $load > 80 ) {
	    push @warns, "$descr load $load%";
	    }
	else  {
	    #push @oks, "$descr load $load%";
	    }
	}


    # storage table
    $data = $uds->snmp_table( $host, $community, 'storage', "$aruba.2.2.1.2.1.14", [ 2, 3, 4, 5 ] );
    #$nrows = scalar( @$data );
    #foreach $row ( 1 .. $nrows - 1 ) {
    foreach $row ( 1 .. $#$data ) {
	my $type = $data->[$row][2];
	my $size = $data->[$row][3];
	my $used = $data->[$row][4];
	my $name = $data->[$row][5];
	my $pc = int( $used * 100 / $size );
	$verbose && print "$name $pc% full\n";
	if ( $name eq '/' ) {
	    # skip it, it's always 98% full
	    }
	elsif ( $pc > 90 ) {
	    push @crits, "$name $pc% full";
	    }
	elsif ( $pc > 80 ) {
	    push @warns, "$name $pc% full";
	    }
	else  {
	    #push @oks, "$name $pc% full";
	    }
	}


    # memory table
    $data = $uds->snmp_table( $host, $community, 'memory', "$aruba.2.2.1.1.1.11", [ 2, 3, 4 ] );
    $nrows = scalar( @$data );
    foreach $row ( 1 .. $nrows - 1 ) {
	my $size = $data->[$row][2];
	my $used = $data->[$row][3];
	my $free = $data->[$row][4];
	my $pc = int( $used * 100 / $size );
	$verbose && print "memory $pc% full\n";
	if ( $pc > 100 ) {  # shouldn't be possible
	    push @crits, "memory $pc% full";
	    }
	elsif ( $pc > 99 ) {
	    push @warns, "memory $pc% full";
	    }
	else  {
	    #push @oks, "memory $pc% full";
	    }
	}



    # auth server table
    if ( 0 ) {
	$data = $uds->snmp_table_multi_index( $host, $community, 'auth-servers', 
	    "$aruba.2.2.1.8.1.1", [ 2, 13 ] );
	#print "dump ", Dumper( $data ), "\n";
	foreach $row ( sort keys %$data ) {
	    my @a = split( /\./, $row );
	    shift @a;  # loose the length byte
	    my $name = pack( "C*", @a );
	    my $type = $data->{$row}[2];
	    my $responsetime = $data->{$row}[13];
	    $verbose && print "auth server $name avg response time $responsetime ms\n";
	    if ( $responsetime > 5000 ) {
		push @crits, "auth server $name avg response time $responsetime ms";
		}
	    elsif ( $responsetime > 1000 ) {
		push @warns, "auth server $name avg response time $responsetime ms";
		}
	    else  {
		#push @oks, "auth server $name avg response time $responsetime ms";
		}
	    }
	}

    # fixme, do something with alarm mib

    # todo: check licences?  Our current ones say they never expire.

    }  # check_controller








# scan a specified ap 
# return 0 if can't find it on any controller, or otherwise mismatched
# return 1 if found it and it's up
# return 2 if found it and it's down
sub check_ap {
    my( $aptarget ) = @_;
    my( $apref, $controller, $apname, $apip, $apmac, $apmac_oid, $result, @oids,
	@apwlanrow, @apradiorow, $radio, %radios, $val, $oid2, $oid,
	$col, $perf_util, $perf_clients, $bssid, @bssidrow );

    $verbose && print "check( $aptarget )\n";

    $apref = find_ap( $aptarget ) || return 0;

    $controller = $apref->{ controller_ip };
    $apname = $apref->{ name };
    $apip = $apref->{ ipaddr };
    $apmac = $apref->{ wired_macaddr };
    $apmac_oid = sprintf "%d.%d.%d.%d.%d.%d", map( hex( $_ ), split( ':', $apmac ) );
    if ( $verbose ) {
	print "$apname, $apip, $apmac, $apmac_oid, $controller, $apref->{ monitor }\n";
	}

    if ( ! $apref->{ 'monitor' } ) { 
	push @oks, "ignoring because not supposed to be monitored";
	return 1;
	}

    if ( $apref->{ 'status' } ne 'up' 
	    && $apref->{ 'status' } ne 'ok' ) { 
	push @warns, $apref->{ 'status' };
	}

    if ( ! $controller ) { 
	push @unknowns, "can't find controller for $apname";
	return 0;
	}

    $uds = UD::SNMP->new();
    $uds->{verbose} = $verbose;

    my $sysDescr_oid = "$mib2.1.1.0";
    my $sysUpTime_oid = "$mib2.1.3.0";
    my $sysName_oid = "$mib2.1.5.0";
    @oids = ( $sysDescr_oid, $sysUpTime_oid, $sysName_oid );
    $result = $uds->snmp_get( $controller, $community, 'system', \@oids );
    my $sysName = $result->{ $sysName_oid };
    my $sysDescr = $result->{ $sysDescr_oid };
    my $sysUpTime = $result->{ $sysUpTime_oid };
    if ( $verbose ) {
	print "sysName $sysName\n";
	print "sysDescr $sysDescr\n";
	print "sysUpTime $sysUpTime\n";
	}
    if ( ! $sysDescr ) {
	#push @unknowns, "unable to get sysDescr";
	return 0;
	}
    elsif ( $sysDescr !~ m/ArubaOS/ ) {
	#push @unknowns, "not an Aruba controller: $sysDescr";
	return 0;
	}

    # pick the stuff for this mac out of the AP table
    # wlsxWlanAPTable
    # INDEX {wlanAPMacAddress}
    # 1 wlanAPMacAddress                       MacAddress,
    # 2 wlanAPIpAddress                        IpAddress,
    # 3 wlanAPName                                     DisplayString,
    # 4 wlanAPGroupName                        DisplayString,
    # 5 wlanAPModel                            OBJECT IDENTIFIER,
    # 6 wlanAPSerialNumber                     DisplayString,
    # 7 wlanAPdot11aAntennaGain        Integer32,
    # 8 wlanAPdot11gAntennaGain        Integer32,
    # 9 wlanAPNumRadios                        Integer32,
    # 10 wlanAPEnet1Mode                        ArubaEnet1Mode,
    # 11 wlanAPIpsecMode                        ArubaEnableValue,
    # 12 wlanAPUpTime                           TimeTicks,
    # 13 wlanAPModelName                        DisplayString,
    # 14 wlanAPLocation                     DisplayString,
    # 15 wlanAPBuilding                         Integer32,
    # 16 wlanAPFloor                            Integer32,
    # 17 wlanAPLoc                                      Integer32,
    # 18 wlanAPExternalAntenna          ArubaAntennaSetting,
    # 19 wlanAPStatus                           ArubaAPStatus,
    # 20 wlanAPNumBootstraps            Integer32,
    # 21 wlanAPNumReboots                       Integer32,
    # 22 wlanAPUnprovisioned        ArubaUnprovisionedStatus,
    # 23 wlanAPMonitorMode          ArubaMonitorMode,
    # 24 wlanAPFQLNBuilding                     DisplayString,
    # 25 wlanAPFQLNFloor                        DisplayString,
    # 26 wlanAPFQLN                                     DisplayString,
    # 27 wlanAPFQLNCampus                       DisplayString,
    # 28 wlanAPLongitude                        DisplayString,
    # 29 wlanAPLatitude                         DisplayString,
    # 30 wlanAPAltitude                         DisplayString,
    # 31 wlanAPMeshRole             ArubaMeshRole
    undef @oids;
    foreach $col ( 2 .. 31 ) {
	$oid = "$aruba.2.2.1.5.2.1.4.1.$col.$apmac_oid";
	push @oids, $oid;
	}
    $result = $uds->snmp_get( $controller, $community, 'ap table', \@oids );
    foreach $oid ( keys %$result ) {
	if ( $oid =~ m/2\.2\.1\.5\.2\.1\.4\.1\.(\d+)\.$apmac_oid/ ) {
	    $val = $result->{ $oid };
	    $verbose > 1 && print "$oid = $val\n";
	    $apwlanrow[ $1 ] = $val;
	    }
	}

    $apip = $apwlanrow[ 2 ];
    $apname = $apwlanrow[ 3 ];

    my @statuses = ( 'unknown', 'up', 'down' );

    if ( $verbose ) {
	print "ap name $apname, ip $apip, mac $apmac\n";
	print "ap group ", $apwlanrow[ 4 ], "\n";
	#print "ap model ", $apwlanrow[ 5 ], "\n";   # as an index code
	print "ap serial ", $apwlanrow[ 6 ], "\n";
	print "ap num radios ", $apwlanrow[ 9 ], "\n";
	print "ap ipsec mode ", $apwlanrow[ 11 ], "\n";
	print "ap uptime ", $apwlanrow[ 12 ], "\n";
	print "ap model name ", $apwlanrow[ 13 ], "\n";   # as a string
	print "ap external antenna ", $apwlanrow[ 18 ], "\n";   # 1 not present, 2 enabled
	print "ap status ", $apwlanrow[ 19 ], " ", $statuses[ $apwlanrow[19] ], "\n";   # 1 up
	print "ap bootstraps ", $apwlanrow[ 20 ], "\n";
	print "ap reboots ", $apwlanrow[ 21 ], "\n";
	print "ap unprovisioned ", $apwlanrow[ 22 ], "\n";   # 1 up
	print "ap monitor mode ", $apwlanrow[ 23 ], "\n";   # 2 none
	print "ap mesh role ", $apwlanrow[ 31 ], "\n";   # 0 nonmesh
	}

    if ( ! defined $apname ) {
	$apname = '';
	}
    if ( ! defined $apip ) {
	$apip = '';
	}

    if ( $apname ne $aptarget && $apip ne $aptarget ) {
	# We didn't get what we asked for.
	# Maybe it got renamed and the cache is out of date?
	$verbose && print "name mismatch: want $aptarget, got $apname $apip\n";
	return 0;
	}

    if ( $apwlanrow[19] != 1 ) {
	push @crits, $statuses[ $apwlanrow[19] ];
	}
    else {
	push @oks, $statuses[ $apwlanrow[19] ];
	}

    push @oks, $apmac;
    #push @oks, "ip $apip";
    #push @oks, "name $apname";
    push @oks, "ap$apwlanrow[13]";
    push @oks, "sn $apwlanrow[6]";
    push @oks, "group $apwlanrow[4]";






    # pick the stuff for this mac out of the radio table
    # wlsxWlanRadioTable
    # INDEX {wlanAPMacAddress, wlanAPRadioNumber}
    # 1 wlanAPRadioNumber                                      Integer32,
    # 2 wlanAPRadioType                                        ArubaPhyType,
    # 3 wlanAPRadioChannel                                     Integer32,
    # 4 wlanAPRadioTransmitPower                       Integer32,
    # 5 wlanAPRadioMode                                        ArubaAccessPointMode,
    # 6 wlanAPRadioUtilization                         Integer32,
    # 7 wlanAPRadioNumAssociatedClients        Integer32,
    # 8 wlanAPRadioNumMonitoredClients         Integer32,
    # 9 wlanAPRadioNumActiveBSSIDs                     Integer32,
    # 10 wlanAPRadioNumMonitoredBSSIDs          Integer32,
    # 11 wlanAPRadioBearing                             DisplayString,
    # 12 wlanAPRadioTiltAngle                           DisplayString,
    # 13 wlanAPRadioHTMode                                      ArubaHTMode,
    # 14 wlanAPRadioHTExtChannel                        ArubaHTExtChannel,
    # 15 wlanAPRadioHTChannel                           DisplayString


    # this doesn't work, it says the tables are empty...
    #undef @oids;
    #foreach $col ( 2 .. 14 ) {
    #	$oid = "$aruba.2.2.1.5.2.1.5.1.$col.$apmac_oid";
    #	print "oid $oid\n";
    #	$result = snmp_walk( $controller, $community, 'radio', $oid );
    #
    #	foreach $oid2 ( keys %$result ) {
    #	    if ( $oid2 =~ m/2\.2\.1\.5\.2\.1\.5\.1\.$col\.$apmac_oid\.(\d+)$/ ) {
    #		$val = $result->{ $oid2 };
    #		$verbose && print "$oid2 = $val\n";
    #		$apradiorow[ $col ][ $1 ] = $val;
    #		$radios{ $1 } = 1;
    #		}
    #	    }
    #	}

    foreach $radio ( 1 .. $apwlanrow[ 9 ] ) {
	# Get each radio seperately, so an snmp not found for an inactive radio
	# won't hide an active one.
	undef @oids;
	foreach $col ( 2 .. 14 ) {
	    $oid = "$aruba.2.2.1.5.2.1.5.1.$col.$apmac_oid.$radio";
	    push @oids, $oid;
	    }
	$result = $uds->snmp_get( $controller, $community, "radio $radio", \@oids );
	foreach $oid ( keys %$result ) {
	    if ( $oid =~ m/2\.2\.1\.5\.2\.1\.5\.1\.(\d+)\.$apmac_oid\.$radio$/ ) {
		$col = $1;
		$val = $result->{ $oid };
		$verbose > 1 && print "$oid = $val\n";
		$apradiorow[ $col ][ $radio ] = $val;
		}
	    }
	}

    my @radio_types = ( 'unknown', 'dot11a', 'dot11b', 'dot11g', 'dot11ag', 'wired' );

    $perf_util = $perf_clients = 0;
    foreach $radio ( 1 .. $apwlanrow[ 9 ] ) {

	next if ( ! $apradiorow[ 2 ][$radio] || $apradiorow[ 2 ][$radio] eq "noSuchInstance" );

	if ( $verbose ) {
	    print "radio $radio type ", $radio_types[ $apradiorow[ 2 ][$radio] ], "\n";
	    print "radio $radio channel ", $apradiorow[ 3 ][$radio], "\n";
	    printf "radio $radio tx power %0.1f dBm\n", $apradiorow[ 4 ][$radio] / 2;
	    print "radio $radio mode ", $apradiorow[ 5 ][$radio], "\n";   # as an index code
	    print "radio $radio utilization ", $apradiorow[ 6 ][$radio], "\n";
	    print "radio $radio num associated clients ", $apradiorow[ 7 ][$radio], "\n";
	    print "radio $radio num monitored clients ", $apradiorow[ 8 ][$radio], "\n";
	    print "radio $radio num active bssids ", $apradiorow[ 9 ][$radio], "\n";
	    print "radio $radio num monitored bssids ", $apradiorow[ 10 ][$radio], "\n";
	    print "radio $radio bearing ", $apradiorow[ 11 ][$radio], "\n";   # as a string
	    print "radio $radio tilt angle ", $apradiorow[ 12 ][$radio], "\n";   # as a string
	    # 13 ?
	    # 14 ?
	    # 15 channel?
	    }

	push @oks, sprintf( "%s ch%d %2.1fdBm %d%%util %dclients",
	    $radio_types[ $apradiorow[ 2 ][$radio] ],
	    $apradiorow[ 3 ][$radio],
	    $apradiorow[ 4 ][$radio] / 2,
	    $apradiorow[ 6 ][$radio],
	    $apradiorow[ 7 ][$radio] );

	$perf_util = $apradiorow[ 6 ][$radio] if ( $apradiorow[ 6 ][$radio] > $perf_util );
	$perf_clients += $apradiorow[ 7 ][$radio];
	}

    push @perfdata, sprintf "util=%d", $perf_util;
    push @perfdata, sprintf "clients=%d", $perf_clients;






    # wlsxWlanAPBssidTable
    # INDEX {wlanAPMacAddress, wlanAPRadioNumber, wlanAPBSSID}
    # 1 wlanAPBSSID                                            MacAddress,
    # 2 wlanAPESSID                                            DisplayString,
    # 3 wlanAPBssidSlot                                        Unsigned32,
    # 4 wlanAPBssidPort                                        Unsigned32,
    # 5 wlanAPBssidPhyType                                     ArubaPhyType,
    # 6 wlanAPBssidRogueType                           ArubaRogueApType,
    # 7 wlanAPBssidMode                                        INTEGER,
    # 8 wlanAPBssidChannel                                     INTEGER,
    # 9 wlanAPBssidUpTime                                      TimeTicks,
    # 10 wlanAPBssidInactiveTime                        TimeTicks,
    # 11 wlanAPBssidLoadBalancing                       TruthValue,
    # 12 wlanAPBssidNumAssociatedStations       Unsigned32,
    # 13 wlanAPBssidAPMacAddress                        MacAddress,
    # 14 wlanAPBssidPhyNumber                           Integer32,
    # 15 wlanAPBssidHTMode                                      ArubaHTMode,
    # 16 wlanAPBssidHTExtChannel                        ArubaHTExtChannel,
    # 17 wlanAPBssidHTChannel                           DisplayString

    if ( $verbose ) {
	undef @oids;
	foreach $col ( 2, 11, 12, 15, 16, 17 ) {
	    $oid = "$aruba.2.2.1.5.2.1.7.1.$col.$apmac_oid";
	    $result = $uds->snmp_walk( $controller, $community, "bssid table $col", $oid );
	    foreach $oid2 ( keys %$result ) {
		if ( $oid2 =~ m/2\.2\.1\.5\.2\.1\.7\.1\.$col\.$apmac_oid\.(\d+)\.([\d\.]+)$/ ) {
		    $radio = $1;
		    $bssid = $2;
		    $val = $result->{ $oid2 };
		    $verbose > 1 && print "$oid2 = $val\n";
		    $bssidrow[ $col ][ $radio ]{ $bssid } = $val;
		    }
		}
	    }

	foreach $radio ( 1 .. $apwlanrow[ 9 ] ) {
	    foreach $bssid ( sort keys %{$bssidrow[ 2 ][ $radio ]} ) {

		printf "%s radio %s ESSID '%s' BSSID %s\n",
		    $apname,
		    $radio_types[ $apradiorow[ 2 ][ $radio ] ],
		    $bssidrow[ 2 ][ $radio ]{ $bssid },
		    sprintf( "%02x:%02x:%02x:%02x:%02x:%02x",
			split( /\./, $bssid ) );
		printf "    load balancing %s\n", $bssidrow[ 11 ][ $radio ]{ $bssid };
		printf "    stations %d\n", $bssidrow[ 12 ][ $radio ]{ $bssid };
		printf "    HTMode %d\n", $bssidrow[ 15 ][ $radio ]{ $bssid };
		printf "    HTExtChannel %d\n", $bssidrow[ 16 ][ $radio ]{ $bssid };
		printf "    HTChannel %s\n", $bssidrow[ 17 ][ $radio ]{ $bssid };
		}

	    # what else?
	    }
	}





    # fixme, do something with alarm mib

    push @oks, "controller $sysName";

    # fixme, update database last_seen, last_up, status, etc. ?

    return 1;
    }







# search the database for an ap ip or name
# return a reference to it's entry, or 0 on failure
sub find_ap {
    my( $ap ) = @_;
    my( $dbpasswd, $dbh, $best, $sql, $wapdata, $name );

    $verbose && print "find_ap( $ap )\n";

    $dbpasswd = &get_passwd( $dbpasswd_file );
    $dbh = DBI->connect( "dbi:mysql:dbname=$dbname",
        $dbuser, $dbpasswd, {
	    AutoCommit => 0,
	    RaiseError => 0,
	    PrintError => 1,
	    ReadOnly => 1,
	    } );
    if ( ! $dbh ) {
	push @unknowns, "can't open database: " . $dbh->errstr;
	return 0;
	}

    $best = '';
    # load the nagios mysql waps table
    $sql = "select name, wired_macaddr, ipaddr, 
	    status, monitor, firestar, 
	    controller_ip, apstatus, last_seen, last_up
	from waps 
	where name = ? or ipaddr = ? ";
    $wapdata = $dbh->selectall_hashref( $sql, 'name', undef, ( $ap, $ap ) );
    $dbh->disconnect();

    $verbose && printf "loaded %d waps from nagios mysql database\n", 
	scalar( keys %$wapdata );

    foreach $name ( keys %$wapdata ) {
	$verbose && printf "found %-20s = %-17s %-15s %-15s %11s %11s %-7s\n", 
	    $name, 
	    $wapdata->{ $name }{ 'wired_macaddr' },
	    $wapdata->{ $name }{ 'ipaddr' },
	    $wapdata->{ $name }{ 'controller_ip' },
	    timefmt( $wapdata->{ $name }{ 'last_seen' } ),
	    timefmt( $wapdata->{ $name }{ 'last_up' } ),
	    $wapdata->{ $name }{ 'apstatus' };

	# we want the matching one that is up, and most recently seen
	if ( ! $best 
		|| ( $wapdata->{ $best }{ apstatus } ne 'up' 
		    && $wapdata->{ $name }{ apstatus } eq 'up' )
		|| $wapdata->{ $best }{ 'last_seen' } 
		    < $wapdata->{ $name }{ 'last_seen' } 
		|| ( ! $wapdata->{ $best }{ 'monitor' } 
		    && $wapdata->{ $name }{ 'monitor' } ) ) {
	    $best = $name;
	    }
	}

    if ( $best ) {
	return \%{$wapdata->{ $best }};
	}

    $verbose && print "    can't find ap $ap\n";
    return 0;   # can't find it
    }











sub timefmt { 
    my( $time ) = @_;

    if ( $time == 0 ) { 
	return 'never';
	}
    my( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( $time );
    my $timestr = sprintf "%04d%02d%02d_%02d%02d%02d",
	$year + 1900, $mon + 1, $mday, $hour, $min, $sec;
    return $timestr;
    }





sub get_passwd{
    my( $file ) = @_;
    my( $password );

    if ( ! open( pwfH, $file ) )  {
	warn "can't open $file: $!\n";
	return undef;
	}
    $password = <pwfH>;
    chomp( $password );
    close pwfH;
    return $password
    }


