diff --git a/db/libdb.pm.in b/db/libdb.pm.in index 9f1bf65dbda48ccdad5236024e172dc30e8197c0..985aba1a33388f6634ee5d6f05ee52e2a07d363f 100644 --- a/db/libdb.pm.in +++ b/db/libdb.pm.in @@ -42,7 +42,7 @@ use Exporter; USERSTATUS_ACTIVE USERSTATUS_FROZEN USERSTATUS_UNAPPROVED USERSTATUS_UNVERIFIED USERSTATUS_NEWUSER - TB_EXPT_READINFO TB_EXPT_MODIFY TB_EXPT_DESTROY + TB_EXPT_READINFO TB_EXPT_MODIFY TB_EXPT_DESTROY TB_EXPT_UPDATE TB_EXPT_MIN TB_EXPT_MAX TB_PROJECT_READINFO TB_PROJECT_MAKEGROUP @@ -352,8 +352,9 @@ sub TB_USERINFO_MAX() { TB_USERINFO_MODIFYINFO; } sub TB_EXPT_READINFO() { 1; } sub TB_EXPT_MODIFY() { 2; } sub TB_EXPT_DESTROY() { 3; } +sub TB_EXPT_UPDATE() { 4; } sub TB_EXPT_MIN() { TB_EXPT_READINFO; } -sub TB_EXPT_MAX() { TB_EXPT_DESTROY; } +sub TB_EXPT_MAX() { TB_EXPT_UPDATE; } # Projects. sub TB_PROJECT_READINFO() { 1; } diff --git a/os/GNUmakefile.in b/os/GNUmakefile.in index 4d90d6360028ba04424abc8c8e52084acaead207..f88eb91bcb984fca8c3cd2d8f777e236e02973ca 100644 --- a/os/GNUmakefile.in +++ b/os/GNUmakefile.in @@ -10,6 +10,7 @@ OBJDIR = .. SUBDIR = os LBINDIR = $(DESTDIR)/usr/local/bin +SYSTEM := $(shell uname -s) include $(OBJDIR)/Makeconf @@ -39,8 +40,10 @@ client-install: $(INSTALL) -m 755 -o root -g wheel -d $(LBINDIR) $(INSTALL_PROGRAM) $(SRCDIR)/install-tarfile $(LBINDIR)/install-tarfile $(INSTALL_PROGRAM) $(SRCDIR)/install-rpm $(LBINDIR)/install-rpm +ifneq ($(SYSTEM),Linux) $(INSTALL_PROGRAM) $(SRCDIR)/create-image $(LBINDIR)/create-image $(MAKE) -C imagezip client-install +endif $(MAKE) -C syncd client-install remote-install: diff --git a/os/install-rpm b/os/install-rpm index 490256e0ca3c179d396562e409ccb119e0688226..b725be78ebd024fc28a6cc005475e81a89fd3f6e 100644 --- a/os/install-rpm +++ b/os/install-rpm @@ -1,17 +1,14 @@ #!/usr/bin/perl -wT - # # EMULAB-COPYRIGHT # Copyright (c) 2000-2003 University of Utah and the Flux Group. # All rights reserved. # - use English; use Getopt::Std; -use POSIX 'setsid'; +use POSIX qw(mktime); # Drag in path stuff so we can find emulab stuff. -# XXX Temporary until I have the new tmcc library finished! BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; } # @@ -54,15 +51,22 @@ my $copymode = 0; my $debug = 0; my $copyfile; my $nodeid; +my $keyhash; +my $filemd5; +my @identlines = (); +my $installed = 0; # # Load the OS independent support library. It will load the OS dependent # library and initialize itself. # use libsetup; +use libtmcc; # Protos -sub GetRPMFile($$$); +sub GetRPMFile($$$$$$$); +sub GetMD5($); +sub WriteIdentFile(); # # Must be running as root to work. @@ -112,14 +116,39 @@ else { } # -# Check to make sure this RPM has not already been installed. Update -# the file now. If the rpm fails, we got big problems. -# +# Check to make sure this rpm has not already been installed. +# If so, we get the old timestamp and md5 so we can compare against +# current ones. +# We need to update the stamp/md5 in place in case it has changed, so +# copy out all the identlines so we can write them back later. We do not +# copyout the current one of course; we make up a new line at the end +# of this script based on the new info. +# if (-e $IDENTFILE) { - if (! system("egrep -q -s '^${rpm}' $IDENTFILE")) { - print STDOUT "RPM $rpm has already been installed!\n"; - exit(1); + if (!open(IDENT, $IDENTFILE)) { + fatal("Could not open $IDENTFILE: $!"); } + while () { + if ($_ =~ /^([-\w\.\/]*) ([\d]*) ([\w]*)$/) { + my $file = $1; + my $stamp= $2; + my $md5 = $3; + + if ($file eq $rpm) { + # + # Save the info and continue; + # + $oldstamp = $stamp; + $oldmd5 = $md5; + next; + } + push(@identlines, "$file $stamp $md5"); + } + else { + warn("*** WARNING: Bad line in $IDENTFILE: $_\n"); + } + } + close(IDENT); } # @@ -133,6 +162,28 @@ if (! $copymode) { if (! -r $rpm) { fatal("$rpm does not exist or is not accessible!"); } + + # + # Compare timestamp. If no change, we are done. + # + (undef,undef,undef,undef,undef,undef, + undef,undef,undef,$filestamp) = stat($rpm); + + if (defined($oldstamp) && $oldstamp >= $filestamp) { + print STDOUT "RPM $rpm has already been installed!\n"; + exit(1); + } + # + # Otherwise compare MD5. + # + $filemd5 = GetMD5($rpm); + if (defined($oldmd5) && $filemd5 eq $oldmd5) { + print STDOUT "RPM $rpm has already been installed!\n"; + # Must write a new ident file to avoid repeated checks. + push(@identlines, "$rpm $filestamp $filemd5"); + WriteIdentFile(); + exit(1); + } } else { $copyfile = `mktemp /var/tmp/rpm.XXXXXX`; @@ -143,18 +194,29 @@ else { else { die("Bad data in copyfile name: $copyfile"); } - GetRPMFile($rpm, $copyfile, $usewget); # - # Dies on any failure! + # Dies on any failure. + # Returns >0 if server copy has not been modifed. + # Returns =0 if okay to install, and gives us new stamp/md5. # + if (GetRPMFile($rpm, $copyfile, $usewget, + $oldstamp, $oldmd5, \$filestamp, \$filemd5)) { + print STDOUT "RPM $rpm has already been installed!\n"; + if (defined($filestamp) && $filestamp != $oldstamp) { + # Must write a new ident file to avoid repeated checks. + push(@identlines, "$rpm $filestamp $oldmd5"); + WriteIdentFile(); + } + unlink($copyfile) + if (-e $copyfile); + exit(1); + } } # -# Add to index first; if fails too bad. -# -if (system("echo \"$rpm\" >> $IDENTFILE")) { - fatal("Could not update $IDENTFILE"); -} +# Okay, add new info to the list for update. +# +push(@identlines, "$rpm $filestamp $filemd5"); # # Run the RPM. @@ -162,12 +224,18 @@ if (system("echo \"$rpm\" >> $IDENTFILE")) { if ($copymode) { $rpm = $copyfile; } -system("rpm -i $rpm"); +system("rpm -U --force --nodeps $rpm"); $exit_status = $? >> 8; if ($copymode) { unlink($copyfile); } +# +# Recreate the index file if the install was okay. +# +if (!$exit_status) { + WriteIdentFile(); +} exit($exit_status); sub fatal { @@ -183,19 +251,33 @@ sub fatal { # # Get an RPM from the server via tmcc and stash. # -sub GetRPMFile($$$) +sub GetRPMFile($$$$$$$) { - my ($rpm, $copyfile, $usewget) = @_; + my ($rpm, $copyfile, $usewget, + $oldstamp, $oldmd5, $filestamp, $filemd5) = @_; my $buf; - my $bytelen; # # If copying via NFS, must watch for read errors and retry. # if (! $usewget) { + # + # Compare timestamp. If no change, we are done. + # + my (undef,undef,undef,undef,undef,undef,undef,$bytelen, + undef,$stamp) = stat($rpm); + + if (defined($oldstamp) && $oldstamp >= $stamp) { + print STDOUT "Timestamp ($stamp) for $rpm unchanged!\n" + if ($debug); + return 1; + } + # Must do this for caller so that if the MD5 has not changed, + # the caller can update the timestamp in the ident file. + $$filestamp = $stamp; + open(TMCC, "< $rpm") or fatal("Could not open rpm on server!"); - $bytelen = (stat($rpm))[7]; # # Open the target file and start dumping the data in. @@ -237,6 +319,17 @@ sub GetRPMFile($$$) } close(JFILE); close(TMCC); + + # + # Compare md5. + # + my $md5 = GetMD5($copyfile); + if (defined($oldmd5) && $oldmd5 eq $md5) { + print STDOUT "MD5 ($md5) for $rpm unchanged!\n" + if ($debug); + return 2; + } + $$filemd5 = $md5; } else { # @@ -274,10 +367,8 @@ sub GetRPMFile($$$) # # Lastly, need our boss node. - # - my ($www) = split(" ", `tmcc bossinfo`); - die("Could not get bossinfo!") - if ($?); + # + my ($www) = tmccbossname(); if ($www =~ /^[-\w]+\.(.*)$/) { $www = "www.${1}"; @@ -285,22 +376,120 @@ sub GetRPMFile($$$) else { fatal("Tainted bossinfo $www!"); } + $www = "https://${www}"; + #$www = "https://${www}/dev/stoller"; + #$www = "http://golden-gw.ballmoss.com:9876/~stoller/testbed"; # # Okay, run wget with the proper arguments. # - my $cmd = "wget -q -O $copyfile ". + my $cmd = "wget -nv -O $copyfile ". ($debug ? "--server-response " : "") . - "'https://${www}/spewrpmtar.php3". - "?nodeid=${nodeid}&file=${rpm}&key=${keyhash}'"; + "'${www}/spewrpmtar.php3". + "?nodeid=${nodeid}&file=${rpm}&key=${keyhash}". + (defined($oldstamp) ? "&stamp=$oldstamp" : "") . + (defined($oldmd5) ? "&md5=$oldmd5" : "") . + "'"; if ($debug) { print STDERR "$cmd\n"; } - system($cmd); - fatal("Could not retrieve $rpm from $www") - if ($?); + # + # We need to read back the response to see if the file was + # unchanged. This is dumb; why doesn't wget exit with reasonable + # error codes? + # + my $nochange = 0; + if (!open(WGET, "$cmd 2>&1 |")) { + fatal("Cannot start wget: $!\n"); + } + while () { + print $_ + if ($debug); + + # Ick! + if ($_ =~ /^.* ERROR 304.*$/i) { + $nochange = 1; + } + } + if (! close(WGET)) { + if ($?) { + fatal("Could not retrieve $rpm from $www") + if (!$nochange); + # Otherwise, not modifed. + print STDOUT "Timestamp for $rpm unchanged!\n" + if ($debug); + return 1; + } + else { + fatal("Error closing wget pipe: $!\n"); + } + } + # Must do this for caller so that if the MD5 has not changed, + # the caller can update the timestamp in the ident file. + # + # Always use GM time for this. The server expects it. + $$filestamp = mktime(gmtime(time())); + + # + # We got a file. Compare the MD5 now. + # + my $md5 = GetMD5($copyfile); + if (defined($oldmd5) && $oldmd5 eq $md5) { + print STDOUT "MD5 ($md5) for $rpm unchanged!\n" + if ($debug); + return 2; + } + $$filemd5 = $md5; } return 0; } + +# +# Get MD5 of file. +# +sub GetMD5($) +{ + my ($file) = @_; + my $md5; + + if ($OSNAME eq "linux") { + $md5 = `md5sum $file`; + + if ($md5 =~ /^([\w]*)\s*.*$/) { + $md5 = $1; + } + else { + fatal("Bad MD5 for $file: $md5."); + } + } + elsif ($OSNAME eq "freebsd") { + $md5 = `md5 -q $file`; + + if ($md5 =~ /^([\w]*)$/) { + $md5 = $1; + } + else { + fatal("Bad MD5 for $file: $md5."); + } + } + else { + fatal("Do not know how to compute MD5s!"); + } + return $md5; +} + +# +# Recreate the ident file. +# +sub WriteIdentFile() +{ + if (!open(IDENT, "> $IDENTFILE")) { + fatal("Could not open $IDENTFILE for writing: $!"); + } + foreach my $id (@identlines) { + print IDENT "$id\n"; + } + close(IDENT); +} diff --git a/os/install-tarfile b/os/install-tarfile index 2f31552fcfab742a5c8219612f9a3955357f0e2e..d16f7a2f9b850a949b3b43b32377960f050c7716 100755 --- a/os/install-tarfile +++ b/os/install-tarfile @@ -1,17 +1,14 @@ #!/usr/bin/perl -wT - # # EMULAB-COPYRIGHT # Copyright (c) 2000-2003 University of Utah and the Flux Group. # All rights reserved. # - use English; use Getopt::Std; -use POSIX 'setsid'; +use POSIX qw(mktime); # Drag in path stuff so we can find emulab stuff. -# XXX Temporary until I have the new tmcc library finished! BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; } # @@ -58,15 +55,23 @@ my $debug = 0; my $copyfile; my $nodeid; my $keyhash; +my $filemd5; +my $filestamp; +my $oldmd5; +my $oldstamp; +my @identlines = (); # # Load the OS independent support library. It will load the OS dependent # library and initialize itself. # use libsetup; +use libtmcc; # Protos -sub GetTarFile($$$); +sub GetTarFile($$$$$$$); +sub GetMD5($); +sub WriteIdentFile(); # # Must be running as root to work. @@ -138,14 +143,39 @@ if (! -d $installdir) { } # -# Check to make sure this tarfile has not already been installed. Update -# the file now. If the tar fails, we got big problems. -# +# Check to make sure this tarfile has not already been installed. +# If so, we get the old timestamp and md5 so we can compare against +# current ones. +# We need to update the stamp/md5 in place in case it has changed, so +# copy out all the identlines so we can write them back later. We do not +# copyout the current one of course; we make up a new line at the end +# of this script based on the new info. +# if (-e $IDENTFILE) { - if (! system("egrep -q -s '^${tarfile}' $IDENTFILE")) { - print STDOUT "Tarfile $tarfile has already been installed!\n"; - exit(1); + if (!open(IDENT, $IDENTFILE)) { + fatal("Could not open $IDENTFILE: $!"); } + while () { + if ($_ =~ /^([-\w\.\/]*) ([\d]*) ([\w]*)$/) { + my $file = $1; + my $stamp= $2; + my $md5 = $3; + + if ($file eq $tarfile) { + # + # Save the info and continue; + # + $oldstamp = $stamp; + $oldmd5 = $md5; + next; + } + push(@identlines, "$file $stamp $md5"); + } + else { + warn("*** WARNING: Bad line in $IDENTFILE: $_\n"); + } + } + close(IDENT); } # @@ -159,6 +189,28 @@ if (! $copymode) { if (! -r $tarfile) { fatal("$tarfile does not exist or is not accessible!"); } + + # + # Compare timestamp. If no change, we are done. + # + (undef,undef,undef,undef,undef,undef, + undef,undef,undef,$filestamp) = stat($tarfile); + + if (defined($oldstamp) && $oldstamp >= $filestamp) { + print STDOUT "Tarfile $tarfile has already been installed!\n"; + exit(1); + } + # + # Otherwise compare MD5. + # + $filemd5 = GetMD5($tarfile); + if (defined($oldmd5) && $filemd5 eq $oldmd5) { + print STDOUT "Tarfile $tarfile has already been installed!\n"; + # Must write a new ident file to avoid repeated checks. + push(@identlines, "$tarfile $filestamp $filemd5"); + WriteIdentFile(); + exit(1); + } } else { $copyfile = `mktemp /var/tmp/tarball.XXXXXX`; @@ -169,18 +221,29 @@ else { else { die("Bad data in copyfile name: $copyfile"); } - GetTarFile($tarfile, $copyfile, $usewget); # - # Dies on any failure! + # Dies on any failure. + # Returns >0 if server copy has not been modifed. + # Returns =0 if okay to install, and gives us new stamp/md5. # + if (GetTarFile($tarfile, $copyfile, $usewget, + $oldstamp, $oldmd5, \$filestamp, \$filemd5)) { + print STDOUT "Tarfile $tarfile has already been installed!\n"; + if (defined($filestamp) && $filestamp != $oldstamp) { + # Must write a new ident file to avoid repeated checks. + push(@identlines, "$tarfile $filestamp $oldmd5"); + WriteIdentFile(); + } + unlink($copyfile) + if (-e $copyfile); + exit(1); + } } # -# Add to index first; if fails too bad. -# -if (system("echo \"$tarfile\" >> $IDENTFILE")) { - fatal("Could not update $IDENTFILE"); -} +# Okay, add new info to the list for update. +# +push(@identlines, "$tarfile $filestamp $filemd5"); # # Figure what decompression flag is required, based on file extension. @@ -211,6 +274,12 @@ if ($copymode) { unlink($copyfile); } +# +# Recreate the index file if the install was okay. +# +if (!$exit_status) { + WriteIdentFile(); +} exit($exit_status); sub fatal { @@ -226,19 +295,33 @@ sub fatal { # # Get a tarfile from the server via tmcc and stash. # -sub GetTarFile($$$) +sub GetTarFile($$$$$$$) { - my ($tarfile, $copyfile, $usewget) = @_; + my ($tarfile, $copyfile, $usewget, + $oldstamp, $oldmd5, $filestamp, $filemd5) = @_; my $buf; - my $bytelen; # # If copying via NFS, must watch for read errors and retry. # if (! $usewget) { + # + # Compare timestamp. If no change, we are done. + # + my (undef,undef,undef,undef,undef,undef,undef,$bytelen, + undef,$stamp) = stat($tarfile); + + if (defined($oldstamp) && $oldstamp >= $stamp) { + print STDOUT "Timestamp ($stamp) for $tarfile unchanged!\n" + if ($debug); + return 1; + } + # Must do this for caller so that if the MD5 has not changed, + # the caller can update the timestamp in the ident file. + $$filestamp = $stamp; + open(TMCC, "< $tarfile") or fatal("Could not open tarfile on server!"); - $bytelen = (stat($tarfile))[7]; # # Open the target file and start dumping the data in. @@ -280,6 +363,17 @@ sub GetTarFile($$$) } close(JFILE); close(TMCC); + + # + # Compare md5. + # + my $md5 = GetMD5($copyfile); + if (defined($oldmd5) && $oldmd5 eq $md5) { + print STDOUT "MD5 ($md5) for $tarfile unchanged!\n" + if ($debug); + return 2; + } + $$filemd5 = $md5; } else { # @@ -317,10 +411,8 @@ sub GetTarFile($$$) # # Lastly, need our boss node. - # - my ($www) = split(" ", `tmcc bossinfo`); - die("Could not get bossinfo!") - if ($?); + # + my ($www) = tmccbossname(); if ($www =~ /^[-\w]+\.(.*)$/) { $www = "www.${1}"; @@ -328,22 +420,121 @@ sub GetTarFile($$$) else { fatal("Tainted bossinfo $www!"); } + $www = "https://${www}"; + #$www = "https://${www}/dev/stoller"; + #$www = "http://golden-gw.ballmoss.com:9876/~stoller/testbed"; # # Okay, run wget with the proper arguments. # - my $cmd = "wget -q -O $copyfile ". + my $cmd = "wget -nv -O $copyfile ". ($debug ? "--server-response " : "") . - "'https://${www}/spewrpmtar.php3". - "?nodeid=${nodeid}&file=${tarfile}&key=${keyhash}'"; + "'${www}/spewrpmtar.php3". + "?nodeid=${nodeid}&file=${tarfile}&key=${keyhash}" . + (defined($oldstamp) ? "&stamp=$oldstamp" : "") . + (defined($oldmd5) ? "&md5=$oldmd5" : "") . + "'"; + if ($debug) { print STDERR "$cmd\n"; } - system($cmd); - fatal("Could not retrieve $tarfile from $www") - if ($?); + # + # We need to read back the response to see if the file was + # unchanged. This is dumb; why doesn't wget exit with reasonable + # error codes? + # + my $nochange = 0; + if (!open(WGET, "$cmd 2>&1 |")) { + fatal("Cannot start wget: $!\n"); + } + while () { + print $_ + if ($debug); + + # Ick! + if ($_ =~ /^.* ERROR 304.*$/i) { + $nochange = 1; + } + } + if (! close(WGET)) { + if ($?) { + fatal("Could not retrieve $tarfile from $www") + if (!$nochange); + # Otherwise, not modifed. + print STDOUT "Timestamp for $tarfile unchanged!\n" + if ($debug); + return 1; + } + else { + fatal("Error closing wget pipe: $!\n"); + } + } + # Must do this for caller so that if the MD5 has not changed, + # the caller can update the timestamp in the ident file. + # + # Always use GM time for this. The server expects it. + $$filestamp = mktime(gmtime(time())); + + # + # We got a file. Compare the MD5 now. + # + my $md5 = GetMD5($copyfile); + if (defined($oldmd5) && $oldmd5 eq $md5) { + print STDOUT "MD5 ($md5) for $tarfile unchanged!\n" + if ($debug); + return 2; + } + $$filemd5 = $md5; } return 0; } + +# +# Get MD5 of file. +# +sub GetMD5($) +{ + my ($file) = @_; + my $md5; + + if ($OSNAME eq "linux") { + $md5 = `md5sum $file`; + + if ($md5 =~ /^([\w]*)\s*.*$/) { + $md5 = $1; + } + else { + fatal("Bad MD5 for $file: $md5."); + } + } + elsif ($OSNAME eq "freebsd") { + $md5 = `md5 -q $file`; + + if ($md5 =~ /^([\w]*)$/) { + $md5 = $1; + } + else { + fatal("Bad MD5 for $file: $md5."); + } + } + else { + fatal("Do not know how to compute MD5s!"); + } + return $md5; +} + +# +# Recreate the ident file. +# +sub WriteIdentFile() +{ + if (!open(IDENT, "> $IDENTFILE")) { + fatal("Could not open $IDENTFILE for writing: $!"); + } + foreach my $id (@identlines) { + print IDENT "$id\n"; + } + close(IDENT); +} diff --git a/tbsetup/GNUmakefile.in b/tbsetup/GNUmakefile.in index 4757307c72c35a7f21f2ed347af426541098af69..693967826f241a72f599fbb8cdee3b99d176ee58 100644 --- a/tbsetup/GNUmakefile.in +++ b/tbsetup/GNUmakefile.in @@ -140,8 +140,6 @@ post-install: chmod u+s $(INSTALL_BINDIR)/node_reboot chown root $(INSTALL_SBINDIR)/newnode_reboot chmod u+s $(INSTALL_SBINDIR)/newnode_reboot - chown root $(INSTALL_BINDIR)/node_update - chmod u+s $(INSTALL_BINDIR)/node_update chown root $(INSTALL_SBINDIR)/vnode_setup chmod u+s $(INSTALL_SBINDIR)/vnode_setup chown root $(INSTALL_BINDIR)/eventsys_control diff --git a/tbsetup/node_update.in b/tbsetup/node_update.in index 4447ec9fc52c651f3cda0d372a9aa8f31f905f2a..e8fedc7d6acb14cbf44af169a83d6e0a530dd587 100644 --- a/tbsetup/node_update.in +++ b/tbsetup/node_update.in @@ -2,7 +2,7 @@ # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2002 University of Utah and the Flux Group. +# Copyright (c) 2000-2003 University of Utah and the Flux Group. # All rights reserved. # @@ -10,28 +10,33 @@ use English; use Getopt::Std; # -# Update mounts and accounts and anything else after changing the permissions -# for a node. This is intended to be invoked from the web interface after -# adding and/or subtracting pids from the experiment pid access list. +# Mark nodes for update. At the moment all kinds of things will get +# updated (mounts, accounts, tarballs, rpms). At some point these +# should be split up. # # XXX There is an inherent race condition with using this script. What if # nodes are released while it is running? # -# Eventually, this script should be tossed. The widearea nodes poll -# for updates, and thats how local nodes should do it too. -# -# The output is all jumbled together since the updates are issued in parallel. -# Might be a pain when debugging. -# sub usage() { - print STDOUT "Usage: node_update [-b] \n". + print STDOUT "Usage: node_update [-b] pid eid [node ...]\n". "Update user accounts and NFS mounts on nodes in your project.\n". "Use -b to use batch operation (place in background, send email).\n"; exit(-1); } -my $optlist = "be:"; - +my $optlist = "b"; + +# +# 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 - Proceeding in the background. Notified later. +# $status > 0 - Expected error. User not allowed for some reason. +# + # # Configure variables # @@ -40,10 +45,15 @@ my $TESTMODE = @TESTMODE@; my $TBOPS = "@TBOPSEMAIL@"; my $TBLOGS = "@TBLOGSEMAIL@"; -my $ssh = "$TB/bin/sshtb -n"; my $expsetup = "$TB/sbin/exports_setup"; my $batchmode = 0; -my $maxchildren = 20; +my @nodes = (); +my $user_name; +my $user_email; +my $logname; +my $failed = 0; +my $dbuid; +my $estate; # # Load the Testbed support stuff. @@ -67,14 +77,14 @@ $| = 1; if (! getopts($optlist, \%options)) { usage(); } -if (@ARGV != 2) { - usage(); -} -my $pid = $ARGV[0]; -my $eid = $ARGV[1]; if (defined($options{"b"})) { $batchmode = 1; } +if (@ARGV < 2) { + usage(); +} +my $pid = shift(@ARGV); +my $eid = shift(@ARGV); # # Untaint the arguments. @@ -90,23 +100,23 @@ if ($eid =~ /^([-\@\w]+)$/) { } else { die("*** Bad data in eid: $eid\n"); -} - -my $user_name; -my $user_email; -my $logname; -my %pids = (); -my $failed = 0; -my $dbuid; -my @autonodes = (); +} # -# We don't want to run this script unless its the real version. -# That is, it must be setuid root. +# If more args, they are node names. # -if ($EUID != 0) { - die("*** $0:\n". - " Must be root! Maybe its a development version?\n"); +if (@ARGV) { + # Untaint the nodes. + foreach my $node ( @ARGV ) { + if ($node =~ /^([-\w]+)$/) { + $node = $1; + } + else { + die("*** Bad node name: $node."); + } + + push(@nodes, $node); + } } # @@ -123,22 +133,12 @@ if (! UserDBInfo($dbuid, \$user_name, \$user_email)) { } # -# Verify that this person is allowed to do this. Must be an admin type, -# the experiment creator or the project leader. +# Verify that this person is allowed to do this. # -if ($UID && !TBAdmin()) { - my $expt_leader = ExpLeader($pid, $eid); - my $proj_leader = ProjLeader($pid); - - if (!$expt_leader || !$proj_leader) { - die("*** $0:\n". - " No such Experiment $eid or no such Project $pid\n"); - } - - if ($expt_leader ne $dbuid && $proj_leader ne $dbuid) { - die("*** $0:\n". - " You must be the experiment creator or the project leader\n"); - } +if (!TBAdmin() && + ! TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_UPDATE)) { + die("*** $0:\n". + " You not have permission to update nodes in $pid/$eid!\n"); } # @@ -146,10 +146,11 @@ if ($UID && !TBAdmin()) { # DBQueryFatal("lock tables experiments write"); -if (TBExpLocked($pid, $eid)) { +if (TBExpLocked($pid, $eid, \$estate)) { DBQueryWarn("unlock tables"); - die("*** $0:\n". - " Experiment $pid/$eid is in transition. Please try later!\n"); + print STDERR "Experiment $pid/$eid is in transition! Please try later!\n"; + # For web page. + exit(1); } # @@ -157,14 +158,44 @@ if (TBExpLocked($pid, $eid)) { # state so that we are not trying to update nodes that are still booting # or swapping out, etc. # -if (ExpState($pid, $eid) ne EXPTSTATE_ACTIVE) { +if ($estate ne BATCHSTATE_RUNNING) { DBQueryWarn("unlock tables"); - die("*** $0:\n". - " The experiment $pid/$eid must be fully activated first!\n"); + print STDERR "Experiment $pid/$eid is not active or is in transition!\n"; + # For web page. + exit(1); } -TBLockExp($pid, $eid); +TBLockExp($pid, $eid, BATCHSTATE_RUNNING_LOCKED); DBQueryFatal("unlock tables"); +# +# Get the list of nodes that need to be "updated." We already have the +# exerpiment locked, so if we have a problem, must unlock it. +# +if (! scalar(@nodes)) { + @nodes = ExpNodes($pid, $eid); +} +else { + # + # Must verify that user supplied node names are really in the + # experiment. + # + my @allnodes = ExpNodes($pid, $eid); + + foreach my $n1 (@nodes) { + if (! grep {$_ eq $n1} @allnodes) { + TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING()); + die("*** $0:\n". + " Node $n1 is not allocated to $pid/$eid!\n"); + } + } +} +if (! scalar(@nodes)) { + print STDERR "There are no nodes allocated to experiment $pid/$eid\n"; + TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING()); + # For web page. + exit(1); +} + # # Batchmode (as from the web interface) goes to background and reports # later via email. @@ -188,24 +219,14 @@ if ($batchmode) { } # -# Currently, we just need to update the mount points. The UID change because -# of PERL sillyness. +# Currently, we just need to update the mount points. # -$UID = $EUID; if (system("$expsetup")) { fatal("Exports Setup Failed"); } # Give ops a chance to react. sleep(2); -# -# Get the list of nodes that need to be "updated." -# -my @nodes = ExpNodes($pid, $eid); -if (! @nodes) { - fatal("No Nodes in the experiment"); -} - # # Mark the nodes for auto update. Nodes may not respect this field # (old local images), but its harmless. @@ -218,170 +239,47 @@ foreach my $node ( @nodes ) { } } -# -# We want some overlap, but not too much since we could burn up -# a lot processes on wedged nodes. Issue a small number in parallel, -# and wait once we reach the limit for one to finish, before issuing -# the next one. -# -my $maxpids = 0; -foreach my $node ( @nodes ) { - my ($jailed, $plab); - - # - # widearea and jailed nodes auto update. Skip them. - # - if (TBIsNodeRemote($node)) { - push(@autonodes, $node); - next; - } - # eventually all virtual nodes will be jailed ... - if (TBIsNodeVirtual($node, \$jailed, \$plab)) { - if ($jailed || $plab) { - push(@autonodes, $node); - } - next; - } - - while ($maxpids >= $maxchildren) { - my $thispid = waitpid(-1, 0); - my $thisnode = $pids{$thispid}; - - if ($?) { - $failed++; - print STDERR "Update of node $thisnode failed!\n"; - } - else { - print STDOUT "$thisnode updated.\n"; - } +print STDOUT "Waiting a while for nodes to auto update ...\n"; +for (my $i = 0; $i < 10; $i++) { + sleep(30); - delete($pids{$thispid}); - $maxpids--; - } - my $thispid = UpdateNode($node); - $pids{$thispid} = $node; - $maxpids++; - sleep(1); -} + my $firstnode = pop(@nodes); + my $querystr = "node_id='$firstnode' "; -# -# Wait for any remaining children to exit before continuing. -# -foreach my $thispid ( keys(%pids) ) { - my $node = $pids{$thispid}; + while (@nodes) { + my $node = pop(@nodes); - waitpid($thispid, 0); - if ($?) { - $failed++; - print STDERR "Update of node $node failed!\n"; - } - else { - print STDOUT "$node updated.\n"; + $querystr .= "or node_id='$node' "; } -} - -# -# Wait a couple of minutes for remote nodes to do the update. We know -# this cause tmcd decrements the update_accounts flag whenever a node -# picks up new accounts. -# -if (@autonodes) { - print STDOUT "Waiting a while for nodes to auto update ...\n"; - for (my $i = 0; $i < 6; $i++) { - sleep(30); - - my $firstnode = pop(@autonodes); - my $querystr = "node_id='$firstnode' "; - - while (@autonodes) { - my $node = pop(@autonodes); + + my $query_result = + DBQueryFatal("select node_id,update_accounts from nodes ". + "where ($querystr)"); - $querystr .= "or node_id='$node' "; + while (my ($node,$notdone) = $query_result->fetchrow_array) { + if ($notdone) { + push(@nodes, $node); } - - my $query_result = - DBQueryFatal("select node_id,update_accounts from nodes ". - "where ($querystr)"); - - while (my ($node,$notdone) = $query_result->fetchrow_array) { - if ($notdone) { - push(@autonodes, $node); - } - else { - print STDOUT "$node updated.\n"; - } + else { + print STDOUT "$node updated.\n"; } - last if (! @autonodes); - print STDOUT "Still waiting for nodes to auto update ...\n"; } + last + if (! @nodes); + print STDOUT "Still waiting for nodes to auto update ...\n"; } -foreach my $node ( @autonodes ) { +foreach my $node ( @nodes ) { print STDOUT "Node update failed on $node.\n"; $failed++; } -TBUnLockExp($pid, $eid); +TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING); NotifyUser("Node Update Complete", $failed); if (defined($logname)) { unlink($logname); } exit($failed); -# -# Update a node in a child process. Return the pid to the parent so -# that it can wait on all the children later. -# -sub UpdateNode { - my($node) = @_; - my($syspid, $mypid); - - print STDOUT "Updating $node.\n"; - - $mypid = fork(); - if ($mypid) { - return $mypid; - } - TBdbfork(); - - # - # Run an ssh command in a child process, protected by an alarm to - # ensure that the ssh is not hung up forever if the machine is in - # some funky state. - # - $syspid = fork(); - - # Must change our real UID to root so that ssh will work. - $UID = 0; - - if ($syspid) { - local $SIG{ALRM} = sub { kill("TERM", $syspid); }; - alarm 15; - waitpid($syspid, 0); - alarm 0; - - print STDERR "update of $node returned $?.\n" if $debug; - - # - # If either ssh is not running or it timed out, - # send it a ping of death. - # - if ($? == 256 || $? == 15) { - if ($? == 256) { - print STDERR "$node is not running sshd.\n" if $debug; - } else { - print STDERR "$node is wedged.\n" if $debug; - } - exit(-1); - } - exit(0); - } - else { - exec("$ssh -host $node /etc/testbed/update"); - exit(0); - } - exit(0); -} - sub NotifyUser($$) { my($mesg, $iserr) = @_; @@ -426,7 +324,7 @@ sub NotifyUser($$) sub fatal($) { my($mesg) = @_; - TBUnLockExp($pid, $eid); + TBUnLockExp($pid, $eid, BATCHSTATE_RUNNING()); NotifyUser($mesg, 1); if (defined($logname)) { unlink($logname); diff --git a/tbsetup/spewrpmtar.in b/tbsetup/spewrpmtar.in index ee1030c8bf7ab52b4135eca053e27bcafdf94b17..ade8ae555b83aa7533ce7068813181f860fc20ac 100644 --- a/tbsetup/spewrpmtar.in +++ b/tbsetup/spewrpmtar.in @@ -6,6 +6,7 @@ # use English; use Getopt::Std; +use POSIX qw(mktime); # # Spew a tar/rpm file to stdout. @@ -14,13 +15,28 @@ use Getopt::Std; # sub usage() { - print STDERR "Usage: spewtarfile [-v] \n". + print STDERR "Usage: spewtarfile [-v] [t timestamp] \n". "Spew a tar/rpm for an experiment.\n"; exit(-1); } -my $optlist = "v"; +my $optlist = "vt:"; my $debug = 1; my $doverify = 0; +my $timestamp; # GM Time. + +# +# 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 - Proceeding. +# $status > 0 - Expected error. No such file, not modified, etc. +# 1. File could not be verified. +# 2. File has not changed since timestamp. +# 3. File could not be opened for reading. +# # # Configure variables @@ -54,6 +70,16 @@ if (! getopts($optlist, \%options)) { if (defined($options{"v"})) { $doverify = 1; } +if (defined($options{"t"})) { + $timestamp = $options{"t"}; + + if ($timestamp =~ /^([\d]+)$/) { + $timestamp = $1; + } + else { + die("*** Bad data in timestamp: $timestamp\n"); + } +} if (@ARGV != 2) { usage(); } @@ -90,12 +116,13 @@ my $gid = ExpGroup($pid, $eid); my $creator = ExpLeader($pid, $eid); my $bytelen = 0; -exit(1) - if (!VerifyFile()); +if (my $retval = VerifyFile()) { + exit($retval); +} exit(0) if ($doverify); -exit(2) - if (!SpewFile()); +exit(3) + if (SpewFile() != 0); exit(0); # @@ -141,11 +168,12 @@ sub SpewFile() fatal("Did not get the entire file! $bytelen bytes left."); } close(FD); - + return 0; } # -# Verify that we can return this file, return 0 if not allowed. +# Verify that we can return this file, return error if not allowed. +# Otherwise return 0 for okay. # sub VerifyFile() { @@ -157,7 +185,7 @@ sub VerifyFile() if ($debug) { print STDERR "VerifyFile: Could not verify $file!\n"; } - return 0; + return 1; } # @@ -183,15 +211,15 @@ sub VerifyFile() if ($debug) { print STDERR "$translated is not in /proj or /groups!\n"; } - return 0; + return 1; } # # Stat the file to confirm that its either owned by the experiment # creator, or in the gid of the experiment. # - (undef,undef,undef,undef,$stat_uid,$stat_gid,undef,$bytelen) - = stat($translated); + (undef,undef,undef,undef,$stat_uid,$stat_gid,undef,$bytelen, + undef,$mtime) = stat($translated); my ($unix_uid, $unix_gid, $unix_gname); @@ -206,10 +234,19 @@ sub VerifyFile() if ($debug) { print STDERR "$translated has wrong uid/gid!\n"; } - return 0; + return 1; } + + # + # Check timestamp if supplied. Remember, we get GM timestamps, so + # must convert the local stamp. + # + $mtime = mktime(gmtime($mtime)); + return 2 + if (defined($timestamp) && $timestamp >= $mtime); + $file = $translated; - return 1; + return 0; } # diff --git a/tmcd/common/update b/tmcd/common/update index 70b0257f24ac0054bef6ceee35bef4f98bce952b..96c50f66fd4d9a424802cdeafdc653ff0bf3c59b 100755 --- a/tmcd/common/update +++ b/tmcd/common/update @@ -15,15 +15,17 @@ use POSIX qw(strftime); # sub usage() { - print "Usage: update [-i] [-a] [-m] [-h] [-l]\n"; + print "Usage: update [-i] [-a] [-m] [-h] [-l] [-t] [-r]\n"; exit(1); } -my $optlist = "imalh"; +my $optlist = "imalhtr"; my $batchmode = 1; my $doall = 0; my $accounts = 0; my $mounts = 0; my $hostsfile = 0; +my $tarballs = 0; +my $rpms = 0; my $immediate = 0; # Drag in path stuff so we can find emulab stuff. @@ -97,11 +99,17 @@ if (defined($options{"m"})) { if (defined($options{"h"})) { $hostsfile = 1; } +if (defined($options{"t"})) { + $tarballs = 1; +} +if (defined($options{"r"})) { + $rpms = 1; +} if (@ARGV) { usage(); } # Backwards compat; no options means mounts and accounts. -if (!$mounts && !$accounts && !$hostsfile) { +if (!$mounts && !$accounts && !$hostsfile && !$doall) { $mounts = $accounts = 1; } @@ -193,5 +201,31 @@ if ($accounts || $doall) { } } +if ($tarballs || $doall) { + print "Updating tarball configuration ... \n"; + + if (dotarballs()) { + die("*** $0:\n". + " Failed to get new tarball configuration!\n"); + } + if (-x TMTARBALLS()) { + print "Installing new tarballs ...\n"; + system(TMTARBALLS()); + } +} + +if ($rpms || $doall) { + print "Updating RPM configuration ... \n"; + + if (dorpms()) { + die("*** $0:\n". + " Failed to get new RPM configuration!\n"); + } + if (-x TMRPM()) { + print "Installing new RPMs ...\n"; + system(TMRPM()); + } +} + close(LOCK); exit(0); diff --git a/tmcd/common/watchdog b/tmcd/common/watchdog index a8d49fe917048f2eb51899f7e45a848b0ae6cb6b..4d9026ffe6ff2b055eb5b213275a2666d4644f42 100755 --- a/tmcd/common/watchdog +++ b/tmcd/common/watchdog @@ -229,7 +229,7 @@ sub startisalive() if ($failed || $tmccresults[0] =~ /^UPDATE=1$/) { print "Running an update at $date ...\n"; - system("update -i"); + system("update -i -a"); $failed = $?; } last; diff --git a/www/dbdefs.php3.in b/www/dbdefs.php3.in index 0ead0d9b4f49c31baacb43ad7814d658fdb956bf..1cf68dd6a7decbd2fdffe65f7c217ea28065aaf6 100644 --- a/www/dbdefs.php3.in +++ b/www/dbdefs.php3.in @@ -86,9 +86,9 @@ $TB_USERINFO_MAX = $TB_USERINFO_MODIFYINFO; $TB_EXPT_READINFO = 1; $TB_EXPT_MODIFY = 2; # Allocate/dealloc nodes $TB_EXPT_DESTROY = 3; -$TB_EXPT_UPDATEACCOUNTS = 4; +$TB_EXPT_UPDATE = 4; $TB_EXPT_MIN = $TB_EXPT_READINFO; -$TB_EXPT_MAX = $TB_EXPT_UPDATEACCOUNTS; +$TB_EXPT_MAX = $TB_EXPT_UPDATE; # Projects. $TB_PROJECT_READINFO = 1; @@ -136,6 +136,7 @@ define("TBDB_IFACEROLE_CONTROL", "ctrl"); define("TBDB_IFACEROLE_EXPERIMENT", "expt"); define("TBDB_IFACEROLE_JAIL", "jail"); define("TBDB_IFACEROLE_FAKE", "fake"); +define("TBDB_IFACEROLE_GW", "gw"); define("TBDB_IFACEROLE_OTHER", "other"); # @@ -504,7 +505,7 @@ function TBExptAccessCheck($uid, $pid, $eid, $access_type) global $TB_EXPT_READINFO; global $TB_EXPT_MODIFY; global $TB_EXPT_DESTROY; - global $TB_EXPT_UPDATEACCOUNTS; + global $TB_EXPT_UPDATE; global $TB_EXPT_MIN; global $TB_EXPT_MAX; global $TBDB_TRUST_USER; diff --git a/www/showexp.php3 b/www/showexp.php3 index 4755d15cfb28243593ca861d465285cfe19edd1f..a436a3e5c1da8cd76d0362134bbb93f687bb95a4 100644 --- a/www/showexp.php3 +++ b/www/showexp.php3 @@ -141,8 +141,8 @@ WRITESUBMENUBUTTON("Edit Experiment Metadata", # # Admin and project/experiment leader get this option. # -if (TBExptAccessCheck($uid, $exp_pid, $exp_eid, $TB_EXPT_UPDATEACCOUNTS)) { - WRITESUBMENUBUTTON("Update Mounts/Accounts", +if (TBExptAccessCheck($uid, $exp_pid, $exp_eid, $TB_EXPT_UPDATE)) { + WRITESUBMENUBUTTON("Update All Nodes", "updateaccounts.php3?pid=$exp_pid&eid=$exp_eid"); } diff --git a/www/showimageid_list.php3 b/www/showimageid_list.php3 index 4fd2ebbeabdbd3702fe1b6501525e23cf17cc804..c74bef2a07201f4a653fe78d0422a2c7c8c7ce11 100644 --- a/www/showimageid_list.php3 +++ b/www/showimageid_list.php3 @@ -76,7 +76,7 @@ echo "Listed below are the Images that you can load on your nodes with the a node is not loaded on that node when the experiment is swapped in, the Testbed system will automatically reload that node's disk with the appropriate image. You might notice that it takes a few minutes longer - to start start your experiment when selecting an OS that is not + to start your experiment when selecting an OS that is not already resident. Please be patient.
More information on how to create your own Images is in the diff --git a/www/shownode.php3 b/www/shownode.php3 index ba4d8eaa59f9f60adcbe89e1cd9dbefcc0198ad5..022468225e20a8f537d05f9a568e139029fc55fa 100644 --- a/www/shownode.php3 +++ b/www/shownode.php3 @@ -100,6 +100,11 @@ WRITESUBMENUBUTTON("Edit Node Info", "nodecontrol_form.php3?node_id=$node_id"); if (TBNodeAccessCheck($uid, $node_id, $TB_NODEACCESS_REBOOT)) { + if (isset($pid)) { + WRITESUBMENUBUTTON("Update Node", + "updateaccounts.php3?pid=$pid&eid=$eid". + "&nodeid=$node_id"); + } WRITESUBMENUBUTTON("Reboot Node", "boot.php3?node_id=$node_id"); } diff --git a/www/showstuff.php3 b/www/showstuff.php3 index 402eea1828d0c6cf32d6a9e37f3dff8043ce7f90..d0ed69fec95e37c7a15d70de9ccaa2af465820c4 100644 --- a/www/showstuff.php3 +++ b/www/showstuff.php3 @@ -1468,7 +1468,7 @@ function SHOWIMAGEID($imageid, $edit, $isadmin = 0) { # # Show node record. # -function SHOWNODE($node_id) { +function SHOWNODE($node_id, $short = 0) { $query_result = DBQueryFatal("select n.*,na.*,r.vname,r.pid,r.eid,i.IP, ". "greatest(last_tty_act,last_net_act,last_cpu_act,". @@ -1564,24 +1564,27 @@ function SHOWNODE($node_id) { } } - if ($vname) { - echo " - Virtual Name: - $vname - \n"; - } + if (!$short) { + if ($vname) { + echo " + Virtual Name: + $vname + \n"; + } - if ($pid) { - echo " - Project: - - $pid - \n"; + if ($pid) { + echo " + Project: + + $pid + \n"; - echo " - Experiment: - $eid - \n"; + echo " + Experiment: + + $eid + \n"; + } } echo " @@ -1589,127 +1592,128 @@ function SHOWNODE($node_id) { $type \n"; - echo " - Def Boot OS: - "; - SPITOSINFOLINK($def_boot_osid); - echo " - \n"; - - if ($eventstate) { - $when = strftime("20%y-%m-%d %H:%M:%S", $state_timestamp); + if (!$short) { echo " - EventState: - $eventstate ($when) + Def Boot OS: + "; + SPITOSINFOLINK($def_boot_osid); + echo " \n"; - } - if ($op_mode) { - $when = strftime("20%y-%m-%d %H:%M:%S", $op_mode_timestamp); - echo " - Operating Mode: - $op_mode ($when) - \n"; - } + if ($eventstate) { + $when = strftime("20%y-%m-%d %H:%M:%S", $state_timestamp); + echo " + EventState: + $eventstate ($when) + \n"; + } - if ($allocstate) { - $when = strftime("20%y-%m-%d %H:%M:%S", $allocstate_timestamp); - echo " - AllocState: - $allocstate ($when) - \n"; - } + if ($op_mode) { + $when = strftime("20%y-%m-%d %H:%M:%S", $op_mode_timestamp); + echo " + Operating Mode: + $op_mode ($when) + \n"; + } - # - # We want the last login for this node, but only if its *after* the - # experiment was created (or swapped in). - # - if ($lastnodeuidlogin = TBNodeUidLastLogin($node_id)) { + if ($allocstate) { + $when = strftime("20%y-%m-%d %H:%M:%S", $allocstate_timestamp); + echo " + AllocState: + $allocstate ($when) + \n"; + } - $foo = $lastnodeuidlogin["date"] . " " . - $lastnodeuidlogin["time"] . " " . - "(" . $lastnodeuidlogin["uid"] . ")"; + # + # We want the last login for this node, but only if its *after* the + # experiment was created (or swapped in). + # + if ($lastnodeuidlogin = TBNodeUidLastLogin($node_id)) { + $foo = $lastnodeuidlogin["date"] . " " . + $lastnodeuidlogin["time"] . " " . + "(" . $lastnodeuidlogin["uid"] . ")"; - echo " - Last Login: - $foo - \n"; - } - - if ($last_act) { - echo " - Last Activity: - $last_act - \n"; + echo " + Last Login: + $foo + \n"; + } - $idletime = TBGetNodeIdleTime($node_id); - echo " - Idle Time: - $idletime hours - \n"; + if ($last_act) { + echo " + Last Activity: + $last_act + \n"; - echo " - Last Act. Report: - $last_report - \n"; + $idletime = TBGetNodeIdleTime($node_id); + echo " + Idle Time: + $idletime hours + \n"; - echo " - Last TTY Act.: - $last_tty_act - \n"; + echo " + Last Act. Report: + $last_report + \n"; - echo " - Last Net. Act.: - $last_net_act - \n"; + echo " + Last TTY Act.: + $last_tty_act + \n"; - echo " - Last CPU Act.: - $last_cpu_act - \n"; + echo " + Last Net. Act.: + $last_net_act + \n"; - echo " - Last Ext. Act.: - $last_ext_act - \n"; + echo " + Last CPU Act.: + $last_cpu_act + \n"; + echo " + Last Ext. Act.: + $last_ext_act + \n"; + } } - - if (!$isvirtnode && !$isremotenode) { - echo " - Def Boot Path: - $def_boot_path - \n"; - echo " - Def Boot Command Line: - $def_boot_cmd_line - \n"; + if (!$short) { + if (!$isvirtnode && !$isremotenode) { + echo " + Def Boot Path: + $def_boot_path + \n"; - echo " - Next Boot OS: - "; + echo " + Def Boot Command Line: + $def_boot_cmd_line + \n"; + + echo " + Next Boot OS: + "; - if ($next_boot_osid) - SPITOSINFOLINK($next_boot_osid); - else - echo " "; + if ($next_boot_osid) + SPITOSINFOLINK($next_boot_osid); + else + echo " "; - echo " - \n"; + echo " + \n"; - echo " - Next Boot Path: - $next_boot_path - \n"; + echo " + Next Boot Path: + $next_boot_path + \n"; - echo " - Next Boot Command Line: - $next_boot_cmd_line - \n"; - } - elseif ($isvirtnode) { - if (!$isplabdslice) { + echo " + Next Boot Command Line: + $next_boot_cmd_line + \n"; + } + elseif ($isvirtnode) { + if (!$isplabdslice) { echo " IP Port Low: $ipport_low @@ -1724,12 +1728,12 @@ function SHOWNODE($node_id) { IP Port High: $ipport_high \n"; + } + echo " + SSHD Port: + $sshdport + \n"; } - - echo " - SSHD Port: - $sshdport - \n"; } echo " @@ -1742,83 +1746,81 @@ function SHOWNODE($node_id) { $tarballs \n"; - if (!$isremotenode) { - echo " - RPMs: - $rpms - \n"; - } - - if (!$isvirtnode && !$isremotenode) { - echo " - Router Type: - $routertype + echo " + RPMs: + $rpms \n"; - } - if ($IP) { - echo " - Control Net IP: - $IP - \n"; - } - elseif ($phys_IP) { - echo " - Physical IP: - $phys_IP - \n"; - } + if (!$short) { + if (!$isvirtnode && !$isremotenode) { + echo " + Router Type: + $routertype + \n"; + } - if ($rsrvrole) { - echo " - Role: - $rsrvrole - \n"; - } + if ($IP) { + echo " + Control Net IP: + $IP + \n"; + } + elseif ($phys_IP) { + echo " + Physical IP: + $phys_IP + \n"; + } - if ($bios) { - echo " - Bios Version: - $bios - \n"; - } + if ($rsrvrole) { + echo " + Role: + $rsrvrole + \n"; + } - if ($isremotenode) { - if ($isvirtnode) { - SHOWWIDEAREANODE($phys_nodeid, 1); + if ($bios) { + echo " + Bios Version: + $bios + \n"; } - else { - SHOWWIDEAREANODE($node_id, 1); + + if ($isremotenode) { + if ($isvirtnode) { + SHOWWIDEAREANODE($phys_nodeid, 1); + } + else { + SHOWWIDEAREANODE($node_id, 1); + } } - } - # - # Show any auxtypes the node has - # - $query_result = - DBQueryFatal("select type, count from node_auxtypes ". - "where node_id='$node_id'"); + # + # Show any auxtypes the node has + # + $query_result = + DBQueryFatal("select type, count from node_auxtypes ". + "where node_id='$node_id'"); - if (mysql_num_rows($query_result) != 0) { - echo " - - Auxiliary Types - - \n"; + if (mysql_num_rows($query_result) != 0) { + echo " + + Auxiliary Types + + \n"; - echo "TypeCount\n"; + echo "TypeCount\n"; - while ($row = mysql_fetch_array($query_result)) { - $type = $row[type]; - $count = $row[count]; - echo " - $type - $count - \n"; + while ($row = mysql_fetch_array($query_result)) { + $type = $row[type]; + $count = $row[count]; + echo " + $type + $count + \n"; + } } - } - echo "\n"; } diff --git a/www/spewrpmtar.php3 b/www/spewrpmtar.php3 index ec57e2ca29478870670694a664952f94be18814a..e8af472eb9559f0530055383a8dff104bef9fea9 100644 --- a/www/spewrpmtar.php3 +++ b/www/spewrpmtar.php3 @@ -35,6 +35,13 @@ if (!isset($key) || strcmp($key, "") == 0) { SPITERROR(400, "You must provide an key."); } +if (!isset($stamp) || !strcmp($stamp, "")) { + unset($stamp); +} +# We ignore MD5 for now. +if (!isset($md5) || !strcmp($md5, "")) { + unset($md5); +} # # Make sure a reserved node. @@ -87,18 +94,30 @@ register_shutdown_function("SPEWCLEANUP"); # $nodeid = escapeshellarg($nodeid); $file = escapeshellarg($file); +$arg = (isset($stamp) ? "-t " . escapeshellarg($stamp) : ""); # # Run once with just the verify option to see if the file exists. # Then do it for real, spitting out the data. Sure, the user could # delete the file in the meantime, but thats his problem. # -$retval = SUEXEC($creator, $gid, "spewrpmtar -v $nodeid $file", +$retval = SUEXEC($creator, $gid, "spewrpmtar -v $arg $nodeid $file", SUEXEC_ACTION_IGNORE); +if ($retval < 0) { + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + SPITERROR(500, "Could not verify file!"); +} + +# +# An expected error. +# if ($retval) { - SPITERROR(404, "Could not find $file!"); -} + if ($retval == 2) { + SPITERROR(304, "File has not changed"); + } + SPITERROR(404, "Could not verify file: $retval!"); +} # # Okay, now do it for real. diff --git a/www/swapexp.php3 b/www/swapexp.php3 index 71fac95cb6b2c1a98a9f17a8735d5e431477a665..4f3534035c2c9d2a6771ea04be7d4153bf335e32 100644 --- a/www/swapexp.php3 +++ b/www/swapexp.php3 @@ -80,6 +80,7 @@ if (mysql_num_rows($query_result) == 0) { $row = mysql_fetch_array($query_result); $exp_gid = $row[gid]; $isbatch = $row[batchmode]; +$batchstate = $row[batchstate]; $swappable = $row[swappable]; $idleswap_bit = $row[idleswap]; $idleswap_time = $row[idleswap_timeout]; @@ -102,8 +103,14 @@ if (!strcmp($inout, "in")) { elseif (!strcmp($inout, "out")) { if ($isbatch) $action = "swapout"; - else - $action = "swapout"; + else { + if (! strcmp($batchstate, TBDB_BATCHSTATE_ACTIVATING)) { + $action = "cancel"; + } + else { + $action = "swapout"; + } + } } elseif (!strcmp($inout, "pause")) { if (!$isbatch) @@ -266,22 +273,35 @@ else { } } else { - if (strcmp($inout, "in") == 0) - $howlong = "two to ten"; - else - $howlong = "less than two"; + if (strcmp($inout, "out") == 0 && + strcmp($batchstate, TBDB_BATCHSTATE_ACTIVATING) == 0) { + + echo "Your experiment swapin has been marked for cancelation. + It typically takes a few minutes for this to be recognized, + assuming you made your request early enough. You will + be notified via email when the original swapin request has + either aborted or finished.\n"; + } + else { + if (strcmp($inout, "in") == 0) + $howlong = "two to ten"; + else + $howlong = "less than two"; - echo "Your experiment has started its $action. - You will be notified via email when the operation is complete. - This typically takes $howlong minutes, depending on the - number of nodes in the experiment. -

- If you do not receive email notification within a reasonable - amount of time, please contact $TBMAILADDR. -

- While you are waiting, you can watch the log in - - realtime.\n"; + echo "Your experiment has started its $action. + You will be notified via email when the operation is complete. + This typically takes $howlong minutes, depending on the + number of nodes in the experiment.\n"; + } + echo "

+ If you do not receive + email notification within a reasonable amount of time, + please contact $TBMAILADDR.\n"; + + echo "

+ While you are waiting, you can watch the log in + + realtime.\n"; } } diff --git a/www/updateaccounts.php3 b/www/updateaccounts.php3 index 44e855705b779a2d585af15c31fa3f3b44ceb77a..84517e9e9e8b38084cf818887bbbadabffe1084f 100644 --- a/www/updateaccounts.php3 +++ b/www/updateaccounts.php3 @@ -1,15 +1,11 @@ ". - "You must wait until the experiment is no longer in transition.". - "

". - "When the transition has completed, a notification will be ". - "sent via email to the user that initiated it.", 1); -} +$gid = $row[gid]; # # Verify permissions. # -if (! TBExptAccessCheck($uid, $exp_pid, $exp_eid, $TB_EXPT_UPDATEACCOUNTS)) { - USERERROR("You do not have permission to update accounts!", 1); +if (! TBExptAccessCheck($uid, $pid, $eid, $TB_EXPT_UPDATE)) { + USERERROR("You do not have permission to initiate node updates!", 1); } +if (isset($nodeid) && !TBValidNodeName($nodeid)) { + USERERROR("Node $nodeid is not a valid nodeid!", 1); +} + +echo "Experiment ". + "$pid/". + "$eid\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 we should -# probably redirect the browser back up a level. +# set. Or, the user can hit the cancel button (see above). # -if ($canceled) { - echo "


- Account Update canceled! -

\n"; - - PAGEFOOTER(); - return; -} - if (!$confirmed) { - echo "Confirming this operation will cause the password file on each - node in experiment '$exp_eid' to be updated. Typically, you would - initiate this operation if a new project member has been approved and - you want his/her account added immediately (without having to reboot - all your nodes or swap the experiment in/out).\n"; + echo "

+ Confirming this operation will initiate various updates to be + performed, including updating the password and group files, + adding new mounts, and installing (or updating if modified) tarballs + and rpms. + This is sometimes quicker and easier than rebooting nodes.\n"; - echo "

-
- Are you sure you want to update Accounts - on all of the nodes in experiment '$exp_eid?'\n"; + echo "


+ Are you sure you want to perform an update on "; + + if (isset($nodeid)) { + echo "node $nodeid in experiment '$eid?'\n"; + } + else { + echo "all of the nodes in experiment '$eid?'\n"; + } + echo "

\n"; + + if (isset($nodeid)) { + SHOWNODE($nodeid, 1); + } + else { + SHOWEXP($pid, $eid, 1); + } echo "
"; echo "\n"; echo "\n"; + if (isset($nodeid)) { + echo "\n"; + } echo "
\n"; echo "
\n"; echo "
- This operation will consume a small number of CPU cycles on + NOTE that this operation will consume a small number of CPU + cycles on each node. If this would disturb an experiment in progress, you should cancel this operation until later. Please note that accounts are automatically updated whenever a node @@ -115,31 +127,54 @@ if (!$confirmed) { # 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); +TBGroupUnixInfo($pid, $gid, $unix_gid, $unix_name); # # Start up a script to do this. # echo "

"; -echo "

Starting account update. Please wait a moment ... +echo "

Starting node update. Please wait a moment ...

"; flush(); -SUEXEC($uid, $unix_gid, "webnodeupdate -b $pid $eid", 1); +$retval = SUEXEC($uid, $unix_gid, + "webnodeupdate -b $pid $eid" . + (isset($nodeid) ? " $nodeid" : ""), + SUEXEC_ACTION_IGNORE); -echo "

- You will be notified via email when the update has completed on - all of the nodes in your experiment. Since this involves rebuilding - the accounts database on each node, it should be just a few minutes - before you receive notification.\n"; +# +# 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(""); +} # -# Back links for convenience. +# Exit status 0 means the operation is proceeding in the background. # -echo "

- - Back to experiment information page\n"; +echo "
\n"; +if ($retval) { + echo "

Node update could not proceed

"; + echo "
$suexec_output
"; +} +else { + echo "You will be notified via email when the update has completed on + all of the nodes in your experiment. This might take a just a + few minutes (if updating only accounts) or it might take longer + if new tar/rpm files need to be installed. In the meantime, the + experiment is locked it may not be swapped or modified.\n"; + + echo "

+ If you do not receive + email notification within a reasonable amount of time, + please contact $TBMAILADDR.\n"; +} # # Standard Testbed Footer