diff --git a/configure b/configure index d4a4436e807ae0aa1704a0f1929fcd2a90a63640..2f23a414ecfe37945277a38fbf07d5cd236c4b74 100755 --- a/configure +++ b/configure @@ -1542,6 +1542,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ tbsetup/plab/plabdiscover tbsetup/plab/etc/netbed_files/GNUmakefile \ tbsetup/ipassign/GNUmakefile tbsetup/ipassign/src/GNUmakefile \ tbsetup/ipassign/ipassign_wrapper tbsetup/assign_prepass \ + tbsetup/panic tbsetup/webpanic \ tip/GNUmakefile \ tmcd/GNUmakefile tmcd/tmcd.restart \ tmcd/common/GNUmakefile tmcd/common/config/GNUmakefile \ diff --git a/configure.in b/configure.in index 69a3a087ceb25dd3212dfc59a649b883f996f4be..899e6f5e2e768ef37a12a66c42ae809fe62a2306 100755 --- a/configure.in +++ b/configure.in @@ -572,6 +572,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ tbsetup/plab/plabdiscover tbsetup/plab/etc/netbed_files/GNUmakefile \ tbsetup/ipassign/GNUmakefile tbsetup/ipassign/src/GNUmakefile \ tbsetup/ipassign/ipassign_wrapper tbsetup/assign_prepass \ + tbsetup/panic tbsetup/webpanic \ tip/GNUmakefile \ tmcd/GNUmakefile tmcd/tmcd.restart \ tmcd/common/GNUmakefile tmcd/common/config/GNUmakefile \ diff --git a/db/libdb.pm.in b/db/libdb.pm.in index 104dfd32b9135180d647ef8c14c227111594f44c..a5bd8e1601c46f7fa467baf5de3cb55b22646ded 100644 --- a/db/libdb.pm.in +++ b/db/libdb.pm.in @@ -67,7 +67,7 @@ use vars qw(@ISA @EXPORT); DBLIMIT_NSFILESIZE NODERELOADPENDING_EID EXPTSTATE_NEW EXPTSTATE_PRERUN EXPTSTATE_SWAPPED EXPTSTATE_SWAPPING - EXPTSTATE_ACTIVATING EXPTSTATE_ACTIVE + EXPTSTATE_ACTIVATING EXPTSTATE_ACTIVE EXPTSTATE_PANICED EXPTSTATE_TERMINATING EXPTSTATE_TERMINATED EXPTSTATE_QUEUED EXPTSTATE_MODIFY_PARSE EXPTSTATE_MODIFY_REPARSE EXPTSTATE_MODIFY_RESWAP EXPTSTATE_RESTARTING @@ -200,6 +200,8 @@ use vars qw(@ISA @EXPORT); TBExptMinMaxNodes TBExptSecurityLevel TBExptIDX TBDB_SECLEVEL_GREEN TBDB_SECLEVEL_YELLOW TBDB_SECLEVEL_ORANGE TBDB_SECLEVEL_RED + + TBExptSetPanicBit TBExptGetPanicBit TBExptClearPanicBit ); # Must come after package declaration! @@ -336,6 +338,7 @@ sub EXPTSTATE_QUEUED() { "queued"; } sub EXPTSTATE_SWAPPING() { "swapping"; } sub EXPTSTATE_ACTIVATING() { "activating"; } sub EXPTSTATE_ACTIVE() { "active"; } +sub EXPTSTATE_PANICED() { "paniced"; } sub EXPTSTATE_TERMINATING() { "terminating"; } sub EXPTSTATE_TERMINATED() { "ended"; } sub EXPTSTATE_MODIFY_PARSE() { "modify_parse"; } @@ -3776,6 +3779,46 @@ sub TBNodeFirewall ($$$) { return 1; } +# +# Set the paniced bit for an experiment. +# +sub TBExptSetPanicBit($$) { + my ($pid, $eid) = @_; + + return DBQueryWarn("update experiments set ". + " paniced=1,panic_date=now() ". + "where pid='$pid' and eid='$eid'"); +} + +# +# Clear the panic bit. +# +sub TBExptClearPanicBit($$) { + my ($pid, $eid) = @_; + + return DBQueryWarn("update experiments set ". + " paniced=0,panic_date=NULL ". + "where pid='$pid' and eid='$eid'"); +} + +# +# Get the value of the paniced bit. +# +sub TBExptGetPanicBit($$$) { + my ($pid, $eid, $panicp) = @_; + + my $query_result = + DBQueryWarn("select paniced,panic_date from experiments ". + "where pid='$pid' and eid='$eid'"); + if (!$query_result || $query_result->num_rows == 0) { + return 0; + } + my @row = $query_result->fetchrow_array(); + $$panicp = $row[0]; + + return 1; +} + # # Issue a DB query. Argument is a string. Returns the actual query object, so # it is up to the caller to test it. I would not for one moment view this diff --git a/tbsetup/GNUmakefile.in b/tbsetup/GNUmakefile.in index 0b7c37905122820042075ec70eee926c9f175ceb..4085da3be34e66a582792688d15d75fcba77f403 100644 --- a/tbsetup/GNUmakefile.in +++ b/tbsetup/GNUmakefile.in @@ -28,14 +28,14 @@ SBIN_STUFF = resetvlans console_setup.proxy sched_reload named_setup \ exports_setup.proxy vnode_setup eventsys_start \ sfskey_update sfskey_update.proxy rmuser idleswap \ newnode_reboot savelogs.proxy eventsys.proxy \ - elabinelab snmpit.proxy + elabinelab snmpit.proxy panic CTRLBIN_STUFF = console_setup.proxy exports_setup.proxy sfskey_update.proxy \ savelogs.proxy eventsys.proxy LIBEXEC_STUFF = rmproj wanlinksolve wanlinkinfo \ os_setup mkexpdir console_setup webnscheck webreport \ - webendexp webbatchexp \ + webendexp webbatchexp webpanic \ assign_wrapper assign_prepass ptopgen webnodeupdate \ webdelay_config \ webrmgroup webswapexp webnodecontrol \ diff --git a/tbsetup/panic.in b/tbsetup/panic.in new file mode 100755 index 0000000000000000000000000000000000000000..7fbd72a27768bc0a1dd1fac4eb8c7fd77d6dd794 --- /dev/null +++ b/tbsetup/panic.in @@ -0,0 +1,283 @@ +#!/usr/bin/perl -wT + +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2004 University of Utah and the Flux Group. +# All rights reserved. +# + +use English; +use Getopt::Std; +use POSIX qw(isatty setsid); + +# +# Press the panic button. Also invoked from web interface. +# +sub usage() +{ + print(STDERR + "Usage: panicbutton [-r] <pid> <eid>\n". + "switches and arguments:\n". + "-r - Reset panic state (admin people only)\n". + "<pid> - The project the experiment belongs to\n". + "<eid> - The experiment name (id)\n"); + exit(-1); +} +my $optlist = "r"; +my $reset = 0; + +sub Fatal($); + +# +# Exit codes are important; they tell the web page what has happened so +# it can say something useful to the user. Fatal errors are mostly done +# with die(), but expected errors use this routine. At some point we will +# use the DB to communicate the actual error. +# +# $status < 0 - Fatal error. Something went wrong we did not expect. +# $status = 0 - Termination is proceeding in the background. Notified later. +# $status > 0 - Expected error. User not allowed for some reason. +# +sub ExitWithStatus($$) +{ + my ($status, $message) = @_; + + if ($status < 0) { + die("*** $0:\n". + " $message\n"); + } + else { + print STDERR "$message\n"; + } + exit($status); +} + +# +# Configure variables +# +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; + +# +# Testbed Support libraries +# +use lib "@prefix@/lib"; +use libdb; +use libtestbed; + +# Be careful not to exit on transient error; 0 means infinite retry. +$libdb::DBQUERY_MAXTRIES = 0; + +my $snmpit = "$TB/bin/snmpit"; +my $dbuid; +my $user_name; +my $user_email; + +# +# Untaint the path +# +$ENV{'PATH'} = '/bin:/usr/bin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Parse command arguments. Once we return from getopts, all that should +# left are the required arguments. +# +%options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (@ARGV != 2) { + usage(); +} +my $pid = $ARGV[0]; +my $eid = $ARGV[1]; +if (defined($options{"r"})) { + $reset = 1; +} + +# +# Untaint the arguments. +# +if ($pid =~ /^([-\w\.]+)$/) { + $pid = $1; +} +else { + die("Tainted argument $pid!\n"); +} +if ($eid =~ /^([-\w\.]+)$/) { + $eid = $1; +} +else { + die("Tainted argument $eid!\n"); +} + +# +# See if the experiment is firewalled. Error if not. +# +my $firewall; +my $firewalled = TBExptFirewall($pid, $eid, \$firewall); + +if (!$firewalled) { + die("*** $0:\n". + " Experiment $pid/$eid is not firewalled!\n"); +} + +# +# Verify user and get his DB uid. +# +if (! UNIX2DBUID($UID, \$dbuid)) { + die("*** $0:\n". + " You do not exist in the Emulab Database.\n"); +} + +# +# Get email info for user. +# +if (! UserDBInfo($dbuid, \$user_name, \$user_email)) { + die("*** $0:\n". + " Cannot determine your name and email address.\n"); +} + +# +# Verify that this person is allowed to press the panic button. +# Note that any script down the line has to do an admin check also. +# +if ($UID && !TBAdmin($UID) && + !TBExptAccessCheck($dbuid, $pid, $eid, TB_EXPT_MODIFY)) { + die("*** $0:\n". + " You do not have permission to end this experiment!\n"); +} + +# +# We have to protect against trying to end an experiment that is currently +# in the process of being terminated. We use a "wrapper" state (actually +# a timestamp so we can say when termination was requested) since +# terminating consists of a couple of different experiment states down inside +# the tb scripts. +# +DBQueryFatal("lock tables experiments write"); + +$query_result = + DBQueryFatal("SELECT * FROM experiments WHERE eid='$eid' and pid='$pid'"); + +if (! $query_result->numrows) { + die("*** $0:\n". + " No such experiment $pid/$eid exists!\n"); +} +my %hashrow = $query_result->fetchhash(); +my $expt_head_login = $hashrow{'expt_head_uid'}; +my $estate = $hashrow{'state'}; + +# +# Called from user (via web interface). +# +if ($reset) { + ExitWithStatus(1, "Experiment $pid/$eid is not paniced!\n") + if ($estate ne EXPTSTATE_PANICED); +} +else { + ExitWithStatus(1, "Experiment $pid/$eid is not active!\n") + if (! ($estate eq EXPTSTATE_ACTIVE || + $estate eq EXPTSTATE_ACTIVATING || + $estate eq EXPTSTATE_SWAPPING)); +} + +# +# Change experiment state and lock it. +# +TBLockExp($pid, $eid, ($reset ? EXPTSTATE_ACTIVE : EXPTSTATE_PANICED)); +DBQueryFatal("unlock tables"); + +# +# XXX - At this point a failure is going to leave things in an +# inconsistent state. Be sure to call fatal() only since we are +# going into the background, and we have to send email since no +# one is going to see printed error messages (output goes into the +# log file, which will be sent along in the email). +# + +# +# Get email address of the experiment head, which may be different than +# the person who is actually terminating the experiment, since its polite +# to let the original creator know whats going on. +# +my $expt_head_name; +my $expt_head_email; + +if (! UserDBInfo($expt_head_login, \$expt_head_name, \$expt_head_email)) { + print "*** WARNING: ". + "Could not determine name/email for $expt_head_login.\n"; + $expt_head_name = "TBOPS"; + $expt_head_email = $TBOPS; +} + +$query_result = + DBQueryFatal("select card1 from wires ". + "where node_id1='$firewall' AND type='Control'"); + +if (!$query_result->numrows) { + fatal("Could not determine firewall port for $pid/$eid!"); +} +my ($port) = $query_result->fetchrow_array(); + +# +# Call snmpit. +# +if ($reset) { + system("$snmpit -e ${firewall}:${port}"); + if ($?) { + fatal("snmpit exited with $?!"); + } + TBExptClearPanicBit($pid, $eid); + print "Panic situation has been cleared!\n"; +} +else { + system("$snmpit -d ${firewall}:${port}"); + if ($?) { + fatal("snmpit exited with $?!"); + } + TBExptSetPanicBit($pid, $eid); + print "Panic Button has been pressed!\n"; +} +TBUnLockExp($pid, $eid); + +# +# Send email notification to user *and* to tbops. +# +SENDMAIL("$user_name <$user_email>", + "Panic Button ". ($reset ? "Cleared" : "Pressed") . + " for Experiment $pid/$eid", + "$dbuid has " . ($reset ? "cleared" : "pressed") . + " the panic button for experiment $pid/$eid", + "$user_name <$user_email>", + "Cc: $expt_head_name <$expt_head_email>\n". + "Bcc: $TBOPS"); + +exit 0; + +sub fatal($) +{ + my($mesg) = $_[0]; + + # + # Send a message to the testbed list. Append the logfile. + # + SENDMAIL("$user_name <$user_email>", + "Panic Button Failure for Experiment $pid/$eid", + "$dbuid ". ($reset ? "cleared" : "pressed") . + " the panic button for experiment $pid/$eid,\n". + "BUT there was a failure!\n\n". + "$mesg\n", + "$user_name <$user_email>", + "Cc: $expt_head_name <$expt_head_email>\n". + "Bcc: $TBOPS"); + + die("*** $0:\n". + " $mesg\n"); +} diff --git a/tbsetup/swapexp.in b/tbsetup/swapexp.in index e97c4031557d7d430541fde6cc2189a3f5820a1a..b5dcd1fa853114134b5b9ed1c295fd0b0c425a3e 100644 --- a/tbsetup/swapexp.in +++ b/tbsetup/swapexp.in @@ -107,6 +107,7 @@ my @row; my $action; my $nextswapstate; my $termswapstate; +my $isadmin = 0; # # Untaint the path @@ -270,12 +271,13 @@ if (! UserDBInfo($dbuid, \$user_name, \$user_email)) { die("*** $0:\n". " Cannot determine your name and email address.\n"); } +$isadmin = TBAdmin($UID); # # Verify that this person can muck with the experiment. # Note that any script down the line has to do an admin check also. # -if ($UID && !TBAdmin($UID) && +if ($UID && !$isadmin && !TBExptAccessCheck($dbuid, $pid, $eid, TB_EXPT_DESTROY)) { die("*** $0:\n". " You do not have permission to swap or modify this experiment!\n"); @@ -470,6 +472,17 @@ else { "or modify the experiment.\n") if ($canceled); + # + # Cannot swapmod an active elabinelab experiment, yet. + # + ExitWithStatus(1, + "Experiment $pid/$eid is an active ElabInElab.\n". + "You cannot modify this type of experiment while it\n". + "is swapped in. We hope to support this soon.\n") + if ($inout eq "modify" && + ($elabinelab || defined($elabinelab_eid)) && + $estate ne EXPTSTATE_SWAPPED()); + # # Check the state for the various operations. # @@ -484,12 +497,25 @@ else { }; /^out$/i && do { if ($estate ne EXPTSTATE_ACTIVE() && + $estate ne EXPTSTATE_PANICED() && $estate ne EXPTSTATE_ACTIVATING()) { ExitWithStatus(1, "Experiment $pid/$eid is not swapped in ". "or activating!\n"); } + # + # Must be an admin person to swap out an experiment that + # has had its panic button pressed. + # + if ($estate eq EXPTSTATE_PANICED() && !$isadmin) { + ExitWithStatus(1, + "Experiment $pid/$eid had its panic ". + "button pressed!\n". + "Only a testbed administrator can swap ". + "this experiment out."); + } + if ($estate eq EXPTSTATE_ACTIVATING()) { # # All we can do is set the cancel flag and hope that @@ -743,6 +769,7 @@ if ($inout eq "out") { } SetExpState($pid, $eid, EXPTSTATE_SWAPPED) or fatal("Failed to set experiment state to " . EXPTSTATE_SWAPPED()); + TBExptClearPanicBit($pid, $eid); } elsif ($inout eq "in") { my $optarg = ""; diff --git a/tbsetup/tbswap.in b/tbsetup/tbswap.in index 802933d0588c46de6f13731a3d5e8bd57fdbd01a..6ba9cb5f46d62fb4c9a065d0f9aa0d1a16b2506c 100644 --- a/tbsetup/tbswap.in +++ b/tbsetup/tbswap.in @@ -416,15 +416,98 @@ sub doSwapout($) { } if ($firewalled) { - # XXX put all nodes into admin mode - print STDERR "Confining firewalled nodes.\n"; - TBDebugTimeStamp("moving nodes to purgatory"); + # + # If the panic button was pressed, put all nodes into admin + # mode and power them off. After the firewall is torn down, + # we can power them back up. + # + my @nodes = ExpNodes($pid, $eid, 1); + my $ADMINOSID = TB_OSID_FREEBSD_MFS; + my $paniced; + + TBExptGetPanicBit($pid, $eid, \$paniced); + + if ($paniced) { + print STDERR "Powering down nodes.\n"; + TBDebugTimeStamp("Powering down nodes"); + + system("power off @nodes"); + if ($?) { + # + # If an error, cannot continue. Must leave firewall in + # place. + # + print STDERR "*** Failed to power nodes off! Stopping.\n"; + return 1; + } + + # + # XXX: node_admin should take a list of nodes, or pid,eid. + # Instead, call os_select directly for now. + # + # Clear any one-shot boots and partition boots. + # + print STDERR "Changing OSID to admin MFS.\n"; + TBDebugTimeStamp("Changing OSID to admin MFS"); + + # Order matters cause os_select sillyness. + if (system("os_select -c -1 @nodes") || + system("os_select -t $ADMINOSID @nodes") || + system("os_select -c @nodes")) { + + # + # If an error, cannot continue. Must leave firewall in + # place. + # + print STDERR "*** Failed to reset OSIDs! Stopping.\n"; + return 1; + } + + # + # Now we can power up the nodes again. + # + print STDERR "Powering up nodes.\n"; + TBDebugTimeStamp("Powering up nodes"); + + system("power on @nodes"); + if ($?) { + # + # If an error, cannot continue. Must leave firewall in + # place. Eventually, we can continue past this, if we + # know what nodes failed. But for now we have to do the + # ISUP test to make sure nodes really got into the MFS. + # + print STDERR "*** Failed to power on nodes! Stopping.\n"; + return 1; + } + } # # Once all nodes are safely in the admin MFS, we can take # down the firewall # doFW($pid, $eid, FWTEARDOWN); + + if ($paniced) { + # + # Now wait for ISUP. I do this here cause the reload daemon + # will power cycle the nodes again if its reboot fails, and + # that will happen if the nodes are not back into the MFS in + # time. THe right thing to do is to store the power state in + # the DB, and have the reload daemon turn the nodes back on. + # + print STDERR "Waiting for nodes to boot the MFS.\n"; + TBDebugTimeStamp("Waiting for nodes to boot the MFS"); + + system("node_statewait -s " . TBDB_NODESTATE_ISUP . " @nodes"); + if ($?) { + # + # Okay to continue; reload daemon will probably send + # email later when the reload fails. + # + print STDERR "*** Some nodes failed to reboot. Continuing\n"; + } + } } # @@ -1050,6 +1133,7 @@ sub doFW($$$) { # my $fwsetupstr1 = "snmpit $cnetstack -m $fwvlanname $portlist"; my $fwsetupstr2 = "snmpit $cnetstack -T $fwport $cnetvlanname $fwvlanname"; + my $fwtakedownstr0 = "snmpit $cnetstack -e $fwport"; my $fwtakedownstr1 = "snmpit $cnetstack -m $cnetvlanname $portlist"; my $fwtakedownstr2 = "snmpit $cnetstack -o $fwvlanname"; my $fwtakedownstr3 = "snmpit $cnetstack -U $fwport"; @@ -1102,8 +1186,14 @@ sub doFW($$$) { # Record VLAN info now that everything is done TBSetExptFirewallVlan($pid, $eid, $fwvid, $fwvlan); } else { - TBDebugTimeStamp("snmpit firewall teardown: VLAN"); + TBDebugTimeStamp("snmpit re-enable fw control port: $fwport"); my $failed = 0; + if (system($fwtakedownstr0)) { + print STDERR + "*** Could not re-enable firewall control port $fwport!\n"; + $failed = 1; + } + TBDebugTimeStamp("snmpit firewall teardown: VLAN"); if (system($fwtakedownstr1)) { print STDERR "*** Could not return $portlist to Control VLAN!\n"; diff --git a/tbsetup/webpanic.in b/tbsetup/webpanic.in new file mode 100644 index 0000000000000000000000000000000000000000..8797d5bf12123ddcab825b2145cd01398f52c95b --- /dev/null +++ b/tbsetup/webpanic.in @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +# +# EMULAB-COPYRIGHT +# Copyright (c) 2004 University of Utah and the Flux Group. +# All rights reserved. +# +use English; + +# +# This gets invoked from the Web interface. Simply a wrapper ... +# + +# +# Configure variables +# +my $TB = "@prefix@"; + +# +# Run the real thing, and never return. +# +exec "$TB/sbin/panic", @ARGV; + +die("webpanic: Could not exec panic: $!"); diff --git a/utils/node_statewait.in b/utils/node_statewait.in index 1c5b84a620ceeeede8487357b6c5ea7a62468c8d..d6c0c70cf58e85cffcaf032b2c82671344e816db 100644 --- a/utils/node_statewait.in +++ b/utils/node_statewait.in @@ -14,11 +14,13 @@ use Getopt::Std; # sub usage() { - print STDOUT "Usage: node_statewait [-t timeout] [-a] | [node ...]\n"; + print STDOUT + "Usage: node_statewait [-s state] [-t timeout] [-a] | [node ...]\n"; exit(-1); } -my $optlist = "at:"; +my $optlist = "at:s:"; my $timeout = 60 * 6; +my $state = TBDB_NODESTATE_PXEWAIT; # # Configure variables @@ -50,6 +52,9 @@ if (! getopts($optlist, \%options)) { if (defined($options{"t"})) { $timeout = $options{"t"}; } +if (defined($options{"s"})) { + $state = $options{"s"}; +} # # All testnodes, or just some nodes. @@ -90,7 +95,7 @@ foreach my $node (sort(@nodes)) { # # Skip if something failed earlier. # - if (!TBNodeStateWait($node, TBDB_NODESTATE_PXEWAIT, $waitstart, $timeout)) { + if (!TBNodeStateWait($node, $state, $waitstart, $timeout)) { print STDOUT "nodewait ($node): Success\n"; next; } diff --git a/www/dbdefs.php3.in b/www/dbdefs.php3.in index aa13afa1bb9226e3e9a35e7cadc55d8c22daaa6c..e095d14905c20e4620ff8f87d613b30ce0dc8eea 100644 --- a/www/dbdefs.php3.in +++ b/www/dbdefs.php3.in @@ -130,6 +130,7 @@ $TB_EXPTSTATE_SWAPPING = "swapping"; $TB_EXPTSTATE_SWAPPED = "swapped"; $TB_EXPTSTATE_ACTIVATING = "activating"; $TB_EXPTSTATE_ACTIVE = "active"; +$TB_EXPTSTATE_PANICED = "paniced"; $TB_EXPTSTATE_QUEUED = "queued"; # Interfaces roles. @@ -1851,6 +1852,25 @@ function TBPlabAvail() { return $types; } +# +# Is an experiment firewalled. +# +function TBExptFirewall($pid, $eid) { + # + # Short form: is there a firewall? + # Only check the firewalls table so that we can be called for a swapped + # experiment (swapped experiments don't have reserved table info). + # + $query_result = + DBQueryWarn("SELECT eid FROM firewalls ". + "WHERE pid='$pid' and eid='$eid' ". + "AND type LIKE '%-vlan'"); + if (!$query_result || !mysql_num_rows($query_result)) + return 0; + + return 1; +} + # # DB Interface. # diff --git a/www/panicbutton.gif b/www/panicbutton.gif new file mode 100644 index 0000000000000000000000000000000000000000..ed8fa6488d2934ff40d15a176c21f0e4b8be8ab9 Binary files /dev/null and b/www/panicbutton.gif differ diff --git a/www/panicbutton.php3 b/www/panicbutton.php3 new file mode 100644 index 0000000000000000000000000000000000000000..62da119287714823d2bc809dd5cb2d95823ac0d3 --- /dev/null +++ b/www/panicbutton.php3 @@ -0,0 +1,142 @@ +<?php +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2004 University of Utah and the Flux Group. +# All rights reserved. +# +include("defs.php3"); +include("showstuff.php3"); + +# +# Only known and logged in users. +# +$uid = GETLOGIN(); +LOGGEDINORDIE($uid); + +# +# Must provide the EID! +# +if (!isset($pid) || + strcmp($pid, "") == 0) { + USERERROR("The project ID was not provided!", 1); +} + +if (!isset($eid) || + strcmp($eid, "") == 0) { + USERERROR("The experiment ID was not provided!", 1); +} + +$exp_eid = $eid; +$exp_pid = $pid; + +# Canceled operation redirects back to showexp page. See below. +if ($canceled) { + header("Location: showexp.php3?pid=$pid&eid=$eid"); + return; +} + +# +# Standard Testbed Header, after checking for cancel above. +# +PAGEHEADER("Press the Panic Button!"); + +# +# Check to make sure thats this is a valid PID/EID, while getting the +# experiment GID. +# +if (! TBExptGroup($exp_pid, $exp_eid, $exp_gid)) { + USERERROR("The experiment $exp_eid is not a valid experiment ". + "in project $exp_pid.", 1); +} + +# +# Verify permissions. +# +if (! TBExptAccessCheck($uid, $exp_pid, $exp_eid, $TB_EXPT_MODIFY)) { + USERERROR("You do not have permission to press the panic button for ". + "experiment $exp_eid!", 1); +} + +echo "<font size=+2>Experiment <b>". + "<a href='showproject.php3?pid=$exp_pid'>$exp_pid</a>/". + "<a href='showexp.php3?pid=$exp_pid&eid=$exp_eid'>$exp_eid</a>". + "</b></font>\n"; + +# +# 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 redirect the +# browser back up a level. +# +if (!$confirmed) { + echo "<center><h2><br> + Are you <b>REALLY</b> + sure you want to press the panic button for Experiment '$exp_eid?' + </h2>\n"; + + SHOWEXP($exp_pid, $exp_eid, 1); + + echo "<form action='panicbutton.php3?pid=$exp_pid&eid=$exp_eid'". + "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. +# Note usage of default group in project. +# +TBGroupUnixInfo($exp_pid, $exp_gid, $unix_gid, $unix_name); + +# +# We run a wrapper script that does all the work. +# +echo "<center><br>"; +echo "<h2>Pressing the panic button. Please wait a moment ... + </h2></center>"; + +flush(); + +# +# Run the backend script. +# +$retval = SUEXEC($uid, "$exp_pid,$unix_gid", "webpanic $exp_pid $exp_eid", + SUEXEC_ACTION_IGNORE); + +# +# Fatal Error. Report to the user, even though there is not much he can +# do with the error. Also reports to tbops. +# +if ($retval < 0) { + SUEXECERROR(SUEXEC_ACTION_DIE); + # + # Never returns ... + # + die(""); +} + +# +# Exit status >0 means the operation could not proceed. +# Exit status =0 means the experiment is terminating in the background. +# +echo "<br>\n"; +if ($retval) { + echo "<h3>Panic Button failure</h3>"; + echo "<blockquote><pre>$suexec_output<pre></blockquote>"; +} +else { + echo "<h3>The panic button has been pressed!</h3><br> + You will need to contact testbed operations to continue.\n"; +} + +# +# Standard Testbed Footer +# +PAGEFOOTER(); +?> diff --git a/www/showexp.php3 b/www/showexp.php3 index a0cdd5dd0c92fd4a2c1063771db0430f7eb5c256..c96856aa29d48c160bdccec8b8bc38fd36dcc4ab 100644 --- a/www/showexp.php3 +++ b/www/showexp.php3 @@ -12,6 +12,7 @@ include("showstuff.php3"); # $uid = GETLOGIN(); LOGGEDINORDIE($uid); +$isadmin = ISADMIN($uid); # # Verify page arguments. @@ -59,7 +60,7 @@ if (! TBExptAccessCheck($uid, $exp_pid, $exp_eid, $TB_EXPT_READINFO)) { # $query_result = DBQueryFatal("select e.idx,e.state,e.batchmode,e.linktest_pid,". - " s.rsrcidx,r.wirelesslans ". + " e.paniced,e.panic_date,s.rsrcidx,r.wirelesslans ". " from experiments as e ". "left join experiment_stats as s on s.exptidx=e.idx ". "left join experiment_resources as r on s.rsrcidx=r.idx ". @@ -71,6 +72,8 @@ $rsrcidx = $row["rsrcidx"]; $isbatch = $row["batchmode"]; $wireless = $row["wirelesslans"]; $linktest_running = $row["linktest_pid"]; +$paniced = $row["paniced"]; +$panic_date = $row["panic_date"]; # # Get a list of node types and classes in this experiment @@ -132,7 +135,8 @@ if ($expstate) { WRITESUBMENUBUTTON("Swap Experiment In", "swapexp.php3?inout=in&pid=$exp_pid&eid=$exp_eid"); } - elseif ($expstate == $TB_EXPTSTATE_ACTIVE) { + elseif ($expstate == $TB_EXPTSTATE_ACTIVE || + ($expstate == $TB_EXPTSTATE_PANICED && $isadmin)) { WRITESUBMENUBUTTON("Swap Experiment Out", "swapexp.php3?inout=out&pid=$exp_pid&eid=$exp_eid"); } @@ -142,8 +146,10 @@ if ($expstate) { "&pid=$exp_pid&eid=$exp_eid"); } } - WRITESUBMENUBUTTON("Terminate Experiment", - "endexp.php3?pid=$exp_pid&eid=$exp_eid"); + if ($expstate != $TB_EXPTSTATE_PANICED) { + WRITESUBMENUBUTTON("Terminate Experiment", + "endexp.php3?pid=$exp_pid&eid=$exp_eid"); + } # Batch experiments can be modifed only when paused. if ($expstate == $TB_EXPTSTATE_SWAPPED || @@ -215,7 +221,7 @@ if ($types['garcia'] || $classes['sg']) { "moteleds.php3?pid=$exp_pid&eid=$exp_eid"); } -if (ISADMIN($uid)) { +if ($isadmin) { if ($expstate == $TB_EXPTSTATE_ACTIVE) { SUBMENUSECTION("Beta-Test Options"); WRITESUBMENUBUTTON("Restart Experiment", @@ -241,12 +247,34 @@ SUBMENUEND_2A(); echo "<br> <a href='shownsfile.php3?pid=$exp_pid&eid=$exp_eid'> <img border=1 alt='experiment vis' - src='showthumb.php3?idx=$rsrcidx'></a>\n"; + src='showthumb.php3?idx=$rsrcidx'></a>"; SUBMENUEND_2B(); SHOWEXP($exp_pid, $exp_eid); +if (TBExptFirewall($exp_pid, $exp_eid) && + ($expstate == $TB_EXPTSTATE_ACTIVE || + $expstate == $TB_EXPTSTATE_PANICED || + $expstate == $TB_EXPTSTATE_ACTIVATING || + $expstate == $TB_EXPTSTATE_SWAPPING)) { + echo "<center>\n"; + if ($paniced) { + echo "<br><font size=+1 color=red><blink>". + "Your experiment was cut off via the Panic Button on $panic_date!". + "<br>". + "You will need to contact testbed operations to make further ". + "changes (swap, terminate) to your experiment.</blink></font>"; + } + else { + echo "<br><a href='panicbutton.php3?pid=$exp_pid&eid=$exp_eid'> + <img border=1 alt='panic button' src='panicbutton.gif'></a>"; + echo "<br><font color=red size=+2>". + " Press the Panic Button to contain your experiment". + "</font>\n"; + } + echo "</center>\n"; +} SUBPAGEEND(); # @@ -254,7 +282,7 @@ SUBPAGEEND(); # SHOWNODES($exp_pid, $exp_eid, $sortby); -if (ISADMIN($uid)) { +if ($isadmin) { echo "<center> <h3>Experiment Stats</h3> </center>\n";