Commit 043b4334 authored by Kirk Webb's avatar Kirk Webb Committed by Kirk Webb

Fixes/updates to the idlestats reporting script.

parent b8d01145
......@@ -25,37 +25,38 @@ use strict;
use English;
use Getopt::Std;
use Date::Parse;
use POSIX (ceil floor);
use POSIX qw/ceil floor/;
use RRDs;
#use JSON;
use Data::Dumper;
use JSON;
my $TB = "@prefix@";
use lib "$TB/lib";
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use EmulabConstants;
use Experiment;
use Interface;
use Node;
use User;
# Constants
my $TB = "@prefix@";
my $STEP = 3600; # 1 hour (in seconds). This should be an RRA epoch.
my $MAXWINDOW = 86400 * 14; # two weeks (in seconds).
my $DEFWINDOW = 86400 * 14; # two weeks (in seconds).
my $MINTIMESPECLEN = 6;
my $MAXTIMESPECLEN = 100;
my $SD_STATSDIR = "$TB/data/slothd_rrd";
my $ALLZEROMAC = "000000000000";
# Globals
my $now = time();
my $end = floor($now/$STEP)*$STEP; # Now, normalized to STEP.
my $start = $end - floor($MAXWINDOW/$STEP)*$STEP; # Maximum window, normalized.
my $end = floor($now/$STEP)*$STEP - $STEP; # Now, normalized to STEP.
my $start = $end - floor($DEFWINDOW/$STEP)*$STEP + 2*$STEP; # Default window, normalized.
my $experiment;
my @nodelist = ();
sub usage() {
print STDERR
"Return JSON-encoded node activity stastics.\n\n".
"Usage: $0 [-d] [-S <start_time>] [-E <end_time>] node [node ...]\n" .
" $0 [-d] [-S <start_time>] [-E <end_time>] -e <pid>,<eid>\n".
"-d: turn on debugging.\n" .
......@@ -63,10 +64,15 @@ sub usage() {
"-S <start_time>: bound the start of the returned data.\n".
" Default is beginning of available data for a list of nodes,\n".
" or the beginning of the specified experiment.\n".
"-E <end_time>: bound the end of the returned data. Default is 'now'.\n "
"-E <end_time>: bound the end of the returned data. Default is 'now'.\n".
"\n".
"Start/end times can be specified as anything recognized by the Date::Parse module.\n"
"When requesting experiment data, start times prior to the start of the experiment will be truncated to the beginning of the experiment (with a warning).\n";
"Start/end times can be specified as anything recognized by the\n".
"Date::Parse module. When requesting experiment data, start times\n".
"prior to the start of the experiment will be truncated to the beginning\n".
"of the experiment (with a warning). The start time must be less than\n".
"the end time. Returned data is reported at a fixed 1 hour granularity.\n".
"Data series with no data points are indicated as such with stub\n".
"entries in the output.\n";
exit 1;
}
......@@ -111,7 +117,7 @@ if ($opts{'e'}) {
warn "Experiment $experiment is not active!\n";
exit 1;
}
@nodelist = $experiment->NodeList(1,1);
@nodelist = $experiment->NodeList(0,1);
# Bump start time to the beginning of this experiment plus one
# step. Adding one step epoch prevents us from grabbing stastics
# that incorporate data from prior to the start of the experiment.
......@@ -136,7 +142,7 @@ if (@ARGV) {
warn "You ($user) do not have access to $node\n";
exit 1;
}
push @nodelist, $node->node_id();
push @nodelist, $node;
}
}
......@@ -145,6 +151,25 @@ if (!@nodelist) {
exit 1;
}
if ($opts{'S'}) {
if ($opts{'S'} !~ /^([-.:\/,\w\s]{$MINTIMESPECLEN,$MAXTIMESPECLEN})$/) {
warn "Illegal start time spec!\n";
exit 1;
}
my $stime = str2time($1);
if (!defined($stime)) {
warn "Start time could not be parsed!\n";
exit 1;
}
$stime = floor($stime/$STEP)*$STEP;
if ($experiment && $stime < $start) {
warn "Specified start time is prior to start of experiment!\n".
"Truncating to: $start\n";
} else {
$start = $stime;
}
}
if ($opts{'E'}) {
if ($opts{'E'} !~ /^([-.:\/,\w\s]{$MINTIMESPECLEN,$MAXTIMESPECLEN})$/) {
warn "Illegal end time spec!\n";
......@@ -156,33 +181,12 @@ if ($opts{'E'}) {
exit 1;
}
$etime = floor($etime/$STEP)*$STEP;
if ($etime < $start) {
warn "End time is too early!\n";
exit 1;
}
if ($etime > $now) {
warn "End time is in the future!\n";
exit 1;
warn "End time is in the future! Truncated to: $now\n";
}
$end = $etime;
}
if ($opts{'S'}) {
if ($opts{'S'} !~ /^([-.:\/,\w\s]{$MINTIMESPECLEN,$MAXTIMESPECLEN})$/) {
warn "Illegal start time spec!\n";
exit 1;
}
my $stime = str2time($1);
if (!defined($stime)) {
warn "Start time could not be parsed!\n";
exit 1;
}
$stime = floor($stime/$STEP)*$STEP;
if ($stime < $start) {
warn "Start time is too early!\n";
exit 1;
else {
$end = $etime;
}
$start = $stime;
}
if ($start > $end) {
......@@ -190,16 +194,14 @@ if ($start > $end) {
exit 1;
}
if ($start < $now-$MAXWINDOW || $end < $now-$MAXWINDOW) {
warn "Either the start time or the end time (or both) is before the maximum window of $MAXWINDOW seconds ago.\n";
exit 1;
}
# Finally do some real work!
my @results = ();
foreach my $node_id (@nodelist) {
foreach my $node (@nodelist) {
my $node_id = $node->node_id();
my $nobj = {};
$nobj->{'node_id'} = $node_id;
# Process top-level node stats.
my $mainrrd = "$SD_STATSDIR/${node_id}.rrd";
if (!-f $mainrrd) {
warn "Could not find main rrd file ($mainrrd) for $node_id\n";
......@@ -212,19 +214,52 @@ foreach my $node_id (@nodelist) {
warn "Could not get data for $node_id from $mainrrd: ". RRDs::error ."\n";
next;
}
my $hasvalues = 0;
my @main_tmp = ();
push @main_tmp,
['timestamp', 'tty_active', 'load_1min', 'load_5min', 'load_15min'];
foreach my $rrd_line (@$rrd_data) {
push @{$nobj->{'main'}}, [$rrd_stamp, @$rrd_line];
my ($last_tty, $load_1m, $load_5m, $load_15m) = @$rrd_line;
$last_tty = ($last_tty > 0) ? 1 : 0
if defined($last_tty);
$hasvalues = 1
if (defined($last_tty) && defined($load_1m)
&& defined($load_5m) && defined($load_15m));
push @main_tmp,
[$rrd_stamp, $last_tty, $load_1m, $load_5m, $load_15m];
$rrd_stamp += $rrd_step;
}
if ($hasvalues) {
@{$nobj->{'main'}} = @main_tmp;
} else {
$nobj->{'main'} = [];
}
# Process interface statistics.
my @interfaces = ();
my %ifmap = ();
my $ctrlmac = "*unknown*";
Interface->LookupAll($node, \@interfaces);
foreach my $intf (@interfaces) {
next if ($intf->mac() eq $ALLZEROMAC);
if ($intf->IsControl()) {
$ctrlmac = uc($intf->mac());
$ifmap{$ctrlmac} = $intf;
$intf->{'SEEN'} = 0;
}
elsif ($intf->IsExperimental()) {
$ifmap{uc($intf->mac())} = $intf;
$intf->{'SEEN'} = 0;
}
}
$nobj->{'interfaces'}->{'ctrl_iface'} = $ctrlmac;
my @intfrrds = glob "$SD_STATSDIR/${node_id}-*.rrd";
foreach my $intfrrd (@intfrrds) {
$intfrrd =~ /^$node_id-([0-9a-f]{12}).rrd$/i;
my $maddr = $1;
if (!$maddr) {
warn "Malformed interface MAC in $node_id RRD file: $intfrrd\n";
next;
}
$intfrrd =~ /${node_id}-([0-9a-f]{12}).rrd$/i;
next if (!$1);
my $mac = uc($1);
next if (!exists($ifmap{$mac}));
$ifmap{$mac}->{'SEEN'} = 1;
($rrd_stamp,$rrd_step,$rrd_names,$rrd_data) =
RRDs::fetch($intfrrd, "MAX", "--start=$start", "--end=$end",
"--resolution=$STEP");
......@@ -232,13 +267,28 @@ foreach my $node_id (@nodelist) {
warn "Could not get interface data for $node_id from $intfrrd: ". RRDs::error ."\n";
next;
}
my $hasvalues = 0;
my @intf_tmp = (['timestamp', 'ipkt_rate', 'opkt_rate'],);
foreach my $rrd_line (@$rrd_data) {
push @{$nobj->{'interfaces'}->{$maddr}}, [$rrd_stamp, @$rrd_line];
my ($ipkt_rate, $opkt_rate) = @$rrd_line;
$hasvalues = 1
if (defined($ipkt_rate) && defined($opkt_rate));
push @intf_tmp, [$rrd_stamp, @$rrd_line];
$rrd_stamp += $rrd_step;
}
if ($hasvalues) {
@{$nobj->{'interfaces'}->{$mac}} = @intf_tmp;
} else {
$nobj->{'interfaces'}->{$mac} = [];
}
}
foreach my $mac (keys %ifmap) {
if (!$ifmap{$mac}->{'SEEN'}) {
$nobj->{'interfaces'}->{$mac} = [];
}
}
push @results, $nobj;
}
print Dumper(@results);
print to_json(\@results);
exit 0;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment