This annoyed me, as well, so I wrote the following SNMP agent extension to implement the hrSWInstalled MIB for Debian and other dpkg based Linux distributions.
Yeah, this code is a bit messy, but it works. You need to use the "perl do" statement in your snmpd.conf, for example, if you saved this as /etc/snmp/hrswinstalled.pl, you would use: perl do "/etc/snmp/hrswinstalled.pl"; That should be all you need. Code: #!/usr/bin/perl #This is a rather quick (?) and dirty (!) script to implement the hrSWInstalled #MIB on Debian and other dpkg based Linux distributions. # #If you have improvements, please send to [email protected] #Configuration #maximum allowed cache age (in seconds) #the agent will check the mtime on $statusfile if it hasn't checked in this long $maxCacheAge = 10; #location of dpkg files my $statusfile = "/var/lib/dpkg/status"; my $infodir = "/var/lib/dpkg/info"; # 0 - quiet, 1 - basic debugging, 2 - trace debugging $debug = 0; my $regat = '1.3.6.1.2.1.25.6'; my $hrSWInstalledLastChange = $regat . ".1"; my $hrSWInstalledLastUpdateTime = $regat . ".2"; my $hrSWInstalledEntry = $regat . ".3.1"; my $hrSWInstalledIndex = $hrSWInstalledEntry . ".1"; my $hrSWInstalledName = $hrSWInstalledEntry . ".2"; my $hrSWInstalledID = $hrSWInstalledEntry . ".3"; my $hrSWInstalledType = $hrSWInstalledEntry . ".4"; my $hrSWInstalledDate = $hrSWInstalledEntry . ".5"; use Net::SNMP qw(oid_lex_sort); use YAML; use IO::File; use Parse::Debian::Packages; use Data::Dumper; use NetSNMP::OID (':all'); use NetSNMP::agent (':all'); use NetSNMP::ASN (':all'); use SNMP; my $inittime = time(); my $lastCacheCheck; my $cacheAge = 0; my %oidtable; my @oidkeys; my %nextOIDMap; BEGIN { print STDERR "loading Debian hrSWInstalled MIB extension \n"; } populateCaches(); # if we're not embedded, this will get auto-set below to 1 $subagent = 0; # where we are going to hook onto my $regoid = new NetSNMP::OID($regat); print STDERR "registering at ", $regoid, "\n" if ($debug); # If we're not running embedded within the agent, then try to start # our own subagent instead. if ( !$agent ) { $agent = new NetSNMP::agent( 'Name' => 'hrswinstalled', 'AgentX' => 1 ); # make us a subagent $subagent = 1; print STDERR "started us as a subagent ($agent)\n"; } # we register ourselves with the master agent we're embedded in. The # global $agent variable is how we do this: $agent->register( 'myname', $regoid, \&hrSWInstalledHandler ); if ($subagent) { # We need to perform a loop here waiting for snmp requests. We # aren't doing anything else here, but we could. $SIG{'INT'} = \&shutDown; $SIG{'QUIT'} = \&shutDown; $running = 1; while ($running) { $agent->agent_check_and_process(1); # 1 = block print STDERR "main loop\n" if ( $debug > 2 ); } $agent->shutdown(); } #the actual SNMP handler sub hrSWInstalledHandler { my ( $handler, $registration_info, $request_info, $requests ) = @_; my $request; #check package cache time populateCaches() if statusCacheUpdated($statusfile); print STDERR "processing a request of type " . $request_info->getMode() . "\n" if $debug > 1; for ( $request = $requests ; $request ; $request = $request->next() ) { my $oid = $request->getOID(); print STDERR " processing request of $oid\n" if $debug > 1; if ( $request_info->getMode() == MODE_GET ) { my $numericOid = SNMP::translateObj($oid); $numericOid =~ s/^\.//; $request->setOID($numericOid); $request->setValue( $oidtable{$numericOid}{type}, $oidtable{$numericOid}{val} ); } elsif ( $request_info->getMode() == MODE_GETNEXT ) { print STDERR "requested oid $oid\n" if $debug > 1; for my $oidentry ( $nextOIDMap{$oid} ? ( $nextOIDMap{$oid}, @oidkeys ) : @oidkeys ) { if ( $oid < $oidtable{$oidentry}{oid} ) { print STDERR "returned $oidentry" if $debug > 1; $request->setOID($oidentry); $request->setValue( $oidtable{$oidentry}{type}, $oidtable{$oidentry}{val} ); last; } } } } print STDERR " finished processing\n" if $debug > 1; } #optimised oid map sub nextOIDMap { my ( $oidtable, $oidkeys ) = @_; my %nextmap; my %oididx; my $idx = 0; print STDERR "Generating OID cache...\n" if $debug; #generate hash mapping OIDs in @{$oidkeys} array to the index of that key #within the array, for performance reasons for my $oidkey ( @{$oidkeys} ) { $oididx{$oidkey} = $idx; } for my $oid ( keys %{$oidtable} ) { my $index = $oididx{$oid}; $nextmap{ $oidtable{$oid}{oid} } = $oidkeys->[ $index + 1 ]; } print STDERR "\t...done.\n" if $debug; return %nextmap; } #check if package status has changed sub statusCacheUpdated { my ($statusfile) = @_; #allow for max cache age return 0 if time() <= $lastCacheCheck + $maxCacheAge; $lastCacheCheck = time(); print STDERR "dpkg cache expired\n" if $debug && ( stat($statusfile) )[9] > $cacheAge; return ( stat($statusfile) )[9] > $cacheAge ? 1 : 0; } #enumerate package status sub enumPkgs { my ( $statusfile, $infodir ) = @_; my $index = 1; my %packages; my %oidtable; print STDERR "Enumerating installed packages...\n" if $debug; #remember our cache age $cacheAge = time(); my $fh = IO::File->new($statusfile); my $parser = Parse::Debian::Packages->new($fh); while ( my %package = $parser->next ) { next unless $package{Status} =~ /(?<!not-)installed/; $packages{ $package{Package} } = \%package; } close $fh; for my $name ( sort keys %packages ) { my $oid; #hrSWInstalledIndex $oid = $hrSWInstalledIndex . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledIndex . "." . "$index" ); $oidtable{$oid}{type} = ASN_INTEGER; $oidtable{$oid}{val} = $index; #hrSWInstalledName $oid = $hrSWInstalledName . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledName . "." . "$index" ); $oidtable{$oid}{type} = ASN_OCTET_STR; $oidtable{$oid}{val} = "$name $packages{$name}{Version}/$packages{$name}{Architecture} (" . ( $packages{$name}{Description} || "No description" ) . ")"; #hrSWInstalledDate $oid = $hrSWInstalledDate . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledDate . "." . "$index" ); $oidtable{$oid}{type} = ASN_OCTET_STR; my $mtime = ( stat( "$infodir" . "/" . $name . ".list" ) )[9]; $oidtable{$oid}{val} = snmpDateStamp($mtime); #hrSWInstalledID $oid = $hrSWInstalledID . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledID . "." . "$index" ); $oidtable{$oid}{type} = ASN_OBJECT_ID; $oidtable{$oid}{val} = "0.0"; #hrSWInstalledType $oid = $hrSWInstalledType . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledType . "." . "$index" ); $oidtable{$oid}{type} = ASN_INTEGER; if ( $packages{$name}{Priority} eq "required" || $packages{$name}{Section} eq "base" ) { #OS package $oidtable{$oid}{val} = 2; } else { #application $oidtable{$oid}{val} = 4; } #increment index $index++; } #hrSWInstalledLastChange $oid = $hrSWInstalledLastChange . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledLastChange . "." . "$index" ); $oidtable{$oid}{type} = ASN_TIMETICKS; my $mtime = ( stat($statusfile) )[9]; $oidtable{$oid}{val} = ( time() - $inittime ) * 100; #hrSWInstalledLastUpdateTime $oid = $hrSWInstalledLastUpdateTime . "." . $index; $oidtable{$oid}{oid} = new NetSNMP::OID( $hrSWInstalledLastUpdateTime . "." . "$index" ); $oidtable{$oid}{type} = ASN_TIMETICKS; $oidtable{$oid}{val} = ( time() - $inittime ) * 100; print "\tdone. (" . scalar( keys %packages ) . " packages)\n" if $debug; return %oidtable; } #convert unix timestamp to snmp TimeAndDate sub snmpDateStamp { my ($ts) = @_; my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = gmtime($ts); return pack 'n C6 a C2', $year + 1900, $mon + 1, $mday, $hour, $min, $sec, 0, '-', 0, 0; } sub populateCaches { %oidtable = enumPkgs( $statusfile, $infodir ); @oidkeys = oid_lex_sort( keys %oidtable ); %nextOIDMap = nextOIDMap( \%oidtable, \...@oidkeys ); } sub shutDown { $running = 0; print STDERR "shutting down\n" if ($debug); } [/code] -------------------- m2f -------------------- Read this topic online here: http://forums.zenoss.com/viewtopic.php?p=30257#30257 -------------------- m2f -------------------- _______________________________________________ zenoss-users mailing list [email protected] http://lists.zenoss.org/mailman/listinfo/zenoss-users
