version 1.1, 2007/02/07 16:55:12 |
version 1.6, 2008/09/04 22:05:21 |
|
|
#!/usr/bin/perl |
#!/usr/bin/perl |
# $RedRiver$ |
# $RedRiver: su.cgi,v 1.5 2008/09/04 20:53:55 andrew Exp $ |
######################################################################## |
######################################################################## |
# su.cgi *** a CGI for Trango SU utilities. |
# su.cgi *** a CGI for Trango SU utilities. |
# |
# |
# 2007.02.07 #*#*# andrew fresh <andrew@mad-techies.org> |
# 2007.02.07 #*#*# andrew fresh <andrew@mad-techies.org> |
######################################################################## |
######################################################################## |
# Copyright (C) 2007 by Andrew Fresh |
# Copyright (C) 2007 by Andrew Fresh |
# |
# |
# This program is free software; you can redistribute it and/or modify |
# This program is free software; you can redistribute it and/or modify |
# it under the same terms as Perl itself. |
# it under the same terms as Perl itself. |
######################################################################## |
######################################################################## |
use strict; |
use strict; |
use warnings; |
use warnings; |
|
|
my $host_file = '/conf/wstationinfo/hosts.xml'; |
my $host_file = 'su.yaml'; |
|
|
my $default_mac = '0001DE'; |
my $default_timeout = 5; |
my $default_suid = 'all'; |
my $default_mac = '0001DE'; |
my $default_cir = 256; |
my $default_suid = 'all'; |
my $default_mir = 9999; |
my $default_cir = 256; |
my $Start_SUID = 3; |
my $default_mir = 9999; |
|
my $Start_SUID = 3; |
|
|
use CGI qw/:standard/; |
use CGI qw/:standard/; |
use XML::Simple; |
use YAML qw/ LoadFile Dump /; |
use Net::Telnet::Trango; |
use Net::Telnet::Trango; |
|
|
use File::Basename; |
print header; |
|
|
my $me = basename($0); |
my $aps = get_aps($host_file); |
|
|
print header, |
my ( $header, $body ); |
start_html('Trango SU Utilities'), |
my $head; |
h1('Trango SU Utilities'); |
my $show_form = 0; |
|
|
my $aps = get_aps($host_file); |
if ( param() ) { |
|
my $AP = param('AP'); |
|
|
if (param()) { |
unless ( exists $aps->{$AP} ) { |
|
print h3("AP '$AP' does not exist!"); |
|
print end_html; |
|
exit; |
|
} |
|
|
my $AP = param('AP'); |
my $sumac = param('sumac') || ''; |
|
$sumac =~ s/[^0-9A-Fa-f]//g; |
|
$sumac = uc($sumac); |
|
|
unless (exists $aps->{$AP}) { |
my $suid = param('suid'); |
print h3("AP '$AP' does not exist!"); |
my $test_type = param('test_type'); |
print end_html; |
|
exit; |
|
} |
|
|
|
my $sumac = param('sumac'); |
if ( length $sumac == 12 ) { |
|
( $header, $body ) = add_su( $aps->{$AP}, $sumac, $suid ); |
|
} |
|
elsif ( length $suid ) { |
|
if ( $test_type && $test_type eq 'linktest' ) { |
|
( $header, $body ) = linktest( $aps->{$AP}, $suid ); |
|
} |
|
else { |
|
( $header, $body ) = testrflink( $aps->{$AP}, $suid ); |
|
$head = '<meta http-equiv=refresh content=5>'; |
|
} |
|
} |
|
else { |
|
$header = "Invalid SUID '$suid' and MAC '$sumac'"; |
|
$show_form = 1; |
|
} |
|
|
$sumac =~ s/[^0-9A-Fa-f]//g; |
} |
$sumac = uc($sumac); |
else { |
|
$show_form = 1; |
|
} |
|
|
my $suid = param('suid'); |
if ($header) { |
|
|
if (length $sumac == 12) { |
# We don't really want to do this here because we don't want to refresh if we're adding an SU |
add_su($aps->{$AP}, $sumac); |
if ($head) { |
} elsif (length $suid) { |
print start_html( -title => $header, -head => ["$head"] ); |
testrflink($aps->{$AP}, $suid); |
} |
} else { |
else { |
print h3("Invalid SUID '$suid' and MAC '$sumac'"); |
print start_html($header); |
show_form($aps, $default_mac); |
} |
} |
if ( not defined param('bare') ) { |
|
print h1($header); |
|
} |
|
|
} else { |
if ($body) { |
show_form($aps, $default_mac); |
print $body; |
|
} |
} |
} |
|
else { |
|
print start_html('Trango SU Utilities'), h1('Trango SU Utilities'); |
|
} |
|
|
|
show_form( $aps, $default_mac ) if $show_form; |
|
|
print end_html; |
print end_html; |
|
|
|
sub get_aps { |
|
my $file = shift; |
|
|
sub get_aps |
my $conf = LoadFile($file); |
{ |
|
my $file = shift; |
|
|
|
my $conf = Read_Hosts($file); |
my %aps; |
|
|
my %aps; |
my @hosts; |
|
foreach my $ap ( keys %{$conf} ) { |
|
next if $ap eq 'default'; |
|
my $h = $conf->{$ap}; |
|
|
foreach my $ap (@{ $conf }) { |
if ( $h->{name} |
if ($ap->{'group'} eq 'Trango') { |
=~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.)(\d{1,3})-(\d{1,3})/ ) |
my $name = $ap->{'comment'} || $ap->{'name'}; |
{ |
$aps{ $name } = $ap; |
for ( $2 .. $3 ) { |
} |
my %cur_host; |
} |
foreach my $k ( keys %{$h} ) { |
|
$cur_host{$k} = $h->{$k}; |
|
} |
|
$cur_host{name} = $1 . $_; |
|
if ( !grep { $cur_host{name} eq $h->{name} } values %aps ) { |
|
my $ap_name = $ap . $_; |
|
$aps{$ap_name} = \%cur_host; |
|
} |
|
} |
|
} |
|
else { |
|
$aps{$ap} = $conf->{$ap}; |
|
push @hosts, $h; |
|
} |
|
} |
|
|
return \%aps; |
if ( ref $conf->{default} eq 'HASH' ) { |
|
foreach my $ap ( keys %aps ) { |
|
foreach my $k ( keys %{ $conf->{default} } ) { |
|
$aps{$ap}{$k} ||= $conf->{default}->{$k}; |
|
} |
|
} |
|
} |
|
|
return { |
return \%aps; |
'rrlhcwap0000' => { |
|
group => 'Trango', |
|
version => 1, |
|
name => '192.168.1.1', |
|
port => 161, |
|
Read_Community => 'private', |
|
Write_Community => 'private', |
|
} |
|
}; |
|
|
|
|
return { |
|
'rrlhcwap0000' => { |
|
group => 'Trango', |
|
version => 1, |
|
name => '192.168.1.1', |
|
port => 161, |
|
Read_Community => 'private', |
|
Write_Community => 'private', |
|
} |
|
}; |
|
|
} |
} |
|
|
sub show_form |
sub show_form { |
{ |
my $aps = shift; |
my $aps = shift; |
|
|
|
my %cache = (); |
my %cache = (); |
my @ap_names = sort { |
my @ap_names = sort { |
my @a = $a =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; |
my @a = $a =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; |
my @b = $b =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; |
my @b = $b =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; |
|
|
if (@a) { |
if (@a) { |
$cache{$a} ||= pack('C4' => @a); |
$cache{$a} ||= pack( 'C4' => @a ); |
} else { |
} |
$cache{$a} ||= lc($a); |
else { |
} |
$cache{$a} ||= lc($a); |
if (@b) { |
} |
$cache{$b} ||= pack('C4' => @b); |
if (@b) { |
} else { |
$cache{$b} ||= pack( 'C4' => @b ); |
$cache{$b} ||= lc($b); |
} |
} |
else { |
|
$cache{$b} ||= lc($b); |
|
} |
|
|
$cache{$a} cmp $cache{$b}; |
$cache{$a} cmp $cache{$b}; |
} keys %{ $aps }; |
} keys %{$aps}; |
|
|
print p(start_form(-method => 'GET'), |
print p( |
'AP: ', popup_menu(-name=>'AP', -values=>\@ap_names),br, |
start_form( -method => 'GET' ), |
'SUMAC: ', textfield(-name=>'sumac',-default=>$default_mac),br, |
'AP: ', |
'SUID: ', textfield(-name=>'suid',-default=>$default_suid),br, |
popup_menu( -name => 'AP', -values => \@ap_names ), |
submit, |
br, |
end_form); |
'SUMAC: ', |
|
textfield( -name => 'sumac', -default => $default_mac ), |
|
br, |
|
'SUID: ', |
|
textfield( -name => 'suid', -default => $default_suid ), |
|
br, |
|
'Test Type: ', |
|
radio_group( |
|
-name => 'test_type', |
|
-values => [ 'su testrflink', 'linktest' ], |
|
-default => 'su testrflink', |
|
), |
|
br, submit, end_form |
|
); |
|
|
print p('Fill in the SUMAC if you wish to add an SU ', |
print p( |
'or fill in the SUID to run an rflinktest.'); |
'Fill in the SUMAC if you wish to add an SU ', |
|
'or fill in the SUID to run an RF link test. ', |
|
'If you enter both a valid SUMAC and a numeric SUID, ', |
|
'the SU will be added with that SUID. ', |
|
'If the SUID is already in the AP, it will be deleted ', |
|
'before the new SU is added. ' |
|
); |
|
|
return 1; |
return 1; |
} |
} |
|
|
sub login |
sub login { |
{ |
my $host = shift; |
my $host = shift; |
my $password = shift; |
my $password = shift; |
|
|
|
my $t = new Net::Telnet::Trango ( Timeout => 5 ); |
my $t = new Net::Telnet::Trango( Timeout => $default_timeout ); |
|
|
#$t->input_log('/tmp/telnet_log'); |
#$t->input_log('/tmp/telnet_log'); |
#$t->dump_log('/tmp/telnet_log'); |
#$t->dump_log('/tmp/telnet_log'); |
|
|
unless ($t->open( Host => $host )) { |
unless ( $t->open( Host => $host ) ) { |
print h3("Error connecting!"); |
print h3("Error connecting!"); |
$t->close; |
$t->close; |
return undef; |
return undef; |
} |
} |
|
|
unless ($t->login( $password ) ) { |
unless ( $t->login($password) ) { |
print h3("Couldn't log in: $!"); |
print h3("Couldn't log in: $!"); |
$t->exit; |
$t->exit; |
$t->close; |
$t->close; |
return undef; |
return undef; |
} |
} |
|
|
return $t; |
return $t; |
} |
} |
|
|
sub add_su |
sub add_su { |
{ |
my ( $ap, $sumac, $suid ) = @_; |
my $ap = shift; |
|
my $sumac = shift; |
|
|
|
my $t = login($ap->{'name'}, $ap->{'Telnet_Password'}); |
my $t = login( $ap->{'name'}, $ap->{'Telnet_Password'} ); |
|
|
my $cur_sus = $t->sudb_view; |
my $cur_sus = $t->sudb_view; |
|
|
my $new_suid = next_suid($cur_sus); |
my $new_suid = $suid; |
|
$new_suid =~ s/\D//gxms; |
|
|
foreach my $su (@{ $cur_sus }) { |
if ( !$new_suid ) { |
if ($sumac eq $su->{'mac'}) { |
$new_suid = next_suid($cur_sus); |
print h3("MAC '$sumac' already in AP '$ap->{'name'}' with SUID '$su->{'suid'}'"); |
} |
$t->exit; |
|
$t->close; |
|
return undef; |
|
} |
|
} |
|
|
|
unless ($t->sudb_add( |
my $old_su = ''; |
$new_suid, 'reg', $default_cir, $default_mir, $sumac |
foreach my $su ( @{$cur_sus} ) { |
) ) { |
if ( $new_suid == $su->{'suid'} ) { |
print h3("Error adding SU!"); |
$old_su = $su; |
$t->exit; |
} |
$t->close; |
|
return undef; |
|
} |
|
|
|
my $new_sus = $t->sudb_view; |
if ( $sumac eq $su->{'mac'} ) { |
my $added = 0; |
$t->exit; |
foreach my $su (@{ $new_sus }) { |
$t->close; |
if ($su->{'suid'} == $new_suid) { |
return "MAC '$sumac' already in AP '$ap->{'name'}' " |
$added = 1; |
. "with SUID '$su->{'suid'}'"; |
last; |
} |
} |
} |
} |
|
|
|
unless ($added) { |
my $cir = $default_cir; |
print h3("Couldn't add su id: $new_suid"); |
my $mir = $default_mir; |
$t->exit; |
|
$t->close; |
|
return undef; |
|
} |
|
|
|
unless ($t->save_sudb) { |
if ($old_su) { |
print h3("Couldn't save sudb"); |
$cir = $old_su->{'cir'} if $old_su->{'cir'}; |
$t->exit; |
$mir = $old_su->{'mir'} if $old_su->{'mir'}; |
$t->close; |
|
return undef; |
|
} |
|
|
|
print p( |
if ( !$t->sudb_delete($new_suid) ) { |
"Added new SU with ID '$new_suid' " . |
$t->exit; |
"and MAC '$sumac' " . |
$t->close; |
"to '$ap->{'name'}'. " . |
return "Error removing SU!"; |
'<a href="' . $me . '?' . |
} |
'AP=' . $ap->{'name'} . '&' . |
} |
'suid=' . $new_suid . |
|
'">Test SU RFLink</a>' |
|
); |
|
|
|
$t->exit; |
if ( !$t->sudb_add( $new_suid, 'reg', $cir, $mir, $sumac ) ) { |
$t->close; |
$t->exit; |
return 1; |
$t->close; |
|
return "Error adding SU!"; |
|
} |
|
|
} |
my $new_sus = $t->sudb_view; |
|
my $added = 0; |
|
foreach my $su ( @{$new_sus} ) { |
|
if ( $su->{'suid'} == $new_suid ) { |
|
$added = 1; |
|
last; |
|
} |
|
} |
|
|
sub testrflink |
unless ($added) { |
{ |
$t->exit; |
my $ap = shift; |
$t->close; |
my $suid = shift; |
return "Couldn't add su id: $new_suid"; |
|
} |
|
|
my $t = login($ap->{'name'}, $ap->{'Telnet_Password'}); |
unless ( $t->save_sudb ) { |
|
$t->exit; |
|
$t->close; |
|
return "Couldn't save sudb"; |
|
} |
|
|
my $result = $t->su_testrflink( $suid ); |
$t->exit; |
|
$t->close; |
|
|
unless ($result) { |
my $msg = ''; |
print h3("Error testing SU rflink!"); |
|
$t->exit; |
|
$t->close; |
|
return undef; |
|
} |
|
|
|
my @keys = ('suid', 'AP Tx', 'AP Rx', 'SU Rx'); |
if ($old_su) { |
|
$msg |
|
.= "Removed old SU with ID '$new_suid' " |
|
. "and MAC '" |
|
. $old_su->{'mac'} . "' " |
|
. "from '$ap->{'name'}'. "; |
|
} |
|
|
my @table; |
$msg |
foreach my $su (@{ $result }) { |
.= "Added new SU with ID '$new_suid' " |
next unless ref $su eq 'HASH'; |
. "and MAC '$sumac' " |
next unless exists $su->{'suid'}; |
. "to '$ap->{'name'}'. " |
$su->{'suid'} =~ s/\D//g; |
. '<a href="' |
next unless $su->{'suid'}; |
. url(-relative => 1) |
|
. '?' . 'AP=' |
|
. $ap->{'name'} . '&' . 'suid=' |
|
. $new_suid |
|
. '">Test SU RFLink</a>'; |
|
|
push @table, td([ @{ $su }{ @keys } ]); |
return $msg; |
} |
} |
|
|
print table({-border=>1,-cellspacing=>0,-cellpadding=>1}, |
sub testrflink { |
caption($ap->{'name'} . ': su testrflink ' . $suid), |
my $ap = shift; |
Tr({-align=>'CENTER', -valign=>'TOP'}, |
my $suid = shift; |
[ th(\@keys), @table ] |
|
) |
|
); |
|
|
|
$t->exit; |
my $t = login( $ap->{'name'}, $ap->{'Telnet_Password'} ); |
$t->close; |
|
return 1; |
|
|
|
} |
my $timeout = $default_timeout; |
|
if ( $suid eq 'all' ) { |
|
my $sudb = $t->sudb_view(); |
|
my $count = scalar @{$sudb}; |
|
$timeout = $count * $default_timeout; |
|
} |
|
my $result = $t->su_testrflink( args => $suid, Timeout => $timeout ); |
|
|
sub next_suid |
unless ($result) { |
{ |
$t->exit; |
my $sudb = shift; |
$t->close; |
|
return "Error testing SU rflink!"; |
|
} |
|
|
my $next_id = $Start_SUID; |
my @keys = ( 'suid', 'AP Tx', 'AP Rx', 'SU Rx' ); |
|
|
my %ids = map { $_->{'suid'} => 1 } @{ $sudb }; |
my @table; |
|
foreach my $su ( @{$result} ) { |
|
next unless ref $su eq 'HASH'; |
|
next unless exists $su->{'suid'}; |
|
$su->{'suid'} =~ s/\D//g; |
|
next unless $su->{'suid'}; |
|
|
my $next_key = sprintf('%04d', $next_id); |
push @table, td( [ @{$su}{@keys} ] ); |
while (exists $ids{$next_key}) { |
} |
$next_id++; |
|
$next_key = sprintf('%04d', $next_id); |
|
} |
|
|
|
return $next_id; |
$t->exit; |
|
$t->close; |
|
return $ap->{'name'} . ': su testrflink ' . $suid, |
|
table( |
|
{ -border => 1, -cellspacing => 0, -cellpadding => 1 }, |
|
Tr( { -align => 'CENTER', -valign => 'TOP' }, |
|
[ th( \@keys ), @table ] |
|
) |
|
); |
|
|
} |
} |
|
|
sub Read_Hosts |
sub linktest { |
{ |
my $ap = shift; |
my $file = shift; |
my $suid = shift; |
my $xs = XML::Simple->new(); |
|
|
|
my %Default_SNMP = ( |
if ( !$suid =~ /^\d+$/ ) { |
Read_Community => 'public', |
return "Invalid SUID [$suid]"; |
Telnet_Password => 'password', |
} |
port => 161, |
|
version => 1, |
|
); |
|
|
|
my @hosts; |
my $t = login( $ap->{'name'}, $ap->{'Telnet_Password'} ); |
my $hosts = $xs->XMLin($file, |
|
keyattr => [ 'Station' ], |
my $result = $t->linktest($suid); |
ForceArray => qr/^Host$/, |
|
|
$t->exit; |
|
$t->close; |
|
|
|
unless ($result) { |
|
return "Error testing SU rflink!"; |
|
} |
|
|
|
my @keys = ( |
|
{ caption => 'Overview', |
|
fields => [ |
|
'AP to SU Error Rate', |
|
'SU to AP Error Rate', |
|
'Avg of Throughput', |
|
], |
|
}, |
|
{ caption => 'Details', |
|
fields => [ |
|
'AP Total nTx', |
|
'AP Total nRx', |
|
'AP Total nRxErr', |
|
|
|
'SU Total nTx', |
|
'SU Total nRx', |
|
'SU Total nRxErr', |
|
], |
|
}, |
); |
); |
|
|
foreach my $group (keys %{ $hosts }) { |
my @detail_keys = ( |
next if $group eq 'Read_Community'; |
'AP Tx', 'AP Rx', 'AP RxErr', 'SU Tx', |
|
'SU Rx', 'SU RxErr', 'time', 'rate', |
|
); |
|
|
foreach my $host (@{ $hosts->{$group}->{Host} }) { |
my $html; |
foreach my $item ('Read_Community', 'Telnet_Password', 'port', 'version') { |
foreach my $keys (@keys) { |
$host->{$item} = |
my @table; |
$host->{$item} || |
foreach my $k ( @{ $keys->{fields} } ) { |
$hosts->{$group}->{$item} || |
if ( $result->{$k} ) { |
$hosts->{$item} || |
push @table, td( [ b($k), $result->{$k} ] ); |
$Default_SNMP{$item}; |
|
} |
} |
$host->{group} = $group; |
else { |
|
push @table, td( [] ); |
push @hosts, $host; |
} |
} |
} |
|
$html .= table( |
|
{ -border => 1, -cellspacing => 0, -cellpadding => 1, }, |
|
caption( $keys->{caption} ), |
|
Tr( { -align => 'CENTER', -valign => 'TOP' }, \@table ), |
|
); |
} |
} |
|
|
return \@hosts; |
my @detail_table; |
|
foreach my $test ( @{ $result->{tests} } ) { |
|
push @detail_table, td( [ @{$test}{@detail_keys} ] ); |
|
} |
|
$html .= table( |
|
{ -border => 1, -cellspacing => 0, -cellpadding => 1 }, |
|
caption('Test Details'), |
|
Tr( { -align => 'CENTER', -valign => 'TOP' }, |
|
[ th( \@detail_keys ), @detail_table, ], |
|
), |
|
); |
|
|
|
return $ap->{'name'} . ': linktest ' . $suid, $html; |
} |
} |
|
|
|
sub next_suid { |
|
my $sudb = shift; |
|
|
|
my $next_id = $Start_SUID; |
|
|
|
my %ids = map { $_->{'suid'} => 1 } @{$sudb}; |
|
|
|
my $next_key = sprintf( '%04d', $next_id ); |
|
while ( exists $ids{$next_key} ) { |
|
$next_id++; |
|
$next_key = sprintf( '%04d', $next_id ); |
|
} |
|
|
|
return $next_id; |
|
} |