Commit f070014e authored by Leigh Stoller's avatar Leigh Stoller

Tweaks to webtasks and exit status to avoid some errors going to tbops.

parent 064f04fe
......@@ -83,6 +83,7 @@ use GeniXML;
# Protos
sub fatal($);
sub uerror($);
sub DoCreate();
sub DoDelete();
sub DoRefresh();
......@@ -107,6 +108,11 @@ if (defined($options{"d"})) {
}
if (defined($options{"t"})) {
$webtask_id = $options{"t"};
$webtask = WebTask->LookupOrCreate(undef, $webtask_id);
if (!defined($webtask)) {
fatal("Could not create webtask object");
}
$webtask->AutoStore(1);
}
if (@ARGV < 1) {
usage();
......@@ -297,14 +303,6 @@ sub DoCreate()
if (!defined($dataset)) {
fatal("Internal error creating dataset object");
}
# Now we can create.
if (defined($webtask_id)) {
$webtask = WebTask->Create($dataset->uuid(), $webtask_id);
if (defined($webtask)) {
# Convenient.
$webtask->AutoStore(1);
}
}
# new dataset is returned locked. If we have instance, try to lock
# that now, else its a failure.
......@@ -314,7 +312,17 @@ sub DoCreate()
goto failed;
}
}
#
# For IM datasets always create a webtask for tracking imaging status.
#
if ($type eq "imdataset") {
$webtask = WebTask->Create($dataset->uuid());
if (!defined($webtask)) {
$errmsg = "Could not create webtask object";
goto failed;
}
$webtask->AutoStore(1);
}
#
# Ask the aggregate to create the dataset.
#
......@@ -386,7 +394,7 @@ sub DoCreate()
#
sub DoDelete()
{
my $errmsg = "Could not delete dataset";
my $errmsg = "Could not delete dataset";
if (@ARGV != 1) {
fatal("usage: $0 delete pid/name");
......@@ -397,14 +405,19 @@ sub DoDelete()
fatal("No such dataset");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
uerror("dataset is busy, cannot lock it");
}
my $response = $dataset->DeleteDataset();
if ($response->code() != GENIRESPONSE_SUCCESS &&
$response->code() != GENIRESPONSE_SEARCHFAILED) {
$response->code() != GENIRESPONSE_SEARCHFAILED &&
$response->code() != GENIRESPONSE_BUSY) {
$errmsg = "DeleteDataset failed: ". $response->output() . "\n";
goto failed;
}
if ($response->code() != GENIRESPONSE_BUSY) {
$dataset->Unlock();
uerror("dataset was busy at the remote cluster, try again later");
}
$dataset->Delete();
return 0;
......@@ -430,7 +443,7 @@ sub DoRefresh()
fatal("No such dataset");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
uerror("dataset is busy, cannot lock it");
}
if (DoRefreshInternal($dataset, \$errmsg)) {
goto failed;
......@@ -451,7 +464,7 @@ sub DoRefreshInternal($$)
my $response = $dataset->DescribeDataset();
if ($response->code() != GENIRESPONSE_SUCCESS) {
if ($response->code() == GENIRESPONSE_SEARCHFAILED) {
$$pmesg = "Dataset no longer exists at the target\n";
$$pmesg = "Dataset no longer exists at the remote cluster\n";
}
else {
$$pmesg = "DescribeDataset failed: ". $response->output() . "\n";
......@@ -529,7 +542,7 @@ sub DoModify()
$blob->{'write_access'} = $write_access;
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
uerror("dataset is busy, cannot lock it");
}
if (keys(%$blob)) {
if ($dataset->Update($blob)) {
......@@ -580,7 +593,7 @@ sub DoExtend()
fatal("No such dataset");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
uerror("dataset is busy, cannot lock it");
}
my $response = $dataset->ExtendDataset();
if ($response->code() != GENIRESPONSE_SUCCESS) {
......@@ -645,20 +658,31 @@ sub DoSnapshot()
fatal("Could not find aggregate for $nodeid");
}
if ($dataset->Lock()) {
fatal("dataset is busy, cannot lock it");
uerror("dataset is busy, cannot lock it");
}
if ($instance->Lock()) {
# undef so we do not try to unlock it below.
$instance = undef;
$errmsg = "instance is busy, cannot lock it";
goto failed
$dataset->Unlock();
uerror("instance is busy, cannot lock it");
}
# Create a webtask for the caller, might be ignored but thats okay.
$webtask = WebTask->Create($dataset->uuid(), $webtask_id);
#
# Always create a webtask for tracking imaging status. Must be
# associated with the object.
#
if (defined($webtask)) {
# Convenient.
if ($webtask->object_uuid() ne $dataset->uuid()) {
$errmsg = "Webtask not associated with dataset!";
goto failed;
}
}
else {
$webtask = WebTask->LookupOrCreate($dataset->uuid());
if (!defined($webtask)) {
$errmsg = "Could not create webtask object!";
goto failed;
}
$webtask->AutoStore(1);
}
if (DoSnapShotInternal($dataset, $aggregate, $bsname, $nodeid, \$errmsg)) {
goto failed;
}
......@@ -671,7 +695,7 @@ sub DoSnapshot()
return 0;
failed:
$instance->Unlock() if (defined($instance));
$instance->Unlock();
$dataset->Unlock();
# This will set the webtask, see below.
fatal($errmsg);
......@@ -773,7 +797,7 @@ sub PollDatasetStatus($$)
if ($dataset->state() eq "valid") {
$project->SendEmail($this_user->email(),
"Your dataset is now ready to use",
"Dataset '$dname' is now allocated and ready to use.\n",
"Dataset '$dname' is now ready to use.\n",
$project->LogsEmailAddress(), undef, $logfile);
$webtask->Exited(0)
if (defined($webtask));
......@@ -797,12 +821,23 @@ sub fatal($)
$webtask->output($mesg);
$webtask->Exited(-1);
}
print STDERR "*** $0:\n".
" $mesg\n";
print STDERR "$mesg\n";
# Exit with negative status so web interface treats it as system error.
exit(-1);
}
sub uerror($)
{
my ($mesg) = @_;
if (defined($webtask)) {
$webtask->output($mesg);
$webtask->Exited(1);
}
print STDERR "$mesg\n";
exit(1);
}
sub escapeshellarg($)
{
my ($str) = @_;
......
......@@ -185,7 +185,7 @@ sub DoSnapshot()
{
my $errmsg;
my $logfile;
my $errcode = 1;
my $errcode = -1;
my $needunlock = 0;
my $old_status = $instance->status();
my $node_id;
......@@ -415,7 +415,7 @@ sub DoSnapshot()
if ($image_host ne $authority_host) {
$errmsg = "Not allowed to take a snapshot on this cluster";
$errcode = 1;
goto bad;
goto uerror;
}
}
}
......@@ -425,7 +425,9 @@ sub DoSnapshot()
$this_user, $project);
}
if ($slice->Lock()) {
fatal("Slice is busy, cannot lock it");
$errmsg = "Experiment is busy, please try again later.";
$errcode = 1;
goto uerror;
}
$needunlock = 1;
......@@ -576,12 +578,11 @@ sub DoSnapshot()
if ($failed) {
$errmsg = "Imaging failed"
if (!defined($errmsg));
$errcode = 1;
goto bad;
}
elsif (!$ready) {
$errmsg = "Imaging timed out";
$errcode = 60;
$errcode = -2;
goto bad;
}
elsif (defined($update_profile)) {
......@@ -643,13 +644,6 @@ sub DoSnapshot()
else {
$instance->SetStatus("imaging-failed");
}
print STDERR "$errmsg\n";
if (defined($errmsg) && defined($webtask)) {
$webtask->Exited($errcode);
$webtask->output($errmsg);
}
$slice->UnLock()
if ($needunlock);
if (defined($logfile)) {
SENDMAIL($TBOPS,
"Snapshot failed",
......@@ -658,6 +652,15 @@ sub DoSnapshot()
$TBOPS, undef, $logfile);
unlink($logfile);
}
uerror:
print STDERR "$errmsg\n";
if (defined($errmsg) && defined($webtask)) {
$webtask->Exited($errcode);
$webtask->output($errmsg);
}
$slice->UnLock()
if ($needunlock);
exit($errcode);
}
......
......@@ -115,16 +115,6 @@ if (! defined($this_user)) {
}
}
if ($action eq "delete") {
exit(DeleteProfile($ARGV[0]));
}
elsif ($action eq "publish") {
exit(PublishProfile($ARGV[0]));
}
elsif (! ($action eq "create" || $action eq "update")) {
usage();
}
#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
......@@ -144,9 +134,20 @@ if (defined($options{"t"})) {
$webtask_id = $options{"t"};
}
if ($action eq "update") {
usage()
if (!@ARGV);
$update = 1;
$uuid = shift(@ARGV);
}
elsif ($action eq "delete") {
exit(DeleteProfile($ARGV[0]));
}
elsif ($action eq "publish") {
exit(PublishProfile($ARGV[0]));
}
elsif ($action ne "create") {
usage();
}
my $xmlfile = shift(@ARGV);
#
......@@ -436,12 +437,15 @@ if (defined($instance)) {
# commmand line, so that we can communicate with the script we
# call that does the work.
#
$webtask = WebTask->Create($profile->uuid(), $webtask_id);
if (!defined($webtask)) {
$profile->Delete(1);
$webtask = WebTask->Create($profile->uuid(), $webtask_id);
if (!defined($webtask)) {
$profile->Delete(1);
fatal("Could not create webtask for snapshot");
}
$webtask->AutoStore(1);
}
$webtask->AutoStore(1);
if ($profile->Lock()) {
$profile->Delete(1);
fatal("Could not lock new profile");
......@@ -458,11 +462,16 @@ if (defined($instance)) {
#
my $output = emutil::ExecQuiet($command);
if ($?) {
my $stat = $? >> 8;
$profile->Delete(1);
$webtask->Delete()
if (!defined($webtask_id));
print STDERR $output . "\n";
fatal("Failed to create disk image!");
if ($stat < 0) {
fatal("Failed to create disk image!");
}
UserError($output);
}
#
# The script helpfully put the new image urn in the webtask.
......@@ -560,6 +569,10 @@ sub fatal($)
{
my ($mesg) = @_;
if (defined($webtask)) {
$webtask->output($mesg);
$webtask->Exited(-1);
}
print STDERR "*** $0:\n".
" $mesg\n";
# Exit with negative status so web interface treats it as system error.
......@@ -568,8 +581,8 @@ sub fatal($)
#
# Generate a simple XML file that PHP can parse. The web interface
# relies on using the same name attributes for the errors, as for
# the incoming values.
# relies on using the same name attributes for the errors, as for the
# incoming values. This makes sense to use from Create/Update only.
#
sub UserError(;$)
{
......@@ -579,12 +592,23 @@ sub UserError(;$)
$errors{"error"} = $msg;
}
if (keys(%errors)) {
print "<errors>\n";
foreach my $key (keys(%errors)) {
print "<error name='$key'>" . CGI::escapeHTML($errors{$key});
print "</error>\n";
if (defined($webtask_id)) {
my $xml = "<errors>\n";
foreach my $key (keys(%errors)) {
$xml .= "<error name='$key'>" . CGI::escapeHTML($errors{$key});
$xml .= "</error>\n";
}
$xml .= "</errors>\n";
$webtask->Exited(1);
$webtask->output($xml);
}
else {
foreach my $key (keys(%errors)) {
print "$key: " . $errors{$key} . "\n";
}
}
print "</errors>\n";
}
# Exit with positive status so web interface treats it as user error.
exit(1);
......@@ -604,15 +628,26 @@ sub escapeshellarg($)
sub DeleteProfile($)
{
my ($name) = @_;
my $errmsg;
my $profile = APT_Profile->Lookup($name);
if (!defined($profile)) {
fatal("No such profile exists");
}
if (defined($webtask_id)) {
$webtask = WebTask->LookupOrCreate(undef, $webtask_id);
if (!defined($webtask)) {
fatal("Could not lookup/create webtask");
}
$webtask->AutoStore(1);
}
if (!$profile->IsHead()) {
UserError("Only allowed to delete the most recent profile");
$errmsg = "Only allowed to delete the most recent profile";
goto uerror;
}
if (!CanDelete($profile)) {
UserError("Not allowed to delete this profile (version)");
$errmsg = "Not allowed to delete this profile (version)";
goto uerror;
}
#
# Version zero is special of course.
......@@ -626,7 +661,17 @@ sub DeleteProfile($)
$profile->Delete(1) == 0 or
fatal("Could not delete profile");
}
# No need for this anymore.
$webtask->Delete()
if (!defined($webtask));
return 0;
uerror:
if (defined($webtask)) {
$webtask->Exited(1);
$webtask->output($errmsg);
}
print STDERR "$errmsg\n";
return 1;
}
#
......@@ -635,16 +680,34 @@ sub DeleteProfile($)
sub PublishProfile($)
{
my ($name) = @_;
my $errmsg;
my $profile = APT_Profile->Lookup($name);
if (!defined($profile)) {
fatal("No such profile exists");
}
if (defined($webtask_id)) {
$webtask = WebTask->LookupOrCreate(undef, $webtask_id);
if (!defined($webtask)) {
fatal("Could not lookup/create webtask");
}
$webtask->AutoStore(1);
}
if (!$profile->IsHead()) {
UserError("Only allowed to publish the most recent profile");
$errmsg = "Only allowed to publish the most recent profile";
goto uerror;
}
$profile->Publish() == 0 or
fatal("Could not publish profile");
return 0;
uerror:
if (defined($webtask)) {
$webtask->Exited(1);
$webtask->output($errmsg);
}
print STDERR "$errmsg\n";
return 1;
}
#
......
......@@ -57,14 +57,7 @@ function Do_CreateDataSet()
$command = "webcreatedataset ";
}
else {
$command = "webmanage_dataset ";
# We are going to track imaging status.
if ($formfields["dataset_type"] == "imdataset") {
$webtask_id = WebTask::GenerateID();
$command .= " -t " . $webtask_id . " -- ";
}
$command .= " create ";
$command = "webmanage_dataset create ";
}
$required = array("dataset_pid", "dataset_name", "dataset_type",
"dataset_fstype", "dataset_read", "dataset_modify");
......@@ -235,14 +228,6 @@ function Do_CreateDataSet()
$retval = SUEXEC($this_uid, $pid, $command, SUEXEC_ACTION_CONTINUE);
if ($retval) {
if (isset($webtask_id)) {
$webtask = WebTask::Lookup($webtask_id);
if ($webtask) {
$webtask->Delete();
SPITAJAX_ERROR(1, $webtask->TaskValue("output"));
return;
}
}
SPITAJAX_ERROR(1, $suexec_output);
return;
}
......
......@@ -109,22 +109,32 @@ function Do_DeleteProfile()
SPITAJAX_ERROR(1, "Not enough permission");
return;
}
#
# Invoke backend.
#
$webtask = WebTask::CreateAnonymous();
if (!$webtask) {
SPITAJAX_ERROR(-1, "Internal webtask Error");
return;
}
$retval = SUEXEC($this_uid, $profile->pid(),
"webmanage_profile delete " . $profile->uuid(),
SUEXEC_ACTION_CONTINUE);
"webmanage_profile -t " . $webtask->task_id() . " " .
"delete " . $profile->uuid(),
SUEXEC_ACTION_IGNORE);
if ($retval != 0) {
$error = "Transient error; please try again later";
if ($retval && count($suexec_output_array)) {
$error = $suexec_output_array[0];
}
$webtask->Refresh();
if ($retval < 0) {
$error = "Internal Error; please try again later\n\n";
$error .= $webtask->output();
SUEXECERROR(SUEXEC_ACTION_CONTINUE);
}
else {
$error = $webtask->output();
}
$webtask->Delete();
SPITAJAX_ERROR(1, $error);
return;
}
$webtask->Delete();
# Lookup next most recent version
$profile = Profile::Lookup($profile->profileid());
if (!$profile) {
......@@ -174,21 +184,30 @@ function Do_PublishProfile()
SPITAJAX_ERROR(1, "Not allowed to publish non-head version");
return;
}
#
# Invoke backend.
#
$webtask = WebTask::CreateAnonymous();
if (!$webtask) {
SPITAJAX_ERROR(-1, "Internal webtask Error");
return;
}
$retval = SUEXEC($this_uid, $profile->pid(),
"webmanage_profile publish " . $profile->uuid(),
SUEXEC_ACTION_CONTINUE);
"webmanage_profile -t " . $webtask->task_id() . " " .
"publish " . $profile->uuid(),
SUEXEC_ACTION_IGNORE);
if ($retval != 0) {
$error = "Transient error; please try again later";
if ($retval && count($suexec_output_array)) {
$error = $suexec_output_array[0];
}
$webtask->Refresh();
if ($retval < 0) {
$error = "Internal Error; please try again later\n\n";
$error .= $webtask->output();
SUEXECERROR(SUEXEC_ACTION_CONTINUE);
}
else {
$error = $webtask->output();
}
$webtask->Delete();
SPITAJAX_ERROR(1, $error);
return;
}
$webtask->Delete();
$profile->Refresh();
SPITAJAX_RESPONSE(array("published" => $profile->published()));
}
......
<?php
#
# Copyright (c) 2006-2014 University of Utah and the Flux Group.
# Copyright (c) 2006-2015 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -73,6 +73,40 @@ class WebTask {
return WebTask::Lookup($idx);
}
#
# Create an anonymous web task (not associated with an object). This
# is useful when using a webtask to create a new object via a backend
# script.
#
function CreateAnonymous() {
$task_id = WebTask::GenerateID();
$query_result =
DBQueryWarn("insert into web_tasks set task_id='$task_id', ".
" created=now(), object_uuid='$task_id'");
if (!$query_result || !mysql_num_rows($query_result)) {
return null;
}
return WebTask::Lookup($task_id);
}
function Refresh($task_id) {
if (! $this->IsValid())
return -1;
$task_id = $this->task_id();
$query_result =
DBQueryWarn("select * from web_tasks ".
"where task_id='$task_id'");
if (!$query_result || !mysql_num_rows($query_result)) {
$this->webtask = NULL;
return -1;
}
$this->webtask = mysql_fetch_array($query_result);
return 0;
}
# We delete from the web interface.
function Delete() {
$task_id = $this->task_id();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment