Re: [CODE] Graphing individual CPUs on a Linux SMP (multiprocessor) machine

Christian Hammers <[email protected]>
Newsgroups gmane.network.cricket.user
Message-ID <[email protected]>
On Wed, Aug 23, 2006 at 06:13:12PM +0200, Claus Koch wrote:
> will you be so kind and publish your plugin here? I also need this kind
> of data and would be happy to help with testing.

Of course, I just wanted to test it for a day. The script is a shrinked
down version of another plugin and includes hardcoded values but at least
I commented it a bit so that it may be helpful to others and maybe put
on the contrib page.

bye,

-christian-



#!/usr/bin/perl -W
#
# snmpd plugin providing usage statistics of individual CPUs on Linux SMP.
#
# This plugin provides the output of "mpstat -P ALL 1 1". The data is 
# refreshed every minute.
#
# To active this plugin put this line into /etc/snmp/snmpd.conf:
#   pass_persist .1.3.6.1.4.1.4242.2 /usr/local/maint/mpstat-snmp-plugin.pl
#
# Debugging is possible by setting the global $DEBUG=1 and watch syslog.
#
# MIB Tree:
#   .1.3.6.1.4.1.4242.2
#                      .1 %user
#                      .2 %nice
#                      .3 %system
# 
# Usage:
#   The following command tells to how much the fifth cpu (".4" as we start 
#   at 0) was busy with system calls (".3"):
#       snmpwalk localhost .1.3.6.1.4.1.4242.2.3.4
# 
# Changelog:
#   2006-08-24, Christian Hammers <[email protected]> 
#
use strict; 
use Data::Dumper;
use POSIX qw(strftime);
use Unix::Syslog qw(:macros :subs);
$|=1;

############################################################################
################################ globals ###################################
############################################################################




my $NUM_CPUS = 16;  # FIXME: WATCH THIS! CHANGE THIS! AUTOMATE THIS AND SEND ME PATCH! :)




my $OID_BASE_TABLE = ".1.3.6.1.4.1.4242.2";

# This activates syslogging.
my $DEBUG = 0;

# Two dimensional array for the mpstat output.
my @mpstat;

# Used to check if a new mpstat call is necessary.
my $last_update = 0;

############################################################################
########################## misc functions ##################################
############################################################################

sub debug($) {
  return if not $DEBUG;
  syslog(LOG_DEBUG, "#42#DEBUG# %s", $_[0]);
}

sub output($) {
    debug("out=|".$_[0]."|");
    print($_[0]);
}

sub input() {
    my($s) = scalar(<STDIN>);
    debug("in=|$s|");
    chomp($s);
    return $s;
}

############################################################################
############################ main functions ################################
############################################################################

## Get all packages and their info.
sub read_mpstat() {
    # Update data every 60s, quit this function else.
    my $t = time();
    return if ($t < $last_update + 60);
    $last_update = $t;
    
    # Save the output of "mpstat" in @mpstat;
    #              CPU   %user   %nice %system %iowait    %irq   %soft   %idle    intr/s
    # 10:05:45       0    2,98    0,00    1,30    5,28    0,06    0,19   90,19   2223,00
    # 10:05:45       1   19,00    0,00   13,00    0,00    0,00    4,00   64,00   1638,00
    # ...
    @mpstat = ();
    if ( ! open(P, "LC_ALL=C mpstat -P ALL 1 1 | tail -n +5 | head -n $NUM_CPUS | awk '{ print \$3 \" \" \$4 \" \" \$5 }' | ")) {
        syslog(LOG_ERR, "close: $!/$?");
        die("open: $!");
    }
    my($line);
    while (defined($line = <P>)) {
        chomp($line);
        my(@what) = map { int($_) } split(/ /, $line);
        push(@mpstat, \@what);
    }
    if ( ! close(P)) {
        syslog(LOG_ERR, "close: $!/$?");
        die("close: $!");
    }
    debug("read_mpstat: read $#mpstat entries");
}

## Go through the commands sent on STDIN.
sub main_loop() {
    my($line, $cmd, $oid);

    LOOP: while (defined($line = input())) {

        # snmpd checks if this program is alive.
        if ($line =~ /^PING$/) {
            output("PONG\n");
            next LOOP;
        } elsif ($line !~ /^(get|getnext)$/) {
            syslog(LOG_ERR, "Invalid command line |$line|");
            next LOOP;
        }
        $cmd = $1;

        # Get next line from STDIN, it should be the OID number.
        $oid = input();

        # Update data if necessary.
        read_mpstat();

        # Currently we have only one base oid to work on...
        if ($oid =~ /^$OID_BASE_TABLE.(\d+)(\.(\d+))?$/) {
            my($what) = $1;
            my($i) = $3;

            # first OID suffix out of range
            if (not defined $what or $what < 0 or $what > 2) {
                output("NONE\n");
                next LOOP;
            }

            # snmpwalk starts on parent OID
            if (not defined $i) {
                $i = 0;
            }

            if ($cmd eq 'get') {
                if ($i <= 15) {
                    output("$OID_BASE_TABLE.$what.$i\ninteger\n".$mpstat[$i][$what]."\n");
                } else {
                    output("NONE\n");
                }
            } elsif ($cmd eq 'getnext') {
                # Remember that getnext asks for the successor of the previously given OID.
                if ($i <= 14) {
                    $i++;
                    output("$OID_BASE_TABLE.$what.$i\ninteger\n".$mpstat[$i][$what]."\n");
                } else {
                    output("NONE\n");
                }
            }
            
        # OID unknown.
        } else {
            syslog(LOG_WARNING, "Unsupported OID $cmd |$oid|");
            output("NONE\n");
            next LOOP;
        }
    }
}

############################################################################
################################# main() ###################################
############################################################################

openlog("mpstat-snmp-plugin", 0, LOG_DAEMON);
debug("starting");

# argument and config file parsing
for (my $i=0; $ARGV[$i]; $i++) {
    if ($ARGV[$i] eq '--help') {
        print("Providing mpstat output as snmpd plugin.\n");
        print("Usage: $0 [--help|--debug]\n");
        exit(1);
    } elsif ($ARGV[$i] eq '--debug') {
        $DEBUG = 1;
    }
}

main_loop();

debug("ending");
# vim: ts=4 expandtab

-- 
Christian Hammers             WESTEND GmbH  |  Internet-Business-Provider
Technik                       CISCO Systems Partner - Authorized Reseller
                              Lütticher Straße 10      Tel 0241/701333-11
[email protected]                D-52064 Aachen              Fax 0241/911879


-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.