Commit 4bf3c2bf authored by Leigh Stoller's avatar Leigh Stoller

Merge in my cooked mode changes

parent c3b4d6d3
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005-2009 University of Utah and the Flux Group.
# Copyright (c) 2005-2010 University of Utah and the Flux Group.
# All rights reserved.
#
package Experiment;
......@@ -324,6 +324,8 @@ sub security_level($) { return field($_[0], 'security_level');}
sub linktest_pid($) { return field($_[0], 'linktest_pid');}
sub linktest_level($) { return field($_[0], 'linktest_level');}
sub logfile($) { return field($_[0], 'logfile');}
sub eventkey($) { return field($_[0], 'eventkey');}
sub keyhash($) { return field($_[0], 'keyhash');}
sub paniced($) { return field($_[0], 'paniced');}
sub cpu_usage($) { return field($_[0], 'cpu_usage');}
sub encap_style($) { return field($_[0], 'encap_style');}
......
......@@ -518,6 +518,29 @@ sub Update($$)
return Refresh($self);
}
#
# Lookup a specific attribute in the type capabilities table.
#
sub TypeCapability($$$)
{
my ($self, $capkey, $pcapval) = @_;
return -1
if (!ref($self));
my $itype = $self->interface_type();
my $query_result =
DBQueryWarn("select capval from interface_capabilities ".
"where type='$itype' and capkey=$capkey");
return -1
if (!$query_result || !$query_result->numrows());
my ($capval) = $query_result->fetchrow_array();
$pcapval = $capval;
return 0;
}
##############################################################################
package Interface::VInterface;
use libdb;
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005-2009 University of Utah and the Flux Group.
# Copyright (c) 2005-2010 University of Utah and the Flux Group.
# All rights reserved.
#
package Node;
......@@ -2458,5 +2458,25 @@ sub GetOsids($) {
$self->{"DBROW"}{"next_boot_osid"});
}
#
# Look for a widearea node by its external node id.
#
sub LookupWideArea($$)
{
my ($class, $external_node_id) = @_;
my $safe_id = DBQuoteSpecial($external_node_id);
my $query_result =
DBQueryWarn("select node_id from widearea_nodeinfo ".
"where external_node_id=$safe_id");
return undef
if (!defined($query_result) || !$query_result->numrows);
my ($node_id) = $query_result->fetchrow_array();
return Node->Lookup($node_id);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2009 University of Utah and the Flux Group.
# Copyright (c) 2009, 2010 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
......@@ -52,6 +52,7 @@ sub ClearAll();
sub StartAll();
sub WaitAll();
sub PurgeAll();
sub RenewAll();
#
# Turn off line buffering on output
......@@ -82,7 +83,8 @@ my $pid = shift;
my $eid = shift;
my $action = shift;
if ($action =~ /^(alloc|free|clear|wait|purge|start|register|unregister)$/) {
if ($action =~
/^(alloc|free|clear|wait|purge|start|register|unregister|renew)$/) {
$action = $1;
}
else {
......@@ -166,6 +168,10 @@ SWITCH: for ($action) {
PurgeAll();
last SWITCH;
};
/^renew$/ && do {
RenewAll();
last SWITCH;
};
fatal("Unknown action $action");
}
exit($exitval);
......@@ -229,8 +235,10 @@ sub PurgeAll()
foreach my $resource (@resources) {
$resource->Purge($this_user) == 0
or fatal("Could not purge resources from $resource");
$resource->Delete() == 0
or fatal("Could not delete $resource");
}
UnRegister();
return 0;
}
......@@ -246,6 +254,12 @@ sub WaitAll()
fatal("Cannot wait on slivers!\n");
}
sub RenewAll()
{
libGeni::RenewSlivers($experiment, 1) == 0 or
fatal("Cannot renew resources");
}
#
# Register.
#
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2009 University of Utah and the Flux Group.
# Copyright (c) 2009, 2010 University of Utah and the Flux Group.
# All rights reserved.
#
package libGeni;
......@@ -50,23 +50,19 @@ sub UnRegister($)
return GeniEmulab::UnRegisterExperiment($experiment);
}
#
# Quickie solution.
#
sub RenewAllSlivers()
sub RenewSlivers($;$)
{
#
# Hand this off ...
#
return GeniResource::RenewAll();
my ($experiment, $force) = @_;
return GeniResource::RenewExperimentResources($experiment, $force);
}
#
# Map rspec to resources using assign.
#
sub MapResources($$$)
sub MapResources($$$$)
{
my ($experiment, $user, $rspec) = @_;
my ($experiment, $user, $rspec, $verbose) = @_;
my %cm_urns = ();
my %fragments = ();
my %nodemap = ();
......@@ -112,6 +108,7 @@ sub MapResources($$$)
$node_cms{$ref->{'virtual_id'}} = $cm;
$nodemap{$ref->{'virtual_id'}} = $ref;
}
#
# As above, need to split the interfaces into the correct fragments.
#
......@@ -119,6 +116,15 @@ sub MapResources($$$)
foreach my $ref (@{ $rspec->{'link'} }) {
my $linkname = $ref->{'virtual_id'};
# 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'};
......@@ -206,12 +212,16 @@ sub MapResources($$$)
my $solution =
eval { XMLin($soln, KeyAttr => [],
ForceArray => ["node", "link", "interface",
"interface_ref", "linkendpoints"]) };
"interface_ref", "linkendpoints",
"component_manager"]) };
if ($@) {
print STDERR "XMLin error reading $soln: $@\n";
return -1;
}
# print STDERR Dumper($solution);
if ($verbose) {
print STDERR "Solution for $resource\n";
print STDERR Dumper($solution);
}
foreach my $ref (@{ $solution->{'node'} }) {
my $virtual_id = $ref->{'virtual_id'};
......@@ -233,6 +243,11 @@ sub MapResources($$$)
my $iface_id = $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.
......@@ -244,7 +259,28 @@ sub MapResources($$$)
}
}
}
# print STDERR Dumper($rspec);
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;
}
}
$ref->{'component_manager'} = [@cms];
}
}
if ($verbose) {
print STDERR "Final rspec:\n";
print STDERR Dumper($rspec);
}
return 0;
}
......@@ -252,6 +288,7 @@ sub GetTickets($$$$)
{
my ($experiment, $impotent, $user, $rspec) = @_;
my %cm_urns = ();
my %node_cms = ();
Register($experiment, $user) == 0
or return -1;
......@@ -272,6 +309,7 @@ sub GetTickets($$$$)
my $cm = GeniHRN::Generate($auth, "authority", "cm");
$cm_urns{$cm} = $cm;
$node_cms{$ref->{'virtual_id'}} = $cm;
#
# This is how we get the client side to do cooked mode properly.
......@@ -279,7 +317,6 @@ sub GetTickets($$$$)
$ref->{'tarfiles'} = "/usr/local/etc/emulab ".
"$TBDOCBASE/downloads/geniclient.tar";
}
#print STDERR Dumper($rspec);
#
# Get the resource objects.
......@@ -295,6 +332,7 @@ sub GetTickets($$$$)
}
$cm_urns{$cm} = $resource;
}
#
# Ask for tickets.
#
......@@ -343,9 +381,11 @@ sub RedeemTickets($$$)
# Map the local nodes to the external nodes. This just sets some DB
# state for now.
#
sub MapNodes($)
sub MapNodes($$)
{
my ($experiment) = @_;
my ($experiment, $verbose) = @_;
my $eventkey = $experiment->eventkey();
my $keyhash = $experiment->keyhash();
my %ifacemap = ();
#
......@@ -361,11 +401,21 @@ sub MapNodes($)
return -1
if (!defined($manifest));
print STDERR Dumper($manifest);
foreach my $ref (@{ $manifest->{'node'} }) {
my $sliver_urn = $ref->{'sliver_urn'};
my $vname = $ref->{'virtual_id'};
my $node = $experiment->VnameToNode($vname);
#
# The manifest can include nodes from other CMs. There will not
# be a sliver urn in that case.
#
my $component_manager_urn = $ref->{'component_manager_urn'};
next
if (!defined($sliver_urn) && !defined($component_manager_urn));
my $node = $experiment->VnameToNode($vname);
if (!defined($node)) {
print STDERR
"MapNodes: Could not locate node $vname in $experiment\n";
......@@ -385,8 +435,9 @@ sub MapNodes($)
#
# This is how we get the client side to do cooked mode properly.
#
$ref->{'startup_command'} = "/usr/local/etc/emulab/rc.pgeni ".
"-s $BOSSNODE -u '$sliver_urn' boot";
$ref->{'startup_command'} =
"sudo /usr/local/etc/emulab/rc/rc.pgeni ".
"-s $BOSSNODE -k $eventkey,$keyhash -u '$sliver_urn' boot";
# Interface map for loop below.
if (exists($ref->{'interface'})) {
......@@ -401,6 +452,20 @@ sub MapNodes($)
foreach my $ref (@{ $manifest->{'link'} }) {
my $linkname = $ref->{"virtual_id"};
my $interfaces = $ref->{'interface_ref'};
my %managers = map { $_->{'id'} => $_->{'id'} }
@{ $ref->{'component_manager'} };
# Skip tunnels in this loop for now.
next
if (exists($ref->{'link_type'}) &&
$ref->{'link_type'} eq "tunnel");
#
# The manifest can include links for other CMs. Skip those
# for now.
#
next
if (!exists($managers{$resource->manager_urn()}));
foreach my $ifaceref (@{ $interfaces }) {
my $vname = $ifaceref->{'virtual_node_id'};
......@@ -418,18 +483,18 @@ sub MapNodes($)
if (!defined($iface)) {
print STDERR "Could not determine iface for" .
"$vname,$iface_id\n";
print Dumper($manifest);
print STDERR Dumper($manifest);
return -1;
}
my $interface = Interface->LookupByIface($node,$iface);
if (!defined($interface)) {
print STDERR "Could not map iface for $node,$iface\n";
print Dumper($manifest);
print STDERR Dumper($manifest);
return -1;
}
if (! ($MAC =~ /^[\w]*$/)) {
print STDERR "Bad mac '$MAC' for $node,$iface\n";
print Dumper($manifest);
print STDERR Dumper($manifest);
return -1;
}
if ($interface->Update({"mac" => "$MAC"})) {
......@@ -439,6 +504,11 @@ sub MapNodes($)
}
}
}
# The manifest was changed above.
if ($resource->UpdateManifest($manifest)) {
print STDERR "Could not store manifest for $resource\n";
return -1;
}
}
return 0;
}
......@@ -511,17 +581,46 @@ sub WaitForSlivers($$@)
}
#
# Build a map of the nodes.
# Build a map of the nodes. I made a real mess of this in Version 1.
#
foreach my $resource (@resources) {
my $manifest = $resource->Manifest();
return -1
if (!defined($manifest));
foreach my $ref (@{ $manifest->{'node'} }) {
my $vname = $ref->{'virtual_id'};
my $urn = $ref->{'component_urn'};
my $node = $experiment->VnameToNode($vname);
#
# The manifest can include nodes from other CMs. There will not
# be a sliver urn in that case.
#
my @nodelist = $experiment->NodeList(0, 1);
foreach my $node (@nodelist) {
my $sliver_urn = $ref->{'sliver_urn'};
next
if (!defined($sliver_urn));
if (!defined($node)) {
print STDERR "WaitForSlivers: ".
"Could not locate node $vname in $experiment\n";
return -1;
}
if ($resource->ManagerVersion() == 1.0) {
my ($domain,undef,$node_id) = GeniHRN::Parse($urn);
$urn = GeniHRN::Generate($domain, "sliver", $node_id);
$nodemap{$urn} = $node;
}
else {
next
if (!defined($node->external_resource_id()) ||
$node->external_resource_id() eq "");
$nodemap{$node->external_resource_id()} = $node;
}
$node->Refresh();
}
}
#
# Now we use parrun again to get the sliver status. We are waiting
......@@ -541,9 +640,23 @@ sub WaitForSlivers($$@)
}
print STDERR Dumper($ref);
foreach my $key (keys(%{ $ref->{'detailsNew'} })) {
my $val = $ref->{'detailsNew'}->{$key};
my $node = $nodemap{$key};
foreach my $key (keys(%{ $ref->{'details'} })) {
my $val = $ref->{'details'}->{$key};
my ($status, $node);
if ($resource->ManagerVersion() == 1.0) {
$node = $nodemap{$key};
$status = $val;
}
elsif ($resource->ManagerVersion() == 2.0) {
$node = $nodemap{$key};
$status = $val->{'status'};
}
else {
print STDERR
"*** WaitForSlivers: Unknown version on $resource\n";
next;
}
if (!defined($node)) {
print STDERR "No node in map for $key ($resource)\n";
......@@ -555,14 +668,14 @@ sub WaitForSlivers($$@)
#
# Only send on state change to avoid multiple events.
#
if ($val eq "ready" && !$node->IsUp()) {
print STDERR " Sending ISUP event.\n";
$node->SetEventState(TBDB_NODESTATE_ISUP());
if ($status eq "ready" && !$node->IsUp()) {
# print STDERR " Sending ISUP event.\n";
# $node->SetEventState(TBDB_NODESTATE_ISUP());
}
elsif ($val eq "failed" &&
elsif ($status eq "failed" &&
$node->eventstate() ne TBDB_NODESTATE_TBFAILED) {
print STDERR " Sending TBFAILED event.\n";
$node->SetEventState(TBDB_NODESTATE_TBFAILED());
# print STDERR " Sending TBFAILED event.\n";
# $node->SetEventState(TBDB_NODESTATE_TBFAILED());
}
else {
$notready++;
......@@ -646,8 +759,11 @@ sub DeleteAllSlivers($$)
foreach my $resource (@resources) {
print STDERR "Deleting sliver for $resource\n";
if ($resource->Clear($user)) {
print STDERR
"DeleteSlivers: Could not delete sliver for $resource\n";
print STDERR "DeleteSlivers: Could not clear $resource\n";
return -1;
}
if ($resource->Delete()) {
print STDERR "DeleteSlivers: Could not delete $resource\n";
return -1;
}
}
......
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2000-2008 University of Utah and the Flux Group.
* Copyright (c) 2000-2010 University of Utah and the Flux Group.
* All rights reserved.
*/
......@@ -583,6 +583,29 @@ event_notification_alloc(event_handle_t handle, address_tuple_t tuple)
notification->pubsub_notification = pubsub_notification;
notification->has_hmac = 0;
/*
* Event version number
*/
if (!event_notification_set_version(handle, notification, "1.0")) {
ERROR("pubsub_notification_alloc failed to set version number\n");
event_notification_free(handle, notification);
return NULL;
}
#ifdef ELVIN_COMPAT
if (!event_notification_set_elvincompat(handle, notification)) {
ERROR("pubsub_notification_alloc failed to set elvin compat\n");
event_notification_free(handle, notification);
return NULL;
}
#endif
#ifdef ELVIN_COMPAT0
notification->hashtable = elvin_hashtable_alloc(0, &handle->status);
if (notification->hashtable == NULL) {
ERROR("pubsub_notification_alloc failed to allocate hashtable\n");
event_notification_free(handle, notification);
return NULL;
}
#endif
if (tuple == NULL)
return notification;
......@@ -638,6 +661,11 @@ event_notification_free(event_handle_t handle,
pubsub_notification_free(handle->server, notification->pubsub_notification,
&handle->status);
#ifdef ELVIN_COMPAT0
if (notification->hashtable) {
elvin_hashtable_free(notification->hashtable);
}
#endif
free(notification);
return 1;
......@@ -670,10 +698,13 @@ event_notification_clone(event_handle_t handle,
free(clone);
return 0;
}
#ifdef ELVIN_COMPAT0
notification->hashtable = elvin_hashtable_clone(notification->hashtable,
&handle->status);
#endif
clone->has_hmac = notification->has_hmac;
return clone;
}
......@@ -910,7 +941,6 @@ event_notification_put_double(event_handle_t handle,
pubsub_error_fprintf(stderr, &handle->status);
return 0;
}
return 1;
}
......@@ -1487,14 +1517,13 @@ hmac_traverse(void *rock, char *name,
if (!strcmp(name, "__hmac__"))
return 1;
#ifdef ELVIN_COMPAT
/*
* The elvin gateway sticks this flag in, but we need to ignore it
* when doing hmac computation.
* Never include this in hmac computation. See elvin_gateway and
* the elvin compat code below.
*/
if (!strcmp(name, "___elvin_ordered___"))
return 1;
#endif
switch (type) {
case INT32_TYPE:
HMAC_Update(ctx,
......@@ -1535,60 +1564,60 @@ hmac_traverse(void *rock, char *name,
#ifdef ELVIN_COMPAT
static int
hmac_fill_hash(void *rock, char *name,
notify_order_traverse(void *arg, char *name,
pubsub_type_t type, pubsub_value_t value,
pubsub_error_t *status)
pubsub_error_t *error)
{
struct elvin_hashtable *table = (struct elvin_hashtable *) rock;
pubsub_notification_t *pubsub_notification =
(pubsub_notification_t *) arg;
if (elvin_hashtable_add(table, name, value, type, status) == -1)
return 0;
switch (type) {
case STRING_TYPE:
pubsub_notification_add_string(pubsub_notification,
name, value.pv_string, error);
break;
case INT32_TYPE:
pubsub_notification_add_int32(pubsub_notification,
name, value.pv_int32, error);
break;
case INT64_TYPE:
pubsub_notification_add_int64(pubsub_notification,
name, value.pv_int64, error);
break;
case REAL64_TYPE:
pubsub_notification_add_real64(pubsub_notification,
name, value.pv_real64, error);
break;
case OPAQUE_TYPE:
pubsub_notification_add_opaque(pubsub_notification,
name,
(char *)(value.pv_opaque.data),
value.pv_opaque.length,
error);
break;
default:
break;
}
return 1;
}
#endif
static int
notification_hmac(pubsub_notification_t *notification, HMAC_CTX *ctx,
hmac_fill_hash(void *rock, char *name,
pubsub_type_t type, pubsub_value_t value,
pubsub_error_t *status)
{
int retval = 0;
#ifdef ELVIN_COMPAT
struct elvin_hashtable *table;
int elvin_ordered;
struct elvin_hashtable *table = (struct elvin_hashtable *) rock;
if (pubsub_notification_get_int32(notification,
"___elvin_ordered___",
&elvin_ordered, status) == 0) {
if (!pubsub_notification_traverse(notification, hmac_traverse,
ctx, status)) {
return -1;
}
if (elvin_hashtable_add(table, name, value, type, status) == -1) {
ERROR("hmac_fill_hash failure %s: ", name);
pubsub_error_fprintf(stderr, status);
return 0;
}
if ((table = elvin_hashtable_alloc(0, status)) == NULL) {
return -1;
}
else if (!pubsub_notification_traverse(notification, hmac_fill_hash,
table, status)) {
retval = -1;
}
else if (!elvin_hashtable_traverse(table, hmac_traverse,
ctx, status)) {
retval = -1;
}
elvin_hashtable_free(table);
table = NULL;
#else
if (!pubsub_notification_traverse(notification, hmac_traverse,
ctx, status)) {
return -1;
}
#endif
return retval;
return 1;
}
#endif
int
event_notification_insert_hmac(event_handle_t handle,
......@@ -1597,8 +1626,11 @@ event_notification_insert_hmac(event_handle_t handle,
HMAC_CTX ctx;
unsigned char mac[EVP_MAX_MD_SIZE];
int i, len = EVP_MAX_MD_SIZE;
if (0)
#ifdef ELVIN_COMPAT
struct elvin_hashtable *hashtable;
pubsub_notification_t *notecopy;
#endif
if (1)
INFO("event_notification_insert_hmac: %d %s\n",
handle->keylen, handle->keydata);
......@@ -1608,12 +1640,73 @@ event_notification_insert_hmac(event_handle_t handle,
}
#ifdef ELVIN_COMPAT
/*
* Remove this so we recompute the elvin ordering above, since the
* notification might have changed, and the exiting linear order
* will no longer correspond to elvin ordering.
* The point of this is to convert the ordering of the pubsub
* notification into elvin ordering according to its hash
* function, and then create a new notification with that
* order as returned by pubsub_traverse (which is linear). Then
* insert the ___elvin_ordered___ flag. At the client:
*
* 1) With Elvin Compat: That the pubsub notification is in the
* correct order to generate the hmac using pubsub traverse.
* Without ___elvin_ordered___, the client will convert the
* ordering to elvin hash order to generate the hmac.
* 2) No Elvin Compat: The client always uses pubsub traverse, so
* it will get the correct hmac.
*
* This is backwards compatabile with clients linked with old
* versions of the event library, and new versions of the event
* library.
*
* XXX This impl is just a prototype. It would be much more efficient
* to do this as items are added to the notification. Later.
*/
notecopy = pubsub_notification_alloc(handle->server, &handle->status);
if (notecopy == NULL) {
ERROR("event_notification_insert_hmac failed: notecopy alloc\n");
return -1;
}