From 7a82778e3c97e18f4e018400bd9bff016392467d Mon Sep 17 00:00:00 2001 From: Leigh B Stoller Date: Tue, 7 Dec 2010 11:24:56 -0700 Subject: [PATCH] A slew of new support for cooked mode. Swapmod now works pretty well, although the default is currently to do cooked mode in "basic" mode, rather then "full" mode. --- db/libGeni.pm.in | 1266 ++++++++++++++++++++++------- protogeni/lib/GeniAggregate.pm.in | 105 ++- protogeni/lib/GeniCM.pm.in | 102 ++- protogeni/lib/GeniCMV2.pm.in | 4 +- protogeni/lib/GeniResource.pm.in | 1013 +++++++++++++++++------ protogeni/lib/GeniSliver.pm.in | 20 + tbsetup/libossetup.pm.in | 33 +- tbsetup/libvtop.pm.in | 1115 +++++++++---------------- tbsetup/mapper.in | 38 +- tbsetup/tbswap.in | 17 +- 10 files changed, 2400 insertions(+), 1313 deletions(-) diff --git a/db/libGeni.pm.in b/db/libGeni.pm.in index 39d65d442..41c6cbea5 100644 --- a/db/libGeni.pm.in +++ b/db/libGeni.pm.in @@ -16,9 +16,9 @@ use vars qw(@ISA @EXPORT); # Configure variables my $TB = "@prefix@"; my $BOSSNODE = "@BOSSNODE@"; -my $TBDOCBASE = "@TBDOCBASE@"; +#my $TBDOCBASE = "@TBDOCBASE@"; +my $TBDOCBASE = "http://www.emulab.net"; my $ELVIN_COMPAT= @ELVIN_COMPAT@; - my $ASSIGN = "$TB/libexec/assign"; use libdb; @@ -32,13 +32,17 @@ use Lan; use GeniEmulab; use GeniResource; use GeniResponse; +use GeniAuthority; use GeniXML; use English; use Socket; use XML::Simple; +use XML::LibXML; +use XML::SemanticDiff; use Data::Dumper; use File::Temp; use IO::File; +use POSIX qw(strftime); sub Register($$) { @@ -64,61 +68,51 @@ sub RenewSlivers($;$) # # Map rspec to resources using assign. # -sub MapResources($$$$) +sub MapResources($$$$$) { - my ($experiment, $user, $rspec, $verbose) = @_; - my %cm_urns = (); - my %fragments = (); - my %nodemap = (); - my %node_cms = (); - + my ($experiment, $user, $topo, $rspecref, $verbose) = @_; + my %cmurn2res = (); + my %cmurn2nodes = (); + my %cmurn2links = (); + my %cmurn2frags = (); + my %vname2res = (); + my %vname2doc = (); + Register($experiment, $user) == 0 or return -1; - foreach my $ref (@{ $rspec->{'node'} }) { + # + # Build up rspec fragments from the "intermediate" representation + # that libvtop passed in. Ick. + # + foreach my $ref (@{ $topo->{'node'} }) { + # Skip non geni nodes; handled in libvtop + next + if (! (exists($ref->{'isgeninode'}) && $ref->{'isgeninode'})); + my $resource; - my %copy = %{ $ref }; - my $copy = \%copy; + my $node_urn = $ref->{'request_urn'}; + my $manager_urn = $ref->{'manager_urn'}; + my $virtual_id = $ref->{'virtual_id'}; + my (undef,undef,$node_id) = GeniHRN::Parse($node_urn); - my $node_urn = $copy->{'request_urn'}; - my ($auth,$type,$node_id) = GeniHRN::Parse($node_urn); - my $cm = GeniHRN::Generate($auth, "authority", "cm"); - $copy->{'component_manager_uuid'} = $cm; - # # Get the resource object. # - if (!exists($cm_urns{$cm})) { - $resource = GeniResource->Lookup($experiment->idx(), $cm); + if (!exists($cmurn2res{$manager_urn})) { + $resource = GeniResource->Lookup($experiment->idx(), $manager_urn); if (!defined($resource)) { - $resource = GeniResource->Create($experiment, $cm); + $resource = GeniResource->Create($experiment, $manager_urn); if (!defined($resource)) { - print STDERR "Could not create GeniResource for $cm\n"; + print STDERR + "Could not create GeniResource for $manager_urn\n"; return -1; } } - $cm_urns{$cm} = $resource; - } - $resource = $cm_urns{$cm}; - - # - # request_urn means nothing to assign; kill that from the copy. - # - delete($copy->{'request_urn'}); - # Ditto - delete($copy->{'tarfiles'}); - - # - # If already have the ticket, then leave the urn alone. - # We do not run assign again, but we need the rspec to be - # complete for loops below. - # - if (!$resource->HaveTicket()) { - if ($node_id ne "*") { - $copy->{'component_uuid'} = $node_urn; - $copy->{'component_urn'} = $node_urn; - } + $cmurn2res{$manager_urn} = $resource; } + $resource = $cmurn2res{$manager_urn}; + $vname2res{$virtual_id} = $resource; # # The point of this is to split the rspec apart, since at present @@ -126,108 +120,91 @@ sub MapResources($$$$) # to pass in an rspec that references CMs other then the one # advertisement being passed in. # - # This should go away when assign is fixed. - # - if (!exists($fragments{$cm})) { - $fragments{$cm} = { - 'generated_by' => 'libvtop', - 'type' => 'request', - 'xmlns' => 'http://www.protogeni.net/resources/rspec/0.2', - # Assign now wants this stuff. I have no clue what it means! - 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", - 'xsi:schemaLocation' => - "http://www.protogeni.net/resources/rspec/0.2 ". - "http://www.protogeni.net/resources/rspec/0.2/request.xsd", - 'node' => [] }; - } - push(@{ $fragments{$cm}->{'node'} }, $copy); - $node_cms{$ref->{'virtual_id'}} = $cm; - $nodemap{$ref->{'virtual_id'}} = $ref; + my $fragment; + if (!exists($cmurn2frags{$manager_urn})) { + $fragment = CreateNewRspec(); + return -1 + if (!defined($fragment)); + + $cmurn2frags{$manager_urn} = $fragment; + } + $fragment = $cmurn2frags{$manager_urn}; + + my $rspecdoc = AddNodeToRspec($fragment, $ref); + return -1 + if (!defined($rspecdoc)); + $vname2doc{$virtual_id} = $rspecdoc; } # # As above, need to split the interfaces into the correct fragments. # - if (exists($rspec->{'link'})) { - foreach my $ref (@{ $rspec->{'link'} }) { + if (exists($topo->{'link'})) { + foreach my $ref (@{ $topo->{'link'} }) { my $linkname = $ref->{'virtual_id'}; - # means nothing to assign; added again below. - delete($ref->{'component_manager'}); - # Skip tunnels until rspec stitching in place. next if (exists($ref->{'link_type'}) && $ref->{'link_type'} eq "tunnel"); - next - if (0 && exists($ref->{'link_type'}) && - exists($ref->{'link_type'}->{'type_name'}) && - $ref->{'link_type'}->{'type_name'} eq "tunnel"); - foreach my $ifaceref (@{ $ref->{'interface_ref'} }) { - my $virtual_node_id = $ifaceref->{'virtual_node_id'}; + my $ifaceref0 = $ref->{'interface_ref'}->[0]; + my $ifaceref1 = $ref->{'interface_ref'}->[1]; + # Do not want to add this twice. + my $virtid0 = $ifaceref0->{'virtual_node_id'}; + my $resource0 = $vname2res{$virtid0}; + my $fragment0 = $cmurn2frags{$resource0->manager_urn()}; - if (!exists($node_cms{$virtual_node_id}) || - !exists($fragments{$node_cms{$virtual_node_id}})) { - print STDERR "Inconsistency in rspec link $linkname\n"; - print Dumper($rspec); - return -1; - } - my $fragment = $fragments{$node_cms{$virtual_node_id}}; - if (!exists($fragment->{'link'})) { - $fragment->{'link'} = []; - } - push(@{ $fragment->{'link'} }, $ref); - # - # There should be a sanity check here ... too lazy. - # - last; + my $rspecdoc = AddLinkToRspec($fragment0, $ref); + return -1 + if (!defined($rspecdoc)); + # Only one of these in the array, for building combined rspec. + $vname2doc{$linkname} = $rspecdoc; + + my $virtid1 = $ifaceref1->{'virtual_node_id'}; + my $resource1 = $vname2res{$virtid1}; + my $fragment1 = $cmurn2frags{$resource1->manager_urn()}; + if ($resource0->manager_urn() ne $resource1->manager_urn()) { + $rspecdoc = AddLinkToRspec($fragment1, $ref); + return -1 + if (!defined($rspecdoc)); } } } + if ($verbose) { + print STDERR "Rspec Fragments:\n"; + foreach my $fragment (values(%cmurn2frags)) { + my $rspecstr = GeniXML::Serialize($fragment, 1); + print STDERR "$rspecstr\n"; + } + } + # - # Discover resources at the component and run assign. + # Discover resources in parallel and run assign, writing the solution + # to a file. # - foreach my $cm (keys(%cm_urns)) { - my $resource = $cm_urns{$cm}; - my $fragment = $fragments{$cm}; + my $coderef = sub { + my ($resource, $fragment, $tmp) = @{ $_[0] }; my $advertisement; - # - # We got the ticket on a previous loop. - # - if ($resource->HaveTicket()) { - print STDERR "Already have a ticket for $resource; skipping ...\n"; - next; - } - print STDERR "Asking for resource list from $resource\n"; if ($resource->Discover($user, \$advertisement)) { print STDERR "Could not get resource list for $resource\n"; return -1; } - my $tmp = File::Temp::mktemp("XXXXX"); my $soln = $tmp . ".soln"; my $log = $tmp . ".log"; my $ptop = $tmp . ".ptop"; my $vtop = $tmp . ".vtop"; - my $reqstring = - eval { XMLout($fragment, "NoAttr" => 0, RootName => "rspec") }; - if ($@) { - print STDERR "XMLout error: $@\n"; - print STDERR Dumper($fragment); - return -1; - } - + my $reqstring = GeniXML::Serialize($fragment); my $vtopfh = new IO::File "> $vtop"; my $ptopfh = new IO::File "> $ptop"; if (! (defined($vtopfh) && defined($ptopfh))) { print STDERR "Could not create temporary files for ptop/vtop\n"; return -1; } - print $vtopfh "\n"; print $vtopfh $reqstring; print $ptopfh $advertisement; $vtopfh->close(); @@ -242,11 +219,75 @@ sub MapResources($$$$) if ($?) { print STDERR "Could not map to physical resources on $resource\n"; - print STDERR Dumper($fragment); my $logstuff = `cat $log`; print STDERR "\n" . $logstuff . "\n"; + my $string = GeniXML::Serialize($fragment, 1); + print STDERR "$string\n"; return -1; } + return 0; + }; + + # + # Figure out which resources still need to be mapped. + # + my @todo = (); + my @results = (); + + foreach my $manager_urn (keys(%cmurn2res)) { + my $resource = $cmurn2res{$manager_urn}; + my $fragment = $cmurn2frags{$manager_urn}; + + # + # We got the ticket on a previous loop. + # + if ($resource->HaveTicket()) { + print STDERR "Already have a ticket for $resource; skipping ...\n"; + next; + } + my $tmp = File::Temp::mktemp("XXXXX"); + push(@todo, [$resource, $fragment, $tmp]); + } + + print STDERR "Mapping resources in parallel ...\n"; + if (ParRun({'maxwaittime' => 600, 'maxchildren' => 4}, + \@results, $coderef, @todo)) { + print STDERR "*** MapResources: Internal error mapping resources\n"; + return -1; + } + + # + # Check the exit codes. + # + my $errors = 0; + my $count = 0; + foreach my $result (@results) { + my ($resource, $fragment, $tmp) = @{ $todo[$count] }; + + # + # ParRun does a fork; so need to refresh the resource object + # to sync it to the DB. + # + if ($resource->Refresh()) { + print STDERR "*** MapResources: Error synchronizing $resource\n"; + $errors++; + } + elsif ($result != 0) { + print STDERR "*** Error mapping resources for $resource\n"; + $errors++; + } + $count++; + } + return -1 + if ($errors); + + # + # Since everything mapped, read the solutions and write back to the rspec + # + foreach my $ref (@todo) { + my ($resource, $fragment, $tmp) = @{ $ref }; + my $soln = $tmp . ".soln"; + my $solution = eval { XMLin($soln, KeyAttr => [], ForceArray => ["node", "link", "interface", @@ -265,41 +306,33 @@ sub MapResources($$$$) my $virtual_id = $ref->{'virtual_id'}; my $node_urn = $ref->{'component_uuid'}; my $cm_urn = $ref->{'component_manager_uuid'}; - my $noderef = $nodemap{$virtual_id}; + my $rspecdoc = $vname2doc{$virtual_id}; # - # This writes the solution back into the original rspec. + # This writes the solution back into the fragment. # - $noderef->{'component_uuid'} = $node_urn; - $noderef->{'component_urn'} = $node_urn; - $noderef->{'component_manager_uuid'} = $cm_urn; - - if (exists($noderef->{'disk_image'})) { - my $osid = $noderef->{'disk_image'}->{'name'}; - my ($auth,$type,$id) = GeniHRN::Parse($node_urn); + $rspecdoc->setAttribute("component_uuid", $node_urn); + $rspecdoc->setAttribute("component_urn", $node_urn); + $rspecdoc->setAttribute("component_manager_uuid", $cm_urn); + $rspecdoc->setAttribute("component_manager_urn", $cm_urn); - my $urn = GeniHRN::Generate($auth, "image", "emulab-ops"); - $urn .= "//" . $osid; - - $noderef->{'disk_image'}->{'name'} = $urn; - } - if (exists($ref->{'interface'})) { my $interfaces = $ref->{'interface'}; foreach my $ifaceref (@{ $interfaces }) { - my $iface_id = $ifaceref->{'virtual_id'}; - my $compid = $ifaceref->{'component_id'}; + my $virtid = $ifaceref->{'virtual_id'}; + my $compid = $ifaceref->{'component_id'}; # Not supposed to happen, but does cause of issues # with tunnels and rspec stitching. next if (!defined($compid)); - foreach my $oref (@{ $noderef->{'interface'} }) { - if ($oref->{'virtual_id'} eq $iface_id) { - # write the solution back into the original rspec. - $oref->{'component_id'} = $compid; + foreach my $linkref (GeniXML::FindNodes("n:interface", + $rspecdoc)->get_nodelist()) { + my $ovirtid = GeniXML::GetText("virtual_id", $linkref); + if ($ovirtid eq $virtid) { + $linkref->setAttribute("component_id", $compid); last; } } @@ -307,133 +340,225 @@ sub MapResources($$$$) } } } - if (exists($rspec->{'link'})) { - foreach my $ref (@{ $rspec->{'link'} }) { - my %cms = (); - my @cms = (); - - foreach my $ifaceref (@{ $ref->{'interface_ref'} }) { - my $virtual_node_id = $ifaceref->{'virtual_node_id'}; - my $node_cm = $node_cms{$virtual_node_id}; - - if (!exists($cms{$node_cm})) { - push(@cms, {'id' => $node_cm}); - $cms{$node_cm} = $node_cm; - } + # + # One last chore; we had to skip tunnels above since assign does + # not know how to deal with them. + # + if (exists($topo->{'link'})) { + foreach my $ref (@{ $topo->{'link'} }) { + my $linkname = $ref->{'virtual_id'}; + + # Skip non-tunnels. + next + if (! (exists($ref->{'link_type'}) && + $ref->{'link_type'} eq "tunnel")); + + my $ifaceref0 = $ref->{'interface_ref'}->[0]; + my $ifaceref1 = $ref->{'interface_ref'}->[1]; + # Do not want to add this twice. + my $virtid0 = $ifaceref0->{'virtual_node_id'}; + my $resource0 = $vname2res{$virtid0}; + my $fragment0 = $cmurn2frags{$resource0->manager_urn()}; + + my $rspecdoc = AddLinkToRspec($fragment0, $ref); + return -1 + if (!defined($rspecdoc)); + # Only one of these in the array, for building combined rspec. + $vname2doc{$linkname} = $rspecdoc; + + my $virtid1 = $ifaceref1->{'virtual_node_id'}; + my $resource1 = $vname2res{$virtid1}; + my $fragment1 = $cmurn2frags{$resource1->manager_urn()}; + if ($resource0->manager_urn() ne $resource1->manager_urn()) { + $rspecdoc = AddLinkToRspec($fragment1, $ref); + return -1 + if (!defined($rspecdoc)); } - $ref->{'component_manager'} = [@cms]; } } - if ($verbose) { - print STDERR "Final rspec:\n"; - print STDERR Dumper($rspec); + # + # Compare to previous rspec; this tells us if we need to request + # a new ticket. Easier to use the rspec then the manifest since + # the CM added a ton of cruft to it. + # + foreach my $manager_urn (keys(%cmurn2res)) { + my $resource = $cmurn2res{$manager_urn}; - if (1) { - my $rspecdoc = GeniResource::ConvertRspec($rspec); - my $rspecstr = GeniXML::Serialize($rspecdoc); - print STDERR "In XML:\n"; - print STDERR "$rspecstr\n"; + # We got the ticket on a previous loop. + next + if ($resource->HaveTicket()); + + my $fragment = $cmurn2frags{$manager_urn}; + my $frag_string = GeniXML::Serialize($fragment); + # Must stash for redeemticket. + $resource->stashrspec($frag_string); + + next + if (! $resource->HaveRspec()); + + # + # The idea is to do a diff of the rspec. If there are no + # meaningful changes, then we can skip trying to get a new + # ticket. + # + my $rspec = $resource->Rspec(); + my $diff = XML::SemanticDiff->new(); + my $rspec_string = GeniXML::Serialize($rspec); + $resource->setmodified(0); + + print STDERR "Comparing old and new rspecs for $resource\n"; + foreach my $change ($diff->compare($rspec_string, $frag_string)) { + print STDERR " $change->{message} in context $change->{context}\n"; + $resource->setmodified(1); + } + if ($resource->modified() && $resource->ManagerApiLevel() == 0) { + print STDERR "*** Difference to rspec for level 0 $resource. ". + "Not allowed, aborting\n"; + return -1; } } + + # + # Combine the fragments into final rspec document. + # + my $rspec = CombineRspecDocs(values(%vname2doc)); + return -1 + if (!defined($rspec)); + + # Make sure all that namespace stuff is done correctly. + $rspec = GeniXML::Parse(GeniXML::Serialize($rspec)); + + if ($verbose) { + print STDERR "Final rspec:\n"; + my $rspecstr = GeniXML::Serialize($rspec, 1); + print STDERR "$rspecstr\n"; + } + $$rspecref = $rspec; return 0; } -sub GetTickets($$$$) +sub GetTickets($) { - my ($experiment, $verbose, $user, $rspec) = @_; - my %cm_urns = (); - my %nodemap = (); + my ($vtop) = @_; + my $experiment = $vtop->experiment(); + my $verbose = $vtop->verbose(); + my $user = $vtop->user(); + my $rspec = $vtop->genirspec(); + my $eventkey = $experiment->eventkey(); + my $keyhash = $experiment->keyhash(); Register($experiment, $user) == 0 or return -1; - foreach my $ref (@{ $rspec->{'node'} }) { - # - # Delete the fake lan nodes before sending off the request. - # They were needed for the mapping above, but the remote CM - # will barf on them. - # - if (0 && exists($ref->{'node_type'}) && - exists($ref->{'node_type'}->{'type_name'}) && - $ref->{'node_type'}->{'type_name'} eq "lan") { - next; - } - my $virtual_id = $ref->{'virtual_id'}; - my $node_urn = $ref->{'component_urn'}; - my ($auth,$type,$node_id) = GeniHRN::Parse($node_urn); - my $cm = GeniHRN::Generate($auth, "authority", "cm"); + # + # Get the resource objects. + # + my @resources = GeniResource->LookupAll($experiment); + if (! @resources) { + print STDERR "RedeemTickets: No resource objects\n"; + return 0; + } + my @todo = (); - # - # Get the resource objects for below. - # - if (!exists($cm_urns{$cm})) { - my $resource = GeniResource->Lookup($experiment->idx(), $cm); - if (!defined($resource)) { - print STDERR "Could not get GeniResource for $cm\n"; - return -1; - } - $nodemap{$virtual_id} = $resource; - - # - # We got the ticket on a previous loop. - # - next - if ($resource->HaveTicket()); - - $cm_urns{$cm} = $resource; - } - # - # This is how we get the client side to do cooked mode properly. - # - $ref->{'tarfiles'} = "/usr/local/etc/emulab ". - "$TBDOCBASE/downloads/geniclient.tar"; + foreach my $resource (@resources) { + next + if ($resource->HaveTicket() || !$resource->modified()); + + push(@todo, $resource); } - # No tickets needed, return now. return 0 - if (! scalar(keys(%cm_urns))); + if (! @todo); # - # XXX Convert to a proper XML looking thing. This is just a temporay - # hack for now. + # Before sending off the rspec, add the stuff that we need for + # full cooked mode to work. # - my $rspecdoc = GeniResource::ConvertRspec($rspec); - my $rspecstr = GeniXML::Serialize($rspecdoc); + foreach my $ref (GeniXML::FindNodes("n:node", + $rspec)->get_nodelist()) { + my $vname = GeniXML::GetVirtualId($ref); + my $node = $vtop->VnameToNode($vname); + + # XXX Boot mode; simple vs full. Have not decided the + # proper interface for this yet. + my $bootmode = $experiment->GetEnvVariable("${vname}_BOOTMODE"); + next + if (defined($bootmode) && lc($bootmode) eq "basic"); + + if (!defined($node)) { + print STDERR "*** GetTickets: No proxy node for $vname\n"; + return -1; + } + + # Generate a key for the node if not already there. The caller + # has created and allocated the proxy nodes by now. + my $key; + + if (!defined($node->external_resource_key())) { + $key = TBGenSecretKey(); + $node->ModifyReservation({"external_resource_key" => $key}) == 0 + or return -1; + } + else { + $key = $node->external_resource_key(); + } + + $ref->setAttribute("tarfiles", + "/usr/local/etc/emulab ". + "$TBDOCBASE/downloads/geniclient.tar"); + + my $cmd = "sudo /usr/local/etc/emulab/rc/rc.pgeni ". + "-s $BOSSNODE -k $eventkey,$keyhash -r '$key'"; + $cmd .= " -j " . $node->node_id() + if ($node->isvirtnode()); + $cmd .= " -e 16509" + if ($ELVIN_COMPAT); + + $ref->setAttribute("startup_command", "$cmd boot"); + } if ($verbose) { - print STDERR "In XML:\n"; - print STDERR "$rspecstr\n"; + print STDERR "Rspec for GetTicket():\n"; + print STDERR GeniXML::Serialize($rspec, 1) . "\n"; } + my $rspecstr = GeniXML::Serialize($rspec); # # Get Tickets in parallel. # - my @resources = values(%cm_urns); my @results = (); my $coderef = sub { my ($resource) = @_; - print STDERR "Asking for ticket from $resource\n"; - if ($resource->GetTicket($user, $rspecstr, 0)) { - # - # Print this here since we do not save this in the - # DB, and the parent side of the fork will not have - # the error message. - # - if ($resource->last_rpc_error() && - defined($resource->last_rpc_value()) && - $resource->last_rpc_value()) { - print STDERR $resource->last_rpc_value() . "\n"; - } - # Return indicator of possible forward progress. - return 1 - if ($resource->last_rpc_output() =~ /Could not map to/i); - return -1; + if ($resource->ManagerApiLevel() == 0) { + print STDERR "Creating sliver on level 0 API $resource\n"; + return 0 + if ($resource->CreateSliver($user, $rspecstr, 0) == 0); } - return 0; + else { + print STDERR "Asking for ticket from $resource\n"; + return 0 + if ($resource->GetTicket($user, $rspecstr, 0) == 0); + } + # + # Print this here since we do not save this in the + # DB, and the parent side of the fork will not have + # the error message. + # + if ($resource->last_rpc_error() && + defined($resource->last_rpc_value()) && + $resource->last_rpc_value()) { + print STDERR $resource->last_rpc_value() . "\n"; + } + # Return indicator of possible forward progress. + return 1 + if (defined($resource->last_rpc_output()) && + $resource->last_rpc_output() =~ /Could not map to/i); + + return -1; }; - print STDERR "Getting all tickets in parallel ...\n"; + print STDERR "Getting all tickets/slivers in parallel ...\n"; - if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @resources)) { + if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @todo)) { print STDERR "*** GetTickets: Internal error getting tickets\n"; # # Need to be careful here; some of the tickets might have been @@ -472,17 +597,46 @@ sub GetTickets($$$$) } else { $progress++; + my $object; + + if ($resource->ManagerApiLevel() == 0) { + # + # Created the sliver; + # + $object = $resource->Manifest(); + + # File the rspec away since that is what we have. + if ($resource->UpdateRspec($resource->stashedrspec())) { + print STDERR "Could not store rspec for $resource\n"; + $errors++; + } + $resource->stashrspec(undef); + } + else { + # + # Got a ticket. + # + $object = $resource->Ticket(); + $object = $object->rspec() + if (defined($object)); + } + return -1 + if (!$object); # - # Got a ticket; mark the proxy nodes so that libvtop knows. - # Failure to get a ticket means we need to release the node - # up in libvtop. Probably need a state variable instead. - # - foreach my $virtual_id (keys(%nodemap)) { - next - if (!$resource->SameResource($nodemap{$virtual_id})); + # Mark the proxy nodes so that libvtop knows. Failure means we + # need to release the node up in libvtop. + # + foreach my $ref (GeniXML::FindNodes("n:node", + $object)->get_nodelist()) { + my $vname = GeniXML::GetVirtualId($ref); + my $manager_urn= GeniXML::GetManagerId($ref); - my $node = $experiment->VnameToNode($virtual_id); + next + if (!defined($manager_urn) || + $resource->manager_urn() ne $manager_urn); + + my $node = $experiment->VnameToNode($vname); if (defined($node)) { $node->ModifyReservation({"external_resource_index" => $resource->idx()}) @@ -503,9 +657,9 @@ sub GetTickets($$$$) # # Redeem the tickets for an experiment. # -sub RedeemTickets($$$) +sub RedeemTickets($$) { - my ($experiment, $user, $rspec) = @_; + my ($experiment, $user) = @_; # # Get the resource objects. @@ -515,6 +669,14 @@ sub RedeemTickets($$$) print STDERR "RedeemTickets: No resource objects\n"; return 0; } + my @todo = (); + + foreach my $resource (@resources) { + push(@todo, $resource) + if ($resource->HaveTicket()); + } + return 0 + if (! @todo); # # Redeem Tickets in parallel. @@ -531,7 +693,7 @@ sub RedeemTickets($$$) }; print STDERR "Redeeming all tickets in parallel ...\n"; - if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @resources)) { + if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @todo)) { print STDERR "*** RedeemTickets: Internal error getting tickets\n"; # # Need to be careful here; some of the tickets might have been @@ -547,7 +709,7 @@ sub RedeemTickets($$$) my $errors = 0; my $count = 0; foreach my $result (@results) { - my $resource = $resources[$count]; + my $resource = $todo[$count]; # # ParRun does a fork; so need to refresh the resource object @@ -561,9 +723,16 @@ sub RedeemTickets($$$) print STDERR "*** Error redeeming ticket for $resource\n"; $errors++; } + else { + # File the rspec away since that is what we have. + if ($resource->UpdateRspec($resource->stashedrspec())) { + print STDERR "Could not store rspec for $resource\n"; + $errors++; + } + $resource->stashrspec(undef); + } $count++; } - print STDERR Dumper($rspec) if ($errors); return $errors; } @@ -574,9 +743,8 @@ sub RedeemTickets($$$) sub MapNodes($$) { my ($experiment, $verbose) = @_; - my $eventkey = $experiment->eventkey(); - my $keyhash = $experiment->keyhash(); my %ifacemap = (); + my $manifest_string; # # Get the resource objects. @@ -590,10 +758,11 @@ sub MapNodes($$) my $manifest = $resource->Manifest(); return -1 if (!defined($manifest)); - my $manifest_string = $resource->manifest_string(); + $manifest_string = GeniXML::Serialize($manifest, 1); - print STDERR "$manifest_string\n" - if ($verbose); + if ($verbose) { + print STDERR "$manifest_string\n" + } foreach my $ref (GeniXML::FindNodes("n:node", $manifest)->get_nodelist()) { @@ -618,7 +787,7 @@ sub MapNodes($$) if (!defined($node)) { print STDERR "MapNodes: Could not locate node $vname in $experiment\n"; - return -1; + goto bad; } $node->ModifyReservation({"external_resource_index" => $resource->idx(), @@ -629,17 +798,6 @@ sub MapNodes($$) if (defined($sshdport)) { $node->Update({'sshdport' => $sshdport}); } - # - # This is how we get the client side to do cooked mode properly. - # - my $cmd = "sudo /usr/local/etc/emulab/rc/rc.pgeni ". - "-s $BOSSNODE -k $eventkey,$keyhash -u '$sliver_urn'"; - $cmd .= " -j " . $node->node_id() - if ($node->isvirtnode()); - $cmd .= " -e 16509" - if ($ELVIN_COMPAT); - - GeniXML::SetText("startup_command", $ref, "$cmd boot"); # Interface map for loop below. if (defined(GeniXML::FindFirst("n:interface", $ref))) { @@ -662,7 +820,7 @@ sub MapNodes($$) my %managers = (); if (GeniXML::FindNodes("n:component_manager", $ref)) { - %managers = map { GeniXML::GetText("id", $_) => $_ } + %managers = map { GetLinkManager($_) => $_ } GeniXML::FindNodes("n:component_manager", $ref)->get_nodelist(); } @@ -684,13 +842,12 @@ sub MapNodes($$) if (defined($TAG)) { if (!($TAG =~ /^[\w]*$/)) { print STDERR "Bad vlantag '$TAG' for $linkname\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } my $lan = Lan->Lookup($experiment, $linkname, 1); if (!defined($lan)) { print STDERR "Could not find vlan for $linkname\n"; - return -1; + goto bad; } # # XXX This seems backwards. If the lan is pointing @@ -728,8 +885,7 @@ sub MapNodes($$) if (!defined($iface)) { print STDERR "Could not determine iface for" . "$vname,$iface_id\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } # # If this is a virtual node, then we want to set the @@ -740,8 +896,7 @@ sub MapNodes($$) my $pnode = $node->GetPhysHost(); if (!defined($pnode)) { print STDERR "Could not physical node for $node\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } $node = $pnode; } @@ -754,23 +909,19 @@ sub MapNodes($$) my $interface = Interface->LookupByIface($node,$iface); if (!defined($interface)) { print STDERR "Could not map iface for $node,$iface\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } if (!defined($MAC)) { print STDERR "No mac (or vmac) for $node,$iface\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } if (! ($MAC =~ /^[\w]*$/)) { print STDERR "Bad mac '$MAC' for $node,$iface\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } if ($interface->Update({"mac" => "$MAC"})) { print STDERR "Could not update $node,$iface\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } } if (defined($VMAC)) { @@ -781,24 +932,23 @@ sub MapNodes($$) if (!defined($vinterface)) { print STDERR "Could not map vinterface for $linkname,$vname\n"; - print STDERR Dumper($manifest); - return -1; + goto bad; } if (! ($VMAC =~ /^[\w]*$/)) { print STDERR "Bad vmac '$VMAC' for $linkname,$vname\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } if ($vinterface->Update({"mac" => "$VMAC"})) { print STDERR "Could not update $linkname,$vname\n"; - print STDERR "$manifest_string\n"; - return -1; + goto bad; } } } } - print STDERR Dumper($manifest) - if ($verbose); + if ($verbose) { + my $string = GeniXML::Serialize($manifest, 1); + print STDERR "$string\n"; + } # The manifest was changed above. if ($resource->UpdateManifest($manifest)) { @@ -807,15 +957,21 @@ sub MapNodes($$) } } return 0; + + bad: + if ($manifest_string) { + print STDERR "$manifest_string\n"; + } + return -1; } # # Boot (Start) all of the slivers. This does the entire set, and blocks -# till done. +# till done. Expressly for use from os_setup. # -sub StartSlivers($$$$) +sub StartSlivers($$$) { - my ($experiment, $user, $restart, $verbose) = @_; + my ($experiment, $user, $verbose) = @_; # # Get the resource objects. @@ -832,11 +988,45 @@ sub StartSlivers($$$$) my $coderef = sub { my ($resource) = @_; + # The sliver was auto started. We just need to wait for it. + if ($resource->ManagerApi() eq "AM" || $resource->ManagerApiLevel() == 0) { + print STDERR + "Skipping start sliver on level 0 resource $resource\n"; + return 0; + } + + if ($resource->ManagerVersion() >= 2) { + # + # First get the state; we might not need to start it (would be wrong) + # if the sliver is in the started start. We deal with restart later. + # + print STDERR "Getting current sliver status for $resource\n"; + my $status; + + while (1) { + my $retval = $resource->SliverStatus($user, \$status); + last + if ($retval == 0); + + if ($resource->last_rpc_error() && + $resource->last_rpc_error() == GENIRESPONSE_BUSY()) { + sleep(10); + next; + } + else { + print STDERR "Error getting sliver status for $resource\n"; + # Tell the parent error. + return -1; + } + } + if ($status->{'state'} eq "started") { + print STDERR "Skipping start on already started $resource.\n"; + return 0; + } + } + print STDERR "Starting sliver $resource\n"; while (1) { - print STDERR "Starting ($$) sliver $resource\n"; - my $retval = ($restart ? - $resource->RestartSliver($user) : - $resource->StartSliver($user)); + my $retval = $resource->StartSliver($user); last if (!$retval); return -1 @@ -977,7 +1167,7 @@ sub WaitForSlivers($$$@) if (!defined($sliver_urn)); if (!defined($node)) { - print STDERR "WaitForSlivers: ". + print STDERR "*** WaitForSlivers: ". "Could not locate node $vname in $experiment\n"; return -1; } @@ -994,6 +1184,12 @@ sub WaitForSlivers($$$@) $nodemap{$node->external_resource_id()} = $node; } $node->Refresh(); + + # XXX Boot mode; simple vs full. Have not decided on the + # proper interface for this yet. + my $bootmode = $experiment->GetEnvVariable("${vname}_BOOTMODE"); + # Stash it in the node object. + $node->_bootmode($bootmode || ""); } } @@ -1012,7 +1208,7 @@ sub WaitForSlivers($$$@) if ($resource->SliverStatus($user, \$ref) != 0) { # Tell the parent to keep trying. - return 0 + return 1 if ($resource->last_rpc_error() && $resource->last_rpc_error() == GENIRESPONSE_BUSY()); @@ -1027,7 +1223,11 @@ sub WaitForSlivers($$$@) my $val = $ref->{'details'}->{$key}; my ($status, $node); - if ($resource->ManagerVersion() == 1.0) { + if ($resource->ManagerApi() eq "AM") { + $node = $nodemap{$key}; + $status = $val->{'status'}; + } + elsif ($resource->ManagerVersion() == 1.0) { $node = $nodemap{$key}; $status = $val; } @@ -1053,6 +1253,12 @@ sub WaitForSlivers($$$@) # print statement would be repeated. # Normal node waiting at this point, for ISUP to arrive. $ready++; + + # Basic mode; node is immediately ISUP. + if (($node->_bootmode()) eq "basic" && + $node->eventstate() ne TBDB_NODESTATE_ISUP()) { + $node->SetEventState(TBDB_NODESTATE_ISUP()); + } } elsif ($status eq "failed") { # print statement would be repeated. @@ -1140,6 +1346,332 @@ sub WaitForSlivers($$$@) return 0; } +# +# Reboot (restart) slivers. +# +sub RestartNodes($$@) +{ + my ($user, $verbose, @nodes) = @_; + + my %resources = (); + my @todo = (); + + # + # Figure out which resources and slivers, from the set of nodes. + # + foreach my $node (@nodes) { + my $index = $node->external_resource_index(); + my $resource = GeniResource->Lookup($index); + my $urn = $node->external_resource_id(); + + if (!defined($resource)) { + print STDERR "*** RestartSlivers: No resource for $node\n"; + return -1; + } + if (!exists($resources{"$index"})) { + $resources{"$index"} = []; + } + push(@{ $resources{"$index"} }, $urn); + + # XXX Move this. Force the node into the new state. + $node->SetEventState(TBDB_NODESTATE_SHUTDOWN()); + } + foreach my $index (keys(%resources)) { + my $resource = GeniResource->Lookup($index); + my $urns = $resources{"$index"}; + + push(@todo, [$resource, $urns]); + } + + # + # Start slivers in parallel. + # + my @results = (); + my $coderef = sub { + my ($argref) = @_; + my ($resource, $urns) = @{ $argref }; + my @urns = @{ $urns }; + + if ($resource->ManagerApi() eq "AM") { + print STDERR "Skipping restart on AM $resource\n"; + return 0; + } + + while (1) { + print STDERR "Restarting slivers on $resource\n"; + my $retval = $resource->RestartSliver($user, @urns); + last + if (!$retval); + return -1 + if (!$resource->last_rpc_error() || + $resource->last_rpc_error() != GENIRESPONSE_BUSY()); + + sleep(10); + } + return 0; + }; + print STDERR "Restarting slivers in parallel ...\n"; + + if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @todo)) { + print STDERR "*** StartSlivers: Internal error starting slivers.\n"; + return -1; + } + # + # Check the exit codes. Eventually return specific error info. + # + my $errors = 0; + my $count = 0; + my @tmp = (); + my @failed = (); + foreach my $result (@results) { + my ($resource) = @{ $todo[$count] }; + + if ($result != 0) { + print STDERR "*** Error restarting slivers for $resource\n"; + $errors++; + push(@failed, $resource); + } + else { + # + # ParRun does a fork; so need to refresh the resource object + # to sync it to the DB. + # + if ($resource->Refresh()) { + print STDERR + "*** RestartSlivers: Error synchronizing $resource\n"; + $errors++; + } + push(@tmp, $resource); + } + $count++; + } + return $errors; +} + +# +# Wait for specific nodes (slivers). +# +sub WaitForNodes($$$@) +{ + my ($user, $verbose, $options, @nodes) = @_; + my %nodemap = (); + my %resources = (); + my @todo = (); + + # + # Figure out which resources from the set of nodes. + # + foreach my $node (@nodes) { + $node->Refresh(); + + my $index = $node->external_resource_index(); + my $resource = GeniResource->Lookup($index); + my $urn = $node->external_resource_id(); + my $vname = $node->vname(); + + if (!defined($resource)) { + print STDERR "*** RestartSlivers: No resource for $node\n"; + return -1; + } + if (!exists($resources{"$index"})) { + push(@todo, $resource); + $resources{"$index"} = $resource; + } + # Needed below. + $nodemap{$urn} = $node; + + # XXX Boot mode; simple vs full. Have not decided on the + # proper interface for this yet. + my $experiment = $node->Reservation(); + if (!defined($experiment)) { + $node->_bootmode("basic"); + } + else { + my $bootmode = $experiment->GetEnvVariable("${vname}_BOOTMODE"); + # Stash it in the node object. + $node->_bootmode($bootmode || ""); + } + } + print STDERR "Waiting for protogeni nodes ...\n"; + + # + # Now we use parrun again to get the sliver status. We are waiting + # for them to become ready so we can send them into ISUP. + # + my $coderef = sub { + my ($resource) = @_; + my $ref; + my $failed = 0; + my $ready = 0; + my $count = 0; + + print STDERR "Getting sliver status for $resource\n"; + + if ($resource->SliverStatus($user, \$ref) != 0) { + # Tell the parent to keep trying. + return 1 + if ($resource->last_rpc_error() && + $resource->last_rpc_error() == GENIRESPONSE_BUSY()); + + print STDERR "Error getting sliver status for $resource\n"; + # Tell the parent error. + return -1; + } + print STDERR Dumper($ref) + if ($verbose); + + foreach my $key (keys(%{ $ref->{'details'} })) { + my $val = $ref->{'details'}->{$key}; + my ($status, $node); + + if ($resource->ManagerApi() eq "AM") { + $node = $nodemap{$key}; + $status = $val->{'status'}; + } + elsif ($resource->ManagerVersion() == 1.0) { + $node = $nodemap{$key}; + $status = $val; + } + elsif ($resource->ManagerVersion() == 2.0) { + $node = $nodemap{$key}; + $status = $val->{'status'}; + } + else { + print STDERR + "*** WaitForNodes: Unknown version on $resource\n"; + next; + } + + if (!defined($node)) { + # Not a node we are waiting for. + next; + } + # State was changed in a another process. + $node->Refresh(); + + $count++; + if ($status eq "ready") { + # print statement would be repeated. + # Normal node waiting at this point, for ISUP to arrive. + $ready++; + + # Basic mode; node is immediately ISUP. + if (($node->_bootmode()) eq "basic" && + $node->eventstate() ne TBDB_NODESTATE_ISUP()) { + $node->SetEventState(TBDB_NODESTATE_ISUP()); + } + } + elsif ($status eq "failed") { + # print statement would be repeated. + # We want to do something here, to avoid waiting + # for something that failed, but might not report in any + # status. os_setup might wait a really long time for the + # timeout, and that is silly. + # + if ($node->eventstate() ne TBDB_NODESTATE_TBFAILED()) { + $node->SetEventState(TBDB_NODESTATE_TBFAILED()); + } + $failed++; + } + } + # Tell the parent to stop if all nodes are ready. + if ($ref->{'status'} eq "ready" || ($failed + $ready) == $count) { + return 0; + } + # Tell the parent not ready. + return 1; + }; + + print STDERR "Waiting for all protogeni nodes in parallel ...\n"; + + while (@todo) { + my @results = (); + + if (ParRun(undef, \@results, $coderef, @todo)) { + print STDERR + "*** WaitForNodes: Internal error waiting on slivers.\n"; + return -1; + } + + my @tmp = (); + while (@results) { + my $result = pop(@results); + my $resource = pop(@todo); + if ($result > 0) { + push(@tmp, $resource); + } + } + @todo = @tmp; + + sleep(15) + if (@todo); + } + return 0; +} + +# +# Release outstanding tickets, as for a swapmod error. +# +sub ReleaseTickets($$) +{ + my ($experiment, $user) = @_; + + # + # Get the resource objects. + # + my @resources = GeniResource->LookupAll($experiment); + return 0 + if (! @resources); + # Skip resources with no ticket. + my @tmp = (); + foreach my $resource (@resources) { + push(@tmp, $resource) + if ($resource->newticket_idx()); + } + @resources = @tmp; + return 0 + if (!@resources); + + # + # Delete tickets in parallel. + # + my @results = (); + my $coderef = sub { + my ($resource) = @_; + + print STDERR "Releasing ticket for $resource\n"; + + while (1) { + my $retval = $resource->ReleaseTicket($user); + last + if (!$retval); + return -1 + if (!$resource->last_rpc_error() || + ($resource->last_rpc_error() != GENIRESPONSE_BUSY() && + $resource->last_rpc_error() != GENIRESPONSE_SEARCHFAILED())); + + sleep(10); + } + return 0; + }; + print STDERR "Releasing outstanding tickets in parallel ...\n"; + + if (ParRun({'maxwaittime' => 600}, \@results, $coderef, @resources)) { + print STDERR + "*** ReleaseTickets: Internal error releasing tickets.\n"; + # + # Need to be careful here; some of the tickets might have been + # deleted, and that happened in the child of a fork. Sync with + # the DB so the parent sees the current state. + # + map { $_->Refresh() } @resources; + return -1; + } + # Ditto, above. + map { $_->Refresh() } @resources; + return 0; +} + # # Delete all slivers for an Experiment. # @@ -1169,7 +1701,8 @@ sub DeleteAllSlivers($$) if (!$retval); return -1 if (!$resource->last_rpc_error() || - $resource->last_rpc_error() != GENIRESPONSE_BUSY()); + ($resource->last_rpc_error() != GENIRESPONSE_BUSY() && + $resource->last_rpc_error() != GENIRESPONSE_SEARCHFAILED())); sleep(10); } @@ -1226,4 +1759,163 @@ sub LookupProxyNode($) return GeniEmulab::CreatePhysNode($node_urn); } +# +# Generate a new fragment. +# +sub CreateNewRspec() +{ + my $doc = XML::LibXML::Document->new(); + + my $root = $doc->createElement("rspec"); + $root->setAttribute("type", "request"); + $root->setAttribute("generated_by", "libvtop"); + $root->setAttribute("xmlns", + 'http://www.protogeni.net/resources/rspec/0.2'); + $root->setAttribute("xmlns:xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + $root->setAttribute("xsi:schemaLocation", + "http://www.protogeni.net/resources/rspec/0.2 ". + "http://www.protogeni.net/resources/rspec/0.2/request.xsd"); + + $doc->setDocumentElement($root); + return $doc; +} + +# +# Combine a bunch of documents into a single rspec. +# +sub CombineRspecDocs(@) +{ + my $rspec = CreateNewRspec(); + return -1 + if (!defined($rspec)); + my $root = $rspec->getDocumentElement(); + + # These are probably not needed, but nice to do. + my $creation = time(); + my $expiration = $creation + (3600 * 6); + $root->setAttribute("generated", + POSIX::strftime("20%y-%m-%dT%H:%M:%S", + gmtime($creation))); + $root->setAttribute("valid_until", + POSIX::strftime("20%y-%m-%dT%H:%M:%S", + gmtime($expiration))); + foreach my $element (@_) { + $root->appendChild($element); + } + return $rspec; +} + +# +# Creates a child node with name "nodeName" whose parent is "parent" +# in the XML document "document" +# +sub addChildToRspec($$$) +{ + my ($doc, $root, $name) = @_; + my $newnode = $doc->createElement($name); + $root->appendChild($newnode); + return $newnode; +} + +# +# Add a node to the fragment. Return the element. This creates a 0.2 rspec +# at the moment. Eventually replace with the code in libvtop that creates +# version 2, but not until all sites are running version 2. The libvtop code +# will need some minor changes though. +# +sub AddNodeToRspec($$) +{ + my ($doc, $node) = @_; + my $root = $doc->getDocumentElement(); + my ($authority,undef,$node_id) = GeniHRN::Parse($node->{'request_urn'}); + + my $rspecnode = addChildToRspec($doc, $root, 'node'); + $rspecnode->setAttribute('virtual_id', $node->{'virtual_id'}); + $rspecnode->setAttribute('component_manager_urn', $node->{'manager_urn'}); + if ($node_id ne "*") { + $rspecnode->setAttribute('component_urn', $node->{'request_urn'}); + } + if (exists($node->{'virtualization_type'})) { + $rspecnode->setAttribute('virtualization_type', + $node->{'virtualization_type'}); + } + if (exists($node->{'virtualization_subtype'})) { + $rspecnode->setAttribute('virtualization_subtype', + $node->{'virtualization_subtype'}); + } + if (exists($node->{'exclusive'})) { + $rspecnode->setAttribute('exclusive', $node->{'exclusive'}); + } + if (exists($node->{'node_type'})) { + my $tmp = addChildToRspec($doc, $rspecnode, 'node_type'); + $tmp->setAttribute("type_name", $node->{'node_type'}); + $tmp->setAttribute("type_slots", $node->{'type_slots'}); + } + if (exists($node->{'disk_image'})) { + my $osname = $node->{'disk_image'}->{'name'}; + my $osurn = GeniHRN::Generate($authority, "image", "emulab-ops"); + $osurn .= "//" . $osname; + + my $tmp = addChildToRspec($doc, $rspecnode, 'disk_image'); + $tmp->setAttribute("name", $osurn); + } + # Add interfaces to the node + if (exists($node->{'interfaces'})) { + foreach my $interface (@{$node->{'interfaces'}}) { + my $virtid = $interface->{'virtual_id'}; + my $compid = $interface->{'component_id'}; + + my $tmp = addChildToRspec($doc, $rspecnode, 'interface'); + $tmp->setAttribute('virtual_id', $virtid); + if (defined($compid)) { + $tmp->setAttribute('component_id', $compid); + } + } + } + return $rspecnode; +} + +sub AddLinkToRspec($$) +{ + my ($doc, $link) = @_; + my $root = $doc->getDocumentElement(); + + my $rspeclink = addChildToRspec($doc, $root, 'link'); + $rspeclink->setAttribute('virtual_id', $link->{'virtual_id'}); + if (exists($link->{'virtualization_type'})) { + $rspeclink->setAttribute('virtualization_type', + $link->{'virtualization_type'}); + } + + if (0) { + my $linktype = addChildToRspec($doc, $rspeclink, 'link_type'); + $linktype->setAttribute('name', $link->{'link_type'}); + } + else { + $rspeclink->setAttribute('link_type', $link->{'link_type'}); + } + my $bandwidth = addChildToRspec($doc, $rspeclink, 'bandwidth'); + $bandwidth->appendText($link->{'capacity'}); + + if (exists($link->{'manager_urn'})) { + foreach my $urn (@{ $link->{'manager_urn'} }) { + my $tmp = addChildToRspec($doc, $rspeclink, 'component_manager'); + $tmp->setAttribute('name', $urn); + } + } + # Add the interface refs + foreach my $ifaceref (@{ $link->{'interface_ref'} }) { + my $nodeid = $ifaceref->{'virtual_node_id'}; + my $ifaceid = $ifaceref->{'virtual_interface_id'}; + my $tmp = addChildToRspec($doc, $rspeclink, 'interface_ref'); + $tmp->setAttribute("virtual_node_id", $nodeid); + $tmp->setAttribute("virtual_interface_id", $ifaceid); + if (exists($ifaceref->{'tunnel_ip'})) { + $tmp->setAttribute("tunnel_ip", $ifaceref->{'tunnel_ip'}); + } + } + return $rspeclink; +} + 1; diff --git a/protogeni/lib/GeniAggregate.pm.in b/protogeni/lib/GeniAggregate.pm.in index cec5ef1fb..176502b21 100755 --- a/protogeni/lib/GeniAggregate.pm.in +++ b/protogeni/lib/GeniAggregate.pm.in @@ -239,6 +239,17 @@ sub GetCertificate($) { return $_[0]->{'CERTIFICATE'}; } # An alias so that slivers look like aggregates. sub resource_type($) { return field($_[0], "type"); } +# A place to stash a temporary rspec. +sub rspec($;$) +{ + my ($self, $rspec) = @_; + + if (defined($rspec)) { + $self->{'RSPEC'} = $rspec; + } + return $self->{'RSPEC'}; +} + # Return the URN. sub urn($) { @@ -770,6 +781,8 @@ sub Start($$$) return -1; } if ($reservation->SameExperiment($experiment)) { + my $vnode; + # # Since this is an aggregate, some slivers may already be # in the started state. Skip those, unless doing a restart. @@ -778,14 +791,28 @@ sub Start($$$) if ($sliver->state() eq "started" && !$restart); if ($node->isvirtnode()) { - $vnodes{$node->node_id} = $node; - - # A virtnode on a shared physical node needs nothing else. - next - if ($node->sharing_mode()); - - # But if non-shared, have to make sure that the phys node - # gets loaded. + # A virtnode on a shared physical node needs reboot or setup + if ($node->sharing_mode()) { + if ($restart && $sliver->state() eq "started") { + $reboots{$node->node_id} = $node; + } + else { + $vnodes{$node->node_id} = $node; + } + next; + } + # See below. + $vnode = $node; + + # + # Now it gets messy. Do not want to mess with the physnode + # if its running other vnodes, and we just need to fire up + # a new one. But if the physnode is going to get rebooted, + # then there is no need to do anything with the vnodes; they + # will boot up with the physnode. + # + # But, have to make sure that the phys node gets setup. + # my $physnodeid = $node->phys_nodeid(); next if (exists($poweron{$physnodeid}) || @@ -798,21 +825,22 @@ sub Start($$$) } } # - # Look to see if local physical node was stopped (powered off). + # If the node is not imageable, then there is not much to + # do except turn it on or reboot it. I am assuming that a + # a non imageable node is always in raw mode. # - if (!$node->isremotenode() && - ($sliver->state() eq "stopped")) { - $poweron{$node->node_id} = $node; - } - else { - # node_reboot is smart enough to know that if a pnode - # is rebooted it can ignore the vnodes on it, so do - # not optimize this here. - $reboots{$node->node_id} = $node; + if (!$node->imageable()) { + if ($sliver->state() eq "stopped") { + $poweron{$node->node_id} = $node; + } + else { + $reboots{$node->node_id} = $node; + } + next; } - next - if (!$node->imageable()); - + # + # See if the node is running the requested OS. + # my $osinfo = OSinfo->Lookup($node->def_boot_osid()); if (!defined($osinfo)) { print STDERR "Could not get osinfo for $node\n"; @@ -852,6 +880,14 @@ sub Start($$$) $reloads{$image->imageid()} = [ ]; } push(@{ $reloads{$image->imageid()} }, $node); + + # Reload means reboot or power on. + if (!defined($vnode) && $sliver->state() eq "stopped") { + $poweron{$node->node_id} = $node; + } + else { + $reboots{$node->node_id} = $node; + } } else { # @@ -861,6 +897,33 @@ sub Start($$$) print STDERR " Could not os_select $node to $osinfo\n"; return -1; } + # + # If the node is going to get rebooted, then do not need + # to worry about the vnodes on it. But if the node is ready + # to go, then we have to do the vnodes. Remember, we do not + # reboot the physnode since we might only be adding a new node + # in which case, a full reboot is wrong. + # + if (! $node->IsUp()) { + if ($sliver->state() eq "stopped" && !defined($vnode)) { + $poweron{$node->node_id} = $node; + } + else { + $reboots{$node->node_id} = $node; + } + } + elsif ($restart && !defined($vnode)) { + # Just a physnode that needs restarting. + $reboots{$node->node_id} = $node; + } + elsif (defined($vnode)) { + if ($sliver->state() eq "started") { + $reboots{$vnode->node_id} = $vnode; + } + else { + $vnodes{$vnode->node_id} = $vnode; + } + } } } else { diff --git a/protogeni/lib/GeniCM.pm.in b/protogeni/lib/GeniCM.pm.in index f6e911501..f372a709f 100644 --- a/protogeni/lib/GeniCM.pm.in +++ b/protogeni/lib/GeniCM.pm.in @@ -150,7 +150,7 @@ sub Resolve($) undef, "Nothing here by that name"); } - my $rspec = GetAdvertisement(0, $node->node_id(), "0.1"); + my $rspec = GetAdvertisement(0, $node->node_id(), "0.1", undef); if (! defined($rspec)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "Could not start avail"); @@ -237,10 +237,29 @@ sub DiscoverResourcesAux($$$) } } + # + # See if one of the credentials is a slice credential. If it is, and + # that slice is active, pass it to ptopgen so that it includes the current + # resources as available. + # + my $experiment = undef; + foreach my $credential (@$credentials) { + my ($auth, $type, $id) = GeniHRN::Parse($credential->target_urn()); + if ($type eq "slice") { + # Might not exist here yet. + my $slice = GeniSlice->Lookup($credential->target_urn()); + if (defined($slice)) { + # See if the local experiment exists yet. + $experiment = Experiment->Lookup($slice->uuid()); + } + last; + } + } + # # Acquire the advertisement from ptopgen and compress it if requested. # - my $xml = GetAdvertisement($available, undef, "0.2"); + my $xml = GetAdvertisement($available, undef, "0.2", $experiment); if (! defined($xml)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "Could not start avail"); @@ -258,12 +277,16 @@ sub DiscoverResourcesAux($$$) # # Use ptopgen in xml mode to spit back an xml file. # -sub GetAdvertisement($$$) +sub GetAdvertisement($$$$) { - my ($available, $pc, $version) = @_; + my ($available, $pc, $version, $experiment) = @_; my $xml = undef; my $invocation = "$PTOPGEN -x -g $version -r -p GeniSlices"; + if (defined($experiment)) { + my $eid = $experiment->eid(); + $invocation .= " -e $eid"; + } $invocation .= " -a" unless $available; if (defined($pc)) { $invocation .= " -1 $pc"; @@ -1535,6 +1558,7 @@ sub SliverWorkAux($$$$$$$) my $response; my $ticket; my $rspec; + my $oldmanifest; require Interface; # V2 API support. @@ -1665,10 +1689,18 @@ sub SliverWorkAux($$$$$$$) print GeniXML::Serialize($rspec); + # The Manifest starts out as a copy of the rspec. + my $manifest = $rspec->cloneNode(1); + # # Find current slivers and save. # if (defined($aggregate)) { + $oldmanifest = $aggregate->GetManifest(0); + if (!defined($oldmanifest)) { + $message = "Internal error getting manifest"; + goto bad; + } my @slivers; if ($aggregate->SliverList(\@slivers) != 0) { $message = "Could not get sliverlist for $aggregate"; @@ -2040,8 +2072,22 @@ sub SliverWorkAux($$$$$$$) } # Already in the aggregate? - next - if (grep {$_ eq $virtual_id} keys(%nodemap)); + if (grep {$_ eq $virtual_id} keys(%nodemap)) { + # + # Need to find the old manifest entry and make sure it + # gets into the new manifest. + # + if (defined($oldmanifest)) { + my $oldnode = GeniXML::GetNodeByVirtualId($virtual_id, + $oldmanifest); + my $curnode = GeniXML::GetNodeByVirtualId($virtual_id, + $manifest); + if (defined($oldnode) && defined($curnode)) { + GeniXML::ReplaceNode($curnode, $oldnode); + } + } + next; + } my $node = GeniUtil::LookupNode($resource_id); if (!defined($node)) { @@ -2080,6 +2126,13 @@ sub SliverWorkAux($$$$$$$) } push(@plabnodes, $vnode); } + # Store the updated rspec for later. + goto bad + if ($sliver->UpdateRspec($ref)); + + # And store into the new manifest. + my $oldnode = GeniXML::GetNodeByVirtualId($virtual_id, $manifest); + GeniXML::ReplaceNode($oldnode, $ref); } # @@ -2126,8 +2179,22 @@ sub SliverWorkAux($$$$$$$) # Do not worry about modifying a link that is setup, unless a tunnel. # Need to deal with this later. if (grep {$_ eq $linkname} keys(%linkmap)) { - next - if ($is_tunnel); + if (!$is_tunnel) { + # + # Need to find the old manifest entry and make sure it + # gets into the new manifest. + # + if (defined($oldmanifest)) { + my $oldlink = GeniXML::GetLinkByVirtualId($linkname, + $oldmanifest); + my $curlink = GeniXML::GetLinkByVirtualId($linkname, + $manifest); + if (defined($oldlink) && defined($curlink)) { + GeniXML::ReplaceNode($curlink, $oldlink); + } + } + next; + } # # Gack, we have to recreate all the tunnel physical state, but @@ -2201,7 +2268,7 @@ sub SliverWorkAux($$$$$$$) $message = "Could not set aggregate for $tunnel to $aggregate"; goto bad; } - next; + goto manifest; } my $linkaggregate = GeniAggregate::Link->Create($slice, $owner, @@ -2340,6 +2407,11 @@ sub SliverWorkAux($$$$$$$) goto bad; } } + manifest: + # And store into the new manifest. + my $oldlink = GeniXML::GetLinkByVirtualId($linkname, $manifest); + GeniXML::ReplaceNode($oldlink, $linkref); + } skiplinks: # @@ -2445,14 +2517,12 @@ sub SliverWorkAux($$$$$$$) } } - # The Manifest. - my $manifest = GeniXML::Serialize($rspec); - # # Move this elsewhere. # + my $manifest_string = GeniXML::Serialize($manifest); DBQueryWarn("replace into geni_manifests set ". - " manifest=". DBQuoteSpecial($manifest) . ", " . + " manifest=". DBQuoteSpecial($manifest_string) . ", " . " idx=NULL, slice_uuid='$slice_uuid', created=now()"); if (GeniUsage->NewManifest($aggregate, $rspec)) { @@ -2485,7 +2555,7 @@ sub SliverWorkAux($$$$$$$) $slice->UnLock(); return GeniResponse->Create(GENIRESPONSE_SUCCESS, [$sliver_credential->asString(), - $manifest]); + $manifest_string]); } # # Free any slivers that were no longer wanted. @@ -2505,9 +2575,9 @@ sub SliverWorkAux($$$$$$$) if ($v2) { return GeniResponse->Create(GENIRESPONSE_SUCCESS, [$sliver_credential->asString(), - $manifest]); + $manifest_string]); } else { - return GeniResponse->Create(GENIRESPONSE_SUCCESS, $manifest); + return GeniResponse->Create(GENIRESPONSE_SUCCESS, $manifest_string); } bad: diff --git a/protogeni/lib/GeniCMV2.pm.in b/protogeni/lib/GeniCMV2.pm.in index 0ee9bc7ee..d019af1bf 100755 --- a/protogeni/lib/GeniCMV2.pm.in +++ b/protogeni/lib/GeniCMV2.pm.in @@ -125,7 +125,7 @@ sub Resolve($) if ($type eq "node") { my $node = $object; - my $rspec = GeniCM::GetAdvertisement(0, $node->node_id(), "0.1"); + my $rspec = GeniCM::GetAdvertisement(0, $node->node_id(), "0.1", undef); if (! defined($rspec)) { return GeniResponse->Create(GENIRESPONSE_ERROR, undef, "Error getting advertisement"); @@ -585,6 +585,8 @@ sub SliverAction($$$$$) { my ($action, $slice_urn, $sliver_urns, $credentials, $manifest) = @_; my $response; + + print STDERR "fooey\n"; if (! (defined($credentials) && (defined($slice_urn) || defined($sliver_urns)))) { diff --git a/protogeni/lib/GeniResource.pm.in b/protogeni/lib/GeniResource.pm.in index c888cd381..4f14b3782 100644 --- a/protogeni/lib/GeniResource.pm.in +++ b/protogeni/lib/GeniResource.pm.in @@ -24,6 +24,7 @@ use GeniTicket; use GeniCredential; use GeniAuthority; use GeniComponent; +use GeniUtil; use GeniUser; use GeniHRN; use GeniXML; @@ -49,44 +50,67 @@ my $BOSSNODE = "@BOSSNODE@"; my $OURDOMAIN = "@OURDOMAIN@"; my $SACERT = "$TB/etc/genisa.pem"; +# Cache of objects +my %resources = (); +BEGIN { use GeniUtil; GeniUtil::AddCache(\%resources); } + # # Lookup by uuid. # sub Lookup($$;$) { my ($class, $arg1, $arg2) = @_; - my $query_result; + my $idx; if (!defined($arg2)) { if ($arg1 =~ /^(\d*)$/) { - $query_result = - DBQueryWarn("select * from geni_resources ". - "where idx='$arg1'"); + $idx = $arg1; } else { return undef; } } elsif (($arg1 =~ /^\d*$/) && GeniHRN::IsValid($arg2)) { - $query_result = - DBQueryWarn("select * from geni_resources ". + my $query_result = + DBQueryWarn("select idx from geni_resources ". "where exptidx='$arg1' and manager_urn='$arg2'"); + + return undef + if (!$query_result || !$query_result->numrows); + + ($idx) = $query_result->fetchrow_array(); } else { return undef; } + + return $resources{"$idx"} + if (exists($resources{"$idx"})); + + my $query_result = + DBQueryWarn("select * from geni_resources where idx='$idx'"); + return undef if (!$query_result || !$query_result->numrows); my $self = {}; $self->{'DBROW'} = $query_result->fetchrow_hashref(); $self->{'NEWTICKET'} = undef; - $self->{'manager_version'} = undef; + $self->{'MANIFEST'} = undef; + $self->{'RSPEC'} = undef; + $self->{'NEWRSPEC'} = undef; + $self->{'MODIFIED'} = 1; + $self->{'manager_version'} = undef; + $self->{'manager_apilevel'} = undef; + $self->{'manager_api'} = undef; $self->{'last_rpc_error'} = 0; $self->{'last_rpc_value'} = undef; $self->{'last_rpc_output'} = undef; bless($self, $class); + # Add to cache. + $resources{"$idx"} = $self; + return $self; } @@ -215,11 +239,19 @@ sub credential_idx($) { return field($_[0], "credential_idx"); } sub manifest_idx($) { return field($_[0], "manifest_idx"); } sub ticket_idx($) { return field($_[0], "ticket_idx"); } sub newticket_idx($) { return field($_[0], "newticket_idx"); } +sub rspec_idx($) { return field($_[0], "rspec_idx"); } sub manager_version($) { return $_[0]->{'manager_version'}; } +sub manager_apilevel($) { return $_[0]->{'manager_apilevel'}; } +sub manager_api($) { return $_[0]->{'manager_api'}; } sub last_rpc_error($) { return $_[0]->{'last_rpc_error'}; } sub last_rpc_output($) { return $_[0]->{'last_rpc_output'}; } sub last_rpc_value($) { return $_[0]->{'last_rpc_value'}; } sub manifest_string($) { return $_[0]->{'MANIFESTSTR'}; } +sub rspec_string($) { return $_[0]->{'RSPECSTR'}; } +sub modified($) { return $_[0]->{'MODIFIED'}; } +sub setmodified($$) { $_[0]->{'MODIFIED'} = $_[1]; } +sub stashrspec($$) { $_[0]->{'NEWRSPEC'} = $_[1]; } +sub stashedrspec($$) { return $_[0]->{'NEWRSPEC'}; } # # Delete a resource record from the DB. @@ -236,6 +268,7 @@ sub Delete($) "where idx='$idx'") or return -1; + delete($resources{"$idx"}); return 0; } @@ -273,6 +306,8 @@ sub Refresh($) $self->{'DBROW'} = $query_result->fetchrow_hashref(); $self->{'NEWTICKET'} = undef; $self->{'MANIFEST'} = undef; + $self->{'RSPEC'} = undef; + return 0; } @@ -366,6 +401,78 @@ sub HaveTicket($) return $self->newticket_idx(); } +# +# Return the rspec +# +sub Rspec($) +{ + my ($self) = @_; + + return $self->{'RSPEC'} + if (defined($self->{'RSPEC'})); + + if (!$self->rspec_idx()) { + print STDERR "*** No rspec defined for $self.\n"; + return undef; + } + my $rspec_idx = $self->rspec_idx(); + + my $query_result = + DBQueryWarn("select rspec from geni_rspecs ". + "where idx=$rspec_idx"); + if (!$query_result || !$query_result->numrows) { + print STDERR "Could not locate rspec for $self\n"; + return undef; + } + my ($rspec_string) = $query_result->fetchrow_array(); + my $rspec = GeniXML::Parse($rspec_string); + if (!defined($rspec)) { + print STDERR "Could not parse rspec for $self\n"; + return undef; + } + $self->{'RSPEC'} = $rspec; + $self->{'RSPECSTR'} = $rspec_string; + return $rspec; +} +sub HaveRspec($) +{ + my ($self) = @_; + + return $self->rspec_idx(); +} +sub UpdateRspec($$) +{ + my ($self, $rspec) = @_; + + my $rspec_idx = $self->rspec_idx(); + my $rspec_string = (ref($rspec) ? GeniXML::Serialize($rspec) : $rspec); + + if ($rspec_idx) { + my $query_result = + DBQueryWarn("update geni_rspecs set ". + " rspec=". DBQuoteSpecial($rspec_string) . ", " . + " created=now() ". + "where idx=$rspec_idx"); + return -1 + if (!defined($query_result)); + } + else { + my $query_result = + DBQueryWarn("insert into geni_rspecs set idx=NULL, ". + " rspec=". DBQuoteSpecial($rspec_string) . ", " . + " created=now() "); + return -1 + if (!defined($query_result)); + + $rspec_idx = $query_result->insertid; + $self->Update({"rspec_idx" => $rspec_idx}); + } + + $self->{'RSPEC'} = $rspec; + $self->{'RSPECSTR'} = $rspec_string; + return 0; +} + sub Manifest($) { my ($self) = @_; @@ -459,106 +566,35 @@ sub ManagerVersion($) return undef; } } - $self->{'manager_version'} = $authority->Version(); + $self->{'manager_version'} = $authority->Version(); + $self->{'manager_apilevel'} = $authority->ApiLevel(); + $self->{'manager_api'} = $authority->Api(); return $self->{'manager_version'}; } - -# -# Convert an rspec structure (as from libvtop) into proper format. -# This is horrible. -# -sub ConvertRspec($) +# Ditto the API level. +sub ManagerApiLevel($) { - my ($rspec) = @_; - my $template; + my ($self) = @_; - if (1) { - $template = - "". - ""; - } - else { - $template = - " ". - ""; - } - - my $new = GeniXML::Parse($template); - if (!defined($new)) { - print STDERR "ConvertRspec: Could not create new document\n"; - return undef; - } - # ??? - my $root = $new; + return $self->manager_apilevel() + if (defined($self->manager_apilevel())); - foreach my $ref (@{ $rspec->{'node'} }) { - my $node = GeniXML::AddElement("node", $root); - - foreach my $key ('virtual_id', 'component_urn', 'exclusive', - 'component_manager_urn', 'component_manager_uuid', - 'virtualization_type', 'virtualization_subtype', - 'startup_command', 'tarfiles') { - GeniXML::SetText($key, $node, $ref->{$key}) - if (exists($ref->{$key})); - } - foreach my $ifaceref (@{ $ref->{'interface'} }) { - my $iface = GeniXML::AddElement("interface", $node); - foreach my $key ('virtual_id', 'component_id') { - GeniXML::SetText($key, $iface, $ifaceref->{$key}) - if (exists($ifaceref->{$key})); - } - } - if (exists($ref->{'disk_image'})) { - my $osref = $ref->{'disk_image'}; - my $os = GeniXML::AddElement("disk_image", $node); + return undef + if (!defined($self->ManagerVersion())); - foreach my $key ('name') { - GeniXML::SetText($key, $os, $osref->{$key}) - if (exists($osref->{$key})); - } - } - } - if (exists($rspec->{'link'})) { - foreach my $linkref (@{ $rspec->{'link'} }) { - my $link = GeniXML::AddElement("link", $root); - foreach my $key ('virtual_id', 'link_type', - 'virtualization_type') { - GeniXML::SetText($key, $link, $linkref->{$key}) - if (exists($linkref->{$key})); - } - # These are special; see libvtop. - foreach my $key ('bandwidth', 'latency', 'packet_loss') { - if (exists($linkref->{$key})) { - my ($val) = @{ $linkref->{$key} }; + return $self->manager_apilevel(); +} +sub ManagerApi($) +{ + my ($self) = @_; - GeniXML::SetText($key, $link, $val); - } - } - foreach my $ifaceref (@{ $linkref->{'interface_ref'} }) { - my $iface = GeniXML::AddElement("interface_ref", $link); - - foreach my $key ('virtual_node_id', 'virtual_interface_id', - 'tunnel_ip') { - GeniXML::SetText($key, $iface, $ifaceref->{$key}) - if (exists($ifaceref->{$key})); - } - } - foreach my $cmref (@{ $linkref->{'component_manager'} }) { - my $cm = GeniXML::AddElement("component_manager", $link); - - foreach my $key ('id') { - GeniXML::SetText($key, $cm, $cmref->{$key}) - if (exists($cmref->{$key})); - } - } - } - } - return $new; + return $self->manager_api() + if (defined($self->manager_api())); + + return undef + if (!defined($self->ManagerVersion())); + + return $self->manager_api(); } # @@ -607,6 +643,7 @@ sub GetTicket($$$$) return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); # # Create a Geni user from current user doing the operation. @@ -647,41 +684,53 @@ sub GetTicket($$$$) } } - my $method_name = undef; + my $method_name = "GetTicket"; my $method_args = {}; - # - # Already have a credential for a sliver, then we want to update - # the existing ticket we also have on file, and then redeem that. - # - if (defined($sliver_credential)) { - if ($self->newticket_idx()) { - print STDERR "*** Still have a new ticket defined for $self.\n"; - return -1; - } - if (!$self->ticket_idx()) { - print STDERR "*** No ticket defined for $self.\n"; - return -1; - } - $ticket = GeniTicket->Lookup($self->ticket_idx()); - if (!defined($ticket)) { - print STDERR "*** Could not get ticket for $self.\n"; - return -1; - } - $method_name = "UpdateTicket"; - $method_args->{'ticket'} = $ticket->asString(); - } - else { - $method_name = "GetTicket"; - } $method_args->{'impotent'} = $impotent; $method_args->{'rspec'} = $rspec_string; if ($manager_version == 1.0) { $method_args->{'credential'} = $slice_credential->asString(); + # + # Already have a credential for a sliver, then we want to update + # the existing ticket we also have on file, and then redeem that. + # + if (defined($sliver_credential)) { + if ($self->newticket_idx()) { + print STDERR + "*** Still have a new ticket defined for $self.\n"; + return -1; + } + if (!$self->ticket_idx()) { + print STDERR "*** No ticket defined for $self.\n"; + return -1; + } + $ticket = GeniTicket->Lookup($self->ticket_idx()); + if (!defined($ticket)) { + print STDERR "*** Could not get ticket for $self.\n"; + return -1; + } + $method_name = "UpdateTicket"; + $method_args->{'ticket'} = $ticket->asString(); + } } elsif ($manager_version == 2.0) { $method_args->{'credentials'} = [$slice_credential->asString()]; $method_args->{'slice_urn'} = $slice->urn(); + + if ($self->newticket_idx()) { + $ticket = GeniTicket->Lookup($self->newticket_idx()); + if (!defined($ticket)) { + print STDERR "*** Could not get ticket for $self.\n"; + return -1; + } + $method_name = "UpdateTicket"; + $method_args->{'ticket'} = $ticket->asString(); + } + elsif (defined($sliver_credential)) { + $method_name = "UpdateSliver"; + $method_args->{'sliver_urn'} = $sliver_credential->target_urn(); + } } else { print STDERR "GeniResource::GetTicket Unknown version at $authority\n"; @@ -705,7 +754,7 @@ sub GetTicket($$$$) else { print STDERR "Could not get new ticket from $self. Error: "; } - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } my $newticket = GeniTicket->CreateFromSignedTicket($response->value()); @@ -769,6 +818,8 @@ sub RedeemTicket($$) return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); + my $manager_apilevel= $self->ManagerApiLevel(); # # Create a Geni user from current user doing the operation. @@ -789,6 +840,17 @@ sub RedeemTicket($$) print STDERR "Could not get keys for $geniuser\n"; return -1; } + # + # Why is the key structure different in the AM? + # + if (@keys && $manager_api eq "AM") { + my @tmp = (); + foreach my $key (@keys) { + push(@tmp, $key->{'key'}) + if ($key->{'type'} eq "ssh"); + } + @keys = @tmp; + } # # Generate a slice credential for the user. @@ -846,7 +908,12 @@ sub RedeemTicket($$) $method_args->{'credential'} = $slice_credential->asString(); } elsif ($manager_version == 2.0) { - $method_args->{'credentials'} = [$slice_credential->asString()]; + if (defined($sliver_credential)) { + $method_args->{'credentials'} = [$sliver_credential->asString()]; + } + else { + $method_args->{'credentials'} = [$slice_credential->asString()]; + } $method_args->{'slice_urn'} = $slice->urn(); } else { @@ -873,7 +940,7 @@ sub RedeemTicket($$) else { print STDERR "Could not redeem ticket $ticket. Error: "; } - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } # @@ -887,11 +954,28 @@ sub RedeemTicket($$) # Okay, so that worked. Store the new credential and the ticket and # the manifest. # - my $manifest; - if (!defined($sliver_credential)) { - my $credstring; - ($credstring,$manifest) = @{ $response->value() }; + my ($credstring, $manifest); + # + # Gack, too many variations. + # + if (ref($response->value())) { + ($credstring,$manifest) = @{ $response->value() }; + } + else { + $manifest = $response->value(); + } + + if (defined($credstring)) { + # + # Delete the old credential since it no longer has any value after + # the redeem suceeded; a new credential was issued. + # + if (defined($sliver_credential)) { + $sliver_credential->Delete(); + $self->Update({"credential_idx" => 0}); + $sliver_credential = undef; + } $sliver_credential = GeniCredential->CreateFromSigned($credstring); if (!defined($sliver_credential)) { print STDERR @@ -904,31 +988,47 @@ sub RedeemTicket($$) } $self->Update({"credential_idx" => $sliver_credential->idx()}); } - else { - $manifest = $response->value(); - } if (defined($oldticket)) { $oldticket->Delete(TICKET_DELETED); } - $self->Update({"newticket_idx" => 0, - "ticket_idx" => $ticket->idx()}); + + # For the version 2 API, there is no reason to save the ticket + # after it is redeemed. + if ($manager_version == 1.0) { + $self->Update({"newticket_idx" => 0, + "ticket_idx" => $ticket->idx()}); + } + else { + if ($self->Update({"newticket_idx" => 0}) == 0) { + $ticket->Delete(TICKET_DELETED()); + } + } # # Move this elsewhere. # - my $manifest_idx = ($self->manifest_idx() ? - $self->manifest_idx() : "NULL"); - my $sliver_uuid = $sliver_credential->uuid(); - my $query_result = - DBQueryWarn("replace into geni_manifests set ". + if ($self->manifest_idx()) { + my $manifest_idx = $self->manifest_idx(); + + DBQueryWarn("update geni_manifests set ". " manifest=". DBQuoteSpecial($manifest) . ", " . - " idx=$manifest_idx, slice_uuid='$sliver_uuid', ". - " created=now()"); - return -1 - if (!defined($query_result)); + " created=now() ". + "where idx=$manifest_idx") + or return -1; + } + else { + # We do not care about this uuid in cooked mode. + my $uuid = GeniUtil::NewUUID(); + + my $query_result = + DBQueryWarn("insert into geni_manifests set ". + " manifest=". DBQuoteSpecial($manifest) . ", " . + " idx=NULL, slice_uuid='$uuid', ". + " created=now()"); + return -1 + if (!defined($query_result)); - if (!$self->manifest_idx()) { - $manifest_idx = $query_result->insertid; + my $manifest_idx = $query_result->insertid; $self->Update({"manifest_idx" => $manifest_idx}); } @@ -936,11 +1036,11 @@ sub RedeemTicket($$) } # -# Update the manifest. +# Create a sliver on a level 0 API manager. # -sub GetManifest($$) +sub CreateSliver($$$$) { - my ($self, $user) = @_; + my ($self, $user, $rspec_string, $impotent) = @_; my $response; # Always clear for caller. @@ -950,17 +1050,36 @@ sub GetManifest($$) my $slice = GeniSlice->Lookup($self->slice_idx()); if (!defined($slice)) { - print STDERR "*** GetManifest: No slice for $self\n"; + print STDERR "*** CreateSliver: No slice for $self\n"; return -1; } my $manager_urn = $self->manager_urn(); + # + # Load the SA cert to act as caller context. + # + my $certificate = GeniCertificate->LoadFromFile($SACERT); + if (!defined($certificate)) { + print STDERR "*** Could not load certificate from $SACERT\n"; + return -1; + } + my $context = Genixmlrpc->Context($certificate); + if (!defined($context)) { + print STDERR "*** Could not create context to talk to clearinghouse\n"; + return -1; + } + # + # Set the default RPC context. + # + Genixmlrpc->SetContext($context); + my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); if (!defined($authority)) { print STDERR "*** Could not find $manager_urn at ClearingHouse\n"; return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); # # Create a Geni user from current user doing the operation. @@ -976,65 +1095,131 @@ sub GetManifest($$) return -1; } + my @keys; + if ($geniuser->GetKeyBundle(\@keys) != 0) { + print STDERR "Could not get keys for $geniuser\n"; + return -1; + } # - # Load sliver credential. + # Why is the key structure different in the AM? # - if (!$self->credential_idx()) { - print STDERR "*** No credential defined for $self.\n"; - return -1; + if (@keys && $manager_api eq "AM") { + my @tmp = (); + foreach my $key (@keys) { + push(@tmp, $key->{'key'}) + if ($key->{'type'} eq "ssh"); + } + @keys = @tmp; } - my $sliver_credential = GeniCredential->Lookup($self->credential_idx()); - if (!defined($sliver_credential)) { - print STDERR "*** Could not get sliver credential for $self.\n"; + + # + # Generate a slice credential for the user. + # + my $slice_credential = + GeniCredential->CreateSigned($slice, + $geniuser, + $GeniCredential::LOCALSA_FLAG); + if (!defined($slice_credential)) { + print STDERR + "*** Could not create a slice credential for $slice/$geniuser!\n"; return -1; } # - # Doit it. + # Do it. # - my $method_name = "Resolve"; - my $method_args = { 'credentials' => [$sliver_credential->asString()], - 'urn' => $sliver_credential->target_urn(), - }; + my $method_name = "CreateSliver"; + my $method_args = {}; + my @method_args = ($method_args); + $method_args->{'impotent'} = $impotent; + $method_args->{'rspec'} = $rspec_string; + $method_args->{'keys'} = \@keys; + if ($manager_api eq "AM") { + @method_args = ($slice->urn(), + [$slice_credential->asString()], $rspec_string, + [{"urn" => $geniuser->urn(), "keys" => \@keys}]); + } + elsif ($manager_version == 2.0) { + $method_args->{'credentials'} = [$slice_credential->asString()]; + $method_args->{'slice_urn'} = $slice->urn(); + } + else { + print STDERR + "*** GeniResource::CreateSliver Unknown version at $authority\n"; + return -1; + } $response = Genixmlrpc::CallMethod($authority->url(), $usercontext, - $method_name, $method_args); + $method_name, @method_args); if (!defined($response)) { $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); - print STDERR - "*** Internal error getting manifest for $self\n"; + print STDERR "*** Internal error creating sliver for $self\n"; return -1; } - $self->{'last_rpc_error'} = $response->code(); + $self->{'last_rpc_error'} = $response->code(); $self->{'last_rpc_output'} = $response->output(); $self->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS) { - print STDERR "Could not get manifest for $self. Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR "Could not create sliver on $self Error: "; + print STDERR $response->output() . "\n"; return -1; } # - # Okay, so that worked. Store the new manifest. + # It worked, so mark the expiration time for the resource. # - my $manifest = $response->value()->{'manifest'}; + if ($self->SetExpiration($slice->expires())) { + print STDERR "Could not set expiration for $self!\n"; + } + # + # Okay, so that worked. Store the new credential and the manifest. + # + my ($credstring,$manifest); + if ($manager_api eq "AM") { + $manifest = $response->value(); + } + else { + ($credstring,$manifest) = @{ $response->value() }; + my $sliver_credential = GeniCredential->CreateFromSigned($credstring); + if (!defined($sliver_credential)) { + print STDERR + "** Could not create new credential from $credstring\n"; + return -1; + } + if ($sliver_credential->Store()) { + print STDERR "** Could not store $sliver_credential\n"; + return -1; + } + $self->Update({"credential_idx" => $sliver_credential->idx()}); + } + # # Move this elsewhere. # - my $manifest_idx = ($self->manifest_idx() ? - $self->manifest_idx() : "NULL"); - my $sliver_uuid = $sliver_credential->uuid(); - my $query_result = - DBQueryWarn("replace into geni_manifests set ". + if ($self->manifest_idx()) { + my $manifest_idx = $self->manifest_idx(); + + DBQueryWarn("update geni_manifests set ". " manifest=". DBQuoteSpecial($manifest) . ", " . - " idx=$manifest_idx, slice_uuid='$sliver_uuid', ". - " created=now()"); - return -1 - if (!defined($query_result)); + " created=now() ". + "where idx=$manifest_idx") + or return -1; + } + else { + # We do not care about this uuid in cooked mode. + my $uuid = GeniUtil::NewUUID(); - if (!$self->manifest_idx()) { - $manifest_idx = $query_result->insertid; + my $query_result = + DBQueryWarn("insert into geni_manifests set ". + " manifest=". DBQuoteSpecial($manifest) . ", " . + " idx=NULL, slice_uuid='$uuid', ". + " created=now()"); + + return -1 + if (!defined($query_result)); + + my $manifest_idx = $query_result->insertid; $self->Update({"manifest_idx" => $manifest_idx}); } @@ -1042,9 +1227,9 @@ sub GetManifest($$) } # -# Clear all resources. +# Update the manifest. # -sub Clear($$) +sub GetManifest($$) { my ($self, $user) = @_; my $response; @@ -1056,21 +1241,296 @@ sub Clear($$) my $slice = GeniSlice->Lookup($self->slice_idx()); if (!defined($slice)) { - print STDERR "*** ClearResources: No slice for $self\n"; + print STDERR "*** GetManifest: No slice for $self\n"; return -1; } my $manager_urn = $self->manager_urn(); - # - # Load the SA cert to act as caller context. - # - my $certificate = GeniCertificate->LoadFromFile($SACERT); - if (!defined($certificate)) { - print STDERR "*** Could not load certificate from $SACERT\n"; + my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); + if (!defined($authority)) { + print STDERR "*** Could not find $manager_urn at ClearingHouse\n"; return -1; } - my $context = Genixmlrpc->Context($certificate); - if (!defined($context)) { + my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); + + # + # Create a Geni user from current user doing the operation. + # + my $geniuser = GeniUser->CreateFromLocal($user); + if (!defined($geniuser)) { + print STDERR "*** Could not create a geni user from $user\n"; + return -1; + } + my $usercontext = Genixmlrpc->UserContext($geniuser); + if (!defined($usercontext)) { + print STDERR "*** Could not create user context from $geniuser\n"; + return -1; + } + + # + # Do it. + # + my $method_name = "Resolve"; + my $method_args = {}; + my @method_args = ($method_args); + + if ($manager_api eq "AM") { + # + # Generate a slice credential for the user. + # + my $slice_credential = + GeniCredential->CreateSigned($slice, + $geniuser, + $GeniCredential::LOCALSA_FLAG); + if (!defined($slice_credential)) { + print STDERR + "*** Could not create a slice credential for ". + "$slice/$geniuser!\n"; + return -1; + } + $method_name = "ListResources"; + @method_args = ([$slice_credential->asString()], + {"geni_slice_urn" => $slice->urn()}); + } + else { + # + # Load sliver credential. + # + if (!$self->credential_idx()) { + print STDERR "*** No credential defined for $self.\n"; + return -1; + } + my $sliver_credential = + GeniCredential->Lookup($self->credential_idx()); + if (!defined($sliver_credential)) { + print STDERR "*** Could not get sliver credential for $self.\n"; + return -1; + } + $method_args->{'credentials'} = [$sliver_credential->asString()]; + $method_args->{'urn'} = $sliver_credential->target_urn(); + } + $response = + Genixmlrpc::CallMethod($authority->url(), $usercontext, + $method_name, @method_args); + if (!defined($response)) { + $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); + print STDERR + "*** Internal error getting manifest for $self\n"; + return -1; + } + $self->{'last_rpc_error'} = $response->code(); + $self->{'last_rpc_output'} = $response->output(); + $self->{'last_rpc_value'} = $response->value(); + if ($response->code() != GENIRESPONSE_SUCCESS) { + print STDERR "Could not get manifest for $self. Error: "; + print STDERR $response->output() . "\n"; + return -1; + } + my $manifest = ($manager_api eq "AM" ? $response->value() : + $response->value()->{'manifest'}); + + # + # Okay, so that worked. Store the new manifest. + # + # Move this elsewhere. + # + if ($self->manifest_idx()) { + my $manifest_idx = $self->manifest_idx(); + + DBQueryWarn("update geni_manifests set ". + " manifest=". DBQuoteSpecial($manifest) . ", " . + " created=now() ". + "where idx=$manifest_idx") + or return -1; + } + else { + # We do not care about this uuid in cooked mode. + my $uuid = GeniUtil::NewUUID(); + + my $query_result = + DBQueryWarn("insert into geni_manifests set ". + " manifest=". DBQuoteSpecial($manifest) . ", " . + " idx=NULL, slice_uuid='$uuid', ". + " created=now()"); + + return -1 + if (!defined($query_result)); + + my $manifest_idx = $query_result->insertid; + + $self->Update({"manifest_idx" => $manifest_idx}); + } + return 0; +} + +# +# Release outstanding ticket. +# +sub ReleaseTicket($$) +{ + my ($self, $user) = @_; + my $response; + + return 0 + if (!$self->newticket_idx()); + + # Always clear for caller. + $self->{'last_rpc_error'} = 0; + $self->{'last_rpc_output'} = undef; + $self->{'last_rpc_value'} = undef; + + my $slice = GeniSlice->Lookup($self->slice_idx()); + if (!defined($slice)) { + print STDERR "*** ClearResources: No slice for $self\n"; + return -1; + } + my $manager_urn = $self->manager_urn(); + + # + # Load the SA cert to act as caller context. + # + my $certificate = GeniCertificate->LoadFromFile($SACERT); + if (!defined($certificate)) { + print STDERR "*** Could not load certificate from $SACERT\n"; + return -1; + } + my $context = Genixmlrpc->Context($certificate); + if (!defined($context)) { + print STDERR "*** Could not create context to talk to clearinghouse\n"; + return -1; + } + # + # Set the default RPC context. + # + Genixmlrpc->SetContext($context); + + my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); + if (!defined($authority)) { + print STDERR "*** Could not find $manager_urn at ClearingHouse\n"; + return -1; + } + my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); + + # + # Create a Geni user from current user doing the operation. + # + my $geniuser = GeniUser->CreateFromLocal($user); + if (!defined($geniuser)) { + print STDERR "*** Could not create a geni user from $user\n"; + return -1; + } + my $usercontext = Genixmlrpc->UserContext($geniuser); + if (!defined($usercontext)) { + print STDERR "*** Could not create user context from $geniuser\n"; + return -1; + } + + # + # Generate a slice credential for the user. + # + my $slice_credential = + GeniCredential->CreateSigned($slice, + $geniuser, + $GeniCredential::LOCALSA_FLAG); + if (!defined($slice_credential)) { + print STDERR + "*** Could not create a slice credential for ". + "$slice/$geniuser!\n"; + return -1; + } + + # + # Load sliver credential if we have it. + # + my $sliver_credential; + if ($self->credential_idx()) { + $sliver_credential = GeniCredential->Lookup($self->credential_idx()); + if (!defined($sliver_credential)) { + print STDERR "*** Could not get sliver credential for $self.\n"; + return -1; + } + } + my $newticket; + if ($self->newticket_idx()) { + $newticket = GeniTicket->Lookup($self->newticket_idx()); + if (!defined($newticket)) { + print STDERR "*** Could not get new ticket for $self.\n"; + return -1; + } + } + + if (defined($newticket)) { + my $method_name = "ReleaseTicket"; + my $method_args = { "ticket" => $newticket->asString() }; + + if ($manager_version == 1.0) { + $method_args->{'credential'} = $slice_credential->asString(); + } + elsif ($manager_version == 2.0) { + $method_args->{'credentials'} = [$slice_credential->asString()]; + $method_args->{'slice_urn'} = $slice->urn(); + } + else { + print STDERR + "GeniResource::Clear Unknown version at $authority\n"; + return -1; + } + print STDERR "Deleting $newticket on $authority\n"; + $response = + Genixmlrpc::CallMethod($authority->url(), $usercontext, + $method_name, $method_args); + if (!defined($response)) { + $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); + print STDERR "*** Internal error releasing ticket for $self\n"; + return -1; + } + $self->{'last_rpc_error'} = $response->code(); + $self->{'last_rpc_output'} = $response->output(); + $self->{'last_rpc_value'} = $response->value(); + if ($response->code() != GENIRESPONSE_SUCCESS && + $response->code() != GENIRESPONSE_BADARGS && + $response->code() != GENIRESPONSE_SEARCHFAILED) { + print STDERR "Could not release ticket for $self. Error: "; + print STDERR $response->output() . "\n"; + return -1; + } + $newticket->Delete(TICKET_DELETED); + $self->Update({"newticket_idx" => 0}); + } + return 0; +} +# +# Clear all resources. +# +sub Clear($$) +{ + my ($self, $user) = @_; + my $response; + + # Always clear for caller. + $self->{'last_rpc_error'} = 0; + $self->{'last_rpc_output'} = undef; + $self->{'last_rpc_value'} = undef; + + my $slice = GeniSlice->Lookup($self->slice_idx()); + if (!defined($slice)) { + print STDERR "*** ClearResources: No slice for $self\n"; + return -1; + } + my $manager_urn = $self->manager_urn(); + + # + # Load the SA cert to act as caller context. + # + my $certificate = GeniCertificate->LoadFromFile($SACERT); + if (!defined($certificate)) { + print STDERR "*** Could not load certificate from $SACERT\n"; + return -1; + } + my $context = Genixmlrpc->Context($certificate); + if (!defined($context)) { print STDERR "*** Could not create context to talk to clearinghouse\n"; return -1; } @@ -1085,6 +1545,7 @@ sub Clear($$) return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); # # Create a Geni user from current user doing the operation. @@ -1171,9 +1632,10 @@ sub Clear($$) $self->{'last_rpc_output'} = $response->output(); $self->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS && + $response->code() != GENIRESPONSE_BADARGS && $response->code() != GENIRESPONSE_SEARCHFAILED) { print STDERR "Could not release ticket for $self. Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } $newticket->Delete(TICKET_DELETED); @@ -1185,9 +1647,14 @@ sub Clear($$) } my $method_name = "DeleteSlice"; - my $method_args = undef; + my $method_args = {}; + my @method_args = ($method_args); - if ($manager_version == 1.0) { + if ($manager_api eq "AM") { + $method_name = "DeleteSliver"; + @method_args = ($slice->urn(), [$slice_credential->asString()]); + } + elsif ($manager_version == 1.0) { $method_args->{'credential'} = $slice_credential->asString(); } elsif ($manager_version == 2.0) { @@ -1201,7 +1668,7 @@ sub Clear($$) print STDERR "Deleting sliver on $authority\n"; $response = Genixmlrpc::CallMethod($authority->url(), $usercontext, - $method_name, $method_args); + $method_name, @method_args); if (!defined($response)) { $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); print STDERR "*** Internal error deleting sliver for $self\n"; @@ -1213,7 +1680,7 @@ sub Clear($$) if ($response->code() != GENIRESPONSE_SUCCESS && $response->code() != GENIRESPONSE_SEARCHFAILED) { print STDERR "Could not delete sliver for $self. Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } # @@ -1231,6 +1698,11 @@ sub Clear($$) $sliver_credential->Delete(); $self->Update({"credential_idx" => 0}); } + if (defined($self->rspec_idx())) { + my $rspec_idx = $self->rspec_idx(); + DBQueryWarn("delete from geni_rspecs where idx='$rspec_idx'") + && $self->Update({"rspec_idx" => 0}); + } # # Move this elsewhere. @@ -1271,6 +1743,7 @@ sub Purge($$) return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); # # Create a Geni user from current user doing the operation. @@ -1334,8 +1807,13 @@ sub Purge($$) } my $method_name = "DeleteSlice"; my $method_args = {}; + my @method_args = ($method_args); - if ($manager_version == 1.0) { + if ($manager_api eq "AM") { + $method_name = "DeleteSliver"; + @method_args = ($slice->urn(), [$slice_credential->asString()]); + } + elsif ($manager_version == 1.0) { $method_args->{'credential'} = $slice_credential->asString(); } elsif ($manager_version == 2.0) { @@ -1349,7 +1827,7 @@ sub Purge($$) } $response = Genixmlrpc::CallMethod($authority->url(), $usercontext, - $method_name, $method_args); + $method_name, @method_args); if (!defined($response)) { $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); print STDERR "*** Internal error deleting sliver for $self\n"; @@ -1361,9 +1839,14 @@ sub Purge($$) if ($response->code() != GENIRESPONSE_SUCCESS && $response->code() != GENIRESPONSE_SEARCHFAILED) { print STDERR "Could not delete sliver for $self. Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } + if (defined($self->rspec_idx())) { + my $rspec_idx = $self->rspec_idx(); + DBQueryWarn("delete from geni_rspecs where idx='$rspec_idx'") + && $self->Update({"rspec_idx" => 0}); + } # # Delete this now; no point in waiting for the CM to tell us. # @@ -1380,9 +1863,9 @@ sub Purge($$) # # Start/Stop/Restart a sliver. # -sub SliverAction($$$$) +sub SliverAction($$$@) { - my ($self, $action, $user, $urn) = @_; + my ($self, $action, $user, @urns) = @_; my $response; # Always clear for caller. @@ -1418,6 +1901,16 @@ sub SliverAction($$$$) return -1; } my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); + + # + # Not supported by the AM interface. + # + if ($manager_api eq "AM") { + print STDERR + "GeniResource::${action}Sliver Not supported on AM $authority\n"; + return -1; + } # # Create a Geni user from current user doing the operation. @@ -1461,7 +1954,12 @@ sub SliverAction($$$$) } elsif ($manager_version == 2.0) { $method_args->{'credentials'} = [$slice_credential->asString()]; - $method_args->{'slice_urn'} = $slice->urn(); + if (@urns) { + $method_args->{'sliver_urns'} = \@urns; + } + else { + $method_args->{'slice_urn'} = $slice->urn(); + } } else { print STDERR @@ -1482,7 +1980,7 @@ sub SliverAction($$$$) $self->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS) { print STDERR "Could not ${action} sliver $sliver_credential Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } return 0; @@ -1499,11 +1997,11 @@ sub StopSliver($$) return $self->SliverAction("Stop", $user); } -sub RestartSliver($$) +sub RestartSliver($$@) { - my ($self, $user) = @_; + my ($self, $user, @urns) = @_; - return $self->SliverAction("Restart", $user); + return $self->SliverAction("Restart", $user, @urns); } # @@ -1519,6 +2017,20 @@ sub SliverStatus($$$) $self->{'last_rpc_output'} = undef; $self->{'last_rpc_value'} = undef; + my $slice = GeniSlice->Lookup($self->slice_idx()); + if (!defined($slice)) { + print STDERR "*** SliverStatus: No slice for $self\n"; + return -1; + } + my $manager_urn = $self->manager_urn(); + my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); + if (!defined($authority)) { + print STDERR "*** Could not find $manager_urn at ClearingHouse\n"; + return -1; + } + my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); + # # Load sliver credential if we have it. # @@ -1530,24 +2042,11 @@ sub SliverStatus($$$) return -1; } } - else { + elsif ($manager_api ne "AM") { print STDERR "*** No sliver credential for $self.\n"; return -1; } - my $slice = GeniSlice->Lookup($self->slice_idx()); - if (!defined($slice)) { - print STDERR "*** SliverStatus: No slice for $self\n"; - return -1; - } - my $manager_urn = $self->manager_urn(); - my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); - if (!defined($authority)) { - print STDERR "*** Could not find $manager_urn at ClearingHouse\n"; - return -1; - } - my $manager_version = $self->ManagerVersion(); - # # Create a Geni user from current user doing the operation. # @@ -1567,8 +2066,25 @@ sub SliverStatus($$$) # my $method_name = "SliverStatus"; my $method_args = {}; + my @method_args = ($method_args); - if ($manager_version == 1.0) { + if ($manager_api eq "AM") { + # + # Generate a slice credential for the user. + # + my $slice_credential = + GeniCredential->CreateSigned($slice, + $geniuser, + $GeniCredential::LOCALSA_FLAG); + if (!defined($slice_credential)) { + print STDERR + "*** Could not create a slice credential for ". + "$slice/$geniuser!\n"; + return -1; + } + @method_args = ($slice->urn(), [$slice_credential->asString()]); + } + elsif ($manager_version == 1.0) { $method_args->{'credential'} = $sliver_credential->asString(); } elsif ($manager_version == 2.0) { @@ -1582,7 +2098,7 @@ sub SliverStatus($$$) } $response = Genixmlrpc::CallMethod($authority->url(), $usercontext, - $method_name, $method_args); + $method_name, @method_args); if (!defined($response)) { $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); print STDERR @@ -1593,20 +2109,35 @@ sub SliverStatus($$$) $self->{'last_rpc_output'} = $response->output(); $self->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS) { - print STDERR "Could not get sliver status $sliver_credential Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR "Could not get sliver status for $self. Error: "; + print STDERR $response->output() . "\n"; return -1; } my $blob = { "state" => "unknown", - "status" => $response->value()->{'status'}, }; - if ($manager_version == 1.0) { + if ($manager_api eq "AM") { + my $details = {}; + + foreach my $amg (@{ $response->value()->{'geni_resources'} }) { + $details->{$amg->{'geni_urn'}} = { + "component_urn" => undef, + "state" => "unknown", + "status" => $amg->{'geni_status'}, + "error" => $amg->{'geni_error'}, + }; + } + $blob->{'details'} = $details; + $blob->{'status'} = $response->value()->{'geni_status'}, + } + elsif ($manager_version == 1.0) { $blob->{'details'} = $response->value()->{'detailsNew'}; + $blob->{'status'} = $response->value()->{'status'}, } elsif ($manager_version == 2.0) { $blob->{'state'} = $response->value()->{'state'}; $blob->{'details'} = $response->value()->{'details'}; + $blob->{'status'} = $response->value()->{'status'}, } $$pref = $blob if (defined($pref)); @@ -1632,8 +2163,9 @@ sub Discover($$$) print STDERR "*** Discover: No slice for $self\n"; return -1; } - my $manager_urn = $self->manager_urn(); + my $manager_urn = $self->manager_urn(); my $manager_version = $self->ManagerVersion(); + my $manager_api = $self->ManagerApi(); my $authority = GeniAuthority->CreateFromRegistry("cm", $manager_urn); if (!defined($authority)) { @@ -1672,13 +2204,19 @@ sub Discover($$$) # Do it. # my $method_name = "DiscoverResources"; - my $method_args = { "available" => "true" }; + my $method_hash = { "available" => "true" }; + my @method_args = ($method_hash); - if ($manager_version == 1.0) { - $method_args->{'credential'} = $slice_credential->asString(); + if ($manager_api eq "AM") { + $method_name = "ListResources"; + @method_args = ([$slice_credential->asString()], + {'geni_available' => "true"}); + } + elsif ($manager_version == 1.0) { + $method_hash->{'credential'} = $slice_credential->asString(); } elsif ($manager_version == 2.0) { - $method_args->{'credentials'} = [$slice_credential->asString()]; + $method_hash->{'credentials'} = [$slice_credential->asString()]; } else { print STDERR @@ -1687,7 +2225,7 @@ sub Discover($$$) } $response = Genixmlrpc::CallMethod($authority->url(), $usercontext, - $method_name, $method_args); + $method_name, @method_args); if (!defined($response)) { $self->{'last_rpc_error'} = GENIRESPONSE_RPCERROR(); print STDERR @@ -1699,7 +2237,7 @@ sub Discover($$$) $self->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS) { print STDERR "Could not discover resources $slice_credential Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; return -1; } $$pref = $response->value() @@ -1831,6 +2369,7 @@ sub RenewResources(@) next; } my $manager_version = $resource->ManagerVersion(); + my $manager_api = $resource->ManagerApi(); # # Generate a slice credential. @@ -1885,7 +2424,7 @@ sub RenewResources(@) $resource->{'last_rpc_value'} = $response->value(); if ($response->code() != GENIRESPONSE_SUCCESS) { print STDERR "RenewAll: Could not renew $slice_credential Error: "; - print STDERR " " . $response->output() . "\n"; + print STDERR $response->output() . "\n"; next; } # diff --git a/protogeni/lib/GeniSliver.pm.in b/protogeni/lib/GeniSliver.pm.in index b851c89d8..95d456f18 100755 --- a/protogeni/lib/GeniSliver.pm.in +++ b/protogeni/lib/GeniSliver.pm.in @@ -321,6 +321,26 @@ sub GetManifest($$) return $xml; } +# +# Store the rspec/manifest string. +# +sub UpdateRspec($$) +{ + my ($self, $rspec) = @_; + + my $idx = $self->idx(); + my $rspec_string = GeniXML::Serialize($rspec); + my $safe_rspec = DBQuoteSpecial($rspec_string); + + return -1 + if (!DBQueryWarn("update geni_slivers set ". + " rspec_string=$safe_rspec ". + "where idx='$idx'")); + + $self->{'RSPEC'} = $rspec; + return 0; +} + # # Set the aggregate for a sliver. # diff --git a/tbsetup/libossetup.pm.in b/tbsetup/libossetup.pm.in index dc9f8f3b3..d326f89dc 100644 --- a/tbsetup/libossetup.pm.in +++ b/tbsetup/libossetup.pm.in @@ -1645,14 +1645,45 @@ sub LightUpNodes($@) my $experiment = $parent->experiment(); my $pid = $experiment->pid(); my $eid = $experiment->eid(); + my @rebootlist = (); + # + # What nodes need reboot. + # + foreach my $node (@nodelist) { + push (@rebootlist, $node) + if ($node->allocstate() eq TBDB_ALLOCSTATE_RES_REBOOT()); + } + TBDebugTimeStamp("Starting Geni setup."); + # + # This will start newly added slivers, but not existing. + # if (libGeni::StartSlivers($experiment, - $parent->user(), 0, $parent->debug())) { + $parent->user(), $parent->debug())) { print STDERR "*** Could not start Geni slivers\n"; return 1; } TBDebugTimeStamp("Geni slivers have been started."); + + # Now reboot the rest. + if (@rebootlist) { + if (libGeni::RestartNodes($parent->user(), $parent->debug(), @rebootlist)) { + tbdie("Could not restart protogeni nodes\n"); + } + # + # We always do a wait for geni nodes since for nodes in "basic" + # cooked mode, nothing will be reporting a state change from the + # node. We have to go poll it to make sure that the node is alive, + # and so we can report ISUP for it. This is a bit of a violation of + # the default reboot model, which is fire and forget when waitmode + # is not set, but no way around it. + # + if (libGeni::WaitForNodes($parent->user(), + $parent->debug(), undef, @rebootlist)) { + tbdie("Error in waiting for protogeni nodes\n"); + } + } return 0; } diff --git a/tbsetup/libvtop.pm.in b/tbsetup/libvtop.pm.in index e45c78ff5..ec5c81560 100644 --- a/tbsetup/libvtop.pm.in +++ b/tbsetup/libvtop.pm.in @@ -31,6 +31,7 @@ use Carp; use POSIX; use XML::LibXML; use XML::Simple; +use GeniHRN; # Configure variables my $TB = "@prefix@"; @@ -43,11 +44,7 @@ my $DELAYCAPACITY = @DELAYCAPACITY@; # Can be overridden by user. my $DELAYTHRESH = @DELAYTHRESH@; my $PGENISUPPORT = @PROTOGENI_SUPPORT@; my $OURDOMAIN = "@OURDOMAIN@"; -my $cmid = ''; - -if ($PGENISUPPORT) { - require GeniHRN; -} +my $mycmurn = GeniHRN::Generate("@OURDOMAIN@", "authority", "cm"); # Flags. $VTOP_FLAGS_VERBOSE = 0x01; @@ -204,21 +201,24 @@ sub VirtTypes($) { return $_[0]->virt_vtypes()->Rows(); } # Caller will want these. sub minimum_nodes($) { return $_[0]->counter("minimum_nodes"); } sub maximum_nodes($) { return $_[0]->counter("maximum_nodes"); } +sub nodecount($) { return $_[0]->counter("nodecount"); } sub plabcount($) { return $_[0]->counter("plabcount"); } +sub genicount($) { return $_[0]->counter("genicount"); } sub virtnodecount($) { return $_[0]->counter("virtcount"); } sub simnodecount($) { return $_[0]->counter("simcount"); } sub remotenodecount($) { return $_[0]->counter("remotecount"); } sub sharednodecount($) { return $_[0]->counter("sharedcount"); } -sub createLink($$$$$$$$) +sub createLink($$$$$$$$$) { # $others here will be a hashtable for the link flags. The table # could also contain non-default values for latency and # packet_loss - my ($self, $name, $cm, $src, $dst, $bw, $type, $others) = @_; + my ($self, $name, $plink, $cm, $src, $dst, $bw, $type, $others) = @_; my $ref = { - 'name' => $name, - 'cm' => $cm, + 'virtual_id' => $name, + 'plink' => $plink, + 'manager_urn' => $cm, 'link_type' => $type, 'interface_ref' => [$src, $dst], 'capacity' => $bw, @@ -237,15 +237,15 @@ sub createLink($$$$$$$$) push(@{ $self->rspec()->{'link'} }, $ref); } -sub createNode ($$$$$$$) +sub createNode ($$$$$$$;$) { # $others here will be a hashtable for the desires # The key will be the desire name and the value will be a pair # of the desire type and the penalty - my ($self, $name, $cm, $type, $typecount, $desires, $others) = @_; + my ($self, $name, $cm, $type, $typecount, $desires, $others, $ifaces) = @_; my $ref = { - 'name' => $name, - 'cm' => $cm, + 'virtual_id' => $name, + 'manager_urn' => $cm, }; my $typename = $type; @@ -261,7 +261,7 @@ sub createNode ($$$$$$$) # We will need this to generate interface names later $ref->{'interface_count'} = 0; - $ref->{'interfaces'} = []; + $ref->{'interfaces'} = $ifaces; $ref->{'node_type'} = $typename; $ref->{'type_slots'} = $typecount; @@ -314,33 +314,6 @@ sub createFixedNode ($$$) push(@{ $self->rspec()->{'fixed'} }, $ref); } -# Given a link and an interface_ref index, -# updates the interface_ref with a generated interface name -# and adds the interface to the node structure -sub updateInterfaceRefAndNode ($$$) -{ - my ($self, $link, $idx) = @_; - my $nodename = $link->{'interface_ref'}->[$idx]; - foreach my $node (@{$self->rspec()->{'node'}}) { - if ($node->{'name'} eq $nodename) { - my $ifacenumber = $node->{'interface_count'}; - my $iface = $nodename . ":" . "$ifacenumber"; - my $fixed = ''; - if (exists($link->{'fixiface'})) { - foreach my $fixediface (@{$link->{'fixiface'}}) { - if ($fixediface->[0] eq $node->{'name'}) { - $fixed = $fixediface->[1]; - } - } - } - push(@{$node->{'interfaces'}}, [$iface, $fixed]); - $link->{'interface_ref'}->[$idx] = $iface; - $node->{'interface_count'} = $node->{'interface_count'} + 1; - last; - } - } -} - ############################################################################### # Virtual Nodes. A separate package so we can create objects for each one # and then add local stuff to them. @@ -491,7 +464,8 @@ sub addmember($$) my ($self, $vlanmember) = @_; $self->members()->{$vlanmember->member()} = $vlanmember; - push(@{ $self->{'MEMBERLIST'} }, $vlanmember); + + @{ $self->{'MEMBERLIST'} }[$vlanmember->vindex()] = $vlanmember; return 0; } @@ -947,7 +921,8 @@ sub LoadCurrentResources($) # WIDEAREA nodes are going to break. # if ($pnode->isremotenode() && - !$pnode->isplabdslice() && !$pnode->isdedicatedremote()) { + !($pnode->isplabdslice() || $pnode->isfednode() || + $pnode->isdedicatedremote())) { tberror("Cannot update widearea nodes yet!\n"); return -1; } @@ -1149,7 +1124,7 @@ sub LoadVirtNodes($) return -1; } $vnode->_parent($fixed); - $vnode->fixed(""); + $vnode->fixed(undef); undef($fixed); } @@ -1162,10 +1137,36 @@ sub LoadVirtNodes($) # to a physical node or another node in the topology. See # the post pass loop below. $vnode->_fixedvm($fixed) - if ($isvirt); + if ($isvirt && !$isgeni); } else { $vnode->fixed(undef); + undef($fixed); + } + + # For the moment, geni nodes must be bound to a CM. + if ($isgeni) { + if (!defined($fixed) || $fixed eq "") { + tberror("Geninode $vnode must be fixed to a CM."); + return -1; + } + my ($authority,$gtype,undef) = GeniHRN::Parse($fixed); + # + # We allow an authority instead of a node urn. This is just a + # kludge so I can test against the AM interface. + # + if ($gtype eq "node") { + $vnode->_cmurn(GeniHRN::Generate($authority, + "authority", "cm")); + } + elsif ($gtype eq "authority") { + $vnode->_cmurn($fixed); + $fixed = GeniHRN::Generate($authority, "node", "*"); + $vnode->fixed($fixed); + } + } + else { + $vnode->_cmurn($mycmurn); } $self->printdb(" $vname type:$type ips:$ips\n"); @@ -1265,6 +1266,7 @@ sub LoadVirtNodes($) $vnode->_startloc($startloc); # Counters + $self->{'COUNTERS'}->{'nodecount'}++; $self->{'COUNTERS'}->{'simcount'}++ if ($issim); $self->{'COUNTERS'}->{'remotecount'}++ @@ -1273,6 +1275,8 @@ sub LoadVirtNodes($) if ($isvirt); $self->{'COUNTERS'}->{'plabcount'}++ if ($isplab); + $self->{'COUNTERS'}->{'genicount'}++ + if ($isgeni); $self->{'COUNTERS'}->{'physcount'}++ if (!$issim && !$isvirt); $self->{'COUNTERS'}->{'sharedcount'}++ @@ -1293,7 +1297,6 @@ sub LoadVirtNodes($) require libGeni; require GeniXML; import GeniXML; - $cmid = GeniHRN::Generate($OURDOMAIN, "authority", "cm"); } else { tberror("Using geni nodes but PROTOGENI_SUPPORT is not defined\n"); @@ -1640,11 +1643,13 @@ sub LoadVirtLans($) my $lastnode = $vpath->lastmember()->virt_node(); if ("$virtnode0" ne "$firstnode") { - tberror("First node of $vpath is not the same as $virtlan\n"); + tberror("First node of $vpath is not the same as $virtlan ". + "$virtnode0, $firstnode, $member0, $member1\n"); return -1; } if ("$virtnode1" ne "$lastnode") { - tberror("Last node of $vpath is not the same as $virtlan\n"); + tberror("Last node of $vpath is not the same as $virtlan ". + "$virtnode1, $lastnode, $member0, $member1\n"); return -1; } @@ -1695,80 +1700,6 @@ sub GenVirtNodes($) next if ($vnode->_isvirtnode() && $vnode->_fixedvm()); - # - # Temporary rpsec generation. - # - if ($vnode->_isgeninode()) { - my @ifaces = @{ $vnode->_virtifaces() }; - - if (!defined($vnode->fixed()) || $vnode->fixed() eq "") { - tberror("Geninode $vnode must be fixed to a CM.\n"); - return -1; - } - my $ref = { 'virtual_id' => $vname, - 'request_urn' => $vnode->fixed() - }; - if ($vnode->_isvirtnode()) { - $ref->{'virtualization_type'} = 'emulab-vnode'; - $ref->{'virtualization_subtype'} = 'emulab-openvz'; - if ($vnode->_sharedokay()) { - $ref->{'exclusive'} = 0; - } - $ref->{'node_type'} = {"type_name" => "pcvm", - "type_slots" => 1}; - } - else { - $ref->{'virtualization_type'} = 'raw'; - $ref->{'exclusive'} = 1; - - if (defined($vnode->_osinfo())) { - my $osname = $vnode->_osinfo()->osname(); - - # - # If this is the default image, then do not put - # this in; let the other side do what it wants. - # - my $default_osinfo = - OSinfo->Lookup($vnode->_typeinfo()->default_osid()); - if (!defined($default_osinfo)) { - tberror("Could not find default osinfo for $vnode\n"); - return -1; - } - if ($osname ne $default_osinfo->osname()) { - $ref->{'disk_image'} = {"name" => $osname}; - } - } - } - - if (@ifaces) { - my @refs = (); - foreach my $vlanmember (@ifaces) { - my $virtlan = $vlanmember->virt_lan(); - my $iface = {'virtual_id' => "$vlanmember"}; - - if ($vlanmember->fixed_iface() ne "") { - $iface->{'component_id'} = $vlanmember->fixed_iface(); - } - push(@refs, $iface); - } - $ref->{'interface'} = [ @refs ] - if (@refs); - } - if (!defined($self->genirspec())) { - $self->{'GENIRSPEC'} = { - 'generated_by' => 'libvtop', - 'type' => 'request', - 'xmlns' => 'http://www.protogeni.net/resources/rspec/0.2', - # Assign now wants this stuff. - 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", - 'xsi:schemaLocation' => - "http://www.protogeni.net/resources/rspec/0.2 ". - "http://www.protogeni.net/resources/rspec/0.2/request.xsd", - 'node' => [] }; - } - push(@{ $self->genirspec()->{'node'} }, $ref); - next; - } my $desires = {}; my $others = {}; if ($vnode->_issubnode()) { @@ -1825,6 +1756,67 @@ sub GenVirtNodes($) } } } + # + # Some stuff for geni. + # + if ($vnode->_isgeninode()) { + $others->{'isgeninode'} = 1; + $others->{'request_urn'} = $vnode->fixed(); + + # + # Gack; no features and desires in 0.2, so have to use a fixnode. + # + if (exists($self->current_v2p()->{$vname})) { + my $pnode_id = $self->current_v2p()->{$vname}; + my $pnode = $self->oldreservednodes()->{$pnode_id}; + $others->{'request_urn'} = $pnode->WideAreaInfo("external_node_id"); + } + + if ($vnode->_isvirtnode()) { + $others->{'virtualization_type'} = 'emulab-vnode'; + $others->{'virtualization_subtype'} = 'emulab-openvz'; + $others->{'exclusive'} = 0 + if ($vnode->_sharedokay()); + $type = "pcvm"; + } + else { + $others->{'virtualization_type'} = 'raw'; + $others->{'exclusive'} = 1; + $type = "pc"; + + if (defined($vnode->_osinfo())) { + my $osname = $vnode->_osinfo()->osname(); + + # + # If this is the default image, then do not put + # this in; let the other side do what it wants. + # + my $default_osinfo = + OSinfo->Lookup($vnode->_typeinfo()->default_osid()); + if (!defined($default_osinfo)) { + tberror("Could not find default osinfo for $vnode\n"); + return -1; + } + if ($osname ne $default_osinfo->osname()) { + $others->{'disk_image'} = {"name" => $osname}; + } + } + } + } + my @interfaces = (); + my @ifaces = @{ $vnode->_virtifaces() }; + if (@ifaces) { + my @refs = (); + foreach my $vlanmember (@ifaces) { + my $virtlan = $vlanmember->virt_lan(); + my $iface = {'virtual_id' => "$vlanmember"}; + + if ($vlanmember->fixed_iface() ne "") { + $iface->{'component_id'} = $vlanmember->fixed_iface(); + } + push(@interfaces, $iface); + } + } # # Now the type string, which might include some stuff for @@ -1842,8 +1834,8 @@ sub GenVirtNodes($) } } - $self->createNode($vname, - $cmid, $type, $nodeweight, $desires, $others); + $self->createNode($vname, $vnode->_cmurn(), + $type, $nodeweight, $desires, $others, \@interfaces); } # @@ -1995,7 +1987,7 @@ sub GenVirtLans($) my $ip1 = $member1->ip(); $vlan->_geninodes(1) - if ($virtnode0->_isgeninode() || $virtnode0->_isgeninode()); + if ($virtnode0->_isgeninode() || $virtnode1->_isgeninode()); my ($delay0,$bw0,$ebw0,$backfill0,$loss0, $rdelay0,$rbw0,$rebw0,$rbackfill0,$rloss0) = @@ -2007,36 +1999,34 @@ sub GenVirtLans($) my $bw = min($bw0,$rbw1); # - # If the link has geninode members, need to extend the rspec. - # - # XXX We eventually want to support tunnels between a local - # cluster node and a protogeni node. Should be doable, but might - # be easier once assign can take a single rspec for the entire - # topology. + # Tunnels go into the geni rspec, but not locally since assign + # does not really know what to do with tunnels. Special case + # handling below. # if ($vlan->_geninodes()) { - my $ref = { - 'virtual_id' => $vname, - # The list references are so XML::Simple does not - # turn them into attributes. Need a better solution. - 'bandwidth' => [$bw], - 'latency' => [0], - 'packet_loss' => [0], -# 'link_type' => {"type_name" => "tunnel"}, - 'link_type' => "tunnel", - 'interface_ref' => - [ {'virtual_node_id' => $vname0, - 'virtual_interface_id' => "$member0", - 'tunnel_ip' => $ip0 }, - {'virtual_node_id' => $vname1, - 'virtual_interface_id' => "$member1", - 'tunnel_ip' => $ip1 }, - ], - }; - if (!exists($self->genirspec()->{'link'})) { - $self->genirspec()->{'link'} = []; + # + # We eventually want to support tunnels between a local cluster node + # and a protogeni node. + # + # Enforce tunnel between two geni nodes. It would be nice to support + # this, but it requires that the cluster node go into the rspec, and + # that complicates things. + # + if (! ($virtnode0->_isgeninode() && $virtnode1->_isgeninode())) { + tberror("Tunnel $vname: ". + "Unsupported tunnel between cluster and protogeni node!\n"); + return -1; } - push(@{ $self->genirspec()->{'link'} }, $ref); + $self->createLink($vname, $vname, + [ $virtnode0->_cmurn(), + $virtnode1->_cmurn() ], + {'virtual_node_id' => $vname0, + 'virtual_interface_id' => "$member0", + 'tunnel_ip' => $ip0 }, + {'virtual_node_id' => $vname1, + 'virtual_interface_id' => "$member1", + 'tunnel_ip' => $ip1 }, + $bw, "tunnel", {'isgeninode' => 1}); } } @@ -2479,7 +2469,7 @@ sub GenVirtLans($) # treatment applies as well. # $self->createNode("fakelan/$vname", - $cmid, $protocol, '1', undef, undef); + $mycmurn, $protocol, '1', undef, undef); # So we ignore it when it comes back from assign. $self->lannodes()->{"fakelan/$vname"} = 1; @@ -2489,10 +2479,14 @@ sub GenVirtLans($) foreach my $member (@members) { my $plink = "fakelan/$vname/$member"; - my $vnode = $member->virt_node()->vname(); + my $vname = $member->virt_node()->vname(); my ($top_bw, $top_rbw) = $self->virtlantopbw($vlan, $member); - $self->createLink($plink, $cmid, $vnode, "fakelan/$vname", + $self->createLink($vname, $plink, [$mycmurn], + {'virtual_node_id' => $vname, + 'virtual_interface_id' => "$member" }, + {'virtual_node_id' => "fakelan/$vname", + 'virtual_interface_id' => "$member" }, $top_bw, $protocol); next; } @@ -2540,36 +2534,6 @@ sub GenVirtLans($) $errors++; } - # - # Geni links and lans are dead simple right now. - # - if ($vlan->_geninodes()) { - my $ref = { - 'virtual_id' => $vname, - # The list references are so XML::Simple does not - # turn them into attributes. Need a better solution. - 'bandwidth' => [$bw], - 'latency' => [0], - 'packet_loss' => [0], -# 'link_type' => {"type_name" => "ethernet"}, - 'link_type' => "ethernet", - 'interface_ref' => - [ {'virtual_node_id' => $vname0, - 'virtual_interface_id' => "$member0" }, - {'virtual_node_id' => $vname1, - 'virtual_interface_id' => "$member1" }, - ], - }; - if ($emulated) { - $ref->{'virtualization_type'} = $vlan->_encapstyle(); - } - if (!exists($self->genirspec()->{'link'})) { - $self->genirspec()->{'link'} = []; - } - push(@{ $self->genirspec()->{'link'} }, $ref); - next; - } - # # See if the link is really being shaped, or if just need # a delay node cause of tracing/monitoring. @@ -2589,7 +2553,9 @@ sub GenVirtLans($) # Link must be shaped for other reasons (q_red). $mustdelay || # Global force, or per-link force. - $self->option('forcelinkdelays') || $uselinkdelay)) { + $self->option('forcelinkdelays') || $uselinkdelay) && + # No shaping of geni nodes. + $vlan->_geninodes() == 0) { # Need a delay node and its really a shaped link. $shaped = 1; # Mark the links as shaped for later. @@ -2670,26 +2636,6 @@ sub GenVirtLans($) my ($top_bw0, $top_rbw0) = $self->virtlantopbw($vlan, $member0); my ($top_bw1, $top_rbw1) = $self->virtlantopbw($vlan, $member1); - # - # Get the fix-interface info for the members and setup the - # fix string. - # - my $fixi0 = $member0->fixed_iface(); - my $fixi1 = $member1->fixed_iface(); - my ($fixsrc0,$fixdst0,$fixsrc1,$fixdst1)=('','','',''); - my ($fixsrc, $fixdst) = ('',''); - - if (defined($fixi0) && $fixi0 ne '') { - $fixsrc0 = "$fixi0"; - $fixdst0 = "$fixi0"; - $fixsrc = "$fixi0"; - } - if (defined($fixi1) && $fixi1 ne '') { - $fixsrc1 = "$fixi1"; - $fixdst1 = "$fixi1"; - $fixdst = "$fixi1"; - } - my $top_bw = min($top_bw0, $top_rbw1); my $top_rbw = min($top_rbw0, $top_bw1); @@ -2714,6 +2660,8 @@ sub GenVirtLans($) my $others = {}; if ($emulated) { $others->{'emulated'} = 1; + $others->{'virtualization_type'} = + $vlan->_encapstyle(); } if ($trivial_ok) { $others->{'trivial_ok'} = 1; @@ -2730,18 +2678,6 @@ sub GenVirtLans($) $vname1 = $virtnode1->_fixedvm()->vname(); } - if ($fixsrc ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, [$vname0, $fixsrc]); - } - if ($fixdst ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, [$vname1, $fixdst]); - } if (defined($vlan->_implemented_by())) { # # We post pass these links. Save. @@ -2749,7 +2685,11 @@ sub GenVirtLans($) $self->vlinks()->{"$vlan"} = $plink; } else { - $self->createLink($plink, $cmid, $vname0, $vname1, + $self->createLink($vname, $plink, [$mycmurn], + {'virtual_node_id' => $vname0, + 'virtual_interface_id' => "$member0" }, + {'virtual_node_id' => $vname1, + 'virtual_interface_id' => "$member1" }, ($top_bw == 0 ? "*" : max($top_bw, $top_rbw)), $protocol, $others); @@ -2779,37 +2719,27 @@ sub GenVirtLans($) $desire->{$delaydesirename} = [$delaydesiretype, $delaydesirepenalty]; } - $self->createNode($delayname, $cmid, "delay", '1', + $self->createNode($delayname, $mycmurn, "delay", '1', $desire, undef); my $others = {}; - if ($fixsrc0 ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, [$vname0, $fixsrc0]); - } - - $self->createLink("linksdelaysrc/$vname/$member0,$member1", - $cmid, $vname0, $delayname, + $self->createLink($vname, + "linksdelaysrc/$vname/$member0,$member1", + [$mycmurn], + {'virtual_node_id' => $vname0, + 'virtual_interface_id' => "$member0" }, + {'virtual_node_id' => $delayname, + 'virtual_interface_id' => "$member1" }, ($top_bw == 0 ? "*" : $top_bw), $protocol, $others); - # XXX: I am not too sure about this. Looking at the old - # code seems to suggest that I should be doing - # $others->{'fixiface'} = [$delayname, $fixdst0]; but - # fixing an interface on a delay node doesn't seem to - # make a great deal of sense. If something goes wrong, - # this would be a place to look. - if ($fixdst0 ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, [$vname1, $fixdst0]); - } - $self->createLink("linksdelaydst/$vname/$member1,$member0", - $cmid, $vname1, $delayname, - ($top_bw == 0 ? "*" : $top_bw), + $self->createLink($vname, + "linksdelaydst/$vname/$member1,$member0", + [$mycmurn], + {'virtual_node_id' => $vname1, + 'virtual_interface_id' => "$member1" }, + {'virtual_node_id' => $delayname, + 'virtual_interface_id' =>"$member0" }, $protocol, $others); $self->printdb("Delay node $plink ($delayname) = " . @@ -2833,7 +2763,7 @@ sub GenVirtLans($) else { my $plink = "linksimple/$vname/$member0,$member1"; my $bw = ''; - my $others = {}; + my $others = {'isgeninode' => $vlan->_geninodes()}; if ($emulated) { $bw = max($top_bw,$top_rbw); @@ -2844,6 +2774,7 @@ sub GenVirtLans($) } if ($emulated) { $others->{'emulated'} = 1; + $others->{'virtualization_type'} = $vlan->_encapstyle(); } if ($layer == 1) { $protocol = "wire"; @@ -2867,18 +2798,6 @@ sub GenVirtLans($) $rdelay,$rbw,$rbackfill,$rloss,1.000000]; } } - if ($fixsrc ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}},[$vname0, $fixsrc]); - } - if ($fixdst ne '') { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, [$vname1, $fixdst]); - } if (defined($vlan->_implemented_by())) { # # We post pass these links. Save. @@ -2897,85 +2816,30 @@ sub GenVirtLans($) if (defined($virtnode1->_fixedvm())) { $vname1 = $virtnode1->_fixedvm()->vname(); } - $self->createLink($plink, $cmid, $vname0, $vname1, $bw, - $protocol, $others); + $self->createLink($vname, $plink, + [$virtnode0->_cmurn(), + $virtnode1->_cmurn() ], + {'virtual_node_id' => $vname0, + 'virtual_interface_id' =>"$member0" }, + {'virtual_node_id' => $vname1, + 'virtual_interface_id' =>"$member1" }, + $bw, $protocol, $others); } } } elsif ($#members != 0) { $self->exptstats()->{"lans"} += 1; - + my $lannode = ($vlan->_geninodes() ? "lan-$vname" : "lan/$vname"); + my $cmurn = ($vlan->_geninodes() ? + $members[0]->virt_nodes()->_cmurn() : $mycmurn); + # Lan node for assign. - $self->createNode("lan/$vname", $cmid, "lan", '1', undef, undef); + $self->createNode($lannode, $cmurn, "lan", '1', undef, + { 'virtualization_type' => 'raw' , + 'isgeninode' => $vlan->_geninodes() }); # So we ignore it when it comes back from assign. - $self->lannodes()->{"lan/$vname"} = 1; - - # - # Geni links and lans are dead simple right now. - # - if ($vlan->_geninodes()) { - # Different naming for geni. - $self->lannodes()->{"lan-$vname"} = 1; - - # - # Need a node for the fake lan node. - # - my $noderef = { - 'virtual_id' => "lan-$vname", - 'virtualization_type' => 'raw', - 'exclusive' => 1, - 'node_type' => {"type_name" => "lan", - "type_slots" => 1}, - }; - if (!exists($self->genirspec()->{'link'})) { - $self->genirspec()->{'link'} = []; - } - my @nrefs = (); - - foreach my $member (@members) { - my (undef,$bw) = @{$member->_delayinfo()}; - my $virtnode = $member->virt_node(); - my $fixed = $virtnode->fixed(); - - # - # Need to tie the fake lan node to the same CM. - # Should probably sanity check that all the nodes - # in the lan have the same CM. - # - if (defined($fixed) && $fixed ne "") { - my ($authority,$type,$nodeid) = GeniHRN::Parse($fixed); - - $noderef->{'request_urn'} = - GeniHRN::Generate($authority, "node", "*"); - } - my $ref = { - 'virtual_id' => "$vname:$member", - # The list references are so XML::Simple does not - # turn them into attributes. Need a better solution. - 'latency' => [0], - 'packet_loss' => [0], -# 'link_type' => {"type_name" => "ethernet"}, - 'link_type' => "ethernet", - 'bandwidth' => [$bw], - 'interface_ref' => - [ - { 'virtual_node_id' => $virtnode->vname(), - 'virtual_interface_id' => "$member" }, - { 'virtual_node_id' => "lan-$vname", - 'virtual_interface_id' => "$member" } - ] - }; - if ($emulated) { - $ref->{'virtualization_type'} = $vlan->_encapstyle(); - } - push(@{ $self->genirspec()->{'link'} }, $ref); - push(@nrefs, { 'virtual_id' => "$member"}); - } - $noderef->{'interface'} = \@nrefs; - push(@{ $self->genirspec()->{'node'} }, $noderef); - next; - } + $self->lannodes()->{$lannode} = 1; foreach my $member (@members) { my $virtnode = $member->virt_node(); @@ -3009,7 +2873,9 @@ sub GenVirtLans($) # Link must be shaped for other reasons (q_red). $mustdelay || # Global force, or per-lan force. - $self->option('forcelinkdelays') || $uselinkdelay)) { + $self->option('forcelinkdelays') || $uselinkdelay) && + # No shaping for geni nodes. + $vlan->_geninodes() == 0) { $shaped = 1; # Mark the link as shaped for later. $vlan->setmembershaped($member); @@ -3051,17 +2917,6 @@ sub GenVirtLans($) } } } - - # - # Get the fix-interface info for the member. - # - my $fixi0 = $member->fixed_iface(); - my ($fixsrc0) = (''); - - if (defined($fixi0) && $fixi0 ne '') { - $fixsrc0 = "$fixi0"; - } - my ($top_bw, $top_rbw) = $self->virtlantopbw($vlan, $member); my $bandwidth = $self->getbandwidth($member,$vlan,$bw); my $rbandwidth = $self->getbandwidth($member,$vlan,$rbw); @@ -3088,6 +2943,8 @@ sub GenVirtLans($) my $others = {}; if ($emulated) { $others->{'emulated'} = 1; + $others->{'virtualization_type'} = + $vlan->_encapstyle(); } if ($trivial_ok) { $others->{'trivial_ok'} = 1; @@ -3101,15 +2958,11 @@ sub GenVirtLans($) $vnodevname = $virtnode->_fixedvm()->vname(); } - if ($fixsrc0) { - if(!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, - [$vnodevname, $fixi0]); - } - $self->createLink($plink, $cmid, $vnodevname, - "lan/$vname", + $self->createLink($vname, $plink, [], + {'virtual_node_id' => $vnodevname, + 'virtual_interface_id' =>"$member" }, + {'virtual_node_id' => "$lannode", + 'virtual_interface_id' =>"$member" }, $top_bw == 0 ? "*" : max($top_bw,$top_rbw), $protocol, $others); @@ -3135,24 +2988,27 @@ sub GenVirtLans($) $desire->{$delaydesirename} = [$delaydesiretype, $delaydesirepenalty]; } - $self->createNode($delayname, $cmid, "delay", '1', + $self->createNode($delayname, $mycmurn, "delay", '1', $desire, undef); my $others = {}; - if ($fixsrc0) { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}}, - [$vnodevname,$fixi0]); - } - $self->createLink("linkdelaysrc/$vname/$member", $cmid, - $vnodevname, $delayname, + $self->createLink($vname, + "linkdelaysrc/$vname/$member", + [$mycmurn], + {'virtual_node_id' => $vnodevname, + 'virtual_interface_id' =>"$member" }, + {'virtual_node_id' => $delayname, + 'virtual_interface_id' =>"$member" }, $top_bw == 0 ? "*" : $top_bw, $protocol, $others); - $self->createLink("linkdelaydst/$vname/$member", $cmid, - "lan/$vname", $delayname, + $self->createLink($vname, + "linkdelaydst/$vname/$member", + [$mycmurn], + {'virtual_node_id' => "$lannode", + 'virtual_interface_id' =>"$member" }, + {'virtual_node_id' => $delayname, + 'virtual_interface_id' =>"$member" }, $top_bw == 0 ? "*" : $top_bw, $protocol, {}); @@ -3178,9 +3034,11 @@ sub GenVirtLans($) } else { my $plink = "linklan/$vname/$member"; - my $others = {}; + my $others = {'isgeninode' => $vlan->_geninodes()}; if ($emulated) { $others->{'emulated'} = 1; + $others->{'virtualization_type'} = + $vlan->_encapstyle(); } if ($trivial_ok) { $others->{'trivial_ok'} = 1; @@ -3194,13 +3052,6 @@ sub GenVirtLans($) $vnodevname = $virtnode->_fixedvm()->vname(); } - if ($fixsrc0) { - if (!exists($others->{'fixiface'})) { - $others->{'fixiface'} = []; - } - push(@{$others->{'fixiface'}},[$vnodevname, $fixsrc0]); - } - # # We store this info in case assign actually does # turn it into a trivial link. If that happens, we @@ -3217,8 +3068,12 @@ sub GenVirtLans($) [$delay,$bw,$backfill,$loss, $rdelay,$rbw,$rbackfill,$rloss,1.000000]; } - $self->createLink($plink, $cmid, - "$vnodevname", "lan/$vname", + $self->createLink($vname, $plink, + [$virtnode->_cmurn()], + {'virtual_node_id' => $vnodevname, + 'virtual_interface_id' =>"$member" }, + {'virtual_node_id' => $lannode, + 'virtual_interface_id' =>"$member" }, ($top_bw == 0 ? "*" : $top_bw), $protocol, $others); } @@ -3237,6 +3092,7 @@ sub PrintTop($;$) # print Dumper($self); my $output = ""; + my %fixedifaces = (); foreach my $vclass (@{$self->rspec()->{'vclass'}}) { $output .= 'make-vclass ' . ' '; @@ -3249,8 +3105,11 @@ sub PrintTop($;$) } foreach my $node (@{$self->rspec()->{'node'}}) { + next + if (exists($node->{'isgeninode'}) && $node->{'isgeninode'}); + $output .= 'node' . ' '; - $output .= $node->{'name'} . ' '; + $output .= $node->{'virtual_id'} . ' '; $output .= $node->{'node_type'} . ':' . $node->{'type_slots'} . ' '; if (exists($node->{'disallow_trivial_mix'})) { $output .= 'disallow_trivial_mix' . ' '; @@ -3263,13 +3122,30 @@ sub PrintTop($;$) ':' . sprintf("%f", $desireval->[1]) . ' '; } $output .= "\n"; + + # + # In ptop format, the fixed interfaces go on the link lines, + # so just save them up for below. + # + if (exists($node->{'interfaces'})) { + foreach my $ref (@{ $node->{'interfaces'} }) { + my $virtid = $ref->{'virtual_id'}; + my $compid = $ref->{'component_id'}; + + $fixedifaces{$virtid} = $compid + if (defined($compid)); + } + } } foreach my $link (@{$self->rspec()->{'link'}}) { + next + if (exists($link->{'isgeninode'}) && $link->{'isgeninode'}); + $output .= 'link' . ' '; - $output .= $link->{'name'} . ' '; - $output .= $link->{'interface_ref'}->[0] . ' '; - $output .= $link->{'interface_ref'}->[1] . ' '; + $output .= $link->{'plink'} . ' '; + $output .= $link->{'interface_ref'}->[0]->{'virtual_node_id'} . ' '; + $output .= $link->{'interface_ref'}->[1]->{'virtual_node_id'} . ' '; $output .= $link->{'capacity'} . ' '; $output .= $link->{'latency'} . ' '; $output .= $link->{'packet_loss'} . ' '; @@ -3283,15 +3159,13 @@ sub PrintTop($;$) if (exists($link->{'trivial_ok'})) { $output .= 'trivial_ok' . ' '; } - if (exists($link->{'fixiface'})) { - foreach my $fixediface (@{$link->{'fixiface'}}) { - if ($fixediface->[0] eq $link->{'interface_ref'}->[0]) { - $output .= 'fixsrciface:' . $fixediface->[1] . ' '; - } - if ($fixediface->[0] eq $link->{'interface_ref'}->[1]) { - $output .= 'fixdstiface:' . $fixediface->[1] . ' '; - } - } + my $ifaceid1 = $link->{'interface_ref'}->[0]->{'virtual_interface_id'}; + my $ifaceid2 = $link->{'interface_ref'}->[0]->{'virtual_interface_id'}; + if (exists($fixedifaces{$ifaceid1})) { + $output .= 'fixsrciface:' . $fixedifaces{$ifaceid1} . ' '; + } + if (exists($fixedifaces{$ifaceid2})) { + $output .= 'fixdstiface:' . $fixedifaces{$ifaceid2} . ' '; } $output .= "\n"; } @@ -3320,9 +3194,6 @@ sub PrintRspec($;$) $output = *STDOUT if (!defined($output)); - my $pid = $self->experiment()->pid(); - my $eid = $self->experiment()->eid(); - my $doc = XML::LibXML::Document->new(); # Construct the current time string @@ -3353,16 +3224,6 @@ sub PrintRspec($;$) $doc->setDocumentElement($root); - # XXX: This shouldn't be here, but I don't want to make sense of 6000+ - # lines of code in order to find the proper place for it. - # We need to make a pass through the structures built so far and - # add interfaces to all the nodes - foreach my $link (@{$self->rspec()->{'link'}}) { - for (my $i = 0; $i < scalar(@{$link->{'interface_ref'}}); $i++) { - $self->updateInterfaceRefAndNode($link, $i); - } - } - foreach my $ref (@{$self->rspec()->{'node'}}) { $self->processNodeRspec($doc, $root, $ref); } @@ -3377,41 +3238,6 @@ sub PrintRspec($;$) return 0; } -# -# Print in XML. -# -sub PrintXML($;$) -{ - my ($self, $output) = @_; - $output = *STDOUT - if (!defined($output)); - - my $pid = $self->experiment()->pid(); - my $eid = $self->experiment()->eid(); - - my $doc = XML::LibXML::Document->new(); - - my $root = $doc->createElement("vtop"); - $root->setAttribute("xmlns", "http://emulab.net/resources/vtop/0.2"); - $doc->setDocumentElement($root); - - foreach my $vclass (@{$self->results->{'class'}}) { - $self->processVClass($doc, $root, $vclass); - } - foreach my $ref (@{$self->results->{'nodeslinks'}}) { - my ($which, $what) = @{ $ref }; - if ($which eq "node") { - $self->processNode($doc, $root, $what); - } - else { - $self->processLink($doc, $root, $what); - } - } - print $output $doc->toString(1) . "\n"; - - return 0; -} - # # One time initialization for a vtop. # @@ -3455,6 +3281,7 @@ sub CreateVtop($) $self->{'COUNTERS'}->{'remotecount'} = 0; $self->{'COUNTERS'}->{'virtcount'} = 0; $self->{'COUNTERS'}->{'plabcount'} = 0; + $self->{'COUNTERS'}->{'genicount'} = 0; $self->{'COUNTERS'}->{'physcount'} = 0; $self->{'COUNTERS'}->{'sharedcount'} = 0; @@ -3575,10 +3402,6 @@ sub CreateVtop($) $self->exptstats()->{"plabnodes"} = $self->counters()->{'plabcount'}; $self->exptstats()->{"simnodes"} = $self->counters()->{'simcount'}; - if ($self->verbose() && defined($self->rspec())) { - $self->printdb(Dumper($self->rspec())); - } - return 0; } @@ -3806,19 +3629,22 @@ sub MapResources($) { my ($self) = @_; - if (defined($self->genirspec())) { + if ($self->genicount()) { + my $genirspec; + $self->printdb("Mapping geni resources ...\n"); if (libGeni::MapResources($self->experiment(), - $self->user(), $self->genirspec(), - $self->verbose())) { + $self->user(), $self->rspec(), + \$genirspec, $self->verbose())) { tberror("Could not map Geni resources\n"); return -1; } - if ($self->ReadRspecSolution($self->genirspec()) != 0) { + if ($self->ReadRspecSolution($genirspec) != 0) { tberror("Could not parse rspec solution! $!\n"); return -1; } + $self->{'GENIRSPEC'} = $genirspec; } return 0; } @@ -4122,9 +3948,9 @@ sub ReadRspecSolution($$) $self->printdb("Processing rspec\n"); $self->printdb("Nodes:\n"); - foreach my $ref (@{ $rspec->{'node'} }) { - my $node_urn = $ref->{'component_urn'} || $ref->{'component_uuid'}; - my $virtual = $ref->{'virtual_id'}; + foreach my $ref (GeniXML::FindNodes("n:node", $rspec)->get_nodelist()) { + my $node_urn = GeniXML::GetNodeId($ref); + my $virtual = GeniXML::GetVirtualId($ref); # Skip LAN/Fake nodes. return 0 @@ -4133,9 +3959,12 @@ sub ReadRspecSolution($$) $self->AddNodeToSolution($virtual, $node_urn); $ifacemap{$virtual} = {}; - foreach my $linkref (@{$ref->{'interface'}}) { - my $component_id = $linkref->{"component_id"}; - my $virtual_id = $linkref->{"virtual_id"}; + foreach my $linkref (GeniXML::FindNodes("n:interface", + $ref)->get_nodelist()) { + + + my $component_id = GeniXML::GetNodeId($linkref); + my $virtual_id = GeniXML::GetVirtualId($linkref); my $virtlan = $self->memberof()->{$virtual_id}; # Skip tunnels until assign can handle multiple rspecs @@ -4150,9 +3979,11 @@ sub ReadRspecSolution($$) $ifacemap{$virtual}->{$virtual_id} = $component_id; } } + $self->printdb("Links:\n"); - foreach my $ref (@{ $rspec->{'link'} }) { - my $virtual_id = $ref->{"virtual_id"}; + foreach my $ref (GeniXML::FindNodes("n:link", + $rspec)->get_nodelist()) { + my $virtual_id = GeniXML::GetVirtualId($ref); my $virtlan = $self->vlans()->{$virtual_id}; if (!defined($virtlan)) { @@ -4163,16 +3994,17 @@ sub ReadRspecSolution($$) next if ($virtlan->_tunnel()); - my @ifacerefs = @{ $ref->{'interface_ref'} }; + my @ifacerefs = GeniXML::FindNodes("n:interface_ref", + $ref)->get_nodelist(); if (scalar(@ifacerefs) == 2) { my ($ifaceA,$ifaceB) = @ifacerefs; my $trivial = 0; - my $virtA = $ifaceA->{'virtual_interface_id'}; - my $virtB = $ifaceB->{'virtual_interface_id'}; - my $vnodeA = $ifaceA->{'virtual_node_id'}; - my $vnodeB = $ifaceB->{'virtual_node_id'}; + my $virtA = GeniXML::GetText("virtual_interface_id", $ifaceA); + my $virtB = GeniXML::GetText("virtual_interface_id", $ifaceB); + my $vnodeA = GeniXML::GetText("virtual_node_id", $ifaceA); + my $vnodeB = GeniXML::GetText("virtual_node_id", $ifaceB); my $nodeA = $self->solution()->{'V2P'}->{$vnodeA}; my $nodeB = $self->solution()->{'V2P'}->{$vnodeB}; my $portA = $ifacemap{$vnodeA}->{$virtA}; @@ -4189,9 +4021,9 @@ sub ReadRspecSolution($$) $nodeA, $portA, $nodeB, $portB); } else { - foreach my $ifaceref (@{ $ref->{'interface_ref'} }) { - my $virtA = $ifaceref->{'virtual_interface_id'}; - my $vnodeA = $ifaceref->{'virtual_node_id'}; + foreach my $iref (@ifacerefs) { + my $virtA = GeniXML::GetText("virtual_interface_id", $iref); + my $vnodeA = GeniXML::GetText("virtual_node_id", $iref); my $nodeA = $self->solution()->{'V2P'}->{$vnodeA}; my $portA = $ifacemap{$vnodeA}->{$virtA}; @@ -4475,7 +4307,7 @@ sub AllocNodes($) if (exists($self->solution()->{'TORESERVE'}->{$nodeid})) { $self->newreserved()->{$nodeid} = $nodeid; $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_INIT_DIRTY()) - if ($self->impotent()); + if (!$self->impotent()); # # Fix all of the nodes assigned to the pnode. @@ -4595,11 +4427,16 @@ sub AllocNodes($) # Node is being reused, but for a different purpose, so # it should be rebooted. # - $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_INIT_DIRTY()) + $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_REBOOT()) if (! $self->impotent()); } } skip: + if ($self->AllocVirtNodes() != 0) { + tberror("Could not allocate virtual nodes\n"); + return -1; + } + # # Allocate Geni nodes (get tickets). Eventually we want to retry # the assignment and replace tickets that we could not get. @@ -4608,9 +4445,7 @@ sub AllocNodes($) !($self->impotent() || $self->alloconly())) { $self->printdb("Requesting geni tickets ...\n"); - my $progress = libGeni::GetTickets($self->experiment(), - $self->verbose(), - $self->user(), $self->genirspec()); + my $progress = libGeni::GetTickets($self); if ($progress) { tberror("Error allocating (some) Geni Tickets\n"); # @@ -4648,27 +4483,23 @@ sub AllocNodes($) return $progress; } tbinfo("Successfully got all geni tickets we needed.\n"); - } - - if ($self->AllocVirtNodes() != 0) { - tberror("Could not allocate virtual nodes\n"); - return -1; - } - if (! ($self->impotent() || $self->alloconly())) { + # - # Redeem the tickets. If this fails, we are going to be - # really hosed. + # Redeem the tickets. # - if (defined($self->genirspec())) { - $self->printdb("Redeeming geni tickets ...\n"); + $self->printdb("Redeeming geni tickets ...\n"); - if (libGeni::RedeemTickets($self->experiment(), $self->user())) { - tberror("Could not redeem Geni Tickets\n"); - return -1; + if (libGeni::RedeemTickets($self->experiment(), $self->user())) { + tberror("Could not redeem Geni Tickets\n"); + # + # Release outstanding tickets now since we are hosed. + # + if (libGeni::ReleaseTickets($self->experiment(), $self->user())) { + tberror("Could not release Geni Tickets\n"); } - tbinfo("Successfully redeemed all geni tickets.\n"); + return -1; } - tbinfo("Successfully reserved all physical nodes we needed.\n"); + tbinfo("Successfully redeemed all geni tickets.\n"); } # @@ -5056,19 +4887,20 @@ sub AllocVirtNodes($) # if (! $self->impotent()) { if (!exists($self->current_v2v()->{$virtual})) { + $self->newreserved()->{$virtphys} = $virtphys; $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_INIT_CLEAN()); } elsif ($self->current_v2v()->{$virtual} ne $virtphys) { - # Node has moved! - $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_INIT_DIRTY()); + # Node has moved. + $pnode->SetAllocState(TBDB_ALLOCSTATE_RES_REBOOT()); } } # - # When doing alloconly, we have to set the vname for the - # physical nodes so that update works. + # Set the vname for the reserved node right away. Needed in + # the libGeni::GetTickets() to find the pnode. # $pnode->ModifyReservation({"vname" => $virtual}) == 0 - or return -1 if (!$self->impotent() && $self->alloconly()); + or return -1 if (!$self->impotent()); } # Since we have some extra physical vnodes reserved on this pnode, @@ -6297,21 +6129,36 @@ sub InitializePhysNode($$$) # if (defined($osid) && (!$pnode->isremotenode() || $pnode->isdedicatedremote())) { + # osselect wants an osinfo object. + my $osinfo = OSinfo->Lookup($osid); + if (!defined($osinfo)) { + tberror("Could not map $osid to osinfo object\n"); + return -1; + } + # + # Map generic OSID to the specific one. + # + if ($osinfo->IsGeneric()) { + my $tmp = $osinfo->ResolveNextOSID($self->experiment()); + if (!defined($tmp)) { + tberror("Could not resolve specific osid for $osinfo on $pnode!\n"); + return -1; + } + # If the specific OS is already loaded, go ahead and set to that + # so that we do not cause a boot problem on the node. + if ($pnode->IsOSLoaded($tmp)) { + $osinfo = $tmp; + } + } + if ($self->impotent() || $self->alloconly()) { - $self->printdb(" pretending to os_select $osid\n"); + $self->printdb(" pretending to os_select $osinfo\n"); } else { - $self->printdb(" os_select $osid\n"); + $self->printdb(" os_select $osinfo\n"); - # osselect wants an osinfo object. - my $tmposinfo = OSinfo->Lookup($osid); - if (!defined($tmposinfo)) { - tberror("Could not map $osid to osinfo object\n"); - return -1; - } - if ($pnode->OSSelect($tmposinfo, "def_boot_osid", - $self->verbose())) { - tberror("OSSelect($pnode,$tmposinfo) failed\n"); + if ($pnode->OSSelect($osinfo, "def_boot_osid", $self->verbose())) { + tberror("OSSelect($pnode,$osinfo) failed\n"); return -1; } } @@ -7831,231 +7678,12 @@ sub nextipportnum($) # Creates a child node with name "nodeName" whose parent is "parent" # in the XML document "document" # -sub addNode($$$) -{ - my ($document, $parent, $nodeName) = @_; - - my $newNode = $document->createElement($nodeName); - $parent->appendChild($newNode); - return $newNode; -} - sub addNodeRspec ($$$) { - my ($doc, $root, $name) = @_; - my $newnode = $doc->createElement($name); - $root->appendChild($newnode); - return $newnode; -} - -# -# Creates a child node with name "nodeName" whose parent is "parent" -# in the XML document "document" The child node has a textnode within -# it with the text "nodeText" -# -sub addNodeWithText($$$$) -{ - my ($document, $parent, $nodeName, $nodeText) = @_; - - my $newNode = $document->createElement($nodeName); - my $newTextNode = $document->createTextNode($nodeText); - $newNode->appendChild($newTextNode); - $parent->appendChild($newNode); - return $newNode; -} - -# -# Processes a node -# -sub processNode($$$$) -{ - my ($self, $xmlDocument, $root, $line) = @_; - - my ($nodename, $nodetype, @tokens) = split(/\s+/, $line); - - # To keep track of whether or not a features and desires element - # has been added to the current node - my $addedFeatureDesireSpecNode = 0; - - my $newNode = addNode($xmlDocument, $root, "node"); - $newNode->setAttribute("name", $nodename); - - my $NodeTypeNode = addNode($xmlDocument, $newNode, "node_type"); - - # The number of type_slots is optional. Default to 1. If there is - # no ':' in the name, then the number of slots is 1, else it is - # whatever number follows the ':' - my ($nodeTypeName, $nodeTypeSlots) = split(":", $nodetype); - $nodeTypeSlots = 1 - if (!defined($nodeTypeSlots)); - - # If the name starts with a *, the the node is to be marked static - my $isNodeStatic = 0; - if ($nodeTypeName =~ /^\*([-\w]*)$/) { - $nodeTypeName = $1; - $isNodeStatic = 1; - } - $NodeTypeNode->setAttribute("type_name", $nodeTypeName); - - # If the number of slots is *, then the number of slots is unlimited - if ($nodeTypeSlots eq "*") { - $NodeTypeNode->setAttribute("type_slots", "unlimited"); - } - else { - $NodeTypeNode->setAttribute("type_slots", $nodeTypeSlots); - } - if ($isNodeStatic) { - $NodeTypeNode->setAttribute("static", "true"); - } - - # Handle fixed node. - if (exists($self->fixednodes()->{$nodename}) && - ($self->isatoponode($nodename) || $self->isadelaynode($nodename))) { - my $fixed = $self->fixednodes()->{$nodename}; - - $newNode->setAttribute("assigned_to", $fixed); - } - - # Iterate through all the optional parameters on the line - foreach my $token (@tokens) { - # If disallow_trivial_mix is present, put it under the NodeFlagSpec - if ($token eq "disallow_trivial_mix") { - addNode($xmlDocument, $newNode, $token); - } - # If subnode_of is present, find the parent node and add the - # appropriate node under NodeFlagSpec - elsif ($token =~ /^subnode_of/) { - my ($subNodeOf, $parentNode) = split(":", $token); - addNodeWithText($xmlDocument, $newNode, "subnode_of", $parentNode); - } - # If an optional desire is present, put it under the - # features and desires node - elsif ($token =~ /:/) { - my $FeatureDesireSpecNode = addNode($xmlDocument, $newNode, "fd"); - - # The feature name is optionally prepended with a 2 - # character prefix. Detect the prefix and remove it from - # the feature name as appropriate - my ($featureName, $featureWeight) = split(":", $token); - my ($a, $b, $rest) = ($featureName =~ /^(.)(.)(.*)?/); - - if (! ("$a" eq "*" || "$a" eq "?")) { - $FeatureDesireSpecNode->setAttribute("fd_name", $featureName); - } - else { - $FeatureDesireSpecNode->setAttribute("fd_name", $rest); - } - $FeatureDesireSpecNode->setAttribute("fd_weight", $featureWeight); - - if ($featureWeight >= 1.000000) { - $FeatureDesireSpecNode->setAttribute("violatable", "true"); - } - - # The desire prefix is "*!" - if ("$b" eq "!") { - $FeatureDesireSpecNode->setAttribute("global_operator", - "OnceOnly"); - } - # The desire prefix is "*&" - elsif ("$b" eq "&") { - $FeatureDesireSpecNode->setAttribute("global_operator", - "FirstFree"); - } - # The desire prefix is "?+" - elsif ("$b" eq "+") { - $FeatureDesireSpecNode->setAttribute("local_operator", - "+"); - } - } - } - return 0; -} - -# -# Processes a link -# -sub processLink($$$$) -{ - my ($self, $xmlDocument, $root, $line) = @_; - - my ($linkname, - $srcNameInterface, $destNameInterface, @tokens) = split(/\s+/, $line); - - my $newLinkNode = addNode($xmlDocument, $root, "link"); - $newLinkNode->setAttribute("name", $linkname); - - # Find the source name and the source interface. - # If the node has no interface specified, assign it a "safe" interface - # number. Increment the interface number. - my ($srcName,$srcInterface) = split(":", $srcNameInterface); - if (!defined($srcInterface)) { - $srcInterface = $self->nextifacenumber(); - } - # Do the same thing for the destination interface - my ($destName,$destInterface) = split(":", $destNameInterface); - if (!defined($destInterface)) { - $destInterface = $self->nextifacenumber(); - } - - # Add a source interface element - my $sourceInterfaceNode = - addNode($xmlDocument, $newLinkNode, "source_interface"); - $sourceInterfaceNode->setAttribute("node_name", $srcName); - $sourceInterfaceNode->setAttribute("interface_name", $srcInterface); - - # Add a destination interface element - my $destinationInterfaceNode = - addNode($xmlDocument, $newLinkNode, "destination_interface"); - $destinationInterfaceNode->setAttribute("node_name", $destName); - $destinationInterfaceNode->setAttribute("interface_name", $destInterface); - - # Add other stuff that appears on the line - addNodeWithText($xmlDocument, $newLinkNode, "bandwidth", shift(@tokens)); - addNodeWithText($xmlDocument, $newLinkNode, "latency", shift(@tokens)); - addNodeWithText($xmlDocument, $newLinkNode, "packet_loss", shift(@tokens)); - - # Add a link_type element - my $newLinkTypeNode = addNode($xmlDocument, $newLinkNode, "link_type"); - $newLinkTypeNode->setAttribute("type_name", shift(@tokens)); - - # Run through the optional parameters at the end of the line - foreach my $token (@tokens) { - # If fixsrciface or fixdstiface are found - if ($token =~ /:/) { - my ($fixIface, $ifaceName) = split(":", $token); - addNodeWithText($xmlDocument, $newLinkNode, $fixIface, $ifaceName); - } - elsif ($token eq "emulated") { - addNode($xmlDocument, $newLinkNode, "multiplex_ok"); - } - elsif ($token ne "") { - addNode($xmlDocument, $newLinkNode, $token); - } - } -} - -# -# Process a v-class. -# -sub processVClass($$$$) -{ - my ($self, $xmlDocument, $root, $line) = @_; - - my ($classname, $classweight, @tokens) = split(/\s+/, $line); - - my $newVClassNode = addNode($xmlDocument, $root, "vclass"); - $newVClassNode->setAttribute("name", $classname); - - # TODO: Need to take care of the hard case later - addNode($xmlDocument, $newVClassNode, "soft"); - addNodeWithText($xmlDocument, $newVClassNode, "weight", $classweight); - - # The remaining entries on this line will be physical_types. - foreach my $token (@tokens) { - addNodeWithText($xmlDocument, $newVClassNode, - "physical_type", $token); - } - return 0; + my ($doc, $root, $name) = @_; + my $newnode = $doc->createElement($name); + $root->appendChild($newnode); + return $newnode; } # @@ -8068,17 +7696,17 @@ sub processNodeRspec ($$$$) # Check if the node has been fixed my $fixedto = ''; foreach my $fixednode (@{$self->rspec()->{'fixed'}}) { - if ($fixednode->{'vnode'} eq $node->{'name'}) { + if ($fixednode->{'vnode'} eq $node->{'virtual_id'}) { $fixedto = $fixednode->{'pnode'}; last; } } my $rspecnode = addNodeRspec ($doc, $root, 'node'); - $rspecnode->setAttribute('client_id', $node->{'name'}); - $rspecnode->setAttribute('component_manager_id', $node->{'cm'}); + $rspecnode->setAttribute('client_id', $node->{'virtual_id'}); + $rspecnode->setAttribute('component_manager_id', $node->{'manager_urn'}); if ($fixedto ne '') { - my ($authority,undef,undef) = GeniHRN::Parse($node->{'cm'}); + my ($authority,undef,undef) = GeniHRN::Parse($node->{'manager_urn'}); $rspecnode->setAttribute('component_id', GeniHRN::Generate($authority, "node", $fixedto)); } @@ -8138,12 +7766,15 @@ sub processNodeRspec ($$$$) # Add interfaces to the node foreach my $interface (@{$node->{'interfaces'}}) { + my $iface = $interface->{'virtual_id'}; + my $fixed = $interface->{'component_id'}; + my $interfacedecl = addNodeRspec($doc, $rspecnode, 'interface'); - $interfacedecl->setAttribute('client_id', $interface->[0]); - if ($interface->[1] ne '') { + $interfacedecl->setAttribute('client_id', $iface); + if (defined($fixed)) { my $fixediface = addNodeRspec($doc, $interfacedecl, 'emulab:fixedinterface'); - $fixediface->setAttribute('name', $interface->[1]); + $fixediface->setAttribute('name', $fixed); } } @@ -8174,10 +7805,10 @@ sub processLinkRspec ($$$$) my ($self, $doc, $root, $link) = @_; my $rspeclink = addNodeRspec($doc, $root, 'link'); - $rspeclink->setAttribute('client_id', $link->{'name'}); + $rspeclink->setAttribute('client_id', $link->{'plink'}); my $cmnode = addNodeRspec($doc, $rspeclink, 'component_manager'); - $cmnode->setAttribute('name', $link->{'cm'}); + $cmnode->setAttribute('name', $link->{'manager_urn'}); my $linktype = addNodeRspec($doc, $rspeclink, 'link_type'); $linktype->setAttribute('name', $link->{'link_type'}); @@ -8234,9 +7865,18 @@ sub processVClassRspec($$$$) } } -################################################################################ +############################################################################### # Print the solution as an rspec. # +sub addNode($$$) +{ + my ($document, $parent, $nodeName) = @_; + + my $newNode = $document->createElement($nodeName); + $parent->appendChild($newNode); + return $newNode; +} + sub PrintSolution($$) { my ($self, $output) = @_; @@ -8251,7 +7891,8 @@ sub PrintSolution($$) my $root = $doc->createElement("rspec"); $root->setAttribute("pid", "$pid"); $root->setAttribute("eid", "$eid"); - $root->setAttribute("xmlns:rspec", "http://www.protogeni.net/resources/rspec/0.1"); + $root->setAttribute("xmlns:rspec", + "http://www.protogeni.net/resources/rspec/0.1"); $doc->setDocumentElement($root); foreach my $pnodename (keys(%{ $self->solution_p2v() })) { @@ -8300,4 +7941,24 @@ sub PrintSolution($$) return 0; } +# +# Little helper routine to map a vname to the physnode. See libGeni. +# +sub VnameToNode($$) +{ + my ($self, $vname) = @_; + my $nodeid; + + if (exists($self->solution_v2v()->{$vname})) { + $nodeid = $self->solution_v2v()->{$vname}; + } + elsif (exists($self->solution_v2p()->{$vname})) { + $nodeid = $self->solution_v2p()->{$vname}; + } + return undef + if (!defined($nodeid)); + + return Node->Lookup($nodeid); +} + 1; diff --git a/tbsetup/mapper.in b/tbsetup/mapper.in index 7a1e462ce..ddc0c5567 100644 --- a/tbsetup/mapper.in +++ b/tbsetup/mapper.in @@ -321,9 +321,9 @@ sub AssignLoop() TBDebugTimeStamp("mapper loop started"); while (1) { - chat("Assign run $currentrun\n"); + chat("Mapper loop $currentrun\n"); - my $prefix = ($debug || $regression ? "$pid-$eid" : "$pid-$eid-$$"); + my $prefix = "$pid-$eid-$$"; # # When precheck is on, we only do one run in impotent mode and exit. @@ -469,9 +469,24 @@ sub RunAssign($$) return -1; } close(VTOPFILE); + system("/bin/cp -fp $vtopfile ${pid}-${eid}.vtop") if ($debug); return 0 if ($toponly); + if (! ($impotent || $regression || $alloconly)) { + if ($experiment->Update({"maximum_nodes" => $vtop->maximum_nodes(), + "minimum_nodes" => $vtop->minimum_nodes(), + "virtnode_count"=> $vtop->virtnodecount() })){ + tberror("Could not update min/max/virt nodes for $experiment\n"); + return -1; + } + } + # New solution each time. + $vtop->ClearSolution(); + + goto skipassign + if ($vtop->nodecount() == $vtop->genicount()); + # Debugging hack for regression mode. Avoid really long assign runs. if ($regression && $noassign) { if (! -e "assign.log") { @@ -550,17 +565,9 @@ sub RunAssign($$) tberror("Failure in ptopgen\n"); return -1; } + system("/bin/cp -fp $ptopfile ${pid}-${eid}.ptop") if ($debug); TBDebugTimeStamp("ptopgen finished"); - if (! ($impotent || $regression || $alloconly)) { - if ($experiment->Update({"maximum_nodes" => $vtop->maximum_nodes(), - "minimum_nodes" => $vtop->minimum_nodes(), - "virtnode_count"=> $vtop->virtnodecount() })){ - tberror("Could not update min/max/virt nodes for $experiment\n"); - return -1; - } - } - # Run assign my $cmd = "assign"; $cmd .= "-new" @@ -650,7 +657,8 @@ sub RunAssign($$) # purposes We do not call it .log though, since we do not want it # copied out to the user directory every swapin. See Experiment.pm # - system("/bin/cp assign.log ${prefix}.assign"); + system("/bin/cp -fp assign.log ${prefix}.assign"); + system("/bin/cp -fp assign.log ${pid}-${eid}.assign") if ($debug); if ($assignexitcode) { print "Assign exited with $assignexitcode\n" if ($debug); @@ -680,9 +688,6 @@ sub RunAssign($$) print("Could not open assign logfile! $!\n"); return -1; } - # New solution each time. - $vtop->ClearSolution(); - TBDebugTimeStamp("ReadSolution started"); if ($vtop->ReadTextSolution(*ASSIGNFP) != 0) { print("Could not parse assign logfile! $!\n"); @@ -690,8 +695,9 @@ sub RunAssign($$) } close(ASSIGNFP); TBDebugTimeStamp("ReadSolution ended"); + skipassign: - if (defined($vtop->genirspec())) { + if (defined($vtop->genicount())) { TBDebugTimeStamp("Map Geni Resources Started"); if ($vtop->MapResources() != 0) { print("Could not map external resources! $!\n"); diff --git a/tbsetup/tbswap.in b/tbsetup/tbswap.in index a7949d7f6..7c178feb7 100644 --- a/tbsetup/tbswap.in +++ b/tbsetup/tbswap.in @@ -1021,7 +1021,7 @@ sub doSwapin($) { if ($isvirt) { push(@virtnodes, $node); } - elsif (!$isrem) { + else { push(@physnodes, $node); } } @@ -1234,16 +1234,19 @@ sub doSwapin($) { "where r.pid='$pid' and r.eid='$eid'"); while (my ($node,$allocstate) = $db_result->fetchrow_array) { + print STDERR "A: $node, $allocstate\n"; + # - # If the node is INIT_CLEAN, leave it alone. It will still get - # rebooted, but will not falsely be tagged as dirty. This is - # important for vnodes too, where INIT_CLEAN indicated the vnode - # does not even exist yet (plab nodes). + # INIT_CLEAN and INIT_DIRTY indicate brand new nodes. Leave + # the state alone for os_setup so it knows. REBOOT means libvtop + # knows something we do not, so do not change that either. # - if ($allocstate ne TBDB_ALLOCSTATE_RES_INIT_CLEAN()) { + if ($allocstate ne TBDB_ALLOCSTATE_RES_INIT_CLEAN() && + $allocstate ne TBDB_ALLOCSTATE_RES_INIT_DIRTY() && + $allocstate ne TBDB_ALLOCSTATE_RES_REBOOT()) { TBSetNodeAllocState($node, ($needreboot ? - TBDB_ALLOCSTATE_RES_INIT_DIRTY() : + TBDB_ALLOCSTATE_RES_REBOOT() : TBDB_ALLOCSTATE_RES_RECONFIG())); } } -- GitLab