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

Reply via email to