From 89c96adbacfd0bc76af73249a03aeaf17028742a Mon Sep 17 00:00:00 2001 From: "Leigh B. Stoller" Date: Fri, 30 May 2008 23:25:47 +0000 Subject: [PATCH] Checkpoint --- protogeni/lib/GNUmakefile.in | 2 +- protogeni/lib/GeniAggregate.pm.in | 340 ++++++++++++++++++++++++++++ protogeni/lib/GeniCH.pm.in | 14 +- protogeni/lib/GeniCHClient.pm.in | 19 +- protogeni/lib/GeniCM.pm.in | 155 +++++++++++-- protogeni/lib/GeniCertificate.pm.in | 5 +- protogeni/lib/GeniComponent.pm.in | 37 ++- protogeni/lib/GeniCredential.pm.in | 34 ++- protogeni/lib/GeniSlice.pm.in | 31 +++ protogeni/lib/GeniSliver.pm.in | 136 ++++++++--- protogeni/lib/GeniTicket.pm.in | 2 +- protogeni/lib/GeniUser.pm.in | 106 ++++++++- protogeni/lib/test.pl.in | 11 +- protogeni/xmlrpc/Genixmlrpc.pm.in | 2 +- protogeni/xmlrpc/protogeni-cm.pl.in | 1 + 15 files changed, 799 insertions(+), 96 deletions(-) create mode 100644 protogeni/lib/GeniAggregate.pm.in diff --git a/protogeni/lib/GNUmakefile.in b/protogeni/lib/GNUmakefile.in index 549393fd4..3f7d6162e 100644 --- a/protogeni/lib/GNUmakefile.in +++ b/protogeni/lib/GNUmakefile.in @@ -15,7 +15,7 @@ LIB_SCRIPTS = GeniDB.pm GeniUser.pm GeniSAClient.pm \ GeniSlice.pm GeniSA.pm GeniCM.pm GeniCMClient.pm \ test.pl GeniTicket.pm GeniSliver.pm GeniCredential.pm \ GeniComponent.pm GeniCH.pm GeniCHClient.pm \ - GeniAuthority.pm GeniCertificate.pm + GeniAuthority.pm GeniCertificate.pm GeniAggregate.pm # # Force dependencies on the scripts so that they will be rerun through diff --git a/protogeni/lib/GeniAggregate.pm.in b/protogeni/lib/GeniAggregate.pm.in new file mode 100644 index 000000000..1b94d9617 --- /dev/null +++ b/protogeni/lib/GeniAggregate.pm.in @@ -0,0 +1,340 @@ +#!/usr/bin/perl -wT +# +# EMULAB-COPYRIGHT +# Copyright (c) 2008 University of Utah and the Flux Group. +# All rights reserved. +# +package GeniAggregate; + +# +# Some simple ticket stuff. +# +use strict; +use Exporter; +use vars qw(@ISA @EXPORT); + +@ISA = "Exporter"; +@EXPORT = qw ( ); + +# Must come after package declaration! +use lib '@prefix@/lib'; +use GeniDB; +use GeniCredential; +use GeniCertificate; +use GeniSliver; +use libdb qw(TBGetUniqueIndex); +use English; +use overload ('""' => 'Stringify'); +use XML::Simple; + +# Configure variables +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAPPROVAL = "@TBAPPROVALEMAIL@"; +my $TBAUDIT = "@TBAUDITEMAIL@"; +my $BOSSNODE = "@BOSSNODE@"; +my $OURDOMAIN = "@OURDOMAIN@"; +my $SIGNCRED = "$TB/sbin/signgenicred"; +my $VERIFYCRED = "$TB/sbin/verifygenicred"; + +# Cache of instances to avoid regenerating them. +my %aggregates = (); + +# +# Lookup by idx, or uuid. +# +sub Lookup($$) +{ + my ($class, $token) = @_; + my $query_result; + my $idx; + + if ($token =~ /^\d+$/) { + $idx = $token; + } + elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) { + $query_result = + DBQueryWarn("select idx from geni_aggregates ". + "where uuid='$token'"); + return undef + if (! $query_result || !$query_result->numrows); + + ($idx) = $query_result->fetchrow_array(); + } + else { + return undef; + } + # Look in cache first + return $aggregates{"$idx"} + if (exists($aggregates{"$idx"})); + + $query_result = + DBQueryWarn("select * from geni_aggregates where idx='$idx'"); + + return undef + if (!$query_result || !$query_result->numrows); + + my $self = {}; + $self->{'AGGREGATE'} = $query_result->fetchrow_hashref(); + $self->{'CREDENTIAL'} = undef; + bless($self, $class); + + # + # Grab the certificate, since we will probably want it. + # + my $uuid = $self->{'AGGREGATE'}->{'uuid'}; + my $certificate = GeniCertificate->Lookup($uuid); + if (!defined($certificate)) { + print STDERR "Could not find certificate for aggregate $idx ($uuid)\n"; + return undef; + } + $self->{'CERTIFICATE'} = $certificate; + + # Add to cache. + $aggregates{$self->{'AGGREGATE'}->{'idx'}} = $self; + + return $self; +} + +# +# Stringify for output. +# +sub Stringify($) +{ + my ($self) = @_; + + my $uuid = $self->uuid(); + my $idx = $self->idx(); + + return "[GeniAggregate: $uuid, IDX: $idx]"; +} + +# +# Create a Geni aggregate in the DB. This happens on the server side only +# for now. The client side does not actually know its an aggregate, at +# least not yet. +# +sub Create($$) +{ + my ($class, $ticket) = @_; + my @insert_data = (); + + # Every aggregate gets a new unique index. + my $idx = TBGetUniqueIndex('next_aggregate', 1); + + # Create a cert pair, which gives us a new uuid. + my $certificate = GeniCertificate->Create("aggregate"); + if (!defined($certificate)) { + print STDERR "Could not generate new certificate and UUID!\n"; + return undef; + } + my $uuid = $certificate->uuid(); + my $slice_uuid = $ticket->slice_uuid(); + my $owner_uuid = $ticket->owner_uuid(); + + # Now tack on other stuff we need. + push(@insert_data, "created=now()"); + push(@insert_data, "idx='$idx'"); + push(@insert_data, "uuid='$uuid'"); + push(@insert_data, "creator_uuid='$owner_uuid'"); + push(@insert_data, "slice_uuid='$slice_uuid'"); + + # Insert into DB. + if (!DBQueryWarn("insert into geni_aggregates set " . + join(",", @insert_data))) { + $certificate->Delete(); + return undef; + } + return GeniAggregate->Lookup($idx); +} +# accessors +sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'AGGREGATE'}->{$_[1]}); } +sub idx($) { return field($_[0], "idx"); } +sub uuid($) { return field($_[0], "uuid"); } +sub slice_uuid($) { return field($_[0], "slice_uuid"); } +sub creator_uuid($) { return field($_[0], "creator_uuid"); } +sub created($) { return field($_[0], "created"); } +sub credential_idx($) { return field($_[0], "credential_idx"); } +sub ticket_idx($) { return field($_[0], "ticket_idx"); } +sub cert($) { return $_[0]->{'CERTIFICATE'}->cert(); } +sub GetCertificate($) { return $_[0]->{'CERTIFICATE'}; } + +# +# List of slivers for this aggregate. +# +sub SliverList($$) +{ + my ($self, $pref) = @_; + my @result = (); + + return -1 + if (! (ref($self) && ref($pref))); + + my $idx = $self->idx(); + my $query_result = + DBQueryWarn("select idx from geni_slivers where aggregate_idx='$idx'"); + return -1 + if (!$query_result); + + while (my ($sliver_idx) = $query_result->fetchrow_array()) { + my $sliver = GeniSliver->Lookup($sliver_idx); + if (!defined($sliver)) { + print STDERR "Could not find sliver object for $sliver_idx\n"; + return -1; + } + push(@result, $sliver); + } + @$pref = @result; + return 0; + +} + +# +# Get the credential for the aggregate. +# +sub GetCredential($) +{ + my ($self) = @_; + + return undef + if (! ref($self)); + + return $self->{'CREDENTIAL'} if (defined($self->{'CREDENTIAL'})); + + if (!defined($self->credential_idx())) { + print STDERR "No credential associated with $self\n"; + return undef; + } + my $credential = GeniCredential->Lookup($self->credential_idx()); + if (!defined($credential)) { + print STDERR "Could not get credential object associated with $self\n"; + return undef; + } + $self->{'CREDENTIAL'} = $credential; + return $credential; +} + +# +# Create a signed credential for this aggregate, issued to the provided user. +# The credential will grant all permissions for now. +# +# Should we store these credentials in the DB, recording what we hand out? +# +sub NewCredential($$) +{ + my ($self, $owner) = @_; + + return undef + if (! (ref($self) && ref($owner))); + + my $credential = GeniCredential->Create($self, $owner); + if (!defined($credential)) { + print STDERR "Could not create credential for $self, $owner\n"; + return undef; + } + if ($credential->Sign($self->GetCertificate()) != 0) { + print STDERR "Could not sign credential for $self, $owner\n"; + return undef; + } + return $credential; +} + +# +# Start all the slivers in the aggregate. +# +sub StartUp($) +{ + my ($self) = @_; + + return -1 + if (! ref($self)); + + my @slivers = (); + if ($self->SliverList(\@slivers) != 0) { + print STDERR "Could not get sliver list for $self\n"; + return -1; + } + foreach my $sliver (@slivers) { + if ($sliver->StartUp() != 0) { + print STDERR "Could not start $sliver in $self\n"; + next; + } + } + return 0; +} + +# +# Unprovision all the slivers in the aggregate. +# +sub UnProvision($) +{ + my ($self) = @_; + + return -1 + if (! ref($self)); + + my @slivers = (); + if ($self->SliverList(\@slivers) != 0) { + print STDERR "Could not get sliver list for $self\n"; + return -1; + } + foreach my $sliver (@slivers) { + if ($sliver->UnProvision() != 0) { + print STDERR "Could not unprovision $sliver in $self\n"; + DBQueryWarn("update geni_slivers set status='broken' ". + "where idx=" . $sliver->idx()); + next; + } + } + return 0; +} + +# +# Destroy all the slivers in the aggregate, and then the aggregate if there +# is nothing in it. Leave it around if something goes wrong. +# +sub Delete($) +{ + my ($self) = @_; + my $broken = 0; + + return -1 + if (! ref($self)); + + my @slivers = (); + if ($self->SliverList(\@slivers) != 0) { + print STDERR "Could not get sliver list for $self\n"; + return -1; + } + foreach my $sliver (@slivers) { + if ($sliver->status() eq "broken") { + $broken++; + next; + } + if ($sliver->Delete() != 0) { + print STDERR "Could not delete $sliver from $self\n"; + DBQueryWarn("update geni_slivers set status='broken' ". + "where idx=" . $sliver->idx()); + $broken++; + next; + } + } + return -1 + if ($broken); + + my $idx = $self->idx(); + my $uuid = $self->uuid(); + + DBQueryWarn("delete from geni_credentials where this_uuid='$uuid'") + or return -1; + DBQueryWarn("delete from geni_certificates where uuid='$uuid'") + or return -1; + DBQueryWarn("delete from geni_aggregates where idx='$idx'") + or return -1; + + return 0; +} + +# _Always_ make sure that this 1 is at the end of the file... +1; + diff --git a/protogeni/lib/GeniCH.pm.in b/protogeni/lib/GeniCH.pm.in index 7aa55b0e0..2703834cb 100644 --- a/protogeni/lib/GeniCH.pm.in +++ b/protogeni/lib/GeniCH.pm.in @@ -65,6 +65,11 @@ sub LookupUser($) return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef, "No slice authority found for user"); } + # Grab ssh key. + my $sshkey; + if ($user->GetSSHKey(\$sshkey) != 0) { + print STDERR "Could not get ssh key for $user\n"; + } # Return a blob. my $blob = { "uid" => $user->uid(), @@ -78,6 +83,8 @@ sub LookupUser($) "cert" => $authority->cert(), "url" => $authority->url() } }; + $blob->{'sshkey'} = $sshkey + if (defined($sshkey)); return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob); } @@ -127,6 +134,7 @@ sub RegisterUser($) my $name = $argref->{'name'}; my $email = $argref->{'email'}; my $cert = $argref->{'cert'}; + my $sshkey= $argref->{'sshkey'}; if (! (defined($hrn) && defined($name) && defined($email) && defined($cert) && defined($uuid))) { @@ -156,6 +164,10 @@ sub RegisterUser($) return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "cert: Invalid characters"); } + if (defined($sshkey) && ! ($sshkey =~ /^[\012\015\040-\176]*$/)) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "sshkey: Invalid characters"); + } # # The SA UUID comes from the SSL environment (certificate). Verify it @@ -209,7 +221,7 @@ sub RegisterUser($) "uid: ". TBFieldErrorString()); } my $newuser = GeniUser->Create($hrn, $uid, $uuid, - $name, $email, $cert, $sa_idx); + $name, $email, $cert, $sa_idx, $sshkey); if (!defined($newuser)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "$hrn/$email could not be registered"); diff --git a/protogeni/lib/GeniCHClient.pm.in b/protogeni/lib/GeniCHClient.pm.in index 682e004a9..ca474bc71 100644 --- a/protogeni/lib/GeniCHClient.pm.in +++ b/protogeni/lib/GeniCHClient.pm.in @@ -82,18 +82,21 @@ sub LookupSlice($$) # Register a local Emulab user at the Geni ClearingHouse (which in the # prototype is Utah Emulab). # -sub RegisterUser($$$$$) +sub RegisterUser($$$$$$) { - my ($hrn, $uuid, $name, $email, $cert) = @_; + my ($hrn, $uuid, $name, $email, $cert, $sshkey) = @_; + + my $args = { "hrn" => $hrn, + "uuid" => $uuid, + "name" => $name, + "email" => $email, + "cert" => $cert}; + $args->{"sshkey"} = $sshkey + if (defined($sshkey)); my $response = Genixmlrpc::CallMethodHTTP($GENICENTRALURL, undef, - "CH::RegisterUser", - { "hrn" => $hrn, - "uuid" => $uuid, - "name" => $name, - "email" => $email, - "cert" => $cert}); + "CH::RegisterUser", $args); return -1 if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS); diff --git a/protogeni/lib/GeniCM.pm.in b/protogeni/lib/GeniCM.pm.in index ee05a02b8..d0cd8f9d1 100644 --- a/protogeni/lib/GeniCM.pm.in +++ b/protogeni/lib/GeniCM.pm.in @@ -26,6 +26,7 @@ use GeniTicket; use GeniCredential; use GeniCertificate; use GeniSlice; +use GeniAggregate; use GeniSliver; use GeniUser; use libtestbed; @@ -267,6 +268,7 @@ sub CreateSliver($) my $owner_uuid = $ENV{'GENIUSER'}; my $ticket = $argref->{'ticket'}; my $impotent = $argref->{'impotent'}; + my $message = "Error creating sliver/aggregate"; $impotent = 0 if (!defined($impotent)); @@ -313,45 +315,92 @@ sub CreateSliver($) "No user record for $owner_uuid"); } - my $sliver = GeniSliver->Create($ticket); - if (!defined($sliver)) { + # + # Create an emulab nonlocal user for tmcd. + # + $owner->BindToSlice($slice) == 0 + or return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Error binding user to slice"); + + # + # We are actually an Aggregate, so return an aggregate of sliver, + # even if there is just one node (simpler). + # + my $aggregate = GeniAggregate->Create($ticket); + if (!defined($aggregate)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, - "Could not create GeniSliver object"); + "Could not create GeniAggregate object"); } # - # Provision the slice. Okay, we already allocated the node above, - # so this should just work, unless the node has been released cause - # it has been too long. + # Now for each resource (okay, node) in the ticket create a sliver and + # add it to the aggregate. # - if (!$impotent && $sliver->Provision() != 0) { - $sliver->Delete(); - return GeniResponse->Create(GENIRESPONSE_ERROR, undef, - "Could not provision sliver"); + my @slivers = (); + foreach my $resource_uuid (keys(%{$ticket->rspec()->{'node'}})) { + if (! ($resource_uuid =~ /^[-\w]*$/)) { + $message = "Improper resource_uuid in ticket: $resource_uuid"; + goto bad; + } + my $sliver = GeniSliver->Create($slice, $owner, $resource_uuid); + if (!defined($sliver)) { + $message = "Could not create GeniSliver object for $resource_uuid"; + goto bad; + } + push(@slivers, $sliver); } + + # + # Now do the provisioning (note that we actually allocated the node + # above when the ticket was granted). The add the sliver to the aggregate. + # + foreach my $sliver (@slivers) { + if (!$impotent && $sliver->Provision() != 0) { + $message = "Could not provision $sliver"; + goto bad; + } + if ($sliver->SetAggregate($aggregate) != 0) { + $message = "Could not aggregate for $sliver to $aggregate"; + goto bad; + } + } + # - # The API states we return a credential to control the sliver. + # The API states we return a credential to control the sliver/aggregate. # - my $credential = $sliver->NewCredential($owner); + my $credential = $aggregate->NewCredential($owner); if (!defined($credential)) { - $sliver->UnProvision(); - $sliver->Delete(); - return GeniResponse->Create(GENIRESPONSE_ERROR, undef, - "Could not create credential sliver"); + $message = "Could not create credential for $aggregate"; + goto bad; } return GeniResponse->Create(GENIRESPONSE_SUCCESS, $credential->asString()); + + bad: + foreach my $sliver (@slivers) { + $sliver->UnProvision() + if (! $impotent); + $sliver->Delete(); + } + $aggregate->Delete() + if (defined($aggregate)); + + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, $message); } # -# Destroy a sliver. +# Start a sliver (not sure what this means yet, so reboot for now). # -sub DestroySliver($) +sub StartSliver($) { my ($argref) = @_; my $owner_uuid = $ENV{'GENIUSER'}; my $sliver_cert = $argref->{'sliver'}; my $credential = $argref->{'credential'}; my $sliver_uuid; + my $impotent = $argref->{'impotent'}; + + $impotent = 0 + if (!defined($impotent)); if (!defined($sliver_cert) || !defined($credential)) { return GeniResponse->Create(GENIRESPONSE_BADARGS); @@ -360,28 +409,86 @@ sub DestroySliver($) return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "Could not get uuid from Certificate"); + $credential = GeniCredential->CreateFromSigned($credential); + if (!defined($credential)) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not create GeniCredential object"); + } my $sliver = GeniSliver->Lookup($sliver_uuid); if (!defined($sliver)) { - return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, - "No such sliver $sliver_uuid"); + # Might be an aggregate instead. + $sliver = GeniAggregate->Lookup($sliver_uuid); + if (!defined($sliver)) { + return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, + "No such sliver/aggregate $sliver_uuid"); + } } + + # The credential owner has to match what is in the ticket. + if (! ($owner_uuid eq $credential->owner_uuid() && + $sliver_uuid eq $credential->this_uuid())) { + return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef, + "Invalid credentials for operation"); + } + if (!$impotent) { + $sliver->StartUp() == 0 or + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not start sliver/aggregate"); + } + return GeniResponse->Create(GENIRESPONSE_SUCCESS); +} + +# +# Destroy a sliver/aggregate. +# +sub DestroySliver($) +{ + my ($argref) = @_; + my $owner_uuid = $ENV{'GENIUSER'}; + my $sliver_cert = $argref->{'sliver'}; + my $credential = $argref->{'credential'}; + my $sliver_uuid; + my $impotent = $argref->{'impotent'}; + + $impotent = 0 + if (!defined($impotent)); + + if (!defined($sliver_cert) || !defined($credential)) { + return GeniResponse->Create(GENIRESPONSE_BADARGS); + } + GeniCertificate->CertificateInfo($sliver_cert, \$sliver_uuid) == 0 or + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not get uuid from Certificate"); + $credential = GeniCredential->CreateFromSigned($credential); if (!defined($credential)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "Could not create GeniCredential object"); } + my $sliver = GeniSliver->Lookup($sliver_uuid); + if (!defined($sliver)) { + # Might be an aggregate instead. + $sliver = GeniAggregate->Lookup($sliver_uuid); + if (!defined($sliver)) { + return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, + "No such sliver/aggregate $sliver_uuid"); + } + } + # The credential owner has to match what is in the ticket. if (! ($owner_uuid eq $credential->owner_uuid() && $sliver_uuid eq $credential->this_uuid())) { return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef, "Invalid credentials for operation"); } - $sliver->UnProvision() == 0 or - return GeniResponse->Create(GENIRESPONSE_ERROR, undef, - "Could not unprovision sliver"); + if (!$impotent) { + $sliver->UnProvision() == 0 or + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not unprovision sliver/aggregate"); + } $sliver->Delete() == 0 or return GeniResponse->Create(GENIRESPONSE_ERROR, undef, - "Could not delete sliver"); + "Could not delete sliver/aggregate"); return GeniResponse->Create(GENIRESPONSE_SUCCESS); } diff --git a/protogeni/lib/GeniCertificate.pm.in b/protogeni/lib/GeniCertificate.pm.in index b6e4926e5..37ddd65d0 100644 --- a/protogeni/lib/GeniCertificate.pm.in +++ b/protogeni/lib/GeniCertificate.pm.in @@ -209,9 +209,8 @@ sub WriteToFile($) { my ($self) = @_; - # Deleted when object is released - my $tempfile = new File::Temp(UNLINK => 1); - my $filename = $tempfile->filename; + # We want this file to be passed back. + my ($tempfile, $filename) = tempfile(UNLINK => 1); print $tempfile "-----BEGIN CERTIFICATE-----\n"; print $tempfile $self->cert(); print $tempfile "-----END CERTIFICATE-----\n"; diff --git a/protogeni/lib/GeniComponent.pm.in b/protogeni/lib/GeniComponent.pm.in index 13cfa66c9..7de022282 100644 --- a/protogeni/lib/GeniComponent.pm.in +++ b/protogeni/lib/GeniComponent.pm.in @@ -41,7 +41,7 @@ my $SIGNCRED = "$TB/sbin/signgenicred"; my $VERIFYCRED = "$TB/sbin/verifygenicred"; # Do not allocated nodes. -my $impotent = 1; +my $impotent = 0; # Cache of instances to avoid regenerating them. my %components = (); @@ -312,13 +312,13 @@ sub CreateSliver($$$$) # # We get back a signed credential, which has the sliver uuid inside. # - my $credential = GeniCredential->CreateFromSigned($response->value()); + my $credential = GeniCredential->CreateFromSigned($response->value(), 1); if (!defined($credential)) { print STDERR "Could not create local credential object.\n"; return undef; } - my $sliver = GeniSliver->Create($ticket, $credential); + my $sliver = GeniSliver->Create($slice, $user, undef, $credential, $self); if (!defined($sliver)) { print STDERR "Could not create local sliver object.\n"; return undef; @@ -345,6 +345,7 @@ sub DestroySliver($$$) Genixmlrpc::CallMethodHTTP($self->url(), $user, "CM::DestroySliver", { "sliver" => $sliver->cert(), + "impotent" => $impotent, "credential" => $credential->asString() }); @@ -355,6 +356,36 @@ sub DestroySliver($$$) return 0; } +# +# Start a sliver. +# +sub StartSliver($$$) +{ + my ($self, $sliver, $user) = @_; + + # Must be a real reference. + return -1 + if (! ref($self)); + + my $credential = $sliver->GetCredential(); + return -1 + if (!defined($credential)); + + my $response = + Genixmlrpc::CallMethodHTTP($self->url(), $user, + "CM::StartSliver", + { "sliver" => $sliver->cert(), + "impotent" => $impotent, + "credential" => + $credential->asString() }); + + if ($response->code() != GENIRESPONSE_SUCCESS) { + print STDERR "Could not start sliver $sliver\n"; + return -1; + } + return 0; +} + # _Always_ make sure that this 1 is at the end of the file... 1; diff --git a/protogeni/lib/GeniCredential.pm.in b/protogeni/lib/GeniCredential.pm.in index 972fca7b8..d3cfbf894 100644 --- a/protogeni/lib/GeniCredential.pm.in +++ b/protogeni/lib/GeniCredential.pm.in @@ -105,22 +105,32 @@ sub AddCapability($$$) # # Create a credential object from a signed credential string. # -sub CreateFromSigned($$) +sub CreateFromSigned($$;$) { - my ($class, $string) = @_; + my ($class, $string, $nosig) = @_; + + # + # This flag is used to avoid verifying the signature since I do not + # really care if the component gives me a bad ticket; I am not using + # it locally, just passing it back to the component at some point. + # + $nosig = 0 + if (!defined($nosig)); # First verify the credential - my ($fh, $filename) = tempfile(UNLINK => 0); - return undef - if (!defined($fh)); - print $fh $string; - close($fh); - system("$VERIFYCRED $filename"); - if ($?) { - print STDERR "Credential in $filename did not verify\n"; - return undef; + if (! $nosig) { + my ($fh, $filename) = tempfile(UNLINK => 0); + return undef + if (!defined($fh)); + print $fh $string; + close($fh); + system("$VERIFYCRED $filename"); + if ($?) { + print STDERR "Credential in $filename did not verify\n"; + return undef; + } + unlink($filename); } - unlink($filename); # Use XML::Simple to convert to something we can mess with. my $parser = XML::LibXML->new; diff --git a/protogeni/lib/GeniSlice.pm.in b/protogeni/lib/GeniSlice.pm.in index e4e525106..3c51044e6 100644 --- a/protogeni/lib/GeniSlice.pm.in +++ b/protogeni/lib/GeniSlice.pm.in @@ -300,5 +300,36 @@ sub Delete($) return 0; } +# +# Bind user to slice. +# +sub BindUserToSlice($$) +{ + my ($self, $user) = @_; + + return -1 + if (! (ref($self) && ref($user))); + + my $experiment = Experiment->Lookup($self->uuid()); + if (!defined($experiment)) { + print STDERR "Could not locate experiment object for $self\n"; + return -1; + } + return $user +} + +# +# Return the emulab experiment for this slice. +# +sub GetExperiment($) +{ + my ($self) = @_; + + return undef + if (!ref($self)); + + return Experiment->Lookup($self->uuid()); +} + # _Always_ make sure that this 1 is at the end of the file... 1; diff --git a/protogeni/lib/GeniSliver.pm.in b/protogeni/lib/GeniSliver.pm.in index 56116a5c2..98de1f55f 100644 --- a/protogeni/lib/GeniSliver.pm.in +++ b/protogeni/lib/GeniSliver.pm.in @@ -20,6 +20,7 @@ use GeniDB; use GeniComponent; use GeniCredential; use GeniCertificate; +use GeniAggregate; # Hate to import all this crap; need a utility library. use libdb qw(TBGetUniqueIndex); use libtestbed; @@ -41,6 +42,7 @@ my $SIGNCRED = "$TB/sbin/signgenicred"; my $AVAIL = "$TB/sbin/avail"; my $NALLOC = "$TB/bin/nalloc"; my $NFREE = "$TB/bin/nfree"; +my $NODEREBOOT = "$TB/bin/node_reboot"; # Cache of instances to avoid regenerating them. my %slivers = (); @@ -82,9 +84,9 @@ sub Lookup($$) my $self = {}; $self->{'SLIVER'} = $query_result->fetchrow_hashref(); - $self->{'TICKET'} = undef; # Server side. $self->{'COMPONENT'} = undef; # Client side. $self->{'CREDENTIAL'} = undef; # Client side. + $self->{'AGGREGATE'} = undef; # server side. # # Grab the certificate, since we will probably want it. @@ -119,12 +121,11 @@ sub Stringify($) # # Create a sliver record in the DB. On the client side we save the credential -# that allows control of it, for later operations. We also save the component -# pointer, but not sure that is correct. +# that allows control of it, for later operations. # -sub Create($$;$) +sub Create($$$;$$) { - my ($class, $ticket, $credential) = @_; + my ($class, $slice, $user, $resource_uuid, $credential, $component) = @_; my @insert_data = (); my $uuid; my $certificate; @@ -153,15 +154,15 @@ sub Create($$;$) } $uuid = $certificate->uuid(); } - my $slice_uuid = $ticket->slice_uuid(); - my $owner_uuid = $ticket->owner_uuid(); - my $node_id = $ticket->rspec()->{'node_id'}; + my $slice_uuid = $slice->uuid(); + my $owner_uuid = $user->uuid(); # Now tack on other stuff we need. push(@insert_data, "created=now()"); push(@insert_data, "idx='$idx'"); push(@insert_data, "uuid='$uuid'"); - push(@insert_data, "node_id='$node_id'"); + push(@insert_data, "resource_uuid='$resource_uuid'") + if (defined($resource_uuid)); push(@insert_data, "creator_uuid='$owner_uuid'"); push(@insert_data, "slice_uuid='$slice_uuid'"); @@ -169,8 +170,8 @@ sub Create($$;$) push(@insert_data, "credential_idx=" . $credential->idx()) if (defined($credential)); # Only on the client side. - push(@insert_data, "component_idx=" . $ticket->component()->idx()) - if (defined($ticket->component())); + push(@insert_data, "component_idx=" . $component->idx()) + if (defined($component)); # Insert into DB. if (!DBQueryWarn("insert into geni_slivers set " . @@ -185,8 +186,9 @@ sub Create($$;$) $sliver->{'CREDENTIAL'} = $credential if (defined($credential)); - $sliver->{'COMPONENT'} = $ticket->component() - if (defined($ticket->component())); + $sliver->{'COMPONENT'} = $component + if (defined($component)); + $sliver->{'AGGREGATE'} = undef; return $sliver; } @@ -198,9 +200,11 @@ sub slice_uuid($) { return field($_[0], "slice_uuid"); } sub creator_uuid($) { return field($_[0], "creator_uuid"); } sub created($) { return field($_[0], "created"); } sub credential_idx($) { return field($_[0], "credential_idx"); } -sub node_id($) { return field($_[0], "node_id"); } +sub resource_uuid($) { return field($_[0], "resource_uuid"); } sub ticket_idx($) { return field($_[0], "ticket_idx"); } sub component_idx($) { return field($_[0], "component_idx"); } +sub aggregate_idx($) { return field($_[0], "aggregate_idx"); } +sub status($) { return field($_[0], "status"); } sub cert($) { return $_[0]->{'CERTIFICATE'}->cert(); } sub GetCertificate($) { return $_[0]->{'CERTIFICATE'}; } @@ -227,6 +231,52 @@ sub Delete($) return 0; } +# +# Set the aggregate for a sliver. +# +sub SetAggregate($$) +{ + my ($self, $aggregate) = @_; + + return -1 + if (! (ref($self) && ref($aggregate))); + + my $idx = $self->idx(); + my $agg_idx = $aggregate->idx(); + + return -1 + if (!DBQueryWarn("update geni_slivers set ". + " aggregate_idx='$agg_idx' ". + "where idx='$idx'")); + + $self->{'SLIVER'}->{'aggregate_idx'} = $agg_idx; + $self->{'AGGREGATE'} = $aggregate; + return 0; +} + +# +# Get the aggregate for a sliver. +# +sub GetAggregate($) +{ + my ($self) = @_; + + return undef + if (! ref($self)); + + return $self->{'AGGREGATE'} if (defined($self->{'AGGREGATE'})); + return undef + if (!defined($self->aggregate_idx())); + + my $aggregate = GeniAggregate->Lookup($self->aggregate_idx()); + if (!defined($aggregate)) { + print STDERR "Could not get aggregate object associated with $self\n"; + return undef; + } + $self->{'AGGREGATE'} = $aggregate; + return $aggregate; +} + # # Get the experiment for the slice this sliver belongs to. # @@ -302,7 +352,7 @@ sub Destroy($$) my $component = $self->GetComponent(); return -1 - if (defined($component)); + if (!defined($component)); return -1 if ($component->DestroySliver($self, $user) != 0); @@ -314,6 +364,26 @@ sub Destroy($$) return 0; } +# +# Client side method to contact the sliver component and start it. +# +sub Start($$) +{ + my ($self, $user) = @_; + + return -1 + if (! ref($self)); + + my $component = $self->GetComponent(); + return -1 + if (!defined($component)); + + return -1 + if ($component->StartSliver($self, $user) != 0); + + return 0; +} + # # Provision a slice. We actually did this when the ticket was requested. # @@ -334,12 +404,12 @@ sub Provision($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $node_id = $self->node_id(); + my $resource_uuid = $self->resource_uuid(); return 0 - if (!defined($node_id)); - my $node = Node->Lookup($node_id); + if (!defined($resource_uuid)); + my $node = Node->Lookup($resource_uuid); if (!defined($node)) { - print STDERR "Could not map node $node_id to its object\n"; + print STDERR "Could not map node $resource_uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -352,6 +422,10 @@ sub Provision($) print STDERR "Could not add virtnode entry for $node to $self\n"; return -1; } + # Set sliver_idx in the reservation so that Emulab knows. + if ($node->ModifyReservation({"genisliver_idx" => $self->idx()}) != 0){ + return -1; + } } else { print STDERR "$node is reserved to another, not $self\n"; @@ -401,12 +475,12 @@ sub UnProvision($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $node_id = $self->node_id(); + my $resource_uuid = $self->resource_uuid(); return 0 - if (!defined($node_id)); - my $node = Node->Lookup($node_id); + if (!defined($resource_uuid)); + my $node = Node->Lookup($resource_uuid); if (!defined($node)) { - print STDERR "Could not map node $node_id to its object\n"; + print STDERR "Could not map node $resource_uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -415,6 +489,7 @@ sub UnProvision($) return 0; } if ($reservation->SameExperiment($experiment)) { + my $node_id = $node->node_id(); my $pid = $experiment->pid(); my $eid = $experiment->eid(); @@ -438,9 +513,9 @@ sub UnProvision($) } # -# Start a slice, which roughly translates to reboot the node. +# Start a sliver, which roughly translates to reboot the node. # -sub Start($) +sub StartUp($) { my ($self) = @_; @@ -452,12 +527,12 @@ sub Start($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $node_id = $self->node_id(); + my $resource_uuid = $self->resource_uuid(); return 0 - if (!defined($node_id)); - my $node = Node->Lookup($node_id); + if (!defined($resource_uuid)); + my $node = Node->Lookup($resource_uuid); if (!defined($node)) { - print STDERR "Could not map node $node_id to its object\n"; + print STDERR "Could not map node $resource_uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -466,10 +541,11 @@ sub Start($) return -1; } if ($reservation->SameExperiment($experiment)) { + my $node_id = $node->node_id(); # # Reboot and wait? # - #system("$NODEREBOOT $node_id"); + system("$NODEREBOOT $node_id"); } else { print STDERR "$node is reserved to another, not $self\n"; diff --git a/protogeni/lib/GeniTicket.pm.in b/protogeni/lib/GeniTicket.pm.in index 426d767f7..e43d02c60 100644 --- a/protogeni/lib/GeniTicket.pm.in +++ b/protogeni/lib/GeniTicket.pm.in @@ -113,7 +113,7 @@ sub CreateFromSignedTicket($$;$$) my ($rspec_node) = $doc->getElementsByTagName("rspec"); return undef if (!defined($rspec_node)); - my $rspec = XMLin($rspec_node->toString(), ForceArray => 0); + my $rspec = XMLin($rspec_node->toString(), ForceArray => ["node"]); # Dig out the slice uuid. Locally, I am not sure if we bother to # keep slices in the DB (they are in the DB at geni central). diff --git a/protogeni/lib/GeniUser.pm.in b/protogeni/lib/GeniUser.pm.in index 263676874..9ac5bd189 100644 --- a/protogeni/lib/GeniUser.pm.in +++ b/protogeni/lib/GeniUser.pm.in @@ -157,9 +157,10 @@ sub CheckExisting($$$) # Class function to create new Geni user and return object. Note that this # is used by the ClearingHouse *and* the CM/SA to record users. # -sub Create($$$$$$$$) +sub Create($$$$$$$$$;$) { - my ($class, $hrn, $uid, $uuid, $name, $email, $cert, $sa_idx) = @_; + my ($class, $hrn, $uid, $uuid, $name, $email, + $cert, $sa_idx, $sshkey) = @_; my @insert_data = (); # Every user gets a new unique index. @@ -190,9 +191,21 @@ sub Create($$$$$$$$) return undef; } + # Insert the sshkey if we got one. + if (defined($sshkey)) { + my $safe_sshkey = DBQuoteSpecial($sshkey); + + DBQueryWarn("insert into geni_sshkeys set ". + " uuid=$safe_uuid, created=now(), ". + " sshkey=$safe_sshkey") or return undef; + } + # Insert into DB. - DBQueryWarn("insert into geni_users set " . join(",", @insert_data)) - or return undef; + if (! DBQueryWarn("insert into geni_users set " . + join(",", @insert_data))) { + DBQueryWarn("delete from geni_sshkeys where uuid=$safe_uuid"); + return undef; + } return GeniUser->Lookup($idx); } @@ -236,7 +249,10 @@ sub CreateFromRegistry($$) $blob->{'name'}, $blob->{'email'}, $blob->{'cert'}, - $authority->idx()); + $authority->idx(), + (exists($blob->{'sshkey'}) ? + $blob->{'sshkey'} : undef)); + } # @@ -257,6 +273,79 @@ sub Delete($) return 0; } +# +# Get the sshkey for a user. +# +sub GetSSHKey($$) +{ + my ($self, $pref) = @_; + + return -1 + if (! (ref($self) && ref($pref))); + + $$pref = undef; + + my $uuid = $self->uuid(); + my $query_result = + DBQueryWarn("select sshkey from geni_sshkeys where uuid='$uuid'"); + return -1 + if (!$query_result); + return 0 + if (!$query_result->numrows); + + my ($sshkey) = $query_result->fetchrow_array(); + $$pref = $sshkey; + return 0; +} + +# +# Create a nonlocal user. +# +sub CreateNonLocal($) +{ + my ($self) = @_; + + return undef + if (! ref($self)); + + my $sshkey; + $self->GetSSHKey(\$sshkey); + + return User::NonLocal->Create($self->idx(), + $self->uid(), + $self->uuid(), + $self->name(), + $self->email(), + $sshkey); +} + +# +# Bind user to slice, which is another way of saying bind the nonlocal +# user to the experiment (for tmcd). +# +sub BindToSlice($$) +{ + my ($self, $slice) = @_; + + return -1 + if (! (ref($self) && ref($slice))); + + my $emulab_user = User::NonLocal->Lookup($self->uuid()); + if (!defined($emulab_user)) { + $emulab_user = $self->CreateNonLocal(); + if (!defined($emulab_user)) { + print STDERR "Could not create nonlocal user for $self\n"; + return -1; + } + } + my $experiment = $slice->GetExperiment(); + if (!defined($experiment)) { + print STDERR "Could not locate experiment object for $slice\n"; + return -1; + } + return $emulab_user->BindToExperiment($experiment); +} + # # Archive user. # @@ -312,6 +401,7 @@ sub created($) { return $_[0]->{'USER'}->created(); } sub name($) { return $_[0]->{'USER'}->name(); } sub email($) { return $_[0]->{'USER'}->email(); } sub sa_idx($) { return 0; } +sub GetSSHKey($$) { return $_[0]->{'USER'}->GetSSHKey($_[1]); } # Need to construct this since not in User structure. sub hrn($) { return $OURDOMAIN . "." . $_[0]->uid(); } @@ -320,6 +410,7 @@ sub hrn($) { return $OURDOMAIN . "." . $_[0]->uid(); } sub cert($) { return $_[0]->{'CERT'}->cert(); } sub GetCertificate($) { return $_[0]->{'CERT'}; } + # # Register this local user at the ClearingHouse. # @@ -330,9 +421,12 @@ sub Register($) # Register this user at the ClearingHouse, if not already registered. if (GeniCHClient::LookupUser($self->uuid(), \$blob) != 0) { + my $sshkey; + $self->GetSSHKey(\$sshkey); + return GeniCHClient::RegisterUser($self->hrn(), $self->uuid(), $self->name(), $self->email(), - $self->cert()); + $self->cert(), $sshkey); } return 0; } diff --git a/protogeni/lib/test.pl.in b/protogeni/lib/test.pl.in index cba06bf89..d4192f71f 100644 --- a/protogeni/lib/test.pl.in +++ b/protogeni/lib/test.pl.in @@ -98,17 +98,16 @@ if (!defined($ticket)) { } $sliver = $components[0]->CreateSliver($slice, $ticket, $this_user); -if (!defined($ticket)) { +if (!defined($sliver)) { die("Could not create sliver for $slice\n"); } -print Dumper($sliver); -exit(0); - -# Wait for input before proceeding. -$_ = ; if (defined($sliver)) { print Dumper($sliver); + # Wait for input before proceeding. + $_ = ; + $sliver->Start($this_user); + $_ = ; $sliver->Destroy($this_user); } diff --git a/protogeni/xmlrpc/Genixmlrpc.pm.in b/protogeni/xmlrpc/Genixmlrpc.pm.in index dc5e38ec5..992795b31 100644 --- a/protogeni/xmlrpc/Genixmlrpc.pm.in +++ b/protogeni/xmlrpc/Genixmlrpc.pm.in @@ -32,7 +32,7 @@ my $TBOPS = "@TBOPSEMAIL@"; my $BOSSNODE = "@BOSSNODE@"; my $EMULAB_PEMFILE = "@prefix@/etc/genisa.pem"; -my $debug = 1; +my $debug = 0; ## # The package version number diff --git a/protogeni/xmlrpc/protogeni-cm.pl.in b/protogeni/xmlrpc/protogeni-cm.pl.in index dc855e41a..115c4fdc1 100644 --- a/protogeni/xmlrpc/protogeni-cm.pl.in +++ b/protogeni/xmlrpc/protogeni-cm.pl.in @@ -89,6 +89,7 @@ my $responder = Frontier::Responder->new( "methods" => { "CM::DiscoverResources" => \&GeniCM::DiscoverResources, "CM::GetTicket" => \&GeniCM::GetTicket, "CM::CreateSliver" => \&GeniCM::CreateSliver, + "CM::StartSliver" => \&GeniCM::StartSliver, "CM::DestroySliver" => \&GeniCM::DestroySliver, }, ); -- GitLab