Commit c17ecb9b authored by Leigh B. Stoller's avatar Leigh B. Stoller

Checkpoint

parent 503bb661
......@@ -115,7 +115,8 @@ CREATE TABLE `geni_slices` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Geni Slivers. Created on components (by CMs of course).
# Geni Slivers. Created on components (by CMs of course). Locally, a sliver
# corresponds to an allocated node.
#
DROP TABLE IF EXISTS `geni_slivers`;
CREATE TABLE `geni_slivers` (
......@@ -123,6 +124,7 @@ CREATE TABLE `geni_slivers` (
`uuid` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`node_id` varchar(32) default NULL,
`created` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
......@@ -130,3 +132,20 @@ CREATE TABLE `geni_slivers` (
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Table to remember the tickets that a component manager hands out.
#
DROP TABLE IF EXISTS `geni_tickets`;
CREATE TABLE `geni_tickets` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`owner_uuid` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`redeem_before` datetime default NULL,
`valid_until` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
`ticket_string` text,
PRIMARY KEY (`idx`),
INDEX `owner_uuid` (`owner_uuid`),
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -24,8 +24,11 @@ use Genixmlrpc;
use GeniResponse;
use GeniTicket;
use GeniSliver;
use GeniUser;
use libtestbed;
use emutil;
# Hate to import all this crap; need a utility library.
use libdb qw(TBGetUniqueIndex TBcheck_dbslot TBDB_CHECKDBSLOT_ERROR);
use User;
use English;
use Data::Dumper;
use Experiment;
......@@ -38,6 +41,8 @@ my $TBAUDIT = "@TBAUDITEMAIL@";
my $BOSSNODE = "@BOSSNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $CREATEEXPT = "$TB/bin/batchexp";
my $NALLOC = "$TB/bin/nalloc";
#
# Respond to a GetTicket request. No worries about credentials yet; we
......@@ -63,26 +68,75 @@ sub GetTicket($)
}
#
# If the underlying experiment does not exist, need to create
# a holding experiment.
# XXX Should we create a local geni_slices record in the DB?
#
if (0) {
#
# If the underlying experiment does not exist, need to create
# a holding experiment. All these are going to go into the same
# project for now. Generally, users for non-local slices do not
# have local accounts or directories.
#
# An rspec is a structure with a node count. :-)
my $experiment = Experiment->Lookup($slice_uuid);
if (!defined($experiment)) {
#
# Form an eid for the experiment.
#
my $eid = "slice" . TBGetUniqueIndex('next_sliceid', 1);
# Note the -h option; allows experiment with no NS file.
system("$CREATEEXPT -q -i -w -E 'Geni Slice Experiment' ".
"-h '$slice_uuid' -p genislices -e $eid");
if ($?) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal Error");
}
$experiment = Experiment->Lookup($slice_uuid);
}
#
# Find out how many nodes are available and grant that many. Silly, eh?
# An rspec is a structure that requests a specific node. If that node
# is available, then reserve it. Otherwise the ticket cannot be
# granted.
#
$rspec->{'granted'} = $rspec->{'requested'};
my $node_id = $rspec->{'node_id'};
my $pid = $experiment->pid();
my $eid = $experiment->eid();
if (defined($node_id) && $node_id =~ /^(\w*)$/) {
$node_id = $1;
}
else {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper node id");
}
}
#
# Return a signed ticket.
# Create the ticket first, before allocating the node.
#
my $ticket = GeniTicket->Create($slice_uuid, $owner_uuid, $rspec);
if (!defined($ticket)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniTicket object");
}
if (0) {
# Nalloc might fail if the node gets picked up by someone else.
# system("$NALLOC $pid $eid $node_id");
if (($? >> 8) < 0) {
$ticket->Delete();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Allocation failure");
}
elsif (($? >> 8) > 0) {
$ticket->Delete();
return GeniResponse->Create(GENIRESPONSE_UNAVAILABLE, undef,
"Could not allocate node\n");
}
}
if ($ticket->Sign() != 0) {
# Release will free the node.
$ticket->Release();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not sign Ticket");
}
......@@ -110,6 +164,12 @@ sub CreateSliver($)
"Could not create GeniTicket object");
}
my $experiment = Experiment->Lookup($ticket->slice_uuid());
if (!defined($experiment)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No local experiment for slice");
}
#
# XXX TODO: Need to verify the invoking user is the one in the ticket.
#
......@@ -118,5 +178,17 @@ sub CreateSliver($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniSliver object");
}
#
# Provision the slice. Okay, we already allocated the node above,
# so this should just work, unless the node has been released cause
# it has been too long.
#
if ($sliver->Provision() != 0) {
$sliver->Delete();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not provision sliver");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, 0, "Wow!");
}
......@@ -36,7 +36,7 @@ my $TBAPPROVAL = "@TBAPPROVALEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $BOSSNODE = "@BOSSNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $GENICENTRAL = "https://myboss.little-emulab-bsd61.testbed.emulab.net/protogeni/xmlrpc";
#
# Discover resources for a slice (local experiment). This contacts Geni
......@@ -76,7 +76,7 @@ sub DiscoverResources($$)
#
sub GetTicket($$$$)
{
my ($experiment, $component, $requested, $pref) = @_;
my ($experiment, $component, $rspec, $pref) = @_;
#
# XXX
......@@ -87,9 +87,6 @@ sub GetTicket($$$$)
return -1;
}
my $rspec = { "requested" => $requested,
"granted" => 0};
my $response =
Genixmlrpc::CallMethodHTTP($component, "CM::GetTicket",
{ "slice_uuid" => $experiment->uuid(),
......@@ -99,8 +96,6 @@ sub GetTicket($$$$)
return -1
if (!defined($response));
print Dumper($response);
return -1
if ($response->code() != GENIRESPONSE_SUCCESS);
......
......@@ -80,9 +80,7 @@ sub RegisterSlice($)
{ "hrn" => $hrn,
"uuid" => $experiment->uuid(),
"creator_uuid" => $user->uuid()});
print Dumper($response);
return $response->code();
}
......
......@@ -164,7 +164,7 @@ sub Delete($)
{
my ($self) = @_;
return 0
return -1
if (! ref($self));
my $idx = $self->idx();
......
......@@ -20,6 +20,8 @@ use GeniDB;
# Hate to import all this crap; need a utility library.
use libdb qw(TBGetUniqueIndex);
use libtestbed;
use Experiment;
use Node;
use English;
use Data::Dumper;
use File::Temp qw(tempfile);
......@@ -33,6 +35,9 @@ my $BOSSNODE = "@BOSSNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $SIGNCRED = "$TB/sbin/signgenicred";
my $AVAIL = "$TB/sbin/avail";
my $NALLOC = "$TB/bin/nalloc";
my $NFREE = "$TB/bin/nfree";
# Cache of instances to avoid regenerating them.
my %slivers = ();
......@@ -108,12 +113,13 @@ sub Create($$)
}
my $slice_uuid = $ticket->slice_uuid();
my $owner_uuid = $ticket->owner_uuid();
my $node_id = $ticket->rspec()->{'node_id'};
# Now tack on other stuff we need.
push(@insert_data, "created=now()");
push(@insert_data, "idx='$idx'");
push(@insert_data, "uuid='$uuid'");
push(@insert_data, "node_id='$node_id'");
push(@insert_data, "creator_uuid='$owner_uuid'");
push(@insert_data, "slice_uuid='$slice_uuid'");
......@@ -131,6 +137,72 @@ sub slice_uuid($) { return field($_[0], "slice_uuid"); }
sub creator_uuid($) { return field($_[0], "creator_uuid"); }
sub created($) { return field($_[0], "created"); }
sub cm_idx($) { return field($_[0], "cm_idx"); }
sub node_id($) { return field($_[0], "node_id"); }
#
# Delete the sliver. The sliver should not be provisioned when this done.
#
sub Delete($)
{
my ($self) = @_;
return -1
if (! ref($self));
my $idx = $self->idx();
DBQueryWarn("delete from geni_slivers where idx='$idx'")
or return -1;
return 0;
}
#
# Get the experiment for the slice this sliver belongs to.
#
sub GetExperiment($)
{
my ($self) = @_;
return undef
if (! ref($self));
return Experiment->Lookup($self->slice_uuid());
}
#
# Provision a slice. We actually did this when the ticket was requested.
#
sub Provision($)
{
return 0;
}
#
# Unprovision a sliver.
#
sub UnProvision($)
{
my ($self) = @_;
return -1
if (! ref($self));
my $experiment = Experiment->Lookup($self->slice_uuid());
my $node_id = $self->node_id();
my $node = Node->Lookup($node_id);
return -1
if (!defined($node));
my $reservation = Node->Reservation();
if (defined($reservation) && $reservation->SameExperiment($experiment)) {
my $pid = $experiment->pid();
my $eid = $experiment->eid();
system("$NFREE $pid $eid $node_id");
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -20,6 +20,8 @@ use vars qw(@ISA @EXPORT);
use lib '@prefix@/lib';
use GeniDB;
use libtestbed;
use Experiment;
use libdb qw(TBGetUniqueIndex);
use English;
use XML::Simple;
use XML::LibXML;
......@@ -35,12 +37,11 @@ my $BOSSNODE = "@BOSSNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $SIGNCRED = "$TB/sbin/signgenicred";
my $NFREE = "$TB/bin/nfree";
#
# Create an unsigned ticket object from the raw rspec.
#
# Should we keep track of tickets locally in the DB?
#
sub Create($$$$;$$)
{
my ($class, $slice_uuid, $owner_uuid,
......@@ -52,6 +53,7 @@ sub Create($$$$;$$)
$self->{'owner_uuid'} = $owner_uuid;
$self->{'ticket_string'} = $ticket_string;
$self->{'ticket'} = undef;
$self->{'idx'} = undef; # Only set when in DB.
$self->{'component'} = $component;
bless($self, $class);
......@@ -59,12 +61,14 @@ sub Create($$$$;$$)
}
# accessors
sub field($$) { return ($_[0]->{$_[1]}); }
sub idx($) { return field($_[0], "idx"); }
sub rspec($) { return field($_[0], "rspec"); }
sub uuid($) { return field($_[0], "slice_uuid"); }
sub slice_uuid($) { return field($_[0], "slice_uuid"); }
sub owner_uuid($) { return field($_[0], "owner_uuid"); }
sub ticket($) { return field($_[0], "ticket"); }
sub asString($) { return field($_[0], "ticket_string"); }
sub ticket_string($) { return field($_[0], "ticket_string"); }
sub component($) { return field($_[0], "component"); }
#
......@@ -108,6 +112,26 @@ sub CreateFromSignedTicket($$)
return undef;
}
#
# We save copies of the tickets we hand out; lets find that record
# in the DB, just to verify.
#
my ($serial_node) = $doc->getElementsByTagName("serial");
return undef
if (!defined($serial_node));
my $serial = $serial_node->to_literal();
if (! ($serial =~ /^\w+$/)) {
print STDERR "Invalid serial in ticket\n";
return undef;
}
my $query_result =
DBQueryWarn("select * from geni_tickets where idx='$serial'");
if (!$query_result || !$query_result->numrows) {
print STDERR "Could not find the ticket in te DB\n";
return undef;
}
my $self = {};
$self->{'rspec'} = $rspec;
$self->{'slice_uuid'} = $slice_uuid;
......@@ -115,6 +139,7 @@ sub CreateFromSignedTicket($$)
$self->{'ticket_string'} = $ticket_string;
$self->{'xmlref'} = $doc;
$self->{'component'} = undef;
$self->{'idx'} = $serial;
bless($self, $class);
print Dumper($self);
......@@ -122,6 +147,25 @@ sub CreateFromSignedTicket($$)
return $self;
}
#
# Might have to delete this fron the DB, as with an error handing out a ticket.
#
sub Delete($)
{
my ($self) = @_;
return -1
if (! ref($self));
if (defined($self->idx())) {
my $idx = $self->idx();
DBQueryWarn("delete from geni_tickets where idx='$idx'")
or return -1;
}
return 0;
}
#
# Populate the ticket with some stuff, which right now is just the
# number of node we are willing to grant.
......@@ -130,13 +174,44 @@ sub Grant($$)
{
my ($self, $count) = @_;
return 0
return -1
if (! ref($self));
$self->{'count'} = $count;
return 0;
}
#
# Store the given ticket in the DB. We only do this for signed tickets
# which we hand out, so we have a record of them.
#
sub Store($$)
{
my ($self, $idx) = @_;
my @insert_data = ();
my $slice_uuid = $self->slice_uuid();
my $owner_uuid = $self->owner_uuid();
# Now tack on other stuff we need.
push(@insert_data, "created=now()");
push(@insert_data, "idx='$idx'");
push(@insert_data, "slice_uuid='$slice_uuid'");
push(@insert_data, "owner_uuid='$owner_uuid'");
my $safe_ticket = DBQuoteSpecial($self->ticket_string());
push(@insert_data, "ticket_string=$safe_ticket");
# Insert into DB.
DBQueryWarn("insert into geni_tickets set " . join(",", @insert_data))
or return -1;
# If sucessfully stored, set the idx field so we know.
$self->{'idx'} = $idx;
return 0;
}
#
# Sign the ticket before returning it. We capture the output, which is
# in XML.
......@@ -147,27 +222,29 @@ sub Sign($)
return -1
if (!ref($self));
# Every Ticket gets a new unique index, which is used in the xml:id below.
# Also used for the DB insertion below.
my $idx = TBGetUniqueIndex('next_ticket', 1);
my $slice_uuid = $self->slice_uuid();
my $owner_uuid = $self->owner_uuid();
my $requested = $self->rspec()->{'requested'};
my $granted = $self->rspec()->{'granted'};
my $rspec_xml = XMLout($self->rspec(), "NoAttr" => 1);
$rspec_xml =~ s/opt\>/rspec\>/g;
#
# Create a template xml file to sign.
#
my $template =
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n".
"<credential xml:id=\"Ref1\">\n".
"<credential xml:id=\"ref1\">\n".
" <type>ticket</type>\n".
" <serial>$idx</serial>\n".
" <owner_uuid>$owner_uuid</owner_uuid>\n".
" <this_uuid>$slice_uuid</this_uuid>\n".
" <ticket>\n".
" <can_delegate>1</can_delegate>\n".
" <rspec>\n".
" <requested>$requested</requested>\n".
" <granted>$granted</granted>\n".
" </rspec>\n".
" $rspec_xml\n".
" </ticket>\n".
"</credential>\n";
......@@ -193,6 +270,49 @@ sub Sign($)
close(SIGNER);
$self->{'ticket_string'} = $ticket;
$self->Store($idx) == 0
or return -1;
return 0;
}
#
# Get the experiment for the slice this sliver belongs to.
#
sub GetExperiment($)
{
my ($self) = @_;
return undef
if (! ref($self));
return Experiment->Lookup($self->slice_uuid());
}
#
# Release a ticket. Need to release the nodes ...
#
sub Release($)
{
my ($self) = @_;
return undef
if (! ref($self));
my $experiment = Experiment->Lookup($self->slice_uuid());
my $node_id = $self->rspec()->{'node_id'};
my $node = Node->Lookup($node_id);
return -1
if (!defined($node));
my $reservation = Node->Reservation();
if (defined($reservation) && $reservation->SameExperiment($experiment)) {
my $pid = $experiment->pid();
my $eid = $experiment->eid();
system("$NFREE $pid $eid $node_id");
}
$self->Delete();
return 0;
}
......
......@@ -155,7 +155,7 @@ sub RegisterUser($)
}
#
# Register a new Geni slice in the DB. Returns the UUID (GID).
# Register a new Geni slice in the DB.
#
sub RegisterSlice($)
{
......
......@@ -16,13 +16,22 @@ use English;
#GeniSAClient::RegisterUser(User->ThisUser());
my $experiment = Experiment->Lookup("testbed", "one-node");
my $slice;
my $resources;
my $ticket;
my $sliver;
my $rspec = { "node_id" => "pc159",
"granted" => 0};
GeniCMClient::DiscoverResources($experiment, \$resources);
GeniCMClient::GetTicket($experiment, $resources->[0], 1, \$ticket);
print $resources->[0] . "\n";
GeniCMClient::GetTicket($experiment, $resources->[0], $rspec, \$ticket);
open(T, "> /tmp/T");
print T $ticket->asString();
close(T);
exit(0);
if ($ticket) {
GeniCMClient::CreateSliver($experiment, $ticket, \$sliver);
}
......
......@@ -115,7 +115,8 @@ CREATE TABLE `geni_slices` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Geni Slivers. Created on components (by CMs of course).
# Geni Slivers. Created on components (by CMs of course). Locally, a sliver
# corresponds to an allocated node.
#
DROP TABLE IF EXISTS `geni_slivers`;
CREATE TABLE `geni_slivers` (
......@@ -123,6 +124,7 @@ CREATE TABLE `geni_slivers` (
`uuid` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`node_id` varchar(32) default NULL,
`created` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
......@@ -130,3 +132,20 @@ CREATE TABLE `geni_slivers` (
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Table to remember the tickets that a component manager hands out.
#
DROP TABLE IF EXISTS `geni_tickets`;
CREATE TABLE `geni_tickets` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`owner_uuid` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,