jablonka.czprosek.czf

hotsanic

Subversion Repositories:
[/] [branches/] [HotSaNIC-0.5.0-pre6/] [lib/] [HotSaNICmod.pm] - Rev 30 Go to most recent revision

Compare with Previous - Blame - Download


#
# $Id: HotSaNICmod.pm,v 1.42 2004/08/30 11:21:22 bernisys Exp $
#

package HotSaNICmod;

use HotSaNICparser;
use HotSaNIClog;
use RRDs;

my $MODNAME;
my %MODARGS;
my %CONFglob;
my $SAMPLING=0;
my $SAMPLE_LAST=0;
my $SAMPLE_DURATION=0;

($VERSION = '$Revision: 1.42 $') =~ s/.*(\d+\.\d+).*/$1/;

######################################################################
#
# control function to make sure that a module is running only once
#
# Usage:
#   dupe_control($what,@messages);
#
#   $what:
#     start -> call this method at the head of a module to touch the
#               dupe-detection checkfile.
#     stop  -> remove PIDfile and exit normally.
#     die   -> stop process with error.
#     warn  -> print $message as warning and continue execution.
#               FOR COMPATIBILITY REASONS ONLY!
#               please use HotSaNIClog::warn
#     info  -> print $message and continue execution.
#               FOR COMPATIBILITY REASONS ONLY!
#               please use HotSaNIClog::info
#
#   each message will be preceded with a timestamp and the module name
# 
sub dupe_control {
  my $what=shift;
  my $pid=0;
  my $delta=0;
  my $line;

  if ( ($what eq "check") || ($what eq "start") ) { $pid=HotSaNICparser::get_pid(); }

  if ($what eq "info") {
    HotSaNIClog::error("HotSaNICmod::dupe_control(\"info\",...); is deprecated, use HotSaNIClog::info instead!");
    HotSaNIClog::info(@_);
    }
  elsif ($what eq "warn") {
    HotSaNIClog::error("HotSaNICmod::dupe_control(\"warn\",...); is deprecated, use HotSaNIClog::warn instead!");
    HotSaNIClog::warn(@_);
    }
  elsif ($what eq "start") {
    if ($pid > 0) {
      HotSaNIClog::error("process already running on PID $pid for $delta seconds.");
      exit 1;
      }
    open FILE,">running.pid";
    print FILE $$;
    close FILE;
    HotSaNIClog::info("process forked to background.");
    }
  elsif ($what eq "stop") {
    HotSaNIClog::info("exiting normally.");
    if (-e "running.pid") { unlink "running.pid"; }
    exit 0;
    }
  elsif ($what eq "die") {
    HotSaNIClog::error(@_);
    exit 1;
    }
  elsif ($what eq "check") {
    if (!defined $pid) { $pid=0; }
    if (($pid > 0) && (-e "/proc/$pid/stat")) {
      open FILE,"/proc/$pid/stat";
      @fileds=split / /,<FILE>;
      close FILE;
      $starttime=$fileds[21]/100;
      open FILE,"/proc/uptime";
      $line=<FILE>;
      close FILE;
      ($uptime)=split / /,$line;
      $delta=int(($uptime-$starttime)*100)/100;
      }
    if (($pid > 0) && ($^O =~ /bsd/)) {
      open FILE,"/proc/$pid/status";
      @fileds=split / /,<FILE>;
      close FILE;
      ($starttime, $msec)= split /,/, $fileds[7];
      $delta=int((time-$starttime)*100)/100;
      }
    if (!defined $delta) { $delta=0; }
    return ($pid,$delta);
    }
  else { HotSaNIClog::error("HotSaNICmod::dupe_control method \"$what\" not supported."); }
  }


