PageRenderTime 66ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/Info/Layer3/Passport.pm

https://github.com/gitpan/SNMP-Info
Perl | 1434 lines | 1027 code | 318 blank | 89 comment | 174 complexity | f6d410e5df8922db41464b1f3df7a2d3 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. # SNMP::Info::Layer3::Passport
  2. #
  3. # Copyright (c) 2012 Eric Miller
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are met:
  8. #
  9. # * Redistributions of source code must retain the above copyright notice,
  10. # this list of conditions and the following disclaimer.
  11. # * Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. # * Neither the name of the University of California, Santa Cruz nor the
  15. # names of its contributors may be used to endorse or promote products
  16. # derived from this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  22. # LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  23. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  24. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  25. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  26. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  27. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. package SNMP::Info::Layer3::Passport;
  30. use warnings;
  31. use strict;
  32. use Exporter;
  33. use SNMP::Info::SONMP;
  34. use SNMP::Info::RapidCity;
  35. use SNMP::Info::Layer3;
  36. @SNMP::Info::Layer3::Passport::ISA
  37. = qw/SNMP::Info::SONMP SNMP::Info::RapidCity
  38. SNMP::Info::Layer3 Exporter/;
  39. @SNMP::Info::Layer3::Passport::EXPORT_OK = qw//;
  40. use vars qw/$VERSION %GLOBALS %FUNCS %MIBS %MUNGE/;
  41. $VERSION = '3.23';
  42. %MIBS = (
  43. %SNMP::Info::Layer3::MIBS, %SNMP::Info::RapidCity::MIBS,
  44. %SNMP::Info::SONMP::MIBS,
  45. );
  46. %GLOBALS = (
  47. %SNMP::Info::Layer3::GLOBALS, %SNMP::Info::RapidCity::GLOBALS,
  48. %SNMP::Info::SONMP::GLOBALS,
  49. );
  50. %FUNCS = (
  51. %SNMP::Info::Layer3::FUNCS, %SNMP::Info::RapidCity::FUNCS,
  52. %SNMP::Info::SONMP::FUNCS,
  53. );
  54. %MUNGE = (
  55. %SNMP::Info::Layer3::MUNGE, %SNMP::Info::RapidCity::MUNGE,
  56. %SNMP::Info::SONMP::MUNGE,
  57. );
  58. sub model {
  59. my $passport = shift;
  60. my $id = $passport->id();
  61. unless ( defined $id ) {
  62. print
  63. " SNMP::Info::Layer3::Passport::model() - Device does not support sysObjectID\n"
  64. if $passport->debug();
  65. return;
  66. }
  67. my $model = &SNMP::translateObj($id);
  68. return $id unless defined $model;
  69. $model =~ s/^rc(A)?//i;
  70. return $model;
  71. }
  72. sub vendor {
  73. return 'avaya';
  74. }
  75. sub os {
  76. return 'passport';
  77. }
  78. sub os_ver {
  79. my $passport = shift;
  80. my $descr = $passport->description();
  81. return unless defined $descr;
  82. #ERS / Passport
  83. if ( $descr =~ m/(\d+\.\d+\.\d+\.\d+)/ ) {
  84. return $1;
  85. }
  86. #Accelar
  87. if ( $descr =~ m/(\d+\.\d+\.\d+)/ ) {
  88. return $1;
  89. }
  90. return;
  91. }
  92. sub i_index {
  93. my $passport = shift;
  94. my $partial = shift;
  95. my $i_index = $passport->orig_i_index($partial);
  96. my $model = $passport->model();
  97. my %if_index;
  98. foreach my $iid ( keys %$i_index ) {
  99. my $index = $i_index->{$iid};
  100. next unless defined $index;
  101. $if_index{$iid} = $index;
  102. }
  103. # Get VLAN Virtual Router Interfaces
  104. if (!defined $partial
  105. || (defined $model
  106. && ( ( $partial > 2000 && $model =~ /^8[8631]|16|VSP/ )
  107. || ( $partial > 256 && $model =~ /^1[012][05]0/ ) )
  108. )
  109. )
  110. {
  111. my $vlan_index = $passport->rc_vlan_if() || {};
  112. foreach my $vid ( keys %$vlan_index ) {
  113. my $v_index = $vlan_index->{$vid};
  114. next unless defined $v_index;
  115. next if $v_index == 0;
  116. next if ( defined $partial and $v_index !~ /^$partial$/ );
  117. $if_index{$v_index} = $v_index;
  118. }
  119. }
  120. if ( defined $model and $model =~ /^8[86]/ ) {
  121. my $cpu_index = $passport->rc_cpu_ifindex($partial) || {};
  122. my $virt_ip = $passport->rc_virt_ip();
  123. # Get CPU Ethernet Interfaces
  124. foreach my $cid ( keys %$cpu_index ) {
  125. my $c_index = $cpu_index->{$cid};
  126. next unless defined $c_index;
  127. next if $c_index == 0;
  128. $if_index{$c_index} = $c_index;
  129. }
  130. # Check for Virtual Mgmt Interface
  131. unless ( $virt_ip eq '0.0.0.0' ) {
  132. # Make up an index number, 1 is not reserved AFAIK
  133. $if_index{1} = 1;
  134. }
  135. }
  136. return \%if_index;
  137. }
  138. sub interfaces {
  139. my $passport = shift;
  140. my $partial = shift;
  141. my $i_index = $passport->i_index($partial);
  142. my $model = $passport->model();
  143. my $index_factor = $passport->index_factor();
  144. my $port_offset = $passport->port_offset();
  145. my $vlan_index = {};
  146. my %reverse_vlan;
  147. my $vlan_id = {};
  148. if (!defined $partial
  149. || (defined $model
  150. && ( ( $partial > 2000 && $model =~ /^8[8631]|16|VSP/ )
  151. || ( $partial > 256 && $model =~ /^1[012][05]0/ ) )
  152. )
  153. )
  154. {
  155. $vlan_index = $passport->rc_vlan_if() || {};
  156. %reverse_vlan = reverse %$vlan_index;
  157. $vlan_id = $passport->rc_vlan_id();
  158. }
  159. my %if;
  160. foreach my $iid ( keys %$i_index ) {
  161. my $index = $i_index->{$iid};
  162. next unless defined $index;
  163. if ( ( $index == 1 ) and ( $model =~ /^8[86]/ ) ) {
  164. $if{$index} = 'Cpu.Virtual';
  165. }
  166. elsif ( ( $index == 192 ) and ( $model =~ /^8[86]03/ ) ) {
  167. $if{$index} = 'Cpu.3';
  168. }
  169. elsif ( ( $index == 320 ) and ( $model =~ /^8[86][10][06]/ ) ) {
  170. $if{$index} = 'Cpu.5';
  171. }
  172. elsif ( ( $index == 384 ) and ( $model =~ /^8[86][10][06]/ ) ) {
  173. $if{$index} = 'Cpu.6';
  174. }
  175. elsif (( $index > 2000 and $model =~ /^8[8631]|16|VSP/ )
  176. or ( $index > 256 and $model =~ /^1[012][05]0/ ) )
  177. {
  178. my $v_index = $reverse_vlan{$iid};
  179. my $v_id = $vlan_id->{$v_index};
  180. next unless defined $v_id;
  181. my $v_port = 'Vlan' . "$v_id";
  182. $if{$index} = $v_port;
  183. }
  184. else {
  185. my $port = ( $index % $index_factor ) + $port_offset;
  186. my $slot = int( $index / $index_factor );
  187. my $slotport = "$slot.$port";
  188. $if{$iid} = $slotport;
  189. }
  190. }
  191. return \%if;
  192. }
  193. sub i_mac {
  194. my $passport = shift;
  195. my $partial = shift;
  196. my $i_mac = $passport->orig_i_mac($partial) || {};
  197. my $model = $passport->model();
  198. my %if_mac;
  199. foreach my $iid ( keys %$i_mac ) {
  200. my $mac = $i_mac->{$iid};
  201. next unless defined $mac;
  202. $if_mac{$iid} = $mac;
  203. }
  204. # Get VLAN Virtual Router Interfaces
  205. if (!defined $partial
  206. || (defined $model
  207. && ( ( $partial > 2000 && $model =~ /^8[8631]|16|VSP/ )
  208. || ( $partial > 256 && $model =~ /^1[012][05]0/ ) )
  209. )
  210. )
  211. {
  212. my $vlan_index = $passport->rc_vlan_if() || {};
  213. my $vlan_mac = $passport->rc_vlan_mac() || {};
  214. foreach my $iid ( keys %$vlan_mac ) {
  215. my $v_mac = $vlan_mac->{$iid};
  216. next unless defined $v_mac;
  217. my $v_id = $vlan_index->{$iid};
  218. next unless defined $v_id;
  219. next if ( defined $partial and $v_id !~ /^$partial$/ );
  220. $if_mac{$v_id} = $v_mac;
  221. }
  222. }
  223. if ( defined $model and $model =~ /^8[86]/ ) {
  224. my $cpu_mac = $passport->rc_cpu_mac($partial) || {};
  225. my $virt_ip = $passport->rc_virt_ip() || '0.0.0.0';
  226. # Get CPU Ethernet Interfaces
  227. foreach my $iid ( keys %$cpu_mac ) {
  228. my $mac = $cpu_mac->{$iid};
  229. next unless defined $mac;
  230. $if_mac{$iid} = $mac;
  231. }
  232. # Check for Virtual Mgmt Interface
  233. unless ( ( $virt_ip eq '0.0.0.0' )
  234. or ( defined $partial and $partial ne "1" ) )
  235. {
  236. my $chassis_base_mac = $passport->rc_base_mac();
  237. if ( defined $chassis_base_mac ) {
  238. my @virt_mac = split /:/, $chassis_base_mac;
  239. $virt_mac[0] = hex( $virt_mac[0] );
  240. $virt_mac[1] = hex( $virt_mac[1] );
  241. $virt_mac[2] = hex( $virt_mac[2] );
  242. $virt_mac[3] = hex( $virt_mac[3] );
  243. $virt_mac[4] = hex( $virt_mac[4] ) + 0x03;
  244. $virt_mac[5] = hex( $virt_mac[5] ) + 0xF8;
  245. my $mac = join( ':', map { sprintf "%02x", $_ } @virt_mac );
  246. $if_mac{1} = $mac;
  247. }
  248. }
  249. }
  250. return \%if_mac;
  251. }
  252. sub i_description {
  253. my $passport = shift;
  254. my $partial = shift;
  255. my $i_descr = $passport->orig_i_description($partial) || {};
  256. my $model = $passport->model();
  257. my %descr;
  258. foreach my $iid ( keys %$i_descr ) {
  259. my $if_descr = $i_descr->{$iid};
  260. next unless defined $if_descr;
  261. $descr{$iid} = $if_descr;
  262. }
  263. # Get VLAN Virtual Router Interfaces
  264. if (!defined $partial
  265. || (defined $model
  266. && ( ( $partial > 2000 && $model =~ /^8[8631]|16|VSP/ )
  267. || ( $partial > 256 && $model =~ /^1[012][05]0/ ) )
  268. )
  269. )
  270. {
  271. my $v_descr = $passport->v_name();
  272. my $vlan_index = $passport->rc_vlan_if();
  273. foreach my $vid ( keys %$v_descr ) {
  274. my $vl_descr = $v_descr->{$vid};
  275. next unless defined $vl_descr;
  276. my $v_id = $vlan_index->{$vid};
  277. next unless defined $v_id;
  278. next if ( defined $partial and $v_id !~ /^$partial$/ );
  279. $descr{$v_id} = $vl_descr;
  280. }
  281. }
  282. return \%descr;
  283. }
  284. sub i_name {
  285. my $passport = shift;
  286. my $partial = shift;
  287. my $model = $passport->model();
  288. my $i_index = $passport->i_index($partial) || {};
  289. my $rc_alias = $passport->rc_alias($partial) || {};
  290. my $i_name2 = $passport->orig_i_name($partial) || {};
  291. my $v_name = {};
  292. my $vlan_index = {};
  293. my %reverse_vlan;
  294. if (!defined $partial
  295. || (defined $model
  296. && ( ( $partial > 2000 && $model =~ /^8[8631]|16|VSP/ )
  297. || ( $partial > 256 && $model =~ /^1[012][05]0/ ) )
  298. )
  299. )
  300. {
  301. $v_name = $passport->v_name() || {};
  302. $vlan_index = $passport->rc_vlan_if() || {};
  303. %reverse_vlan = reverse %$vlan_index;
  304. }
  305. my %i_name;
  306. foreach my $iid ( keys %$i_index ) {
  307. if ( ( $iid == 1 ) and ( $model =~ /^8[86]/ ) ) {
  308. $i_name{$iid} = 'CPU Virtual Management IP';
  309. }
  310. elsif ( ( $iid == 192 ) and ( $model =~ /^8[86]03/ ) ) {
  311. $i_name{$iid} = 'CPU 3 Ethernet Port';
  312. }
  313. elsif ( ( $iid == 320 ) and ( $model =~ /^8[86][10][06]/ ) ) {
  314. $i_name{$iid} = 'CPU 5 Ethernet Port';
  315. }
  316. elsif ( ( $iid == 384 ) and ( $model =~ /^8[86][10][06]/ ) ) {
  317. $i_name{$iid} = 'CPU 6 Ethernet Port';
  318. }
  319. elsif (
  320. ( $iid > 2000 and defined $model and $model =~ /^8[8631]|16|VSP/ )
  321. or ( $iid > 256
  322. and defined $model
  323. and $model =~ /^1[012][05]0/ )
  324. )
  325. {
  326. my $vlan_idx = $reverse_vlan{$iid};
  327. my $vlan_name = $v_name->{$vlan_idx};
  328. next unless defined $vlan_name;
  329. $i_name{$iid} = $vlan_name;
  330. }
  331. else {
  332. my $name = $i_name2->{$iid};
  333. my $alias = $rc_alias->{$iid};
  334. $i_name{$iid}
  335. = ( defined $alias and $alias !~ /^\s*$/ )
  336. ? $alias
  337. : $name;
  338. }
  339. }
  340. return \%i_name;
  341. }
  342. sub ip_index {
  343. my $passport = shift;
  344. my $partial = shift;
  345. my $model = $passport->model();
  346. my $ip_index = $passport->orig_ip_index($partial) || {};
  347. my %ip_index;
  348. foreach my $ip ( keys %$ip_index ) {
  349. my $iid = $ip_index->{$ip};
  350. next unless defined $iid;
  351. # Skip VSP default CPU addresses
  352. next if ($ip =~ /^192\.168\.1\.1/);
  353. # Skip default CPU addresses
  354. next if ($ip =~ /^192\.168\.168\.16[89]/);
  355. $ip_index{$ip} = $iid;
  356. }
  357. # Only 8600 has CPU and Virtual Management IP
  358. if ( defined $model and $model =~ /^8[86]/ ) {
  359. my $cpu_ip = $passport->rc_cpu_ip($partial) || {};
  360. my $virt_ip = $passport->rc_virt_ip($partial);
  361. # Get CPU Ethernet IP
  362. foreach my $cid ( keys %$cpu_ip ) {
  363. my $c_ip = $cpu_ip->{$cid};
  364. next unless defined $c_ip;
  365. # Skip default CPU addresses
  366. next if ($c_ip =~ /192\.168\.168\.16[89]/);
  367. $ip_index{$c_ip} = $cid;
  368. }
  369. # Get Virtual Mgmt IP
  370. $ip_index{$virt_ip} = 1 if ( defined $virt_ip );
  371. }
  372. return \%ip_index;
  373. }
  374. sub ip_netmask {
  375. my $passport = shift;
  376. my $partial = shift;
  377. my $model = $passport->model();
  378. my $ip_mask = $passport->orig_ip_netmask($partial) || {};
  379. my %ip_index;
  380. foreach my $iid ( keys %$ip_mask ) {
  381. # Skip VSP default CPU addresses
  382. next if ($iid =~ /^192\.168\.1\./);
  383. # Skip default CPU addresses
  384. next if ($iid =~ /^192\.168\.168\.16[89]/);
  385. my $mask = $ip_mask->{$iid};
  386. next unless defined $mask;
  387. $ip_index{$iid} = $mask;
  388. }
  389. # Only 8600 has CPU and Virtual Management IP
  390. if ( defined $model and $model =~ /^8[86]/ ) {
  391. my $cpu_ip = $passport->rc_cpu_ip($partial) || {};
  392. my $cpu_mask = $passport->rc_cpu_mask($partial) || {};
  393. my $virt_ip = $passport->rc_virt_ip($partial);
  394. my $virt_mask = $passport->rc_virt_mask($partial) || {};
  395. # Get CPU Ethernet IP
  396. foreach my $iid ( keys %$cpu_mask ) {
  397. my $c_ip = $cpu_ip->{$iid};
  398. next unless defined $c_ip;
  399. # Skip default CPU addresses
  400. next if ($c_ip =~ /192\.168\.168\.16[89]/);
  401. my $c_mask = $cpu_mask->{$iid};
  402. next unless defined $c_mask;
  403. $ip_index{$c_ip} = $c_mask;
  404. }
  405. # Get Virtual Mgmt IP
  406. $ip_index{$virt_ip} = $virt_mask
  407. if ( defined $virt_mask and defined $virt_ip );
  408. }
  409. return \%ip_index;
  410. }
  411. sub root_ip {
  412. my $passport = shift;
  413. my $model = $passport->model();
  414. my $rc_ip_addr = $passport->rc_ip_addr();
  415. my $rc_ip_type = $passport->rc_ip_type();
  416. my $virt_ip = $passport->rc_virt_ip();
  417. my $router_ip = $passport->router_ip();
  418. my $sonmp_topo_port = $passport->sonmp_topo_port();
  419. my $sonmp_topo_ip = $passport->sonmp_topo_ip();
  420. # Only 8600 and 1600 have CLIP or Management Virtual IP
  421. if ( defined $model and $model =~ /^8[86]|16|VSP/ ) {
  422. # Return CLIP (CircuitLess IP)
  423. foreach my $iid ( keys %$rc_ip_type ) {
  424. my $ip_type = $rc_ip_type->{$iid};
  425. next
  426. unless ( ( defined $ip_type )
  427. and ( $ip_type =~ /circuitLess/i ) );
  428. my $ip = $rc_ip_addr->{$iid};
  429. next unless defined $ip;
  430. return $ip if $passport->snmp_connect_ip($ip);
  431. }
  432. # Return Management Virtual IP address
  433. if ( ( defined $virt_ip ) and ( $virt_ip ne '0.0.0.0' ) ) {
  434. return $virt_ip if $passport->snmp_connect_ip($virt_ip);
  435. }
  436. }
  437. # Return OSPF Router ID
  438. if ( ( defined $router_ip ) and ( $router_ip ne '0.0.0.0' ) ) {
  439. foreach my $iid ( keys %$rc_ip_addr ) {
  440. my $ip = $rc_ip_addr->{$iid};
  441. next unless $router_ip eq $ip;
  442. return $router_ip if $passport->snmp_connect_ip($router_ip);
  443. }
  444. }
  445. # Otherwise Return SONMP Advertised IP Address
  446. foreach my $entry ( keys %$sonmp_topo_port ) {
  447. my $port = $sonmp_topo_port->{$entry};
  448. next unless $port == 0;
  449. my $ip = $sonmp_topo_ip->{$entry};
  450. return $ip
  451. if (( defined $ip )
  452. and ( $ip ne '0.0.0.0' )
  453. and ( $passport->snmp_connect_ip($ip) ) );
  454. }
  455. return;
  456. }
  457. # Required for SNMP::Info::SONMP
  458. sub index_factor {
  459. my $passport = shift;
  460. my $model = $passport->model();
  461. my $index_factor = 64;
  462. # Older Accelar models use base 16 instead of 64
  463. $index_factor = 16
  464. if ( defined $model and $model =~ /^1[012][05]0/ );
  465. return $index_factor;
  466. }
  467. sub slot_offset {
  468. return 0;
  469. }
  470. sub port_offset {
  471. return 1;
  472. }
  473. # Bridge MIB does not map Bridge Port to ifIndex correctly
  474. sub bp_index {
  475. my $passport = shift;
  476. my $partial = shift;
  477. my $if_index = $passport->i_index($partial) || {};
  478. my %bp_index;
  479. foreach my $iid ( keys %$if_index ) {
  480. $bp_index{$iid} = $iid;
  481. }
  482. # If we have MLT's map them to the designated port
  483. my $trunks = $passport->rc_mlt_index;
  484. my $dps = $passport->rc_mlt_dp || {};
  485. if ( ref {} eq ref $trunks and scalar keys %$trunks ) {
  486. foreach my $m ( keys %$trunks ) {
  487. my $m_idx = $trunks->{$m};
  488. next unless $m_idx;
  489. my $i_idx = $dps->{$m} ? $dps->{$m} : $m_idx;
  490. $bp_index{$m_idx} = $i_idx;
  491. }
  492. }
  493. return \%bp_index;
  494. }
  495. # We have devices which support BRIDGE-MIB, Q-BRIDGE-MIB, and RAPID-CITY
  496. # exclusively. Use standards-based first and fall back to RAPID-CITY.
  497. sub fw_mac {
  498. my $passport = shift;
  499. my $partial = shift;
  500. my $qb = $passport->SUPER::fw_mac($partial);
  501. return $qb if (ref {} eq ref $qb and scalar keys %$qb);
  502. my $qb_fw_port = $passport->rcBridgeTpFdbPort($partial);
  503. my $qb_fw_mac = {};
  504. foreach my $idx ( keys %$qb_fw_port ) {
  505. my ( $fdb_id, $mac ) = _rc_fdbtable_index($idx);
  506. $qb_fw_mac->{$idx} = $mac;
  507. }
  508. return $qb_fw_mac;
  509. }
  510. sub fw_port {
  511. my $passport = shift;
  512. my $partial = shift;
  513. my $qb = $passport->SUPER::fw_port($partial);
  514. return $qb if (ref {} eq ref $qb and scalar keys %$qb);
  515. return $passport->rcBridgeTpFdbPort($partial);
  516. }
  517. sub fw_status {
  518. my $passport = shift;
  519. my $partial = shift;
  520. my $qb = $passport->SUPER::fw_status($partial);
  521. return $qb if (ref {} eq ref $qb and scalar keys %$qb);
  522. return $passport->rcBridgeTpFdbStatus($partial);
  523. }
  524. sub qb_fw_vlan {
  525. my $passport = shift;
  526. my $partial = shift;
  527. my $qb = $passport->SUPER::qb_fw_vlan($partial);
  528. return $qb if (ref {} eq ref $qb and scalar keys %$qb);
  529. my $qb_fw_port = $passport->rcBridgeTpFdbPort($partial);
  530. my $qb_fw_vlan = {};
  531. foreach my $idx ( keys %$qb_fw_port ) {
  532. my ( $fdb_id, $mac ) = _rc_fdbtable_index($idx);
  533. $qb_fw_vlan->{$idx} = $fdb_id;
  534. }
  535. return $qb_fw_vlan;
  536. }
  537. # break up the rcBridgeTpFdbEntry INDEX into FDB ID and MAC Address.
  538. sub _rc_fdbtable_index {
  539. my $idx = shift;
  540. my @values = split( /\./, $idx );
  541. my $fdb_id = shift(@values);
  542. return ( $fdb_id, join( ':', map { sprintf "%02x", $_ } @values ) );
  543. }
  544. # Pseudo ENTITY-MIB methods
  545. sub e_index {
  546. my $passport = shift;
  547. my $model = $passport->model();
  548. my $rc_ps_t = $passport->rc_ps_type() || {};
  549. # We're going to hack an index: Slot/Mda/Postion
  550. # We're going to put chassis and power supplies in a slot
  551. # which doesn't exist
  552. my %rc_e_index;
  553. # Make up a chassis index
  554. $rc_e_index{1} = 1;
  555. # Power supplies are common, handle them first
  556. foreach my $idx ( keys %$rc_ps_t ) {
  557. next unless $idx;
  558. # We should never have 90 slots, they will also
  559. # sort numerically at the bottom
  560. my $index = $idx + 90 . "0000";
  561. $rc_e_index{$index} = $index;
  562. }
  563. # Older Accelars use RAPID-CITY::rcCardTable
  564. if ( defined $model and $model =~ /^1[012][05]0/ ) {
  565. my $rc_c_t = $passport->rc_c_type() || {};
  566. foreach my $idx ( keys %$rc_c_t ) {
  567. next unless $idx;
  568. my $index = "$idx" . "0000";
  569. $rc_e_index{$index} = $index;
  570. $index++;
  571. $rc_e_index{$index} = $index;
  572. }
  573. }
  574. # All newer models use RAPID-CITY::rc2kCardTable
  575. else {
  576. my $rc2_c_t = $passport->rc2k_c_ftype() || {};
  577. my $rc2_m_t = $passport->rc2k_mda_type() || {};
  578. foreach my $idx ( keys %$rc2_c_t ) {
  579. next unless $idx;
  580. my $index = "$idx" . "0000";
  581. for ( 0 .. 2 ) {
  582. $rc_e_index{$index} = $index;
  583. $index++;
  584. }
  585. }
  586. foreach my $idx ( keys %$rc2_m_t ) {
  587. next unless $idx;
  588. next if $idx == 0;
  589. my ( $slot, $mda ) = split /\./, $idx;
  590. $mda = sprintf( "%02d", $mda );
  591. my $index = "$idx" . "$mda" . "00";
  592. $rc_e_index{$index} = $index;
  593. $index++;
  594. $rc_e_index{$index} = $index;
  595. }
  596. }
  597. return \%rc_e_index;
  598. }
  599. sub e_class {
  600. my $passport = shift;
  601. my $rc_e_idx = $passport->e_index() || {};
  602. my %rc_e_class;
  603. foreach my $iid ( keys %$rc_e_idx ) {
  604. if ( $iid == 1 ) {
  605. $rc_e_class{$iid} = 'chassis';
  606. }
  607. elsif ( $iid =~ /^9(\d)/ and length $iid > 5 ) {
  608. $rc_e_class{$iid} = 'powerSupply';
  609. }
  610. elsif ( $iid =~ /0000$/ ) {
  611. $rc_e_class{$iid} = 'container';
  612. }
  613. else {
  614. $rc_e_class{$iid} = 'module';
  615. }
  616. }
  617. return \%rc_e_class;
  618. }
  619. sub e_descr {
  620. my $passport = shift;
  621. my $model = $passport->model();
  622. my $rc_ps = $passport->rc_ps_detail() || {};
  623. my $rc_ch = $passport->chassis() || '';
  624. $rc_ch =~ s/a//;
  625. my %rc_e_descr;
  626. # Chassis
  627. $rc_e_descr{1} = $rc_ch;
  628. # Power supplies are common, handle them first
  629. foreach my $idx ( keys %$rc_ps ) {
  630. next unless $idx;
  631. my $ps = $rc_ps->{$idx};
  632. next unless $ps;
  633. my $index = $idx + 90 . "0000";
  634. $rc_e_descr{$index} = $ps;
  635. }
  636. # Older Accelars use RAPID-CITY::rcCardTable
  637. if ( defined $model and $model =~ /^1[012][05]0/ ) {
  638. my $rc_c_t = $passport->rc_c_type() || {};
  639. foreach my $idx ( keys %$rc_c_t ) {
  640. next unless $idx;
  641. my $type = $rc_c_t->{$idx};
  642. next unless $type;
  643. my $index = "$idx" . "0000";
  644. $rc_e_descr{$index} = "Slot " . "$idx";
  645. $index++;
  646. $rc_e_descr{$index} = $type;
  647. }
  648. }
  649. # All newer models use RAPID-CITY::rc2kCardTable
  650. else {
  651. my $rc2_cf = $passport->rc2k_c_fdesc() || {};
  652. my $rc2_cb = $passport->rc2k_c_bdesc() || {};
  653. my $rc2_m = $passport->rc2k_mda_desc() || {};
  654. foreach my $idx ( keys %$rc2_cf ) {
  655. next unless $idx;
  656. my $cf = $rc2_cf->{$idx};
  657. next unless $idx;
  658. my $cb = $rc2_cb->{$idx};
  659. my $index = "$idx" . "0000";
  660. $rc_e_descr{$index} = "Slot " . "$idx";
  661. $index++;
  662. $rc_e_descr{$index} = $cf;
  663. $index++;
  664. $rc_e_descr{$index} = $cb;
  665. }
  666. foreach my $idx ( keys %$rc2_m ) {
  667. next unless $idx;
  668. my $cm = $rc2_m->{$idx};
  669. next unless $cm;
  670. my ( $slot, $mda ) = split /\./, $idx;
  671. $mda = sprintf( "%02d", $mda );
  672. my $index = "$idx" . "$mda" . "00";
  673. $rc_e_descr{$index} = $cm;
  674. }
  675. }
  676. return \%rc_e_descr;
  677. }
  678. sub e_type {
  679. my $passport = shift;
  680. my $model = $passport->model();
  681. my $rc_ps = $passport->rc_ps_type() || {};
  682. my $rc_ch = $passport->chassis();
  683. my %rc_e_type;
  684. # Chassis
  685. $rc_e_type{1} = $rc_ch;
  686. # Power supplies are common, handle them first
  687. foreach my $idx ( keys %$rc_ps ) {
  688. next unless $idx;
  689. my $ps = $rc_ps->{$idx};
  690. next unless $ps;
  691. my $index = $idx + 90 . "0000";
  692. $rc_e_type{$index} = $ps;
  693. }
  694. # Older Accelars use RAPID-CITY::rcCardTable
  695. if ( defined $model and $model =~ /^1[012][05]0/ ) {
  696. my $rc_c_t = $passport->rc_c_type() || {};
  697. foreach my $idx ( keys %$rc_c_t ) {
  698. next unless $idx;
  699. my $type = $rc_c_t->{$idx};
  700. next unless $type;
  701. my $index = "$idx" . "0000";
  702. $rc_e_type{$index} = "zeroDotZero";
  703. $index++;
  704. $rc_e_type{$index} = $type;
  705. }
  706. }
  707. # All newer models use RAPID-CITY::rc2kCardTable
  708. else {
  709. my $rc2_cf = $passport->rc2k_c_ftype() || {};
  710. my $rc2_cb = $passport->rc2k_c_btype() || {};
  711. my $rc2_m = $passport->rc2k_mda_type() || {};
  712. foreach my $idx ( keys %$rc2_cf ) {
  713. next unless $idx;
  714. my $cf = $rc2_cf->{$idx};
  715. next unless $idx;
  716. my $cb = $rc2_cb->{$idx};
  717. my $index = "$idx" . "0000";
  718. $rc_e_type{$index} = "zeroDotZero";
  719. $index++;
  720. $rc_e_type{$index} = $cf;
  721. $index++;
  722. $rc_e_type{$index} = $cb;
  723. }
  724. foreach my $idx ( keys %$rc2_m ) {
  725. next unless $idx;
  726. my $cm = $rc2_m->{$idx};
  727. next unless $cm;
  728. my ( $slot, $mda ) = split /\./, $idx;
  729. $mda = sprintf( "%02d", $mda );
  730. my $index = "$idx" . "$mda" . "00";
  731. $rc_e_type{$index} = $cm;
  732. }
  733. }
  734. return \%rc_e_type;
  735. }
  736. sub e_name {
  737. my $passport = shift;
  738. my $model = $passport->model();
  739. my $rc_e_idx = $passport->e_index() || {};
  740. my %rc_e_name;
  741. foreach my $iid ( keys %$rc_e_idx ) {
  742. if ( $iid == 1 ) {
  743. $rc_e_name{$iid} = 'Chassis';
  744. next;
  745. }
  746. my $mod = int( substr( $iid, -4, 2 ) );
  747. my $slot = substr( $iid, -6, 2 );
  748. if ( $iid =~ /^9(\d)/ and length $iid > 5 ) {
  749. $rc_e_name{$iid} = "Power Supply $1";
  750. }
  751. elsif ( $iid =~ /(00){2}$/ ) {
  752. $rc_e_name{$iid} = "Slot $slot";
  753. }
  754. elsif ( $iid =~ /(00){1}$/ ) {
  755. $rc_e_name{$iid} = "Card $slot, MDA $mod";
  756. }
  757. elsif ( defined $model
  758. and $model =~ /^1[012][05]0/
  759. and $iid =~ /1$/ )
  760. {
  761. $rc_e_name{$iid} = "Card $slot";
  762. }
  763. elsif ( $iid =~ /1$/ ) {
  764. $rc_e_name{$iid} = "Card $slot (front)";
  765. }
  766. elsif ( $iid =~ /2$/ ) {
  767. $rc_e_name{$iid} = "Card $slot (back)";
  768. }
  769. }
  770. return \%rc_e_name;
  771. }
  772. sub e_hwver {
  773. my $passport = shift;
  774. my $model = $passport->model();
  775. my $rc_ps = $passport->rc_ps_rev() || {};
  776. my %rc_e_hwver;
  777. # Chassis
  778. $rc_e_hwver{1} = $passport->rc_ch_rev();
  779. # Power supplies are common, handle them first
  780. foreach my $idx ( keys %$rc_ps ) {
  781. next unless $idx;
  782. my $ps = $rc_ps->{$idx};
  783. next unless $ps;
  784. my $index = $idx + 90 . "0000";
  785. $rc_e_hwver{$index} = $ps;
  786. }
  787. # Older Accelars use RAPID-CITY::rcCardTable
  788. if ( defined $model and $model =~ /^1[012][05]0/ ) {
  789. my $rc_c_t = $passport->rc_c_rev() || {};
  790. foreach my $idx ( keys %$rc_c_t ) {
  791. next unless $idx;
  792. my $type = $rc_c_t->{$idx};
  793. next unless $type;
  794. my $index = "$idx" . "0001";
  795. $rc_e_hwver{$index} = $type;
  796. }
  797. }
  798. # All newer models use RAPID-CITY::rc2kCardTable
  799. else {
  800. my $rc2_cf = $passport->rc2k_c_frev() || {};
  801. my $rc2_cb = $passport->rc2k_c_brev() || {};
  802. my $rc2_m = $passport->rc2k_mda_rev() || {};
  803. foreach my $idx ( keys %$rc2_cf ) {
  804. next unless $idx;
  805. my $cf = $rc2_cf->{$idx};
  806. next unless $idx;
  807. my $cb = $rc2_cb->{$idx};
  808. my $index = "$idx" . "0001";
  809. $rc_e_hwver{$index} = $cf;
  810. $index++;
  811. $rc_e_hwver{$index} = $cb;
  812. }
  813. foreach my $idx ( keys %$rc2_m ) {
  814. next unless $idx;
  815. my $cm = $rc2_m->{$idx};
  816. next unless $cm;
  817. my ( $slot, $mda ) = split /\./, $idx;
  818. $mda = sprintf( "%02d", $mda );
  819. my $index = "$idx" . "$mda" . "00";
  820. $rc_e_hwver{$index} = $cm;
  821. }
  822. }
  823. return \%rc_e_hwver;
  824. }
  825. sub e_vendor {
  826. my $passport = shift;
  827. my $rc_e_idx = $passport->e_index() || {};
  828. my %rc_e_vendor;
  829. foreach my $iid ( keys %$rc_e_idx ) {
  830. $rc_e_vendor{$iid} = 'avaya';
  831. }
  832. return \%rc_e_vendor;
  833. }
  834. sub e_serial {
  835. my $passport = shift;
  836. my $model = $passport->model();
  837. my $rc_ps = $passport->rc_ps_serial() || {};
  838. my %rc_e_serial;
  839. # Chassis
  840. $rc_e_serial{1} = $passport->rc_serial();
  841. # Power supplies are common, handle them first
  842. foreach my $idx ( keys %$rc_ps ) {
  843. next unless $idx;
  844. my $ps = $rc_ps->{$idx};
  845. next unless $ps;
  846. my $index = $idx + 90 . "0000";
  847. $rc_e_serial{$index} = $ps;
  848. }
  849. # Older Accelars use RAPID-CITY::rcCardTable
  850. if ( defined $model and $model =~ /^1[012][05]0/ ) {
  851. my $rc_c_t = $passport->rc_c_serial() || {};
  852. foreach my $idx ( keys %$rc_c_t ) {
  853. next unless $idx;
  854. my $type = $rc_c_t->{$idx};
  855. next unless $type;
  856. my $index = "$idx" . "0001";
  857. $rc_e_serial{$index} = $type;
  858. }
  859. }
  860. # All newer models use RAPID-CITY::rc2kCardTable
  861. else {
  862. my $rc2_cf = $passport->rc2k_c_fserial() || {};
  863. my $rc2_cb = $passport->rc2k_c_bserial() || {};
  864. my $rc2_m = $passport->rc2k_mda_serial() || {};
  865. foreach my $idx ( keys %$rc2_cf ) {
  866. next unless $idx;
  867. my $cf = $rc2_cf->{$idx};
  868. next unless $idx;
  869. my $cb = $rc2_cb->{$idx};
  870. my $index = "$idx" . "0001";
  871. $rc_e_serial{$index} = $cf;
  872. $index++;
  873. $rc_e_serial{$index} = $cb;
  874. }
  875. foreach my $idx ( keys %$rc2_m ) {
  876. next unless $idx;
  877. my $cm = $rc2_m->{$idx};
  878. next unless $cm;
  879. my ( $slot, $mda ) = split /\./, $idx;
  880. $mda = sprintf( "%02d", $mda );
  881. my $index = "$idx" . "$mda" . "00";
  882. $rc_e_serial{$index} = $cm;
  883. }
  884. }
  885. return \%rc_e_serial;
  886. }
  887. sub e_pos {
  888. my $passport = shift;
  889. my $rc_e_idx = $passport->e_index() || {};
  890. my %rc_e_pos;
  891. foreach my $iid ( keys %$rc_e_idx ) {
  892. next unless $iid;
  893. if ( $iid == 1 ) {
  894. $rc_e_pos{$iid} = -1;
  895. next;
  896. }
  897. my $sub = int( substr( $iid, -2, 2 ) );
  898. my $mod = int( substr( $iid, -4, 2 ) );
  899. my $slot = substr( $iid, -6, 2 );
  900. if ( $iid =~ /(00){2}$/ ) {
  901. $rc_e_pos{$iid} = $slot;
  902. }
  903. elsif ( $iid =~ /(00){1}$/ ) {
  904. $rc_e_pos{$iid} = $mod * 100;
  905. }
  906. else {
  907. $rc_e_pos{$iid} = $sub;
  908. }
  909. }
  910. return \%rc_e_pos;
  911. }
  912. sub e_parent {
  913. my $passport = shift;
  914. my $rc_e_idx = $passport->e_index() || {};
  915. my %rc_e_parent;
  916. foreach my $iid ( keys %$rc_e_idx ) {
  917. next unless $iid;
  918. if ( $iid == 1 ) {
  919. $rc_e_parent{$iid} = 0;
  920. next;
  921. }
  922. my $slot = substr( $iid, -6, 2 );
  923. if ( $iid =~ /(00){1,2}$/ ) {
  924. $rc_e_parent{$iid} = 1;
  925. }
  926. else {
  927. $rc_e_parent{$iid} = "$slot" . "0000";
  928. }
  929. }
  930. return \%rc_e_parent;
  931. }
  932. 1;
  933. __END__
  934. =head1 NAME
  935. SNMP::Info::Layer3::Passport - SNMP Interface to modular Avaya
  936. Ethernet Routing Switch 8000 Series and VSP 9000 Series switches.
  937. =head1 AUTHOR
  938. Eric Miller
  939. =head1 SYNOPSIS
  940. # Let SNMP::Info determine the correct subclass for you.
  941. my $passport = new SNMP::Info(
  942. AutoSpecify => 1,
  943. Debug => 1,
  944. DestHost => 'myswitch',
  945. Community => 'public',
  946. Version => 2
  947. )
  948. or die "Can't connect to DestHost.\n";
  949. my $class = $passport->class();
  950. print "SNMP::Info determined this device to fall under subclass : $class\n";
  951. =head1 DESCRIPTION
  952. Abstraction subclass for modular Avaya Ethernet Routing Switch 8000 Series
  953. (formerly Nortel/Bay Passport/Accelar) and VSP 9000 Series switches.
  954. These devices have some of the same characteristics as the stackable Avaya
  955. Ethernet Switches (Baystack). For example, extended interface information is
  956. gleaned from F<RAPID-CITY>.
  957. For speed or debugging purposes you can call the subclass directly, but not
  958. after determining a more specific class using the method above.
  959. my $passport = new SNMP::Info::Layer3::Passport(...);
  960. =head2 Inherited Classes
  961. =over
  962. =item SNMP::Info::SONMP
  963. =item SNMP::Info::RapidCity
  964. =item SNMP::Info::Layer3
  965. =back
  966. =head2 Required MIBs
  967. =over
  968. =item Inherited Classes' MIBs
  969. See L<SNMP::Info::SONMP/"Required MIBs"> for its own MIB requirements.
  970. See L<SNMP::Info::RapidCity/"Required MIBs"> for its own MIB requirements.
  971. See L<SNMP::Info::Layer3/"Required MIBs"> for its own MIB requirements.
  972. =back
  973. =head1 GLOBALS
  974. These are methods that return scalar value from SNMP
  975. =over
  976. =item $passport->model()
  977. Returns model type. Checks $passport->id() against the
  978. F<RAPID-CITY-MIB> and then parses out C<rcA>.
  979. =item $passport->vendor()
  980. Returns 'avaya'
  981. =item $passport->os()
  982. Returns 'passport'
  983. =item $passport->os_ver()
  984. Returns the software version extracted from C<sysDescr>
  985. =item $passport->serial()
  986. Returns (C<rcChasSerialNumber>)
  987. =item $passport->root_ip()
  988. Returns the primary IP used to communicate with the device. Returns the first
  989. found: CLIP (CircuitLess IP), Management Virtual IP (C<rcSysVirtualIpAddr>),
  990. OSPF Router ID (C<ospfRouterId>), SONMP Advertised IP Address.
  991. =back
  992. =head2 Overrides
  993. =over
  994. =item $passport->index_factor()
  995. Required by SNMP::Info::SONMP. Returns 64 for 8600, 16 for Accelar.
  996. =item $passport->port_offset()
  997. Required by SNMP::Info::SONMP. Returns 1.
  998. =item $passport->slot_offset()
  999. Required by SNMP::Info::SONMP. Returns 0.
  1000. =back
  1001. =head2 Global Methods imported from SNMP::Info::SONMP
  1002. See documentation in L<SNMP::Info::SONMP/"GLOBALS"> for details.
  1003. =head2 Global Methods imported from SNMP::Info::RapidCity
  1004. See documentation in L<SNMP::Info::RapidCity/"GLOBALS"> for details.
  1005. =head2 Globals imported from SNMP::Info::Layer3
  1006. See documentation in L<SNMP::Info::Layer3/"GLOBALS"> for details.
  1007. =head1 TABLE METHODS
  1008. These are methods that return tables of information in the form of a reference
  1009. to a hash.
  1010. =head2 Overrides
  1011. =over
  1012. =item $passport->i_index()
  1013. Returns SNMP IID to Interface index. Extends (C<ifIndex>) by adding the index
  1014. of the CPU virtual management IP (if present), each CPU Ethernet port, and
  1015. each VLAN to ensure the virtual router ports are captured.
  1016. =item $passport->interfaces()
  1017. Returns reference to the map between IID and physical Port.
  1018. Slot and port numbers on the Passport switches are determined by the formula:
  1019. port = (C<ifIndex % index_factor>) + port_offset,
  1020. slot = int(C<ifIndex / index_factor>).
  1021. The physical port name is returned as slot.port. CPU Ethernet ports are
  1022. prefixed with CPU and VLAN interfaces are returned as the VLAN ID prefixed
  1023. with Vlan.
  1024. =item $passport->i_mac()
  1025. MAC address of the interface. Note this is just the MAC of the port, not
  1026. anything connected to it.
  1027. =item $passport->i_description()
  1028. Description of the interface. Usually a little longer single word name that is both
  1029. human and machine friendly. Not always.
  1030. =item $passport->i_name()
  1031. Crosses rc_alias() (C<rcPortName>) with ifAlias() and returns the human set
  1032. port name if exists.
  1033. =item $passport->ip_index()
  1034. Maps the IP Table to the IID. Extends (C<ipAdEntIfIndex>) by adding the index of
  1035. the CPU virtual management IP (if present) and each CPU Ethernet port.
  1036. =item $passport->ip_netmask()
  1037. Extends (C<ipAdEntNetMask>) by adding the mask of the CPU virtual management
  1038. IP (if present) and each CPU Ethernet port.
  1039. =item $passport->bp_index()
  1040. Returns reference to hash of bridge port table entries map back to interface
  1041. identifier (iid)
  1042. Returns (C<ifIndex>) for both key and value since some devices seem to have
  1043. problems with F<BRIDGE-MIB>
  1044. =back
  1045. =head2 Forwarding Table
  1046. These methods utilize, in order; F<Q-BRIDGE-MIB>, F<BRIDGE-MIB>, and
  1047. F<RAPID-CITY> to obtain the forwarding table information.
  1048. =over
  1049. =item $passport->fw_mac()
  1050. Returns reference to hash of forwarding table MAC Addresses
  1051. (C<dot1qTpFdbAddress>), (C<dot1dTpFdbAddress>), (C<rcBridgeTpFdbAddress>)
  1052. =item $passport->fw_port()
  1053. Returns reference to hash of forwarding table entries port interface
  1054. identifier (iid)
  1055. (C<dot1qTpFdbPort>), (C<dot1dTpFdbPort>), (C<rcBridgeTpFdbPort>)
  1056. =item $passport->fw_status()
  1057. Returns reference to hash of forwarding table entries status
  1058. (C<dot1qTpFdbStatus>), (C<dot1dTpFdbStatus>), (C<rcBridgeTpFdbStatus>)
  1059. =item $passport->qb_fw_vlan()
  1060. Returns reference to hash of forwarding table entries VLAN ID
  1061. (C<dot1qFdbId>), (C<rcBridgeTpFdbVlanId>)
  1062. =back
  1063. =head2 Pseudo F<ENTITY-MIB> information
  1064. These devices do not support F<ENTITY-MIB>. These methods emulate Physical
  1065. Table methods using the F<RAPID-CITY MIB>.
  1066. =over
  1067. =item $passport->e_index()
  1068. Returns reference to hash. Key and Value: Integer. The index is created by
  1069. combining the slot, module, and position into a five or six digit integer.
  1070. Slot can be either one or two digits while the module and position are each
  1071. two digits padded with leading zero if required.
  1072. =item $passport->e_class()
  1073. Returns reference to hash. Key: IID, Value: General hardware type. This
  1074. class only returns container, module, and power supply types.
  1075. =item $passport->e_descr()
  1076. Returns reference to hash. Key: IID, Value: Human friendly name.
  1077. =item $passport->e_name()
  1078. Returns reference to hash. Key: IID, Value: Human friendly name.
  1079. =item $passport->e_hwver()
  1080. Returns reference to hash. Key: IID, Value: Hardware version.
  1081. =item $passport->e_vendor()
  1082. Returns reference to hash. Key: IID, Value: avaya.
  1083. =item $passport->e_serial()
  1084. Returns reference to hash. Key: IID, Value: Serial number.
  1085. =item $passport->e_pos()
  1086. Returns reference to hash. Key: IID, Value: The relative position among all
  1087. entities sharing the same parent.
  1088. =item $passport->e_type()
  1089. Returns reference to hash. Key: IID, Value: Type of component/sub-component.
  1090. =item $passport->e_parent()
  1091. Returns reference to hash. Key: IID, Value: The value of e_index() for the
  1092. entity which 'contains' this entity. A value of zero indicates this entity
  1093. is not contained in any other entity.
  1094. =back
  1095. =head2 Table Methods imported from SNMP::Info::SONMP
  1096. See documentation in L<SNMP::Info::SONMP/"TABLE METHODS"> for details.
  1097. =head2 Table Methods imported from SNMP::Info::RapidCity
  1098. See documentation in L<SNMP::Info::RapidCity/"TABLE METHODS"> for details.
  1099. =head2 Table Methods imported from SNMP::Info::Layer3
  1100. See documentation in L<SNMP::Info::Layer3/"TABLE METHODS"> for details.
  1101. =cut