Commit 557260ef authored by Leigh B Stoller's avatar Leigh B Stoller

Add ElabInElab support (create ElabInElab by rspec). Return raw

node state in sliver status. Use a web task to track create image
and return status from ImageInfo.
parent 54fef77d
......@@ -36,8 +36,8 @@ use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
@ISA = qw(Exporter);
@EXPORT = qw();
# Must come after package declaration!
use GeniDB;
......@@ -89,6 +89,7 @@ my $GENELISTS = "$TB/sbin/genelists";
my $IMAGE_SETUP = "$TB/sbin/image_setup";
my $ARPLOCKDOWN = "$TB/sbin/arplockdown";
my $TARFILES_SETUP = "$TB/bin/tarfiles_setup";
my $ELAB_SETUP = "$TB/sbin/elabinelab";
# Cache of instances to avoid regenerating them.
my %aggregates = ();
......@@ -487,6 +488,28 @@ sub SliverList($$)
}
#
# Look for any slivers with provided state.
#
sub CheckSliverStates($$)
{
my ($self, $state) = @_;
my @slivers = ();
if ($self->SliverList(\@slivers) != 0) {
print STDERR "Could not get sliver list for $self\n";
return -1;
}
foreach my $sliver (@slivers) {
next
if (ref($sliver) ne "GeniSliver::Node");
return 1
if ($sliver->state() eq $state);
}
return 0;
}
#
# Set the aggregate for an aggregate.
#
......@@ -934,6 +957,8 @@ sub Action($$$)
my %reloads = ();
my %vnodekills = ();
my %imageinfo = ();
my @waitvnodes = ();
my @waitpnodes = ();
# See "bad" label below; want to know what sliver failed (if any).
my $sliver;
......@@ -1015,6 +1040,7 @@ sub Action($$$)
$vnodes{$node->node_id} = $node;
}
}
push(@waitvnodes, $node);
# See below.
$vnode = $node;
$vnode->_parent(undef);
......@@ -1104,6 +1130,8 @@ sub Action($$$)
# that the vnode is running one.
#
}
push(@waitpnodes, $node);
#
# If the node is not imageable, then there is not much to
# do except turn it on or reboot it. I am assuming that a
......@@ -1228,9 +1256,6 @@ sub Action($$$)
# See "bad" label below.
$sliver = undef;
my @waitvnodes = values(%vnodes);
my @waitpnodes = (values(%poweron), values(%reboots));
# Want to make sure we see fresh logs (and do not store the same log).
foreach my $node (@waitpnodes, @waitpnodes) {
$node->ClearBootLog();
......@@ -1497,10 +1522,32 @@ sub Action($$$)
$sliver->SetState("started")
if (ref($sliver) eq "GeniSliver::Node");
}
if (1) {
$self->WaitForNodes(@waitpnodes, @waitvnodes);
my @failed = ();
my $childpid = $self->WaitForNodes(\@failed, @waitpnodes, @waitvnodes);
# Parent returns
return 0
if ($childpid > 0);
return -1
if ($childpid < 0);
# Waiting is done.
if ($experiment->elab_in_elab()) {
#
# We cannot use ComputeState since it knows about elabinelab,
# we need to know that all the nodes are ISUP, so we used the
# @failed list returned from WaitForNodes().n
#
if (@failed) {
print STDERR
"Some nodes did not boot, not doing elabinelab setup\n";
return -1;
}
print STDERR "Setting up elabinelab. This could take a while!\n";
if (system("$ELAB_SETUP $pid $eid")) {
print STDERR "Failed to setup elabinelab!\n";
return -1;
}
}
return 0;
bad:
......@@ -1519,9 +1566,9 @@ sub Action($$$)
#
# Wait for nodes
#
sub WaitForNodes($@)
sub WaitForNodes($$@)
{
my ($self, @nodes) = @_;
my ($self, $pfailed, @nodes) = @_;
my %nodes = ();
my @waitstates = (TBDB_NODESTATE_TBFAILED, TBDB_NODESTATE_ISUP);
......@@ -1558,7 +1605,7 @@ sub WaitForNodes($@)
#
my $mypid = main::WrapperFork();
if ($mypid) {
return 0;
return $mypid;
}
$slice->SetMonitorPid($PID);
......@@ -1764,6 +1811,9 @@ sub WaitForNodes($@)
"in $urn failed.\n\n" .
"$logs\n\n",
$TBOPS, "Cc: $TBOPS");
# Tell caller.
@$pfailed = @failed;
}
# Too late, but reset before return; do not want it left set.
$slice->LockTables();
......
......@@ -37,8 +37,8 @@ use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
@ISA = qw(Exporter);
@EXPORT = qw();
use GeniDB;
use Genixmlrpc;
......@@ -745,6 +745,20 @@ sub GetTicketAuxAux($$$$$$$$$$)
"vname" => "ns",
"objecttype" => "6"});
#
# Look for toplevel elabinelab section.
#
my $elabinelab_settings = GeniXML::GetElabInElabSettings($rspec);
if (defined($elabinelab_settings)) {
$virtexperiment->elab_in_elab(1);
if (exists($elabinelab_settings->{'singlenet'})) {
$virtexperiment->elabinelab_singlenet(1);
}
if (exists($elabinelab_settings->{'xen'})) {
$virtexperiment->multiplex_factor(2);
}
}
#
# An rspec is a structure that requests specific nodes. If those
# nodes are available, then reserve it. Otherwise the ticket
......@@ -1391,6 +1405,47 @@ sub GetTicketAuxAux($$$$$$$$$$)
} # END TEMPORARY ELSE
}
my $elabsettings = GeniXML::GetElabInElabSettings($ref);
if (defined($elabsettings)) {
my $extradisksize;
my $extradiskmnts;
if (exists($elabsettings->{'role'})) {
my $erole = $elabsettings->{'role'};
my $role;
$nodeblob->{'inner_elab_role'} = $erole;
if ($erole =~ /boss/) {
$extradisksize = "disk1:10G,disk2:10G,disk3:10G";
$extradiskmnts =
"da1:/usr/testbed,da2:/usr/testbed/data,da3:".
"/usr/testbed/log";
$role = "boss";
}
else {
$extradisksize = "disk1:4G,disk2:10G,disk3:10G";
$extradiskmnts =
"da1:/usr/testbed,da2:/q,da3:/share";
$role = "ops";
}
$virtexperiment->NewTableRow("virt_node_attributes",
{"vname" => $node_nickname,
"attrkey" => "XEN_EXTRADISKS",
"attrvalue" => $extradisksize});
$virtexperiment->NewTableRow("elabinelab_attributes",
{"role" => $role,
"attrkey" => "EXTRADISKS",
"attrvalue" => $extradiskmnts,
"ordering" => 0});
}
else {
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No role for elabinelab");
goto bad;
}
}
my $virtnode = $virtexperiment->NewTableRow("virt_nodes", $nodeblob);
if (!defined($virtnode)) {
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......
......@@ -37,8 +37,8 @@ use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
@ISA = qw(Exporter);
@EXPORT = qw();
# Must come after package declaration!
use GeniDB;
......@@ -596,6 +596,14 @@ sub DeleteSliver($)
if ($slice->monitor_pid()) {
return GeniResponse->MonitorResponse();
}
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
# If any slivers are imaging, then we are busy as well.
if ($aggregate->CheckSliverStates("imaging")) {
$slice->UnLock();
return GeniResponse->BusyResponse();
}
main::AddLogfileMetaData("sliver_urn", $sliver_urn);
main::AddLogfileMetaDataFromSlice($slice);
......@@ -725,6 +733,11 @@ sub DeleteSlice($)
$slice->UnLock();
return GeniResponse->MonitorResponse();
}
# If any slivers are imaging, then we are busy as well.
if ($aggregate->CheckSliverStates("imaging")) {
$slice->UnLock();
return GeniResponse->BusyResponse();
}
if (GeniCM::CleanupDeadSlice($slice, 1) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -1153,6 +1166,8 @@ sub SliverStatus($)
}
$blob->{'details'}->{$sliver_urn} = {
"component_urn" => $resource_id,
"rawstate" => (defined($sliver->rawstate()) ?
$sliver->rawstate() : "unknown"),
"state" => $state,
"status" => $status,
"error" => $error,
......@@ -2341,6 +2356,7 @@ sub CreateImage($)
my $wholedisk = 0;
require Image;
require WebTask;
if (! (defined($credentials) && defined($imagename) &&
defined($slice_urn) && defined($sliver_urn))) {
......@@ -2413,6 +2429,17 @@ sub CreateImage($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Node is not in the proper experiment");
}
#
# On this path, we do not allow system images to be shadowed.
# We define system images as those in the emulab-ops project.
#
if (Image->LookupByName($imagename)) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Not allowed to shadow system images; ".
"use a different name for your image");
}
#
# Lock the slice; we do not the user to mess with things.
#
......@@ -2453,6 +2480,23 @@ sub CreateImage($)
$pid . "//" . $imagename);
my $image_url = "$TBBASE/image_metadata.php?uuid=" . $image->uuid();
# Mark the sliver for sliver status calls. We can also unlock
# since the state will prevent anything from being done.
$sliver->SetState("imaging");
#
# Create a task object to track the status. The backend
# updates the task structure as the imaging proceeds, and
# ImageInfo below can reference that status.
#
my $webtask = WebTask->Create($image->uuid());
if (!defined($webtask)) {
print STDERR "Could not create new web task structure\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
$webtask->status("preparing");
$webtask->imagesize(0);
#
# We used the -s (nosnapshot) option above, to get the descriptor
# built, so now run create_image so that we can capture its output.
......@@ -2464,6 +2508,8 @@ sub CreateImage($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
[$image_urn, $image_url]);
}
$slice->UnLock();
#
# Do the snapshot.
#
......@@ -2471,6 +2517,8 @@ sub CreateImage($)
GeniUtil::ExecQuiet("$CREATEIMAGE -e -f -p $pid $imagename $node_id");
# Not a typical op, so always print debugging info;
print STDERR $output;
# Return to normal state even if it failed.
$sliver->SetState("started");
if ($?) {
print STDERR "Failed to take the snapshot of $node_id\n";
$slice->UnLock();
......@@ -2494,7 +2542,6 @@ sub CreateImage($)
$user->email(),
"Bcc: $TBOPS");
}
$slice->UnLock();
return 0;
}
......@@ -2590,6 +2637,109 @@ sub DeleteImage($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# Image status call, mostly while creating a new image.
#
sub ImageInfo($)
{
my ($argref) = @_;
my $image_urn = $argref->{'image_urn'};
my $credentials = $argref->{'credentials'};
my ($imagename,$imagepid);
require Image;
require File::stat;
require WebTask;
if (! (defined($credentials) && defined($image_urn))) {
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
if (! GeniHRN::IsValid($image_urn)) {
return GeniResponse->MalformedArgsResponse("Invalid URN");
}
my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
return $credential
if (GeniResponse::IsResponse($credential));
main::AddLogfileMetaData("image_urn", $image_urn);
my $user = GeniCM::CreateUserFromCertificate($credential);
return $user
if (GeniResponse::IsResponse($user));
my $authority = GeniCM::CreateAuthorityFromCertificate($credential);
return $authority
if (GeniResponse::IsResponse($authority));
my ($auth,undef,$id) = GeniHRN::Parse($image_urn);
return GeniResponse->MalformedArgsResponse("Malformed URN")
if (!defined($id));
if ($id =~ m{(.*)//(.*)}) {
$imagepid = $1;
$imagename = $2;
}
else {
return GeniResponse->MalformedArgsResponse("Could not parse $id");
}
#
# Make sure we can get the image descriptor.
#
my $image = Image->Lookup($imagepid, $imagename);
if (!defined($image)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"No such image");
}
my $imageid = $image->imageid();
my $creator_urn = $image->creator_urn();
#
# Need the project to compare the manager urn against the user SA.
#
my $project = $image->GetProject();
if (!defined($project)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No project for image");
}
if (! ($image->global() ||
(defined($creator_urn) && $creator_urn eq $user->urn()) ||
$project->nonlocal_id() eq $authority->urn())) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Not enough permission to access image");
}
my $blob = { "size" => 0, "status" => "ready" };
#
# Is there an active webtask, then we are taking a snapshot, so
# report that info. Otherwise the current file.
#
my $webtask = WebTask->LookupByObject($image->uuid());
if (defined($webtask)) {
$blob->{'size'} = $webtask->imagesize() . "KB";
$blob->{'status'} = $webtask->status();
#
# We have reported status, kill it. So if two parties are
# trying to determine the status, one gets nothing. Maybe
# we need a notion of staleness.
#
if ($webtask->HasExited()) {
$webtask->Delete();
}
}
else {
my $path = $image->path();
if (-r $path) {
my $size = File::stat::stat($path)->size;
$blob->{'size'} = int($size / 1024) . "KB";
}
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
#
# List images for a user. If no user supplied, when for the caller.
# If user is supplied, then it must be the caller of from the same
......
......@@ -141,6 +141,7 @@ sub Lookup($$)
$self->{'AGGREGATE'} = undef; # server
$self->{'RSPEC'} = undef; # server
$self->{'CERTIFICATE'} = undef;
$self->{'RAWSTATE'} = undef;
my $rspec_string = $self->{'SLIVER'}->{'rspec_string'};
if (defined($rspec_string) && $rspec_string ne "") {
......@@ -300,6 +301,7 @@ sub aggregate_uuid($) { return field($_[0], "aggregate_uuid"); }
sub rspec_string($) { return field($_[0], "rspec_string"); }
sub status($) { return field($_[0], "status"); }
sub state($) { return field($_[0], "state"); }
sub rawstate($) { return $_[0]->{'RAWSTATE'}; }
sub ErrorLog($) { return field($_[0], "errorlog"); }
sub cert($) { return GetCertificate($_[0])->cert(); }
sub rspec($) { return $_[0]->{'RSPEC'}; }
......@@ -1304,6 +1306,9 @@ sub ComputeStatus($$)
print STDERR "$node was already released from $self\n";
return -1;
}
# Stash this away.
$self->{'RAWSTATE'} = $node->eventstate();
#
# Special state for updating user accounts. We never go into the
# state unless the sliver was started/ready, so we know we can go
......@@ -1319,6 +1324,32 @@ sub ComputeStatus($$)
$self->SetState("started");
}
}
elsif ($self->state() eq "imaging") {
$status = "changing";
goto done;
}
elsif ($reservation->elab_in_elab()) {
#
# The inner nodes do not tell us what is happening, so we just
# call them ready when inner_elab_boot is set, which means that
# inner elab is setup and running okay. Inner boss/ops do tell
# us their state, so we use that to determine ready.
#
if ($node->eventstate() eq TBDB_NODESTATE_TBFAILED()) {
$status = "failed";
goto done;
}
elsif ($node->inner_elab_role() eq "node") {
$status = ($node->inner_elab_boot() ? "ready" : "notready");
goto done;
}
elsif (!$node->inner_elab_boot()) {
$status = "notready";
goto done;
}
# Fall through, waiting for ISUP for boss/ops.
}
#
# Emulab does not return "unknown" ... we always know ...
#
......
......@@ -871,6 +871,21 @@ sub GetXenSettings($)
return $result;
}
sub GetElabInElabSettings($)
{
my ($ref) = @_;
my $result = {};
my $settings = FindNodesNS("n:elabinelab", $ref, $EMULAB_NS)->pop();
return undef
if (!defined($settings));
foreach my $attr ($settings->attributes()) {
$result->{$attr->nodeName} = $attr->getValue();
}
return $result;
}
sub HasFirewallSettings($)
{
my ($node) = @_;
......
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