######################################################################
#
# check cmdline arguments
#
sub init {
  $args=shift || "";
  $MODNAME=HotSaNICparser::get_module_name();
  %CONFglob=HotSaNICparser::get_config("../..",$MODNAME);
  if (scalar(keys(%CONFglob)) == 0) { HotSaNICmod::dupe_control("die","missing global settings file"); }
  HotSaNIClog::set_timestamping($CONFglob{TIMESTAMPING});

  my $found=0;
  my @possible_args=("nodaemon","start","stop","status","configure","sample","update","version","help","showargs");
  foreach (@possible_args) { if ($args eq $_) { $found++; } }
  if ($found == 0) { $args=""; }

  if ( $args eq "" ) {
    print "usage: $0 [".join("/",@possible_args)."]\n";
    print "\n";
    exit 1;
    }

  if ( $args =~ /help/) {
    print "usage: $0 [".join("/",@possible_args)."]\n";
    print "\n";
    print "alternative usage: send a signal to <module PID>\n";
    print "\n";
    print "argument   signal   function\n";
    print "---------  -------  --------------------------------------------------\n";
    print "nodaemon            start module in foreground\n";
    print "start               start module daemon\n";
    print "stop       SIGTERM  terminate module daemon\n";
    print "status              show status of module daemon\n";
    print "configure  SIGHUP   daemon re-reads its config file\n";
    print "sample     SIGUSR1  sample now\n";
    print "update     SIGUSR2  (not implemented yet)\n";
    print "version             show version of OS-dependant module used\n";
    print "showargs            show settings hash\n";
    print "\n";
    exit 0;
    }

# import common functions
#
  my $COM_LIB="./platform/common.pm";
  if ( -e $COM_LIB ) {
    eval { require $COM_LIB; };
    if ($@) { HotSaNICmod::dupe_control("die","can't import common library: $COM_LIB",$!,$@); }
    }
  else { HotSaNICmod::dupe_control("die","can't import common library: $COM_LIB","file not found."); }

# import OS-specific functions
#
  my $fallback=0;
  my $OS_LIB="./platform/$^O.pm";
  if ( -e $OS_LIB ) {
    eval { require $OS_LIB; };
    if ($@) {
      HotSaNIClog::error("can't import library: $OS_LIB",$!,$@);
      $fallback=1;
      }
    }
  else { HotSaNIClog::info("no special library available for \"$^O\""); $fallback=1; }

# OS-lib not found -> try to load default lib
#
  if ($fallback == 1) {
    HotSaNIClog::info("falling back to library \"default\"");
    eval { require "./platform/default.pm"; };
    if ($@) {
      HotSaNICmod::dupe_control("die","can't import library ./platform/default.pm",$!,$@);
      }
    }

  # read configuration, but don't initalize module (parameter "0")
  configure(0);

  if ( ($args =~ /version/) or (HotSaNIClog::check_debuglevel("MODULE_VERBOSE"))) {
    undef my @VERSIONS;
    push @VERSIONS,HotSaNICmod::common::version() if defined &HotSaNICmod::common::version;
    push @VERSIONS,HotSaNICmod::OSdep::version() if defined &HotSaNICmod::OSdep::version; 
    push @VERSIONS,HotSaNICmod::syssnmp::version() if defined &HotSaNICmod::syssnmp::version;
    HotSaNIClog::info("using libs: ".join(" / ",@VERSIONS));
    }

  if ( ($args =~ /start/) or ($args =~ /nodaemon/) ) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) { HotSaNIClog::info("module already running on PID $pid"); }
    else {

      # initialize module
      #
      unlink "*.dat","*.old";
      if ( -e "init" ) {
        HotSaNIClog::info("-- begin init --");
        system "./init";
        HotSaNIClog::info("-- end init --");
        }
      elsif (defined &HotSaNICmod::OSdep::init) {
        HotSaNIClog::info("-- begin init --");
        HotSaNICmod::OSdep::init(%MODARGS);
        HotSaNIClog::info("-- end init --");
        }

      # set signal handlers and create background childprocess
      #
      $SIG{TERM} = \&terminate;
      $SIG{HUP} = \&configure;
      $SIG{USR1} = \&sample;
      $SIG{USR2} = \&update;
      if ($args =~ /start/) { fork && exit; }
      HotSaNICmod::dupe_control("start","");
      while (1 == 1) { sleep; }
      }
    }
        
  if ( $args =~ /status/) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) {
      print "module \"$MODNAME\" running on PID $pid for $uptime sec.\n";
      }
    else { print "$MODNAME: no process running.\n"; }
    my @DBs=HotSaNICmod::get_DBs();
    my ($min,$max)=HotSaNICmod::get_last_DB_changes();
    if ($min>=0) {
      print "last DB update: $min";
      if ($max != $min) { print " (max: $max)"; }
      print " seconds ago\n";
      print "DBs found:  ",join("\n            ",@DBs),"\n";
      }
    }
        
  if ( $args =~ /sample/) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) { kill "SIGUSR1",$pid; }
    else { print "$MODNAME: no process running.\n"; }
    }
        
  if ( $args =~ /stop/) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) { kill "SIGTERM",$pid; }
    else { print "$MODNAME: no process running.\n"; }
    }
        
  if ( $args =~ /configure/) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) { kill "SIGHUP",$pid; }
    else { print "$MODNAME: no process running.\n"; }
    }
        
  if ( $args =~ /update/) {
    my ($pid,$uptime)=HotSaNICmod::dupe_control("check","");
    if ($pid > 0) { kill "SIGUSR2",$pid; }
    else { print "$MODNAME: no process running.\n"; }
    }

  if ( $args =~ /showargs/) {
    print "---------- CONFIGURATION ----------\n";
    #
    # alternative code - higher memory usage!
    #
    #   use Data::Dumper;
    #   $Data::Dumper::Varname="MODARGS";
    #   $Data::Dumper::Sortkeys=1;
    #   print Dumper(\%MODARGS);
    #
    for (sort keys %MODARGS) {
      print "  $_ = ".$MODARGS{$_}."\n";
      if (ref $MODARGS{$_} eq "ARRAY") {
        if (! @{$MODARGS{$_}}) { print " " x ((length $_)+5),"(empty)\n"; }
        foreach $line (@{$MODARGS{$_}}) { print " " x ((length $_)+5),"-> $line\n"; }
        }
      }
    print "-----------------------------------\n";
    }
  }


