diff --git a/GNUmakefile.in b/GNUmakefile.in index 86f0be312ffdfd9cb9ba45bdb28768a6619f6f7d..f8ec73aa37ab6ba320f90e2b05435a6dfa0c4a0f 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -43,6 +43,8 @@ install-mkdirs: -mkdir -p $(INSTALL_TOPDIR)/log -mkdir -p $(INSTALL_TOPDIR)/lists -mkdir -p $(INSTALL_TOPDIR)/backup + -mkdir -p $(INSTALL_TOPDIR)/batch + -chmod 777 $(INSTALL_TOPDIR)/batch clean: clean-subdirs distclean: distclean-subdirs diff --git a/configure b/configure index c85d88bbfd2e8e6f3ce0cb851db701485dc81d9b..2482893cfb70d41c401d00b3df630856b2336718 100755 --- a/configure +++ b/configure @@ -957,6 +957,8 @@ outfiles="$outfiles Makeconf GNUmakefile \ tbsetup/os_load tbsetup/os_setup tbsetup/mkprojdir tbsetup/power \ tbsetup/resetvlans tbsetup/rmacct-ctrl tbsetup/rmproj \ tbsetup/sched_reload \ + tbsetup/batchexp tbsetup/killbatchexp tbsetup/batch_daemon \ + tbsetup/webbatchexp tbsetup/webkillbatchexp \ tbsetup/startexp tbsetup/endexp tbsetup/webstartexp tbsetup/webendexp \ tbsetup/ir/GNUmakefile tbsetup/ir/postassign tbsetup/snmpit \ tbsetup/ir/assign_wrapper tbsetup/ns2ir/GNUmakefile \ diff --git a/configure.in b/configure.in index 2f2127187e02be850e39847316acbf755d81d5b2..b754c30b2aa622151cdf23282addc13233b8a3a4 100755 --- a/configure.in +++ b/configure.in @@ -116,6 +116,8 @@ outfiles="$outfiles Makeconf GNUmakefile \ tbsetup/os_load tbsetup/os_setup tbsetup/mkprojdir tbsetup/power \ tbsetup/resetvlans tbsetup/rmacct-ctrl tbsetup/rmproj \ tbsetup/sched_reload \ + tbsetup/batchexp tbsetup/killbatchexp tbsetup/batch_daemon \ + tbsetup/webbatchexp tbsetup/webkillbatchexp \ tbsetup/startexp tbsetup/endexp tbsetup/webstartexp tbsetup/webendexp \ tbsetup/ir/GNUmakefile tbsetup/ir/postassign tbsetup/snmpit \ tbsetup/ir/assign_wrapper tbsetup/ns2ir/GNUmakefile \ diff --git a/tbsetup/GNUmakefile.in b/tbsetup/GNUmakefile.in index d0120f06df94b89cd952427b3f54db4b11df17a4..6eb3a742ccc141265185279afc2821910e8cbb53 100644 --- a/tbsetup/GNUmakefile.in +++ b/tbsetup/GNUmakefile.in @@ -11,13 +11,14 @@ include $(OBJDIR)/Makeconf SUBDIRS = checkpass ir ns2ir BIN_STUFF = power snmpit tbend tbrun tbprerun tbreport \ - os_load savevlans startexp endexp + os_load savevlans startexp endexp batchexp killbatchexp -SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup +SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \ + batch_daemon LIBEXEC_STUFF = mkprojdir rmproj mkacct-ctrl rmacct-ctrl \ os_setup mkexpdir console_setup \ - webstartexp webendexp + webstartexp webendexp webbatchexp webkillbatchexp LIB_STUFF = libtbsetup.pm @@ -76,6 +77,8 @@ post-install: chmod u+s $(INSTALL_BINDIR)/savevlans chown root $(INSTALL_LIBEXECDIR)/console_setup chmod u+s $(INSTALL_LIBEXECDIR)/console_setup + chown root $(INSTALL_SBINDIR)/batch_daemon + chmod u+s $(INSTALL_SBINDIR)/batch_daemon # # Control node installation (okay, plastic) diff --git a/tbsetup/batch_daemon.in b/tbsetup/batch_daemon.in new file mode 100644 index 0000000000000000000000000000000000000000..e8c3f3afed907ff9c80f992b15da08fac9aa5276 --- /dev/null +++ b/tbsetup/batch_daemon.in @@ -0,0 +1,476 @@ +#!/usr/bin/perl -wT +use English; +use Getopt::Std; + +# +# Create a batch experiment. +# +# BIG ASS WARNING: This works great as long as paper does not reboot! +# Needs some work if we want it to be stateless across reboots. +# +# usage: batch_daemon +# +sub usage() +{ + print STDOUT "Usage: batch_daemon\n"; + exit(-1); +} +my $optlist = ""; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $DBNAME = "@TBDBNAME@"; +my $TBOPS = "@TBOPSEMAIL@"; + +my $tbbindir = "$TB/bin/"; +my $batchdir = "$TB/batch"; +my $startexp = "$TB/bin/startexp"; +my $endexp = "$TB/bin/endexp"; +my $batchlog = "$TB/log/batchlog"; +my $projroot = "/proj"; +my $dirname; + +# +# These are valid in the children, not the parent. I suppose I could use +# dynamically scoped variables, but hardly worth it. +# +my $eid; +my $pid; +my $logname; +my $user_name = "Batch Daemon"; +my $user_email = "$TBOPS"; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Untaint the path +# +$ENV{'PATH'} = "/bin:/usr/bin:"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Parse command arguments. Once we return from getopts, all that should be +# left are the required arguments. +# +%options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (@ARGV != 0) { + usage(); +} + +# Go to ground. +daemonize(); + +# +# Set up for querying the database. +# +use Mysql; +my $DB = Mysql->connect("localhost", $DBNAME, "script", "none"); + +# +# Loop, looking for batch experiments that want to run. +# +while (1) { + # + # Need to lock the table here because of cancelation in killbatchexp. + # See the comments in there. We need to atomically grab the next + # batch experiment we want to try, and then change its state from + # new to configuring. We want to grab just one experiment, since + # it takes a while to configure an experiment, and grabbing a bunch and + # locking them up might result in having to wait a really long time + # to cancel a batch experiment that hasn't really tried to start yet! + # Thats would ne annoying to users, and we love our users, right? + # + # So, now your'e wondering what my selection criteria is? Well, its + # damn simplistic. I set the "started" datetime field each attempt, + # and I pick the batch_experiment with the oldest time, thereby cycling + # through in a "least recently attempted" manner. + # + DBquery("lock tables batch_experiments write"); + + $query_result = + DBquery("SELECT * FROM batch_experiments ". + "WHERE status='new' and canceled=0 ORDER BY started LIMIT 1"); + + if (! $query_result->numrows) { + DBquery("unlock tables"); + sleep(10); + next; + } + my %row = $query_result->fetchhash(); + + # + # Set the configuring flag right away so killbatchexp won't see them + # in a "new" state. Might as well set the started time to ensure that + # it goes to the end of the line. + # + # Local vars! + my $eid = $row{'eid'}; + my $pid = $row{'pid'}; + my $now = `date '+20%y-%m-%d %H:%M:%S'`; + + DBquery("update batch_experiments set status='configuring', ". + "started='$now' where eid='$eid' and pid='$pid'"); + + DBquery("unlock tables"); + + runexp(%row); + sleep(300); +} + +# +# The guts of running a single experiment. +# +sub runexp($) +{ + my(%exphash) = @_; + my($uid, $gid, $row); + + # Global vars + $eid = $exphash{'eid'}; + $pid = $exphash{'pid'}; + + my $creator = $exphash{'creator_uid'}; + my $longname = $exphash{'name'}; + + print STDOUT "Trying to start experiment $eid in project $pid\n"; + + # + # Start up a child to run the guts. The parent waits. If the + # experiment configures okay, the parent can return to try something + # new, while the child is going to hang out and wait for all the nodes + # to report exit status, or for the cancel bit to get set. + # + $childpid = fork(); + if ($childpid) { + waitpid($childpid, 0); + my $status = $?; + + return; + } + + # global var + $dirname = "$batchdir/$pid-$eid"; + my $nsfile = "$dirname/$eid.ns"; + + # + # Get some user information. + # + $query_result = + $DB->query("SELECT usr_name,usr_email from users ". + "WHERE uid='$creator'"); + + if (! $query_result || + $query_result->numrows != 1) { + fatal("DB Error getting user information for uid $creator\n"); + } + @row = $query_result->fetchrow_array(); + $user_name = $row[0]; + $user_email = $row[1]; + + # + # Figure out the unix uid/gid that the experiment configuration is + # going to run as. + # + (undef,undef,$uid) = getpwnam($creator) or + fatal("No such user $creator"); + (undef,undef,$gid) = getgrnam($pid) or + fatal("No such group $pid"); + + $EGID = $GID = $gid; + $EUID = $UID = $uid; + + # + # Create a temporary name for a log file and open it up. + # + $logname = `mktemp /tmp/start-batch-$pid-$eid.XXXXXX`; + + # Note different taint check (allow /). + if ($logname =~ /^([-\@\w.\/]+)$/) { + $logname = $1; + } else { + die "Bad data in $logname"; + } + openlog($logname); + + # + # Insert an experiment record for startexp. + # + my $rightnow = `date '+20%y-%m-%d %H:%M:%S'`; + DBquery("insert into experiments ". + "(eid, pid, expt_created, expt_name, ". + "expt_head_uid, expt_start, expt_ready, batchmode) ". + "VALUES ('$eid', '$pid', '$rightnow', '$longname', ". + "'$creator', '$rightnow', 0, 1)"); + + # + # Try to start the experiment. If it fails, the experiment is gone. + # + system("$startexp -b $pid $eid $nsfile"); + my $exit_status = $? >> 8; + my $running = 1; + if ($exit_status) { + $running = 0; + } + + # + # Look for cancelation. + # + $query_result = + DBquery("select canceled from batch_experiments ". + "where eid='$eid' and pid='$pid'"); + + @row = $query_result->fetchrow_array(); + my $canceled = $row[0]; + + # + # If canceled and the experiment got running, need to tear it down + # and tell the owner about it. + # + if ($canceled) { + cancel_batch($running); + exit(0); + } + + # + # If the configuration failed, then send email for now. This + # part needs work. We have to reset the state to "new" so that + # it will be retried again later. + # + if (! $running) { + DBquery("update batch_experiments set status='new' ". + "where eid='$eid' and pid='$pid'"); + + fatal("Could not configure Batch Mode experiment $pid/$eid"); + } + + # + # Well, it configured! Lets set it state to running. + # + DBquery("update batch_experiments set status='running' ". + "where eid='$eid' and pid='$pid'"); + + email_status("Batch Mode experiment $pid/$eid is now running!\n". + "Please consult the Web interface to see how its doing\n"); + + # + # Now loop, periodically looking for a change in the status of the + # nodes, or for a cancelation request. + # + while (1) { + $query_result = + DBquery("select canceled from batch_experiments ". + "where eid='$eid' and pid='$pid'"); + + if ($query_result->numrows != 1) { + # + # Jeez, something really went wrong! + # + fatal("Batch Mode record for $pid/$eid is gone! HELP ME!"); + } + + @row = $query_result->fetchrow_array(); + + if ($row[0]) { + cancel_batch(1); + exit(0); + } + + $query_result = + DBquery("SELECT startstatus FROM nodes LEFT JOIN reserved ". + "ON nodes.node_id=reserved.node_id ". + "WHERE reserved.eid='$eid' and reserved.pid='$pid'"); + + # + # Look to see if any nodes yet to report status. If so, spin again. + # + my $done = 1; + for ($i = 0; $i < $query_result->numrows; $i++) { + @row = $query_result->fetchrow_array(); + + if ($row[0] eq "none") { + $done = 0; + } + } + if ($done) { + last; + } + + sleep(15); + } + + # + # Yippie! Tear it down and send email. Need to look for failures + # in the teardown! + # + system("$endexp -b $pid $eid"); + DBquery("DELETE from batch_experiments WHERE eid='$eid' and pid='$pid'"); + system("rm -rf $dirname"); + email_status("Batch Mode experiment $pid/$eid has finished!\n"); + + # + # Child must exit! + # + exit(0); +} + +sub DBquery($) +{ + my($query) = $_[0]; + + $query_result = $DB->query($query); + + if (! $query_result) { + fatal("DB Error: $query"); + } + + return $query_result; +} + +# +# Start up a child, and set its descriptors talking to a log file. +# +sub openlog($) +{ + my($logname) = $_[0]; + + # + # We have to disconnect from the caller by redirecting both STDIN and + # STDOUT away from the pipe. Otherwise the caller (the web server) will + # continue to wait even though the parent has exited. + # + open(STDIN, "< /dev/null") or + fatal("opening /dev/null for STDIN: $!"); + + open(STDERR, ">> $logname") or + fatal("opening $logname for STDERR: $!"); + open(STDOUT, ">> $logname") or + fatal("opening $logname for STDOUT: $!"); + + return 0; +} + +sub fatal() +{ + my($mesg) = $_[0]; + + print STDOUT "$mesg\n"; + + # + # Send a message to the testbed list. Append the logfile if it got + # that far. + # + open(MAIL, "| /usr/bin/mail ". + "-s \"TESTBED: Batch Mode Failure $pid/$eid\" ". + "-c $TBOPS \"$user_name <$user_email>\" >/dev/null 2>&1") + or die "Cannot start mail program: $!"; + + print MAIL $mesg; + + if (defined($logname) && open(IN, "$logname")) { + print MAIL "\n\n---------\n\n"; + + while (<IN>) { + print MAIL "$_"; + } + close(IN); + unlink("$logname"); + } + close(MAIL); + + exit(-1); +} + +sub cancel_batch($) +{ + my($running) = $_[0]; + + if ($running) { + system("$endexp -b $pid $eid"); + } + + DBquery("DELETE from batch_experiments WHERE eid='$eid' and pid='$pid'"); + + open(MAIL, "| /usr/bin/mail ". + "-s \"TESTBED: Batch Mode Cancelation $pid/$eid\" ". + "-c $TBOPS \"$user_name <$user_email>\" >/dev/null 2>&1") + or die "Cannot start mail program: $!"; + + print MAIL + "Your Batch Mode experiment has been canceled. You may now\n". + "reuse the experiement name\n\n"; + + if (defined($logname) && open(IN, "$logname")) { + print MAIL "\n\n---------\n\n"; + + while (<IN>) { + print MAIL "$_"; + } + close(IN); + unlink("$logname"); + } + close(MAIL); + + # + # And kill the batch directory. + # + system("rm -rf $dirname"); +} + +sub email_status($) +{ + my($mesg) = $_[0]; + + print STDOUT "$mesg\n"; + + open(MAIL, "| /usr/bin/mail ". + "-s \"TESTBED: Batch Mode Experiment Status $pid/$eid\" ". + "-c $TBOPS \"$user_name <$user_email>\" >/dev/null 2>&1") + or die "Cannot start mail program: $!"; + + print MAIL $mesg; + + if (defined($logname) && open(IN, "$logname")) { + print MAIL "\n\n---------\n\n"; + + while (<IN>) { + print MAIL "$_"; + } + close(IN); + } + close(MAIL); +} + +# +# Become a daemon. +# +sub daemonize() +{ + my $mypid = fork(); + if ($mypid) { + exit(0); + } + + # + # We have to disconnect from the caller by redirecting both STDIN and + # STDOUT away from the pipe. Otherwise the caller will continue to wait + # even though the parent has exited. + # + open(STDIN, "< /dev/null") or + die("opening /dev/null for STDIN: $!"); + + # + # Open the batch log and start writing to it. + # + open(STDERR, ">> $batchlog") or die("opening $batchlog for STDERR: $!"); + open(STDOUT, ">> $batchlog") or die("opening $batchlog for STDOUT: $!"); + + return 0; +} diff --git a/tbsetup/batchexp.in b/tbsetup/batchexp.in new file mode 100755 index 0000000000000000000000000000000000000000..0bdff47ebc44365d1fbab47c52e5b2a2de67b802 --- /dev/null +++ b/tbsetup/batchexp.in @@ -0,0 +1,292 @@ +#!/usr/bin/perl -wT +use English; +use Getopt::Std; + +# +# Create a batch experiment. +# +# usage: batchexp <batchfile> +# +sub usage() +{ + print STDOUT "Usage: batchexp <batchfile>\n"; + exit(-1); +} +my $optlist = ""; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $DBNAME = "@TBDBNAME@"; +my $TBOPS = "@TBOPSEMAIL@"; + +my $tbbindir = "$TB/bin/"; +my $batchdir = "$TB/batch"; +my $projroot = "/proj"; +my $dirname; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Untaint the path +# +$ENV{'PATH'} = "/bin:/usr/bin:$TB/libexec:$TB/libexec/ir". + ":$TB/libexec/ns2ir:$TB/sbin:$TB/bin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +$TBIRLIB = "$TB/lib/ir"; +push(@INC,$TBIRLIB); +require libir; + +# +# Parse command arguments. Once we return from getopts, all that should +# left are the required arguments. +# +%options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (@ARGV != 1) { + usage(); +} +my $tempfile = $ARGV[0]; + +# +# Untaint the arguments. +# +# Note different taint check (allow /). +if ($tempfile =~ /^([-\@\w.\/]+)$/) { + $tempfile = $1; +} +else { + fatal("Tainted argument $tempfile"); +} + +# +# Set up for querying the database. +# +use Mysql; +my $DB = Mysql->connect("localhost", $DBNAME, "script", "none"); + +# +# Parse the batchfile. +# +my $eid; +my $pid; +my $longname; +my $expires; +my $webnsfile; + +parse_batchfile($tempfile) or + fatal("Could not parse batchfile $tempfile"); + +# +# Sanity check a few things. +# +if (!defined($eid) || !defined($pid) || !defined($longname) || + !defined($expires) || !defined($webnsfile)) { + fatal("Batchfile is incomplete!"); +} +$nsfile = "$eid.ns"; +$irfile = "$eid.ir"; + +# +# Create a subdir in the batch directory to work in. +# +$dirname = "$batchdir/$pid-$eid"; + +mkdir($dirname, 0775) or + fatal("Could not mkdir $dirname"); + +chdir($dirname) or + fatal("Could not chdir to $dirname"); + +# +# Copy in the batch file. Web script is responsible for removing the +# original. +# +if (system("/bin/cp", "$tempfile", "batchfile")) { + fatal("Could not copy $tempfile to $dirname"); +} + +# +# Now a bunch of DB checks. +# +# First off, get some user information. +# +$query_result = + DBquery("SELECT uid,usr_name,usr_email from users ". + "WHERE unix_uid='$EUID'"); + +if ($query_result->numrows < 1) { + fatal("Go Away! You do not exist in the Emulab Database."); +} + +@row = $query_result->fetchrow_array(); +$uid = $row[0]; +$user_name = $row[1]; +$user_email = $row[2]; + +# +# Make sure UID is allowed to create experiments in this project. +# +$query_result = + DBquery("SELECT trust from proj_memb WHERE uid='$uid' and pid='$pid'"); + +if ($query_result->numrows == 0) { + fatal("Go Away! You are not a member of project $pid!"); +} + +@row = $query_result->fetchrow_array(); +$trust = $row[0]; + +if ($trust ne "local_root" && + $trust ne "group_root") { + fatal("Go Away! You are not a trusted member of project $pid!"); +} + +# +# The pid/eid pair has to be unique. LOCKING! +# +$query_result = + DBquery("SELECT * FROM experiments WHERE eid='$eid' and pid='$pid'"); + +if ($query_result->numrows) { + fatal("Experiment $eid in project $pid already exists!"); +} + +$query_result = + DBquery("SELECT * FROM batch_experiments WHERE eid='$eid' and pid='$pid'"); + +if ($query_result->numrows) { + fatal("Batch experiment $eid in project $pid already exists!"); +} + +# +# Now we can get the NS file! +# +if (system("/bin/cp", "$webnsfile", "$nsfile")) { + fatal("Could not copy $webnsfile to $dirname/$nsfile"); +} + +# +# Do a firstcut parse on the NS file, converting it to IR format. This +# operates as a syntax check on the NS file, so we can kick back bad NS +# files now instead of later. It also means we don't need the NS file after +# this. +# +# XXX This is copied from tbprerun. +# +$tbcmdfile = "tbcmds"; +$id = "$pid-$eid"; + +if (system("parse.tcl $id $nsfile $irfile") != 0) { + fatal("NS Parse failed!"); +} +if (system("extract_tb $nsfile $tbcmdfile") != 0) { + fatal("NS extract_tb pass failed!"); +} +if (system("postparse $tbcmdfile $irfile") != 0) { + fatal("NS postparse pass failed!"); +} + +# +# Figure out what resources are needed so the batch daemon can make an +# informed decision about whether to even try. +# +$pcs = 0; +$sharks = 0; + +&ir_read($irfile); +foreach my $foo (split("\n", &ir_get("/topology/nodes"))) { + ($node,$type) = split(' ', $foo); + if ($type eq "pc") { + $pcs++; + } + if ($type eq "sh") { + $sharks++; + } +} + +# +# Gen up the creation time. +# +$created = `date '+%Y:%m:%d %H:%M:%S'`; + +# +# Insert the record. We leave this to very last cause the batch daemon +# is looking for batch experiments to run. Easy race avoidance. +# +DBquery("INSERT INTO batch_experiments ". + "(eid, pid, created, started, expires, ". + " name, creator_uid, numpcs, numsharks, status) ". + "VALUES ('$eid', '$pid', '$created', '$created', '$expires', ". + "'$longname', '$uid', $pcs, $sharks, 'new')"); + +exit 0; + +sub fatal($) +{ + my($mesg) = $_[0]; + + print STDOUT "$mesg\n"; + print STDOUT "Cleaning up ...\n"; + +# system("/bin/rm", "-rf", "$dirname"); + exit(-1); +} + +# +# Open up the batch file and parse it. +# +sub parse_batchfile() +{ + my($batchfile) = $_[0]; + + if (! open(BATCH, "$batchfile")) { + print STDERR "Could not open $batchfile\n"; + return 0; + } + + while (<BATCH>) { + if ($_ =~ /^EID:\s+([-\@\w.]*)/) { + $eid = $1; + next; + } + if ($_ =~ /^PID:\s+([-\@\w.]*)/) { + $pid = $1; + next; + } + if ($_ =~ /^name:\s+([-\@\w.]*)/) { + $longname = $1; + next; + } + if ($_ =~ /^expires:\s+([-\@\w.: ]*)$/) { + $expires = $1; + next; + } + if ($_ =~ /^nsfile:\s+([-\@\w.\/]*)/) { + $webnsfile = $1; + next; + } + } + close(BATCH); + return 1; +} + +sub DBquery() +{ + my($query) = $_[0]; + + $query_result = $DB->query($query); + + if (! $query_result) { + fatal("DB Error: $query"); + } + + return $query_result; +} diff --git a/tbsetup/killbatchexp.in b/tbsetup/killbatchexp.in new file mode 100644 index 0000000000000000000000000000000000000000..4dc9e313cb25aef70d0a380d77caf483b25bebe9 --- /dev/null +++ b/tbsetup/killbatchexp.in @@ -0,0 +1,168 @@ +#!/usr/bin/perl -wT +use English; +use Getopt::Std; + +# +# Create a batch experiment. +# +# usage: killbatchexp $pid $eid +# +sub usage() +{ + print STDOUT "Usage: killbatchexp $pid $eid\n"; + exit(-1); +} +my $optlist = ""; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $DBNAME = "@TBDBNAME@"; +my $TBOPS = "@TBOPSEMAIL@"; + +my $tbbindir = "$TB/bin/"; +my $batchdir = "$TB/batch"; +my $projroot = "/proj"; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Untaint the path +# +$ENV{'PATH'} = "/bin:/usr/bin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Parse command arguments. Once we return from getopts, all that should +# be left are the required arguments. +# +%options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (@ARGV != 2) { + usage(); +} +my $pid = $ARGV[0]; +my $eid = $ARGV[1]; + +# +# Untaint the arguments. +# +if ($pid =~ /^([-\@\w.]+)$/) { + $pid = $1; +} +if ($eid =~ /^([-\@\w.]+)$/) { + $eid = $1; +} + +# +# Set up for querying the database. +# +use Mysql; +my $DB = Mysql->connect("localhost", $DBNAME, "script", "none"); + +# +# This is where we currently hold the batch goo. +# +$dirname = "$batchdir/$pid-$eid"; + +# +# Need to lock the table for this. We could avoid the locking if we just +# set the canceled bit and let the batch_daemon clean things up, but that +# introduces needless (and annoying) delay when killing a batch experiment +# that is not even running. See corresponding lock in the batch_daemon. +# +DBquery("lock tables batch_experiments write"); + +# +# Set the canceled state right away. This will prevent the batch_daemon +# from trying to run it. It might already be running, but thats okay. +# +DBquery("UPDATE batch_experiments set canceled=1 ". + "WHERE eid='$eid' and pid='$pid'"); + +# +# Now its safe to look at the state. If its in the "new" state, then +# all we need to do is kill the record and the directory, since the +# batch daemon will not bother with it once the canceled bit is set. +# +$query_result = + DBquery("SELECT status from batch_experiments ". + "WHERE eid='$eid' and pid='$pid'"); + +DBquery("unlock tables"); + +@row = $query_result->fetchrow_array(); +$state = $row[0]; + +if ($state ne "new") { + # + # Daemon does the rest ... + # + print STDOUT + "Batch Experiment $eid in project $pid is running on the testbed\n". + "You will receive email notification when the experiment is torn\n". + "down and you can reuse the experiment name\n"; + + # + # exit status is special. Tells the caller that cancelation is pending. + # The web script will say something useful. + # + exit(1); +} + +# +# Delete the DB record. LOCKING! +# +DBquery("DELETE from batch_experiments WHERE eid='$eid' and pid='$pid'"); + +# +# And kill the directory. +# +system("rm -rf $dirname"); + +# +# Lets not bother with an email message. Just print out something nice +# and tell the caller (the php script) to say something nice too). +# +print STDOUT + "Batch Experiment $eid in project $pid has been canceled!\n"; + +exit(0); + +sub fatal($) +{ + my($mesg) = $_[0]; + + print STDOUT "$mesg\n"; + + # + # Send a message to the testbed list + # + open(MAIL, "| /usr/bin/mail ". + "-s \"TESTBED: Batch Mode Cancelation Failure $pid/$eid\" ". + "$TBOPS >/dev/null 2>&1") + or die "Cannot start mail program: $!"; + + print MAIL $mesg; + close(MAIL); + exit(-1); +} + +sub DBquery() +{ + my($query) = $_[0]; + + $query_result = $DB->query($query); + + if (! $query_result) { + fatal("DB Error: $query"); + } + + return $query_result; +} diff --git a/tbsetup/mkexpdir b/tbsetup/mkexpdir index f947c8e84bde1ff5ecbd4c5739b0a663044dd4f2..5b10986b543811649734d260f96caef9161d1fb1 100755 --- a/tbsetup/mkexpdir +++ b/tbsetup/mkexpdir @@ -51,18 +51,18 @@ if (! chdir($expdir)) { } if (! mkdir($eid, 0770)) { - print STDOUT "Could not mkdir $eid in $piddir: $!\n"; + print STDOUT "Could not mkdir $eid in $expdir: $!\n"; exit(-1); } if (! chmod(0770, "$eid")) { - print STDOUT "Could not chmod $eid to 0770 in $piddir: $!\n"; + print STDOUT "Could not chmod $eid to 0770 in $expdir: $!\n"; rmdir($eid); exit(-1); } if (! chdir($eid)) { - print STDOUT "Could not chdir to $eid in $piddir: $!\n"; + print STDOUT "Could not chdir to $eid in $expdir: $!\n"; rmdir($eid); exit(-1); } diff --git a/tbsetup/startexp.in b/tbsetup/startexp.in index 40d1efb31d58cee09c3c252208a6b1440351bd85..73cf9118690a831cc774affe4d56f1ec555c0cb5 100755 --- a/tbsetup/startexp.in +++ b/tbsetup/startexp.in @@ -78,9 +78,12 @@ if ($eid =~ /^([-\@\w.]+)$/) { $eid = $1; } # Note different taint check (allow /). -if ($tempfile =~ /^([\/-\@\w.]+)$/) { +if ($tempfile =~ /^([-\w.\/]+)$/) { $tempfile = $1; } +else { + die("Tainted tempfile name: $tempfile"); +} my $piddir = "$projroot/$pid"; my $expdir = "$piddir/exp"; @@ -360,7 +363,7 @@ sub fatal() # open(MAIL, "| /usr/bin/mail ". "-s \"TESTBED: Experiment Configure Failure $pid/$eid\" ". - "$TBOPS \"$user_name <$user_email>\" >/dev/null 2>&1") + "-c $TBOPS \"$user_name <$user_email>\" >/dev/null 2>&1") or die "Cannot start mail program: $!"; print MAIL $mesg; diff --git a/tbsetup/webbatchexp.in b/tbsetup/webbatchexp.in new file mode 100644 index 0000000000000000000000000000000000000000..962f78137b2d742d27fcbb0f8190c6c0450a771f --- /dev/null +++ b/tbsetup/webbatchexp.in @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w +use English; + +# +# This gets invoked from the Web interface. Simply a wrapper. +# +# usage: webbatchexp arguments ... +# + +# +# Configure variables +# +my $TB = "@prefix@"; + +# +# Run the real thing, and never return. +# +exec "$TB/bin/batchexp", @ARGV; + +die("webbatchexp: Could not exec batchexp: $!"); diff --git a/tbsetup/webkillbatchexp.in b/tbsetup/webkillbatchexp.in new file mode 100644 index 0000000000000000000000000000000000000000..208c829bf094cf249de4bd37f38cbf6c44acf3f1 --- /dev/null +++ b/tbsetup/webkillbatchexp.in @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w +use English; + +# +# This gets invoked from the Web interface. Simply a wrapper. +# +# usage: webkillbatchexp arguments ... +# + +# +# Configure variables +# +my $TB = "@prefix@"; + +# +# Run the real thing, and never return. +# +exec "$TB/bin/killbatchexp", @ARGV; + +die("webkillbatchexp: Could not exec killbatchexp: $!"); diff --git a/www/batchexp.php3 b/www/batchexp.php3 new file mode 100644 index 0000000000000000000000000000000000000000..d09ae167f10217c95e7af5052b8f56141c4779b7 --- /dev/null +++ b/www/batchexp.php3 @@ -0,0 +1,186 @@ +<?php +include("defs.php3"); + +# +# Standard Testbed Header +# +PAGEHEADER("Create a Batch Mode Experiment"); + +$mydebug = 0; + +# +# First off, sanity check the form to make sure all the required fields +# were provided. I do this on a per field basis so that we can be +# informative. Be sure to correlate these checks with any changes made to +# the project form. +# +if (!isset($uid) || + strcmp($uid, "") == 0) { + FORMERROR("Username"); +} +if (!isset($exp_pid) || + strcmp($exp_pid, "") == 0) { + FORMERROR("Select Project"); +} +if (!isset($exp_id) || + strcmp($exp_id, "") == 0) { + FORMERROR("Experiment Name (short)"); +} +if (!isset($exp_name) || + strcmp($exp_name, "") == 0) { + FORMERROR("Experiment Name (long)"); +} + +# +# Only known and logged in users can begin experiments. Name came in as +# a POST var. +# +LOGGEDINORDIE($uid); + +# +# Database limits +# +if (strlen($exp_id) > $TBDB_EIDLEN) { + USERERROR("The experiment name \"$exp_id\" is too long! ". + "Please select another.", 1); +} + +# +# Certain of these values must be escaped or otherwise sanitized. +# +$exp_name = addslashes($exp_name); + +# +# Must provide an NS file! +# +$nonsfile = 0; +if (!isset($exp_nsfile) || + strcmp($exp_nsfile, "") == 0 || + strcmp($exp_nsfile, "none") == 0) { + + USERERROR("The NS file '$exp_nsfile_name' does not appear to be a ". + "valid filename. Please go back and try again.", 1); +} + +# +# Make sure the PID/EID tuple does not already exist in the database. +# It may not exist in either the current experiments list, or the +# batch experiments list. +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT eid FROM experiments ". + "WHERE eid=\"$exp_id\" and pid=\"$exp_pid\""); +if ($row = mysql_fetch_row($query_result)) { + USERERROR("The experiment name \"$exp_id\" you have chosen is already ". + "in use in project $exp_pid. Please select another.", 1); +} + +$query_result = mysql_db_query($TBDBNAME, + "SELECT eid FROM batch_experiments ". + "WHERE eid=\"$exp_id\" and pid=\"$exp_pid\""); +if ($row = mysql_fetch_row($query_result)) { + USERERROR("The experiment name \"$exp_id\" you have chosen is already ". + "in use in project $exp_pid. Please select another.", 1); +} + +# +# Next, is this person a member of the project specified, and is the trust +# equal to group or local root? +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT * FROM proj_memb WHERE pid=\"$exp_pid\" and uid=\"$uid\""); +if (($row = mysql_fetch_array($query_result)) == 0) { + USERERROR("You are not a member of Project $exp_pid, so you cannot begin ". + "an experiment in that project.", 1); +} +$trust = $row[trust]; +if (strcmp($trust, "group_root") && strcmp($trust, "local_root")) { + USERERROR("You are not group or local root in Project $exp_pid, so you ". + "cannot begin an experiment in that project.", 1); +} + +# +# We need the unix gid for the project for running the scripts below. +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT unix_gid from projects where pid=\"$exp_pid\""); +if (($row = mysql_fetch_row($query_result)) == 0) { + TBERROR("Database Error: Getting GID for project $exp_pid.", 1); +} +$gid = $row[0]; + +# +# Create a temporary file with the goo in it. +# +$tmpfname = tempnam( "/tmp", "batch-$pid-$eid" ); +$fp = fopen($tmpfname, "w"); +if (! $fp) { + TBERROR("Opening temporary file $tmpfname.", 1); +} + +# +# XXX The batchexp script parses this file, so if you change something +# here, go change it there too! +# +fputs($fp, "EID: $exp_id\n"); +fputs($fp, "PID: $exp_pid\n"); +fputs($fp, "name: $exp_name\n"); +fputs($fp, "expires: $exp_expires\n"); +fputs($fp, "nsfile: $exp_nsfile\n"); +fclose($fp); + +# +# XXX +# Set the permissions on the files so that the scripts can get to them. +# It is owned by nobody, and most likely protected. This leaves the +# script open for a short time. A potential security hazard we should +# deal with at some point, but since the files are on paper:/tmp, its +# a minor problem. +# +chmod($tmpfname, 0666); +chmod($exp_nsfile, 0666); + +echo "<center><br>"; +echo "<h3>Starting batch experiment setup. Please wait a moment ... + </center><br><br> + </h3>"; + +flush(); + +# +# Run the scripts. We use a script wrapper to deal with changing +# to the proper directory and to keep most of these details out +# of this. +# +$output = array(); +$retval = 0; +$last = time(); + +$result = exec("$TBSUEXEC_PATH $uid $gid webbatchexp $tmpfname", + $output, $retval); + +if ($retval) { + echo "<br><br><h2> + Setup Failure($retval): Output as follows: + </h2> + <br> + <XMP>\n"; + for ($i = 0; $i < count($output); $i++) { + echo "$output[$i]\n"; + } + echo "</XMP>\n"; + + die(""); +} + +echo "<center><br>"; +echo "<h2>Experiment `$exp_id' in project `$exp_pid' has been batched!<br><br> + You will be notified via email when the experiment has been run<br>"; +echo "</h2>"; +echo "</center>\n"; + +# +# Standard Testbed Footer +# +PAGEFOOTER(); +?> diff --git a/www/batchexp_form.php3 b/www/batchexp_form.php3 new file mode 100644 index 0000000000000000000000000000000000000000..1d77b7bf3c7e6f73a77d5e6bca84812a111c4ebc --- /dev/null +++ b/www/batchexp_form.php3 @@ -0,0 +1,135 @@ +<?php +include("defs.php3"); + +# +# Standard Testbed Header +# +PAGEHEADER("Create a Batch Experiment"); + +# +# Only known and logged in users can begin experiments. +# +$uid = GETLOGIN(); +LOGGEDINORDIE($uid); + +# +# See what projects the uid is a member of. Must be at least one! +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT pid FROM proj_memb WHERE uid=\"$uid\" ". + "and (trust='local_root' or trust='group_root')"); + +if (! $query_result) { + $err = mysql_error(); + TBERROR("Database Error finding project membership: $uid: $err\n", 1); +} +if (mysql_num_rows($query_result) == 0) { + USERERROR("You do not appear to be a member of any Projects in which ". + "you have permission (root) to create new experiments.", 1); +} + +?> +<table align="center" border="1"> +<tr> + <td align="center" colspan="2"> + <h1>Create a new Batch Mode Experiment on the Testbed</h1> + </td> +</tr> + +<tr> + <td align="center" colspan="3"> + <em>(Fields marked with * are required)</em> + </td> +</tr> + +<?php +echo "<form enctype=\"multipart/form-data\" + action=\"batchexp.php3\" method=\"post\">\n"; + +# +# UID to feed back. +# +echo "<tr> + <td>*Username:</td> + <td class=\"left\"> + <input type=\"readonly\" name=\"uid\" value=\"$uid\"></td> + </tr>\n"; + +# +# Select Project +# +echo "<tr> + <td>*Select Project:</td>"; +echo " <td><select name=\"exp_pid\">"; + while ($row = mysql_fetch_array($query_result)) { + $project = $row[pid]; + echo "<option value=\"$project\">$project</option>\n"; + } +echo " </select>"; +echo " </td> + </tr>\n"; + +# +# Experiment ID and Long Name: +# +# Note DB max length. +# +echo "<tr> + <td>*Name (no blanks):</td> + <td><input type=\"text\" name=\"exp_id\" + size=$TBDB_EIDLEN maxlength=$TBDB_EIDLEN> + </td> + </tr>\n"; + +echo "<tr> + <td>*Long Name:</td> + <td><input type=\"text\" name=\"exp_name\" size=\"40\"> + </td> + </tr>\n"; + + +# +# NS file upload. +# +echo "<tr> + <td>*Your NS file (20K max):</td> + <td><input type=\"hidden\" name=\"MAX_FILE_SIZE\" value=\"20000\"> + <input type=\"file\" name=\"exp_nsfile\" size=\"30\"> + </td> + </tr>\n"; + + +# +# Expires, Starts, Ends. Also the hidden Created field. +# +$utime = time(); +$year = date("Y", $utime); +$month = date("m", $utime); +$thismonth = $month++; +if ($month > 12) { + $month -= 12; + $month = "0".$month; +} +$rest = date("d H:i:s", $utime); + +echo "<tr> + <td>Expiration date:</td> + <td><input type=\"text\" value=\"$year:$month:$rest\" + name=\"exp_expires\"></td> + </tr>\n"; + +?> + +<tr> + <td align="center" colspan="2"> + <b><input type="submit" value="Submit"></b></td> +</tr> +</form> +</table> + +<?php +# +# Standard Testbed Footer +# +PAGEFOOTER(); +?> diff --git a/www/beginexp_process.php3 b/www/beginexp_process.php3 index 763ac6784c4412f8d6416f93bb4c5df444d4d341..4ddbba237c8d46832a34cf854a50a454d6f6a1f4 100644 --- a/www/beginexp_process.php3 +++ b/www/beginexp_process.php3 @@ -53,11 +53,10 @@ if (strlen($exp_id) > $TBDB_EIDLEN) { # Certain of these values must be escaped or otherwise sanitized. # $exp_name = addslashes($exp_name); - + # -# I'm going to allow shell experiments to be created (No NS file). +# I am not going to allow shell experiments to be created (No NS file). # -$nonsfile = 0; if (!isset($exp_nsfile) || strcmp($exp_nsfile, "") == 0 || strcmp($exp_nsfile, "none") == 0) { @@ -69,7 +68,7 @@ if (!isset($exp_nsfile) || USERERROR("The NS file '$exp_nsfile_name' does not appear to be a ". "valid filename. Please go back and try again.", 1); } - + $nonsfile = 1; } @@ -84,6 +83,15 @@ if ($row = mysql_fetch_row($query_result)) { "in use in project $exp_pid. Please select another.", 1); } +$query_result = mysql_db_query($TBDBNAME, + "SELECT eid FROM batch_experiments ". + "WHERE eid=\"$exp_id\" and pid=\"$exp_pid\""); +if ($row = mysql_fetch_row($query_result)) { + USERERROR("The experiment name \"$exp_id\" you have chosen is a current ". + "batch mode experiment in project $exp_pid. ". + "Please select another name.", 1); +} + # # Next, is this person a member of the project specified, and is the trust # equal to group or local root? diff --git a/www/defs.php3.in b/www/defs.php3.in index d3550cc8bf2ec2be2df66b742d465d375a0ca52c..e700b0430cadcce6726345ea0253b33ee2192ebc 100644 --- a/www/defs.php3.in +++ b/www/defs.php3.in @@ -28,7 +28,7 @@ $TBNSSUBDIR = "nsdir"; $TBAUTHCOOKIE = "HashCookie"; $TBNAMECOOKIE = "MyUidCookie"; -$TBAUTHTIMEOUT = 10800; +$TBAUTHTIMEOUT = 21600; # # Database constants and the like. diff --git a/www/endbatch.php3 b/www/endbatch.php3 new file mode 100644 index 0000000000000000000000000000000000000000..c235c046d42f2091968ae3a72fc59e9bbcda52be --- /dev/null +++ b/www/endbatch.php3 @@ -0,0 +1,160 @@ +<?php +include("defs.php3"); + +# +# Standard Testbed Header +# +PAGEHEADER("Cancel Batch Mode Experiment"); + +# +# Only known and logged in users can end experiments. +# +$uid = GETLOGIN(); +LOGGEDINORDIE($uid); +$isadmin = ISADMIN($uid); + +# +# Must provide the EID! +# +if (!isset($exp_pideid) || + strcmp($exp_pideid, "") == 0) { + USERERROR("The experiment ID was not provided!", 1); +} + +# +# First get the project (PID) from the form parameter, which came in +# as <pid>$$<eid>. +# +$exp_eid = strstr($exp_pideid, "\$\$"); +$exp_eid = substr($exp_eid, 2); +$exp_pid = substr($exp_pideid, 0, strpos($exp_pideid, "\$\$", 0)); + +# +# Check to make sure thats this is a valid PID/EID tuple. +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT * FROM batch_experiments WHERE ". + "eid=\"$exp_eid\" and pid=\"$exp_pid\""); +if (mysql_num_rows($query_result) == 0) { + USERERROR("The experiment $exp_eid is not a valid batch mode experiment ". + "in project $exp_pid.", 1); +} +$row = mysql_fetch_array($query_result); + +# +# Verify that this uid is a member of the project for the experiment +# being displayed, or is an admin type. +# +if (! $isadmin) { + $query_result = + mysql_db_query($TBDBNAME, + "SELECT pid FROM proj_memb ". + "WHERE uid=\"$uid\" and pid=\"$exp_pid\""); + + if (mysql_num_rows($query_result) == 0) { + USERERROR("You are not a member of Project $exp_pid for ". + "Experiment: $exp_eid.", 1); + } +} + +# +# 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) { + echo "<center><h2><br> + Batch Mode Experiment, Cancelation Canceled! + </h2></center>\n"; + + PAGEFOOTER(); + return; +} + +if (!$confirmed) { + echo "<center><h2><br> + Are you <b>REALLY</b> + sure you want to cancel Batch Mode Experiment '$exp_eid?' + </h2>\n"; + + echo "<form action=\"endbatch.php3\" method=\"post\">"; + echo "<input type=hidden name=exp_pideid value=\"$exp_pideid\">\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; +} + +# +# We need the unix gid for the project for running the scripts below. +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT unix_gid from projects where pid=\"$exp_pid\""); +if (($row = mysql_fetch_row($query_result)) == 0) { + TBERROR("Database Error: Getting GID for project $exp_pid.", 1); +} +$gid = $row[0]; + +# +# We run a wrapper script that does all the work of terminating the +# experiment. +# +# tbstopit <pid> <eid> +# +echo "<center><br>"; +echo "<h3>Starting Batch Mode Experiment Cancelation. Please wait a moment ... + </center><br><br> + </h3>"; + +flush(); + +# +# Run the scripts. We use a script wrapper to deal with changing +# to the proper directory and to keep some of these details out +# of this. +# +$output = array(); +$retval = 0; +$result = exec("$TBSUEXEC_PATH $uid $gid webkillbatchexp $exp_pid $exp_eid", + $output, $retval); + +if ($retval && $retval != 1) { + echo "<br><br><h2> + Cancelation Failure($retval): Output as follows: + </h2> + <br> + <XMP>\n"; + for ($i = 0; $i < count($output); $i++) { + echo "$output[$i]\n"; + } + echo "</XMP>\n"; + + die(""); +} + +echo "<center><h2><br>"; +# +# Exit status 0 means cancelation was immediate. +# Exit status 1 means the experiment was running, and will terminate later. +# +if ($retval) { + echo "Cancelation has started<br><br> + You will be notified via email when the process has completed, + and you can reuse the experiment name.<br><br> + This might take a few minutes. Please be patient.\n"; +} +else { + echo "Batchmode Experiment $exp_eid in project $exp_pid has + been canceled!\n"; +} +echo "</center></h2>\n"; + +# +# Standard Testbed Footer +# +PAGEFOOTER(); +?> diff --git a/www/endexp.php3 b/www/endexp.php3 index 62eefda5f8487f5f5f82148aa8109cf82230c1ed..749a5749b5e9e6a545cf3408f1a17081e06d0196 100644 --- a/www/endexp.php3 +++ b/www/endexp.php3 @@ -11,6 +11,7 @@ PAGEHEADER("Terminate Experiment"); # $uid = GETLOGIN(); LOGGEDINORDIE($uid); +$isadmin = ISADMIN($uid); # # Must provide the EID! @@ -68,15 +69,33 @@ if ($terminating) { "experiment has been torn down.", 1); } +# +# If this is a running batch mode experiment, then force user through the +# terminate batchmode path, to ensure that this is really what the person +# wanted to do. Its also easier for me. +# +$batchmode = $row[batchmode]; +if ($batchmode) { + USERERROR("The experiment $exp_eid is a batch mode experiment that ". + "is currently running on the testbed. If you really want to ". + "terminate this experiment, please go back and terminate it ". + "using the entry in the batch mode experiments listing.", 1); +} + # # Verify that this uid is a member of the project for the experiment -# being displayed. +# being displayed, or is an admin type. # -$query_result = mysql_db_query($TBDBNAME, - "SELECT pid FROM proj_memb WHERE uid=\"$uid\" and pid=\"$exp_pid\""); -if (mysql_num_rows($query_result) == 0) { - USERERROR("You are not a member of Project $exp_pid for ". - "Experiment: $exp_eid.", 1); +if (! $isadmin) { + $query_result = + mysql_db_query($TBDBNAME, + "SELECT pid FROM proj_memb ". + "WHERE uid=\"$uid\" and pid=\"$exp_pid\""); + + if (mysql_num_rows($query_result) == 0) { + USERERROR("You are not a member of Project $exp_pid for ". + "Experiment: $exp_eid.", 1); + } } # diff --git a/www/endexp_list.php3 b/www/endexp_list.php3 deleted file mode 100644 index 10300a13447d23f533a941dcaa8f93cd5b7e4c31..0000000000000000000000000000000000000000 --- a/www/endexp_list.php3 +++ /dev/null @@ -1,74 +0,0 @@ -<?php -include("defs.php3"); - -# -# Standard Testbed Header -# -PAGEHEADER("Terminate Experiment List"); - -# -# Only known and logged in users can end experiments. -# -$uid = GETLOGIN(); -LOGGEDINORDIE($uid); - -$isadmin = ISADMIN($uid); - -# -# Lets see if the user is even part of any experiements -# -if ($isadmin) { - $query_result = mysql_db_query($TBDBNAME, - "select pid,eid,expt_name from experiments ". - "order by pid,eid,expt_name"); -} -else { - $query_result = mysql_db_query($TBDBNAME, - "select e.pid,eid,expt_name from experiments as e ". - "left join proj_memb as p on p.pid=e.pid ". - "where p.uid='$uid' and (trust='local_root' or trust='group_root') ". - "order by e.pid,eid"); -} - -if (mysql_num_rows($query_result) == 0) { - USERERROR("There are no experiments running in any of the projects ". - "you are a member of.", 1); -} -?> - -<center> -<h1>Terminate Experiment Selection</h1> -<h2>Select an experiment from the list below.<br> -These are the experiments in the projects -you are a member of.</h2> -<table width="100%" border=2 cellpadding=0 cellspacing=2 align=center> -<tr> - <td>PID</td> - <td>EID</td> - <td align=center>Name</td> - <td align=center>Terminate</td> -</tr> - -<?php - -while ($row = mysql_fetch_array($query_result)) { - $pid = $row[pid]; - $eid = $row[eid]; - $name = $row[expt_name]; - - echo "<tr> - <td><A href='showproject.php3?pid=$pid'>$pid</A></td> - <td><A href='showexp.php3?exp_pideid=$pid\$\$$eid'>$eid</A></td> - <td>$name</td> - <td align=center><A href='endexp.php3?exp_pideid=$pid\$\$$eid'> - <img alt=\"o\" src=\"redball.gif\"></A></td> - </tr>\n"; -} - -echo "</table>\n"; - -# -# Standard Testbed Footer -# -PAGEFOOTER(); -?> diff --git a/www/faq.html b/www/faq.html index d719e9a6451c793a4e50f41554a6a181510dcac7..0db867c8fd0cde32e2eeead49538f7cfa76a1cbb 100644 --- a/www/faq.html +++ b/www/faq.html @@ -16,12 +16,31 @@ <ul> <li> <a href="#GS-1">How do I start a project?</a> <li> <a href="#GS-2">How do I join a project?</a> + <li> <a href="#GS-3">Can I be in more than one project?</a> + <li> <a href="#GS-4">Do I have a login account at Emulab?</a> </ul> <li> <a href="#UTT">Using the Testbed</a> <ul> <li> <a href="#UTT-1">Is there a tutorial?</a> <li> <a href="#UTT-2">Do I get root access on my nodes?</a> + <li> <a href="#UTT-3">Do my nodes have consoles I can look at?</a> + <li> <a href="#UTT-4">Where do I store files needed by my experiment?</a> + <li> <a href="#UTT-5">Are my nodes backed up (filesaved)?</a> + </ul> + +<li> <a href="#HDS">Hardware setup</a> + <ul> + <li> <a href="#HDS-1">How many nodes are there?</a> + <li> <a href="#HDS-2">How many ethernet cards are on each node?</a> + <li> <a href="#HDS-3">Can I do traffic shaping on my links?</a> + </ul> + +<li> <a href="#SWS">Software setup</a> + <ul> + <li> <a href="#SWS-1">What OS do the nodes run?</a> + <li> <a href="#SWS-2">Can I run my own OS?</a> + <li> <a href="#SWS-3">How do I load my OS on all my nodes?</a> </ul> </ul> diff --git a/www/index.php3 b/www/index.php3 index 43e1686b2937b5fe4e2aae53de3e75d617fd5ca5..0543f8c97f9eaaf4ba96814e4bdc7c88e9f410c6 100755 --- a/www/index.php3 +++ b/www/index.php3 @@ -86,10 +86,11 @@ echo "</head> if (isset($uid)) { echo "<hr>"; $query_result = mysql_db_query($TBDBNAME, - "SELECT status,admin FROM users WHERE uid='$uid'"); + "SELECT status,admin,stud FROM users WHERE uid='$uid'"); $row = mysql_fetch_row($query_result); $status = $row[0]; $admin = $row[1]; + $stud = $row[1]; # # See if group_root in any projects, not just the last one in the DB! @@ -125,10 +126,12 @@ if (isset($uid)) { Project Information</A><p>\n"; echo "<A href='beginexp_form.php3'> Begin an Experiment</A><p>\n"; - echo "<A href='endexp_list.php3'> - End an Experiment</A><p>\n"; echo "<A href='showexp_list.php3'> Experiment Information</A><p>\n"; + if ($stud) { + echo "<A href='batchexp_form.php3'> + Create a Batch Experiment</A><p>\n"; + } echo "<A href='modusr_form.php3'> Update user information</A><p>\n"; echo "<A href='reserved.php3'> diff --git a/www/showbatch.php3 b/www/showbatch.php3 new file mode 100644 index 0000000000000000000000000000000000000000..23531ec183edb29c24194d66794ea727985ce57b --- /dev/null +++ b/www/showbatch.php3 @@ -0,0 +1,139 @@ +<?php +include("defs.php3"); +include("showstuff.php3"); + +# +# Standard Testbed Header +# +PAGEHEADER("Show Batch Mode Experiment Information"); + +# +# Only known and logged in users can end experiments. +# +$uid = GETLOGIN(); +LOGGEDINORDIE($uid); + +$isadmin = ISADMIN($uid); + +# +# Verify form arguments. +# +if (!isset($exp_pideid) || + strcmp($exp_pideid, "") == 0) { + USERERROR("You must provide an experiment ID.", 1); +} + +# +# First get the project (PID) from the form parameter, which came in +# as <pid>$$<eid>. +# +$exp_eid = strstr($exp_pideid, "$$"); +$exp_eid = substr($exp_eid, 2); +$exp_pid = substr($exp_pideid, 0, strpos($exp_pideid, "$$", 0)); + +# +# Check to make sure thats this is a valid PID/EID tuple. +# +$query_result = mysql_db_query($TBDBNAME, + "SELECT * FROM batch_experiments WHERE ". + "eid=\"$exp_eid\" and pid=\"$exp_pid\""); +if (mysql_num_rows($query_result) == 0) { + USERERROR("The experiment $exp_eid is not a valid batch mode experiment ". + "in project $exp_pid.", 1); +} +$exprow = mysql_fetch_array($query_result); + +# +# Verify that this uid is a member of the project for the experiment +# being displayed. +# +if (!$isadmin) { + $query_result = mysql_db_query($TBDBNAME, + "SELECT pid FROM proj_memb WHERE uid=\"$uid\" and pid=\"$exp_pid\""); + if (mysql_num_rows($query_result) == 0) { + USERERROR("You are not a member of Project $exp_pid for ". + "Experiment: $exp_eid.", 1); + } +} + +echo "<center> + <h1>Batch Mode Experiment Information</h1> + <table align=center border=1>\n"; + +$created = $exprow[created]; +$expires = $exprow[expires]; +$longname = $exprow[name]; +$creator = $exprow[creator_uid]; +$numpcs = $exprow[numpcs]; +$numsharks = $exprow[numsharks]; +$status = $exprow[status]; + +# +# Generate the table. +# +echo "<tr> + <td>Name: </td> + <td class=\"left\">$exp_eid</td> + </tr>\n"; + +echo "<tr> + <td>Long Name: </td> + <td class=\"left\">$longname</td> + </tr>\n"; + +echo "<tr> + <td>Project: </td> + <td class=left> + <A href='showproject.php3?pid=$exp_pid'>$exp_pid</A></td> + </tr>\n"; + +echo "<tr> + <td>Experiment Head: </td> + <td class=\"left\"> + <A href='showuser.php3?target_uid=$creator'> + $creator</td> + </tr>\n"; + +echo "<tr> + <td>Created: </td> + <td class=\"left\">$created</td> + </tr>\n"; + +echo "<tr> + <td>Expires: </td> + <td class=\"left\">$expires</td> + </tr>\n"; + +echo "<tr> + <td>Extimated #PCs: </td> + <td class=\"left\">$numpcs</td> + </tr>\n"; + +echo "<tr> + <td>Extimated #Sharks: </td> + <td class=\"left\">$numsharks</td> + </tr>\n"; + +echo "<tr> + <td>Status: </td> + <td class=\"left\">$status</td> + </tr>\n"; + +echo "</table>\n"; + +# +# Dump experiment record if its currently running. +# +if (strcmp($status, "running") == 0) { + echo "<center> + <h1>Experiment Information</h1> + </center>\n"; + SHOWEXP($exp_pid, $exp_eid); + SHOWNODES($exp_pid, $exp_eid); +} + +# +# Standard Testbed Footer +# +PAGEFOOTER(); +?> diff --git a/www/showexp.php3 b/www/showexp.php3 index 187949081332362f07cc539180aa2170acb09583..fcdc9fdf1bd7d65b83f3314f07bdd8e5efa49b4a 100644 --- a/www/showexp.php3 +++ b/www/showexp.php3 @@ -41,7 +41,6 @@ if (mysql_num_rows($query_result) == 0) { USERERROR("The experiment $exp_eid is not a valid experiment ". "in project $exp_pid.", 1); } -$exprow = mysql_fetch_array($query_result); # # Verify that this uid is a member of the project for the experiment @@ -55,145 +54,20 @@ if (!$isadmin) { "Experiment: $exp_eid.", 1); } } -?> - -<center> -<h1>Experiment Information</h1> -<table align="center" border="1"> - -<?php - -$exp_expires = $exprow[expt_expires]; -$exp_name = $exprow[expt_name]; -$exp_created = $exprow[expt_created]; -$exp_start = $exprow[expt_start]; -$exp_end = $exprow[expt_end]; -$exp_created = $exprow[expt_created]; -$exp_head = $exprow[expt_head_uid]; # -# Generate the table. +# Dump experiment record. # -echo "<tr> - <td>Name: </td> - <td class=\"left\">$exp_eid</td> - </tr>\n"; - -echo "<tr> - <td>Long Name: </td> - <td class=\"left\">$exp_name</td> - </tr>\n"; - -echo "<tr> - <td>Project: </td> - <td class=\"left\">$exp_pid</td> - </tr>\n"; - -echo "<tr> - <td>Experiment Head: </td> - <td class=\"left\"> - <A href='showuser.php3?target_uid=$exp_head'> - $exp_head</td> - </tr>\n"; - -echo "<tr> - <td>Created: </td> - <td class=\"left\">$exp_created</td> - </tr>\n"; - -echo "<tr> - <td>Starts: </td> - <td class=\"left\">$exp_start</td> - </tr>\n"; - -echo "<tr> - <td>Ends: </td> - <td class=\"left\">$exp_end</td> - </tr>\n"; - -echo "<tr> - <td>Expires: </td> - <td class=\"left\">$exp_expires</td> - </tr>\n"; - -?> -</table> - -<?php +echo "<center> + <h1>Experiment Information</h1> + </center>\n"; +SHOWEXP($exp_pid, $exp_eid); # -# Suck out the node information. -# -$reserved_result = mysql_db_query($TBDBNAME, - "SELECT * FROM reserved WHERE ". - "eid=\"$exp_eid\" and pid=\"$exp_pid\""); -if (mysql_num_rows($reserved_result)) { - echo "<h3>Reserved Nodes</h3> - <table align=center border=1> - <tr> - <td align=center>Change</td> - <td align=center>Node ID</td> - <td align=center>Node Name</td> - <td align=center>Type</td> - <td align=center>Default<br>Image</td> - <td align=center>Default<br>Path</td> - <td align=center>Default<br>Cmdline</td> - <td align=center>Startup<br>Command</td> - <td align=center>Startup<br>Status</td> - </tr>\n"; - - # - # I'm so proud! - # - $query_result = mysql_db_query($TBDBNAME, - "SELECT nodes.*,reserved.vname ". - "FROM nodes LEFT JOIN reserved ". - "ON nodes.node_id=reserved.node_id ". - "WHERE reserved.eid=\"$exp_eid\" and reserved.pid=\"$exp_pid\" ". - "ORDER BY type,node_id"); - - while ($row = mysql_fetch_array($query_result)) { - $node_id = $row[node_id]; - $vname = $row[vname]; - $type = $row[type]; - $def_boot_image_id = $row[def_boot_image_id]; - $def_boot_path = $row[def_boot_path]; - $def_boot_cmd_line = $row[def_boot_cmd_line]; - $next_boot_path = $row[next_boot_path]; - $next_boot_cmd_line = $row[next_boot_cmd_line]; - $startupcmd = $row[startupcmd]; - $startstatus = $row[startstatus]; - - if (!$def_boot_cmd_line) - $def_boot_cmd_line = "NULL"; - if (!$def_boot_path) - $def_boot_path = "NULL"; - if (!$next_boot_path) - $next_boot_path = "NULL"; - if (!$next_boot_cmd_line) - $next_boot_cmd_line = "NULL"; - if (!$startupcmd) - $startupcmd = "NULL"; - if (!$vname) - $vname = "--"; - - echo "<tr> - <td align=center> - <A href='nodecontrol_form.php3?node_id=$node_id&refer=$exp_pideid'> - <img alt=\"o\" src=\"redball.gif\"></A></td> - <td>$node_id</td> - <td>$vname</td> - <td>$type</td> - <td>$def_boot_image_id</td> - <td>$def_boot_path</td> - <td>$def_boot_cmd_line</td> - <td>$startupcmd</td> - <td align=center>$startstatus</td> - </tr>\n"; - } - echo "</table>\n"; -} - +# Dump the node information. +# +SHOWNODES($exp_pid, $exp_eid); + # # Lets dump the project information too. # diff --git a/www/showexp_list.php3 b/www/showexp_list.php3 index e9e73c9ac29228d4a9f5f9ad6e3650c587283af6..d52b0fb19ac48b6a2152d3b84818a24712ceb5d5 100644 --- a/www/showexp_list.php3 +++ b/www/showexp_list.php3 @@ -19,48 +19,100 @@ $isadmin = ISADMIN($uid); # is a member of. Or, if an admin type person, show them all! # if ($isadmin) { - $query_result = mysql_db_query($TBDBNAME, - "select pid,eid,expt_name from experiments order by pid,eid"); + $experiments_result = mysql_db_query($TBDBNAME, + "select pid,eid,expt_name from experiments ". + "order by pid,eid,expt_name"); + + $batch_result = mysql_db_query($TBDBNAME, + "select pid,eid,name from batch_experiments ". + "order by pid,eid,name"); } else { - $query_result = mysql_db_query($TBDBNAME, + $experiments_result = mysql_db_query($TBDBNAME, "select e.pid,eid,expt_name from experiments as e ". - "left join proj_memb as p on p.pid=e.pid where p.uid='$uid' ". - "order by e.pid,eid"); + "left join proj_memb as p on p.pid=e.pid ". + "where p.uid='$uid' and (trust='local_root' or trust='group_root') ". + "order by e.pid,eid,expt_name"); + + $batch_result = mysql_db_query($TBDBNAME, + "select e.pid,eid,name from batch_experiments as e ". + "left join proj_memb as p on p.pid=e.pid ". + "where p.uid='$uid' and (trust='local_root' or trust='group_root') ". + "order by e.pid,eid,name"); } -if (mysql_num_rows($query_result) == 0) { - USERERROR("There are no experiments to ". - "show any experiment information", 1); +if (mysql_num_rows($experiments_result) == 0 && + mysql_num_rows($batch_result) == 0) { + USERERROR("There are no experiments running in any of the projects ". + "you are a member of.", 1); } -?> -<center> -<h1>Experiment Information Selection</h1> -<h2>Select an experiment from the list below.<br> -These are the experiments in the projects -you are a member of.</h2> -<table width="100%" border=2 cellpadding=0 cellspacing=2 align=center> -<tr> - <td>PID</td> - <td>EID</td> - <td>Name</td> -</tr> +echo "<center> + <h1>Experiment Information Listing</h1> + </center>\n"; -<?php +if (mysql_num_rows($experiments_result)) { + echo "<center> + <h2>Running Experiments</h2> + </center>\n"; + + echo "<table width=\"100%\" border=2 + cellpadding=0 cellspacing=2 align=center> + <tr> + <td>PID</td> + <td>EID</td> + <td>Name</td> + <td align=center>Terminate</td> + </tr>\n"; -while ($row = mysql_fetch_array($query_result)) { - $pid = $row[pid]; - $eid = $row[eid]; - $name = $row[expt_name]; + while ($row = mysql_fetch_array($experiments_result)) { + $pid = $row[pid]; + $eid = $row[eid]; + $name = $row[expt_name]; - echo "<tr> - <td><A href='showproject.php3?pid=$pid'>$pid</A></td> - <td><A href='showexp.php3?exp_pideid=$pid\$\$$eid'>$eid</A></td> - <td>$name</td> - </tr>\n"; + echo "<tr> + <td><A href='showproject.php3?pid=$pid'>$pid</A></td> + <td><A href='showexp.php3?exp_pideid=$pid\$\$$eid'> + $eid</A></td> + <td>$name</td> + <td align=center> + <A href='endexp.php3?exp_pideid=$pid\$\$$eid'> + <img alt=\"o\" src=\"redball.gif\"></A></td> + </tr>\n"; + } + echo "</table>\n"; } -echo "</table>\n"; +if (mysql_num_rows($batch_result)) { + echo "<center> + <h2>Batch Mode Experiments</h2> + </center>\n"; + + echo "<table width=\"100%\" border=2 + cellpadding=0 cellspacing=2 align=center> + <tr> + <td>PID</td> + <td>EID</td> + <td>Name</td> + <td align=center>Terminate</td> + </tr>\n"; + + while ($row = mysql_fetch_array($batch_result)) { + $pid = $row[pid]; + $eid = $row[eid]; + $name = $row[name]; + + echo "<tr> + <td><A href='showproject.php3?pid=$pid'>$pid</A></td> + <td><A href='showbatch.php3?exp_pideid=$pid\$\$$eid'> + $eid</A></td> + <td>$name</td> + <td align=center> + <A href='endbatch.php3?exp_pideid=$pid\$\$$eid'> + <img alt=\"o\" src=\"redball.gif\"></A></td> + </tr>\n"; + } + echo "</table>\n"; +} # # Standard Testbed Footer diff --git a/www/showstuff.php3 b/www/showstuff.php3 index 820ad1f10d4014b57843318729ff5cab67adf3b8..0810660a856a22de36268adb8cb954de679f2c74 100644 --- a/www/showstuff.php3 +++ b/www/showstuff.php3 @@ -181,6 +181,153 @@ function SHOWUSER($uid) { } +# +# Show an experiment. +# +function SHOWEXP($pid, $eid) { + global $TBDBNAME; + + $query_result = mysql_db_query($TBDBNAME, + "SELECT * FROM experiments WHERE ". + "eid=\"$eid\" and pid=\"$pid\""); + + $exprow = mysql_fetch_array($query_result); + + $exp_expires = $exprow[expt_expires]; + $exp_name = $exprow[expt_name]; + $exp_created = $exprow[expt_created]; + $exp_start = $exprow[expt_start]; + $exp_end = $exprow[expt_end]; + $exp_created = $exprow[expt_created]; + $exp_head = $exprow[expt_head_uid]; + + # + # Generate the table. + # + echo "<table align=center border=1>\n"; + + echo "<tr> + <td>Name: </td> + <td class=\"left\">$eid</td> + </tr>\n"; + + echo "<tr> + <td>Long Name: </td> + <td class=\"left\">$exp_name</td> + </tr>\n"; + + echo "<tr> + <td>Project: </td> + <td class=\"left\">$pid</td> + </tr>\n"; + + echo "<tr> + <td>Experiment Head: </td> + <td class=\"left\"> + <A href='showuser.php3?target_uid=$exp_head'> + $exp_head</td> + </tr>\n"; + + echo "<tr> + <td>Created: </td> + <td class=\"left\">$exp_created</td> + </tr>\n"; + + echo "<tr> + <td>Starts: </td> + <td class=\"left\">$exp_start</td> + </tr>\n"; + + echo "<tr> + <td>Ends: </td> + <td class=\"left\">$exp_end</td> + </tr>\n"; + + echo "<tr> + <td>Expires: </td> + <td class=\"left\">$exp_expires</td> + </tr>\n"; + + echo "</table>\n"; +} + +# +# Show Node information for an experiment. +# +function SHOWNODES($pid, $eid) { + global $TBDBNAME; + + $reserved_result = mysql_db_query($TBDBNAME, + "SELECT * FROM reserved WHERE ". + "eid=\"$eid\" and pid=\"$pid\""); + + if (mysql_num_rows($reserved_result)) { + echo "<center> + <h3>Reserved Nodes</h3> + </center> + <table align=center border=1> + <tr> + <td align=center>Change</td> + <td align=center>Node ID</td> + <td align=center>Node Name</td> + <td align=center>Type</td> + <td align=center>Default<br>Image</td> + <td align=center>Default<br>Path</td> + <td align=center>Default<br>Cmdline</td> + <td align=center>Startup<br>Command</td> + <td align=center>Startup<br>Status</td> + </tr>\n"; + + $query_result = mysql_db_query($TBDBNAME, + "SELECT nodes.*,reserved.vname ". + "FROM nodes LEFT JOIN reserved ". + "ON nodes.node_id=reserved.node_id ". + "WHERE reserved.eid=\"$eid\" and reserved.pid=\"$pid\" ". + "ORDER BY type,node_id"); + + while ($row = mysql_fetch_array($query_result)) { + $node_id = $row[node_id]; + $vname = $row[vname]; + $type = $row[type]; + $def_boot_image_id = $row[def_boot_image_id]; + $def_boot_path = $row[def_boot_path]; + $def_boot_cmd_line = $row[def_boot_cmd_line]; + $next_boot_path = $row[next_boot_path]; + $next_boot_cmd_line = $row[next_boot_cmd_line]; + $startupcmd = $row[startupcmd]; + $startstatus = $row[startstatus]; + + if (!$def_boot_cmd_line) + $def_boot_cmd_line = "NULL"; + if (!$def_boot_path) + $def_boot_path = "NULL"; + if (!$next_boot_path) + $next_boot_path = "NULL"; + if (!$next_boot_cmd_line) + $next_boot_cmd_line = "NULL"; + if (!$startupcmd) + $startupcmd = "NULL"; + if (!$vname) + $vname = "--"; + + echo "<tr> + <td align=center> + <A href='nodecontrol_form.php3?node_id=$node_id&refer=$pid\$\$$eid'> + <img alt=\"o\" src=\"redball.gif\"></A></td> + <td>$node_id</td> + <td>$vname</td> + <td>$type</td> + <td>$def_boot_image_id</td> + <td>$def_boot_path</td> + <td>$def_boot_cmd_line</td> + <td>$startupcmd</td> + <td align=center>$startstatus</td> + </tr>\n"; + } + echo "</table>\n"; + } +} + # # This is an included file. # diff --git a/www/welcome.html b/www/welcome.html index 29d59e1fa6faa9d4f14609e8422ed4abc40b1f25..9e5d2e626581515a078fc421d45553bfa625f75d 100644 --- a/www/welcome.html +++ b/www/welcome.html @@ -42,6 +42,13 @@ the software you run on it, including all bits on the disks, is replaceable and up to you. The same applies to the network's characteristics, including its topology: configurable by users. +<h2>Late Breaking News:</h2> + +<ul> +<li> <blink>Experiment Termination is now part of the Experiment + Information link!</blink> +</ul> + <h2>Links to help you get started:</h2>