File: Snmp.pm

package info (click to toggle)
ocsinventory-agent 2%3A2.0.5-1.2
  • links: PTS
  • area: main
  • in suites: stretch
  • size: 4,120 kB
  • ctags: 899
  • sloc: perl: 20,687; sh: 576; objc: 468; ansic: 333; makefile: 55
file content (381 lines) | stat: -rw-r--r-- 12,311 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
###############################################################################
## OCSINVENTORY-NG
## Copyleft Guillaume PROTET 2010
## Web : http://www.ocsinventory-ng.org
##
## This code is open source and may be copied and modified as long as the source
## code is always made freely available.
## Please refer to the General Public Licence http://www.gnu.org/ or Licence.txt
################################################################################

package Ocsinventory::Agent::Modules::Snmp;

use strict;
no strict 'refs';
use warnings;

use XML::Simple;
use Digest::MD5;

sub new {
   my $name="snmp";   #Set the name of your module here

   my (undef,$context) = @_;
   my $self = {};


   #Create a special logger for the module
   $self->{logger} = new Ocsinventory::Logger ({
            config => $context->{config}
   });
   $self->{logger}->{header}="[$name]";

   $self->{common} = $context->{common};

   $self->{context}=$context;

   $self->{structure}= {
			name => $name,
			start_handler => $name."_start_handler", 
			prolog_writer => undef,      
			prolog_reader => $name."_prolog_reader", 
			inventory_handler => undef,
			end_handler => $name."_end_handler",
   };

   $self->{number_scan}=0;
   $self->{snmp_oid_run}=$name."_oid_run";
   $self->{func_oid}={};
   $self->{snmp_dir}=[];

   my $spec_dir_snmp="Ocsinventory/Agent/Modules/Snmp/";
   $self->{spec_dir_snmp}=$spec_dir_snmp;
   $self->{spec_module_snmp}="Ocsinventory::Agent::Modules::Snmp::";

   # We are going to search where is the directory Ocsinventory/Modules/snmp
   foreach my $dir ( @INC ) {
      my $res_dir=$dir."/".$spec_dir_snmp;
      if ( -d $res_dir ) {
        push(@{$self->{snmp_dir}},$res_dir);
      }
   }

   #We create a xml for the snmp inventory that we will be sent to server
   $self->{inventory}={};

   bless $self;
}


sub snmp_start_handler { 	
   my $self = shift;
   my $logger = $self->{logger};
   my $common = $self->{context}->{common};
   my $config = $self->{context}->{config};
   
   $logger->debug("Calling snmp_start_handler");

   #Disabling module if local mode
   if ($config->{stdout} || $config->{local}) {
     $self->{disabled} = 1;
     $logger->info("Agent is running in local mode...disabling module");
   }

   #If we cannot load prerequisite, we disable the module 
   unless ($common->can_load('Net::SNMP')) { 
     $self->{disabled} = 1;
     $logger->error("Net::SNMP perl module is missing !!");
     $logger->error("Humm my prerequisites are not OK...disabling module :( :(");
   }
}


sub snmp_prolog_reader {
   my ($self, $prolog) = @_;
   my $logger = $self->{logger};
   my $network = $self->{context}->{network};

   my $option;

   $logger->debug("Calling snmp_prolog_reader");
   
   $prolog	= XML::Simple::XMLin( $prolog, ForceArray => ['OPTION', 'PARAM']);

   for $option (@{$prolog->{OPTION}}){
      if( $option->{NAME} =~/snmp/i){
         $self->{doscans} = 1 ;
         for ( @{ $option->{PARAM} } ) {

            if($_->{'TYPE'} eq 'DEVICE'){
              #Adding the IP in the devices array
              push @{$self->{netdevices}},{
                IPADDR => $_->{IPADDR},
                MACADDR => $_->{MACADDR},
              };
            }

            if($_->{'TYPE'} eq 'COMMUNITY'){
              #Adding the community in the communities array
              push @{$self->{communities}},{
                VERSION=>$_->{VERSION},
                NAME=>$_->{NAME},
                USERNAME=>$_->{USERNAME},
                AUTHKEY=>$_->{AUTHKEY},
                AUTHPASSWD=>$_->{AUTHPASSWD},
              };
            }
         }
      }
   }
}


