Annotation of trango/Net-Telnet-Trango/lib/Net/Telnet/Trango.pm, Revision 1.34
1.34 ! andrew 1: package Net::Telnet::Trango;
! 2:
! 3: # $RedRiver: Trango.pm,v 1.33 2007/02/02 21:26:56 andrew Exp $
! 4: use strict;
! 5: use warnings;
! 6: use base 'Net::Telnet';
! 7:
! 8: =pod
! 9:
! 10: =head1 NAME
! 11:
! 12: Net::Telnet::Trango
! 13: - Perl extension for accessing the Trango telnet interface
! 14:
! 15: =head1 SYNOPSIS
! 16:
! 17: use Net::Telnet::Trango;
! 18: my $t = new Net::Telnet::Trango ( Timeout => 5 );
! 19:
! 20: $t->open( Host => $fox ) or die "Error connecting: $!";
! 21:
! 22: $t->login('password') or die "Couldn't log in: $!";
! 23:
! 24: # Do whatever
! 25:
! 26: $t->exit;
! 27: $t->close;
! 28:
! 29: =head1 DESCRIPTION
! 30:
! 31: Perl access to the telnet interface on Trango Foxes, SUs and APs.
! 32:
! 33: Another handy feature is that it will parse the output from certain
! 34: commands that is in the format "[key1] value1 [key2] value2" and put
! 35: those in a hashref that is returned. This makes using the output from
! 36: things like sysinfo very easy to do.
! 37:
! 38: =head2 EXPORT
! 39:
! 40: None
! 41:
! 42: =head1 METHODS
! 43:
! 44: =cut
! 45:
! 46: our $VERSION = '0.01';
! 47:
! 48: my %PRIVATE = (
! 49: is_connected => 0,
! 50: logged_in => 0,
! 51: );
! 52:
! 53: =pod
! 54:
! 55: =head2 B<new>
! 56: - Creates a new Net::Telnet::Trango object.
! 57:
! 58: new([Options from Net::Telnet,]
! 59: [Decode => 0,]);
! 60:
! 61: Same as new from L<Net::Telnet> but sets the default Trango Prompt:
! 62: '/#> *$/'
! 63:
! 64: It also takes an optional parameter 'Decode'. If not defined it
! 65: defaults to 1, if it is set to 0, it will not decode the output and
! 66: instead return a reference to an array of the lines that were returned
! 67: from the command.
! 68:
! 69: =cut
! 70:
! 71: sub new {
! 72: my $class = shift;
! 73:
! 74: my %args;
! 75: if ( @_ == 1 ) {
! 76: $args{'Host'} = shift;
! 77: }
! 78: else {
! 79: %args = @_;
! 80: }
! 81:
! 82: $args{'Prompt'} ||= '/#> *$/';
! 83:
! 84: foreach my $key ( keys %args ) {
! 85: $PRIVATE{$key} = $args{$key};
! 86: }
! 87: $PRIVATE{'Decode'} = 1 unless defined $PRIVATE{'Decode'};
! 88: delete $args{'Decode'};
! 89:
! 90: my $self = $class->SUPER::new(%args);
! 91: bless $self if ref $self;
! 92:
! 93: return $self;
! 94: }
! 95:
! 96: # _password <new password> <new password>
! 97: # ? [command]
! 98: # apsearch <secs> <ch#> <h|v> [<ch#> <h|v>]...
! 99: # arp -bcast <on|off>
! 100: # bcastscant <all|suid> <ch#> <h|v> [<ch#> <h|v> ...
! 101: # bye
! 102: # cf2cf ap [default|<size>]
! 103: # date
! 104: # date <month> <day> <year>
! 105: # freq scantable
! 106: # freq channeltable
! 107: # freq writescan [<ch#> <h|v>]
! 108: # freq writechannel [<ch#> <freq>] ...
! 109: # freq <ch #> <h|v>
! 110: # help [command]
! 111: # heater [<on temp> <off temp>]
! 112: # ipconfig [<new ip> <new subnet mask> <new gateway>]
! 113: # log [<# of entries, 1..179>]
! 114: # log <sum> <# of entries, 1..179>
! 115: # logout
! 116: # opmode [ap [y]]
! 117: # password
! 118: # ping <ip addr>
! 119: # polar <h|v>
! 120: # power <setism|setunii> <max|min|<dBm>>
! 121: # reboot
! 122: # restart
! 123: # remarks [<str>]
! 124: # rfrxthreshold [<ism|unii> <-90|-85|-80|-75|-70|-65>]
! 125: # rfrxth [<ism|unii> <-90|-85|-80|-75|-70|-65>]
! 126: # sysinfo
! 127: # set suid <id>
! 128: # set apid <id>
! 129: # set baseid <id>
! 130: # set defaultopmode [<ap|su> <min,0..10>]
! 131: # set defaultopmode off
! 132: # set snmpcomm [<read | write | trap (id or setall)> <str>]
! 133: # set mir [on|off]
! 134: # set mir threshold <kbps>
! 135: # set rssitarget [<ism|unii> <dBm>]
! 136: # set serviceradius [<ism | unii> <miles>]
! 137: # ssrssi <ch #> <h|v>
! 138: # su [<suid>|all]
! 139: # su changechannel <all|suid> <ch#> <h|v>
! 140: # su ipconfig <suid> <new ip> <new subnet> <new gateway>
! 141: # su [live|poweroff|priority]
! 142: # su <ping|info|status> <suid>
! 143: # su powerleveling <all|suid>
! 144: # su reboot <all|suid>
! 145: # su restart <all|suid>
! 146: # su testrflink <all|suid> [r]
! 147: # su testrflink <setlen> [64..1600]
! 148: # su testrflink <aptx> [20..100]
! 149: # su sw <suid|all> <sw #> <on|off>
! 150: # sudb [dload | view]
! 151: # sudb add <suid> pr <cir,kbps> <mir,kbps> <device id,hex>
! 152: # sudb add <suid> reg <cir,kbps> <mir,kbps> <device id,hex>
! 153: # sudb delete <all|<suid>>
! 154: # sudb modify <suid> <cir|mir> <kbps>
! 155: # sudb modify <suid> <su2su> <group id,hex>
! 156: # sudb view
! 157: # sulog [lastmins | sampleperiod <1..60>]
! 158: # sulog [<# of entry,1..18>]
! 159: # survey <ism|unii> <time, sec> <h|v>
! 160: # sw [<sw #> <on|off>]
! 161: # temp
! 162: # tftpd [on|off]
! 163: # time
! 164: # time <hour> <min> <sec>
! 165: # save <mainimage|fpgaimage> <current chksum> <new chksum>
! 166: # save <systemsetting|sudb>
! 167: # updateflash <mainimage|fpgaimage> <current chksum> <new chksum>
! 168: # updateflash <systemsetting|sudb>
! 169:
! 170: =pod
! 171:
! 172: =head1 ACCESSORS
! 173:
! 174: These are usually only set internally.
! 175:
! 176: =head2 B<firmware_version>
! 177: - returns the firmware version
! 178:
! 179: Returns the firmware version if available, otherwise undef.
! 180:
! 181: It should be available after a successful open().
! 182:
! 183: =head2 B<host_type>
! 184: - return the type of host you are connected to.
! 185:
! 186: returns the type of host from the login banner for example M5830S or M5300S.
! 187:
! 188: Should be available after a successful open().
! 189:
! 190: =head2 B<is_connected>
! 191: - Status of the connection to host.
! 192:
! 193: returns 1 when connected, undef otherwise.
! 194:
! 195: =head2 B<logged_in>
! 196: - Status of being logged in to the host.
! 197:
! 198: returns 1 after a successful login(), 0 if it failed and undef if
! 199: login() was never called.
! 200:
! 201: =head2 B<login_banner>
! 202: - The banner when first connecting to the host.
! 203:
! 204: returns the banner that is displayed when first connected at login.
! 205: Only set after a successful open().
! 206:
! 207: =head2 B<last_lines>
! 208: - The last lines of output from the last cmd().
! 209:
! 210: returns, as an array ref, the output from the last cmd() that was run.
! 211:
! 212: =head2 B<last_error>
! 213: - A text output of the last error that was encountered.
! 214:
! 215: returns the last error reported. Probably contains the last entry in
! 216: last_lines.
! 217:
! 218: =head1 ALIASES
! 219:
! 220: =head2 B<bye>
! 221: - alias of exit()
! 222:
! 223: Does the same as exit()
! 224:
! 225: =head2 B<restart>
! 226: - alias of reboot()
! 227:
! 228: Does the same as reboot()
! 229:
! 230: =head1 COMMANDS
! 231:
! 232: Most of these are just shortcuts to C<cmd(String =E<gt> METHOD)>,
! 233: as such they accept the same options as C<cmd()>.
! 234: Specifically they take a named paramater "args", for example:
! 235: C<tftpd(args =E<gt> 'on')> would enable tftpd
! 236:
! 237: =head2 B<tftpd>
! 238: - The output from the tftpd command
! 239:
! 240: Returns a hash ref of the decoded output from the
! 241: command.
! 242:
! 243: Also see enable_tftpd() and disable_tftpd() as those check that it was
! 244: successfully changed.
! 245:
! 246: =head2 B<ver>
! 247: - The output from the ver command
! 248:
! 249: Returns a hash ref of the decoded output from the
! 250: command.
! 251:
! 252: =head2 B<sysinfo>
! 253: - The output from the sysinfo command
! 254:
! 255: Returns a hash ref of the decoded output from the
! 256: command.
! 257:
! 258: =head2 B<exit>
! 259: - Exits the connection
! 260:
! 261: exits the command session with the Trango and closes
! 262: the connection
! 263:
! 264: =head2 B<reboot>
! 265: - Sends a reboot command
! 266:
! 267: reboots the Trango and closes the connection
! 268:
! 269: =head2 B<remarks>
! 270: - Set or retrieve the remarks.
! 271:
! 272: Takes an optional argument, which sets the remarks.
! 273: If there is no argument, returns the current remarks.
! 274:
! 275: my $old_remarks = $t->remarks();
! 276: $t->remarks($new_remarks);
! 277:
! 278: =head2 B<sulog>
! 279: - The output from the sulog command
! 280:
! 281: Returns an array ref of hashes containing each log
! 282: line.
! 283:
! 284: =head2 B<save_sudb>
! 285: - saves the sudb
! 286:
! 287: Returns true on success, undef on failure
! 288:
! 289: =head2 B<syslog>
! 290: - The output from the sulog command
! 291:
! 292: Returns a hashref of the output from the syslog command
! 293:
! 294: =head2 B<pipe>
! 295: - the pipe command
! 296:
! 297: Returns the output from the pipe command
! 298:
! 299: =head2 B<maclist>
! 300: - retrieves the maclist
! 301:
! 302: Returns the output from the maclist command
! 303:
! 304: =head2 B<maclist_reset>
! 305: - resets the maclist.
! 306:
! 307: No useful output.
! 308:
! 309: =head2 B<eth_list>
! 310: - eth list command
! 311:
! 312: Returns the output from the eth list command
! 313:
! 314:
! 315: =head2 B<su_info>
! 316: - gets the su info
! 317:
! 318: Returns information about the SU.
! 319:
! 320: You need to pass in the $suid and it will return the info for that suid.
! 321:
! 322: $t->su_info($suid);
! 323:
! 324: =head2 B<su_testrflink>
! 325: - tests the RF Link to an su
! 326:
! 327: $t->su_testrflink($suid|'all');
! 328:
! 329: =head2 B<save_ss>
! 330: - saves the config.
! 331:
! 332: Returns 1 on success, undef on failure.
! 333:
! 334: =cut
! 335:
! 336: my $success = 'Success\\.';
! 337: my %COMMANDS = (
! 338: tftpd => { decode => 'all', expect => $success },
! 339: ver => { decode => 'all' },
! 340: sysinfo => { decode => 'all', expect => $success },
! 341: updateflash => { decode => 'all', expect => $success },
! 342: sulog => { decode => 'sulog', expect => $success },
! 343: 'exit' => { no_prompt => 1, cmd_disconnects => 1 },
! 344: reboot => { no_prompt => 1, cmd_disconnects => 1 },
! 345: remarks => { decode => 'all', expect => $success },
! 346: save_sudb => { String => 'save sudb', expect => $success },
! 347: syslog => { expect => $success },
! 348: 'pipe' => {}, # XXX needs a special decode
! 349: maclist => { decode => 'maclist' },
! 350: maclist_reset => { String => 'maclist reset', expect => 'done' },
! 351: eth_link => { String => 'eth link', expect => $success },
! 352: su_info => { String => 'su info', decode => 'all', expect => $success },
! 353: su_testrflink =>
! 354: { String => 'su testrflink', decode => 'each', expect => $success },
! 355: save_ss => { String => 'save ss', expect => $success },
! 356: opmode => { decode => 'all', expect => $success },
! 357:
! 358: # eth r, w and reset???
! 359: #su password???
! 360: #_bootloader
! 361: #temp
! 362: #heater
! 363: );
! 364:
! 365: my %ALIASES = (
! 366: bye => 'exit',
! 367: restart => 'reboot',
! 368: Host => 'host',
! 369: );
! 370:
! 371: my %ACCESS = map { $_ => 1 } qw(
! 372: firmware_version
! 373: host_type
! 374: is_connected
! 375: logged_in
! 376: login_banner
! 377: Timeout
! 378: last_lines
! 379: last_vals
! 380: last_error
! 381: Decode
! 382: );
! 383:
! 384: sub AUTOLOAD {
! 385: my $self = shift;
! 386:
! 387: my ($method) = ( our $AUTOLOAD ) =~ /^.*::(\w+)$/
! 388: or die "Weird: $AUTOLOAD";
! 389:
! 390: if ( exists $ALIASES{$method} ) {
! 391: $method = $ALIASES{$method};
! 392: return $self->$method(@_);
! 393: }
! 394:
! 395: if ( exists $COMMANDS{$method} ) {
! 396: my %cmd;
! 397: foreach my $k ( keys %{ $COMMANDS{$method} } ) {
! 398: $cmd{$k} = $COMMANDS{$method}{$k};
! 399: }
! 400: $cmd{'String'} ||= $method;
! 401: $cmd{'args'} .= ' ' . shift if ( @_ == 1 );
! 402: return $self->cmd( %cmd, @_ );
! 403: }
! 404:
! 405: if ( exists $ACCESS{$method} ) {
! 406: my $prev = $PRIVATE{$method};
! 407: ( $PRIVATE{$method} ) = @_ if @_;
! 408: return $prev;
! 409: }
! 410:
! 411: $method = "SUPER::$method";
! 412: return $self->$method(@_);
! 413: }
! 414:
! 415: =pod
! 416:
! 417: =head2 B<open>
! 418: - Open a connection to a Trango AP.
! 419:
! 420: Calls Net::Telnet::open() then makes sure you get a password prompt so
! 421: you are ready to login() and parses the login banner so you can get
! 422: host_type() and firmware_version()
! 423:
! 424: =cut
! 425:
! 426: sub open {
! 427: my $self = shift;
! 428:
! 429: unless ( $self->SUPER::open(@_) ) {
! 430: $self->last_error( "Couldn't connect to " . $self->host . ": $!" );
! 431: return;
! 432: }
! 433:
! 434: ## Get to login prompt
! 435: unless (
! 436: $self->waitfor(
! 437: -match => '/password: ?$/i',
! 438: -errmode => "return",
! 439: )
! 440: )
! 441: {
! 442: $self->last_error( "problem connecting to host ("
! 443: . $self->host . "): "
! 444: . $self->lastline );
! 445: return;
! 446: }
! 447:
! 448: $self->parse_login_banner( $self->lastline );
! 449:
! 450: $self->is_connected(1);
! 451:
! 452: return $self->is_connected;
! 453: }
! 454:
! 455: =pod
! 456:
! 457: =head2 B<login>
! 458: - Login to the AP.
! 459:
! 460: Calls open() if not already connected, then sends the password and sets
! 461: logged_in() if successful
! 462:
! 463: =cut
! 464:
! 465: sub login {
! 466: my $self = shift;
! 467:
! 468: unless ( $self->is_connected ) {
! 469: $self->open or return;
! 470: }
! 471:
! 472: my $password = shift;
! 473:
! 474: $self->print($password);
! 475: unless (
! 476: $self->waitfor(
! 477: -match => $self->prompt,
! 478: -errmode => "return",
! 479: )
! 480: )
! 481: {
! 482: $self->last_error( "login ($self->host) failed: " . $self->lastline );
! 483: return;
! 484: }
! 485:
! 486: $self->logged_in(1);
! 487:
! 488: return $self->logged_in;
! 489: }
! 490:
! 491: =pod
! 492:
! 493: =head2 B<parse_login_banner>
! 494: - Converts the login_banner to some useful
! 495: variables.
! 496:
! 497: Takes a login banner (what you get when you first connect to the Trango)
! 498: or reads what is already in login_banner() then parses it and sets
! 499: host_type() and firmware_version() as well as login_banner()
! 500:
! 501: =cut
! 502:
! 503: sub parse_login_banner {
! 504: my $self = shift;
! 505:
! 506: if (@_) {
! 507: $self->login_banner(@_);
! 508: }
! 509:
! 510: my $banner = $self->login_banner;
! 511:
! 512: my ( $type, $sep1, $subtype, $sep2, $ver ) =
! 513: $banner =~
! 514: /Welcome to Trango Broadband Wireless (\S+)([\s-]+)(\S+)([\s-]+)(.+)$/i;
! 515:
! 516: $type .= $sep1 . $subtype;
! 517: $ver = $subtype . $sep2 . $ver;
! 518:
! 519: $self->login_banner($banner);
! 520: $self->host_type($type);
! 521: $self->firmware_version($ver);
! 522:
! 523: return 1;
! 524: }
! 525:
! 526: =pod
! 527:
! 528: =head2 B<su_password>
! 529: - Set the password on SUs connected to the AP.
! 530:
! 531: su_password('new_password'[, 'suid']) If no suid is specified,
! 532: the default is "all".
! 533:
! 534: $t->su_password('good_pass', 5);
! 535:
! 536: =cut
! 537:
! 538: sub su_password {
! 539: my $self = shift;
! 540: my $new_pass = shift || '';
! 541: my $su = shift || 'all';
! 542:
! 543: unless ( defined $new_pass ) {
! 544: $self->last_error("No new password");
! 545:
! 546: #return;
! 547: }
! 548:
! 549: return $self->cmd(
! 550: String => 'su password ' . $su . ' ' . $new_pass . ' ' . $new_pass,
! 551: expect => $success,
! 552: );
! 553: }
! 554:
! 555: =pod
! 556:
! 557: =head2 B<su_ipconfig>
! 558: - Change IP configuration on SUs connected to
! 559: the AP.
! 560:
! 561: su_ipconfig( 'suid', 'new_ip', 'new_subnet', 'new_gateway' )
! 562:
! 563: $t->su_ipconfig( 5, '10.0.1.5', '255.255.255.0', '10.0.1.1' );
! 564:
! 565: =cut
! 566:
! 567: sub su_ipconfig {
! 568: my $self = shift;
! 569:
! 570: my $suid = shift;
! 571: my $new_ip = shift;
! 572: my $new_subnet = shift;
! 573: my $new_gateway = shift;
! 574:
! 575: if ( $suid =~ /\D/ ) {
! 576: $self->last_error("Invalid suid '$suid'");
! 577: return;
! 578: }
! 579: unless ($new_ip) {
! 580: $self->last_error("no new_ip passed");
! 581: return;
! 582: }
! 583: unless ($new_subnet) {
! 584: $self->last_error("no new_subnet passed");
! 585: return;
! 586: }
! 587: unless ($new_gateway) {
! 588: $self->last_error("no new_gateway passed");
! 589: return;
! 590: }
! 591:
! 592: # su ipconfig <suid> <new ip> <new subnet> <new gateway>
! 593: return $self->cmd(
! 594: String => 'su ipconfig ' . $suid . ' ' . $new_ip . ' '
! 595: . $new_subnet . ' '
! 596: . $new_gateway,
! 597: expect => $success,
! 598: );
! 599: }
! 600:
! 601: =pod
! 602:
! 603: =head2 B<sudb_view>
! 604: - Returns the output from the sudb view command
! 605:
! 606: returns a reference to an array of hashes each containing these keys
! 607: 'suid', 'type', 'cir', 'mir' and 'mac'
! 608:
! 609: =cut
! 610:
! 611: sub sudb_view {
! 612: my $self = shift;
! 613:
! 614: my $lines = $self->cmd( String => 'sudb view', expect => $success ) || [];
! 615:
! 616: return unless @{$lines};
! 617:
! 618: unless ( $PRIVATE{'Decode'} ) {
! 619: return $lines;
! 620: }
! 621:
! 622: my @sus;
! 623: foreach ( @{$lines} ) {
! 624: next unless $_;
! 625: if (/^\[(\d+)\]\s+(\d+)\s+(\d+)\s+(\d+)\s+([0-9A-Fa-f\s]+)$/) {
! 626: my %s = (
! 627: suid => $1,
! 628: type => $2,
! 629: cir => $3,
! 630: mir => $4,
! 631: mac => $5,
! 632: );
! 633:
! 634: $s{'mac'} =~ s/\s//g;
! 635: $s{'mac'} = uc( $s{'mac'} );
! 636:
! 637: push @sus, \%s;
! 638: }
! 639: }
! 640:
! 641: return \@sus;
! 642: }
! 643:
! 644: =pod
! 645:
! 646: =head2 B<sudb_add>
! 647:
! 648: Takes the following paramaters
! 649:
! 650: suid : numeric,
! 651: type : (reg|pr)
! 652: cir : numeric,
! 653: mir : numeric,
! 654: mac : Almost any format, it will be reformatted,
! 655:
! 656: and returns true on success or undef otherwise.
! 657:
! 658: $t->sudb_add($suid, 'reg', $cir, $mir, $mac);
! 659:
! 660: You should save_sudb() after calling this, or your changes will be lost
! 661: when the AP is rebooted.
! 662:
! 663: =cut
! 664:
! 665: sub sudb_add {
! 666: my $self = shift;
! 667: my $suid = shift;
! 668: my $type = shift;
! 669: my $cir = shift;
! 670: my $mir = shift;
! 671: my $mac = shift;
! 672:
! 673: if ( $suid =~ /\D/ ) {
! 674: $self->last_error("Invalid suid '$suid'");
! 675: return;
! 676: }
! 677:
! 678: unless ( lc($type) eq 'reg' || lc($type) eq 'pr' ) {
! 679: $self->last_error("Invalid type '$type'");
! 680: return;
! 681: }
! 682:
! 683: if ( $cir =~ /\D/ ) {
! 684: $self->last_error("Invalid CIR '$cir'");
! 685: return;
! 686: }
! 687:
! 688: if ( $mir =~ /\D/ ) {
! 689: $self->last_error("Invalid MIR '$mir'");
! 690: return;
! 691: }
! 692:
! 693: my $new_mac = $mac;
! 694: $new_mac =~ s/[^0-9A-Fa-f]//;
! 695: unless ( length $new_mac == 12 ) {
! 696: $self->last_error("Invalid MAC '$mac'");
! 697: return;
! 698: }
! 699: $new_mac = join ' ', $new_mac =~ /../g;
! 700:
! 701: my $string =
! 702: 'sudb add ' . $suid . ' ' . $type . ' ' . $cir . ' ' . $mir . ' '
! 703: . $new_mac;
! 704:
! 705: return $self->cmd( String => $string, expect => $success );
! 706: }
! 707:
! 708: =pod
! 709:
! 710: =head2 B<sudb_delete>
! 711:
! 712: Takes either 'all' or the suid of the su to delete
! 713: and returns true on success or undef otherwise.
! 714:
! 715: $t->sudb_delete($suid);
! 716:
! 717: You should save_sudb() after calling this, or your changes will be lost
! 718: when the AP is rebooted.
! 719:
! 720: =cut
! 721:
! 722: sub sudb_delete {
! 723: my $self = shift;
! 724: my $suid = shift;
! 725:
! 726: #if (lc($suid) ne 'all' || $suid =~ /\D/) {
! 727: if ( $suid =~ /\D/ ) {
! 728: $self->last_error("Invalid suid '$suid'");
! 729: return;
! 730: }
! 731:
! 732: return $self->cmd( String => 'sudb delete ' . $suid, expect => $success );
! 733: }
! 734:
! 735: =pod
! 736:
! 737: =head2 B<sudb_modify>
! 738:
! 739: Takes either the suid of the su to change
! 740: as well as what you are changing, either "cir, mir or su2su"
! 741: and returns true on success or undef otherwise.
! 742:
! 743: cir and mir also take a value to set the cir/mir to.
! 744:
! 745: su2su takes a group id parameter that is in hex.
! 746:
! 747: $t->sudb_modify($suid, 'cir', 512);
! 748:
! 749: You should save_sudb() after calling this, or your changes will be lost
! 750: when the AP is rebooted.
! 751:
! 752: =cut
! 753:
! 754: sub sudb_modify {
! 755: my $self = shift;
! 756: my $suid = shift;
! 757: my $opt = shift;
! 758: my $value = shift;
! 759:
! 760: if ( $suid =~ /\D/ ) {
! 761: $self->last_error("Invalid suid '$suid'");
! 762: return;
! 763: }
! 764:
! 765: if ( lc($opt) eq 'cir' or lc($opt) eq 'mir' ) {
! 766: if ( $value =~ /\D/ ) {
! 767: $self->last_error("Invalid $opt '$value'");
! 768: return;
! 769: }
! 770: }
! 771: elsif ( lc($opt) eq 'su2su' ) {
! 772: if ( $value =~ /[^0-9A-Za-f]/ ) {
! 773: $self->last_error("Invalid MAC '$value'");
! 774: return;
! 775: }
! 776: }
! 777: else {
! 778: $self->last_error("Invalid option '$opt'");
! 779: return;
! 780: }
! 781:
! 782: my $string = 'sudb modify ' . $suid . ' ' . $opt . ' ' . $value;
! 783:
! 784: return $self->cmd( String => $string, expect => $success );
! 785: }
! 786:
! 787: =pod
! 788:
! 789: =head2 B<enable_tftpd>
! 790: - enable the TFTP server
! 791:
! 792: runs C<tftpd(args =E<gt> 'on')> and makes sure that Tftpd is now 'listen'ing
! 793:
! 794: =cut
! 795:
! 796: sub enable_tftpd {
! 797: my $self = shift;
! 798:
! 799: my $vals = $self->tftpd( args => 'on' );
! 800:
! 801: if ( ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'listen' ) {
! 802: return $vals;
! 803: }
! 804: else {
! 805: return;
! 806: }
! 807: }
! 808:
! 809: =pod
! 810:
! 811: =head2 B<disable_tftpd>
! 812: - disable the TFTP server
! 813:
! 814: runs C<tftpd(args =E<gt> 'off')> and makes sure that Tftpd is now 'disabled'
! 815:
! 816: =cut
! 817:
! 818: sub disable_tftpd {
! 819: my $self = shift;
! 820:
! 821: my $vals = $self->tftpd( args => 'off' );
! 822:
! 823: if ( ref $vals eq 'HASH' && $vals->{'Tftpd'} eq 'disabled' ) {
! 824: return $vals;
! 825: }
! 826: else {
! 827: return;
! 828: }
! 829: }
! 830:
! 831: =pod
! 832:
! 833: =head2 B<cmd> - runs a command on the AP.
! 834:
! 835: This does most of the work. At the heart, it calls Net::Telnet::cmd()
! 836: but it also does some special stuff for Trango.
! 837:
! 838: Normally returns the last lines from from the command
! 839:
! 840: If you are using this, rather than one of the "easy" methods above,
! 841: you probably want to read through the source of this module to see how
! 842: some of the other commands are called.
! 843:
! 844: In addition to the Net::Telnet::cmd() options, it also accepts these:
! 845:
! 846: I<decode>
! 847: - if this is true, then it will send the output lines to _decode_lines()
! 848: and then returns the decoded output
! 849:
! 850: I<no_prompt>
! 851: - if this is true, it does not wait for a prompt, so you are not stuck
! 852: waiting for something that will never happen.
! 853:
! 854: I<cmd_disconnects>
! 855: - if this is true, it then sets logged_in() to false, then it will
! 856: close() the connection and set is_connected() to false
! 857:
! 858: I<expect>
! 859: - if this is set (usually to 'Success.') it will check for that in the
! 860: last line of output and if it does not, will return undef because the
! 861: command probably failed
! 862:
! 863: I<args>
! 864: - a string containing the command line options that are passed to the
! 865: command
! 866:
! 867: $t->cmd( String => 'exit', no_prompt => 1, cmd_disconnects => 1 );
! 868:
! 869: =cut
! 870:
! 871: sub cmd {
! 872: my $self = shift;
! 873:
! 874: my @valid_net_telnet_opts = qw(
! 875: String
! 876: Output
! 877: Cmd_remove_mode
! 878: Errmode
! 879: Input_record_separator
! 880: Ors
! 881: Output_record_separator
! 882: Prompt
! 883: Rs
! 884: Timeout
! 885: );
! 886:
! 887: my %cfg;
! 888: if ( @_ == 1 ) {
! 889: $cfg{'String'} = shift;
! 890: }
! 891: elsif ( @_ > 1 ) {
! 892: %cfg = @_;
! 893: }
! 894:
! 895: $cfg{'Timeout'} ||= $self->Timeout;
! 896:
! 897: unless ( $cfg{'String'} ) {
! 898: $self->last_error("No command passed");
! 899: return;
! 900: }
! 901:
! 902: unless ( $self->is_connected ) {
! 903: $self->last_error("Not connected");
! 904: return;
! 905: }
! 906:
! 907: unless ( $self->logged_in ) {
! 908: $self->last_error("Not logged in");
! 909: return;
! 910: }
! 911:
! 912: my %cmd;
! 913: foreach (@valid_net_telnet_opts) {
! 914: if ( exists $cfg{$_} ) {
! 915: $cmd{$_} = $cfg{$_};
! 916: }
! 917: }
! 918: if ( $cfg{'args'} ) {
! 919: $cmd{'String'} .= ' ' . $cfg{'args'};
! 920: }
! 921:
! 922: my @lines;
! 923: if ( $cfg{'no_prompt'} ) {
! 924: $self->print( $cmd{'String'} );
! 925: @lines = $self->lastline;
! 926: }
! 927: else {
! 928: @lines = $self->SUPER::cmd(%cmd);
! 929: }
! 930:
! 931: $self->last_lines( \@lines );
! 932:
! 933: my $vals = 1;
! 934: if ( $PRIVATE{'Decode'} && $cfg{'decode'} ) {
! 935: if ( $cfg{'decode'} eq 'each' ) {
! 936: $vals = _decode_each_line(@lines);
! 937: }
! 938: elsif ( $cfg{'decode'} eq 'sulog' ) {
! 939: $vals = _decode_sulog(@lines);
! 940: }
! 941: elsif ( $cfg{'decode'} eq 'maclist' ) {
! 942: $vals = _decode_maclist(@lines);
! 943: }
! 944: else {
! 945: $vals = _decode_lines(@lines);
! 946: }
! 947: }
! 948:
! 949: $self->last_vals($vals);
! 950:
! 951: my $last = $self->lastline;
! 952:
! 953: if ( ( not $cfg{'expect'} ) || $last =~ /$cfg{'expect'}$/ ) {
! 954: if ( $cfg{'cmd_disconnects'} ) {
! 955: $self->logged_in(0);
! 956: $self->close;
! 957: $self->is_connected(0);
! 958: }
! 959:
! 960: if ( $PRIVATE{'Decode'} && $cfg{'decode'} ) {
! 961: return $vals;
! 962: }
! 963: else {
! 964: return \@lines;
! 965: }
! 966: }
! 967: else {
! 968: $self->last_error("Error with command ($cfg{'String'}): $last");
! 969: return;
! 970: }
! 971: }
! 972:
! 973: #=item _decode_lines
! 974:
! 975: sub _decode_lines {
! 976: my @lines = @_;
! 977:
! 978: my %conf;
! 979:
! 980: my $key = '';
! 981: my $val = undef;
! 982: my @vals;
! 983: my $in_key = 0;
! 984: my $in_val = 1;
! 985:
! 986: foreach my $line (@lines) {
! 987: next if $line =~ /$success$/;
! 988:
! 989: my @chars = split //, $line;
! 990:
! 991: my $last_key = '';
! 992: foreach my $c (@chars) {
! 993:
! 994: if ( $c eq '[' || $c eq "\r" || $c eq "\n" ) {
! 995: if ( $c eq '[' ) {
! 996: $in_key = 1;
! 997: $in_val = 0;
! 998: }
! 999: else {
! 1000: $in_key = 0;
! 1001: $in_val = 1;
! 1002: }
! 1003:
! 1004: if ($key) {
! 1005: $key =~ s/^\s+//;
! 1006: $key =~ s/\s+$//;
! 1007:
! 1008: if ($val) {
! 1009: $val =~ s/^\s+//;
! 1010: $val =~ s/\s+$//;
! 1011: }
! 1012:
! 1013: if ( $key eq 'Checksum' && $last_key ) {
! 1014:
! 1015: # Special case for these bastids.
! 1016: my $new = $last_key;
! 1017: $new =~ s/\s+\S+$//;
! 1018: $key = $new . " " . $key;
! 1019: }
! 1020:
! 1021: $conf{$key} = $val;
! 1022: $last_key = $key;
! 1023: $key = '';
! 1024: }
! 1025: elsif ($val) {
! 1026: push @vals, $val;
! 1027: }
! 1028: $val = '';
! 1029:
! 1030: }
! 1031: elsif ( $c eq ']' ) {
! 1032: $in_val = 1;
! 1033: $in_key = 0;
! 1034: $c = shift @chars;
! 1035:
! 1036: }
! 1037: elsif ($in_key) {
! 1038: $key .= $c;
! 1039:
! 1040: }
! 1041: elsif ($in_val) {
! 1042: $val .= $c;
! 1043: }
! 1044: }
! 1045: }
! 1046:
! 1047: unless ($key) {
! 1048: push @vals, $val;
! 1049: }
! 1050:
! 1051: if ( @vals == 1 ) {
! 1052: $val = $vals[0];
! 1053: }
! 1054: elsif (@vals) {
! 1055: $val = \@vals;
! 1056: }
! 1057: else {
! 1058: $val = undef;
! 1059: }
! 1060:
! 1061: if (%conf) {
! 1062: $conf{_pre} = $val if $val;
! 1063: return \%conf;
! 1064: }
! 1065: else {
! 1066: return $val;
! 1067: }
! 1068: }
! 1069:
! 1070: #=item _decode_each_line
! 1071:
! 1072: sub _decode_each_line {
! 1073: my @lines = @_;
! 1074: my @decoded;
! 1075: foreach my $line (@lines) {
! 1076: my $decoded = _decode_lines($line);
! 1077: push @decoded, $decoded if defined $decoded;
! 1078: }
! 1079: return \@decoded;
! 1080: }
! 1081:
! 1082: #=item _decode_sulog
! 1083:
! 1084: sub _decode_sulog {
! 1085: my @lines = @_;
! 1086: my @decoded;
! 1087: my $last_tm;
! 1088: foreach my $line (@lines) {
! 1089: my $decoded = _decode_lines($line);
! 1090:
! 1091: if ( defined $decoded ) {
! 1092: if ( $decoded->{'tm'} ) {
! 1093: $last_tm = $decoded->{'tm'};
! 1094: next;
! 1095: }
! 1096: else {
! 1097: $decoded->{'tm'} = $last_tm;
! 1098: }
! 1099: next unless $last_tm;
! 1100:
! 1101: push @decoded, $decoded if defined $decoded;
! 1102: }
! 1103: }
! 1104: return \@decoded;
! 1105: }
! 1106:
! 1107: #=item _decode_maclist
! 1108:
! 1109: sub _decode_maclist {
! 1110: my @lines = @_;
! 1111: my @decoded;
! 1112: my $total_entries = 0;
! 1113: my $current_tm = 0;
! 1114: foreach my $line (@lines) {
! 1115: $line =~ s/\r?\n$//;
! 1116: my ( $mac, $loc, $tm ) = $line =~ /
! 1117: ([0-9a-fA-F ]{17})\s+
! 1118: (.*)\s+
! 1119: tm\s+
! 1120: (\d+)
! 1121: /x;
! 1122:
! 1123: if ($mac) {
! 1124: $mac =~ s/\s+//g;
! 1125: $loc =~ s/^\s+//;
! 1126: $loc =~ s/\s+$//;
! 1127:
! 1128: my $suid = undef;
! 1129: if ( $loc =~ /suid\s+=\s+(\d+)/ ) {
! 1130: $suid = $1;
! 1131: $loc = undef;
! 1132: }
! 1133:
! 1134: push @decoded,
! 1135: {
! 1136: mac => $mac,
! 1137: loc => $loc,
! 1138: tm => $tm,
! 1139: suid => $suid,
! 1140: };
! 1141: }
! 1142: elsif ( $line =~ /(\d+)\s+entries/ ) {
! 1143: $total_entries = $1;
! 1144: }
! 1145: elsif ( $line =~ /current tm = (\d+)\s+sec/ ) {
! 1146: $current_tm = $1;
! 1147: }
! 1148: }
! 1149:
! 1150: map { $_->{'cur_tm'} = $current_tm } @decoded;
! 1151:
! 1152: if ( scalar @decoded == $total_entries ) {
! 1153: return \@decoded;
! 1154: }
! 1155: else {
! 1156:
! 1157: # XXX we should have a way to set last error, not sure why we don't
! 1158: return;
! 1159: }
! 1160: }
! 1161:
! 1162: 1; # End of Net::Telnet::Trango
! 1163: __END__
! 1164:
! 1165: =head1 SEE ALSO
! 1166:
! 1167: Trango Documentation -
! 1168: L<http://www.trangobroadband.com/support/product_docs.htm>
! 1169:
! 1170: L<Net::Telnet>
! 1171:
! 1172: =head1 TODO
! 1173:
! 1174: There are still a lot of commands that are not accessed directly. If
! 1175: you call them (as cmd("command + args") or whatever) and it works,
! 1176: please send me examples that work and I will try to get it incorporated
! 1177: into the next version of the script.
! 1178:
! 1179: I also want to be able to parse the different types of output from
! 1180: commands like su, sudb all and anything else that would be better
! 1181: available as a perl datastructure.
! 1182:
! 1183: =head1 AUTHOR
! 1184:
! 1185: Andrew Fresh E<lt>andrew@rraz.netE<gt>
! 1186:
! 1187: =head1 SUPPORT
! 1188:
! 1189: You can find documentation for this module with the perldoc command.
! 1190:
! 1191: perldoc Net::Telnet::Trango
! 1192:
! 1193: You can also look for information at:
! 1194:
! 1195: =over 4
! 1196:
! 1197: =item * AnnoCPAN: Annotated CPAN documentation
! 1198:
! 1199: L<http://annocpan.org/dist/Net-Telnet-Trango>
! 1200:
! 1201: =item * CPAN Ratings
! 1202:
! 1203: L<http://cpanratings.perl.org/d/Net-Telnet-Trango>
! 1204:
! 1205: =item * RT: CPAN's request tracker
! 1206:
! 1207: L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-Telnet-Trango>
! 1208:
! 1209: =item * Search CPAN
! 1210:
! 1211: L<http://search.cpan.org/dist/Net-Telnet-Trango>
! 1212:
! 1213: =back
! 1214:
! 1215: =head1 COPYRIGHT AND LICENSE
! 1216:
! 1217: Copyright (C) 2005,2006,2007 by Andrew Fresh
! 1218:
! 1219: This program is free software; you can redistribute it and/or modify it
! 1220: under the same terms as Perl itself.
! 1221:
! 1222: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>