diff --git a/db/libGeni.pm.in b/db/libGeni.pm.in
index 39d65d44218e0ef4f308926f61e5f87dcc962b61..41c6cbea55d17b74dc7a8bbc8bf82ba0db2cbed1 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 cec5ef1fbf756e0a478f0a02e5be9b80db9bbce6..176502b21c395afadc3861236d8638e756c3f302 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 f6e911501a9f821b3464ba58a42114e9da00406b..f372a709f68c208ec0c0a327f05e57c63480f281 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 0ee9bc7eed5794fa4fc76b0a5ac9875f46cb10ac..d019af1bf750840a36b8ebfb284793e08e058987 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 c888cd381ef2d4ff6af7b82001a92daba3bd4d3e..4f14b3782ac0ea41907d97f381fa5b3d89454c44 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 b851c89d80ddf2dfbeafe96ccdf8aa5ee724798f..95d456f1862af79a310fa4f823c55979b5fed451 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 dc9f8f3b349434b4ed399e7ed1886bd5410b5404..d326f89dcd9d54fa5e43cdf4adc07ea097b077b1 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 e45c78ff565ca34d33493ae2d0bb1d760b7972d1..ec5c815603c67f89c791a5cbc1b129c5dedcde30 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 7a1e462ce169ee5e5353e2b60cee3e5e2a90c765..ddc0c55677dcabaefae0da1ee95ad686cfb0cb63 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 a7949d7f64c19cf9da1899e96f7f41af82943923..7c178feb724480ad0d1c8a26e54592a7bdb327f5 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()));
}
}