Commit 4fce3057 authored by Leigh B. Stoller's avatar Leigh B. Stoller

Checkpoint

parent 32d7d504
#
# These are new tables to add to the Emulab DB.
#
#
# Remote SAs register users at Emulab (we export GENI ClearingHouse
# APIs, whatever they are). Local users do not need to be in this table,
# we can get their data via the Emulab libraries.
#
# * This is a GENI ClearingHouse table; remote emulabs have just a
# local users table.
# * Users in this table are users at other emulabs.
# * uid_idx is for indexing into other tables, like user_sslcerts and
# user_pubkeys. No need to have geni versions of those tables, as
# long as we maintain uid_idx uniqueness.
# * sa_idx is an index into another table. SAs in in the prototype are
# other Emulabs.
# * The uid does not have to be unique, except for a given SA.
#
DROP TABLE IF EXISTS `geni_users`;
CREATE TABLE `geni_users` (
`hrn` varchar(256) NOT NULL default '',
`uid` varchar(8) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`archived` datetime default NULL,
`status` enum('active','archived','frozen') NOT NULL default 'frozen',
`name` tinytext,
`email` tinytext,
`sa_idx` int(10) unsigned NOT NULL default '0',
`cert` text,
PRIMARY KEY (`idx`),
KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# All geni components have a GID (UUID->Key) and this
# table stores them all. In protogeni there are not that many components,
# and they mostly refer to other Emulabs.
#
# * This is a GENI Clearinghouse table; Components are remote emulabs.
# * The ID is a GENI dotted name.
# * We store the pubkey for the component here. Remember that
# components self generate their keys.
# * Not worrying about Management Authorities at this point. I think
# the prototype has a single MA, and its operated by hand with
# direct mysql statements.
#
DROP TABLE IF EXISTS `geni_components`;
CREATE TABLE `geni_components` (
`hrn` varchar(256) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`name` tinytext,
`url` tinytext,
`cert` text,
PRIMARY KEY (`idx`),
UNIQUE KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Geni Slice Authorities also have a GID. As with components, SAs are
# mostly other emulabs. GENI users are registered by SAs, as are slices.
#
# * This is a GENI Clearinghouse table; SAs are remote emulabs.
# * The ID is a GENI dotted name.
# * We store the pubkey for the SA here. Remember that SAs self
# generate their keys.
# * The uuid_prefix is the topmost 8 bytes assigned to the SA; all
# UUIDs generated (signed) by and registered must have these top
# 8 bytes. See wiki discussion.
#
DROP TABLE IF EXISTS `geni_sliceauthorities`;
CREATE TABLE `geni_sliceauthorities` (
`id` varchar(8) NOT NULL default '',
`id_idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`uuid_prefix` varchar(8) NOT NULL default '',
`created` datetime default NULL,
`name` tinytext,
`url` tinytext,
`pubkey` text,
PRIMARY KEY (`id_idx`),
UNIQUE KEY `id` (`id`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Geni Slices. Not to be confused with plab_slices ... a geni slice
# is an emulab experiment that spans the entire set of emulabs. SAs
# register geni slices at the Clearinghouse, in this table.
#
# * Slices have UUIDs.
# * The sa_idx refers to the slice authority that created the slice.
# * The creator UUID should already be in the geni_users table, or in
# the local users table.
# * The pubkey bound to the slice is that of the user creating the slice.
#
DROP TABLE IF EXISTS `geni_slices`;
CREATE TABLE `geni_slices` (
`hrn` varchar(256) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`creator_uuid` varchar(40) NOT NULL default '',
`name` tinytext,
`sa_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
UNIQUE KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Geni Slivers. Created on components (by CMs of course).
#
DROP TABLE IF EXISTS `geni_slivers`;
CREATE TABLE `geni_slivers` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`cm_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
UNIQUE KEY `uuid` (`uuid`),
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -13,7 +13,7 @@ include $(OBJDIR)/Makeconf
LIB_SCRIPTS = Protogeni.pm GeniDB.pm GeniUser.pm GeniSAClient.pm \
GeniSlice.pm GeniSA.pm GeniCM.pm GeniCMClient.pm \
test.pl GeniTicket.pm
test.pl GeniTicket.pm GeniSliver.pm
#
# Force dependencies on the scripts so that they will be rerun through
......
......@@ -23,9 +23,12 @@ use GeniDB;
use Genixmlrpc;
use GeniResponse;
use GeniTicket;
use GeniSliver;
use libtestbed;
use emutil;
use English;
use Data::Dumper;
use Experiment;
# Configure variables
my $TB = "@prefix@";
......@@ -44,15 +47,26 @@ my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
sub GetTicket($)
{
my ($argref) = @_;
my $slice_uuid = $argref->{'uuid'};
my $owner_uuid = $argref->{'owner_uuid'};
my $slice_uuid = $argref->{'slice_uuid'};
my $rspec = $argref->{'rspec'};
if (! (defined($slice_uuid) && ($slice_uuid =~ /^[-\w]+$/))) {
return GeniResponse->MalformedArgsResponse();
}
# XXX This needs to come from the SSL environment.
if (! (defined($owner_uuid) && ($owner_uuid =~ /^[-\w]+$/))) {
return GeniResponse->MalformedArgsResponse();
}
if (! defined($rspec)) {
return GeniResponse->MalformedArgsResponse();
}
#
# If the underlying experiment does not exist, need to create
# a holding experiment.
#
#
# An rspec is a structure with a node count. :-)
#
......@@ -63,7 +77,7 @@ sub GetTicket($)
#
# Return a signed ticket.
#
my $ticket = GeniTicket->Create($slice_uuid, $rspec);
my $ticket = GeniTicket->Create($slice_uuid, $owner_uuid, $rspec);
if (!defined($ticket)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniTicket object");
......@@ -72,5 +86,37 @@ sub GetTicket($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not sign Ticket");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $ticket->ticket());
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
$ticket->asString());
}
#
# Create a sliver.
#
sub CreateSliver($)
{
my ($argref) = @_;
my $ticket = $argref->{'ticket'};
if (! (defined($ticket) &&
!TBcheck_dbslot($ticket, "default", "text",
TBDB_CHECKDBSLOT_ERROR))) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"ticket: ". TBFieldErrorString());
}
$ticket = GeniTicket->CreateFromSignedTicket($ticket);
if (!defined($ticket)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniTicket object");
}
#
# XXX TODO: Need to verify the invoking user is the one in the ticket.
#
my $sliver = GeniSliver->Create($ticket);
if (!defined($sliver)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniSliver object");
}
return GeniResponse->Create(GENIRESPONSE_SUCCESS, 0, "Wow!");
}
......@@ -23,6 +23,8 @@ use GeniDB;
use Genixmlrpc;
use GeniResponse;
use GeniTicket;
use GeniSliver;
use User;
use libtestbed;
use English;
use Data::Dumper;
......@@ -76,12 +78,22 @@ sub GetTicket($$$$)
{
my ($experiment, $component, $requested, $pref) = @_;
#
# XXX
#
my $this_user = User->LookupByUnixId($UID);
if (! defined($this_user)) {
print STDERR "You ($UID) do not exist!\n";
return -1;
}
my $rspec = { "requested" => $requested,
"granted" => 0};
my $response =
Genixmlrpc::CallMethodHTTP($component, "CM::GetTicket",
{ "uuid" => $experiment->uuid(),
{ "slice_uuid" => $experiment->uuid(),
"owner_uuid" => $this_user->uuid(),
"rspec" => $rspec });
return -1
......@@ -95,9 +107,33 @@ sub GetTicket($$$$)
#
# Convert this into a (signed) ticket object.
#
my $ticket = GeniTicket->Create($experiment->uuid(), $rspec,
$response->value());
my $ticket = GeniTicket->Create($experiment->uuid(), $this_user->uuid(),
$rspec, $response->value(), $component);
$$pref = $ticket;
return 0;
}
sub CreateSliver($$$)
{
my ($experiment, $ticket, $pref) = @_;
my $response =
Genixmlrpc::CallMethodHTTP($ticket->component(), "CM::CreateSliver",
{ "ticket" => $ticket->asString() });
return -1
if (!defined($response));
print Dumper($response);
return -1
if ($response->code() != GENIRESPONSE_SUCCESS);
my $sliver = undef;
$$pref = $sliver;
return 0;
}
......@@ -136,7 +136,6 @@ sub Create($$$$)
my ($class, $hrn, $uuid, $creator_uuid, $sa_idx) = @_;
my @insert_data = ();
# Every slice gets a new unique index.
my $idx = TBGetUniqueIndex('next_exptidx', 1);
......@@ -176,3 +175,5 @@ sub Delete($)
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
package GeniSliver;
#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration
use lib '@prefix@/lib';
use GeniDB;
# Hate to import all this crap; need a utility library.
use libdb qw(TBGetUniqueIndex);
use libtestbed;
use English;
use Data::Dumper;
use File::Temp qw(tempfile);
# Configure variables
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAPPROVAL = "@TBAPPROVALEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $BOSSNODE = "@BOSSNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $SIGNCRED = "$TB/sbin/signgenicred";
# Cache of instances to avoid regenerating them.
my %slivers = ();
#
# Lookup by idx, or uuid.
#
sub Lookup($$)
{
my ($class, $token) = @_;
my $query_result;
# Look in cache first
return $slivers{"$token"}
if (exists($slivers{"$token"}));
if ($token =~ /^\d+$/) {
$query_result =
DBQueryWarn("select * from geni_slivers ".
"where idx='$token'");
}
elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
$query_result =
DBQueryWarn("select * from geni_slivers ".
"where uuid='$token'");
}
else {
return undef;
}
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'SLIVER'} = $query_result->fetchrow_hashref();
bless($self, $class);
# Add to cache.
$slivers{$self->{'SLIVER'}->{'idx'}} = $self;
return $self;
}
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $uuid = $self->uuid();
my $idx = $self->idx();
return "[GeniSliver: $uuid, IDX: $idx]";
}
#
# Create a sliver. Not much to it yet.
#
sub Create($$)
{
my ($class, $ticket) = @_;
my @insert_data = ();
# Every sliver gets a new unique index.
my $idx = TBGetUniqueIndex('next_sliver', 1);
# And a new uuid.
my $uuid = NewUUID();
if (!defined($uuid)) {
print "*** WARNING: Could not generate a UUID!\n";
return undef;
}
my $slice_uuid = $ticket->slice_uuid();
my $owner_uuid = $ticket->owner_uuid();
# 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, "creator_uuid='$owner_uuid'");
push(@insert_data, "slice_uuid='$slice_uuid'");
# Insert into DB.
DBQueryWarn("insert into geni_slivers set " . join(",", @insert_data))
or return undef;
return GeniSlice->Lookup($idx);
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'SLIVER'}->{$_[1]}); }
sub idx($) { return field($_[0], "idx"); }
sub uuid($) { return field($_[0], "uuid"); }
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"); }
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -21,6 +21,8 @@ use lib '@prefix@/lib';
use GeniDB;
use libtestbed;
use English;
use XML::Simple;
use XML::LibXML;
use Data::Dumper;
use File::Temp qw(tempfile);
......@@ -35,18 +37,22 @@ my $GENICENTRAL = "https://boss/protogeni/xmlrpc";
my $SIGNCRED = "$TB/sbin/signgenicred";
#
# Create a ticket. Not much to it yet.
# Create an unsigned ticket object from the raw rspec.
#
# Should we keep track of tickets locally in the DB?
#
sub Create($$$;$)
sub Create($$$$;$$)
{
my ($class, $uuid, $rspec, $ticket) = @_;
my ($class, $slice_uuid, $owner_uuid,
$rspec, $ticket_string, $component) = @_;
my $self = {};
$self->{'rspec'} = $rspec;
$self->{'uuid'} = $uuid; # The slice UUID.
$self->{'ticket'} = $ticket;
$self->{'rspec'} = $rspec;
$self->{'slice_uuid'} = $slice_uuid;
$self->{'owner_uuid'} = $owner_uuid;
$self->{'ticket_string'} = $ticket_string;
$self->{'ticket'} = undef;
$self->{'component'} = $component;
bless($self, $class);
return $self;
......@@ -54,8 +60,67 @@ sub Create($$$;$)
# accessors
sub field($$) { return ($_[0]->{$_[1]}); }
sub rspec($) { return field($_[0], "rspec"); }
sub uuid($) { return field($_[0], "uuid"); }
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 component($) { return field($_[0], "component"); }
#
# Create a ticket object from a signed ticket string.
#
sub CreateFromSignedTicket($$)
{
my ($class, $ticket_string) = @_;
# Use XML::Simple to convert to something we can mess with.
my $parser = XML::LibXML->new;
my $doc = $parser->parse_string($ticket_string);
# Dig out the rspec.
my ($rspec_node) = $doc->getElementsByTagName("rspec");
return undef
if (!defined($rspec_node));
my $rspec = XMLin($rspec_node->toString(), ForceArray => 0);
# Dig out the slice uuid. Locally, I am not sure if we bother to
# keep slices in the DB (they are in the DB at geni central).
my ($uuid_node) = $doc->getElementsByTagName("this_uuid");
return undef
if (!defined($uuid_node));
my $slice_uuid = $uuid_node->to_literal();
if (! ($slice_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
print STDERR "Invalid slice_uuid in ticket\n";
return undef;
}
# Dig out the owner uuid. Locally, I am not sure if we bother to
# keep users in the DB (they are in the DB at geni central).
($uuid_node) = $doc->getElementsByTagName("owner_uuid");
return undef
if (!defined($uuid_node));
my $owner_uuid = $uuid_node->to_literal();
if (! ($owner_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
print STDERR "Invalid owner_uuid in ticket\n";
return undef;
}
my $self = {};
$self->{'rspec'} = $rspec;
$self->{'slice_uuid'} = $slice_uuid;
$self->{'owner_uuid'} = $owner_uuid;
$self->{'ticket_string'} = $ticket_string;
$self->{'xmlref'} = $doc;
$self->{'component'} = undef;
bless($self, $class);
print Dumper($self);
return $self;
}
#
# Populate the ticket with some stuff, which right now is just the
......@@ -83,9 +148,10 @@ sub Sign($)
return -1
if (!ref($self));
my $uuid = $self->uuid();
my $requested = $self->rspec()->{'requested'};
my $granted = $self->rspec()->{'granted'};
my $slice_uuid = $self->slice_uuid();
my $owner_uuid = $self->owner_uuid();
my $requested = $self->rspec()->{'requested'};
my $granted = $self->rspec()->{'granted'};
#
# Create a template xml file to sign.
......@@ -94,8 +160,8 @@ sub Sign($)
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n".
"<credential xml:id=\"Ref1\">\n".
" <type>ticket</type>\n".
" <owner_uuid>$uuid</owner_uuid>\n".
" <this_uuid>$uuid</this_uuid>\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".
......@@ -125,8 +191,10 @@ sub Sign($)
$ticket .= $_;
}
close(SIGNER);
$self->{'ticket'} = $ticket;
$self->{'ticket_string'} = $ticket;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use lib '@prefix@/lib';
use User;
use Experiment;
use GeniSAClient;
use GeniCMClient;
use English;
#GeniSAClient::RegisterUser(User->ThisUser());
my $experiment = Experiment->Lookup("testbed", "one-node");
my $resources;
my $ticket;
my $sliver;
GeniCMClient::DiscoverResources($experiment, \$resources);
GeniCMClient::GetTicket($experiment, $resources->[0], 1, \$ticket);
if ($ticket) {
GeniCMClient::CreateSliver($experiment, $ticket, \$sliver);
}
#
# These are new tables to add to the Emulab DB.
#
#
# Remote SAs register users at Emulab (we export GENI ClearingHouse
# APIs, whatever they are).