From 86cd9965c1020905b1602d04a24a5f4d17c8703a Mon Sep 17 00:00:00 2001 From: "Leigh B. Stoller" Date: Sat, 13 Sep 2008 14:25:37 +0000 Subject: [PATCH] Checkpoint working swapin of experiments using multiple federated nodes with vlans between them. Now I need to figure out how to swap the experiment out --- protogeni/lib/GeniAggregate.pm.in | 59 +++-- protogeni/lib/GeniCM.pm.in | 100 ++++++-- protogeni/lib/GeniComponent.pm.in | 82 ++++++- protogeni/lib/GeniCredential.pm.in | 22 +- protogeni/lib/GeniEmulab.pm.in | 341 ++++++++++++++++++++++------ protogeni/lib/GeniSliver.pm.in | 143 ++++++++---- protogeni/lib/GeniTicket.pm.in | 34 ++- protogeni/xmlrpc/GNUmakefile.in | 2 +- protogeni/xmlrpc/client.py | 5 +- protogeni/xmlrpc/protogeni-cm.pl.in | 1 + 10 files changed, 615 insertions(+), 174 deletions(-) diff --git a/protogeni/lib/GeniAggregate.pm.in b/protogeni/lib/GeniAggregate.pm.in index 122498edc..e59ddd169 100644 --- a/protogeni/lib/GeniAggregate.pm.in +++ b/protogeni/lib/GeniAggregate.pm.in @@ -220,6 +220,16 @@ sub Delete($) return 0; } +# +# Cons up an hrn. +# +sub hrn($) +{ + my ($self) = @_; + + return $OURDOMAIN . ".aggregates." . $self->idx(); +} + # # Look up a list of aggregates for a locally instantiated slice. # Used by the CM. @@ -258,9 +268,11 @@ sub SliverList($$) return -1 if (! (ref($self) && ref($pref))); - my $idx = $self->idx(); + my $idx = $self->idx(); + my $uuid = $self->uuid(); my $query_result = - DBQueryWarn("select idx from geni_slivers where aggregate_idx='$idx'"); + DBQueryWarn("select idx from geni_slivers ". + "where aggregate_uuid='$uuid'"); return -1 if (!$query_result); @@ -530,31 +542,23 @@ sub Provision($;$) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $sliver0 = $slivers[0]; - my $sliver1 = $slivers[1]; - my $interface0 = Interface->LookupByUUID($sliver0->resource_uuid()); - my $interface1 = Interface->LookupByUUID($sliver1->resource_uuid()); - if (! defined($interface0)) { - print STDERR "Could not map $sliver0 to its object\n"; - return -1; - } - if (! defined($interface1)) { - print STDERR "Could not map $sliver1 to its object\n"; - return -1; - } my $vlan = VLan->Create($experiment, $self->uuid()); if (!defined($vlan)) { print STDERR "Could not create vlan for $self\n"; - return -1; - } - if (! $vlan->AddMember($interface0->node_id(), $interface0->iface())) { - print STDERR "$self: Could not add $interface0 to $vlan\n"; goto bad; } - if (! $vlan->AddMember($interface1->node_id(), $interface1->iface())) { - print STDERR "$self: Could not add $interface1 to $vlan\n"; - goto bad; + + foreach my $sliver (@slivers) { + my $interface = Interface->LookupByUUID($sliver->resource_uuid()); + if (! defined($interface)) { + print STDERR "Could not map $sliver to its interface object\n"; + goto bad; + } + if (! $vlan->AddMember($interface->node_id(), $interface->iface())) { + print STDERR "$self: Could not add $interface to $vlan\n"; + goto bad; + } } if ($vlan->Instantiate() != 0) { print STDERR "$self: Could not instantiate $vlan on switches\n"; @@ -591,18 +595,7 @@ sub UnProvision($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $sliver0 = $slivers[0]; - my $sliver1 = $slivers[1]; - my $interface0 = Interface->LookupByUUID($sliver0->resource_uuid()); - my $interface1 = Interface->LookupByUUID($sliver1->resource_uuid()); - if (! defined($interface0)) { - print STDERR "Could not map $sliver0 to its object\n"; - return -1; - } - if (! defined($interface1)) { - print STDERR "Could not map $sliver1 to its object\n"; - return -1; - } + my $vlan = VLan->Lookup($experiment, $self->uuid()); if (! defined($vlan)) { print STDERR "Could not map self to its vlan object\n"; diff --git a/protogeni/lib/GeniCM.pm.in b/protogeni/lib/GeniCM.pm.in index 691977e5a..e2ed65d0a 100644 --- a/protogeni/lib/GeniCM.pm.in +++ b/protogeni/lib/GeniCM.pm.in @@ -121,6 +121,7 @@ sub Resolve($) # Return a blob. my $blob = { "hrn" => "${OURDOMAIN}." . $node->node_id(), "uuid" => $node->uuid(), + "role" => $node->role(), }; # @@ -134,15 +135,23 @@ sub Resolve($) my @iblobs = (); foreach my $interface (@interfaces) { + next + if (!defined($interface->switch_id())); + my $iblob = { "uuid" => $interface->uuid(), "iface" => $interface->iface(), "type" => $interface->type(), "card" => $interface->card(), "port" => $interface->port(), "role" => $interface->role(), - "IP" => $interface->IP(), - "mask" => $interface->mask(), + "IP" => $interface->IP() || "", + "mask" => $interface->mask() || "", "MAC" => $interface->mac(), + "switch_id" => "${OURDOMAIN}." . + $interface->switch_id(), + "switch_card" => $interface->switch_card(), + "switch_port" => $interface->switch_port(), + "wire_type" => $interface->wire_type(), }; push(@iblobs, $iblob); @@ -505,6 +514,8 @@ sub RedeemTicket($) } } + print Dumper($ticket->rspec()); + # # Now for each resource (okay, node) in the ticket create a sliver and # add it to the aggregate. @@ -550,14 +561,11 @@ sub RedeemTicket($) my $linkendpoints = $ticket->rspec()->{'link'}->{$linkname}->{'LinkEndPoints'}; - my $src_interface_spec = $linkendpoints->{'source_interface'}; - my $dst_interface_spec = $linkendpoints->{'destination_interface'}; - - my @interfaces = ($src_interface_spec, $dst_interface_spec); - foreach my $iface (@interfaces) { - my $node_uuid = $iface->{'node_uuid'}; - my $iface = $iface->{'iface_name'}; - my $nodesliver= $slivers{$node_uuid}; + foreach my $ifacename (keys(%{ $linkendpoints })) { + my $iface = $linkendpoints->{$ifacename}; + my $node_uuid = $iface->{'node_uuid'}; + my $iface_name = $iface->{'iface_name'}; + my $nodesliver = $slivers{$node_uuid}; if (!defined($nodesliver)) { $message = "Link $linkname specifies a non-existent node"; goto bad; @@ -567,9 +575,9 @@ sub RedeemTicket($) $message = "Could not find node object for $node_uuid"; goto bad; } - my $interface = Interface->LookupByIface($nodeobject, $iface); + my $interface = Interface->LookupByIface($nodeobject, $iface_name); if (!defined($interface)) { - $message = "No such interface $iface on node $nodeobject"; + $message = "No such interface $iface_name on node $nodeobject"; goto bad; } my $sliver = GeniSliver::Interface->Create($slice, @@ -589,8 +597,9 @@ sub RedeemTicket($) } # - # Now do the provisioning (note that we actually allocated the node - # above when the ticket was granted). The add the sliver to the aggregate. + # Now do the provisioning (note that we actually allocated the + # node above when the ticket was granted). Then add the sliver to + # the aggregate. # foreach my $sliver (values(%slivers)) { if (!$impotent && $sliver->Provision($extraargs) != 0) { @@ -1060,3 +1069,66 @@ sub DeleteSlice($) } return GeniResponse->Create(GENIRESPONSE_SUCCESS); } + +# +# Split an aggregated sliver into its separate parts and return a list. +# +sub SplitSliver($) +{ + my ($argref) = @_; + my $cred = $argref->{'credential'}; + my $impotent = $argref->{'impotent'}; + + $impotent = 0 + if (!defined($impotent)); + + if (!defined($cred)) { + return GeniResponse->Create(GENIRESPONSE_BADARGS); + } + my $credential = GeniCredential->CreateFromSigned($cred); + if (!defined($credential)) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not create GeniCredential object"); + } + my $sliver_uuid = $credential->this_uuid(); + my $user_uuid = $credential->owner_uuid(); + + # + # Make sure the credential was issued to the caller. + # + if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "This is not your credential!"); + } + my $user = GeniUser->Lookup($user_uuid); + if (!defined($user)) { + $user = CreateUserFromRegistry($user_uuid); + if (!defined($user)) { + print STDERR "No user $user_uuid in the ClearingHouse\n"; + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "No user record for $user_uuid"); + } + } + + my $aggregate = GeniAggregate->Lookup($sliver_uuid); + if (!defined($aggregate)) { + return GeniResponse->Create(GENIRESPONSE_BADARGS, undef, + "No such aggregate $sliver_uuid"); + } + my @sliver_list = (); + if ($aggregate->SliverList(\@sliver_list) != 0) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not get slivers for $aggregate"); + } + my @credentials = (); + + foreach my $sliver (@sliver_list) { + my $credential = $sliver->NewCredential($user); + if (!defined($credential)) { + return GeniResponse->Create(GENIRESPONSE_ERROR, undef, + "Could not create credential for $sliver"); + } + push(@credentials, $credential->asString()); + } + return GeniResponse->Create(GENIRESPONSE_SUCCESS, \@credentials); +} diff --git a/protogeni/lib/GeniComponent.pm.in b/protogeni/lib/GeniComponent.pm.in index ba4305d8f..195cd289a 100644 --- a/protogeni/lib/GeniComponent.pm.in +++ b/protogeni/lib/GeniComponent.pm.in @@ -202,6 +202,19 @@ sub NewResource($$) return 0; } +# +# Compare two component refs. +# +sub SameComponent($$) +{ + my ($self, $other) = @_; + + return 0 + if (! (ref($self) && ref($other))); + + return $self->idx() == $other->idx(); +} + # # Refresh a class instance by reloading from the DB. # @@ -398,16 +411,6 @@ sub CreateSliver($$$$;$) return undef; } - # - # We need to store this credential (not the default) so we can - # operate on the sliver later. - # - if ($credential->Store() != 0) { - print STDER "Could not store $credential for new sliver\n"; - $credential->Delete(); - return undef; - } - my $sliver = GeniSliver::Client->Create($slice, $ticket->owner_uuid(), $ticket->rspec(), $credential, $self); @@ -475,6 +478,65 @@ sub StartSliver($$$) return 0; } +# +# Split a sliver. +# +sub SplitSliver($$$$) +{ + my ($self, $sliver, $context, $pref) = @_; + + # Must be a real reference. + return -1 + if (! ref($self)); + + my $credential = $sliver->GetCredential($context->user()); + return -1 + if (!defined($credential)); + + my $slice = $sliver->GetSlice(); + return -1 + if (!defined($slice)); + + my $response = + Genixmlrpc::CallMethod($self->url(), $context, + "SplitSliver", + { "credential" => $credential->asString() }); + + if ($response->code() != GENIRESPONSE_SUCCESS) { + print STDERR "Could not split sliver $sliver\n"; + return -1; + } + + # + # We get back signed credentials, which has the sliver uuid inside. + # + my @slivers = (); + + foreach my $credential (@{ $response->value() }) { + my $credential = GeniCredential->CreateFromSigned($credential, 1); + if (!defined($credential)) { + print STDERR "Could not create local credential object.\n"; + return -1; + } + + my $s = GeniSliver::Client->Create($slice, + $credential->owner_uuid(), + undef, + $credential, $self); + + if (!defined($s)) { + print STDERR "Could not create local sliver object.\n"; + return -1; + } + $s->SetAggregate($sliver); + # XXX Kludge for Emulab aggregates + $s->Sethrn($credential->hrn()); + push(@slivers, $s); + } + @$pref = @slivers; + 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 40685ff7c..a2c7a5b0f 100644 --- a/protogeni/lib/GeniCredential.pm.in +++ b/protogeni/lib/GeniCredential.pm.in @@ -115,6 +115,7 @@ sub Create($$$$) $self->{'target_uuid'} = $target->uuid(); $self->{'target_cert'} = $target->cert(); $self->{'owner_uuid'} = $owner->uuid(); + $self->{'hrn'} = $target->hrn(); $self->{'string'} = undef; $self->{'capabilities'} = undef; $self->{'idx'} = undef; # Only set when stored to DB. @@ -125,6 +126,7 @@ sub Create($$$$) # accessors sub field($$) { return ($_[0]->{$_[1]}); } sub idx($) { return field($_[0], "idx"); } +sub hrn($) { return field($_[0], "hrn"); } sub target($) { return field($_[0], "target"); } sub owner($) { return field($_[0], "owner"); } sub this_uuid($) { return field($_[0], "target_uuid"); } @@ -196,7 +198,14 @@ sub CreateFromSigned($$;$) # Use XML::Simple to convert to something we can mess with. my $parser = XML::LibXML->new; - my $doc = $parser->parse_string($string); + my $doc; + eval { + $doc = $parser->parse_string($string); + }; + if ($@) { + print STDERR "Failed to parse credential string: $@\n"; + return undef; + } # Dig out the capabilities my ($cap_node) = $doc->getElementsByTagName("capabilities"); @@ -219,6 +228,12 @@ sub CreateFromSigned($$;$) return undef; } + # Dig out the hrn. + my ($hrn_node) = $doc->getElementsByTagName("hrn"); + return undef + if (!defined($hrn_node)); + my $hrn = $hrn_node->to_literal(); + # Dig out the owner uuid. Locally, I am not sure if we bother to # keep users in the DB (they are in the DB at geni central). ($uuid_node) = $doc->getElementsByTagName("owner_uuid"); @@ -239,6 +254,7 @@ sub CreateFromSigned($$;$) $self->{'target_uuid'} = $this_uuid; $self->{'target_cert'} = $this_cert; $self->{'owner_uuid'} = $owner_uuid; + $self->{'hrn'} = $hrn; $self->{'string'} = $string; $self->{'target'} = undef; $self->{'owner'} = undef; @@ -288,6 +304,7 @@ sub Sign($$) # Every one gets a new unique index, which is used in the xml:id below. my $idx = TBGetUniqueIndex('next_ticket', 1); + my $hrn = $self->hrn(); # # Need the certificates for target and owner of the credential. @@ -313,7 +330,10 @@ sub Sign($$) " capability\n". " $idx\n". " $owner_cert\n". + " $target_cert\n". " $target_cert\n". + " $hrn\n". + " 2008-05-10T09:00:00\n". " $cap_xml\n". "\n"; diff --git a/protogeni/lib/GeniEmulab.pm.in b/protogeni/lib/GeniEmulab.pm.in index ca968445b..db54052c5 100644 --- a/protogeni/lib/GeniEmulab.pm.in +++ b/protogeni/lib/GeniEmulab.pm.in @@ -31,6 +31,7 @@ use libtestbed; use User; use Node; use Interface; +use Lan; use English; use Data::Dumper; use Experiment; @@ -180,36 +181,25 @@ sub AllocateSlivers($$$) } # - # Loop through each node and grab a ticket for it. The nodes table - # stores the uuid of the node as told to us in resource discovery. - # Use this to create a simple rspec. This will need to get fancier - # later. - # - # XXX We are still not using rspecs anywhere. + # Loop through nodes, combining nodes at the same component into a + # list for that component. # + # Bookkeeping for the rest of this function. + my %components = (); + my %component_nodes = (); + my %node_component = (); + my %pnode_component = (); + foreach my $node (@{ $nodelist }) { - my $sliver_idx; - return -1 - if ($node->GetGeniSliverInfo(\$sliver_idx) != 0); - next - if ($sliver_idx); - # # The node is a virtnode, but we want a ticket for the physnode. # - my $physnode = Node->Lookup($node->phys_nodeid()); + my $pnode = Node->Lookup($node->phys_nodeid()); return -1 - if (!defined($physnode)); - - my $node_uuid = $physnode->uuid(); - - my $rspec = - " " . - " " . - " " . - ""; + if (!defined($pnode)); + my $node_uuid = $pnode->uuid(); + # # XXX The component is stored in the geni_resources table. Not sure # how that will work out. @@ -219,17 +209,161 @@ sub AllocateSlivers($$$) print STDERR "Could not find CM for $node\n"; return -1; } + $component_nodes{$component->uuid()} = [] + if (!exists($component_nodes{$component->uuid()})); + push(@{ $component_nodes{$component->uuid()} }, $node); + $node_component{$node->node_id()} = $component; + $pnode_component{$pnode->node_id()} = $component; + $components{$component->uuid()} = $component; + } + + # + # Now find links and lans between nodes, which of course must be at + # the same components since we talking about real vlans, not tunnels. + # + my %component_lans = (); + my @lans; + + if (Lan->ExperimentLans($experiment, \@lans) != 0) { + print STDERR "Could not get lans for $experiment\n"; + return -1; + } + foreach my $lan (@lans) { + next + if ($lan->type() ne "geni-vlan"); + + my @members; + + if ($lan->MemberList(\@members) != 0) { + print STDERR "Could not get members for $lan\n"; + return -1; + } + + # + # Sanity check; all the nodes in the list must reside on the same + # component. + # + my $component; + foreach my $member (@members) { + my $node; + my $iface; + + if ($member->GetNodeIface(\$node, \$iface) != 0) { + print STDERR "Could not get node/iface for $member\n"; + return -1; + } + # + # + # + my $tmp = $pnode_component{$node->node_id()}; + + $component = $tmp + if (!defined($component)); + + if (!$component->SameComponent($tmp)) { + print STDERR "Component mismatch in members of $lan\n"; + return -1; + } + } + $component_lans{$component->uuid()} = [] + if (!exists($component_lans{$component->uuid()})); + push(@{ $component_lans{$component->uuid()} }, $lan); + } + + # + # Now build up rspecs for each component. + # + my %component_rspecs = (); + + foreach my $component_uuid (keys(%component_nodes)) { + my @nodelist = @{ $component_nodes{$component_uuid} }; + my $component = $components{$component_uuid}; + my $rspec = + " "; + + foreach my $node (@nodelist) { + # + # The node is a virtnode, but we want a ticket for the physnode. + # + my $physnode = Node->Lookup($node->phys_nodeid()); + return -1 + if (!defined($physnode)); + my $node_uuid = $physnode->uuid(); + my $node_id = $node->node_id(); + + # + # We need to pass in some extra stuff for Emulab federation + # (tmcd redirection). + # + $rspec .= " " . + " "; + } + if (exists($component_lans{$component_uuid})) { + my @lans = @{ $component_lans{$component_uuid} }; + + foreach my $lan (@lans) { + my $name = $lan->vname(); + my @members; + + if ($lan->MemberList(\@members) != 0) { + print STDERR "Could not get members for $lan\n"; + return -1; + } + $rspec .= " "; + + foreach my $member (@members) { + my $node; + my $iface; + + if ($member->GetNodeIface(\$node, \$iface) != 0) { + print STDERR "Could not get node/iface for $member\n"; + return -1; + } + # + # The node is a virtnode, but we want the physnode. + # + my $physnode = Node->Lookup($node->phys_nodeid()); + return -1 + if (!defined($physnode)); + my $pnode_uuid = $physnode->uuid(); + my $name = $node->node_id() . ":" . $iface; + + $rspec .= " "; + } + $rspec .= ""; + } + } + $rspec .= ""; + print "$rspec\n"; + $component_rspecs{$component_uuid} = $rspec; + } + + # + # Now submit each rspec to its component. + # + my %component_tickets = (); + + foreach my $component_uuid (keys(%component_nodes)) { + my @nodelist = @{ $component_nodes{$component_uuid} }; + my $component = $components{$component_uuid}; + my $rspec = $component_rspecs{$component_uuid}; # # Get ticket from component. # - print STDERR "Asking for ticket for $node from $component.\n"; + print STDERR "Asking for ticket from $component.\n"; my $ticket = $component->GetTicket($slice, $rspec, $context, $credential); if (!defined($ticket)) { - print STDERR "Could not get ticket from CM for $node\n"; + print STDERR "Could not get ticket from $component\n"; return -1; } @@ -239,7 +373,7 @@ sub AllocateSlivers($$$) # if ($ticket->Store() != 0) { $ticket->Delete(); - print STDERR "Could not store $ticket on $component for $node\n"; + print STDERR "Could not store $ticket for $component\n"; return -1; } @@ -250,12 +384,14 @@ sub AllocateSlivers($$$) # since an rspec can have multiple resources (nodes, links), # and I have not figured out what to do for that yet. # - if ($node->SetGeniSliverInfo($ticket->idx()) != 0) { - print STDERR "Could not set sliver (ticket) idx for $node\n"; - if ($ticket->Delete() != 0) { - print STDERR "Could not destroy $ticket\n"; + foreach my $node (@nodelist) { + if ($node->SetGeniSliverInfo($ticket->idx()) != 0) { + print STDERR "Could not set sliver/ticket idx for $node\n"; + if ($ticket->Delete() != 0) { + print STDERR "Could not destroy $ticket\n"; + } + return -1; } - return -1; } } @@ -287,67 +423,130 @@ sub InstantiateSlivers($$$) } # - # Loop through each node and grab a ticket for it. The nodes table - # stores the uuid of the node as told to us in resource discovery. - # Use this to create a simple rspec. This will need to get fancier - # later. - # - # XXX We are still not using rspecs anywhere. + # Loop through nodes, combining nodes at the same component into a + # list for that component. # + # Bookkeeping for the rest of this function. + my %components = (); + my %component_nodes = (); + my %component_tickets = (); + my %node_component = (); + my %node_slivers = (); + my %pnodes = (); + foreach my $node (@{ $nodelist }) { - my $sliver_idx; + my $ticket_idx; return -1 - if ($node->GetGeniSliverInfo(\$sliver_idx) != 0); - next - if (! $sliver_idx); - - my $sliver = GeniSliver->Lookup($sliver_idx); + if ($node->GetGeniSliverInfo(\$ticket_idx) != 0); next - if (defined($sliver)); + if (! $ticket_idx); - # See if its still a ticket. - my $ticket = GeniTicket->Lookup($sliver_idx); + my $physnode = Node->Lookup($node->phys_nodeid()); + if (!defined($physnode)) { + print STDERR "Could not get pnode object for $node\n"; + return -1; + } + $pnodes{$node->uuid()} = $physnode; + + my $ticket = GeniTicket->Lookup($ticket_idx); if (!defined($ticket)) { print STDERR "Could not find ticket for $node in $experiment\n"; return -1; } my $component = $ticket->component(); - # - # We need to pass in some extra stuff for Emulab federation. - # - my $args = {'tmcd_server' => $BOSSNODE, - 'tmcd_nodeid' => $node->node_id() }; + $component_nodes{$component->uuid()} = [] + if (!exists($component_nodes{$component->uuid()})); + push(@{ $component_nodes{$component->uuid()} }, $node); + $node_component{$node->node_id()} = $component; + $components{$component->uuid()} = $component; + + # Sanity check. + if (!exists($component_tickets{$component->uuid()})) { + $component_tickets{$component->uuid()} = $ticket; + } + elsif (!$ticket->SameTicket($component_tickets{$component->uuid()})) { + print STDERR "Ticket mismatch for $component\n"; + return -1; + } + } + + # + # For each component, submit the ticket associated with the list of + # nodes on that component. + # + foreach my $component_uuid (keys(%component_tickets)) { + my @nodelist = @{ $component_nodes{$component_uuid} }; + my $component = $components{$component_uuid}; + my $ticket = $component_tickets{$component_uuid}; # # Create sliver on component using the ticket. # - print STDERR "Redeeming ticket for $node on $component.\n"; + print STDERR "Redeeming ticket on $component.\n"; - $sliver = $component->CreateSliver($slice, $ticket, $context, $args); + my $sliver = + $component->CreateSliver($slice, $ticket, $context); + + # No longer need the ticket. + if ($ticket->Delete() != 0) { + print STDERR "Could not delete $ticket\n"; + } if (!defined($sliver)) { - print STDERR "Could not create sliver on $component for $node\n"; - if ($ticket->Delete() != 0) { - print STDERR "Could not delete $ticket\n"; - } - if ($node->SetGeniSliverInfo(0) != 0) { - print STDERR "Could not clear sliver idx for $node\n"; - } + print STDERR "Could not create sliver on $component\n"; return -1; } - if ($node->SetGeniSliverInfo($sliver->idx()) != 0) { - print STDERR "Could not set sliver idx for $node\n"; - if ($ticket->Delete() != 0) { - print STDERR "Could not delete $ticket\n"; + + # + # If the sliver corresponds to multiple nodes, then we want to + # split it apart so that we have a credential for each node. + # + if (@nodelist > 1) { + my @slivers; + + if ($component->SplitSliver($sliver, $context, \@slivers) != 0) { + print STDERR "Could not split $sliver on $component\n"; + if ($sliver->Destroy($context) != 0) { + print STDERR "Could not destroy $sliver\n"; + } + return -1; } - if ($sliver->Destroy() != 0) { - print STDERR "Could not destroy $sliver\n"; + foreach my $node (@nodelist) { + my $physnode = $pnodes{$node->uuid()}; + my $node_sliver; + foreach my $s (@slivers) { + # + # XXX Bogus use of hrn, but I am assuming the only + # time this happens is when speaking to another Emulab + # which will be doing the same thing. + # + if ($s->hrn() eq $node->node_id()) { + $node_sliver = $s; + last; + } + } + if (!defined($node_sliver)) { + print STDERR "Could not get sliver for $node in $sliver\n"; + if ($sliver->Destroy($context) != 0) { + print STDERR "Could not destroy $sliver\n"; + } + return -1; + } + $node_slivers{$node->uuid()} = $node_sliver; } - return -1; } - # No longer need the ticket. - if ($ticket->Delete() != 0) { - print STDERR "Could not delete $ticket\n"; + else { + # For loop below. + $node_slivers{$nodelist[0]->uuid()} = $sliver; + } + + foreach my $node (@nodelist) { + my $s = $node_slivers{$node->uuid()}; + + if ($node->SetGeniSliverInfo($s->idx()) != 0) { + print STDERR "Could not set sliver idx for $node\n"; + return -1; + } } } return 0; @@ -432,8 +631,6 @@ sub DestroySlivers($$$) # # Loop through each node and do the sliver thing. # - # XXX We are still not using rspecs anywhere. - # foreach my $node (@{ $nodelist }) { my $sliver_idx; return -1 diff --git a/protogeni/lib/GeniSliver.pm.in b/protogeni/lib/GeniSliver.pm.in index 933f12460..9aa6173f5 100644 --- a/protogeni/lib/GeniSliver.pm.in +++ b/protogeni/lib/GeniSliver.pm.in @@ -93,6 +93,7 @@ sub Lookup($$) $self->{'CREDENTIAL'} = undef; # client $self->{'AGGREGATE'} = undef; # server $self->{'RSPEC'} = undef; # client/server + $self->{'HRN'} = undef; # Bogus kludge for aggregates. my $rspec_string = $self->{'SLIVER'}->{'rspec_string'}; if (defined($rspec_string) && $rspec_string ne "") { @@ -139,16 +140,46 @@ sub Stringify($) return "[GeniSliver: $uuid, IDX: $idx]"; } +# +# Cons up an hrn. +# +sub hrn($) +{ + my ($self) = @_; + + if (defined($self->{'HRN'})) { + # XXX Kludge for Emulab aggregates + return $self->{'HRN'}; + } + elsif (defined($self->rspec()->{'tmcd_nodeid'})) { + return $self->rspec()->{'tmcd_nodeid'}; + } + elsif (defined($self->rspec()->{'hrn'})) { + return $self->rspec()->{'hrn'}; + } + else { + return $OURDOMAIN . "." . $self->resource_type() . "." . + $self->idx(); + } +} +# XXX Kludge for Emulab aggregates +sub Sethrn($$) +{ + my ($self, $hrn) = @_; + + $self->{'HRN'} = $hrn; + return 0; +} + # # Create a sliver record in the DB. On the client side we save the credential # that allows control of it, for later operations. # -sub Create($$$$$;$$$) +sub Create($$$$$$;$$$) { - my ($class, $slice, $owner_uuid, $resource_uuid, $resource_type, + my ($class, $slice, $owner_uuid, $uuid, $resource_uuid, $resource_type, $rspec, $credential, $component) = @_; my @insert_data = (); - my $uuid; my $certificate; # Every sliver gets a new unique index. @@ -161,20 +192,19 @@ sub Create($$$$$;$$$) print STDERR "Could not store certificate\n"; return undef; } - $uuid = $credential->this_uuid(); + $resource_uuid = $uuid = $credential->this_uuid(); # Store the credential return undef if ($credential->Store() != 0); } else { - # Create a cert pair, which gives us a new uuid. - $certificate = GeniCertificate->Create("sliver"); + # Create a cert pair, for this resource uuid. + $certificate = GeniCertificate->Create("sliver", $uuid); if (!defined($certificate)) { print STDERR "Could not generate new certificate and UUID!\n"; return undef; } - $uuid = $certificate->uuid(); } my $slice_uuid = $slice->uuid(); @@ -182,10 +212,8 @@ sub Create($$$$$;$$$) push(@insert_data, "created=now()"); push(@insert_data, "idx='$idx'"); push(@insert_data, "uuid='$uuid'"); - push(@insert_data, "resource_uuid='$resource_uuid'") - if (defined($resource_uuid)); - push(@insert_data, "resource_type='$resource_type'") - if (defined($resource_type)); + push(@insert_data, "resource_uuid='$resource_uuid'"); + push(@insert_data, "resource_type='$resource_type'"); push(@insert_data, "creator_uuid='$owner_uuid'"); push(@insert_data, "slice_uuid='$slice_uuid'"); @@ -487,8 +515,8 @@ sub Create() { my ($class, $slice, $user_uuid, $rspec, $credential, $component) = @_; - return GeniSliver->Create($slice, $user_uuid, - $credential->target_uuid(), "Client", $rspec, + return GeniSliver->Create($slice, $user_uuid, undef, undef, + "Client", $rspec, $credential, $component); } @@ -536,6 +564,45 @@ sub Destroy($$) return 0; } +# +# On the client side, the aggregate points to the parent sliver when it +# contains multiple resources. +# +sub SetAggregate($$) +{ + my ($self, $parent) = @_; + + return -1 + if (! (ref($self) && ref($parent))); + + my $idx = $self->idx(); + my $parent_uuid = $parent->uuid(); + + return -1 + if (!DBQueryWarn("update geni_slivers set ". + " aggregate_uuid='$parent_uuid' ". + "where idx='$idx'")); + + return 0; +} +sub GetAggregate($) +{ + my ($self) = @_; + + return undef + if (! ref($self)); + + return undef + if (!defined($self->aggregate_uuid())); + + my $parent = GeniSliver->Lookup($self->aggregate_uuid()); + if (!defined($parent)) { + print STDERR "Could not get parent object associated with $self\n"; + return undef; + } + return $parent; +} + ############################################################################ # # The server side methods are in packages which inherit from above. @@ -558,6 +625,7 @@ sub Create($$$$$) { my ($class, $slice, $user_uuid, $resource_uuid, $rspec) = @_; my $virtualization_type = $rspec->{'virtualization_type'}; + my $uuid = $resource_uuid; my $experiment = $slice->GetExperiment(); if (!defined($experiment)) { @@ -606,9 +674,9 @@ sub Create($$$$$) return -1; } my $vnode = Node->Lookup($vnodes[0]); - $resource_uuid = $vnode->uuid(); + $uuid = $vnode->uuid(); } - return GeniSliver->Create($slice, $user_uuid, $resource_uuid, + return GeniSliver->Create($slice, $user_uuid, $uuid, $resource_uuid, "Node", $rspec); } @@ -633,12 +701,12 @@ sub Provision($;$) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $resource_uuid = $self->resource_uuid(); + my $uuid = $self->uuid(); return 0 - if (!defined($resource_uuid)); - my $node = Node->Lookup($resource_uuid); + if (!defined($uuid)); + my $node = Node->Lookup($uuid); if (!defined($node)) { - print STDERR "Could not map node $resource_uuid to its object\n"; + print STDERR "Could not map node $uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -655,16 +723,14 @@ sub Provision($;$) return -1; } - if (defined($extraargs)) { - if (exists($extraargs->{'tmcd_server'}) && - exists($extraargs->{'tmcd_nodeid'})) { - my $tmcd_redirect = $extraargs->{'tmcd_server'} . ":" . - $extraargs->{'tmcd_nodeid'}; + if (exists($self->rspec()->{'tmcd_server'}) && + exists($self->rspec()->{'tmcd_nodeid'})) { + my $tmcd_redirect = + $self->rspec()->{'tmcd_server'} . ":" . + $self->rspec()->{'tmcd_nodeid'}; - if ($node->ModifyReservation({"tmcd_redirect" => - $tmcd_redirect})) { - return -1; - } + if ($node->ModifyReservation({"tmcd_redirect" => $tmcd_redirect})){ + return -1; } } @@ -674,7 +740,8 @@ sub Provision($;$) # the vnode since it is going to redirect to tmcd on the remote Emulab # controlling the experiment. # - if ($self->rspec()->{'virtualization_type'} eq "emulab-vnode") { + if (exists($self->rspec()->{'virtualization_type'}) && + $self->rspec()->{'virtualization_type'} eq "emulab-vnode") { my $pnode = Node->Lookup($node->phys_nodeid()); if (!defined($pnode)) { print STDERR "Could not get pnode object for $node\n"; @@ -738,12 +805,12 @@ sub UnProvision($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $resource_uuid = $self->resource_uuid(); + my $uuid = $self->uuid(); return 0 - if (!defined($resource_uuid)); - my $node = Node->Lookup($resource_uuid); + if (!defined($uuid)); + my $node = Node->Lookup($uuid); if (!defined($node)) { - print STDERR "Could not map node $resource_uuid to its object\n"; + print STDERR "Could not map node $uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -808,12 +875,12 @@ sub Start($) print STDERR "Could not map $self to its experiment\n"; return -1; } - my $resource_uuid = $self->resource_uuid(); + my $uuid = $self->uuid(); return 0 - if (!defined($resource_uuid)); - my $node = Node->Lookup($resource_uuid); + if (!defined($uuid)); + my $node = Node->Lookup($uuid); if (!defined($node)) { - print STDERR "Could not map node $resource_uuid to its object\n"; + print STDERR "Could not map node $uuid to its object\n"; return -1; } my $reservation = $node->Reservation(); @@ -869,7 +936,7 @@ sub Create() my ($class, $slice, $user_uuid, $resource_uuid, $rspec) = @_; return GeniSliver->Create($slice, $user_uuid, $resource_uuid, - "Interface", $rspec); + $resource_uuid, "Interface", $rspec); } sub Provision($) diff --git a/protogeni/lib/GeniTicket.pm.in b/protogeni/lib/GeniTicket.pm.in index f6ffc9898..c27db9c00 100644 --- a/protogeni/lib/GeniTicket.pm.in +++ b/protogeni/lib/GeniTicket.pm.in @@ -67,8 +67,8 @@ sub Lookup($$) # Map the component my $component; - if ($row->{'component_idx'}) { - $component = GeniComponent->Lookup($row->{'component_idx'}); + if ($row->{'component_uuid'}) { + $component = GeniComponent->Lookup($row->{'component_uuid'}); return undef if (!defined($component)); } @@ -132,6 +132,7 @@ sub owner_cert($) { return field($_[0], "owner_cert"); } sub ticket($) { return field($_[0], "ticket"); } sub asString($) { return field($_[0], "ticket_string"); } sub ticket_string($) { return field($_[0], "ticket_string"); } +sub component_uuid($) { return field($_[0], "component_uuid"); } sub component($) { return field($_[0], "component"); } sub stored($) { return field($_[0], "stored"); } @@ -183,7 +184,14 @@ sub CreateFromSignedTicket($$;$$) # Use XML::Simple to convert to something we can mess with. my $parser = XML::LibXML->new; - my $doc = $parser->parse_string($ticket_string); + my $doc; + eval { + $doc = $parser->parse_string($ticket_string); + }; + if ($@) { + print STDERR "Failed to parse ticket string: $@\n"; + return undef; + } # Dig out the rspec. my ($rspec_node) = $doc->getElementsByTagName("rspec"); @@ -326,7 +334,7 @@ sub Store($) $self->{'idx'} = $idx; } # A locally generated ticket will not have a component. Might change that. - push(@insert_data, "component_idx=" . $self->component()->idx()) + push(@insert_data, "component_uuid='" . $self->component()->uuid() . "'") if (defined($self->component())); # Now tack on other stuff we need. @@ -362,6 +370,7 @@ sub Sign($) my $idx = $self->seqno(); my $slice_cert = $self->slice_cert(); my $owner_cert = $self->owner_cert(); + my $hrn = "$OURDOMAIN.tickets.$idx"; my $rspec_xml = XMLout($self->rspec(), "NoAttr" => 1); $rspec_xml =~ s/opt\>/rspec\>/g; @@ -374,7 +383,10 @@ sub Sign($) " ticket\n". " $idx\n". " $owner_cert\n". + " $slice_cert\n". " $slice_cert\n". + " $hrn\n". + " 2008-05-10T09:00:00\n". " \n". " 1\n". " $rspec_xml\n". @@ -495,5 +507,19 @@ sub Release($) return 0; } +# +# Equality test for two tickets. +# +sub SameTicket($$) +{ + my ($self, $other) = @_; + + # Must be a real reference. + return 0 + if (! (ref($self) && ref($other))); + + return $self->idx() == $other->idx(); +} + # _Always_ make sure that this 1 is at the end of the file... 1; diff --git a/protogeni/xmlrpc/GNUmakefile.in b/protogeni/xmlrpc/GNUmakefile.in index ec920143c..a16d182c4 100644 --- a/protogeni/xmlrpc/GNUmakefile.in +++ b/protogeni/xmlrpc/GNUmakefile.in @@ -40,7 +40,7 @@ install-scripts: $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-sa.pl \ $(SUDO) chown root $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-cm.pl $(SUDO) chmod u+s $(INSTALL_DIR)/protogeni/xmlrpc/protogeni-cm.pl -install: install-libs +install: install-libs install-scripts control-install: diff --git a/protogeni/xmlrpc/client.py b/protogeni/xmlrpc/client.py index 873d9b263..fb9cb31fb 100755 --- a/protogeni/xmlrpc/client.py +++ b/protogeni/xmlrpc/client.py @@ -122,6 +122,8 @@ if rval: pass mycredential = response["value"] print "Got my SA credential" +#print str(mycredential); +#sys.exit(0); # # Look me up just for the hell of it. I can see why the hrn is "useful" @@ -230,6 +232,7 @@ print "Bogus DiscoverResources returned" # rspec = " " +\ " " +\ " " +\ " new( "methods" => { "StartSliver" => \&GeniCM::StartSliver, "DeleteSliver" => \&GeniCM::DeleteSliver, "DeleteSlice" => \&GeniCM::DeleteSlice, + "SplitSliver" => \&GeniCM::SplitSliver, }, ); -- GitLab