Commit c874636d authored by Leigh Stoller's avatar Leigh Stoller

Cleanup of the Chris' TB scripts. Cosmetic in principle, but reworked

to use the DB library access routines, which also changed in response
to what the tb scripts needed. Added some functions and mor constants.
Removed the -nologfile option from all the scripts (startexp and
endexp too), since there is no reason for these scripts to worry about
log files. Thats handled in the wrappers. Tested with the testsuite
and live in my own tree.
parent 7d6ec7b7
......@@ -2,8 +2,14 @@
use English;
require Mysql;
#
# A library of useful DB stuff. Mostly things that get done a lot.
# Saves typing.
#
# XXX: The notion of "uid" is a tad confused. A unix uid is a number,
# while in the DB a user uid is a string (equiv to unix login).
# Needs to be cleaned up.
#
package libdb;
use Exporter;
......@@ -14,9 +20,14 @@ use Exporter;
NODESTARTSTATUS_NOSTATUS PROJMEMBERTRUST_NONE PROJMEMBERTRUST_USER
PROJMEMBERTRUST_TRUSTED DBLIMIT_NSFILESIZE
EXPTSTATE_NEW EXPTSTATE_PRERUN EXPTSTATE_SWAPPED EXPTSTATE_SWAPPING
EXPTSTATE_ACTIVATING EXPTSTATE_ACTIVE EXPTSTATE_TESTING
EXPTSTATE_TERMINATING EXPTSTATE_TERMINATED
TBAdmin NodeAccessCheck ProjMember ExpLeader MarkNodeDown
SetNodeBootStatus OSFeatureSupported IsShelved NodeidToExp
UIDInfo DBQuery DBQueryFatal DBQueryWarn DBWarn DBFatal DBQuoteSpecial
UserDBInfo DBQuery DBQueryFatal DBQueryWarn DBWarn DBFatal
DBQuoteSpecial UNIX2DBUID ExpState SetExpState
);
......@@ -51,6 +62,16 @@ sub NODEBOOTSTATUS_FAILED() { "failed"; }
sub NODEBOOTSTATUS_UNKNOWN() { "unknown"; }
sub NODESTARTSTATUS_NOSTATUS() { "none"; }
sub EXPTSTATE_NEW() { "new"; }
sub EXPTSTATE_PRERUN() { "prerunning"; }
sub EXPTSTATE_SWAPPED() { "swapped"; }
sub EXPTSTATE_SWAPPING() { "swapping"; }
sub EXPTSTATE_ACTIVATING() { "activating"; }
sub EXPTSTATE_ACTIVE() { "active"; }
sub EXPTSTATE_TESTING() { "testing"; }
sub EXPTSTATE_TERMINATING() { "terminating"; }
sub EXPTSTATE_TERMINATED() { "ended"; }
#
# We want valid project membership to be non-zero for easy membership
# testing. Specific trust levels are encoded thusly.
......@@ -167,28 +188,25 @@ sub NodeAccessCheck($;$)
#
# Check project membership. First argument is the project to check.
# Second argument is optional uid, defaults to the current uid.
# Second argument is optional DB uid, defaults to the current uid.
# The return argument encodes the trust membership for members.
#
# usage: ProjMember(char *pid, [int uid])
# usage: ProjMember(char *pid, [char *dbuid])
# returns PROJMEMBERTRUST_NONE if uid is not a member or trust=none.
# returns PROJMEMBERTRUST_USER if uid is a mere user in pid.
# returns PROJMEMBERTRUST_ROOT if uid is a root user in pid.
#
sub ProjMember($;$)
{
my($pid, $uid) = @_;
my($pid, $dbuid) = @_;
if (!defined($uid)) {
$uid = $UID;
if (!defined($dbuid) && !UNIX2DBUID($UID, \$dbuid)) {
die("$UID is not in the Emulab DB.\n");
}
my ($name) = getpwuid($uid)
or die "$uid not in passwd file\n";
my $query_result =
DBQueryFatal("select trust from proj_memb where ".
"uid='$name' and pid='$pid'");
"uid='$dbuid' and pid='$pid'");
if ($query_result->numrows == 0) {
return PROJMEMBERTRUST_NONE;
......@@ -233,6 +251,52 @@ sub ExpLeader($$)
return $row[0];
}
#
# Return Experiment state.
#
# usage: ExpState(char *pid, char *eid)
# returns state if a valid pid/eid.
# returns 0 if an invalid pid/eid or if an error.
#
sub ExpState($$)
{
my($pid, $eid) = @_;
my $query_result =
DBQueryWarn("select state from experiments ".
"where eid='$eid' and pid='$pid'");
if (! $query_result ||
$query_result->numrows == 0) {
return 0;
}
my @row = $query_result->fetchrow_array();
return $row[0];
}
#
# Set Experiment state.
#
# usage: SetExpState(char *pid, char *eid, char *state)
# returns 1 if okay.
# returns 0 if an invalid pid/eid or if an error.
#
sub SetExpState($$$)
{
my($pid, $eid, $state) = @_;
my $query_result =
DBQueryWarn("update experiments set state='$state' ".
"where eid='$eid' and pid='$pid'");
if (! $query_result ||
$query_result->numrows == 0) {
return 0;
}
return 1;
}
#
# Mark a node as down, moving it to special pid/eid. First argument is nodeid.
#
......@@ -342,21 +406,44 @@ sub NodeidToExp ($$$) {
}
#
# Map UID to user_login, user_name, and user_email.
# Map login (db uid) to a user_name and user_email.
#
# usage: UIDInfo(int uid, \$login, \$name, \$email)
# usage: UserDBInfo(char *dbuid, \$name, \$email)
# returns 1 if the UID is okay.
# returns 0 if the UID is bogus.
#
sub UIDInfo ($$$$) {
my($uid, $userlogin, $username, $useremail) = @_;
sub UserDBInfo ($$$) {
my($dbuid, $username, $useremail) = @_;
my($name) = getpwuid($uid)
or die "$uid not in passwd file\n";
my $query_result =
DBQueryFatal("select usr_name,usr_email from users ".
"where uid='$dbuid'");
if ($query_result->num_rows < 1) {
return 0;
}
my @row = $query_result->fetchrow_array();
$$username = $row[0];
$$useremail = $row[1];
return 1;
}
#
# Map UID to DB UID (login). Does a DB check to make sure user is known to
# the DB (user obviously has a regular account), and that account will
# always match what the DB says. Redundant, I know. But consider it a
# sanity (or consistency) check.
#
# usage: UNIX2DBUID(int uid, \$login)
# returns 1 if the UID is okay.
# returns 0 if the UID is bogus.
#
sub UNIX2DBUID ($$) {
my($unix_uid, $userlogin) = @_;
my $query_result =
DBQueryFatal("select uid,usr_name,usr_email from users ".
"where uid='$name'");
DBQueryFatal("select uid from users where unix_uid='$unix_uid'");
if ($query_result->num_rows < 1) {
return 0;
......@@ -364,8 +451,6 @@ sub UIDInfo ($$$$) {
my @row = $query_result->fetchrow_array();
$$userlogin = $row[0];
$$username = $row[1];
$$useremail = $row[2];
return 1;
}
......@@ -392,7 +477,7 @@ sub DBQuery($)
if (! $result) {
$DBErrorString =
" Query: $query\n".
" Error: " . $DB->errstr . "\n";
" Error: " . $DB->errstr;
}
return $result;
}
......@@ -451,12 +536,13 @@ sub DBWarn($)
$progname = "Tainted";
}
$text = "In TB script: $progname\n\n $message\n" . "$DBErrorString";
$text = "$message - In $progname\n" .
"$DBErrorString\n";
print STDERR "$text\n";
print STDERR "*** $text\n";
system("echo \"$text\" | /usr/bin/mail ".
"-s 'TESTBED: DBFatal - $message' \"$TBOPS\"");
"-s 'TESTBED: DBError - $message' \"$TBOPS\"");
}
#
......
......@@ -79,5 +79,4 @@ foreach $lan (keys(%lans)) {
}
print "\$ns run\n";
exit(0);
#!/usr/bin/perl -w
use English;
use POSIX qw(strftime);
package libtestbed;
use Exporter;
@ISA = "Exporter";
@EXPORT =
qw ( SENDMAIL OPENMAIL );
qw ( SENDMAIL OPENMAIL TBTimeStamp);
# A library of useful stuff.
......@@ -80,4 +81,14 @@ sub OPENMAIL($$;$$)
return(*MAIL);
}
#
# Return a timestamp. We don't care about day/date/year. Just the time mam.
#
# TBTimeStamp()
#
sub TBTimeStamp()
{
return POSIX::strftime("%H:%M:%S", localtime());
}
1;
......@@ -33,6 +33,13 @@ my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $TBINFO = "$TB/expinfo";
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
my $tbdir = "$TB/bin/";
my $projroot = "/proj";
my $tbdata = "tbdata";
......@@ -58,10 +65,6 @@ $| = 1;
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Testbed Support library
use lib "@prefix@/lib";
use libtbsetup;
#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
......@@ -114,12 +117,15 @@ my $repfile = "$eid.report";
my $tempns = "$tempfile.$$";
my $user_name = "Startexp Script";
my $user_email = "$TBOPS";
my $dbuid;
#
# Set up for querying the database.
#
use Mysql;
my $DB = Mysql->connect("localhost", $DBNAME, "script", "none");
# Verify user and get his DB uid.
#
if (! UNIX2DBUID($EUID, \$dbuid)) {
print STDOUT "Go Away! You do not exist in the Emulab Database.\n";
exit(1);
}
#
# Check to make sure the experiment record exists. The experiment must
......@@ -131,56 +137,36 @@ my $DB = Mysql->connect("localhost", $DBNAME, "script", "none");
# Note that tbprerun is going verify and change this state.
#
$query_result =
$DB->query("SELECT state FROM experiments ".
"WHERE eid='$eid' and pid='$pid'");
if (! $query_result) {
fatal("DB Error getting experiment record $pid/$eid\n");
}
DBQueryFatal("SELECT state FROM experiments ".
"WHERE eid='$eid' and pid='$pid'");
if ($query_result->numrows < 1) {
print STDOUT "No experiment record for $pid/$eid exists!\n";
exit(1);
}
@row = $query_result->fetchrow_array();
if ($row[0] ne "new") {
if ($row[0] ne EXPTSTATE_NEW) {
die("Experiment $pid/$eid is already configured (or still configuring)!\n".
"You are not allowed to reconfigure experiments unless you\n".
"first terminate the existing experiment via the web interface.\n");
}
#
# Get some user information.
# Get email info for user.
#
$query_result =
$DB->query("SELECT uid,usr_name,usr_email from users ".
"WHERE unix_uid='$EUID'");
if (! $query_result) {
fatal("DB Error getting user information for uid $EUID\n");
}
if ($query_result->numrows < 1) {
print STDOUT "Go Away! You do not exist in the Emulab Database.\n";
my $user_name;
my $user_email;
if (! UserDBInfo($dbuid, \$user_name, \$user_email)) {
print STDOUT "Cannot determine your name and email address.\n";
exit(1);
}
@row = $query_result->fetchrow_array();
$uid = $row[0];
$user_name = $row[1];
$user_email = $row[2];
#
# Verify that this person is allowed to start the experiment. Must be
# in the project membership table.
#
$query_result =
$DB->query("SELECT pid FROM proj_memb ".
"WHERE uid=\"$uid\" and pid=\"$pid\"");
if (! $query_result) {
fatal("DB Error getting project membership for uid $uid\n");
}
if ($query_result->numrows == 0) {
print STDOUT "Go Away! You are not a member of project $pid\n";
if (ProjMember($pid, $dbuid) != PROJMEMBERTRUST_ROOT) {
print STDOUT
"You do not have permission so start experiments in project $pid\n";
exit(1);
}
......@@ -235,30 +221,30 @@ if (system("/bin/cp", "$tempns", "$nsfile") != 0) {
# Run the various scripts. We want to propogate the error from tbprerun
# and tbrun back out, hence the bogus looking errorstat variable.
#
if (system("$tbdir/tbprerun -nologfile $pid $eid $nsfile") != 0) {
if (system("$tbdir/tbprerun $pid $eid $nsfile") != 0) {
$errorstat = $? >> 8;
fatal("tbprerun failed!\n");
}
# So fatal errors run tbend.
$estate = "prerunned";
$estate = EXPTSTATE_PRERUN;
if (system("$tbdir/tbswapin -nologfile $pid $eid") != 0) {
if (system("$tbdir/tbswapin $pid $eid") != 0) {
$errorstat = $? >> 8;
fatal("tbswapin failed!\n");
}
# So fatal errors run tbswapout,
$estate = "active";
$estate = EXPTSTATE_ACTIVE;
if (system("$tbdir/tbreport -v $pid $eid 2>&1 > $repfile") != 0) {
fatal("tbreport failed!\n");
}
#
# Increment the experiment count, and date. This is informational.
# Increment the project experiment count/lastdate. This is informational.
#
$DB->query("update projects ".
"set expt_count=expt_count+1, expt_last=NOW() ".
"where pid='$pid'");
DBQueryWarn("update projects ".
"set expt_count=expt_count+1, expt_last=NOW() ".
"where pid='$pid'");
#
# In batchmode, send the report to stdout for the batch daemon.
......@@ -272,9 +258,8 @@ if ($batch) {
# Grab the experiment info for the mail message below.
#
$query_result =
$DB->query("SELECT expt_name,expt_created,expt_expires from experiments ".
"WHERE eid='$eid' and pid='$pid'");
DBQueryWarn("SELECT expt_name,expt_created,expt_expires from experiments ".
"WHERE eid='$eid' and pid='$pid'");
if (! $query_result) {
fatal("DB Error getting experiment record for $pid/$eid\n");
}
......@@ -307,17 +292,17 @@ if ($infodir =~ /^([-\@\w.]+)$/) {
$nsfile_string = `cat $nsfile`;
if ($nsfile_string) {
$nsfile_string = $DB->quote($nsfile_string);
$nsfile_string = DBQuoteSpecial($nsfile_string);
$DB->query("delete from nsfiles WHERE eid='$eid' and pid='$pid'");
DBQueryWarn("delete from nsfiles WHERE eid='$eid' and pid='$pid'");
#
# I could strlen check the string, but the webserver has a limit,
# plus the DB is going to truncate it if its longer. Doing it here
# would be a third (call it redundant) check.
#
$DB->query("insert into nsfiles (pid, eid, nsfile) ".
"VALUES('$pid', '$eid', $nsfile_string)");
DBQueryWarn("insert into nsfiles (pid, eid, nsfile) ".
"VALUES('$pid', '$eid', $nsfile_string)");
}
#
......@@ -406,13 +391,8 @@ sub fatal()
#
# Now we can remove all trace from the DB since it failed.
#
$query_result =
$DB->query("DELETE from experiments WHERE eid='$eid' and pid='$pid'");
if (! $query_result) {
print STDOUT "DB Error deleting experiment record for $pid/$eid\n";
}
DBQueryWarn("DELETE from experiments WHERE eid='$eid' and pid='$pid'");
DBQueryWarn("DELETE from nsfiles WHERE eid='$eid' and pid='$pid'");
#
# In batch mode, exit. Make sure to delete tempns file.
......@@ -492,14 +472,14 @@ sub fatal()
#
sub tbendit()
{
if ($estate eq "active") {
print "Running tbswapout with arguments: -nologfile $pid $eid\n";
if (system("$tbdir/tbswapout -nologfile $pid $eid") != 0) {
if ($estate eq EXPTSTATE_ACTIVE) {
print "Running tbswapout with arguments: $pid $eid\n";
if (system("$tbdir/tbswapout $pid $eid") != 0) {
print "tbswapout failed!\n";
}
}
print "Running tbend with arguments: -nologfile -force $pid $eid\n";
if (system("$tbdir/tbend -nologfile -force $pid $eid") != 0) {
print "Running tbend with arguments: -force $pid $eid\n";
if (system("$tbdir/tbend -force $pid $eid") != 0) {
print "tbend failed!\n";
}
}
......
#!/usr/bin/perl -w
use English;
# tbend
# This is the fourth and final program in the
# tbprerun/tbswapin/tbswapout/.../tbend sequences. It's purpose is to
# reverse what tbprerun did. I.e. clear the virt_nodes and virt_lans
# tables.
# tables.
sub usage()
{
print STDERR "Usage: $0 [-force] pid eid\n";
exit(-1);
}
use DBI;
#
# Configure variables
#
my $TBROOT = "@prefix@";
# Set up paths
$TBDB = "@TBDBNAME@";
$TBROOT = "@prefix@";
# Untaint the path
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" .
":$TBROOT/sbin:$TBROOT/bin";
# Turn off line buffering.
$| = 1;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtbsetup;
use libdb;
use libtestbed;
require exitonwarn; # exitonwarn isn't really a module, so just require it
$dolog = 1;
$force = 0;
#
# Turn off line buffering on output
#
$| = 1;
my $force = 0;
my $errors = 0;
my $state;
while ($#ARGV > 1) {
$arg = shift;
if ($arg eq "-nologfile") {
$dolog = 0;
} elsif ($arg eq "-force") {
if ($arg eq "-force") {
$force = 1;
} else {
print STDERR "Syntax: $0 [-nologfile] [-force] pid eid\n";
exit(1);
usage();
}
}
if ($#ARGV < 1) {
print STDERR "Syntax: $0 [-nologfile] [-force] pid eid\n";
exit(1);
}
($pid,$eid) = @ARGV;
$dbh = &tbs_initdbi($TBDB);
$logfile = "$pid-$eid.log";
if ($dolog) {
&tbs_initlog($logfile);
&tbs_out("Log: $logfile\n");
usage();
}
my ($pid,$eid) = @ARGV;
&tbs_out("Beginning cleanup for $pid-$eid. "
. &ctime(time) . "\n");
print "Beginning cleanup for $pid/$eid. " . TBTimeStamp() . "\n";
$errors = 0;
&tbs_out("Checking sanity.\n");
$sth = $dbh->prepare("SELECT state from experiments where pid = \"$pid\"" .
" and eid = \"$eid\"");
$sth->execute();
if (! (($state) = $sth->fetchrow_array())) {
&tbs_out("$0: *** No entry in experiments table. Insane.\n");
if (! ($state = ExpState($pid, $eid))) {
print STDERR "*** No such experiment $pid/$eid\n";
exit(1);
}
if ($force == 0) {
if ($state eq "active") {
&tbs_out("$0: *** Experiment already running." .
" Try swapping out first.\n");
if (! $force) {
if ($state eq EXPTSTATE_ACTIVE) {
print STDERR "*** Experiment is running. Must be swapped out.\n";
exit(1);
} elsif ($state ne "swapped") {
&tbs_out("$0: *** Experiment in strange state: $state.\n");
}
elsif ($state ne EXPTSTATE_SWAPPED) {
print STDERR "*** Experiment is in the wrong state: $state.\n";
exit(1);
}
} elsif ($state ne "swapped") {
&tbs_out("$0: *** WARNING: Ignoring experimental state of $state.\n");
}
if (! $dbh->do("UPDATE experiments set state = \"terminating\" " .
"where pid=\"$pid\" and eid=\"$eid\"")) {
&tbs_out("$0: *** Could not set intermediate experiment state.\n");
}
&tbs_out("Clearing virtual state.\n");
if (! $dbh->do("DELETE from virt_nodes where pid = \"$pid\"" .
" and eid = \"$eid\"")) {
&tbs_out("$0: *** Could not clear virt_nodes table.\n");
$errors = 1;
}
if (! $dbh->do("DELETE from virt_lans where pid = \"$pid\"" .
" and eid = \"$eid\"")) {
&tbs_out("$0: *** Could not clear virt_lans table.\n");
$errors = 1;
elsif ($state ne EXPTSTATE_SWAPPED) {
print STDERR "*** WARNING: Ignoring improper state: $state.\n";
}
if (! $dbh->do("DELETE from portmap where pid = \"$pid\"" .
" and eid = \"$eid\"")) {
&tbs_out("$0: *** Could not clear portmap table.\n");
$errors = 1;
if (! SetExpState($pid, $eid, EXPTSTATE_TERMINATING)) {
print STDERR "*** Failed to set intermediate experiment state.\n";
exit(1);
}
$sth->finish();
print "Clearing out virtual state.\n";
DBQueryWarn("DELETE from virt_nodes where pid='$pid' and eid='$eid'") or
$errors++;
DBQueryWarn("DELETE from virt_lans where pid='$pid' and eid='$eid'") or
$errors++;
DBQueryWarn("DELETE from portmap where pid='$pid' and eid='$eid'") or
$errors++;
if ($errors == 0) {
&tbs_out("Marking as ended.\n");
if (! $dbh->do("UPDATE experiments set state = \"ended\"" .
" where pid=\"$pid\" and eid=\"$eid\"")) {
&tbs_out("$0: *** Could not set experiment state.\n");
$errors = 1;
}
SetExpState($pid, $eid, EXPTSTATE_TERMINATED) or
$errors++;
}
&tbs_out("Cleanup finished - " . &ctime(time) . "\n");
print "Cleanup finished! " . TBTimeStamp() . "\n";
# We exit with whether we had errors or not.
exit $errors;
#!/usr/bin/perl -w
use English;
# tbprerun
......@@ -8,114 +9,81 @@
# virt_nodes and virt_lans. After this script ends successfully the
# NS file is no longer necessary.
use DBI;
#
# Configure variables
#
my $TBROOT = "@prefix@";
# Set up paths
$TBDB = "@TBDBNAME@";
$TBROOT = "@prefix@";
# Untaint the path
$ENV{'PATH'} = "/usr/bin:$TBROOT/libexec:$TBROOT/libexec/ns2ir" .
":$TBROOT/sbin:$TBROOT/bin";
# Turn off line buffering.
$| = 1;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libtbsetup;
use libdb;
use libtestbed;
require exitonwarn; # exitonwarn isn't really a module, so just require it
#
# Turn off line buffering on output
#
$| = 1;
if ($#ARGV < 2 || $#ARGV > 3 ||
($#ARGV == 3 && $ARGV[0] ne "-nologfile")) {
print STDERR "Syntax: $0 [-nologfile] pid eid ns_file\n";
if ($#ARGV != 2) {
print STDERR "Syntax: $0 pid eid ns_file\n";
exit(1);
}
if ($#ARGV == 3) {
shift;
$dolog = 0;
}
else {
$dolog = 1;
}
($pid,$eid,$nsfile) = @ARGV;
$dbh=&tbs_initdbi($TBDB);
$logfile = "$pid-$eid.log";
if ($dolog) {
&tbs_initlog($logfile);
&tbs_out("Log: $logfile\n");
}
&tbs_out("Input: $nsfile\n");
&tbs_out("\n");
&tbs_out("Beginning pre run for $pid-$eid ($nsfile). "
. &ctime(time) . "\n");
my ($pid,$eid,$nsfile) = @ARGV;
if (! -r $nsfile) {
&tbs_out("$nsfile does not exist or is not readable.\n");
print STDERR "*** NS File '$nsfile' does not exist!\n";
exit(1);
}
&tbs_out("Checking sanity.\n");
$sth = $dbh->prepare("SELECT state from experiments where pid = \"$pid\"" .
" and eid = \"$eid\"");
$sth->execute();
if (! (($state) = $sth->fetchrow_array())) {
&tbs_out("$0: *** No entry in experiments table. Insane.\n");
my $state;
print "Beginning pre run for $pid/$eid. " . TBTimeStamp() . "\n";
if (! ($state = ExpState($pid, $eid))) {
print STDERR "*** No such experiment $pid/$eid\n";
exit(1);
}
$sth->finish();
if ($state ne "new") {
&tbs_out("$0: *** Experiment is not in the proper state: $state\n");