Commit 88afbcf0 authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

Add the rest of the V2 API implmentation, except for WaitForSlivers.

Also still working on start/stop/restart on individual slivers in an
aggregate.
parent f2b9f6ba
......@@ -1848,7 +1848,6 @@ sub SliverWorkAux($$$$$$$)
my $sliver = GeniSliver::Node->Create($slice,
$owner,
$resource_uuid,
undef,
$ref);
if (!defined($sliver)) {
$message = "Could not create GeniSliver object for $virtual_id";
......@@ -1857,25 +1856,8 @@ sub SliverWorkAux($$$$$$$)
$slivers{$sliver->uuid()} = $sliver;
$nodemap{$virtual_id} = $sliver;
#
# Find the actual node (might be a vnode) so that we can put
# the info into the manifest.
#
my $realnode = Node->Lookup($sliver->uuid());
if (!defined($realnode)) {
print STDERR "Could not lookup node for $sliver\n";
$message = "Internal Error finding mapped node for $virtual_id\n";
goto bad;
}
my $sliver_urn = GeniHRN::Generate($OURDOMAIN, "sliver",
$realnode->node_id());
# Manifest goes back to the user.
$ref->{'sliver_uuid'} = $sliver->uuid();
$ref->{'sliver_urn'} = $sliver_urn;
$ref->{'component_hostname'} = $realnode->node_id() . ".${OURDOMAIN}";
$ref->{'sshdport'} = $realnode->sshdport()
if ($realnode->isvirtnode());
# For the manifest.
$ref->{'sliver_urn'} = $sliver->sliver_urn();
# Add to the aggregate.
if ($sliver->SetAggregate($aggregate) != 0) {
......@@ -1957,6 +1939,7 @@ sub SliverWorkAux($$$$$$$)
# Manifest goes back to the user.
$linkref->{'sliver_uuid'} = $tunnel->uuid();
$linkref->{'sliver_urn'} = $tunnel->urn();
# Add to the aggregate.
if ($tunnel->SetAggregate($aggregate) != 0) {
......@@ -1977,6 +1960,7 @@ sub SliverWorkAux($$$$$$$)
# Manifest goes back to the user.
$linkref->{'sliver_uuid'} = $linkaggregate->uuid();
$linkref->{'sliver_urn'} = $linkaggregate->urn();
# Add to the aggregate.
if ($linkaggregate->SetAggregate($aggregate) != 0) {
......@@ -2030,8 +2014,8 @@ sub SliverWorkAux($$$$$$$)
}
my $sliver = GeniSliver::Interface->Create($slice,
$owner,
undef,
$nodeobject,
$nodeobject->node_id(),
$iface_name,
$linkname,
$ifaceref);
if (!defined($sliver)) {
......@@ -2040,7 +2024,7 @@ sub SliverWorkAux($$$$$$$)
goto bad;
}
# Manifest goes back to the user.
$ifaceref->{'sliver_uuid'} = $sliver->uuid();
$ifaceref->{'sliver_urn'} = $sliver->sliver_urn();
$ifaceref->{'MAC'} = $interface->mac()
if (defined($interface));
......
......@@ -114,12 +114,9 @@ sub Resolve($)
return $credential
if (GeniResponse::IsResponse($credential));
# The URN encodes the type.
my ($auth,$type,$id) = GeniHRN::Parse($urn);
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Authority mismatch")
if ($auth ne $OURDOMAIN);
$type = lc($type);
my ($object, $type) = LookupURN($urn);
return $object
if (GeniResponse::IsResponse($object));
#
# This is a convenience for testing. If a local user and that
......@@ -132,15 +129,11 @@ sub Resolve($)
}
if ($type eq "node") {
my $node = GeniCM::LookupNode($urn);
if (! defined($node)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "Nothing here by that name");
}
my $node = $object;
my $rspec = GeniCM::GetAdvertisement(0, $node->node_id());
if (! defined($rspec)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not start avail");
"Error getting advertisement");
}
# Return a blob.
my $blob = { "hrn" => "${PGENIDOMAIN}." . $node->node_id(),
......@@ -158,11 +151,8 @@ sub Resolve($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
if ($type eq "slice") {
my $slice = GeniSlice->Lookup($urn);
if (!defined($slice)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "Nothing here by that name");
}
my $slice = $object;
#
# In this implementation, the caller must hold a valid slice
# credential for the slice being looked up.
......@@ -184,22 +174,19 @@ sub Resolve($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
if ($type eq "sliver") {
my $aggregate = GeniAggregate->Lookup($urn);
if (!defined($aggregate)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "Nothing here by that name");
}
my $sliver = $object;
#
# In this implementation, the caller must hold a valid slice
# or sliver credential for the slice being looked up.
#
if (! ($admin ||
$aggregate->uuid() eq $credential->target_uuid() ||
$aggregate->slice_uuid() eq $credential->target_uuid())) {
$sliver->uuid() eq $credential->target_uuid() ||
$sliver->slice_uuid() eq $credential->target_uuid())) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN);
}
my $manifest = $aggregate->GetManifest(1);
if (!defined($aggregate)) {
my $manifest = $sliver->GetManifest(1);
if (!defined($manifest)) {
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
# Return a blob.
......@@ -209,11 +196,8 @@ sub Resolve($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
if ($type eq "ticket") {
my $ticket = GeniTicket->Lookup($urn);
if (!defined($ticket)) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "Nothing here by that name");
}
my $ticket = $object;
#
# In this implementation, the caller must hold a valid slice
# or sliver credential to get the ticket.
......@@ -572,6 +556,7 @@ sub RestartSliver($)
sub SliverAction($$$$)
{
my ($action, $slice_urn, $sliver_urns, $credentials) = @_;
my $response;
if (! (defined($credentials) &&
(defined($slice_urn) || defined($sliver_urns)))) {
......@@ -594,13 +579,6 @@ sub SliverAction($$$$)
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"No slice or aggregate here");
}
# Shutdown slices get nothing.
if ($slice->shutdown()) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Slice has been shutdown");
}
if (defined($slice_urn)) {
if (! GeniHRN::IsValid($slice_urn)) {
return
......@@ -610,57 +588,106 @@ sub SliverAction($$$$)
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
"Credential does not match the URN");
}
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
if ($aggregate->ComputeState()) {
$slice->UnLock();
print STDERR "Could not determine current state\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
}
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
# Shutdown slices get nothing.
if ($slice->shutdown()) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Slice has been shutdown");
}
if ($aggregate->ComputeState()) {
$slice->UnLock();
print STDERR "Could not determine current state\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
my $CheckState = sub {
my ($object, $action) = @_;
if ($action eq "start") {
if ($aggregate->state() ne "stopped") {
$slice->UnLock();
if ($object->state() ne "stopped") {
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Sliver is not stopped (yet)");
}
if ($aggregate->Start($API_VERSION, 0) != 0) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not start sliver");
}
}
elsif ($action eq "stop") {
if ($aggregate->state() ne "started") {
$slice->UnLock();
if ($object->state() ne "started") {
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Sliver is not started (yet)");
}
if ($aggregate->Stop($API_VERSION) != 0) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not stop sliver");
}
}
elsif ($action eq "restart") {
if ($aggregate->state() ne "started") {
$slice->UnLock();
if ($object->state() ne "started") {
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Sliver is not started (yet)");
}
if ($aggregate->Start($API_VERSION, 1) != 0) {
$slice->UnLock();
}
return 0;
};
my $PerformAction = sub {
my ($object, $action) = @_;
if ($action eq "start") {
if ($object->Start($API_VERSION, 0) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not restart sliver");
"Could not start sliver");
}
}
elsif ($action eq "stop") {
if ($object->Stop($API_VERSION) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not stop sliver");
}
}
elsif ($action eq "restart") {
if ($object->Start($API_VERSION, 1) != 0) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not restart sliver");
}
}
return 0;
};
if (defined($slice_urn)) {
$response = &$CheckState($aggregate, $action);
goto bad
if (GeniResponse::IsResponse($response));
$response = &$PerformAction($aggregate, $action);
goto bad
if (GeniResponse::IsResponse($response));
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
else {
my @slivers = ();
#
# Sanity check all arguments before doing anything.
#
foreach my $urn (@{ $sliver_urns }) {
my $sliver = GeniSliver->Lookup($urn);
$response = &$CheckState($sliver, $action);
goto bad
if (GeniResponse::IsResponse($response));
push(@slivers, $sliver);
}
foreach my $sliver (@slivers) {
$response = &$PerformAction($sliver, $action);
goto bad
if (GeniResponse::IsResponse($response));
}
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
return GeniResponse->Create(GENIRESPONSE_UNSUPPORTED);
bad:
$slice->UnLock();
return $response;
}
#
......@@ -731,7 +758,8 @@ sub SliverStatus($)
next
if ($sliver->resource_type() ne "Node");
my $urn = $sliver->urn();
my $sliver_urn = $sliver->sliver_urn();
my $component_urn = $sliver->component_urn();
my $state = $sliver->state();
my $status = $sliver->status();
my $error = "";
......@@ -746,7 +774,8 @@ sub SliverStatus($)
$blob->{'status'} = "notready"
if ($blob->{'status'} ne "failed");
}
$blob->{'details'}->{$urn} = {
$blob->{'details'}->{$sliver_urn} = {
"component_urn" => $component_urn,
"state" => $state,
"status" => $status,
"error" => $error,
......@@ -1147,30 +1176,52 @@ sub BindToSlice($)
sub ReleaseTicket($)
{
my ($argref) = @_;
my $slice_urn = $argref->{'slice_urn'};
my $ticketstr = $argref->{'ticket'};
if (! (defined($ticketstr))) {
my $credentials = $argref->{'credentials'};
if (! (defined($credentials) &&
defined($slice_urn) && defined($ticketstr))) {
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
my $credential = CheckCredentials($credentials);
return $credential
if (GeniResponse::IsResponse($credential));
$credential->HasPrivilege( "pi" ) or
$credential->HasPrivilege( "bind" ) or
return GeniResponse->Create( GENIRESPONSE_FORBIDDEN, undef,
"Insufficient privilege" );
my ($slice, $aggregate) = Credential2SliceAggregate($credential);
if (! (defined($slice))) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"No slice here");
}
if ($slice_urn ne $slice->urn()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
"Credential does not match the URN");
}
my $ticket = GeniTicket->CreateFromSignedTicket($ticketstr);
if (!defined($ticket)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniTicket object");
}
#
# Make sure the ticket was issued to the caller.
#
if ($ticket->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"This is not your ticket");
}
#
# If the ticket is not stored, it is not a ticket that needs
# to be released. It is a copy or a reissue. Needs more thought.
#
if (! $ticket->stored()) {
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# And of course, the ticket has to be for the slice indicated
# by the provided credential.
#
if ($ticket->slice_uuid() ne $slice->uuid()) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This ticket is for another slice");
}
if ($ticket->Lock() != 0) {
return GeniResponse->BusyResponse("ticket");
}
......@@ -1224,8 +1275,7 @@ sub CheckCredentials($)
"Could not create credential object");
}
#
# Make sure the credential was issued to the caller, but no special
# permission required to resolve component resources.
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
......@@ -1268,7 +1318,7 @@ sub LookupURN($)
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
undef, "Nothing here by that name");
}
return $object;
return ($object, $type);
}
#
......
......@@ -64,7 +64,14 @@ sub Lookup($$)
my $query_result;
my $idx;
if ($token =~ /^\d+$/) {
if (GeniHRN::IsValid($token)) {
return undef if !GeniHRN::Authoritative($token, "@OURDOMAIN@");
my ($authority, $type, $id) = GeniHRN::Parse($token);
return undef if $type ne "sliver";
$idx = $id;
}
elsif ($token =~ /^\d+$/) {
$idx = $token;
}
elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
......@@ -98,8 +105,15 @@ sub Lookup($$)
my $rspec_string = $self->{'SLIVER'}->{'rspec_string'};
if (defined($rspec_string) && $rspec_string ne "") {
$self->{'RSPEC'} = XMLin($rspec_string,
ForceArray => ["node", "link"]);
my $rspec =
eval { XMLin($rspec_string, KeyAttr => [],
ForceArray => ["node", "link", "interface",
"interface_ref", "linkendpoints"]) };
if ($@) {
print STDERR "XMLin error reading rspec: $@\n";
return undef;
}
$self->{'RSPEC'} = $rspec;
}
#
......@@ -147,32 +161,26 @@ sub Stringify($)
#
sub Create($$$$$$$$$)
{
my ($class, $slice, $owner, $uuid, $resource_uuid, $resource_type,
my ($class, $slice, $owner, $uuid,
$resource_uuid, $resource_type, $resource_id,
$hrn, $nickname, $rspec) = @_;
my @insert_data = ();
my $certificate;
# Every sliver gets a new unique index.
my $idx = TBGetUniqueIndex('next_sliver', 1);
my $urn = GeniHRN::Generate("@OURDOMAIN@", "sliver", $idx);
# Create a cert pair, for this resource uuid.
if (defined($uuid) && $resource_type eq "Node") {
$certificate = GeniCertificate->Lookup($uuid);
if (defined($certificate) && $certificate->hrn() ne $hrn) {
print STDERR "GeniSliver::Create: ".
"Already have a certificate for $hrn/$uuid\n";
return undef;
}
# Sanity check.
my $certificate = GeniCertificate->Lookup($urn);
if (defined($certificate)) {
print STDERR "GeniSliver::Create: ".
"Already have a certificate for $hrn/$urn\n";
return undef;
}
$hrn =~ /.*([^.]+)$/;
my $urn = GeniHRN::Generate( "@OURDOMAIN@", "node", $1 );
$certificate = GeniCertificate->Create("sliver", $urn, $hrn, $TBOPS, $uuid)
if (!defined($certificate));
$certificate = GeniCertificate->Create("sliver", $urn, $hrn, $TBOPS,$uuid);
if (!defined($certificate)) {
print STDERR "GeniSliver::Create: ".
"Could not generate new certificate and UUID for $hrn/$uuid\n";
"Could not generate new certificate for $hrn/$urn\n";
return undef;
}
my $slice_uuid = $slice->uuid();
......@@ -187,6 +195,7 @@ sub Create($$$$$$$$$)
push(@insert_data, "uuid='$uuid'");
push(@insert_data, "resource_uuid='$resource_uuid'");
push(@insert_data, "resource_type='$resource_type'");
push(@insert_data, "resource_id='$resource_id'");
push(@insert_data, "creator_uuid='$owner_uuid'");
push(@insert_data, "slice_uuid='$slice_uuid'");
......@@ -228,6 +237,7 @@ sub creator_uuid($) { return field($_[0], "creator_uuid"); }
sub created($) { return field($_[0], "created"); }
sub credential_idx($) { return field($_[0], "credential_idx"); }
sub resource_uuid($) { return field($_[0], "resource_uuid"); }
sub resource_id($) { return field($_[0], "resource_id"); }
sub resource_type($) { return field($_[0], "resource_type"); }
sub component_uuid($) { return field($_[0], "component_uuid"); }
sub aggregate_uuid($) { return field($_[0], "aggregate_uuid"); }
......@@ -238,6 +248,14 @@ sub cert($) { return $_[0]->{'CERTIFICATE'}->cert(); }
sub GetCertificate($) { return $_[0]->{'CERTIFICATE'}; }
sub rspec($) { return $_[0]->{'RSPEC'}; }
# Return the sliver URN.
sub sliver_urn($)
{
my ($self) = @_;
return GeniHRN::Generate("@OURDOMAIN@", "sliver", $self->idx());
}
#
# Delete the sliver. The sliver should not be provisioned when this done.
#
......@@ -270,6 +288,30 @@ sub Delete($$)
return 0;
}
#
# Get the manifest for an aggregate. Returns the XML string.
#
sub GetManifest($$)
{
my ($self, $asxml) = @_;
return undef
if (! ref($self));
my $manifest = $self->rspec();
return $manifest
if (!$asxml);
my $xml =
eval { XMLout($manifest, "NoAttr" => 1, RootName => "manifest") };
if ($@) {
print STDERR "XMLout error on manifest: $@\n";
return undef;
}
return $xml;
}
#
# Set the aggregate for a sliver.
#
......@@ -533,15 +575,6 @@ use XML::Simple;
use libdb qw(TBDB_ALLOCSTATE_RES_INIT_DIRTY TBDB_NODESTATE_SHUTDOWN
TBResolveNextOSID TBDB_NODESTATE_ISUP TBDB_NODESTATE_TBFAILED);
# Return the URN.
sub urn($)
{
my ($self) = @_;
my ($node_id) = ($self->hrn() =~ /\.([-\w]*)$/);
return GeniHRN::Generate("@OURDOMAIN@", "node", $node_id);
}
# Return the URN.
sub ErrorLog($)
{
......@@ -560,9 +593,17 @@ sub ErrorLog($)
return $bootlog;
}
# Return the component URN. This is how a resource is resolved.
sub component_urn($)
{
my ($self) = @_;
return GeniHRN::Generate("@OURDOMAIN@", "node", $self->resource_id())
}
sub Create($$$$$$)
{
my ($class, $slice, $user, $resource_uuid, $sliver_uuid, $rspec) = @_;
my ($class, $slice, $user, $resource_uuid, $rspec) = @_;
my $virtualization_type = $rspec->{'virtualization_type'};
my $experiment = $slice->GetExperiment();
......@@ -584,7 +625,7 @@ sub Create($$$$$$)
#
# An artifact of Emulab is that for shared/remote nodes, the physical
# node is already allocated, but not to the current experiment. An
# node is already allocated, but not to the current experiment.
#
if (! ($node->sharing_mode() ||
($node->isremotenode() && $node->isvirtnode()))) {
......@@ -599,10 +640,13 @@ sub Create($$$$$$)
return undef;
}
}
my $hrn = "${PGENIDOMAIN}." . $node->node_id();
my $nickname = $rspec->{'virtual_id'};
$sliver_uuid = $node->uuid();
my $hrn;
my $sshdport;
my $hostname;
my $resource_id;
my $sliver_uuid;
#
# The resource UUID refers to the physical node, but the virtualization
# type might require a vnode.
......@@ -615,9 +659,28 @@ sub Create($$$$$$)
}
$hrn = "${PGENIDOMAIN}." . $vnode->node_id();
$sliver_uuid = $vnode->uuid();
$resource_id = $vnode->node_id();
$hostname = $vnode->node_id() . ".${OURDOMAIN}";
$sshdport = $vnode->sshdport();