sub snmp_end_handler {
   my $self = shift;
   my $logger = $self->{logger};
   my $common = $self->{context}->{common};
   my $network = $self->{context}->{network};

   $logger->debug("Calling snmp_end_handler");

   #If no order form server
   return unless $self->{doscans};

   #Flushing xmltags if it has not been done
   $common->flushXMLTags();

   #We get the config
   my $config = $self->{context}->{config};
   
   
   my $ip=$self->{netdevices};
   my $communities=$self->{communities};
   if ( ! defined ($communities ) ) {
      $logger->debug("We have no Community from server, we use default public community");
      $communities=[{VERSION=>"2",NAME=>"public"}];
   }

   my ($name,$comm,$error,$system_oid);

   # sysName.0
   my $snmp_sysname="1.3.6.1.2.1.1.5.0";
   # sysDescr.0
   my $snmp_sysdescr="1.3.6.1.2.1.1.1.0";
   # sysLocation.0
   my $snmp_syslocation="1.3.6.1.2.1.1.6.0";
   # sysUpTime.0
   my $snmp_sysuptime="1.3.6.1.2.1.1.3.0"; 
   # sysObjectId.0
   my $snmp_sysobjectid="1.3.6.1.2.1.1.2.0";
   # syscontact.0
   my $snmp_syscontact="1.3.6.1.2.1.1.4.0";



   # Initalising the XML properties 
   my $snmp_inventory = $self->{inventory};
   $snmp_inventory->{xmlroot}->{QUERY} = ['SNMP'];
   $snmp_inventory->{xmlroot}->{DEVICEID} = [$self->{context}->{config}->{deviceid}];

   # Begin scanning ip tables 
   foreach my $device ( @$ip ) {
      my $session;
      my $devicedata = $common->{xmltags};     #To fill the xml informations for this device

      $logger->debug("Scanning $device->{IPADDR} device");	
      # Search for the good snmp community in the table community
      LIST_SNMP: foreach $comm ( @$communities ) {

          # Test if we use SNMP v3
          if ( $comm->{VERSION} eq "3"  ) {
	    ($session, $error) = Net::SNMP->session(
                -retries     => 1 ,
                -timeout     => 3,
                -version     => 'snmpv'.$comm->{VERSION},
                -hostname    => $device->{IPADDR},
		# -community   => $comm->{NAME},
                -translate   => [-nosuchinstance => 0, -nosuchobject => 0],
	        -username      => $comm->{USER},
                -authpassword  => $comm->{AUTHPASSWD},
                -authprotocol  => $comm->{AUTHPROTO},
                -privpassword  => $comm->{PRIVPASSWD},
                -privprotocol  => $comm->{PRIVPROTO},
             );
          } else {
            # We have an older version v2c ou v1
	    ($session, $error) = Net::SNMP->session(
                -retries     => 1 ,
                -timeout     => 3,
                -version     => 'snmpv'.$comm->{VERSION},
                -hostname    => $device->{IPADDR},
		          -community   => $comm->{NAME},
                -translate   => [-nosuchinstance => 0, -nosuchobject => 0],
             );
          };
          unless (defined($session)) {
             $logger->error("Snmp ERROR: $error");
          } else {
	          $self->{snmp_session}=$session;

   	     $self->{snmp_community}=$comm->{NAME}; #For a use in constructor module (Cisco)
             $self->{snmp_version}=$comm->{VERSION};

             $name=$session->get_request( -varbindlist => [$snmp_sysname] );
             last LIST_SNMP if ( defined $name);
             $session->close;
	          $self->{snmp_session}=undef;
          }
      }
		
      if ( defined $self->{snmp_session} ) { 
        # We have found the good Community, we can scan this equipment
        my ($constr_oid,$full_oid,$device_name,$description,$location,$contact,$uptime,$domain,$macaddr);

        # We indicate that we scan a new equipment
        $self->{number_scan}++;

        my $result;


        $result=$session->get_request( -varbindlist => [$snmp_sysname]);
        $device_name=$result->{$snmp_sysname}; 

        $result=$session->get_request(-varbindlist => [$snmp_sysobjectid]);
        $full_oid=$result->{$snmp_sysobjectid}; 

        $result=$session->get_request(-varbindlist => [$snmp_sysdescr]);
        $description=$result->{$snmp_sysdescr};

        $result=$session->get_request(-varbindlist => [$snmp_syslocation]);
        $location=$result->{$snmp_syslocation};
        
        $result=$session->get_request(-varbindlist => [$snmp_sysuptime]);
        $uptime=$result->{$snmp_sysuptime};

        $result=$session->get_request(-varbindlist => [$snmp_syscontact]);
        $contact=$result->{$snmp_syscontact};

        if ( $full_oid  =~ /1\.3\.6\.1\.4\.1\.(\d+)/ ) {
            $system_oid=$1;
        }

        # We run the special treatments for the OID vendor 
        if ( $self->{snmp_oid_run}($self,$system_oid) == 1 ) {
           # We have no vendor oid for this equipment
           # we use default.pm
           $self->{snmp_oid_run}($self,"Default");
        }

        $session->close;
	$self->{snmp_session}=undef;

        $macaddr = $device->{MACADDR};

        #Create SnmpDeviceID
        my $md5 = Digest::MD5->new;
        $md5->add($macaddr, $system_oid);
        my $snmpdeviceid = $md5->hexdigest;

        #Adding standard informations
        $common->setSnmpCommons({ 
          IPADDR => $device->{IPADDR},
          MACADDR => $macaddr,
          SNMPDEVICEID => $snmpdeviceid,
          NAME => $device_name,
          DESCRIPTION => $description,
          CONTACT => $contact,
          LOCATION => $location,
          UPTIME => $uptime,
          WORKGROUP => $domain,
        });

        #Add all the informations in the xml for this device
        push @{$snmp_inventory->{xmlroot}->{CONTENT}->{DEVICE}},$devicedata;
      }

      #We clear the xml data for this device 
      $common->flushXMLTags(); 
   }
  
  $logger->info("No more SNMP device to scan"); 

  #Formatting the XML and sendig it to the server
  my $content = XMLout( $snmp_inventory->{xmlroot},  RootName => 'REQUEST' , XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>', SuppressEmpty => undef );

  #Cleaning XML to delete unprintable characters
  my $clean_content = $common->cleanXml($content);

  $network->sendXML({message => $clean_content});

  $logger->debug("End snmp_end_handler :)");
}


sub snmp_oid_run {
    my ($self,$system_oid)=@_;

    my $logger=$self->{logger};
    my $session=$self->{snmp_session};
    my $spec_module_snmp=$self->{spec_module_snmp};

   unless ( defined ( $self->{func_oid}{$system_oid} )) {
      my $spec_dir_snmp=$self->{spec_dir_snmp};

      # We init the default value
      $self->{func_oid}{$system_oid}={};
      $self->{func_oid}{$system_oid}{active}=0;
      $self->{func_oid}{$system_oid}{oid_value}="1.3.6.1.2.1.1.5.0";
      $self->{func_oid}{$system_oid}{oid_name}="Undefined";


      # Can we find it in the snmp directory
      foreach my $dir ( @{$self->{snmp_dir}} ) {
          if ( -r $dir.$system_oid.".pm" ) {
             # We find the module
             my $module_found=$spec_module_snmp.$system_oid;
             eval "use $module_found";
             if ($@) {
                $logger->debug ("Failed to load $module_found: $@");
             } else {
                # We have execute it. We can get the function pointer on snmp_run
                my $package=$module_found."::";
                $self->{func_oid}{$system_oid}{snmp_run}=$package->{'snmp_run'};
                if ( defined ( $package->{'snmp_info'} ) ) {
                   my $return_info=&{$package->{'snmp_info'}};
                   if ( defined $return_info->{oid_value} ) {
                      $self->{func_oid}{$system_oid}{oid_value}=$return_info->{oid_value};
                   }
                   if ( defined $return_info->{oid_name} ) {
                      $self->{func_oid}{$system_oid}{oid_name}=$return_info->{oid_name};
                   }
                }

                $self->{func_oid}{$system_oid}{active}=1;
            	 $self->{func_oid}{$system_oid}{last_exec}=0;
             }
          }
      }
   }

   if ( $self->{func_oid}{$system_oid}{active} == 1 && $self->{func_oid}{$system_oid}{last_exec} < $self->{number_scan} )
   { # we test that this function as never been executed for this equipment
      # We test first that this OID exist for this equipment
      my $oid_scan=$self->{func_oid}{$system_oid}{oid_value};
      my $result=$session->get_request(-varbindlist => [ $oid_scan ] );

      $self->{func_oid}{$system_oid}{last_exec}=$self->{number_scan};
      if ( length ($result->{$oid_scan}) != 0 ) {
         # This OID exist, we can execute it
         #$logger->debug("Launching $system_oid\n");
         &{$self->{func_oid}{$system_oid}{snmp_run}}($session,$self);
      }
   # We indicate that this equipment is the last scanned
   } else {
      return 1;
   }
   return 0;

}


1;