=================================================================== RCS file: /cvs/trango/Net-Telnet-Trango/lib/Net/Telnet/Trango.pm,v retrieving revision 1.29 retrieving revision 1.53 diff -u -r1.29 -r1.53 --- trango/Net-Telnet-Trango/lib/Net/Telnet/Trango.pm 2007/02/01 17:58:33 1.29 +++ trango/Net-Telnet-Trango/lib/Net/Telnet/Trango.pm 2008/10/29 18:27:31 1.53 @@ -1,5 +1,6 @@ package Net::Telnet::Trango; -# $RedRiver: Trango.pm,v 1.28 2007/02/01 17:10:07 mike Exp $ + +# $RedRiver: Trango.pm,v 1.52 2008/02/18 19:13:32 andrew Exp $ use strict; use warnings; use base 'Net::Telnet'; @@ -16,7 +17,7 @@ use Net::Telnet::Trango; my $t = new Net::Telnet::Trango ( Timeout => 5 ); - $t->open( Host => $fox ) or die "Error connecting: $!"; + $t->open( Host => $ap ) or die "Error connecting: $!"; $t->login('password') or die "Couldn't log in: $!"; @@ -27,12 +28,12 @@ =head1 DESCRIPTION -Perl access to the telnet interface on Trango Foxes, SUs and APs. +Perl access to the telnet interface on Trango APs and SUs. -Another handy feature is that it will parse the output from certain -commands that is in the format "[key1] value1 [key2] value2" and put -those in a hashref that is returned. This makes using the output from -things like sysinfo very easy to do. +A handy feature is that it will parse the output from certain commands that is +in the format "[key1] value1 [key2] value2" and put those in a hashref that is +returned. This makes using the output from things like sysinfo very easy to +do. =head2 EXPORT @@ -40,28 +41,27 @@ =head1 METHODS -=over - =cut our $VERSION = '0.01'; +my $EMPTY = q{}; +my $SPACE = q{ }; + my %PRIVATE = ( - is_connected => 0, - logged_in => 0, + is_connected => 0, + logged_in => 0, ); =pod -=item B +=head2 B - Creates a new Net::Telnet::Trango object. -- Creates a new Net::Telnet::Trango object. + new([Options from Net::Telnet,] + [Decode => 0,]); - new([Options from Net::Telnet,] - [Decode => 0,]); - Same as new from L but sets the default Trango Prompt: -'/#> *$/' +'/[#$]>\s*\Z/' It also takes an optional parameter 'Decode'. If not defined it defaults to 1, if it is set to 0, it will not decode the output and @@ -70,29 +70,29 @@ =cut -sub new -{ - my $class = shift; +sub new { + my $class = shift; - my %args; - if (@_ == 1) { - $args{'Host'} = shift; - } else { - %args = @_; - } + my %args; + if ( @_ == 1 ) { + $args{'Host'} = shift; + } + else { + %args = @_; + } - $args{'Prompt'} ||= '/#> *$/'; + $args{'Prompt'} ||= '/[$#]>\s*\r?\n?$/'; - foreach my $key (keys %args) { - $PRIVATE{$key} = $args{$key}; - } - $PRIVATE{'Decode'} = 1 unless defined $PRIVATE{'Decode'}; - delete $args{'Decode'}; + foreach my $key ( keys %args ) { + $PRIVATE{$key} = $args{$key}; + } + $PRIVATE{'Decode'} = 1 unless defined $PRIVATE{'Decode'}; + delete $args{'Decode'}; - my $self = $class->SUPER::new(%args); - bless $self if ref $self; + my $self = $class->SUPER::new(%args); + bless $self if ref $self; - return $self; + return $self; } # _password @@ -112,6 +112,7 @@ # help [command] # heater [ ] # ipconfig [ ] +# linktest [ [<# of pkts> [<# of cycle>]]] # log [<# of entries, 1..179>] # log <# of entries, 1..179> # logout @@ -171,209 +172,192 @@ =pod -=back +=head1 ACCESSORS -=head2 ACCESSORS - These are usually only set internally. -=over +=head2 B - returns the firmware version -=item B - -- returns the firmware version - Returns the firmware version if available, otherwise undef. It should be available after a successful open(). -=item B +=head2 B - return the type of host you are connected to. -- return the type of host you are connected to. - returns the type of host from the login banner for example M5830S or M5300S. Should be available after a successful open(). -=item B +=head2 B - Status of the connection to host. -- Status of the connection to host. - returns 1 when connected, undef otherwise. -=item B +=head2 B - Status of being logged in to the host. -- Status of being logged in to the host. - returns 1 after a successful login(), 0 if it failed and undef if login() was never called. -=item B +=head2 B - The banner when first connecting to the host. -- The banner when first connecting to the host. - returns the banner that is displayed when first connected at login. Only set after a successful open(). -=item B +=head2 B - The last lines of output from the last cmd(). -- The last lines of output from the last cmd(). - returns, as an array ref, the output from the last cmd() that was run. -=item B +=head2 B - A text output of the last error that was encountered. -- A text output of the last error that was encountered. - returns the last error reported. Probably contains the last entry in last_lines. -=back +=head1 ALIASES -=head2 ALIASES +=head2 B - alias of exit() -=over +Does the same as exit() -=item B +=head2 B - alias of reboot() -- alias of L. +Does the same as reboot() -=item B +=head2 B - alias of save_ss() -- alias of L. +Does the same as save_ss() -=back +=head1 COMMANDS -=head2 COMMANDS - Most of these are just shortcuts to C METHOD)>, as such they accept the same options as C. Specifically they take a named paramater "args", for example: C 'on')> would enable tftpd -=over +=head2 B - The output from the tftpd command -=item B - -- Returns a hash ref of the decoded output from the +Returns a hash ref of the decoded output from the command. Also see enable_tftpd() and disable_tftpd() as those check that it was successfully changed. -=item B +=head2 B - The output from the ver command -- Returns a hash ref of the decoded output from the +Returns a hash ref of the decoded output from the command. -=item B +=head2 B - The output from the sysinfo command -- Returns a hash ref of the decoded output from the +Returns a hash ref of the decoded output from the command. -=item B +=head2 B - Exits the connection -- exits the command session with the Trango and closes +exits the command session with the Trango and closes the connection -=item B +=head2 B - Sends a reboot command -- reboots the Trango and closes the connection +reboots the Trango and closes the connection -=item B +=head2 B - Set or retrieve the remarks. -- Set or retrieve the remarks. - Takes an optional argument, which sets the remarks. If there is no argument, returns the current remarks. my $old_remarks = $t->remarks(); $t->remarks($new_remarks); -=item B +=head2 B - The output from the sulog command -- returns an array ref of hashes containing each log +Returns an array ref of hashes containing each log line. -=item B +=head2 B - saves the sudb -- returns true on success, undef on failure +Returns true on success, undef on failure -=item B +=head2 B - The output from the sulog command -- returns the output from the syslog command +Returns a hashref of the output from the syslog command -=item B +=head2 B - the pipe command -- returns the output from the pipe command +Returns the output from the pipe command -=item B +=head2 B - retrieves the maclist -- returns the output from the maclist command +Returns the output from the maclist command -=item B +=head2 B - resets the maclist. -- resets the maclist. - No useful output. -=item B +=head2 B - eth link command -- returns the output from the eth list command +Returns the output from the eth link command -=item B +This command seems to cause some weird issues. It often will cause the +command after it to appear to fail. I am not sure why. -- returns information about the SU. +=head2 B - gets the su info +Returns information about the SU. + You need to pass in the $suid and it will return the info for that suid. $t->su_info($suid); -=item B +=head2 B - tests the RF Link to an su -- saves the config. + $t->su_testrflink($suid|'all'); +=head2 B - saves the config. + Returns 1 on success, undef on failure. -=cut +=head2 B - sets opmode ap y or returns the opmode + $t->opmode([ap y]); -my $success = 'Success\\.'; +=cut + +my $success = 'Success\\.'; my %COMMANDS = ( - tftpd => { decode => 'all', expect => $success }, - ver => { decode => 'all' }, - sysinfo => { decode => 'all', expect => $success }, - updateflash => { decode => 'all', expect => $success }, - sulog => { decode => 'sulog', expect => $success }, - 'exit' => { no_prompt => 1, cmd_disconnects => 1 }, - reboot => { no_prompt => 1, cmd_disconnects => 1 }, - remarks => { decode => 'all', expect => $success }, - save_sudb => { String => 'save sudb', expect => $success }, - syslog => { expect => $success }, - 'pipe' => { }, # XXX needs a special decode - maclist => { decode => 'maclist' }, - maclist_reset => { String => 'maclist reset', expect => 'done' }, - eth_link => { String => 'eth link', expect => $success }, - su_info => { String => 'su info', decode => 'all', expect => $success }, - save_ss => { String => 'save ss', expect => $success }, - opmode => { decode => 'all', expect => $success }, - # eth r, w and reset??? - #su password??? - #_bootloader - #temp - #heater + _clear => { String => "\n" }, + tftpd => { decode => 'all', expect => $success }, + ver => { decode => 'all' }, + sysinfo => { decode => 'all', expect => $success }, + updateflash => { decode => 'all', expect => $success }, + sulog => { decode => 'sulog', expect => $success }, + 'exit' => { no_prompt => 1, cmd_disconnects => 1 }, + reboot => { no_prompt => 1, cmd_disconnects => 1 }, + remarks => { decode => 'all', expect => $success }, + save_sudb => { String => 'save sudb', expect => $success }, + syslog => { expect => $success }, + 'pipe' => {}, # XXX needs a special decode + maclist => { decode => 'maclist' }, + maclist_reset => { String => 'maclist reset', expect => 'done' }, + eth_link => { String => 'eth link', expect => $success }, + su_info => + { String => 'su info', decode => 'all', expect => $success }, + su_testrflink => + { String => 'su testrflink', decode => 'each', expect => $success }, + save_ss => { String => 'save ss', expect => $success }, + opmode => { decode => 'all', expect => $success }, + arq => { decode => 'all' }, ); my %ALIASES = ( - bye => 'exit', - restart => 'reboot', - Host => 'host', + bye => 'exit', + restart => 'reboot', + Host => 'host', + save_systemseting => 'save_ss', ); -my %ACCESS = map { $_ => 1 } qw( - firmware_version - host_type - is_connected +my %ACCESS = map { $_ => 1 } qw( + firmware_version + host_type + is_connected logged_in login_banner Timeout @@ -383,149 +367,183 @@ Decode ); -sub AUTOLOAD -{ - my $self = shift; +sub AUTOLOAD { + my $self = shift; - my ($method) = (our $AUTOLOAD) =~ /^.*::(\w+)$/ - or die "Weird: $AUTOLOAD"; + my ($method) = ( our $AUTOLOAD ) =~ /^.*::(\w+)$/ + or die "Weird: $AUTOLOAD"; - if (exists $ALIASES{$method}) { - $method = $ALIASES{$method}; - return $self->$method(@_); - } + if ( exists $ALIASES{$method} ) { + $method = $ALIASES{$method}; + return $self->$method(@_); + } - if (exists $COMMANDS{$method}) { - $COMMANDS{$method}{'String'} ||= $method; - $COMMANDS{$method}{'args'} .= ' ' . shift if (@_ == 1); - return $self->cmd(%{ $COMMANDS{$method} }, @_); - } + if ( exists $COMMANDS{$method} ) { + my %cmd; + foreach my $k ( keys %{ $COMMANDS{$method} } ) { + $cmd{$k} = $COMMANDS{$method}{$k}; + } + $cmd{'String'} ||= $method; + $cmd{'args'} .= $SPACE . shift if ( @_ == 1 ); + return $self->cmd( %cmd, @_ ); + } - if (exists $ACCESS{$method}) { - my $prev = $PRIVATE{$method}; - ($PRIVATE{$method}) = @_ if @_; - return $prev; - } + if ( exists $ACCESS{$method} ) { + my $prev = $PRIVATE{$method}; + ( $PRIVATE{$method} ) = @_ if @_; + return $prev; + } - $method = "SUPER::$method"; - return $self->$method(@_); + $method = "SUPER::$method"; + return $self->$method(@_); } =pod -=item B +=head2 B - Open a connection to a Trango AP. -- Open a connection to a Trango AP. - Calls Net::Telnet::open() then makes sure you get a password prompt so you are ready to login() and parses the login banner so you can get host_type() and firmware_version() =cut -sub open -{ - my $self = shift; +sub open { + my $self = shift; - unless ( $self->SUPER::open(@_) ) { - $self->last_error("Couldn't connect to " . $self->host . ": $!"); - return; - } + unless ( $self->SUPER::open(@_) ) { + $self->last_error( "Couldn't connect to " . $self->host . ": $!" ); + return; + } - ## Get to login prompt - unless ($self->waitfor( - -match => '/password: ?$/i', - -errmode => "return", - ) ) { - $self->last_error("problem connecting to host (" . $self->host . "): " . - $self->lastline); - return; - } + ## Get to login prompt + unless ( + $self->waitfor( + -match => '/password: ?$/i', + -errmode => "return", + ) + ) + { + $self->last_error( "problem connecting to host (" + . $self->host . "): " + . $self->lastline ); + return; + } - $self->parse_login_banner($self->lastline); + $self->parse_login_banner( $self->lastline ); - $self->is_connected(1); + $self->is_connected(1); - return $self->is_connected; + return $self->is_connected; } =pod -=item B +=head2 B - Login to the AP. -- Login to the AP. - Calls open() if not already connected, then sends the password and sets logged_in() if successful =cut -sub login -{ - my $self = shift; +sub login { + my $self = shift; - unless ($self->is_connected) { - $self->open or return; - } + unless ( $self->is_connected ) { + $self->open or return; + } - my $password = shift; + my $password = shift; - $self->print($password); - unless ($self->waitfor( - -match => $self->prompt, - -errmode => "return", - ) ) { - $self->last_error("login ($self->host) failed: " . $self->lastline); - return; - } + $self->print($password); + unless ( + $self->waitfor( + -match => $self->prompt, + -errmode => "return", + ) + ) + { + $self->last_error( "login ($self->host) failed: " . $self->lastline ); + return; + } - $self->logged_in(1); + $self->logged_in(1); - return $self->logged_in; + return $self->logged_in; } =pod -=item B +=head2 B - Converts the login_banner to something useful. -- Converts the login_banner to some useful -variables. - Takes a login banner (what you get when you first connect to the Trango) or reads what is already in login_banner() then parses it and sets host_type() and firmware_version() as well as login_banner() =cut -sub parse_login_banner -{ - my $self = shift; +sub parse_login_banner { + my $self = shift; - if (@_) { - $self->login_banner(@_); - } + if (@_) { + $self->login_banner(@_); + } - my $banner = $self->login_banner; + my $banner = $self->login_banner; - my ($type, $sep1, $subtype, $sep2, $ver) = $banner =~ - /Welcome to Trango Broadband Wireless (\S+)([\s-]+)(\S+)([\s-]+)(.+)$/i; + my ( $type, $sep1, $subtype, $sep2, $ver ) = + $banner =~ + /Welcome to Trango Broadband Wireless,? (\S+)([\s-]+)(\S+)([\s-]+)(.+)$/i; - $type .= $sep1 . $subtype; - $ver = $subtype . $sep2 . $ver; + $type .= $sep1 . $subtype; + $ver = $subtype . $sep2 . $ver; - $self->login_banner($banner); - $self->host_type($type); - $self->firmware_version($ver); + $self->login_banner($banner); + $self->host_type($type); + $self->firmware_version($ver); - return 1; + return 1; } =pod -=item B +=head2 B - Link test to SU -- Set the password on SUs connected to the AP. +linktest('suid'[, 'pkt len, bytes'[, '# of pkts'[, '# of cycles']]]); +Returns a hash reference to the results of the test + +=cut + +sub linktest +{ + my $self = shift; + my $suid = shift; + # These numbers are what I found as defaults when running the command + my $pkt_len = shift || 1600; + my $pkt_cnt = shift || 500; + my $cycles = shift || 10; + + my %config = @_; + + # * 2, one for the FromAP, one FromSU. Then / 1000 to get to ms. + # XXX This might need to be changed, this makes the default timeout the + # same as $pkt_len, and that might not be enough at slower speeds. + $config{Timeout} ||= int(($pkt_len * $pkt_cnt * $cycles * 2 ) / 1000); + + my $string = join $SPACE, 'linktest', $suid, $pkt_len, $pkt_cnt, $cycles; + return $self->cmd( + %config, + String => $string, + decode => 'linktest', + ); + +} + +=pod + +=head2 B - Set the password on SUs connected to the AP. + su_password('new_password'[, 'suid']) If no suid is specified, the default is "all". @@ -533,130 +551,120 @@ =cut -sub su_password -{ - my $self = shift; - my $new_pass = shift || ''; - my $su = shift || 'all'; +sub su_password { + my $self = shift; + my $new_pass = shift || $EMPTY; + my $su = shift || 'all'; - unless (defined $new_pass) { - $self->last_error("No new password"); - #return; - } + unless ( defined $new_pass ) { + $self->last_error("No new password"); - return $self->cmd(String => 'su password ' . - $su . ' ' . - $new_pass . ' ' . - $new_pass, - expect => $success, - ); + #return; + } + + return $self->cmd( + String => 'su password ' . $su . $SPACE . $new_pass . $SPACE . $new_pass, + expect => $success, + ); } =pod -=item B +=head2 B - Change IP configuration on SUs connected to the AP. -- Change IP configuration on SUs connected to -the AP. - su_ipconfig( 'suid', 'new_ip', 'new_subnet', 'new_gateway' ) $t->su_ipconfig( 5, '10.0.1.5', '255.255.255.0', '10.0.1.1' ); =cut -sub su_ipconfig -{ - my $self = shift; +sub su_ipconfig { + my $self = shift; - my $suid = shift; - my $new_ip = shift; - my $new_subnet = shift; - my $new_gateway = shift; + my $suid = shift; + my $new_ip = shift; + my $new_subnet = shift; + my $new_gateway = shift; - if ($suid =~ /\D/) { - $self->last_error("Invalid suid '$suid'"); - return; - } - unless ($new_ip) { - $self->last_error("no new_ip passed"); - return; - } - unless ($new_subnet) { - $self->last_error("no new_subnet passed"); - return; - } - unless ($new_gateway) { - $self->last_error("no new_gateway passed"); - return; - } - - # su ipconfig - return $self->cmd(String => 'su ipconfig ' . - $suid . ' ' . - $new_ip . ' ' . - $new_subnet . ' ' . - $new_gateway, - expect => $success, - ); + if ( $suid =~ /\D/ ) { + $self->last_error("Invalid suid '$suid'"); + return; + } + unless ($new_ip) { + $self->last_error("no new_ip passed"); + return; + } + unless ($new_subnet) { + $self->last_error("no new_subnet passed"); + return; + } + unless ($new_gateway) { + $self->last_error("no new_gateway passed"); + return; + } + + # su ipconfig + return $self->cmd( + String => 'su ipconfig ' . $suid . $SPACE . $new_ip . $SPACE + . $new_subnet . $SPACE + . $new_gateway, + expect => $success, + ); } =pod -=item B +=head2 B - Returns the output from the sudb view command -- Returns the output from the sudb view command - returns a reference to an array of hashes each containing these keys 'suid', 'type', 'cir', 'mir' and 'mac' =cut -sub sudb_view -{ - my $self = shift; +sub sudb_view { + my $self = shift; - my $lines = $self->cmd( String => 'sudb view', expect => $success ) || []; + my $lines = $self->cmd( String => 'sudb view', expect => $success ) || []; - return unless @{ $lines }; + return unless @{$lines}; - unless ($PRIVATE{'Decode'}) { - return $lines; - } + unless ( $PRIVATE{'Decode'} ) { + return $lines; + } - my @sus; - foreach (@{ $lines }) { - next unless $_; - if (/^\[(\d+)\]\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9A-Fa-f\s]+)$/) { - my %s = ( - suid => $1, - type => $2, - cir => $3, - mir => $4, - mac => $5, - ); + my @sus; + foreach ( @{$lines} ) { + next unless $_; + if (/^\[(\d+)\]\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9A-Fa-f\s]+)$/) { + my %s = ( + suid => $1, + type => $2, + cir => $3, + mir => $4, + mac => $5, + ); - $s{'mac'} =~ s/\s//g; - $s{'mac'} = uc($s{'mac'}); + $s{'mac'} =~ s/\s//g; + $s{'mac'} = uc( $s{'mac'} ); - push @sus, \%s; + push @sus, \%s; + } } - } - return \@sus; + return \@sus; } =pod -=item B +=head2 B - Adds an su to the sudb Takes the following paramaters - suid : numeric, - type : (reg|pr) - cir : numeric, - mir : numeric, - mac : Almost any format, it will be reformatted, + suid : numeric, + type : (reg|pr) + cir : numeric, + mir : numeric, + mac : Almost any format, it will be reformatted, and returns true on success or undef otherwise. @@ -667,57 +675,52 @@ =cut -sub sudb_add -{ - my $self = shift; - my $suid = shift; - my $type = shift; - my $cir = shift; - my $mir = shift; - my $mac = shift; +sub sudb_add { + my $self = shift; + my $suid = shift; + my $type = shift; + my $cir = shift; + my $mir = shift; + my $mac = shift; - if ($suid =~ /\D/) { - $self->last_error("Invalid suid '$suid'"); - return; - } + if ( $suid =~ /\D/ ) { + $self->last_error("Invalid suid '$suid'"); + return; + } - unless (lc($type) eq 'reg' || lc($type) eq 'pr') { - $self->last_error("Invalid type '$type'"); - return; - } + unless ( lc($type) eq 'reg' || lc($type) eq 'pr' ) { + $self->last_error("Invalid type '$type'"); + return; + } - if ($cir =~ /\D/) { - $self->last_error("Invalid CIR '$cir'"); - return; - } + if ( $cir =~ /\D/ ) { + $self->last_error("Invalid CIR '$cir'"); + return; + } - if ($mir =~ /\D/) { - $self->last_error("Invalid MIR '$mir'"); - return; - } + if ( $mir =~ /\D/ ) { + $self->last_error("Invalid MIR '$mir'"); + return; + } - my $new_mac = $mac; - $new_mac =~ s/[^0-9A-Fa-f]//; - unless (length $new_mac == 12) { - $self->last_error("Invalid MAC '$mac'"); - return; - } - $new_mac = join ' ', $new_mac =~ /../g; + my $new_mac = $mac; + $new_mac =~ s/[^0-9A-Fa-f]//; + unless ( length $new_mac == 12 ) { + $self->last_error("Invalid MAC '$mac'"); + return; + } + $new_mac = join $SPACE, $new_mac =~ /../g; - my $string = 'sudb add ' . - $suid . ' ' . - $type . ' ' . - $cir . ' ' . - $mir . ' ' . - $new_mac; + my $string = + 'sudb add ' . $suid . $SPACE . $type . $SPACE . $cir . $SPACE . $mir . $SPACE + . $new_mac; - - return $self->cmd( String => $string, expect => $success ); + return $self->cmd( String => $string, expect => $success ); } =pod -=item B +=head2 B - removes an su from the sudb Takes either 'all' or the suid of the su to delete and returns true on success or undef otherwise. @@ -729,23 +732,22 @@ =cut -sub sudb_delete -{ - my $self = shift; - my $suid = shift; +sub sudb_delete { + my $self = shift; + my $suid = shift; - #if (lc($suid) ne 'all' || $suid =~ /\D/) { - if ($suid =~ /\D/) { - $self->last_error("Invalid suid '$suid'"); - return; - } + #if (lc($suid) ne 'all' || $suid =~ /\D/) { + if ( $suid =~ /\D/ ) { + $self->last_error("Invalid suid '$suid'"); + return; + } - return $self->cmd( String => 'sudb delete ' . $suid, expect => $success ); + return $self->cmd( String => 'sudb delete ' . $suid, expect => $success ); } =pod -=item B +=head2 B - changes the su information in the sudb Takes either the suid of the su to change as well as what you are changing, either "cir, mir or su2su" @@ -762,90 +764,85 @@ =cut -sub sudb_modify -{ - my $self = shift; - my $suid = shift; - my $opt = shift; - my $value = shift; +sub sudb_modify { + my $self = shift; + my $suid = shift; + my $opt = shift; + my $value = shift; - if ($suid =~ /\D/) { - $self->last_error("Invalid suid '$suid'"); - return; - } + if ( $suid =~ /\D/ ) { + $self->last_error("Invalid suid '$suid'"); + return; + } - if (lc($opt) eq 'cir' or lc($opt) eq 'mir') { - if ($value =~ /\D/) { - $self->last_error("Invalid $opt '$value'"); - return; - } - } elsif (lc($opt) eq 'su2su') { - if ($value =~ /[^0-9A-Za-f]/) { - $self->last_error("Invalid MAC '$value'"); - return; - } - } else { - $self->last_error("Invalid option '$opt'"); - return; - } + if ( lc($opt) eq 'cir' or lc($opt) eq 'mir' ) { + if ( $value =~ /\D/ ) { + $self->last_error("Invalid $opt '$value'"); + return; + } + } + elsif ( lc($opt) eq 'su2su' ) { + if ( $value =~ /[^0-9A-Za-f]/ ) { + $self->last_error("Invalid MAC '$value'"); + return; + } + } + else { + $self->last_error("Invalid option '$opt'"); + return; + } - my $string = 'sudb modify ' . $suid . ' ' . $opt . ' ' . $value; + my $string = 'sudb modify ' . $suid . $SPACE . $opt . $SPACE . $value; - return $self->cmd( String => $string, expect => $success ); + return $self->cmd( String => $string, expect => $success ); } =pod -=item B +=head2 B - enable the TFTP server -- enable the TFTP server - runs C 'on')> and makes sure that Tftpd is now 'listen'ing =cut -sub enable_tftpd -{ - my $self = shift; +sub enable_tftpd { + my $self = shift; - my $vals = $self->tftpd( args => 'on' ); + my $vals = $self->tftpd( args => 'on' ); - if ($vals->{'Tftpd'} eq 'listen') { - return $vals; - } else { - return; - } + if ( ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'listen' ) { + return $vals; + } + else { + return; + } } =pod -=item B +=head2 B - disable the TFTP server -- disable the TFTP server - runs C 'off')> and makes sure that Tftpd is now 'disabled' =cut -sub disable_tftpd -{ - my $self = shift; +sub disable_tftpd { + my $self = shift; - my $vals = $self->tftpd( args => 'off' ); + my $vals = $self->tftpd( args => 'off' ); - if (ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'disabled') { - return $vals; - } else { - return; - } + if ( ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'disabled' ) { + return $vals; + } + else { + return; + } } =pod -=item B +=head2 B - runs a command on the AP. -- runs a command on the AP. - This does most of the work. At the heart, it calls Net::Telnet::cmd() but it also does some special stuff for Trango. @@ -857,319 +854,430 @@ In addition to the Net::Telnet::cmd() options, it also accepts these: -I - +I - if this is true, then it will send the output lines to _decode_lines() and then returns the decoded output I - - if this is true, it does not wait for a prompt, so you are not stuck waiting for something that will never happen. I - - if this is true, it then sets logged_in() to false, then it will close() the connection and set is_connected() to false I - - if this is set (usually to 'Success.') it will check for that in the last line of output and if it does not, will return undef because the command probably failed I - - a string containing the command line options that are passed to the command - $t->cmd( String => 'exit', no_prompt => 1, cmd_disconnects => 1 ); + $t->cmd( String => 'exit', no_prompt => 1, cmd_disconnects => 1 ); =cut -sub cmd -{ - my $self = shift; +sub cmd { + my $self = shift; - my @valid_net_telnet_opts = qw( - String - Output - Cmd_remove_mode - Errmode - Input_record_separator - Ors - Output_record_separator - Prompt - Rs - Timeout - ); + my @valid_net_telnet_opts = qw( + String + Output + Cmd_remove_mode + Errmode + Input_record_separator + Ors + Output_record_separator + Prompt + Rs + Timeout + ); - my %cfg; - if (@_ == 1) { - $cfg{'String'} = shift; - } elsif (@_ > 1) { - %cfg = @_; - } + my %cfg; + if ( @_ == 1 ) { + $cfg{'String'} = shift; + } + elsif ( @_ > 1 ) { + %cfg = @_; + } - $cfg{'Timeout'} ||= $self->Timeout; + $cfg{'Timeout'} ||= $self->Timeout; - unless ($cfg{'String'}) { - $self->last_error("No command passed"); - return; - } + unless ( $cfg{'String'} ) { + $self->last_error("No command passed"); + return; + } - unless ($self->is_connected) { - $self->last_error("Not connected"); - return; - } + unless ( $self->is_connected ) { + $self->last_error("Not connected"); + return; + } - unless ($self->logged_in) { - $self->last_error("Not logged in"); - return; - } + unless ( $self->logged_in ) { + $self->last_error("Not logged in"); + return; + } + my %cmd; + foreach (@valid_net_telnet_opts) { + if ( exists $cfg{$_} ) { + $cmd{$_} = $cfg{$_}; + } + } + if ( $cfg{'args'} ) { + $cmd{'String'} .= $SPACE . $cfg{'args'}; + } - my %cmd; - foreach (@valid_net_telnet_opts) { - if (exists $cfg{$_}) { - $cmd{$_} = $cfg{$_}; + #print "Running cmd $cmd{String}\n"; + my @lines; + if ( $cfg{'no_prompt'} ) { + $self->print( $cmd{'String'} ); + @lines = $self->lastline; } - } - if ($cfg{'args'}) { - $cmd{'String'} .= ' ' . $cfg{'args'}; - } - my @lines; - unless ($cfg{'no_prompt'}) { - @lines = $self->SUPER::cmd(%cmd); - } else { - $self->print($cmd{'String'}); - @lines = $self->lastline; - } + else { + @lines = $self->SUPER::cmd(%cmd); + } - $self->last_lines(\@lines); + $self->last_lines( \@lines ); - my $vals = 1; - if ($PRIVATE{'Decode'} && $cfg{'decode'}) { - if ($cfg{'decode'} eq 'each') { - $vals = _decode_each_line(@lines); - } elsif ($cfg{'decode'} eq 'sulog') { - $vals = _decode_sulog(@lines); - } elsif ($cfg{'decode'} eq 'maclist') { - $vals = _decode_maclist(@lines); - } else { - $vals = _decode_lines(@lines); + my $last = $self->lastline; + my $prompt = $self->prompt; + $prompt =~ s{^/}{}xms; + $prompt =~ s{/[gixms]*$}{}xms; + while (@lines && $last =~ qr($prompt)) { + pop @lines; + $last = $lines[-1]; } - } + $self->last_error($EMPTY); - $self->last_vals($vals); + my $vals = 1; + if ( $PRIVATE{'Decode'} && $cfg{'decode'} ) { + if ( $cfg{'decode'} eq 'each' ) { + $vals = _decode_each_line(@lines); + } + elsif ( $cfg{'decode'} eq 'sulog' ) { + $vals = _decode_sulog(@lines); + } + elsif ( $cfg{'decode'} eq 'maclist' ) { + $vals = _decode_maclist(@lines); + if (! $vals) { + $self->last_error("Error decoding maclist"); + } + } + elsif ( $cfg{'decode'} eq 'linktest' ) { + $vals = _decode_linktest(@lines); + if (! $vals) { + $self->last_error("Error decoding linktest"); + } + } + else { + $vals = _decode_lines(@lines); + } + } + $self->last_vals($vals); + if ( ( not $cfg{'expect'} ) || $last =~ /$cfg{'expect'}$/ ) { + if ( $cfg{'cmd_disconnects'} ) { + $self->logged_in(0); + $self->close; + $self->is_connected(0); + } - my $last = $self->lastline; - - if ((not $cfg{'expect'}) || $last =~ /$cfg{'expect'}$/) { - if ($cfg{'cmd_disconnects'}) { - $self->logged_in(0); - $self->close; - $self->is_connected(0); + if ( $PRIVATE{'Decode'} && $cfg{'decode'} ) { + return $vals; + } + else { + return \@lines; + } } + else { + my $err; + if (grep { /\[ERR\]/ } @lines) { + $err = _decode_lines(@lines); + } - if ($PRIVATE{'Decode'} && $cfg{'decode'}) { - return $vals; - } else { - return \@lines; + if (ref $err eq 'HASH' && $err->{ERR}) { + $self->last_error($err->{ERR} ); + } else { + $self->last_error("Error with command ($cmd{'String'}): $last"); + } + return; } - } else { - $self->last_error("Error with command ($cfg{'String'}): $last"); - return; - } } #=item _decode_lines -sub _decode_lines -{ - my @lines = @_; +sub _decode_lines { + my @lines = @_; - my %conf; + my %conf; - my $key = ''; - my $val = undef; - my @vals; - my $in_key = 0; - my $in_val = 1; + my $key = $EMPTY; + my $val = undef; + my @vals; + my $in_key = 0; + my $in_val = 1; - foreach my $line (@lines) { - next if $line =~ /$success$/; + LINE: while (my $line = shift @lines) { + next LINE if $line =~ /$success\Z/; + next LINE if $line =~ /^ \*+ \s+ \d+ \s+ \*+ \Z/xms; - my @chars = split //, $line; + # Special decode for sysinfo on a TrangoLink 45 + if ($line =~ /^(.* Channel \s+ Table):\s*(.*)\Z/xms) { + my $key = $1; + my $note = $2; - my $last_key = ''; - foreach my $c (@chars) { + my %vals; + while ($line = shift @lines) { + if ($line =~ /^\Z/) { + $conf{$key} = \%vals; + $conf{$key}{note} = $note; + next LINE; + } - if ($c eq '[' || $c eq "\r" || $c eq "\n") { - if ($c eq '[') { - $in_key = 1; - $in_val = 0; - } else { - $in_key = 0; - $in_val = 1; + my $decoded = _decode_lines($line); + if ($decoded) { + %vals = (%vals, %{ $decoded }); + } + } } + # Another special decode for the TrangoLink + elsif ($line =~ /^ + RF \s Band \s \# + (\d+) \s+ + \( ([^\)]+) \) \s* + (.*)$ + /xms) { + my $num = $1; + my $band = $2; + my $extra = $3; - if ($key) { - $key =~ s/^\s+//; - $key =~ s/\s+$//; + if ($extra =~ /\[/) { + my $decoded = _decode_lines($extra); + $conf{'RF Band'}{$num} = $decoded; + } + else { + $conf{'RF Band'}{$num}{$extra} = 1; + } + next LINE; + } - if ($val) { - $val =~ s/^\s+//; - $val =~ s/\s+$//; - } + my @chars = split //, $line; - if ($key eq 'Checksum' && $last_key) { - # Special case for these bastids. - my $new = $last_key; - $new =~ s/\s+\S+$//; - $key = $new . " " . $key; - } + my $last_key = $EMPTY; + foreach my $c (@chars) { - $conf{$key} = $val; - $last_key = $key; - $key = ''; - } elsif ($val) { - push @vals, $val; - } - $val = ''; + if ( $c eq '[' || $c eq "\r" || $c eq "\n" ) { + if ( $c eq '[' ) { + $in_key = 1; + $in_val = 0; + } + else { + $in_key = 0; + $in_val = 1; + } - } elsif ($c eq ']') { - $in_val = 1; - $in_key = 0; - $c = shift @chars; + if ($key) { + $key =~ s/^\s+//; + $key =~ s/\s+$//; - } elsif ($in_key) { - $key .= $c; + if ($val) { + $val =~ s/^\s+//; + $val =~ s/\s+$//; + } - } elsif ($in_val) { - $val .= $c; - } + if ( $key eq 'Checksum' && $last_key ) { + + # Special case for these bastids. + my $new = $last_key; + $new =~ s/\s+\S+$//; + $key = $new . $SPACE . $key; + } + + $conf{$key} = $val; + $last_key = $key; + $key = $EMPTY; + } + elsif ($val) { + push @vals, $val; + } + $val = $EMPTY; + + } + elsif ( $c eq ']' ) { + $in_val = 1; + $in_key = 0; + $c = shift @chars; + + } + elsif ($in_key) { + $key .= $c; + + } + elsif ($in_val) { + $val .= $c; + } + } } - } - unless ($key) { - push @vals, $val; - } + unless ($key) { + push @vals, $val; + } - if (@vals == 1) { - $val = $vals[0]; - } elsif (@vals) { - $val= \@vals; - } else { - $val = undef; - } + if ( @vals == 1 ) { + $val = $vals[0]; + } + elsif (@vals) { + $val = \@vals; + } + else { + $val = undef; + } - if (%conf) { - $conf{_pre} = $val if $val; - return \%conf; - } else { - return $val; - } + if (%conf) { + $conf{_pre} = $val if $val; + return \%conf; + } + else { + return $val; + } } #=item _decode_each_line -sub _decode_each_line -{ - my @lines = @_; - my @decoded; - foreach my $line (@lines) { - my $decoded = _decode_lines($line); - push @decoded, $decoded if defined $decoded; - } - return \@decoded; +sub _decode_each_line { + my @lines = @_; + my @decoded; + foreach my $line (@lines) { + my $decoded = _decode_lines($line); + push @decoded, $decoded if defined $decoded; + } + return \@decoded; } +#=item _decode_linktest + +sub _decode_linktest { + my @lines = @_; + my %decoded; + foreach my $line (@lines) { + + if ($line =~ s/^(\d+) \s+ //xms) { + my $line_id = $1; + my ($tm, $rt); + if ($line =~ s/\s+ (\d+ \s+ \w+) \s* $//xms) { + $rt = $1; + } + if ($line =~ s/\s+ (\d+ \s+ \w+) \s* $//xms) { + $tm = $1; + } + + my $d = _decode_lines($line. "\n"); + $decoded{tests}[$line_id] = $d; + $decoded{tests}[$line_id]{'time'} = $tm; + $decoded{tests}[$line_id]{rate} = $rt; + } + + else { + my $d = _decode_lines($line . "\n"); + if ($d) { + while (my ($k, $v) = each %{ $d }) { + $decoded{$k} = $v; + } + } + } + + } + return \%decoded; +} + #=item _decode_sulog -sub _decode_sulog -{ - my @lines = @_; - my @decoded; - my $last_tm; - foreach my $line (@lines) { - my $decoded = _decode_lines($line); +sub _decode_sulog { + my @lines = @_; + my @decoded; + my $last_tm; + foreach my $line (@lines) { + my $decoded = _decode_lines($line); - if (defined $decoded) { - if ($decoded->{'tm'}) { - $last_tm = $decoded->{'tm'}; - next; - } else { - $decoded->{'tm'} = $last_tm; - } - next unless $last_tm; + if ( defined $decoded ) { + if ( $decoded->{'tm'} ) { + $last_tm = $decoded->{'tm'}; + next; + } + else { + $decoded->{'tm'} = $last_tm; + } + next unless $last_tm; - push @decoded, $decoded if defined $decoded; + push @decoded, $decoded if defined $decoded; + } } - } - return \@decoded; + return \@decoded; } #=item _decode_maclist -sub _decode_maclist -{ - my @lines = @_; - my @decoded; - my $total_entries = 0; - my $current_tm = 0; - foreach my $line (@lines) { - $line =~ s/\r?\n$//; - my ($mac, $loc, $tm) = $line =~ / - ([0-9a-fA-F ]{17})\s+ - (.*)\s+ - tm\s+ - (\d+) - /x; +sub _decode_maclist { + my @lines = @_; + my @decoded; + my $total_entries = 0; + my $current_tm = 0; + foreach my $line (@lines) { + $line =~ s/\r?\n$//; + my ( $mac, $loc, $tm ) = $line =~ / + ([0-9a-fA-F ]{17})\s+ + (.*)\s+ + tm\s+ + (\d+) + /x; - if ($mac) { - $mac =~ s/\s+//g; - $loc =~ s/^\s+//; - $loc =~ s/\s+$//; + if ($mac) { + $mac =~ s/\s+//g; + $loc =~ s/^\s+//; + $loc =~ s/\s+$//; - my $suid = undef; - if ($loc =~ /suid\s+=\s+(\d+)/) { - $suid = $1; - $loc = undef; - } + my $suid = undef; + if ( $loc =~ /suid\s+=\s+(\d+)/ ) { + $suid = $1; + $loc = undef; + } - push @decoded, { - mac => $mac, - loc => $loc, - tm => $tm, - suid => $suid, - }; - } elsif ($line =~ /(\d+)\s+entries/) { - $total_entries = $1; - } elsif ($line =~ /current tm = (\d+)\s+sec/) { - $current_tm = $1 - } - } + push @decoded, + { + mac => $mac, + loc => $loc, + tm => $tm, + suid => $suid, + }; + } + elsif ( $line =~ /(\d+)\s+entries/ ) { + $total_entries = $1; + } + elsif ( $line =~ /current tm = (\d+)\s+sec/ ) { + $current_tm = $1; + } + } - map { $_->{'cur_tm'} = $current_tm } @decoded; + map { $_->{'cur_tm'} = $current_tm } @decoded; - if (scalar @decoded == $total_entries) { - return \@decoded; - } else { - # XXX we should have a way to set last error, not sure why we don't - return; - } + if ( scalar @decoded == $total_entries ) { + return \@decoded; + } + else { + return; + } } -1; +1; # End of Net::Telnet::Trango __END__ -=back - =head1 SEE ALSO -Trango Documentation - http://www.trangobroadband.com/support/product_docs.htm +Trango Documentation - +L L @@ -1188,12 +1296,39 @@ Andrew Fresh Eandrew@rraz.netE +=head1 SUPPORT + +You can find documentation for this module with the perldoc command. + + perldoc Net::Telnet::Trango + +You can also look for information at: + +=over 4 + +=item * AnnoCPAN: Annotated CPAN documentation + +L + +=item * CPAN Ratings + +L + +=item * RT: CPAN's request tracker + +L + +=item * Search CPAN + +L + +=back + =head1 COPYRIGHT AND LICENSE Copyright (C) 2005,2006,2007 by Andrew Fresh -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.8.7 or, -at your option, any later version of Perl 5 you may have available. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. =cut