######################################################################
#
# terminate module daemon
#
sub terminate {
  HotSaNICmod::dupe_control("stop","");
  }

######################################################################
#
# interface to OS-dependent functions
#
sub sample {
  my $DIFF=time-$SAMPLE_LAST;
  if (HotSaNIClog::check_debuglevel("MODULE_SAMPLING")) {
    HotSaNIClog::info("last sampling $DIFF seconds ago took $SAMPLE_DURATION seconds.");
    }
  if ( $DIFF >= 9) {
    if ($SAMPLING == 0) {
      # auto throttling
      # skip sample if last sampling process took > 5 seconds and divide last duration by 2
      if ($SAMPLE_DURATION > 5) {
        $SAMPLE_DURATION/=2;
        if (HotSaNIClog::check_debuglevel("MODULE_SAMPLING")) {
          HotSaNIClog::warn("can't sample now, last sampling took > 5s.");
          }
        }
      else {
        $SAMPLING=1;
        $SAMPLE_LAST=time;
        HotSaNICmod::OSdep::sample(%MODARGS);
        if ( (defined $MODARGS{USE_SNMP}) and (defined &HotSaNICmod::syssnmp::sample) ) { HotSaNICmod::syssnmp::sample(%MODARGS); }
        $SAMPLING=0;
        $SAMPLE_DURATION=(time-$SAMPLE_LAST);
        }
      }
    elsif (HotSaNIClog::check_debuglevel("MODULE_SAMPLING")) {
      HotSaNIClog::warn("can't sample now, old sampling process running.");
      }
    }
  elsif (HotSaNIClog::check_debuglevel("MODULE_SAMPLING")) {
    HotSaNIClog::warn("can't sample now, last sample taken less than 10s ago.");
    }
  }

sub update {
  HotSaNICmod::OSdep::update;
  }

