Commit 217de8ab authored by Leigh Stoller's avatar Leigh Stoller

Two somewhat related template changes.

* Reorg the CVS repo so that records and setup are toplevel modules in
  the repo, instead of directories in a single module named by the
  guid (which is redundant and annoying).

* Some changes to the spewlog stuff. It used to handle only
  experiments, but I really wanted it to handle template create and
  modify. Took a bunch of small changes to a lot of places to make
  this work correctly, but it was worth it.

  There are some changes I made that I can retrofit to the other spew
  pages to make it look a little nicer at the top of the page, to use
  less space.
parent 5c7271c9
......@@ -98,9 +98,31 @@ sub mysystem($)
#
# Lookup a template and create a class instance to return.
#
sub Lookup($$$)
sub Lookup($$;$)
{
my ($class, $guid, $vers) = @_;
my ($class, $arg1, $arg2) = @_;
my ($guid, $vers);
#
# A single arg is either an index or a "guid,vers" or "guid/vers" string.
#
if (!defined($arg2)) {
if ($arg1 =~ /^([-\w]*),([-\w]*)$/ ||
$arg1 =~ /^([-\w]*)\/([-\w]*)$/) {
$guid = $1;
$vers = $2;
}
else {
return undef;
}
}
elsif (! (($arg1 =~ /^[-\w]*$/) && ($arg2 =~ /^[-\w]*$/))) {
return undef;
}
else {
$guid = $arg1;
$vers = $arg2;
}
# Look in cache first
return $templates{"$guid/$vers"}
......@@ -147,6 +169,10 @@ sub child_vers($) {
return ((! ref($_[0])) ? -1 : $_[0]->{'TEMPLATE'}->{'child_vers'}); }
sub description($) {
return ((! ref($_[0])) ? -1 : $_[0]->{'TEMPLATE'}->{'description'}); }
sub logfile($) {
return ((! ref($_[0])) ? -1 : $_[0]->{'TEMPLATE'}->{'logfile'}); }
sub logfile_open($) {
return ((! ref($_[0])) ? -1 : $_[0]->{'TEMPLATE'}->{'logfile_open'}); }
#
# Lookup a template given an experiment index.
......@@ -417,6 +443,126 @@ sub Delete($)
return 0;
}
#
# Logfiles. This all needs to change.
#
# Open a new logfile and return its name.
#
sub CreateLogFile($$$)
{
my ($self, $prefix, $ppath) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $vers = $self->vers();
my $guid = $self->guid();
my $pid = $self->pid();
my $projroot = PROJROOT();
my $logdir = "$projroot/$pid/templates/logs";
my $logname = "$logdir/$prefix.${guid}-${vers}.log";
return -1
if (-e $logname);
return -1
if (! -d $logdir && !mkdir($logdir, 0775));
Template::mysystem("touch $logname") == 0
or return -1;
$$ppath = $logname;
return 0;
}
#
# Set the experiment to use the logfile. It becomes the "current" spew.
#
sub SetLogFile($$)
{
my ($self, $logname) = @_;
# Must be a real reference.
return -1
if (! ref($self));
return $self->Update({'logfile' => $logname});
}
#
# Get the experiment logfile.
#
sub GetLogFile($$$)
{
my ($self, $lognamep, $isopenp) = @_;
# Must be a real reference.
return -1
if (! ref($self));
# Must do this to catch updates to the logfile variables.
return -1
if ($self->Refresh());
return -1
if (! $self->logfile());
$$lognamep = $self->logfile();
$$isopenp = $self->logfile_open();
return 0;
}
#
# Mark the log as open so that the spew keeps looking for more output.
#
sub OpenLogFile($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
return $self->Update({'logfile_open' => 1});
}
#
# And close it ...
#
sub CloseLogFile($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
return $self->Update({'logfile_open' => 0});
}
#
# And clear it ...
#
sub ClearLogFile($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $guid = $self->guid();
my $vers = $self->vers();
if (!DBQueryWarn("update experiment_templates set ".
"logfile=NULL,logfile_open=0 ".
"where guid='$guid' and vers='$vers'")) {
return -1;
}
return $self->Refresh();
}
#
# Template permission checks. Using the experiment access check stuff.
#
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2005, 2006 University of Utah and the Flux Group.
# Copyright (c) 2000-2007 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
#
# Spew the current log file for an experiment to stdout. This is for
# use by the web interface, so it can send the lofgile to the user in
# a web page.
# Spew the current log file for an experiment or template to stdout.
# This is for use by the web interface, so it can send the logfile to
# the user in a web page.
#
# The wrinkle is that the logfile only exists while the experiment is
# in transition, and we have to quit when the experiment is no longer in
# transition so that the web page can finish.
#
sub usage()
{
print STDOUT "Usage: spewlogfile <pid> <eid>\n".
"Spew the logfile for an experiment.\n";
print("Usage: spewlogfile -e pid,eid\n".
" spewlogfile -t guid,vers\n".
"Spew the logfile for an experiment or template.\n");
exit(-1);
}
my $optlist = "w";
my $optlist = "we:t:";
my $fromweb = 0;
#
......@@ -37,6 +36,8 @@ my $TBLOGS = "@TBLOGSEMAIL@";
my $logname;
my $isopen;
my $experiment;
my $template;
#
# Load the Testbed support stuff.
......@@ -44,6 +45,9 @@ my $isopen;
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use Experiment;
use Template;
use User;
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
......@@ -56,34 +60,29 @@ $| = 1;
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
%options = ();
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"w"})) {
$fromweb = 1;
}
if (@ARGV != 2) {
usage();
}
my $pid = $ARGV[0];
my $eid = $ARGV[1];
#
# Untaint the arguments.
#
if ($pid =~ /^([-\@\w]+)$/) {
$pid = $1;
}
else {
die("*** Bad data in pid: $pid\n");
}
if ($eid =~ /^([-\@\w]+)$/) {
$eid = $1;
if (defined($options{"e"})) {
$experiment = Experiment->Lookup($options{"e"});
if (! $experiment) {
die("*** $0:\n".
" No such experiment in the Emulab Database.\n");
}
}
else {
die("*** Bad data in eid: $eid\n");
elsif (defined($options{"t"})) {
$template = Template->Lookup($options{"t"});
if (! $template) {
die("*** $0:\n".
" No such template in the Emulab Database.\n");
}
}
usage()
if (@ARGV || !($experiment || $template));
#
# This script is setuid, so please do not run it as root. Hard to track
......@@ -95,19 +94,42 @@ if ($UID == 0) {
}
#
# Verify that this person is allowed to do this.
# Verify user and get his DB uid and other info for later.
#
if (!TBExptAccessCheck($UID, $pid, $eid, TB_EXPT_READINFO)) {
my $this_user = User->ThisUser();
if (! defined($this_user)) {
die("*** $0:\n".
" You do not have permission to view log files for $pid/$eid!\n");
" You ($UID) do not exist!");
}
#
# Get the logfile name.
# Verify that this person is allowed to do this.
#
if (! TBExptGetLogFile($pid, $eid, \$logname, \$isopen)) {
die("*** $0:\n".
" There is no logfile to view for $pid/$eid!\n");
if ($experiment) {
if (!$experiment->AccessCheck($this_user, TB_EXPT_READINFO)) {
die("*** $0:\n".
" You do not have permission to view log for $experiment!\n");
}
#
# Get the logfile name.
#
if ($experiment->GetLogFile(\$logname, \$isopen)) {
die("*** $0:\n".
" There is no logfile to view for $experiment!\n");
}
}
else {
if (!$template->AccessCheck($this_user, TB_EXPT_READINFO)) {
die("*** $0:\n".
" You do not have permission to view log for $template!\n");
}
#
# Get the logfile name.
#
if ($template->GetLogFile(\$logname, \$isopen)) {
die("*** $0:\n".
" There is no logfile to view for $template!\n");
}
}
use Fcntl;
......@@ -118,7 +140,7 @@ STDOUT->autoflush(1);
# If not an admin type, flip back to the UID now to enforce normal
# permissions.
#
if (! TBAdmin($UID)) {
if (!$this_user->IsAdmin()) {
$EUID = $UID;
}
......@@ -129,7 +151,7 @@ sysopen(LOG, $logname, O_RDONLY | O_NONBLOCK) or
#
# If an admin type, flip back to the UID now that the file is open.
#
if (TBAdmin($UID)) {
if ($this_user->IsAdmin()) {
$EUID = $UID;
}
......@@ -142,7 +164,7 @@ my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
# but not sure what else to do.
#
if ($fromweb && $isopen && $size < 1024) {
for ($i = $size; $i <= 1024; $i++) {
for (my $i = $size; $i <= 1024; $i++) {
print " ";
}
print "\n";
......@@ -154,13 +176,20 @@ if ($fromweb && $isopen && $size < 1024) {
#
while (1) {
my $tmp;
my $buf;
while (sysread(LOG, $buf, 2048)) {
print STDOUT "$buf";
}
if (! TBExptGetLogFile($pid, $eid, \$tmp, \$isopen) || !$isopen ||
$tmp ne $logname) {
last;
if ($experiment) {
last
if ($experiment->GetLogFile(\$tmp, \$isopen) ||
!$isopen || $tmp ne $logname);
}
else {
last
if ($template->GetLogFile(\$tmp, \$isopen) ||
!$isopen || $tmp ne $logname);
}
sleep(2);
}
......
......@@ -186,15 +186,15 @@ $SIG{TERM} = \&sighandler;
if (defined($repotag)) {
CommitFromRepo();
}
elsif (defined($eid)) {
CommitFromInstance();
}
else {
tbdie("Unsupported template commit operation!");
if (defined($frompath)) {
CommitFromCheckout();
}
elsif (defined($eid)) {
CommitFromInstance();
}
else {
CommitFromTemplate();
}
......@@ -372,35 +372,22 @@ sub CommitFromInstance()
#
# Start with a plain template modify of the current template.
#
system("$modify -m $guid/$version -w -g $gid $pid $tid $nsfile");
system("$modify -f $frompath ".
" -m $guid/$version -w -g $gid $pid $tid $nsfile");
if ($?) {
fatal($? >> 8, "Failed to commit instance to template!");
}
# Pick up changes to child guid/vers.
# Pick up changes to child guid/vers.
$template->Refresh();
$child_template = Template->Lookup($template->child_guid(),
$template->child_vers());
if (!defined($child_template)) {
my $child_guid = $template->child_guid();
my $child_vers = $template->child_vers();
my $child = Template->Lookup($child_guid, $child_vers);
if (!defined($child)) {
fatal(-1, "Lookup of child template failed!");
}
#
# Now "import" the datastore directory from the instance to the new
# template and commit the changes.
#
if ($child_template->ImportDataStore("$userdir/datastore") != 0) {
fatal(-1, "Could not import datastore from instance to template!");
}
#
# Commit the archive.
#
system("$archcontrol -t import commit $pid " . $child_template->eid());
if ($?) {
fatal(-1, "Could not commit archive!");
}
$child->SetDescription($description)
if (defined($description));
}
#
......
This diff is collapsed.
......@@ -204,7 +204,7 @@ if (my $childpid = TBBackGround($logfile)) {
print "Please wait for email about the status before continuing.\n";
exit(0);
}
exec("$template_commit -r $tag $guid");
exec("$template_commit -r $tag -t $tag $guid");
die("Failed to exec $template_commit!\n");
#
......
......@@ -532,7 +532,7 @@ if (defined($instance->runidx())) {
my $exptidx = $instance->exptidx();
my $instance_dir = $instance->path();
my $cvsdir = "$projroot/$pid/templates/$guid/cvsrepo";
my $cvssubdir = "$guid/records/$exptidx,$runidx";
my $cvssubdir = "records/$exptidx,$runidx";
my $tag = "R${exptidx}-${runidx}_import";
my $instance_tag = "I${exptidx}";
my $xmlfile = "$instance_dir/info.xml";
......
......@@ -19,10 +19,12 @@ function ClearLoadingIndicators(done_msg)
{
var busyimg = getObjbyName('busy');
var loadingspan = getObjbyName('loading');
var loadingdiv = getObjbyName('inner_loaddiv');
loadingspan.innerHTML = done_msg;
loadingdiv.style.display = "none";
busyimg.style.display = "none";
busyimg.src = "1px.gif";
loadingspan.innerHTML = done_msg;
}
function ClearBusyIndicators(done_msg)
......@@ -30,6 +32,14 @@ function ClearBusyIndicators(done_msg)
ClearLoadingIndicators(done_msg);
}
function HideBusyIndicators()
{
var loadingdiv = getObjbyName('outer_loaddiv');
ClearBusyIndicators('');
loadingdiv.style.display = "none";
}
/* Replace the current page */
function PageReplace(URL)
{
......
......@@ -1123,14 +1123,17 @@ function STARTBUSY($msg) {
# Allow for a repeated call; Do nothing.
if ($currently_busy)
return;
echo "<center>\n";
echo "<div id='outer_loaddiv'>\n";
echo "<center><div id='inner_loaddiv'>\n";
echo "<b>$msg</b> ...<br>\n";
echo "This will take a few moments; please be <em>patient</em>.<br>\n";
echo "</div>\n";
echo "<img id='busy' src='busy.gif'>".
"<span id='loading'> Working ...</span>";
echo "<br><br>\n";
echo "</center>\n";
echo "</div>\n";
flush();
$currently_busy = 1;
}
......@@ -1162,6 +1165,19 @@ function CLEARBUSY() {
$currently_busy = 0;
}
function HIDEBUSY() {
global $currently_busy;
if (!$currently_busy)
return;
echo "<script type='text/javascript' language='javascript'>\n";
echo "HideBusyIndicators();\n";
echo "</script>\n";
flush();
$currently_busy = 0;
}
function PAGEREPLACE($newpage) {
echo "<script type='text/javascript' language='javascript'>\n";
echo "PageReplace('$newpage');\n";
......
......@@ -6,10 +6,6 @@
var LOG_STATE_LOADING = 1;
var LOG_STATE_LOADED = 2;
/* The experiment pid/eid used when getting the pnode list. */
var exp_pid = "";
var exp_eid = "";
var pnodes = new Array(); // List of pnode names in longest to shortest order.
var lastLength = 0; // The length of the download text at the last check.
var lastLine = ""; // The last line of the download text.
......@@ -241,22 +237,6 @@ function ml_handleReadyState(state) {
*/
var newData = lastLine + rt.substring(lastLength);
/* Look for assigns "signal" that the pnode list is available. */
/* XXX Turned off since it is slow. */
if (0 && newData.indexOf('Mapped to physical reality!') != -1) {
if (getPNodeProgress == 1) {
/* Still waiting for the reply. */
return;
}
else if (getPNodeProgress == 0) {
/* Send the request. */
getPNodeProgress = 1;
x_GetPNodes(exp_pid, exp_eid, GetPNodes_cb);
return;
}
}
lastLength = rt.length;
lastLine = "";
......
......@@ -102,14 +102,13 @@ function STARTWATCHER($experiment)
echo "</script>\n";
}
function STARTLOG($experiment)
function STARTLOG($object)
{
global $BASEPATH;
$pid = $experiment->pid();
$eid = $experiment->eid();
$url = CreateURL("spewlogfile", $experiment);
STARTWATCHER($experiment);
$url = CreateURL("spewlogfile", $object);
if (is_a($object, 'Experiment')) {
STARTWATCHER($object);
}
echo "<center>\n";
echo "<img id='busy' src='busy.gif'>
......@@ -129,8 +128,6 @@ function STARTLOG($experiment)
echo "SetupOutputArea('outputframe', true);\n";
echo "exp_pid = \"$pid\";\n";
echo "exp_eid = \"$eid\";\n";
echo "</script><div>
<iframe id='downloader' name='downloader'
class='downloader' src='$url'
......
......@@ -16,25 +16,32 @@ $isadmin = ISADMIN();
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("experiment", PAGEARG_EXPERIMENT);
$optargs = OptionalPageArguments("experiment", PAGEARG_EXPERIMENT,
"template", PAGEARG_TEMPLATE);
# Need these below.
$pid = $experiment->pid();
$eid = $experiment->eid();
#
# Verify permission.
#
if (!$experiment->AccessCheck($this_user, $TB_EXPT_READINFO)) {
USERERROR("You do not have permission to view the log for $pid/$eid!", 1);
if (! (isset($experiment) || isset($template))) {
PAGEARGERROR("Must provide either an experiment or a template");
}
#
# Check for a logfile. This file is transient, so it could be gone by
# the time we get to reading it.
# Verify permission and sure there is a logfile.
#
if (! $experiment->logfile()) {
USERERROR("Experiment $pid/$eid is no longer in transition!", 1);
if (isset($experiment)) {
if (!$experiment->AccessCheck($this_user, $TB_EXPT_READINFO)) {
USERERROR("You do not have permission to view logs for $pid/$eid!", 1);
}
if (! $experiment->logfile()) {
USERERROR("Experiment $pid/$eid is no longer in transition!", 1);
}
}
else {
if (!$template->AccessCheck($this_user, $TB_EXPT_READINFO)) {
USERERROR("You do not have permission to view logs for ".
"$guid/$vers!", 1);
}
if (! $template->logfile()) {
USERERROR("Template $guid/$vers is no longer in transition!", 1);
}
}
#
......@@ -56,7 +63,16 @@ function SPEWCLEANUP()
ignore_user_abort(1);
register_shutdown_function("SPEWCLEANUP");
if ($fp = popen("$TBSUEXEC_PATH $uid $pid spewlogfile -w $pid $eid", "r")) {
if (isset($experiment)) {
$args = "-e " . $experiment->pid() . "/" . $experiment->eid();
$pid = $experiment->pid();
}
else {
$args = "-t " . $template->guid() . "/" . $template->vers();
$pid = $template->pid();
}
if ($fp = popen("$TBSUEXEC_PATH $uid $pid spewlogfile -w $args", "r")) {
header("Content-Type: text/plain");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache, must-revalidate");
......@@ -72,7 +88,10 @@ if ($fp = popen("$TBSUEXEC_PATH $uid $pid spewlogfile -w $pid $eid", "r")) {
$fp = 0;
}
else {
USERERROR("Experiment $pid/$eid is no longer in transition!", 1);
if (isset($experiment))
USERERROR("Experiment $pid/$eid is no longer in transition!", 1);
else
USERERROR("Template $guid/$vers is no longer in transition!", 1);
}
?>
......@@ -18,6 +18,9 @@ $this_user = CheckLoginOrDie();
$uid = $this_user->uid();
$isadmin = ISADMIN();
# This will not return if its a sajax request.
include("showlogfile_sup.php3");
#
# Verify page arguments.
#
......@@ -497,27 +500,25 @@ STARTBUSY("Starting template creation!");
# And run that script!
$retval = SUEXEC($uid, "$pid,$unix_gid",
"webtemplate_create -w -q -E $description ".
"webtemplate_create -E $description ".
"-g $gid $pid $tid $thensfile",
SUEXEC_ACTION_IGNORE);
if ($deletensfile) {
unlink($thensfile);
}
/* Clear the various 'loading' indicators. */
STOPBUSY();
HIDEBUSY();
#
# 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_CONTINUE);
}
# User error. Tell user and exit.
if ($retval) {
#
# 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_CONTINUE);
}
# User error. Tell user and exit.
SUEXECERROR(SUEXEC_ACTION_USERERROR);
return;
}
......@@ -525,16 +526,24 @@ if ($retval) {
#
# Parse the last line of output. Ick.
#
if (preg_match("/^Template\s+(\w+)\/(\w+)\s+/",
if (preg_match("/^Template\s+(\w+)\/(\w+)\s+is being/",
$suexec_output_array[count($suexec_output_array)-1],
$matches)) {
$guid = $matches[1];
$vers = $matches[2];
if (($template = Template::Lookup($guid, $vers))) {
PAGEREPLACE(CreateURL("template_show", $template));
$template = Template::Lookup($guid, $vers);
if (! $template) {
TBERROR("Could not lookup template object for $guid/$vers", 1);
return;
}
echo $template->PageHeader();
echo "<br><br>\n";
STARTLOG($template);
}
else {
SUEXECERROR(SUEXEC_ACTION_DIE);
}
#
# Standard Testbed Footer