Commit cfd1974a authored by Leigh B Stoller's avatar Leigh B Stoller

I added two new actions to PerformOperationalAction, which appear to

work fine when the nodes are behaving themselves.

1) geni_update_users: Takes a slice credential and a keys argument. Can
  only be invoked when the sliver is in the started/geni_ready state.
  Moves the slice to the geni_updating_users state until all of the
  nodes have completed the update, at which time the sliver moves back
  to started/geni_ready.

2) geni_updating_users_cancel: We can assume that some nodes will be whacky
  and will not perform the update when told to. This cancels the
  update and moves the sliver back to started/geni_ready.

A couple of notes:

* The current emulab node update time is about three minutes; the
  sliver is in this new state for that time and cannot be restarted or
  stopped. It can of course be deleted.

* Should we allow restart while in the updating phase? We could, but
  then I need more bookkeeping.

* Some nodes might not be running the watch dog, or might not even be
  an emulab image, so the operation will never end, not until
  canceled. I could add a timeout, but that will require a monitor or
  adding DB state to store the start time.
parent 39c7dc5f
......@@ -5344,6 +5344,50 @@ sub MarkNodesForUpdate($)
return 0;
}
sub CancelNodeUpdates($)
{
my ($self) = @_;
my @nodelist = $self->NodeList(0, 1);
return 0
if (! @nodelist);
foreach my $node (@nodelist) {
next
if ($node->erole() eq TBDB_RSRVROLE_NODE());
$node->CancelUpdate();
}
return 0;
}
#
# Return list of nodes that have not updated yet.
#
sub CheckUpdateStatus($$)
{
my ($self, $pnotdone) = @_;
my @tmp = ();
my @nodelist = $self->NodeList(0, 1);
return ()
if (! @nodelist);
foreach my $node (@nodelist) {
next
if ($node->erole() eq TBDB_RSRVROLE_NODE());
push(@tmp, $node);
}
my @done = ();
my @notedone = ();
return -1
if (Node->CheckUpdateStatus(\@done, \@notdone, @tmp));
return @notdone;
}
#
# Look for a lan with ports in another lan. These are currently labled
# with the incredibly obtuse "portlan" type instead of vlan. If one of
......
......@@ -1859,6 +1859,42 @@ sub MarkForUpdate($)
return Refresh($self);
}
sub CancelUpdate($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $node_id = $self->node_id();
return -1
if (! DBQueryWarn("update nodes set update_accounts=0 ".
"where node_id='$node_id'"));
return Refresh($self);
}
sub IsUpdated($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $node_id = $self->node_id();
my $query_result =
DBQueryWarn("select update_accounts from nodes ".
"where node_id='$node_id'");
return -1
if (! $query_result);
return 1
if (!$query_result->numrows);
my ($update_accounts) = $query_result->fetchrow_array();
return ($update_accounts ? 0 : 1);
}
# Class method!
sub CheckUpdateStatus($$$@)
{
......
......@@ -834,11 +834,6 @@ sub Describe
# Add any slivers that are provisioned (exist in the aggregate)
if (defined($aggregate)) {
my $expires = GeniXML::GetText("expires", $manifest);
if (! defined($expires)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Manifest is missing expires tag");
}
my @slivers = ();
if ($aggregate->SliverList(\@slivers) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -1055,7 +1050,7 @@ sub Renew
}
my $credentials = FilterCredentials($credential_args);
my @urns = @{ $urn_args};
return GeniResponse->MalformedArgsResposne("Empty URN List")
return GeniResponse->MalformedArgsResponse("Empty URN List")
if (scalar(@urns) < 1);
my $args = {
'slice_urn' => $urns[0],
......@@ -1083,7 +1078,7 @@ sub Provision
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
my @urns = @{ $urn_args };
return GeniResponse->MalformedArgsResposne("Empty URN List")
return GeniResponse->MalformedArgsResponse("Empty URN List")
if (scalar(@urns) < 1);
my $credentials = FilterCredentials($credential_args);
my $users = $options->{'geni_users'};
......@@ -1165,7 +1160,7 @@ sub PerformOperationalAction
{
my ($urn_args, $credential_args, $action, $options) = @_;
my @urns = @{ $urn_args };
return GeniResponse->MalformedArgsResposne("Empty URN List")
return GeniResponse->MalformedArgsResponse("Empty URN List")
if (scalar(@urns) < 1);
my $credentials = FilterCredentials($credential_args);
......@@ -1177,7 +1172,7 @@ sub PerformOperationalAction
return $cred
if (GeniResponse::IsResponse($cred));
my ($slice, undef) = GeniCMV2::Credential2SliceAggregate($cred);
my ($slice, $aggregate) = GeniCMV2::Credential2SliceAggregate($cred);
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Slice credential not provided")
if (! defined($slice));
......@@ -1194,18 +1189,44 @@ sub PerformOperationalAction
return GeniCMV2::RestartSliver($args);
} elsif ($action eq 'geni_stop') {
return GeniCMV2::StopSliver($args);
} else {
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Invalid operational action");
} elsif ($action eq 'geni_update_users') {
if (!exists($options->{"keys"})) {
return GeniResponse->MalformedArgsResponse("No keys provided!");
}
$args->{'slice_urn'} = $slice->urn();
$args->{'keys'} = $options->{"keys"};
$args->{'amapiv3'} = 1;
return GeniCMV2::BindToSlice($args);
}
elsif ($action eq 'geni_update_users_cancel') {
#
# All we care about is making sure the state is cleared
# from the aggregate and all of the slivers.
#
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
if ($aggregate->state() ne "updating_users") {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
if ($aggregate->CancelUpdateAccounts()) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Invalid operational action");
}
sub Delete
{
my ($urn_args, $credential_args, $option_args) = @_;
my @urns = @{ $urn_args };
return GeniResponse->MalformedArgsResposne("Empty URN List")
return GeniResponse->MalformedArgsResponse("Empty URN List")
if (scalar(@urns) < 1);
# Must create return structure before deletion because this data
......@@ -1240,7 +1261,7 @@ sub Cancel
{
my ($urn_args, $credential_args, $option_args) = @_;
my @urns = @{ $urn_args };
return GeniResponse->MalformedArgsResposne("Empty URN List")
return GeniResponse->MalformedArgsResponse("Empty URN List")
if (scalar(@urns) < 1);
my $credentials = FilterCredentials($credential_args);
......@@ -1305,6 +1326,9 @@ sub GetOpState
} elsif ($sliver->status() eq 'created'
&& $sliver->state() eq 'new') {
$result = 'geni_notready';
} elsif ($sliver->status() eq 'changing'
&& $sliver->state() eq 'updating_users') {
$result = 'geni_updating_users';
}
print STDERR $sliver->status() . ", " . $sliver->state() . ", " . $result . "\n";
return $result;
......
......@@ -53,6 +53,7 @@ use GeniHRN;
use GeniXML;
use emutil;
use EmulabConstants;
use Node;
use Logfile;
use libtestbed;
use Data::Dumper;
......@@ -1912,6 +1913,7 @@ sub ComputeState($)
my ($self) = @_;
my $started = 0;
my $stopped = 0;
my $updating= 0;
my $unknown = 0;
my $ready = 0;
my $notready= 0;
......@@ -1936,21 +1938,25 @@ sub ComputeState($)
if (ref($sliver) ne "GeniSliver::Node");
my $status;
# This might change the state, so get state next.
if ($sliver->ComputeStatus(\$status)) {
print STDERR "Could not determine status for $sliver in $self\n";
return -1;
}
my $state = $sliver->state();
if (!defined($state)) {
print STDERR "Could not determine state for $sliver in $self\n";
return -1;
}
if ($sliver->ComputeStatus(\$status)) {
print STDERR "Could not determine status for $sliver in $self\n";
return -1;
}
if ($state eq "started") {
$started++;
}
elsif ($state eq "stopped" || $state eq "new") {
$stopped++;
}
elsif ($state eq "updating_users") {
$updating++;
}
else {
$unknown++;
}
......@@ -1969,6 +1975,17 @@ sub ComputeState($)
}
$count++;
}
if ($self->state() eq "updating_users") {
# If slivers still updating, we stay in this state.
# Otherwise, fall through to below to compute normal state.
if ($updating) {
$self->SetStatus("changing");
return 0;
}
else {
}
}
if ($stopped == $count) {
$self->SetState("stopped");
}
......@@ -2015,6 +2032,66 @@ sub FindSliverByNickname($$)
return GeniSliver->Lookup($idx);
}
#
# Mark aggregate and slivers for update accounts.
#
sub UpdateAccounts($$)
{
my ($self, $amapi) = @_;
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");
my $node = Node->Lookup($sliver->resource_id());
next
if (!defined($node) ||
$node->erole() ne EmulabConstants::TBDB_RSRVROLE_NODE());
$node->MarkForUpdate();
if ($amapi) {
$sliver->SetState("updating_users");
}
}
if ($amapi) {
$self->SetState("updating_users");
}
return 0;
}
sub CancelUpdateAccounts($)
{
my ($self) = @_;
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");
my $node = Node->Lookup($sliver->resource_id());
next
if (!defined($node));
$node->CancelUpdate();
$sliver->SetState("started")
if ($sliver->state() eq "updating_users");
}
$self->SetState("updating_users")
if ($self->state() eq "updating_users");
return 0;
}
############################################################################
#
# Link aggregates need special handling.
......
......@@ -1030,7 +1030,7 @@ sub SliverAction($$$$$)
#
sub SliverStatus($)
{
my ($argref) = @_;
my ($argref) = @_;
my $slice_urn = $argref->{'slice_urn'};
my $credentials = $argref->{'credentials'};
......@@ -1628,6 +1628,7 @@ sub BindToSlice($)
my $slice_urn = $argref->{'slice_urn'};
my $credentials = $argref->{'credentials'};
my $keys = $argref->{'keys'};
my $amapi = exists($argref->{'amapiv3'});
if (! (defined($credentials) &&
defined($slice_urn) && defined($keys))) {
......@@ -1672,7 +1673,25 @@ sub BindToSlice($)
return $response;
}
}
$slice->MarkNodesForUpdate();
# Need experiment for this.
if (defined($aggregate)) {
if ($amapi) {
if ($aggregate->ComputeState()) {
print STDERR "Could not compute state for $aggregate\n";
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
if ($aggregate->state() ne "started") {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Slice is not in the proper state");
}
}
if ($aggregate->UpdateAccounts($amapi)) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR());
}
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
......
......@@ -1268,6 +1268,21 @@ sub ComputeStatus($$)
return -1;
}
#
# Special state for updating user accounts. We never go into the
# state unless the sliver was started/ready, so we know we can go
# back into the started state when it is done.
#
if ($self->state() eq "updating_users") {
if ($node->IsUpdated() == 0) {
$status = "changing";
goto done;
}
else {
# return to normal started state and continue below.
$self->SetState("started");
}
}
#
# Emulab does not return "unknown" ... we always know ...
#
my $eventstate = $node->eventstate();
......@@ -1284,6 +1299,7 @@ sub ComputeStatus($$)
else {
$status = "changing";
}
done:
$self->SetStatus($status);
$$pref = $status;
return 0;
......
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