######################################################################
#
# configure 
#  - parse main settings
#  - parse module settings
#  - set module-specific configuration
sub configure {
  $initmode=shift || 0;

  my @CONFmod=HotSaNICparser::read_settings(".",$MODNAME);
  if (scalar(@CONFmod) == 0) { HotSaNICmod::dupe_control("die","missing module settings file"); }

  my $modvar=$CONFglob{VARDIR}."/modules/".lc $MODNAME;
  $modvar=~ s/\/+/\//g;
  if (! -d $CONFglob{VARDIR}."/modules") { mkdir $CONFglob{VARDIR}."/modules",0755; }
  if (! -d $modvar) { mkdir $modvar,0755; }

# configure module-specific settings
#
  %MODARGS=HotSaNICmod::common::configure(@CONFmod);

# import module's SNMP library if necessary
#
  if (defined $MODARGS{USE_SNMP}) {
    my $SNMP_LIB="./platform/syssnmp.pm";
    $MODARGS{SNMPWALK}=$CONFglob{SNMPWALK};
    $MODARGS{SNMPGET}=$CONFglob{SNMPGET};
    if ( -e $SNMP_LIB ) {
      eval { require $SNMP_LIB; };
      if ($@) { HotSaNIClog::error("can't import library: $SNMP_LIB",$!,$@); }
      }
    else { HotSaNIClog::info("no SNMP library available"); }
    }

# configure common module settings
#
  foreach (@CONFmod) {
    ($var,$value)=HotSaNICparser::parse_line($_);
    if ($var eq "DEBUGLEVEL") { $MODARGS{DEBUGLEVEL}=HotSaNIClog::get_debuglevel($value); }
    }

# add global settings
#
  $MODARGS{MODNAME}=$MODNAME;
  $MODARGS{VARDIR}=$modvar;
  $MODARGS{DEBUGLEVEL} |= HotSaNIClog::get_debuglevel($CONFglob{DEBUGLEVEL});
  HotSaNIClog::set_debuglevel($MODARGS{DEBUGLEVEL});
  if ($initmode eq "HUP") { HotSaNICmod::OSdep::init(%MODARGS) if defined &HotSaNICmod::OSdep::init; }
  }

######################################################################
#
# returns an array containing all "*.rrd" files in the current module's "rrd" directory
#
sub get_DBs {
  opendir(DIR,"rrd") || HotSaNICmod::dupe_control("die",$MODARGS{"MODNAME"},"error opening database dir - $!");
  my @DBs=grep(/\.rrd/,readdir(DIR));
  closedir(DIR);
  return @DBs;
  }


######################################################################
#
# returns an array containing the min and max seconds since last DB changes
# 
# if no DBs are found, (-1,-1) will be returned.
#
sub get_last_DB_changes {

  my @DBs=get_DBs();

  if (!@DBs) { return (-1,-1); }

  my ($min,$max)=(999999999999,0);
  foreach $test (@DBs) {
    (undef,undef,undef,undef,undef,undef,undef,undef,undef,$mtime,undef,undef,undef)=stat("rrd/".$test);
    $mtime=time-$mtime;
    if ($mtime<$min) { $min=$mtime; }
    if ($mtime>$max) { $max=$mtime; }
    }

  return ($min,$max);
  }


######################################################################
#
#  updates a database, creates a new one if necessary
#
# USAGE:
#
#  do_rrd($database,$maxvalue,$sampletime,@values);
#
#    $database    name of the database (without ".rrd" suffix)
#
#    $maxvalue    the maximum value to be expected on this datasource
#                 (needed for DB creation)
#
#    $sampletime  timestamp of the current sample
#
#    @values      array of all values for this timestamp
#
sub do_rrd {
  my $dbname = shift;
  my $dbmax = shift;
  my $sampletime = shift;

  if ( $#_ < 0 ) {
    HotSaNIClog::warn("no values passed for $dbname");
  }

  my $values=join(":",@_);

  if (HotSaNIClog::check_debuglevel("MODULE_DB_UPDATE")) {
    HotSaNIClog::info("updating $dbname with $values");
    }

  # build new database if needed
  if ( ! -e "rrd/$dbname.rrd" ) { system("./makerrd","$dbname","$dbmax") }
  
  # update database
  RRDs::update "rrd/$dbname.rrd",$sampletime.":$values";
  if ($ERROR = RRDs::error) { HotSaNIClog::error("unable to update '$dbname.rrd': $ERROR"); }
  }

1;


Powered by WebSVN 2.2.1