2 # Copyright (C) 2007 Jon Nistor
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18 # $Id: JunOS.pm,v 1.1 2010-12-27 00:03:53 ivan Exp $
19 # Jon Nistor <nistor at snickers.org>
21 # Juniper JunOS Discovery Module
23 # NOTE: For Class of service, if you are noticing that you are not seeing
24 # all of your queue names show up, this is by design of Juniper.
25 # Solution: Put place-holder names for those queues such as:
27 # This is in reference to JunOS 7.6
29 # NOTE: Options for this module:
31 # JunOS::disable-cos-red
32 # JunOS::disable-cos-tail
33 # JunOS::disable-firewall
34 # JunOS::disable-operating
37 package Torrus::DevDiscover::JunOS;
43 $Torrus::DevDiscover::registry{'JunOS'} = {
45 'checkdevtype' => \&checkdevtype,
46 'discover' => \&discover,
47 'buildConfig' => \&buildConfig
54 'jnxProducts' => '1.3.6.1.4.1.2636.1',
55 'jnxBoxDescr' => '1.3.6.1.4.1.2636.3.1.2.0',
56 'jnxBoxSerialNo' => '1.3.6.1.4.1.2636.3.1.3.0',
59 'jnxOperatingDescr' => '1.3.6.1.4.1.2636.3.1.13.1.5',
60 'jnxOperatingTemp' => '1.3.6.1.4.1.2636.3.1.13.1.7',
61 'jnxOperatingCPU' => '1.3.6.1.4.1.2636.3.1.13.1.8',
62 'jnxOperatingISR' => '1.3.6.1.4.1.2636.3.1.13.1.9',
63 'jnxOperatingDRAMSize' => '1.3.6.1.4.1.2636.3.1.13.1.10', # deprecated
64 'jnxOperatingBuffer' => '1.3.6.1.4.1.2636.3.1.13.1.11',
65 'jnxOperatingMemory' => '1.3.6.1.4.1.2636.3.1.13.1.15',
68 'jnxFWCounterDisplayFilterName' => '1.3.6.1.4.1.2636.3.5.2.1.6',
69 'jnxFWCounterDisplayName' => '1.3.6.1.4.1.2636.3.5.2.1.7',
70 'jnxFWCounterDisplayType' => '1.3.6.1.4.1.2636.3.5.2.1.8',
72 # Class of Service (jnxCosIfqStatsTable deprecated, use jnxCosQstatTable)
73 # COS - Class Of Service
74 # RED - Random Early Detection
75 # PLP - Packet Loss Priority
76 # DSCP - Differential Service Code Point
78 'jnxCosFcIdToFcName' => '1.3.6.1.4.1.2636.3.15.3.1.2',
79 'jnxCosQstatQedPkts' => '1.3.6.1.4.1.2636.3.15.4.1.3',
81 # Reverse path forwarding
82 'jnxRpfStatsPackets' => '1.3.6.1.4.1.2636.3.17.1.1.1.3'
87 # Not all interfaces are normally needed to monitor.
88 # You may override the interface filtering in devdiscover-siteconfig.pl:
89 # redefine $Torrus::DevDiscover::JunOS::interfaceFilter
90 # or define $Torrus::DevDiscover::JunOS::interfaceFilterOverlay
93 our $interfaceFilterOverlay;
94 my %junosInterfaceFilter;
96 if( not defined( $interfaceFilter ) )
98 $interfaceFilter = \%junosInterfaceFilter;
102 # Key is some unique symbolic name, does not mean anything
103 # ifType is the number to match the interface type
104 # ifDescr is the regexp to match the interface description
105 %junosInterfaceFilter =
108 'ifType' => 150, # mplsTunnel
113 'ifType' => 1, # other
117 'ifType' => 24, # softwareLoopback
121 'ifType' => 53, # propVirtual
124 'gre_ipip_pime_pimd_mtun' => {
125 'ifType' => 131, # tunnel
126 'ifDescr' => '^(gre)|(ipip)|(pime)|(pimd)|(mtun)$'
129 'pd_pe_gr_ip_mt_lt' => {
130 'ifType' => 131, # tunnel
131 'ifDescr' => '^(pd)|(pe)|(gr)|(ip)|(mt)|(lt)-\d+\/\d+\/\d+$'
135 'ifType' => 108, # pppMultilinkBundle
136 'ifDescr' => '^ls-\d+\/\d+\/\d+$'
145 my $devdetails = shift;
147 if( not $dd->oidBaseMatch
149 $devdetails->snmpVar( $dd->oiddef('sysObjectID') ) )
155 &Torrus::DevDiscover::RFC2863_IF_MIB::addInterfaceFilter
156 ($devdetails, $interfaceFilter);
158 if( defined( $interfaceFilterOverlay ) )
160 &Torrus::DevDiscover::RFC2863_IF_MIB::addInterfaceFilter
161 ($devdetails, $interfaceFilterOverlay);
164 $devdetails->setCap('interfaceIndexingPersistent');
173 my $devdetails = shift;
175 my $session = $dd->session();
176 my $data = $devdetails->data();
178 # NOTE: Comments and Serial number of device
180 $dd->retrieveSnmpOIDs( 'jnxBoxDescr', 'jnxBoxSerialNo' );
182 if( defined( $chassisSerial ) )
184 $data->{'param'}{'comment'} = $chassisSerial->{'jnxBoxDescr'} .
185 ', Hw Serial#: ' . $chassisSerial->{'jnxBoxSerialNo'};
188 $data->{'param'}{'comment'} = "Juniper router";
192 # PROG: Class of Service
194 if( $devdetails->param('JunOS::disable-cos') ne 'yes' )
196 # Poll table to translate the CoS Index to a Name
197 my $cosQueueNumTable =
198 $session->get_table( -baseoid =>
199 $dd->oiddef('jnxCosFcIdToFcName') );
200 $devdetails->storeSnmpVars( $cosQueueNumTable );
202 if( $cosQueueNumTable )
204 $devdetails->setCap('jnxCoS');
206 # Find the index of the CoS queue name
207 foreach my $cosFcIndex ( $devdetails->getSnmpIndices
208 ($dd->oiddef('jnxCosFcIdToFcName')) )
210 my $cosFcNameOid = $dd->oiddef('jnxCosFcIdToFcName') . "." .
212 my $cosFcName = $cosQueueNumTable->{$cosFcNameOid};
214 Debug("JunOS::CoS FC index: $cosFcIndex name: $cosFcName");
216 # Construct the data ...
217 $data->{'jnxCos'}{'queue'}{$cosFcIndex} = $cosFcName;
220 # We need to find out all the interfaces that have CoS enabled
221 # on them. We will use jnxCosQstatQedPkts as our reference point.
223 $session->get_table( -baseoid =>
224 $dd->oiddef('jnxCosQstatQedPkts') );
225 $devdetails->storeSnmpVars( $cosIfIndex );
229 foreach my $INDEX ( $devdetails->getSnmpIndices
230 ($dd->oiddef('jnxCosQstatQedPkts')) )
232 my( $ifIndex, $cosQueueIndex ) = split( '\.', $INDEX );
233 $data->{'jnxCos'}{'ifIndex'}{$ifIndex} = 1;
237 } # END JunOS::disable-cos
240 # PROG: Grab and store description of parts
242 if( $devdetails->param('JunOS::disable-operating') ne 'yes' )
244 my $tableDesc = $session->get_table( -baseoid =>
245 $dd->oiddef('jnxOperatingDescr'));
246 $devdetails->storeSnmpVars( $tableDesc );
250 # PROG: Set Capability flag
251 $devdetails->setCap('jnxOperating');
253 # PROG: Poll tables for more info to match and index on
255 $session->get_table( -baseoid =>
256 $dd->oiddef('jnxOperatingCPU'));
257 $devdetails->storeSnmpVars( $tableCPU );
260 $session->get_table( -baseoid =>
261 $dd->oiddef('jnxOperatingISR'));
262 $devdetails->storeSnmpVars( $tableISR );
265 $session->get_table( -baseoid =>
266 $dd->oiddef('jnxOperatingMemory'));
267 $devdetails->storeSnmpVars( $tableMEM );
270 $session->get_table( -baseoid =>
271 $dd->oiddef('jnxOperatingTemp'));
272 $devdetails->storeSnmpVars( $tableTemp );
274 # PROG: Build tables for all the oids
275 # We are using the Descr oid base for matching. (cheap hack)
276 foreach my $opIndex ( $devdetails->getSnmpIndices
277 ($dd->oiddef('jnxOperatingDescr')) )
279 my $opCPU = $devdetails->snmpVar
280 ($dd->oiddef('jnxOperatingCPU') . '.' . $opIndex);
281 my $opDesc = $devdetails->snmpVar
282 ($dd->oiddef('jnxOperatingDescr') . '.' . $opIndex);
283 my $opMem = $devdetails->snmpVar
284 ($dd->oiddef('jnxOperatingMemory') . '.' . $opIndex);
285 my $opISR = $devdetails->snmpVar
286 ($dd->oiddef('jnxOperatingISR') . '.' . $opIndex);
287 my $opTemp = $devdetails->snmpVar
288 ($dd->oiddef('jnxOperatingTemp') . '.' . $opIndex);
290 Debug("JunOS:: opIdx: $opIndex Desc: $opDesc");
291 Debug("JunOS:: CPU: $opCPU, CPU: $opISR, MEM: $opMem");
292 Debug("JunOS:: Temp: $opTemp");
295 $data->{'jnxOperating'}{$opIndex}{'index'} = $opIndex;
296 $data->{'jnxOperating'}{$opIndex}{'cpu'} = $opCPU;
297 $data->{'jnxOperating'}{$opIndex}{'desc'} = $opDesc;
298 $data->{'jnxOperating'}{$opIndex}{'isr'} = $opISR;
299 $data->{'jnxOperating'}{$opIndex}{'mem'} = $opMem;
300 $data->{'jnxOperating'}{$opIndex}{'temp'} = $opTemp;
302 } # END: if $tableDesc
303 } # END: JunOS::disable-operating
306 # PROG: Firewall statistics
307 if( $devdetails->param('JunOS::disable-firewall') ne 'yes' )
310 $session->get_table( -baseoid =>
311 $dd->oiddef('jnxFWCounterDisplayFilterName'));
312 $devdetails->storeSnmpVars( $tableFWFilter );
316 # PROG: Set Capability flag
317 $devdetails->setCap('jnxFirewall');
319 # PROG: Poll tables for more info to match and index on
321 $session->get_table( -baseoid =>
322 $dd->oiddef('jnxFWCounterDisplayName') );
323 $devdetails->storeSnmpVars( $tableFWCounter );
325 # Firewall Type (counter = 2, policer = 3)
327 $session->get_table( -baseoid =>
328 $dd->oiddef('jnxFWCounterDisplayType') );
329 $devdetails->storeSnmpVars( $tableFWType );
331 # PROG: Build tables for all the oids
332 # We are using the FW Filter name as the Indexing
333 foreach my $fwIndex ( $devdetails->getSnmpIndices
334 ($dd->oiddef('jnxFWCounterDisplayName')) )
336 my $fwFilter = $devdetails->snmpVar
337 ($dd->oiddef('jnxFWCounterDisplayFilterName') .
339 my $fwCounter = $devdetails->snmpVar
340 ($dd->oiddef('jnxFWCounterDisplayName') .
342 my $fwType = $devdetails->snmpVar
343 ($dd->oiddef('jnxFWCounterDisplayType') .
345 Debug("JunOS::fw Filter: $fwFilter");
346 Debug("JunOS::fw Counter: $fwCounter");
347 Debug("JunOS::fw Type: $fwType");
350 $data->{'jnxFirewall'}{$fwFilter}{$fwCounter}{'oid'} =
352 $data->{'jnxFirewall'}{$fwFilter}{$fwCounter}{'type'} =
355 } # END: if $tableFWfilter
356 } # END: JunOS::diable-firewall
359 # PROG: Check for RPF availability
360 if( $devdetails->param('JunOS::disable-rpf') ne 'yes' )
363 $session->get_table( -baseoid =>
364 $dd->oiddef('jnxRpfStatsPackets') );
365 $devdetails->storeSnmpVars( $tableRPF );
369 # PROG: Set capability flag
370 $devdetails->setCap('jnxRPF');
372 # PROG: Find all the relevent interfaces
373 foreach my $rpfIndex ( $devdetails->getSnmpIndices
374 ($dd->oiddef('jnxRpfStatsPackets')) )
376 my ($ifIndex,$addrFamily) = split('\.',$rpfIndex);
377 if( defined( $data->{'interfaces'}{$ifIndex} ) )
379 my $ifAddrFam = $addrFamily == 1 ? 'ipv4' : 'ipv6';
380 my $intName = $data->{'interfaces'}{$ifIndex}{'ifName'};
381 my $intNameT = $data->{'interfaces'}{$ifIndex}{'ifNameT'};
384 $data->{'jnxRPF'}{$ifIndex}{'ifName'} = $intName;
385 $data->{'jnxRPF'}{$ifIndex}{'ifNameT'} = $intNameT;
387 if( $addrFamily == 1 )
389 $data->{'jnxRPF'}{$ifIndex}{'ipv4'} = 1;
391 if( $addrFamily == 2 )
393 $data->{'jnxRPF'}{$ifIndex}{'ipv6'} = 2;
406 my $devdetails = shift;
409 my $data = $devdetails->data();
412 # PROG: Class of Service information
413 if( $devdetails->hasCap('jnxCoS') &&
414 ( keys %{$data->{'jnxCos'}{'ifIndex'}} > 0 )
417 # PROG: Add CoS information if it exists.
418 my $nodeTop = $cb->addSubtree( $devNode, 'CoS', undef,
419 [ 'JunOS::junos-cos-subtree']);
421 foreach my $ifIndex ( sort {$a <=> $b} keys
422 %{$data->{'jnxCos'}{'ifIndex'}} )
424 my $interface = $data->{'interfaces'}{$ifIndex};
425 my $ifAlias = $interface->{'ifAlias'};
426 my $ifDescr = $interface->{'ifDescr'};
427 my $ifName = $interface->{'ifNameT'};
429 next if( not $ifName ); # Skip since port is likely 'disabled'
430 # This might be better to match against ifType
431 # as well since not all of them support Q's.
433 # Add Subtree per port
435 $cb->addSubtree( $nodeTop, $ifName,
436 { 'comment' => $ifAlias,
437 'precedence' => 1000 - $ifIndex },
438 [ 'JunOS::junos-cos-subtree-interface' ]);
440 # Loop to create subtree's for each QueueName/ID pair
441 foreach my $cosIndex ( sort keys %{$data->{'jnxCos'}{'queue'}} )
443 my $cosName = $data->{'jnxCos'}{'queue'}{$cosIndex};
445 # Add Leaf for each one
446 Debug("JunOS::CoS ifIndex: $ifIndex ($ifName -> $cosName)");
448 $cb->addSubtree( $nodePort, $cosName,
449 { 'comment' => "Class: " . $cosName,
450 'cos-index' => $cosIndex,
451 'cos-name' => $cosName,
452 'ifDescr' => $ifDescr,
453 'ifIndex' => $ifIndex,
455 'precedence' => 1000 - $cosIndex },
456 [ 'JunOS::junos-cos-leaf' ]);
458 if( $devdetails->param('JunOS::disable-cos-tail') ne 'yes' )
460 $cb->addSubtree( $nodeIFCOS, "Tail_drop_stats",
461 { 'comment' => 'Tail drop statistics' },
462 [ 'JunOS::junos-cos-tail' ]);
465 if( $devdetails->param('JunOS::disable-cos-red') ne 'yes' )
468 ( $nodeIFCOS, "RED_stats",
469 { 'comment' => 'Random Early Detection' },
470 [ 'JunOS::junos-cos-red' ]);
473 } # end foreach (INDEX of queue's [Q-ID])
474 } # end foreach (INDEX of port)
475 } # end if HasCap->{CoS}
478 # PROG: Firewall Table (filters and counters)
479 if( $devdetails->hasCap('jnxFirewall') )
482 my $nodeFW = $cb->addSubtree( $devNode, 'Firewall', undef,
483 [ 'JunOS::junos-firewall-subtree' ]);
485 # Loop through and find all the filter names
487 ( sort {$a <=> $b} keys %{$data->{'jnxFirewall'}} )
489 my $firewall = $data->{'jnxFirewall'}{$fwFilter};
491 # Add subtree for FilterName
493 $cb->addSubtree( $nodeFW, $fwFilter,
494 { 'comment' => 'Filter: ' . $fwFilter },
495 [ 'JunOS::junos-firewall-filter-subtree' ]);
497 # Loop through and find all the counter names within the filter
498 foreach my $fwCounter ( sort {$a <=> $b} keys %{$firewall} )
500 my $fwOid = $firewall->{$fwCounter}{'oid'};
501 my $fwType = $firewall->{$fwCounter}{'type'};
502 my @templates = ( 'JunOS::junos-firewall-filter' );
504 # Figure out which templates to apply ...
507 # fwType is a counter ...
509 'JunOS::junos-firewall-filter-counter',
510 'JunOS::junos-firewall-filter-policer' );
514 # fwType is a policer ...
516 'JunOS::junos-firewall-filter-policer' );
519 # Finally, add the subtree...
520 my $fwTypeName = $fwType == 2 ? 'Counter: ' : 'Policer: ';
522 $cb->addSubtree($nodeFWFilter, $fwCounter,
523 { 'comment' => $fwTypeName . $fwCounter,
524 'fw-counter' => $fwCounter,
525 'fw-filter' => $fwFilter,
526 'fw-index' => $fwOid }, \@templates );
527 } # END foreach $fwCounter
528 } # END foreach $fwFilter
529 } # END: if hasCap jnxFirewall
532 # PROG: Operating Status Table
533 # NOTE: According to the Juniper MIB, the following is a statement:
534 # jnxOperatingTemp: The temperature in Celsius (degrees C) of this
535 # subject. Zero if unavailable or inapplicable.
536 # The same applies for all values under Operating status table, if
537 # Zero is shown it might be considered unavail or N/A. We will
538 # also take that into consideration.
539 # NOTE: Also so poorly written, its great.
540 if( $devdetails->hasCap('jnxOperating') )
542 my $nodeCPU = $cb->addSubtree( $devNode, 'CPU_Usage', undef,
543 [ 'JunOS::junos-cpu-subtree' ]);
545 my $nodeMem = $cb->addSubtree( $devNode, 'Memory_Usage', undef,
546 [ 'JunOS::junos-memory-subtree' ]);
549 $cb->addSubtree( $devNode, 'Temperature_Sensors', undef,
550 [ 'JunOS::junos-temperature-subtree' ]);
554 ( sort {$a <=> $b} keys %{$data->{'jnxOperating'}} )
556 my $operating = $data->{'jnxOperating'}{$opIndex};
557 my $jnxCPU = $operating->{'cpu'};
558 my $jnxDesc = $operating->{'desc'};
559 my $jnxMem = $operating->{'mem'};
560 my $jnxTemp = $operating->{'temp'};
561 my $jnxTag = $jnxDesc;
562 $jnxTag =~ s/\W+/_/go;
564 # Fix the .'s into _'s for the RRD-DS and name of leaf
565 my $opIndexFix = $opIndex;
566 $opIndexFix =~ s/\./_/g;
568 # PROG: Find CPU that does not equal 0
571 $cb->addSubtree( $nodeCPU, $jnxTag,
572 { 'comment' => $jnxDesc,
573 'cpu-index' => $opIndex },
574 [ 'JunOS::junos-cpu' ]);
577 # PROG: Find memory that does not equal 0
580 $cb->addSubtree( $nodeMem, $jnxTag,
581 { 'comment' => $jnxDesc,
582 'mem-index' => $opIndex,
583 'mem-indexFix' => $opIndexFix },
584 [ 'JunOS::junos-memory' ]);
587 # PROG: Find Temperature that does not equal 0
590 if ($jnxDesc =~ /(temp.* sensor|Engine)/) {
591 # Small little hack to cleanup the sensor tags
592 $jnxTag =~ s/_temp(erature|)_sensor//g;
593 $cb->addLeaf( $nodeTemp, $jnxTag,
594 { 'comment' => $jnxDesc,
595 'sensor-desc' => $jnxDesc,
596 'sensor-index' => $opIndex,
597 'sensor-indexFix' => $opIndexFix },
598 [ 'JunOS::junos-temperature-sensor' ]);
601 } # END foreach $opIndex
602 } # END if jnxOperating
605 # PROG: Reverse Forwarding Path (RPF)
606 if( $devdetails->hasCap('jnxRPF') )
609 my $nodeRPF = $cb->addSubtree( $devNode, 'RPF', undef,
610 [ 'JunOS::junos-rpf-subtree' ]);
612 # Loop through and find all interfaces with RPF enabled
613 foreach my $ifIndex ( sort {$a <=> $b} keys %{$data->{'jnxRPF'}} )
616 my $ifAlias = $data->{'interfaces'}{$ifIndex}{'ifAlias'};
617 my $ifName = $data->{'interfaces'}{$ifIndex}{'ifName'};
618 my $ifNameT = $data->{'interfaces'}{$ifIndex}{'ifNameT'};
619 my $hasIPv4 = $data->{'jnxRPF'}{$ifIndex}{'ipv4'};
620 my $hasIPv6 = $data->{'jnxRPF'}{$ifIndex}{'ipv6'};
622 Debug("JunOS:: RPF int: $ifName IPv4: $hasIPv4 IPv6: $hasIPv6");
624 # PROG: Process IPv4 first ...
627 $cb->addSubtree( $nodeRPF, 'IPv4_' . $ifNameT,
628 { 'comment' => $ifAlias,
629 'ifAddrType' => "ipv4",
631 'ifNameT' => $ifNameT,
632 'rpfIndex' => $ifIndex . "." . $hasIPv4 },
633 [ 'JunOS::junos-rpf' ]);
638 $cb->addSubtree( $nodeRPF, 'IPv6_' . $ifNameT,
639 { 'comment' => $ifAlias,
640 'ifAddrType' => "ipv6",
642 'ifNameT' => $ifNameT,
643 'rpfIndex' => $ifIndex . "." . $hasIPv6 },
644 [ 'JunOS::junos-rpf' ]);
655 # indent-tabs-mode: nil
656 # perl-indent-level: 4