1# SNMP::Info::Layer3::Timetra 2# 3# Copyright (c) 2008 Bill Fenner 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are met: 7# 8# * Redistributions of source code must retain the above copyright notice, 9# this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# * Neither the name of the University of California, Santa Cruz nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21# LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28 29package SNMP::Info::Layer3::Timetra; 30 31use strict; 32use warnings; 33use Exporter; 34use SNMP::Info::Layer3; 35use SNMP::Info::Aggregate; 36 37@SNMP::Info::Layer3::Timetra::ISA 38 = qw/SNMP::Info::Aggregate SNMP::Info::Layer3 39 Exporter/; 40@SNMP::Info::Layer3::Timetra::EXPORT_OK = qw//; 41 42our ($VERSION, %GLOBALS, %MIBS, %FUNCS, %MUNGE); 43 44$VERSION = '3.81'; 45 46%MIBS = ( 47 %SNMP::Info::Layer3::MIBS, 48 %SNMP::Info::Aggregate::MIBS, 49 'TIMETRA-GLOBAL-MIB' => 'timetraReg', 50 'TIMETRA-LLDP-MIB' => 'tmnxLldpAdminStatus', 51 'TIMETRA-PORT-MIB' => 'tmnxPortEtherDuplex', 52 'TIMETRA-CHASSIS-MIB' => 'tmnxChassisFanOperStatus', 53); 54 55%GLOBALS = ( %SNMP::Info::Layer3::GLOBALS, ); 56 57%FUNCS = ( 58 %SNMP::Info::Layer3::FUNCS, 59 60 # For some reason LLDP-MIB::lldpLocManAddrTable is populated 61 # but LLDP-MIB::lldpRemTable is not and we need to use the 62 # proprietary TIMETRA-LLDP-MIB Note: these tables are 63 # indexed differently than LLDP-MIB 64 # TIMETRA-LLDP-MIB::tmnxLldpRemTable 65 'lldp_rem_id_type' => 'tmnxLldpRemChassisIdSubtype', 66 'lldp_rem_id' => 'tmnxLldpRemChassisId', 67 'lldp_rem_pid_type' => 'tmnxLldpRemPortIdSubtype', 68 'lldp_rem_pid' => 'tmnxLldpRemPortId', 69 'lldp_rem_desc' => 'tmnxLldpRemPortDesc', 70 'lldp_rem_sysname' => 'tmnxLldpRemSysName', 71 'lldp_rem_sysdesc' => 'tmnxLldpRemSysDesc', 72 'lldp_rem_sys_cap' => 'tmnxLldpRemSysCapEnabled', 73 'lldp_rem_cap_spt' => 'tmnxLldpRemSysCapSupported', 74 75 # TIMETRA-LLDP-MIB::tmnxLldpRemManAddrTable 76 'lldp_rman_addr' => 'tmnxLldpRemManAddrIfSubtype', 77 78 # TIMETRA-PORT-MIB::tmnxPortEtherTable 79 'tmnx_eth_speed_admin' => 'tmnxPortEtherSpeed', 80 'tmnx_eth_duplex' => 'tmnxPortEtherOperDuplex', 81 'tmnx_eth_duplex_admin' => 'tmnxPortEtherDuplex', 82 'tmnx_eth_auto' => 'tmnxPortEtherAutoNegotiate', 83 84 # TIMETRA-CHASSIS-MIB::tmnxChassisFanTable 85 'tmnx_fan_state' => 'tmnxChassisFanOperStatus', 86 87 # TIMETRA-CHASSIS-MIB::tmnxChassisPowerSupplyTable 88 'tmnx_ps1_state' => 'tmnxChassisPowerSupply1Status', 89 'tmnx_ps2_state' => 'tmnxChassisPowerSupply2Status', 90 91 # TIMETRA-CHASSIS-MIB::tmnxHwTable 92 'e_descr' => 'tmnxHwName', 93 'e_parent' => 'tmnxHwContainedIn', 94 'e_name' => 'tmnxHwName', 95 'e_class' => 'tmnxHwClass', 96 'e_pos' => 'tmnxHwParentRelPos', 97 'e_swver' => 'tmnxHwSoftwareCodeVersion', 98 'e_model' => 'tmnxHwMfgBoardNumber', 99 'e_serial' => 'tmnxHwSerialNumber', 100 'e_fru' => 'tmnxHwIsFRU', 101 'e_fwver' => 'tmnxHwFirmwareCodeVersion', 102); 103 104%MUNGE = ( 105 %SNMP::Info::Layer3::MUNGE, 106 'tmnx_fan_state' => \&SNMP::Info::Layer3::Timetra::munge_tmnx_state, 107 'tmnx_ps1_state' => \&SNMP::Info::Layer3::Timetra::munge_tmnx_state, 108 'tmnx_ps2_state' => \&SNMP::Info::Layer3::Timetra::munge_tmnx_state, 109 'e_type' => \&SNMP::Info::munge_e_type, 110 'e_class' => \&SNMP::Info::Layer3::Timetra::munge_tmnx_e_class, 111 'e_swver' => \&SNMP::Info::Layer3::Timetra::munge_tmnx_e_swver, 112); 113 114sub model { 115 my $timetra = shift; 116 my $id = $timetra->id(); 117 my $model = SNMP::translateObj($id); 118 my $descr = $timetra->description(); 119 120 my $str; 121 122 if ( defined ($descr) && $descr =~ /\s+(7\d{3})/ ) { 123 $str = $1; 124 } 125 126 if ( defined $model && $model =~ /^tmnxModel/ ) { 127 $model =~ s/^tmnxModel//; 128 $model =~ s/Reg$//; 129 $str .= $str ? " " : ""; 130 $str .= $model; 131 } 132 133 return $str || $id; 134} 135 136sub os { 137 return 'TiMOS'; 138} 139 140sub vendor { 141 return 'nokia'; 142} 143 144sub os_ver { 145 my $timetra = shift; 146 147 my $descr = $timetra->description(); 148 if ( defined ($descr) && $descr =~ m/^TiMOS-(\S+)/x ) { 149 return $1; 150 } 151 return; 152} 153 154# The interface description contains the SFP type, so 155# to avoid losing historical information through a configuration change 156# we use interface name instead. 157sub interfaces { 158 my $alu = shift; 159 my $partial = shift; 160 161 return $alu->orig_i_name($partial); 162} 163 164# The TIMETRA-LLDP-MIB::tmnxLldpRemTable unambiguously states it uses ifIndex 165# Trying to cross reference to ifDescr or ifAlias would cause unpredictable 166# results based upon how the device names ports. 167sub lldp_if { 168 my $alu = shift; 169 my $partial = shift; 170 171 my $addr = $alu->lldp_rem_pid($partial) || {}; 172 173 my %lldp_if; 174 foreach my $key ( keys %$addr ) { 175 my @aOID = split( '\.', $key ); 176 my $port = $aOID[1]; 177 next unless $port; 178 179 $lldp_if{$key} = $port; 180 } 181 return \%lldp_if; 182} 183 184# The proprietary TIMETRA-LLDP-MIB tables are indexed differently than LLDP-MIB 185# We overwrite the private function so that the we don't have to replicate 186# the code in SNMP::Info::LLDP that uses it. 187 188sub _lldp_addr_index { 189 my $alu = shift; 190 my $idx = shift; 191 192 my @oids = split( /\./, $idx ); 193 194 # Index has extra field compared to LLDP-MIB 195 my $index = join( '.', splice( @oids, 0, 4 ) ); 196 my $proto = shift(@oids); 197 shift(@oids) if scalar @oids > 4; # $length 198 199 # IPv4 200 if ( $proto == 1 ) { 201 return ( $index, $proto, join( '.', @oids ) ); 202 } 203 204 # IPv6 205 elsif ( $proto == 2 ) { 206 return ( $index, $proto, 207 join( ':', unpack( '(H4)*', pack( 'C*', @oids ) ) ) ); 208 } 209 210 # MAC 211 elsif ( $proto == 6 ) { 212 return ( $index, $proto, 213 join( ':', map { sprintf "%02x", $_ } @oids ) ); 214 } 215 216 # TODO - Other protocols may be used as well; implement when needed? 217 else { 218 return; 219 } 220}; 221 222sub i_duplex { 223 my $alu = shift; 224 my $partial = shift; 225 226 my $hw_duplex = $alu->tmnx_eth_duplex($partial) || {}; 227 228 my %i_duplex; 229 if ( ref {} eq ref $hw_duplex and scalar keys %$hw_duplex ) { 230 foreach my $if ( keys %$hw_duplex ) { 231 my $duplex = $hw_duplex->{$if}; 232 next unless defined $duplex; 233 next if $duplex eq 'notApplicable'; 234 my ( $slot, $ifindex ) = split( /\./, $if ); 235 236 $duplex = 'half' 237 if ( $duplex =~ /half/i ); 238 $duplex = 'full' 239 if ( $duplex =~ /full/i ); 240 241 $i_duplex{$ifindex} = $duplex; 242 } 243 return \%i_duplex; 244 } 245 return $alu->SUPER::i_duplex($partial); 246} 247 248sub i_duplex_admin { 249 my $alu = shift; 250 my $partial = shift; 251 252 my $hw_duplex_admin = $alu->tmnx_eth_duplex_admin($partial) || {}; 253 my $hw_auto = $alu->tmnx_eth_auto($partial) || {}; 254 255 my %i_duplex_admin; 256 foreach my $if ( keys %$hw_duplex_admin ) { 257 my $duplex = $hw_duplex_admin->{$if}; 258 next unless defined $duplex; 259 next if $duplex eq 'notApplicable'; 260 my $auto = $hw_auto->{$if} || 'false'; 261 my ( $slot, $ifindex ) = split( /\./, $if ); 262 263 my $string = 'other'; 264 $string = 'half' 265 if ( $duplex =~ /half/i and $auto =~ /false/i ); 266 $string = 'full' 267 if ( $duplex =~ /full/i and $auto =~ /false/i ); 268 $string = 'auto' if $auto =~ /true/i; 269 270 $i_duplex_admin{$ifindex} = $string; 271 } 272 return \%i_duplex_admin; 273} 274 275sub agg_ports { 276 my $alu = shift; 277 278 return $alu->agg_ports_ifstack(); 279} 280 281sub fan { 282 my $alu = shift; 283 284 my $state = $alu->tmnx_fan_state() || {}; 285 286 if ( scalar keys %$state ) { 287 my @messages = (); 288 289 foreach my $k ( keys %$state ) { 290 next if $state->{$k} and $state->{$k} eq 'Ok'; 291 my ( $chassis, $fan ) = split( /\./, $k ); 292 push @messages, "Fan $fan, Chassis $chassis: $state->{$k}"; 293 } 294 295 push @messages, ( ( scalar keys %$state ) . " fans OK" ) 296 if scalar @messages == 0; 297 298 return ( join ", ", @messages ); 299 } 300 return; 301} 302 303sub ps1_status { 304 my $alu = shift; 305 306 my $pwr_state = $alu->tmnx_ps1_state() || {}; 307 308 my $ret = ""; 309 my $s = ""; 310 foreach my $i ( sort keys %$pwr_state ) { 311 my ( $chassis, $num ) = split( /\./, $i ); 312 $ret 313 .= $s 314 . "Chassis " 315 . $chassis . " PS " 316 . $num . ": " 317 . $pwr_state->{$i}; 318 $s = ", "; 319 } 320 return if ( $s eq "" ); 321 return $ret; 322} 323 324sub ps2_status { 325 my $alu = shift; 326 327 my $pwr_state = $alu->tmnx_ps2_state() || {}; 328 329 my $ret = ""; 330 my $s = ""; 331 foreach my $i ( sort keys %$pwr_state ) { 332 my ( $chassis, $num ) = split( /\./, $i ); 333 $ret 334 .= $s 335 . "Chassis " 336 . $chassis . " PS " 337 . $num . ": " 338 . $pwr_state->{$i}; 339 $s = ", "; 340 } 341 return if ( $s eq "" ); 342 return $ret; 343} 344 345sub e_index { 346 my $alu = shift; 347 my $partial = shift; 348 349 # Use MIB leaf to force load here 350 my $e_descr = $alu->tmnxHwID($partial); 351 352 return unless ( ref {} eq ref $e_descr and scalar keys %$e_descr ); 353 354 my %e_index; 355 356 foreach my $iid ( keys %$e_descr ) { 357 $e_index{$iid} = $iid; 358 } 359 return \%e_index; 360} 361 362sub munge_tmnx_state { 363 my $state = shift; 364 365 $state =~ s/deviceState//; 366 $state =~ s/device//; 367 return $state; 368} 369 370sub munge_tmnx_e_class { 371 my $class = shift; 372 373 if ($class eq 'physChassis') { 374 $class = 'chassis'; 375 } 376 elsif ($class =~ /Module/i) { 377 $class = 'module'; 378 } 379 return $class; 380} 381 382sub munge_tmnx_e_swver { 383 my $swver = shift; 384 385 if ( $swver =~ m/^TiMOS-(\S+)/x ) { 386 return $1; 387 } 388 return $swver; 389} 390 3911; 392__END__ 393 394=head1 NAME 395 396SNMP::Info::Layer3::Timetra - SNMP Interface to Alcatel-Lucent SR 397 398=head1 AUTHOR 399 400Bill Fenner 401 402=head1 SYNOPSIS 403 404 # Let SNMP::Info determine the correct subclass for you. 405 my $alu = new SNMP::Info( 406 AutoSpecify => 1, 407 Debug => 1, 408 # These arguments are passed directly to SNMP::Session 409 DestHost => 'myswitch', 410 Community => 'public', 411 Version => 2 412 ) 413 or die "Can't connect to DestHost.\n"; 414 415 my $class = $alu->class(); 416 print "SNMP::Info determined this device to fall under subclass : $class\n"; 417 418=head1 DESCRIPTION 419 420Subclass for Alcatel-Lucent Service Routers 421 422=head2 Inherited Classes 423 424=over 425 426=item SNMP::Info::Layer3 427 428=back 429 430=head2 Required MIBs 431 432=over 433 434=item F<TIMETRA-GLOBAL-MIB> 435 436=item F<TIMETRA-LLDP-MIB> 437 438=item F<TIMETRA-PORT-MIB> 439 440=item F<TIMETRA-CHASSIS-MIB> 441 442=item Inherited Classes' MIBs 443 444See L<SNMP::Info::Layer3/"Required MIBs"> for its own MIB requirements. 445 446=back 447 448=head1 GLOBALS 449 450These are methods that return scalar value from SNMP 451 452=over 453 454=item $alu->vendor() 455 456Returns 'nokia' 457 458=item $alu->os() 459 460Returns 'TiMOS' 461 462=item $alu->os_ver() 463 464Grabs the version string from C<sysDescr>. 465 466=item $alu->model() 467 468Tries to combine series and model extracted from $alu->id() to one of the 469product MIBs. 470 471Removes 'tmnxModel' from the name for readability. 472 473=item $alu->fan() 474 475Return the status of all fans from the F<TIMETRA-CHASSIS-MIB>. Returns 476a string indicating the number of fans 'OK' or identification of any fan without 477a 'Ok' operating status. 478 479=item $alu->ps1_status() 480 481Return the status of the first power supply in each chassis from 482the F<TIMETRA-CHASSIS-MIB>. 483 484=item $alu->ps2_status() 485 486Return the status of the second power supply in each chassis from 487the F<TIMETRA-CHASSIS-MIB>. 488 489=back 490 491=head2 Globals imported from SNMP::Info::Layer3 492 493See documentation in L<SNMP::Info::Layer3/"GLOBALS"> for details. 494 495=head1 TABLE METHODS 496 497These are methods that return tables of information in the form of a reference 498to a hash. 499 500=over 501 502=item $alu->i_duplex() 503 504Returns reference to map of IIDs to current link duplex. 505 506=item $alu->i_duplex_admin() 507 508Returns reference to hash of IIDs to admin duplex setting. 509 510=item $alu->agg_ports() 511 512Returns a HASH reference mapping from slave to master port for each member of 513a port bundle on the device. Keys are ifIndex of the slave ports, Values are 514ifIndex of the corresponding master ports. 515 516=back 517 518=head2 Overrides 519 520=over 521 522=item $alu->interfaces() 523 524Returns C<ifName>, since the default Layer3 C<ifDescr> varies based 525upon the transceiver inserted. 526 527=item $alu->lldp_if() 528 529Returns the mapping to the SNMP Interface Table. Utilizes (C<ifIndex>) 530from the (C<tmnxLldpRemEntry >) index. 531 532=back 533 534=head2 LLDP Remote Table (C<lldpRemTable>) uses (C<TIMETRA-LLDP-MIB::tmnxLldpRemTable>) 535 536=over 537 538=item $alu->lldp_rem_id_type() 539 540(C<tmnxLldpRemChassisIdSubtype>) 541 542=item $alu->lldp_rem_id() 543 544(C<tmnxLldpRemChassisId>) 545 546=item $alu->lldp_rem_pid_type() 547 548(C<tmnxLldpRemPortIdSubtype>) 549 550=item $alu->lldp_rem_pid() 551 552(C<tmnxLldpRemPortId>) 553 554=item $alu->lldp_rem_desc() 555 556(C<tmnxLldpRemPortDesc>) 557 558=item $alu->lldp_rem_sysname() 559 560(C<tmnxLldpRemSysName>) 561 562=item $alu->lldp_rem_sysdesc() 563 564(C<tmnxLldpRemSysDesc>) 565 566=item $alu->lldp_rem_sys_cap() 567 568(C<tmnxLldpRemSysCapEnabled>) 569 570=back 571 572=head2 Entity Table 573 574=over 575 576=item $alu->e_index() 577 578(C<tmnxHwIndex>) 579 580=item $alu->e_class() 581 582Chassis, Module, Fan, Power Supply ... 583 584(C<tmnxHwClass>) 585 586=item $alu->e_descr() 587 588Human Friendly 589 590(C<tmnxHwName>) 591 592=item $alu->e_fwver() 593 594(C<tmnxHwFirmwareCodeVersion>) 595 596=item $alu->e_fru() 597 598BOOLEAN. Is a Field Replaceable unit? 599 600(C<tmnxHwIsFRU>) 601 602=item $alu->e_model() 603 604Model Name of Entity. 605 606(C<tmnxHwMfgBoardNumber>) 607 608=item $alu->e_name() 609 610More computer friendly name of entity. 611 612(C<tmnxHwName>) 613 614=item $alu->e_parent() 615 6160 if root. 617 618(C<tmnxHwContainedIn>) 619 620=item $alu->e_pos() 621 622The relative position among all entities sharing the same parent. 623 624(C<tmnxHwParentRelPos>) 625 626=item $alu->e_serial() 627 628(C<tmnxHwSerialNumber>) 629 630=item $alu->e_swver() 631 632(C<tmnxHwSoftwareCodeVersion>) 633 634=back 635 636=head2 Table Methods imported from SNMP::Info::Layer3 637 638See documentation in L<SNMP::Info::Layer3/"TABLE METHODS"> for details. 639 640=head1 Data Munging Callback Subroutines 641 642=over 643 644=item $alu->munge_tmnx_state() 645 646Removes 'deviceState' or 'device' from C<TmnxDeviceState> strings. 647 648=item $alu->munge_tmnx_e_class() 649 650Attempts to normalize C<tmnxHwClass> to an C<IANAPhysicalClass>. 651 652=item $alu->munge_tmnx_e_swver() 653 654Extracts the software version from C<tmnxHwSoftwareCodeVersion> string. 655 656=back 657 658=cut 659