Commit 23b0cbf1 authored by Timothy Stack's avatar Timothy Stack

Merge feedback stuff from the virt tree.

parent 84c5e151
......@@ -43,7 +43,9 @@ version.c: slothd.c slothd.h sdcollectd.c sdcollectd.h
client: slothd
install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS) sdcollectd)
install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS) sdcollectd) webfeedback
$(INSTALL_PROGRAM) $(SRCDIR)/digest-slothd $(INSTALL_LIBEXECDIR)
$(INSTALL_PROGRAM) webfeedback $(INSTALL_LIBEXECDIR)
client-install: client
$(INSTALL_PROGRAM) -s slothd $(DESTDIR)$(CLIENT_BINDIR)
......
This diff is collapsed.
#! /usr/bin/perl -wT
use English;
use Getopt::Std;
use Socket;
#
# Print out the usage statement for this script and exit with a -1 return code.
#
sub usage()
{
print STDOUT
"Usage: webfeedback [-hc] [-d duration] pid gid eid\n".
"Web wrapper for dealing with feedback information.\n".
"\n".
"Required arguments:\n".
" pid - The project ID.\n".
" gid - The group ID.\n".
" eid - The experiment ID.\n".
"\n".
"Optional arguments:\n".
" -h Print this message.\n".
" -c Clear the feedback data.\n".
" -b Clear the bootstrap data.\n".
" -d secs Record feedback for the given duration.\n";
exit(-1);
}
#
# Option list:
#
# -h Print the usage message.
#
my $optlist = "hcbd:f";
#
# Configure variables
#
my $TB = "@prefix@";
# Locals
my $digest_slothd = "$TB/libexec/digest-slothd";
my $SAVEUID = $UID;
my $dbuid;
my $pid;
my $gid;
my $eid;
my $mode = "";
my $duration;
my $fake = 0;
#
# Untaint the path
#
$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/sbin:/usr/sbin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"h"})) {
usage();
}
if (defined($options{"c"}) || defined($options{"b"})) {
$mode = "clear";
}
if (defined($options{"d"})) {
if ($mode ne "") {
print STDERR "error: The clear and feedback options are mutually ".
"exclusive.\n";
usage();
}
$mode = "record";
$duration = $options{"d"};
}
if (defined($options{"f"})) {
$fake = 1;
}
if ($mode eq "") {
print STDERR "error: No mode specified, use '-c' to clear feedback or ".
"'-d N' to record for N seconds.\n";
usage();
}
if (@ARGV != 3) {
usage();
}
$pid = $ARGV[0];
$gid = $ARGV[1];
$eid = $ARGV[2];
#
# Must taint check!
#
if (defined($duration)) {
if ($duration =~ /^([0-9]+)$/) {
$duration = $1;
}
else {
die("Bad duration argument: $duration.");
}
}
if ($pid =~ /^([-\w]+)$/) {
$pid = $1;
}
else {
die("Bad pid argument: $pid.");
}
if ($eid =~ /^([-\w]+)$/) {
$eid = $1;
}
else {
die("Bad eid argument: $eid.");
}
if ($gid =~ /^([-\w]+)$/) {
$gid = $1;
}
else {
die("Bad gid argument: $gid.");
}
#
# Experiment must exist.
#
if (!($state = ExpState($pid,$eid))) {
die("There is no experiment $eid in project $pid\n");
}
#
# User must have permission to view the experiment.
#
if ($UID) {
if (!TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_MODIFY)) {
die("*** You not have permission to view this experiment!\n");
}
}
# XXX
$expdir = "/proj/$pid/exp/$eid";
# Figure out which mode we are in and act accordingly.
if ($mode eq "clear") {
if (defined($options{"c"})) {
unlink("$expdir/tbdata/feedback_data.tcl");
}
if (defined($options{"b"})) {
unlink("$expdir/tbdata/bootstrap_data.tcl");
}
}
if ($mode eq "record") {
if ($state ne EXPTSTATE_ACTIVE) {
# nothing to do
print "Cannot record feedback for an inactive experiment.\n";
exit(0);
}
print STDOUT "Starting tracers...\n";
# Get the list of virtual hosts for the virtual nodes.
my $query_result =
DBQueryFatal("select node_id,vname from reserved ".
"where pid='$pid' and eid='$eid' and erole='virthost'");
# Iterate through the virthosts starting up slothd in high-resolution
# tracing mode.
while (my ($node_id,$vname) = $query_result->fetchrow_array()) {
my $cmd;
$cmd = "rm -f /var/run/slothd.pid";
if (!$fake) {
system("/usr/local/bin/sudo $TB/bin/sshtb -host $node_id \"$cmd\"");
}
$cmd = "/proj/tbres/kwebb/evslothd -e -i 1 -t ${duration}";
if (!$fake) {
system("/usr/local/bin/sudo $TB/bin/sshtb -host $node_id \"$cmd\"");
}
}
# Sleep for the duration of the run, then
sleep($duration + 3);
$query_result->dataseek(0);
print STDOUT "Pulling logs...\n";
$vhost_logs = "";
# ... iterate through the virthosts again picking up the logs.
while (my ($node_id,$vname) = $query_result->fetchrow_array()) {
my $cmd;
$cmd = "/usr/local/bin/rsync -az ".
"--rsh=\"/usr/local/bin/sudo sshtb -host \" ".
"${node_id}:/var/emulab/logs/ ${expdir}/logs/${vname}/";
$vhost_logs .= " ${expdir}/logs/${vname}/slothd.log";
#if (!$fake) {
system($cmd);
#}
}
# Now that we have the logs, we have to find out what the peak resource
# needs are and then dump them into a TCL file. This TCL file is then
# included in the main NS file when it is reevaluated during a modify.
# Generating the file is actually done by a separate program,
# digest-slothd, but it requires a file that maps MAC addresses in the
# slothd log to the virtual node/link names. The rest of this script
# generates that file and pipes it into digest-slothd.
# XXX Most of this code was just lifted from tbreport, it can probably be
# optimized a bit.
# Mappings for IP/MAC addresses and testbed internal node/port member
# descriptors.
my %ipmap;
my %macmap;
my %memmap;
# Get the virtual node names and IP addresses and
my $virtnodes_result =
DBQueryFatal("SELECT vname,ips from virt_nodes ".
"where pid='$pid' and eid='$eid' order by vname");
# ... convert them into "member" form (e.g. node0:0) so we can match them
# up against the virt_lans table.
while (($vname,$ips) = $virtnodes_result->fetchrow_array()) {
foreach $ipinfo (split(" ",$ips)) {
($port,$ip) = split(":",$ipinfo);
$ipmap{"$vname:$port"} = $ip;
$macmap{$ip}->{"MEMBER"} = "$vname:$port";
}
}
# Get the addresses for regular interfaces and
my $result =
DBQueryFatal("select i.ip,i.mac,i.iface from reserved as r ".
"left join interfaces as i on r.node_id=i.node_id ".
"where r.pid='$pid' and r.eid='$eid' and ".
" i.ip is not NULL and i.ip!=''");
# ... add the MAC and node vname to the memmap.
while (($ip,$mac,$iface) = $result->fetchrow_array()) {
if ($mac =~ /^(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/) {
$mac = "$1:$2:$3:$4:$5:$6";
}
$macmap{$ip}->{"MAC"} = $mac;
$macmap{$ip}->{"IFACE"} = $iface;
if (defined($macmap{$ip}->{"MEMBER"})) {
my $member = $macmap{$ip}->{"MEMBER"};
my ($node,$port) = split(":", $member);
$memmap{$member}->{"MAC"} = $mac;
$memmap{$member}->{"NODE"} = $node;
}
}
# Get the addresses for veth interfaces and
$result =
DBQueryFatal("select i.IP,i.mac,i.iface from reserved as r ".
"left join veth_interfaces as i on r.node_id=i.node_id ".
"where r.pid='$pid' and r.eid='$eid' and ".
" i.IP is not NULL and i.IP!=''");
# ... add the MAC and node vname to the memmap.
while (($ip,$mac,$iface) = $result->fetchrow_array()) {
if ($mac =~ /^(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/) {
$mac = "$1:$2:$3:$4:$5:$6";
}
$macmap{$ip}->{"MAC"} = $mac;
$macmap{$ip}->{"IFACE"} = $iface;
if (defined($macmap{$ip}->{"MEMBER"})) {
my $member = $macmap{$ip}->{"MEMBER"};
($node,$port) = split(":", $member);
$memmap{$member}->{"MAC"} = $mac;
$memmap{$member}->{"NODE"} = $node;
}
}
# Get all of the virtual LANs in this experiment and
$result =
DBQueryFatal("select vname,member ".
" from virt_lans as v ".
"where pid='$pid' and eid='$eid' ".
"order by vname,member");
# ... add their vnames to the memmap.
while (($vname,$member) = $result->fetchrow_array()) {
$memmap{$member}->{"LAN"} = $vname;
}
# Start digest-slothd with all of the slothd logs, tee its output to the
# feedback file, and
open(DIGESTER,
"| $digest_slothd ${vhost_logs} - ".
" | tee ${expdir}/tbdata/feedback_data.tcl") or
fatal("Could not run digest-slothd!");
# ... pipe in our mapping data over stdin.
foreach my $m (sort keys(%memmap)) {
if (defined($memmap{$m})) {
printf DIGESTER
"%s %s %s\n",
$memmap{$m}->{"MAC"},
$memmap{$m}->{"NODE"},
$memmap{$m}->{"LAN"};
}
}
# All done, cleanup.
close(DIGESTER) or
fatal("$digest_slothd: " . ($? ? "exited with status $?."
: "error closing pipe: $!"));
}
......@@ -1281,16 +1281,36 @@ sub binpack_sort(@) {
}
}
if ($b->{'desires'} && $b->{'desires'}{'?+cpupercent'} &&
$a->{'desires'} && $a->{'desires'}{'?+cpupercent'}) {
$rv = ( $b->{'desires'}{'?+cpupercent'} <=>
$a->{'desires'}{'?+cpupercent'} );
if ($rv != 0) {
return $rv;
}
}
# Then by memory
if ($b->{'desires'} && $b->{'desires'}{'?+mem'} &&
$a->{'desires'} && $a->{'desires'}{'?+mem'}) {
if ($b->{'desires'} && $b->{'desires'}{'?+ram'} &&
$a->{'desires'} && $a->{'desires'}{'?+ram'}) {
$rv = ( $b->{'desires'}{'?+mem'} <=> $a->{'desires'}{'?+mem'} );
$rv = ( $b->{'desires'}{'?+ram'} <=> $a->{'desires'}{'?+ram'} );
if ($rv != 0) {
return $rv;
}
}
if ($b->{'desires'} && $b->{'desires'}{'?+rampercent'} &&
$a->{'desires'} && $a->{'desires'}{'?+rampercent'}) {
$rv = ( $b->{'desires'}{'?+rampercent'} <=>
$a->{'desires'}{'?+rampercent'} );
if ($rv != 0) {
return $rv;
}
}
# Fall back to count if neither of the others were given
return $b->{'count'} <=> $a->{'count'};
} @_;
......
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003 University of Utah and the Flux Group.
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# All rights reserved.
#
......
......@@ -303,6 +303,26 @@ Simulator instproc run {} {
# If any errors occur stop here.
if {$errors == 1} {return}
# Write out the feedback "bootstrap" file.
var_import ::TBCOMPAT::expdir;
var_import ::TBCOMPAT::BootstrapReservations;
if {! [file isdirectory $expdir]} {
# Experiment directory does not exist, so we cannot write the file...
} elseif {[array size BootstrapReservations] > 0} {
set file [open "$expdir/tbdata/bootstrap_data.tcl" w]
puts $file "# -*- TCL -*-"
puts $file "# Automatically generated feedback bootstrap file."
puts $file "#"
puts $file "# Generated at: [clock format [clock seconds]]"
puts $file "#"
puts $file ""
foreach res [array names BootstrapReservations] {
puts $file "set Reservations($res) $BootstrapReservations($res)"
}
close $file
}
# If we are running in impotent mode we stop here
if {$impotent == 1 && $passmode == 0} {return}
......
This diff is collapsed.
......@@ -388,6 +388,8 @@ foreach $node (keys(%nodes)) {
# Add CPU and RAM information
push @features, "?+cpu:$cpu_speed";
push @features, "?+ram:$ram";
push @features, "?+cpupercent:100";
push @features, "?+rampercent:100";
}
# Add features
......
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include("showstuff.php3");
#
# No PAGEHEADER since we spit out a Location header later. See below.
#
#
# Only known and logged in users can do this.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
#
# Check to make sure a valid experiment.
#
if (isset($pid) && strcmp($pid, "") &&
isset($eid) && strcmp($eid, "")) {
if (! TBvalid_eid($eid)) {
PAGEARGERROR("$eid contains invalid characters!");
}
if (! TBvalid_pid($pid)) {
PAGEARGERROR("$pid contains invalid characters!");
}
if (! TBValidExperiment($pid, $eid)) {
USERERROR("$pid/$eid is not a valid experiment!", 1);
}
if (! TBExptAccessCheck($uid, $pid, $eid, $TB_EXPT_MODIFY)) {
USERERROR("You do not have permission to run feedback on $pid/$eid!",
1);
}
}
else {
PAGEARGERROR("Must specify pid and eid!");
}
if (isset($mode) && strcmp($mode, "")) {
if (strcmp($mode, "record") && strcmp($mode, "clear")) {
PAGEARGERROR("Mode value, $mode, is not 'record' or 'clear'!");
}
}
else {
PAGEARGERROR("Must specify mode!");
}
$query_result = DBQueryFatal("select gid from experiments ".
"where pid='$pid' and eid='$eid'");
$row = mysql_fetch_array($query_result);
$gid = $row[0];
#
# Get the duration of the feedback run, default to 30 seconds if nothing was
# given.
#
if (!isset($duration) || $duration == "") {
$duration = 30; # seconds
}
elseif (! TBvalid_tinyint($duration) || $duration < 3) {
PAGEARGERROR("Duration must be an integer >= 3");
}
#
# We run this twice. The first time we are checking for a confirmation
# by putting up a form. The next time through the confirmation will be
# set. Or, the user can hit the cancel button, in which case we should
# probably redirect the browser back up a level.
#
if ($canceled) {
PAGEHEADER("$mode Feedback");
echo "<center><h3><br>
Operation canceled!
</h3></center>\n";
PAGEFOOTER();
return;
}
if (!$confirmed) {
PAGEHEADER("$mode feedback");
echo "<font size=+2>Experiment <b>".
"<a href='showproject.php3?pid=$pid'>$pid</a>/".
"<a href='showexp.php3?pid=$pid&eid=$eid'>$eid</a></b></font>\n";
if(strcmp($mode, "record") == 0) {
echo "<center><font size=+2><br>
How much feedback data should be recorded?<br>
Must be atleast 3 seconds.
</font>\n";
}
else if(strcmp($mode, "clear") == 0) {
echo "<center><font size=+2><br>
Really clear feedback data?
</font>\n";
}
SHOWEXP($pid, $eid, 1);
echo "<form action=feedback.php3 method=get>";
echo "<input type=hidden name=pid value=$pid>\n";
echo "<input type=hidden name=eid value=$eid>\n";
echo "<input type=hidden name=mode value=$mode>\n";
if(strcmp($mode, "record") == 0) {
echo "<table align=center border=1>\n";
echo "<tr>
<td>
<input type='text' name='duration' value='$duration'> seconds
</td>
</tr>
</table><br>\n";
}
else if(strcmp($mode, "clear") == 0) {
echo "<br>Data to clear:</br>";
echo "<table align=center border=1>\n";
echo "<tr>
<td>
<input type='checkbox' name='clear_last' value='1' checked='1'> Last recorded trace.
</td>
</tr>
<tr>
<td>
<input type='checkbox' name='clear_bootstrap' value='1'> Bootstrap data.
</td>
</tr>
</table><br>\n";
}
echo "<b><input type=submit name=confirmed value=Confirm></b>\n";
echo "<b><input type=submit name=canceled value=Cancel></b>\n";
echo "</form>\n";
echo "</center>\n";
PAGEFOOTER();
return;
}
function SPEWCLEANUP()
{
global $fp;
if (connection_aborted() && $fp) {
pclose($fp);
}
exit();
}
# For backend.
TBGroupUnixInfo($pid, $gid, $unix_gid, $unix_name);
if (strcmp($mode, "record") == 0) {
#
# A cleanup function to keep the child from becoming a zombie, since
# the script is terminated, but the children are left to roam.
#
$fp = 0;
register_shutdown_function("SPEWCLEANUP");
ignore_user_abort(1);
# Start the script and pipe its output to the user.
$fp = popen("$TBSUEXEC_PATH $uid $unix_gid webfeedback -d $duration $pid $gid $eid",
"r");
if (! $fp) {
USERERROR("Feedback failed!", 1);
}
header("Content-Type: text/plain");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
flush();
echo date("D M d G:i:s T");
echo "\n";
echo "Recording feedback for $duration seconds\n";
flush();
while (!feof($fp)) {
$string = fgets($fp, 1024);
echo "$string";
flush();
}
$retval = pclose($fp);
$fp = 0;
if ($retval == 0)
echo "Feedback run was successful!\n";
echo date("D M d G:i:s T");
echo "\n";
return;
}
else if (strcmp($mode, "clear") == 0) {
PAGEHEADER("Clearing feedback");
echo "foo $clear_last $clear_bootstrap";
$options = "";
if (isset($clear_last) && !strcmp($clear_last, "1"))
$options .= " -c";
if (isset($clear_bootstrap) && !strcmp($clear_bootstrap, "1"))
$options .= " -b";
if ($options != "") {
$retval = SUEXEC($uid, $unix_gid,
"webfeedback $options $pid $gid $eid",
SUEXEC_ACTION_USERERROR);
}
echo "<center><h3><br>Done! $options</h3></center>\n";
PAGEFOOTER();
return;
}
?>
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
include("showstuff.php3");
PAGEHEADER("Remap Virtual Nodes");
#
# Only known and logged in users can do this.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
#
# Check to make sure a valid experiment.
#
if (isset($pid) && strcmp($pid, "") &&
isset($eid) && strcmp($eid, "")) {
if (! TBvalid_eid($eid)) {
PAGEARGERROR("$eid is contains invalid characters!");
}
if (! TBvalid_pid($pid)) {
PAGEARGERROR("$pid is contains invalid characters!");
}
if (! TBValidExperiment($pid, $eid)) {
USERERROR("$pid/$eid is not a valid experiment!", 1);
}
if (! TBExptAccessCheck($uid, $pid, $eid, $TB_EXPT_MODIFY)) {
USERERROR("You do not have permission to run remap on $pid/$eid!",
1);
}
}
else {
PAGEARGERROR("Must specify pid and eid!");
}
$expstate = TBExptState($pid, $eid);
if (strcmp($expstate, $TB_EXPTSTATE_ACTIVE) &&
strcmp($expstate, $TB_EXPTSTATE_SWAPPED)) {
USERERROR("You cannot remap an experiment in transition.", 1);
}
if (!strcmp($expstate, $TB_EXPTSTATE_ACTIVE)) {
$reboot = 1;
$eventrestart = 1;
}
else {
$reboot = 0;
$eventrestart = 0;
}
if ($canceled) {
echo "<center><h3><br>
Operation canceled!
</h3></center>\n";
PAGEFOOTER();
return;
}
if (!$confirmed) {
echo "<font size=+2>Experiment <b>".
"<a href='showproject.php3?pid=$pid'>$pid</a>/".
"<a href='showexp.php3?pid=$pid&eid=$eid'>$eid</a></b></font>\n";
echo "<center><font size=+2><br>
Are you sure you want to remap your experiment?
</font>\n";
SHOWEXP($pid, $eid, 1);
echo "<form action=remapexp.php3 method=post>";
echo "<input type=hidden name=pid value=$pid>\n";
echo "<input type=hidden name=eid value=$eid>\n";
echo "<b><input type=submit name=confirmed value=Confirm></b>\n";
echo "<b><input type=submit name=canceled value=Cancel></b>\n";
echo "</form>\n";
echo "</center>\n";
PAGEFOOTER();