import torrus 1.0.9
[freeside.git] / torrus / perllib / Torrus / DevDiscover / RFC2863_IF_MIB.pm
1 #  Copyright (C) 2002  Stanislav Sinyagin
2 #
3 #  This program is free software; you can redistribute it and/or modify
4 #  it under the terms of the GNU General Public License as published by
5 #  the Free Software Foundation; either version 2 of the License, or
6 #  (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software
15 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
16
17 # $Id: RFC2863_IF_MIB.pm,v 1.1 2010-12-27 00:03:57 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
19
20 # Standard IF_MIB discovery, which should apply to most devices
21
22 package Torrus::DevDiscover::RFC2863_IF_MIB;
23
24 use strict;
25 use Torrus::Log;
26
27
28 $Torrus::DevDiscover::registry{'RFC2863_IF_MIB'} = {
29     'sequence'     => 50,
30     'checkdevtype' => \&checkdevtype,
31     'discover'     => \&discover,
32     'buildConfig'  => \&buildConfig,
33     'buildGlobalConfig' => \&buildGlobalConfig
34     };
35
36
37 our %oiddef =
38     (
39      'ifTable'          => '1.3.6.1.2.1.2.2',
40      'ifDescr'          => '1.3.6.1.2.1.2.2.1.2',
41      'ifType'           => '1.3.6.1.2.1.2.2.1.3',
42      'ifSpeed'          => '1.3.6.1.2.1.2.2.1.5',
43      'ifPhysAddress'    => '1.3.6.1.2.1.2.2.1.6',
44      'ifAdminStatus'    => '1.3.6.1.2.1.2.2.1.7',
45      'ifOperStatus'     => '1.3.6.1.2.1.2.2.1.8',
46      'ifInOctets'       => '1.3.6.1.2.1.2.2.1.10',
47      'ifInUcastPkts'    => '1.3.6.1.2.1.2.2.1.11',
48      'ifInDiscards'     => '1.3.6.1.2.1.2.2.1.13',
49      'ifInErrors'       => '1.3.6.1.2.1.2.2.1.14',
50      'ifOutOctets'      => '1.3.6.1.2.1.2.2.1.16',
51      'ifOutUcastPkts'   => '1.3.6.1.2.1.2.2.1.17',
52      'ifOutDiscards'    => '1.3.6.1.2.1.2.2.1.19',
53      'ifOutErrors'      => '1.3.6.1.2.1.2.2.1.20',
54      'ifXTable'         => '1.3.6.1.2.1.31.1.1',
55      'ifName'           => '1.3.6.1.2.1.31.1.1.1.1',
56      'ifHCInOctets'     => '1.3.6.1.2.1.31.1.1.1.6',
57      'ifHCInUcastPkts'  => '1.3.6.1.2.1.31.1.1.1.7',
58      'ifHCOutOctets'    => '1.3.6.1.2.1.31.1.1.1.10',
59      'ifHCOutUcastPkts' => '1.3.6.1.2.1.31.1.1.1.11',
60      'ifHighSpeed'      => '1.3.6.1.2.1.31.1.1.1.15',     
61      'ifAlias'          => '1.3.6.1.2.1.31.1.1.1.18'
62      );
63
64
65
66 # Just curious, are there any devices without ifTable?
67 sub checkdevtype
68 {
69     my $dd = shift;
70     my $devdetails = shift;
71
72     return $dd->checkSnmpTable('ifTable');
73 }
74
75
76 sub discover
77 {
78     my $dd = shift;
79     my $devdetails = shift;
80     
81     my $session = $dd->session();
82     
83     my $ifTable =
84         $session->get_table( -baseoid => $dd->oiddef('ifTable') );
85     if( not defined $ifTable )
86     {
87         Error('Cannot retrieve ifTable');
88         return 0;
89     }
90     $devdetails->storeSnmpVars( $ifTable );
91
92     my $ifXTable =
93         $session->get_table( -baseoid => $dd->oiddef('ifXTable') );
94     if( defined $ifXTable )
95     {
96         $devdetails->storeSnmpVars( $ifXTable );
97         $devdetails->setCap('ifXTable');
98
99         if( $devdetails->hasOID( $dd->oiddef('ifName') ) )
100         {
101             $devdetails->setCap('ifName');
102         }
103
104         if( $devdetails->hasOID( $dd->oiddef('ifAlias') ) )
105         {
106             $devdetails->setCap('ifAlias');
107         }
108         
109         if( $devdetails->hasOID( $dd->oiddef('ifHighSpeed') ) )
110         {
111             $devdetails->setCap('ifHighSpeed');
112         }
113     }
114
115     ## Fill in per-interface data. This is normally done within discover(),
116     ## but in our case we want to give other modules more control as early
117     ## as possible.
118
119     # Define the tables used for subtree naming, interface indexing,
120     # and RRD file naming
121     my $data = $devdetails->data();
122
123     $data->{'param'}{'has-inout-leaves'} = 'yes';
124
125     ## Set default interface index mapping
126     
127     $data->{'nameref'}{'ifSubtreeName'} = 'ifDescrT';
128     $data->{'nameref'}{'ifReferenceName'}   = 'ifDescr';
129
130     if( $devdetails->hasCap('ifName') )
131     {
132         $data->{'nameref'}{'ifNick'} = 'ifNameT';
133     }
134     else
135     {
136         $data->{'nameref'}{'ifNick'} = 'ifDescrT';
137     }
138
139     if( $devdetails->hasCap('ifAlias') )
140     {
141         $data->{'nameref'}{'ifComment'} = 'ifAlias';
142     }
143     
144     # Pre-populate the interfaces table, so that other modules may
145     # delete unneeded interfaces
146     my $includeAdmDown =
147         $devdetails->param('RFC2863_IF_MIB::list-admindown-interfaces')
148         eq 'yes';
149     my $includeNotpresent =
150         $devdetails->param('RFC2863_IF_MIB::list-notpresent-interfaces')
151         eq 'yes';
152     my $excludeOperDown =
153         $devdetails->param('RFC2863_IF_MIB::exclude-down-interfaces')
154         eq 'yes';
155     foreach my $ifIndex
156         ( $devdetails->getSnmpIndices( $dd->oiddef('ifDescr') ) )
157     {
158         my $admStatus =
159             $devdetails->snmpVar($dd->oiddef('ifAdminStatus') .'.'. $ifIndex);
160         my $operStatus =
161             $devdetails->snmpVar($dd->oiddef('ifOperStatus') .'.'. $ifIndex);
162         
163         if( ( $admStatus == 1 or $includeAdmDown ) and
164             ( $operStatus != 6 or $includeNotpresent ) and
165             ( $operStatus != 2 or not $excludeOperDown ) )
166         {
167             my $interface = {};
168             $data->{'interfaces'}{$ifIndex} = $interface;
169
170             $interface->{'param'} = {};
171             $interface->{'vendor_templates'} = [];
172
173             $interface->{'ifType'} =
174                 $devdetails->snmpVar($dd->oiddef('ifType') . '.' . $ifIndex);
175
176             my $descr = $devdetails->snmpVar($dd->oiddef('ifDescr') .
177                                              '.' . $ifIndex);
178             $interface->{'ifDescr'} = $descr;
179             $descr =~ s/\W/_/g;
180             # Some SNMP agents send extra zero byte at the end
181             $descr =~ s/_+$//;
182             $interface->{'ifDescrT'} = $descr;
183
184             if( $devdetails->hasCap('ifName') )
185             {
186                 my $iname = $devdetails->snmpVar($dd->oiddef('ifName') .
187                                                  '.' . $ifIndex);
188                 if( $iname !~ /\w/ )
189                 {
190                     $iname = $interface->{'ifDescr'};
191                     Warn('Empty or invalid ifName for interface ' . $iname);
192                 }
193                 $interface->{'ifName'} = $iname;
194                 $iname =~ s/\W/_/g;
195                 $interface->{'ifNameT'} = $iname;
196             }
197
198             if( $devdetails->hasCap('ifAlias') )
199             {
200                 $interface->{'ifAlias'} =
201                     $devdetails->snmpVar($dd->oiddef('ifAlias') .
202                                          '.' . $ifIndex);
203             }
204
205             my $bw = 0;
206             if( $devdetails->hasCap('ifHighSpeed') )
207             {
208                 my $hiBW = 
209                     $devdetails->snmpVar($dd->oiddef('ifHighSpeed') . '.' .
210                                          $ifIndex);
211                 if( $hiBW >= 10 )
212                 {
213                     $bw = 1e6 * $hiBW;
214                 }
215             }
216
217             if( $bw == 0 )
218             {
219                 $bw = 
220                     $devdetails->snmpVar($dd->oiddef('ifSpeed') . '.' .
221                                          $ifIndex);
222             }
223             
224             if( $bw > 0 )
225             {
226                 $interface->{'ifSpeed'} = $bw;
227             }
228         }
229     }
230
231     ## Process hints on interface indexing
232     ## The capability 'interfaceIndexingManaged' disables the hints
233     ## and lets the vendor discovery module to operate the indexing
234     
235     if( not $devdetails->hasCap('interfaceIndexingManaged') and
236         not $devdetails->hasCap('interfaceIndexingPersistent') )
237     {
238         my $hint =
239             $devdetails->param('RFC2863_IF_MIB::ifindex-map-hint');
240         if( defined( $hint ) )
241         {
242             if( $hint eq 'ifName' )
243             {
244                 if( not $devdetails->hasCap('ifName') )
245                 {
246                     Error('Cannot use ifName interface mapping: ifName is '.
247                           'not supported by device');
248                     return 0;
249                 }
250                 else
251                 {
252                     $data->{'nameref'}{'ifReferenceName'} = 'ifName';
253                     $data->{'param'}{'ifindex-table'} = '$ifName';
254                 }
255             }
256             elsif( $hint eq 'ifPhysAddress' )
257             {
258                 $data->{'param'}{'ifindex-map'} = '$IFIDX_MAC';
259                 retrieveMacAddresses( $dd, $devdetails );
260             }
261             elsif( $hint eq 'ifIndex' )
262             {
263                 $devdetails->setCap('interfaceIndexingPersistent');
264             }
265             else
266             {
267                 Error('Unknown value of RFC2863_IF_MIB::ifindex-map-hint: ' .
268                       $hint);
269             }
270         }
271             
272         $hint =
273             $devdetails->param('RFC2863_IF_MIB::subtree-name-hint');
274         if( defined( $hint ) )
275         {
276             if( $hint eq 'ifName' )
277             {
278                 $data->{'nameref'}{'ifSubtreeName'} = 'ifNameT';
279             }
280             else
281             {
282                 Error('Unknown value of RFC2863_IF_MIB::subtree-name-hint: ' .
283                       $hint);
284             }
285         }
286         
287         $hint =
288             $devdetails->param('RFC2863_IF_MIB::nodeid-hint');
289         if( defined( $hint ) )
290         {
291             $data->{'nameref'}{'ifNodeid'} = $hint;
292         }
293     }
294     
295     if( $devdetails->hasCap('interfaceIndexingPersistent') )
296     {
297         $data->{'param'}{'ifindex-map'} = '$IFIDX_IFINDEX';
298         storeIfIndexParams( $devdetails );
299     }
300
301     if( not defined( $data->{'nameref'}{'ifNodeid'} ) )
302     {
303         $data->{'nameref'}{'ifNodeid'} = 'ifNodeid';
304     }
305     
306     if( not defined( $data->{'nameref'}{'ifNodeidPrefix'} ) )
307     {
308         $data->{'nameref'}{'ifNodeidPrefix'} = 'ifNodeidPrefix';
309     }
310     
311     # Filter out the interfaces if needed
312
313     if( ref( $data->{'interfaceFilter'} ) )
314     {
315         foreach my $ifIndex ( sort {$a<=>$b} keys %{$data->{'interfaces'}} )
316         {
317             my $interface = $data->{'interfaces'}{$ifIndex};
318             my $match = 0;
319
320             foreach my $filterHash ( @{$data->{'interfaceFilter'}} )
321             {
322                 last if $match;
323                 foreach my $filter ( values %{$filterHash} )
324                 {
325                     last if $match;
326
327                     if( defined( $filter->{'ifType'} ) and
328                         $interface->{'ifType'} == $filter->{'ifType'} )
329                     {
330                         if( not defined( $filter->{'ifDescr'} ) or
331                             $interface->{'ifDescr'} =~ $filter->{'ifDescr'} )
332                         {
333                             $match = 1;
334                         }
335                     }
336                 }
337             }
338
339             if( $match )
340             {
341                 Debug('Excluding interface: ' .
342                       $interface->{$data->{'nameref'}{'ifReferenceName'}});
343                 delete $data->{'interfaces'}{$ifIndex};
344             }
345         }
346     }
347
348     my $suppressHCCounters =
349         $devdetails->param('RFC2863_IF_MIB::suppress-hc-counters') eq 'yes';
350
351     # Explore each interface capability
352
353     foreach my $ifIndex ( keys %{$data->{'interfaces'}} )
354     {
355         my $interface = $data->{'interfaces'}{$ifIndex};
356
357         if( $devdetails->hasOID( $dd->oiddef('ifInOctets') .
358                                  '.' . $ifIndex )
359             and
360             $devdetails->hasOID( $dd->oiddef('ifOutOctets') .
361                                  '.' . $ifIndex ) )
362         {
363             $interface->{'hasOctets'} = 1;
364         }
365
366         if( $devdetails->hasOID( $dd->oiddef('ifInUcastPkts') .
367                                  '.' . $ifIndex )
368             and
369             $devdetails->hasOID( $dd->oiddef('ifOutUcastPkts') .
370                                  '.' . $ifIndex ) )
371         {
372             $interface->{'hasUcastPkts'} = 1;
373         }
374
375         if( $devdetails->hasOID( $dd->oiddef('ifInDiscards') .
376                                  '.' . $ifIndex ) )
377         {
378             $interface->{'hasInDiscards'} = 1;
379         }
380
381         if( $devdetails->hasOID( $dd->oiddef('ifOutDiscards') .
382                                  '.' . $ifIndex ) )
383         {
384             $interface->{'hasOutDiscards'} = 1;
385         }
386
387         if( $devdetails->hasOID( $dd->oiddef('ifInErrors') .
388                                  '.' . $ifIndex ) )
389         {
390             $interface->{'hasInErrors'} = 1;
391         }
392
393         if( $devdetails->hasOID( $dd->oiddef('ifOutErrors') .
394                                  '.' . $ifIndex ) )
395         {
396             $interface->{'hasOutErrors'} = 1;
397         }
398
399         if( $devdetails->hasCap('ifXTable') and not $suppressHCCounters )
400         {
401             if( $devdetails->hasOID( $dd->oiddef('ifHCInOctets') .
402                                      '.' . $ifIndex )
403                 and
404                 $devdetails->hasOID( $dd->oiddef('ifHCOutOctets') .
405                                      '.' . $ifIndex ) )
406             {
407                 $interface->{'hasHCOctets'} = 1;
408             }
409
410             if( $devdetails->hasOID( $dd->oiddef('ifHCInUcastPkts') .
411                                      '.' . $ifIndex )
412                 and
413                 $devdetails->hasOID( $dd->oiddef('ifHCOutUcastPkts') .
414                                      '.' . $ifIndex ) )
415             {
416                 $interface->{'hasHCUcastPkts'} = 1;
417             }
418         }
419     }
420
421     push( @{$data->{'templates'}}, 'RFC2863_IF_MIB::rfc2863-ifmib-hostlevel' );
422
423     return 1;
424 }
425
426
427 sub buildConfig
428 {
429     my $devdetails = shift;
430     my $cb = shift;
431     my $devNode = shift;
432     my $globalData = shift;
433
434     my $data = $devdetails->data();
435
436     if( scalar( keys %{$data->{'interfaces'}} ) == 0 )
437     {
438         return;
439     }   
440     
441     # Make sure that ifNick and ifSubtreeName are unique across interfaces
442
443     uniqueEntries( $devdetails, $data->{'nameref'}{'ifNick'} );
444     uniqueEntries( $devdetails, $data->{'nameref'}{'ifSubtreeName'} );
445
446     # If other discovery modules don't set nodeid reference, fall back to
447     # default interface reference
448     
449     
450     # Build interface parameters
451
452     my $nInterfaces = 0;
453    
454     foreach my $ifIndex ( keys %{$data->{'interfaces'}} )
455     {
456         my $interface = $data->{'interfaces'}{$ifIndex};
457
458         next if $interface->{'excluded'};
459         $nInterfaces++;
460
461         $interface->{'param'}{'searchable'} = 'yes';
462         
463         $interface->{'param'}{'interface-iana-type'} = $interface->{'ifType'};
464
465         $interface->{'param'}{'interface-name'} =
466             $interface->{$data->{'nameref'}{'ifReferenceName'}};
467
468         $interface->{'param'}{'node-display-name'} =
469             $interface->{$data->{'nameref'}{'ifReferenceName'}};
470
471         $interface->{'param'}{'interface-nick'} =
472             $interface->{$data->{'nameref'}{'ifNick'}};
473
474         if( not defined( $interface->{$data->{'nameref'}{'ifNodeidPrefix'}} ) )
475         {
476             $interface->{$data->{'nameref'}{'ifNodeidPrefix'}} =
477                 'if//%nodeid-device%//';
478         }
479         
480         if( not defined( $interface->{$data->{'nameref'}{'ifNodeid'}} ) )
481         {
482             $interface->{$data->{'nameref'}{'ifNodeid'}} =
483                 $interface->{$data->{'nameref'}{'ifReferenceName'}};
484         }
485
486         # A per-interface value which is used by leafs in IF-MIB templates
487         $interface->{'param'}{'nodeid-interface'} =
488             $interface->{$data->{'nameref'}{'ifNodeidPrefix'}} .
489             $interface->{$data->{'nameref'}{'ifNodeid'}};
490         
491         $interface->{'param'}{'nodeid'} = '%nodeid-interface%';        
492
493         if( defined $data->{'nameref'}{'ifComment'} and
494             not defined( $interface->{'param'}{'comment'} ) and
495             length( $interface->{$data->{'nameref'}{'ifComment'}} ) > 0 )
496         {
497             my $comment = $interface->{$data->{'nameref'}{'ifComment'}};
498             $interface->{'param'}{'comment'} = $comment;
499             $interface->{'param'}{'interface-comment'} = $comment;
500         }
501
502         # Order the interfaces by ifIndex, not by interface name
503         $interface->{'param'}{'precedence'} = sprintf('%d', 100000-$ifIndex);
504
505         $interface->{'param'}{'devdiscover-nodetype'} =
506             'RFC2863_IF_MIB::interface';
507     }
508
509     if( $nInterfaces == 0 )
510     {
511         return;
512     }
513
514     if( $devdetails->param('RFC2863_IF_MIB::noout') eq 'yes' )
515     {
516         return;
517     }
518
519     # explicitly excluded interfaces    
520     my %excludeName;
521     my $excludeNameList =
522         $devdetails->param('RFC2863_IF_MIB::exclude-interfaces');
523     my $nExplExcluded = 0;
524         
525     if( defined( $excludeNameList ) and length( $excludeNameList ) > 0 )
526     {
527         foreach my $name ( split( /\s*,\s*/, $excludeNameList ) )
528         {
529             $excludeName{$name} = 1;
530         }
531     }
532
533     # explicitly listed interfaces
534     my %onlyName;
535     my $onlyNamesList =
536         $devdetails->param('RFC2863_IF_MIB::only-interfaces');
537     my $onlyNamesDefined = 0;
538     if( defined( $onlyNamesList ) and length( $onlyNamesList ) > 0 )
539     {
540         $onlyNamesDefined = 1;
541         foreach my $name ( split( /\s*,\s*/, $onlyNamesList ) )
542         {
543             $onlyName{$name} = 1;
544         }
545     }
546
547     # Bandwidth usage
548     my %bandwidthLimits;
549     if( $devdetails->param('RFC2863_IF_MIB::bandwidth-usage') eq 'yes' )
550     {
551         my $limits = $devdetails->param('RFC2863_IF_MIB::bandwidth-limits');
552         foreach my $intfLimit ( split( /\s*;\s*/, $limits ) )
553         {
554             my( $intf, $limitIn, $limitOut ) = split( /\s*:\s*/, $intfLimit );
555             $bandwidthLimits{$intf}{'In'} = $limitIn;
556             $bandwidthLimits{$intf}{'Out'} = $limitOut;
557         }
558     }
559
560     # tokenset member interfaces of the form
561     # Format: tset:intf,intf; tokenset:intf,intf;
562     # Format for global parameter:
563     #     tset:host/intf,host/intf; tokenset:host/intf,host/intf;
564     my %tsetMember;
565     my %tsetMemberApplied;
566     my $tsetMembership =
567         $devdetails->param('RFC2863_IF_MIB::tokenset-members');
568     if( defined( $tsetMembership ) and length( $tsetMembership ) > 0 )
569     {
570         foreach my $memList ( split( /\s*;\s*/, $tsetMembership ) )
571         {
572             my ($tset, $list) = split( /\s*:\s*/, $memList );
573             foreach my $intfName ( split( /\s*,\s*/, $list ) )
574             {
575                 if( $intfName =~ /\// )
576                 {
577                     my( $host, $intf ) = split( '/', $intfName );
578                     if( $host eq $devdetails->param('snmp-host') )
579                     {
580                         $tsetMember{$intf}{$tset} = 1;
581                     }
582                 }
583                 else
584                 {
585                     $tsetMember{$intfName}{$tset} = 1;
586                 }
587             }
588         }
589     }
590        
591         
592     # External storage serviceid assignment
593     my $extSrv =
594         $devdetails->param('RFC2863_IF_MIB::external-serviceid');
595     my %extStorage;
596     my %extStorageTrees;
597     
598     if( defined( $extSrv ) and length( $extSrv ) > 0 )
599     {
600         foreach my $srvDef ( split( /\s*,\s*/, $extSrv ) )
601         {
602             my ( $serviceid, $intfName, $direction, $trees ) =
603                 split( /\s*:\s*/, $srvDef );
604
605             if( $intfName =~ /\// )
606             {
607                 my( $host, $intf ) = split( '/', $intfName );
608                 if( $host eq $devdetails->param('snmp-host') )
609                 {
610                     $intfName = $intf;
611                 }
612                 else
613                 {
614                     $intfName = undef;
615                 }
616             }
617
618             if( defined( $intfName ) and length( $intfName ) > 0 )
619             {
620                 if( defined( $trees ) )
621                 {
622                     # Trees are listed with '|' as separator,
623                     # whereas compiler expects commas
624                     
625                     $trees =~ s/\s*\|\s*/,/g;
626                 }
627                             
628                 if( $direction eq 'Both' )
629                 {
630                     $extStorage{$intfName}{'In'} = $serviceid . '_IN';
631                     $extStorageTrees{$serviceid . '_IN'} = $trees;
632                     
633                     $extStorage{$intfName}{'Out'} = $serviceid . '_OUT';
634                     $extStorageTrees{$serviceid . '_OUT'} = $trees;
635                 }
636                 else
637                 {
638                     $extStorage{$intfName}{$direction} = $serviceid;
639                     $extStorageTrees{$serviceid} = $trees;
640                 }
641             }
642         }
643     }
644
645     # Sums of several interfaces into single graphs (via CDef collector)
646     # RFC2863_IF_MIB::traffic-summaries: the list of sums to create;
647     # RFC2863_IF_MIB::traffic-XXX-path: the full path of the summary leaf
648     # RFC2863_IF_MIB::traffic-XXX-comment: description
649     # RFC2863_IF_MIB::traffic-XXX-interfaces: list of interfaces to add
650     #   format: "intf,intf" or "host/intf, host/intf"
651     my $trafficSums = $devdetails->param('RFC2863_IF_MIB::traffic-summaries');
652     my %trafficSummary;
653     if( defined( $trafficSums ) )
654     {
655         foreach my $summary ( split( /\s*,\s*/, $trafficSums ) )
656         {
657             $globalData->{'RFC2863_IF_MIB::summaryAttr'}{
658                 $summary}{'path'} =
659                     $devdetails->param
660                     ('RFC2863_IF_MIB::traffic-' . $summary . '-path');
661             $globalData->{'RFC2863_IF_MIB::summaryAttr'}{
662                 $summary}{'comment'} =
663                     $devdetails->param
664                     ('RFC2863_IF_MIB::traffic-' . $summary . '-comment');
665             
666             $globalData->{'RFC2863_IF_MIB::summaryAttr'}{
667                 $summary}{'data-dir'} = $devdetails->param('data-dir');
668                     
669             my $intfList = $devdetails->param
670                 ('RFC2863_IF_MIB::traffic-' . $summary . '-interfaces');
671
672             # get the intreface names for this host
673             foreach my $intfName ( split( /\s*,\s*/, $intfList ) )
674             {
675                 if( $intfName =~ /\// )
676                 {
677                     my( $host, $intf ) = split( '/', $intfName );
678                     if( $host eq $devdetails->param('snmp-host') )
679                     {
680                         $trafficSummary{$intf}{$summary} = 1;
681                     }
682                 }
683                 else
684                 {
685                     $trafficSummary{$intfName}{$summary} = 1;
686                 }
687             }
688         }
689     }
690     
691     # interface-level parameters to copy
692     my @intfCopyParams = ();
693     my $copyParams = $devdetails->param('RFC2863_IF_MIB::copy-params');
694     if( defined( $copyParams ) and length( $copyParams ) > 0 )
695     {
696         @intfCopyParams = split( /\s*,\s*/m, $copyParams );
697     }
698     
699     # Build configuration tree
700
701     my $subtreeName = $devdetails->param('RFC2863_IF_MIB::subtree-name');
702     if( length( $subtreeName ) == 0 )
703     {
704         $subtreeName = 'Interface_Counters';
705     }
706     my $subtreeParams = {};
707     my $subtreeComment = $devdetails->param('RFC2863_IF_MIB::subtree-comment');
708
709     if( length( $subtreeComment ) > 0 )
710     {
711         $subtreeParams->{'comment'} = $subtreeComment;
712     }
713
714     if( $devdetails->param('RFC2863_IF_MIB::bandwidth-usage') eq 'yes' )
715     {
716         $subtreeParams->{'overview-shortcuts'} = 'traffic,errors,bandwidth';
717     }
718     
719     my $countersNode =
720         $cb->addSubtree( $devNode, $subtreeName, $subtreeParams,
721                          ['RFC2863_IF_MIB::rfc2863-ifmib-subtree'] );
722     
723     foreach my $ifIndex ( sort {$a<=>$b} keys %{$data->{'interfaces'}} )
724     {
725         my $interface = $data->{'interfaces'}{$ifIndex};
726
727         if( $interface->{'selectorActions'}{'RemoveInterface'} )
728         {
729             $interface->{'excluded'} = 1;
730             Debug('Removing interface by selector action: ' .
731                   $interface->{$data->{'nameref'}{'ifReferenceName'}});
732         }
733
734         # Some vendor-specific modules may exclude some interfaces
735         next if $interface->{'excluded'};
736
737         # Create a subtree for the interface
738         my $subtreeName = $interface->{$data->{'nameref'}{'ifSubtreeName'}};
739
740         if( $onlyNamesDefined )
741         {
742             if( not $onlyName{$subtreeName} )
743             {
744                 $interface->{'excluded'} = 1;
745                 $nExplExcluded++;
746                 next;
747             }
748         }
749         
750         if( $excludeName{$subtreeName} )
751         {
752             $interface->{'excluded'} = 1;
753             $nExplExcluded++;
754             next;
755         }
756         elsif( length( $subtreeName ) == 0 )
757         {
758             Warn('Excluding an interface with empty name: ifIndex=' .
759                  $ifIndex);
760             next;
761         }
762
763         my @templates = ();
764
765         if( $interface->{'hasHCOctets'} )
766         {
767             push( @templates, 'RFC2863_IF_MIB::ifxtable-hcoctets' );
768         }
769         elsif( $interface->{'hasOctets'} )
770         {
771             push( @templates, 'RFC2863_IF_MIB::iftable-octets' );
772         }
773
774         if( $interface->{'hasOctets'} or $interface->{'hasHCOctets'} )
775         {
776             $interface->{'hasChild'}{'Bytes_In'} = 1;
777             $interface->{'hasChild'}{'Bytes_Out'} = 1;
778             $interface->{'hasChild'}{'InOut_bps'} = 1;            
779
780             foreach my $dir ( 'In', 'Out' )
781             {
782                 if( defined( $interface->{'selectorActions'}->
783                              {$dir . 'BytesMonitor'} ) )
784                 {
785                     $interface->{'childCustomizations'}->{
786                         'Bytes_' . $dir}->{'monitor'} =
787                             $interface->{'selectorActions'}->{
788                                 $dir . 'BytesMonitor'};
789                 }
790
791                 if( defined( $interface->{'selectorActions'}->
792                              {$dir . 'BytesParameters'} ) )
793                 {
794                     my @pairs =
795                         split('\s*;\s*',
796                               $interface->{'selectorActions'}{
797                                   $dir . 'BytesParameters'});
798                     
799                     foreach my $pair( @pairs )
800                     {
801                         my ($param, $val) = split('\s*=\s*', $pair);
802                         $interface->{'childCustomizations'}->{
803                             'Bytes_' . $dir}->{$param} = $val;
804                     }
805                 }
806             }
807
808             if( defined( $interface->{'selectorActions'}{'HoltWinters'} ) )
809             {
810                 push( @templates, '::holt-winters-defaults' );
811             }
812
813             if( defined( $interface->{'selectorActions'}{'NotifyPolicy'} ) )
814             {
815                 $interface->{'param'}{'notify-policy'} =
816                     $interface->{'selectorActions'}{'NotifyPolicy'};
817             }
818         }
819
820         if( not $interface->{'selectorActions'}{'NoPacketCounters'} )
821         {
822             my $has_someting = 0;
823             if( $interface->{'hasHCUcastPkts'} )
824             {
825                 push( @templates, 'RFC2863_IF_MIB::ifxtable-hcucast-packets' );
826                 $has_someting = 1;
827             }
828             elsif( $interface->{'hasUcastPkts'} )
829             {
830                 push( @templates, 'RFC2863_IF_MIB::iftable-ucast-packets' );
831                 $has_someting = 1;
832             }
833
834             if( $has_someting )
835             {
836                 $interface->{'hasChild'}{'Packets_In'} = 1;            
837                 $interface->{'hasChild'}{'Packets_Out'} = 1;            
838             }
839         }
840
841         if( not $interface->{'selectorActions'}{'NoDiscardCounters'} )
842         {
843             if( $interface->{'hasInDiscards'} )
844             {
845                 push( @templates, 'RFC2863_IF_MIB::iftable-discards-in' );
846                 $interface->{'hasChild'}{'Discards_In'} = 1;            
847
848                 if( defined
849                     ($interface->{'selectorActions'}->{'InDiscardsMonitor'}) )
850                 {
851                     $interface->{'childCustomizations'}->{
852                         'Discards_In'}->{'monitor'} =
853                             $interface->{'selectorActions'}{
854                                 'InDiscardsMonitor'};
855                 }
856             }
857             
858             if( $interface->{'hasOutDiscards'} )
859             {
860                 push( @templates, 'RFC2863_IF_MIB::iftable-discards-out' );
861                 $interface->{'hasChild'}{'Discards_Out'} = 1;
862                 
863                 if( defined( $interface->{'selectorActions'}->{
864                     'OutDiscardsMonitor'} ) )
865                 {
866                     $interface->{'childCustomizations'}->{
867                         'Discards_Out'}->{'monitor'} =
868                             $interface->{'selectorActions'}{
869                                 'OutDiscardsMonitor'};
870                 }
871             }
872         }
873         
874
875         if( not $interface->{'selectorActions'}{'NoErrorCounters'} )
876         {
877             if( $interface->{'hasInErrors'} )
878             {
879                 push( @templates, 'RFC2863_IF_MIB::iftable-errors-in' );
880                 $interface->{'hasChild'}{'Errors_In'} = 1;            
881
882                 if( defined( $interface->{'selectorActions'}->{
883                     'InErrorsMonitor'} ) )
884                 {
885                     $interface->{'childCustomizations'}->{
886                         'Errors_In'}->{'monitor'} =
887                             $interface->{'selectorActions'}{'InErrorsMonitor'};
888                 }
889             }
890
891             if( $interface->{'hasOutErrors'} )
892             {
893                 push( @templates, 'RFC2863_IF_MIB::iftable-errors-out' );
894                 $interface->{'hasChild'}{'Errors_Out'} = 1;            
895
896                 if( defined( $interface->{'selectorActions'}->{
897                     'OutErrorsMonitor'} ) )
898                 {
899                     $interface->{'childCustomizations'}->{
900                         'Errors_Out'}->{'monitor'} =
901                             $interface->{'selectorActions'}{
902                                 'OutErrorsMonitor'};
903                 }
904             }
905         }
906         
907         if( defined( $interface->{'selectorActions'}{'TokensetMember'} ) )
908         {
909             foreach my $tset
910                 ( split('\s*,\s*',
911                         $interface->{'selectorActions'}{'TokensetMember'}) )
912             {
913                 $tsetMember{$subtreeName}{$tset} = 1;
914             }
915         }
916         
917         if( defined( $interface->{'selectorActions'}{'Parameters'} ) )
918         {
919             my @pairs = split('\s*;\s*',
920                               $interface->{'selectorActions'}{'Parameters'});
921             foreach my $pair( @pairs )
922             {
923                 my ($param, $val) = split('\s*=\s*', $pair);
924                 $interface->{'param'}{$param} = $val;
925             }
926         }
927
928         if( $devdetails->param('RFC2863_IF_MIB::bandwidth-usage') eq 'yes' )
929         {
930             if( defined( $bandwidthLimits{$subtreeName} ) )
931             {
932                 $interface->{'param'}{'bandwidth-limit-in'} =
933                     $bandwidthLimits{$subtreeName}{'In'};
934                 $interface->{'param'}{'bandwidth-limit-out'} =
935                     $bandwidthLimits{$subtreeName}{'Out'};
936             }
937
938             # We accept that parameters may be added by some other ways
939
940             if( defined( $interface->{'param'}{'bandwidth-limit-in'} ) and
941                 defined( $interface->{'param'}{'bandwidth-limit-out'} ) )
942             {
943                 push( @templates,
944                       'RFC2863_IF_MIB::interface-bandwidth-usage' );
945             }
946         }
947
948         if( ref( $interface->{'templates'} ) )
949         {
950             push( @templates, @{$interface->{'templates'}} );
951         }
952
953         # Add vendor templates
954         push( @templates, @{$interface->{'vendor_templates'}} );
955         
956         # Add subtree only if there are template references
957
958         if( scalar( @templates ) > 0 )
959         {
960             # process interface-level parameters to copy
961
962             foreach my $param ( @intfCopyParams )
963             {
964                 my $val = $devdetails->param('RFC2863_IF_MIB::' .
965                                              $param . '::' . $subtreeName );
966                 if( defined( $val ) and length( $val ) > 0 )
967                 {
968                     $interface->{'param'}{$param} = $val;
969                 }
970             }
971
972             if( defined( $tsetMember{$subtreeName} ) )
973             {
974                 my $tsetList =
975                     join( ',', sort keys %{$tsetMember{$subtreeName}} );
976                 
977                 $interface->{'childCustomizations'}->{'InOut_bps'}->{
978                     'tokenset-member'} = $tsetList;
979                 $tsetMemberApplied{$subtreeName} = 1;
980             }
981
982             if( defined( $extStorage{$subtreeName} ) )
983             {
984                 foreach my $dir ( 'In', 'Out' )
985                 {
986                     if( defined( $extStorage{$subtreeName}{$dir} ) )
987                     {
988                         my $serviceid = $extStorage{$subtreeName}{$dir};
989
990                         my $params = {
991                             'storage-type'      => 'rrd,ext',
992                             'ext-service-id'    => $serviceid,
993                             'ext-service-units' => 'bytes' };
994                         
995                         if( defined( $extStorageTrees{$serviceid} )
996                             and length( $extStorageTrees{$serviceid} ) > 0 )
997                         {
998                             $params->{'ext-service-trees'} =
999                                 $extStorageTrees{$serviceid};
1000                         }
1001
1002                         foreach my $param ( keys %{$params} )
1003                         {
1004                             $interface->{'childCustomizations'}->{
1005                                 'Bytes_' . $dir}{$param} = $params->{$param};
1006                         }
1007                     }
1008                 }
1009             }
1010             
1011             my $intfNode =
1012                 $cb->addSubtree( $countersNode, $subtreeName,
1013                                  $interface->{'param'}, \@templates );
1014
1015             if( defined( $interface->{'childCustomizations'} ) )
1016             {
1017                 foreach my $childName
1018                     ( sort keys %{$interface->{'childCustomizations'}} )
1019                 {
1020                     if( $interface->{'hasChild'}{$childName} )
1021                     {
1022                         $cb->addLeaf
1023                             ( $intfNode, $childName,
1024                               $interface->{'childCustomizations'}->{
1025                                   $childName} );
1026                     }
1027                 }
1028             }
1029
1030             # If the interafce is a member of traffic summary
1031             if( defined( $trafficSummary{$subtreeName} ) )
1032             {
1033                 foreach my $summary ( keys %{$trafficSummary{$subtreeName}} )
1034                 {
1035                     addTrafficSummaryElement( $globalData,
1036                                               $summary, $intfNode );
1037                 }
1038             }
1039         }
1040     }
1041     
1042     if( $nExplExcluded > 0 )
1043     {
1044         Debug('Explicitly excluded ' . $nExplExcluded .
1045               ' RFC2863_IF_MIB interfaces');
1046     }
1047
1048     if( scalar( %tsetMember ) > 0 )
1049     {
1050         my @failedIntf;
1051         foreach my $intfName ( keys %tsetMember )
1052         {
1053             if( not $tsetMemberApplied{$intfName} )
1054             {
1055                 push( @failedIntf, $intfName );
1056             }
1057         }
1058
1059         if( scalar( @failedIntf ) > 0 )
1060         {
1061             Warn('The following interfaces were not added to tokensets, ' .
1062                  'probably because they do not exist or are explicitly ' .
1063                  'excluded: ' .
1064                  join(' ', sort @failedIntf));
1065         }
1066     }                 
1067     
1068     $cb->{'statistics'}{'interfaces'} += $nInterfaces;
1069     if( $cb->{'statistics'}{'max-interfaces-per-host'} < $nInterfaces )
1070     {
1071         $cb->{'statistics'}{'max-interfaces-per-host'} = $nInterfaces;
1072     }
1073 }
1074
1075
1076 sub addTrafficSummaryElement
1077 {
1078     my $globalData = shift;
1079     my $summary = shift;
1080     my $node = shift;
1081
1082     if( not defined( $globalData->{
1083         'RFC2863_IF_MIB::summaryMembers'}{$summary} ) )
1084     {
1085         $globalData->{'RFC2863_IF_MIB::summaryMembers'}{$summary} = [];
1086     }
1087
1088     push( @{$globalData->{'RFC2863_IF_MIB::summaryMembers'}{$summary}},
1089           $node );
1090 }
1091       
1092
1093 sub buildGlobalConfig
1094 {
1095     my $cb = shift;
1096     my $globalData = shift;
1097
1098     if( not defined( $globalData->{'RFC2863_IF_MIB::summaryMembers'} ) )
1099     {
1100         return;
1101     }
1102     
1103     foreach my $summary ( keys %{$globalData->{
1104         'RFC2863_IF_MIB::summaryMembers'}} )
1105     {
1106         next if scalar( @{$globalData->{
1107             'RFC2863_IF_MIB::summaryMembers'}{$summary}} ) == 0;
1108
1109         my $attr = $globalData->{'RFC2863_IF_MIB::summaryAttr'}{$summary};
1110         my $path = $attr->{'path'};
1111
1112         if( not defined( $path ) )
1113         {
1114             Error('Missing the path for traffic summary ' . $summary);
1115             next;
1116         }
1117
1118         Debug('Building summary: ' . $summary);
1119         
1120         # Chop the first and last slashes
1121         $path =~ s/^\///;
1122         $path =~ s/\/$//;
1123         
1124         # generate subtree path XML
1125         my $subtreeNode = undef;
1126         foreach my $subtreeName ( split( '/', $path ) )
1127         {
1128             $subtreeNode = $cb->addSubtree( $subtreeNode, $subtreeName, {
1129                 'comment'  => $attr->{'comment'},
1130                 'data-dir' => $attr->{'data-dir'} } );
1131         }
1132
1133         foreach my $dir ('In', 'Out')
1134         {
1135             my $rpn = '';
1136             foreach my $member ( @{$globalData->{
1137                 'RFC2863_IF_MIB::summaryMembers'}{$summary}} )
1138             {
1139                 my $memRef = '{' . $cb->getElementPath($member) .
1140                     'Bytes_' . $dir . '}';
1141                 if( length( $rpn ) == 0 )
1142                 {
1143                     $rpn = $memRef;
1144                 }
1145                 else
1146                 {
1147                     $rpn .= ',' . $memRef . ',+';
1148                 }
1149             }
1150             
1151             my $param = {
1152                 'rpn-expr' => $rpn,
1153                 'data-file' => 'summary_' . $summary . '.rrd',
1154                 'rrd-ds' => 'Bytes' . $dir };
1155           
1156             $cb->addLeaf( $subtreeNode, 'Bytes_' . $dir, $param,
1157                           ['::cdef-collector-defaults'] );
1158         }
1159     }
1160 }
1161
1162                        
1163
1164         
1165
1166 # $filterHash is a hash reference
1167 # Key is some unique symbolic name, does not mean anything
1168 # $filterHash->{$key}{'ifType'} is the number to match the interface type
1169 # $filterHash->{$key}{'ifDescr'} is the regexp to match the interface
1170 # description
1171
1172 sub addInterfaceFilter
1173 {
1174     my $devdetails = shift;
1175     my $filterHash = shift;
1176
1177     my $data = $devdetails->data();
1178
1179     if( not ref( $data->{'interfaceFilter'} ) )
1180     {
1181         $data->{'interfaceFilter'} = [];
1182     }
1183
1184     push( @{$data->{'interfaceFilter'}}, $filterHash );
1185 }
1186
1187
1188 sub uniqueEntries
1189 {
1190     my $devdetails = shift;
1191     my $nameref = shift;
1192
1193     my $data = $devdetails->data();
1194     my %count = ();
1195
1196     foreach my $ifIndex ( sort {$a<=>$b} keys %{$data->{'interfaces'}} )
1197     {
1198         my $interface = $data->{'interfaces'}{$ifIndex};
1199
1200         my $entry = $interface->{$nameref};
1201         if( length($entry) == 0 )
1202         {
1203             $entry = $interface->{$nameref} = '_';
1204         }
1205         if( int( $count{$entry} ) > 0 )
1206         {
1207             my $new_entry = sprintf('%s%d', $entry, int( $count{$entry} ) );
1208             $interface->{$nameref} = $new_entry;
1209             $count{$new_entry}++;
1210         }
1211         $count{$entry}++;
1212     }
1213 }
1214
1215 # For devices which require MAC address-to-interface mapping,
1216 # this function fills in the appropriate interface-macaddr parameters.
1217 # To get use of MAC mapping, set
1218 #     $data->{'param'}{'ifindex-map'} = '$IFIDX_MAC';
1219
1220
1221 sub retrieveMacAddresses
1222 {
1223     my $dd = shift;
1224     my $devdetails = shift;
1225
1226     my $data = $devdetails->data();
1227
1228     foreach my $ifIndex ( sort {$a<=>$b} keys %{$data->{'interfaces'}} )
1229     {
1230         my $interface = $data->{'interfaces'}{$ifIndex};
1231
1232         my $macaddr = $devdetails->snmpVar($dd->oiddef('ifPhysAddress') .
1233                                            '.' . $ifIndex);
1234
1235         if( defined( $macaddr ) and length( $macaddr ) > 0 )
1236         {
1237             $interface->{'MAC'} = $macaddr;
1238             $interface->{'param'}{'interface-macaddr'} = $macaddr;
1239         }
1240         else
1241         {
1242             Warn('Excluding interface without MAC address: ' .
1243                   $interface->{$data->{'nameref'}{'ifReferenceName'}});
1244             delete $data->{'interfaces'}{$ifIndex};
1245         }
1246     }
1247 }
1248
1249
1250 # For devices with fixed ifIndex mapping it populates interface-index parameter
1251
1252
1253 sub storeIfIndexParams
1254 {
1255     my $devdetails = shift;
1256
1257     my $data = $devdetails->data();
1258
1259     foreach my $ifIndex ( keys %{$data->{'interfaces'}} )
1260     {
1261         my $interface = $data->{'interfaces'}{$ifIndex};
1262         $interface->{'param'}{'interface-index'} = $ifIndex;        
1263     }
1264 }
1265
1266 #######################################
1267 # Selectors interface
1268 #
1269
1270 $Torrus::DevDiscover::selectorsRegistry{'RFC2863_IF_MIB'} = {
1271     'getObjects'      => \&getSelectorObjects,
1272     'getObjectName'   => \&getSelectorObjectName,
1273     'checkAttribute'  => \&checkSelectorAttribute,
1274     'applyAction'     => \&applySelectorAction,
1275 };
1276
1277
1278 ## Objects are interface indexes
1279
1280 sub getSelectorObjects
1281 {
1282     my $devdetails = shift;
1283     my $objType = shift;
1284     return sort {$a<=>$b} keys ( %{$devdetails->data()->{'interfaces'}} );
1285 }
1286
1287
1288 sub checkSelectorAttribute
1289 {
1290     my $devdetails = shift;
1291     my $object = shift;
1292     my $objType = shift;
1293     my $attr = shift;
1294     my $checkval = shift;
1295
1296     my $data = $devdetails->data();
1297     my $interface = $data->{'interfaces'}{$object};
1298     
1299     if( $attr =~ /^ifSubtreeName\d*$/ )
1300     {
1301         my $value = $interface->{$data->{'nameref'}{'ifSubtreeName'}};
1302         my $match = 0;
1303         foreach my $chkexpr ( split( /\s+/, $checkval ) )
1304         {
1305             if( $value =~ $chkexpr )
1306             {
1307                 $match = 1;
1308                 last;
1309             }
1310         }
1311         return $match;        
1312     }
1313     else
1314     {
1315         my $value;
1316         my $operator = '=~';
1317         if( $attr eq 'ifComment' )
1318         {
1319             $value = $interface->{$data->{'nameref'}{'ifComment'}};
1320         }
1321         elsif( $attr eq 'ifType' )
1322         {
1323             $value = $interface->{'ifType'};
1324             $operator = '==';
1325         }
1326         else
1327         {
1328             Error('Unknown RFC2863_IF_MIB selector attribute: ' . $attr);
1329             $value = '';
1330         }
1331
1332         return eval( '$value' . ' ' . $operator . '$checkval' ) ? 1:0;
1333     }
1334 }
1335
1336
1337 sub getSelectorObjectName
1338 {
1339     my $devdetails = shift;
1340     my $object = shift;
1341     my $objType = shift;
1342     
1343     my $data = $devdetails->data();
1344     my $interface = $data->{'interfaces'}{$object};
1345     return $interface->{$data->{'nameref'}{'ifSubtreeName'}};
1346 }
1347
1348
1349 # Other discovery modules can add their interface actions here
1350 our %knownSelectorActions =
1351     ( 'InBytesMonitor'    => 'RFC2863_IF_MIB',
1352       'OutBytesMonitor'   => 'RFC2863_IF_MIB',
1353       'InDiscardsMonitor'  => 'RFC2863_IF_MIB',
1354       'OutDiscardsMonitor' => 'RFC2863_IF_MIB',
1355       'InErrorsMonitor'   => 'RFC2863_IF_MIB',
1356       'OutErrorsMonitor'  => 'RFC2863_IF_MIB',
1357       'NotifyPolicy'      => 'RFC2863_IF_MIB',
1358       'HoltWinters'       => 'RFC2863_IF_MIB',
1359       'NoPacketCounters'  => 'RFC2863_IF_MIB',
1360       'NoDiscardCounters' => 'RFC2863_IF_MIB',
1361       'NoErrorCounters'   => 'RFC2863_IF_MIB',
1362       'RemoveInterface'   => 'RFC2863_IF_MIB',
1363       'TokensetMember'    => 'RFC2863_IF_MIB',
1364       'Parameters'        => 'RFC2863_IF_MIB',
1365       'InBytesParameters' => 'RFC2863_IF_MIB',
1366       'OutBytesParameters' => 'RFC2863_IF_MIB',);
1367
1368                             
1369 sub applySelectorAction
1370 {
1371     my $devdetails = shift;
1372     my $object = shift;
1373     my $objType = shift;
1374     my $action = shift;
1375     my $arg = shift;
1376
1377     my $data = $devdetails->data();
1378     my $interface = $data->{'interfaces'}{$object};
1379
1380     if( defined( $knownSelectorActions{$action} ) )
1381     {
1382         if( not $devdetails->isDevType( $knownSelectorActions{$action} ) )
1383         {
1384             Error('Action ' . $action . ' is applied to a device that is ' .
1385                   'not of type ' . $knownSelectorActions{$action} .
1386                   ': ' . $devdetails->param('system-id'));
1387         }
1388         $interface->{'selectorActions'}{$action} = $arg;
1389     }
1390     else
1391     {
1392         Error('Unknown RFC2863_IF_MIB selector action: ' . $action);
1393     }
1394 }
1395    
1396
1397 1;
1398
1399
1400 # Local Variables:
1401 # mode: perl
1402 # indent-tabs-mode: nil
1403 # perl-indent-level: 4
1404 # End: