Commit 21890006 authored by Leigh Stoller's avatar Leigh Stoller

Add summary node utilization stats. The initial values are derived by

processing the node_history table, but that is *way* too slow to do on
the fly (say, from the web interface) cause of the number of records,
so the summary info is stored in the new node_utilization table. I
generate the summary info as new entries are added to node_history (in
SetNodeHistory) but if that becomes too messy, we can just as easily
shift to processing the table on a nightly basis.

Note that I added a new "inception" date field to the nodes table,
which will get set on new nodes, but for existing nodes I have to
derive it from the first entry in the node_history table, or else the
numbers will not make sense.
parent 61b938de
......@@ -624,6 +624,67 @@ sub DeleteVnodes(@)
return 0;
}
sub SetNodeHistory($$$$)
{
my ($self, $op, $user, $experiment) = @_;
my $exptidx = $experiment->idx();
my $nodeid = $self->node_id();
my $now = time();
my $uid;
my $uid_idx;
if (!defined($user)) {
# Should this be elabman instead?
$uid = "root";
$uid_idx = 0;
}
else {
$uid = $user->uid();
$uid_idx = $user->uid_idx();
}
if ($op eq TB_NODEHISTORY_OP_MOVE() || $op eq TB_NODEHISTORY_OP_FREE()) {
# Summary info. We need the last entry made.
my $query_result =
DBQueryWarn("select exptidx,stamp from node_history ".
"where node_id='$nodeid' ".
"order by stamp desc limit 1");
if ($query_result && $query_result->numrows) {
my ($lastexptidx,$stamp) = $query_result->fetchrow_array();
my $checkexp;
if ($op eq TB_NODEHISTORY_OP_FREE()) {
$checkexp = $experiment;
}
else {
$checkexp = Experiment->Lookup($lastexptidx);
}
if (defined($checkexp)) {
if ($checkexp->pid() eq NODEDEAD_PID() &&
$checkexp->eid() eq NODEDEAD_EID()) {
my $diff = $now - $stamp;
DBQueryWarn("update node_utilization set ".
" down=down+$diff ".
"where node_id='$nodeid'")
}
else {
my $diff = $now - $stamp;
DBQueryWarn("update node_utilization set ".
" allocated=allocated+$diff ".
"where node_id='$nodeid'");
}
}
}
}
return DBQueryWarn("insert into node_history set ".
" history_id=0, node_id='$nodeid', op='$op', ".
" uid='$uid', uid_idx='$uid_idx', ".
" stamp=$now, exptidx=$exptidx");
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -5266,29 +5266,34 @@ sub TBSetNodeHistory($$$$$)
# XXX Eventually this should change, but the use of $UID is funky.
#
my $dbid;
my $node = Node->Lookup($nodeid);
my $experiment = Experiment->Lookup($pid, $eid);
my $this_user;
if (!TBExptIDX($pid, $eid, \$exptidx)) {
if (!defined($node)) {
print "*** WARNING: No such node $nodeid!\n";
return 0;
}
if (!defined($experiment)) {
print "*** WARNING: No such experiment $pid/$eid!\n";
return 0;
}
if ($uid =~ /^[0-9]+$/) {
# Should this be elabman instead?
$uid = ($uid == 0 ? "root" : MapNumericUID($uid));
$dbid = 0;
if ($uid =~ /^[0-9]+$/ && $uid != 0) {
$this_user = User->LookupByUnixId($uid);
if (! defined($this_user)) {
print "*** WARNING: $UID does not exist in the DB!";
return 0;
}
}
else {
my $this_user = User->Lookup($uid);
$this_user = User->Lookup($uid);
if (! defined($this_user)) {
print "*** WARNING: $UID does not exist in the DB!";
return 0;
}
$dbid = $this_user->dbid();
}
return DBQueryWarn("insert into node_history set ".
" history_id=0, node_id='$nodeid', op='$op', ".
" uid='$uid', uid_idx='$dbid', ".
" stamp=UNIX_TIMESTAMP(now()), exptidx=$exptidx");
return $node->SetNodeHistory($op, $this_user, $experiment);
}
sub TBGetOSBootCmd($$$)
......
......@@ -1892,6 +1892,18 @@ CREATE TABLE `node_types_auxtypes` (
PRIMARY KEY (`auxtype`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `node_utilization`
--
DROP TABLE IF EXISTS `node_utilization`;
CREATE TABLE `node_utilization` (
`node_id` varchar(32) NOT NULL default '',
`allocated` int(10) unsigned NOT NULL default '0',
`down` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`node_id`),
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `nodeipportnum`
--
......@@ -1929,6 +1941,7 @@ CREATE TABLE `nodes` (
`type` varchar(30) NOT NULL default '',
`phys_nodeid` varchar(32) default NULL,
`role` enum('testnode','virtnode','ctrlnode','testswitch','ctrlswitch','powerctrl','unused') NOT NULL default 'unused',
`inception` datetime default NULL,
`def_boot_osid` int(8) unsigned default NULL,
`def_boot_path` text,
`def_boot_cmd_line` text,
......
......@@ -4083,7 +4083,24 @@ last_net_act,last_cpu_act,last_ext_act);
add `byswapin` tinyint(1) unsigned default '0'
after byswapmod;
The run the following script. It is better if the testbed is
not in use at the time!
Then run the following script. It is better if the testbed is
not in use at the time.
./resources.pl
4.124: Add summary node utilization support.
CREATE TABLE `node_utilization` (
`node_id` varchar(32) NOT NULL default '',
`allocated` int(10) unsigned NOT NULL default '0',
`down` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`node_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
alter table nodes add `inception` datetime default NULL after role;
Then run the following script. It is better if the testbed is
not in use at the time.
./nodeutil.pl
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2007 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
use lib "/usr/testbed/devel/stoller/lib";
use libdb;
use libtestbed;
use Experiment;
$| = 1;
my %nodeids = ();
# Need this for computing downtime.
my $downexp = Experiment->Lookup(NODEDEAD_PID(), NODEDEAD_EID());
if (!defined($downexp)) {
die("Could not lookup hwdown experiment object\n");
}
#
# Cleanup some stuff ...
#
$query_result =
DBQueryFatal("select e.pid,e.eid,s.rsrcidx,e.idx,e.creator_idx,s.created ".
" from experiments as e ".
"left join experiment_stats as s on s.exptidx=e.idx ".
"left join experiment_resources as r on r.idx=s.rsrcidx ".
"where r.exptidx is null");
while (my ($pid,$eid,$rsrcidx,$exptidx,$creator_idx,$created) =
$query_result->fetchrow_array()) {
DBQueryFatal("insert into experiment_resources ".
"(idx, tstamp, exptidx, uid_idx) ".
"values ($rsrcidx, '$created', $exptidx, $creator_idx)");
}
#
# Get the list of all current physical nodes.
#
$query_result =
DBQueryFatal("select node_id from nodes as n ".
"left join node_types as t on t.type=n.type ".
"where t.class='pc' or t.class='pcplabphys'");
while (my ($node_id) = $query_result->fetchrow_array()) {
$nodeids{$node_id} = {};
}
#
# Need to form an inception date. Use the first entry we find in the
# node_history table, or the current time.
#
foreach my $nodeid (keys(%nodeids)) {
$query_result =
DBQueryFatal("select MIN(stamp) from node_history ".
"where node_id='$nodeid'");
if (!$query_result->numrows) {
$nodeids{$nodeid}->{'inception'} = time();
}
else {
my ($stamp) = $query_result->fetchrow_array();
$stamp = time()
if (!defined($stamp));
$nodeids{$nodeid}->{'inception'} = $stamp;
}
$nodeids{$nodeid}->{'allocated'} = 0;
$nodeids{$nodeid}->{'down'} = 0;
}
#
# Now compute utilization.
#
foreach my $nodeid (keys(%nodeids)) {
$query_result =
DBQueryFatal("select op,exptidx,stamp from node_history ".
"where node_id='$nodeid' ".
"order by stamp");
next
if (!$query_result->numrows);
my $alloctime = 0;
my $downtime = 0;
my $alloced = 0;
my $lastexpt = 0;
while (my ($op,$exptidx,$stamp) = $query_result->fetchrow_array()) {
#print "$op, $exptidx, $stamp\n";
if ($op eq "alloc") {
$alloced = $stamp;
$lastexpt = $exptidx;
}
elsif ($op eq "move") {
if (!$alloced) {
$alloced = $stamp;
$lastexpt = $exptidx;
next;
}
my $diff = $stamp - $alloced;
if ($lastexpt == $downexp->idx()) {
$downtime += $diff;
}
else {
$alloctime += $diff;
}
$alloced = $stamp;
$lastexpt = $exptidx;
}
else {
# Missing records ...
next
if (!$alloced);
my $diff = $stamp - $alloced;
if ($exptidx == $downexp->idx()) {
$downtime += $diff;
}
else {
$alloctime += $diff;
}
$alloced = 0;
$lastexpt = 0;
}
}
$nodeids{$nodeid}->{'allocated'} = $alloctime;
$nodeids{$nodeid}->{'down'} = $downtime;
}
foreach my $nodeid (keys(%nodeids)) {
$inception = $nodeids{$nodeid}->{'inception'};
$alloctime = $nodeids{$nodeid}->{'allocated'};
$downtime = $nodeids{$nodeid}->{'down'};
print "$nodeid, $inception, $alloctime, $downtime\n";
DBQueryFatal("update nodes set inception=FROM_UNIXTIME($inception) ".
"where node_id='$nodeid'");
DBQueryFatal("replace into node_utilization set ".
" node_id='$nodeid', allocated=$alloctime, down=$downtime");
}
......@@ -733,9 +733,9 @@ class Plab:
" (node_id, type, phys_nodeid, role, priority,"
" op_mode, def_boot_osid,"
" allocstate, allocstate_timestamp,"
" eventstate, state_timestamp)"
" eventstate, state_timestamp, inception)"
" values (%s, %s, %s, %s, %s,"
" %s, %s, %s, now(), %s, now())",
" %s, %s, %s, now(), %s, now(), now())",
(nodeid, 'pcplabphys', nodeid,
'testnode', priority*100,
'ALWAYSUP', defosid,
......@@ -747,6 +747,11 @@ class Plab:
" values (%s)",
(nodeid))
DBQueryFatal("replace into node_utilization"
" (node_id)"
" values (%s)",
(nodeid))
DBQueryFatal("replace into reserved"
" (node_id, exptidx, pid, eid, rsrv_time, vname)"
" values (%s, %s, %s, %s, now(), %s)",
......
......@@ -358,6 +358,7 @@ NODE: foreach my $node_id (@node_ids) {
"phys_nodeid='$node_id', role='$role', priority=$priority, " .
"eventstate='$state', op_mode='$opmode', " .
"def_boot_osid='$osid', " .
"inception=now(), ".
"state_timestamp=unix_timestamp(NOW()), " .
"op_mode_timestamp=unix_timestamp(NOW())");
......@@ -368,6 +369,8 @@ NODE: foreach my $node_id (@node_ids) {
"values ('$node_id', 'down', now()) ");
DBQueryFatal("insert into node_activity ".
"(node_id) values ('$node_id')");
DBQueryFatal("insert into node_utilization ".
"(node_id) values ('$node_id')");
#
# Copy data into the location_info table, if they provided any
......
......@@ -401,8 +401,9 @@ class Node
DBQueryFatal("select n.*,na.*,r.vname,r.pid,r.eid,i.IP, ".
"greatest(last_tty_act,last_net_act,last_cpu_act,".
"last_ext_act) as last_act, ".
" t.isvirtnode,t.isremotenode,t.isplabdslice, ".
" r.erole as rsrvrole, pi.IP as phys_IP, loc.* ".
" t.isvirtnode,t.isremotenode,t.isplabdslice, ".
" r.erole as rsrvrole, pi.IP as phys_IP, loc.*, ".
" util.* ".
" from nodes as n ".
"left join reserved as r on n.node_id=r.node_id ".
"left join node_activity as na on ".
......@@ -416,6 +417,8 @@ class Node
" pi.role='" . TBDB_IFACEROLE_CONTROL . "' ".
"left join location_info as loc on ".
" loc.node_id=n.node_id ".
"left join node_utilization as util on ".
" util.node_id=n.node_id ".
"where n.node_id='$node_id'");
if (mysql_num_rows($query_result) == 0) {
......@@ -465,6 +468,9 @@ class Node
$battery_timestamp = $row["battery_timestamp"];
$boot_errno = $row["boot_errno"];
$reserved_pid = $row["reserved_pid"];
$inception = $row["inception"];
$alloctime = $row["allocated"];
$downtime = $row["down"];
if (!$def_boot_cmd_line)
$def_boot_cmd_line = " ";
......
......@@ -56,7 +56,8 @@ echo "<b>Show: <a href='nodecontrol_list.php3?showtype=summary'>summary</a>,
<a href='nodecontrol_list.php3?showtype=widearea'>widearea</a>";
if ($isadmin) {
echo ", <a href='nodecontrol_list.php3?showtype=virtnodes'>virtual</a>,
echo ", <a href='nodeutilization.php'>utilization</a>,
<a href='nodecontrol_list.php3?showtype=virtnodes'>virtual</a>,
<a href='nodecontrol_list.php3?showtype=physical'>physical</a>,
<a href='nodecontrol_list.php3?showtype=all'>all</a>";
}
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2007 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include_once("node_defs.php");
#
# Standard Testbed Header
#
PAGEHEADER("Node Control Center");
#
# Only known and logged in users can do this.
#
$this_user = CheckLoginOrDie();
$uid = $this_user->uid();
$isadmin = ISADMIN();
if (!$isadmin && !STUDLY()) {
USERERROR("You are not allowed to view this page!", 1);
}
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("type", PAGEARG_STRING);
$query_result =
DBQueryFatal("select n.inception,util.*, ".
" UNIX_TIMESTAMP(now())-UNIX_TIMESTAMP(n.inception) as age ".
" from node_utilization as util ".
"left join nodes as n on n.node_id=util.node_id ".
"left join node_types as t on t.type=n.type ".
"where n.inception is not null and t.class='pc' and ".
" role='testnode'" .
"order BY priority");
if (mysql_num_rows($query_result) == 0) {
echo "<center>Oops, no nodes to show you!</center>";
PAGEFOOTER();
exit();
}
echo "<center>
<table id='nodetable' align=center cellpadding=2 border=1>
<thead class='sort'>
<tr>
<th>Node ID</th>
<th>Inception Date</th>
<th align=center>Age<br>(seconds)</th>
<th align=center>Free<br>(seconds)</th>
<th align=center>Free<br>(percent)</th>
<th align=center>Alloc<br>(seconds)</th>
<th align=center>Alloc<br>(percent)</th>
<th align=center>Down<br>(seconds)</th>
<th align=center>Down<br>(percent)</th>
</tr>
</thead>\n";
while ($row = mysql_fetch_array($query_result)) {
$node_id = $row["node_id"];
$inception = $row["inception"];
$age = $row["age"];
$alloctime = $row["allocated"];
$downtime = $row["down"];
$freetime = $age - ($alloctime + $downtime);
$allocpercent = sprintf("%.3f", ($alloctime / $age) * 100);
$freepercent = sprintf("%.3f", ($freetime / $age) * 100);
$downpercent = sprintf("%.3f", ($downtime / $age) * 100);
echo "<tr>
<td>$node_id</td>
<td>$inception</td>
<td>$age</td>
<td>$freetime</td>
<td>$freepercent</td>
<td>$alloctime</td>
<td>$allocpercent</td>
<td>$downtime</td>
<td>$downpercent</td>
</tr>\n";
}
echo "</table></center>\n";
echo "<script type='text/javascript' language='javascript'>
sorttable.makeSortable(getObjbyName('nodetable'));
</script>\n";
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
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