Commit 46eceecc authored by Leigh B. Stoller's avatar Leigh B. Stoller

Another checkpoint. Raw geni slivers working nicely. See new client

program protogeni/xmlrpc/client.py ... it is much easier to get a
simple python client running on your desktop then a perl client. Only
package you need to install is M2Crypto, which is easy. Perl needs
about 10 packages installed to xmlrpc over ssl. Sheesh.
parent 47798d81
......@@ -28,7 +28,7 @@ CREATE TABLE `geni_users` (
`status` enum('active','archived','frozen') NOT NULL default 'frozen',
`name` tinytext,
`email` tinytext,
`sa_idx` int(10) unsigned NOT NULL default '0',
`sa_uuid` varchar(40) NOT NULL default '',
PRIMARY KEY (`idx`),
KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
......@@ -71,13 +71,14 @@ CREATE TABLE `geni_components` (
# 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` (
DROP TABLE IF EXISTS `geni_authorities`;
CREATE TABLE `geni_authorities` (
`hrn` varchar(256) NOT NULL default '',
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`uuid_prefix` varchar(12) NOT NULL default '',
`created` datetime default NULL,
`type` enum('sa','ma','ch') NOT NULL default 'sa',
`url` tinytext,
PRIMARY KEY (`idx`),
UNIQUE KEY `uuid` (`uuid`)
......@@ -102,7 +103,7 @@ CREATE TABLE `geni_slices` (
`created` datetime default NULL,
`creator_uuid` varchar(40) NOT NULL default '',
`name` tinytext,
`sa_idx` int(10) unsigned NOT NULL default '0',
`sa_uuid` varchar(40) NOT NULL default '',
PRIMARY KEY (`idx`),
UNIQUE KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
......@@ -119,6 +120,7 @@ CREATE TABLE `geni_slivers` (
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`resource_uuid` varchar(40) NOT NULL default '',
`resource_type` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`credential_idx` int(10) unsigned default NULL,
`ticket_idx` int(10) unsigned default NULL,
......@@ -137,19 +139,22 @@ DROP TABLE IF EXISTS `geni_aggregates`;
CREATE TABLE `geni_aggregates` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`uuid` varchar(40) NOT NULL default '',
`type` varchar(40) NOT NULL default '',
`slice_uuid` varchar(40) NOT NULL default '',
`creator_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`credential_idx` int(10) unsigned default NULL,
`ticket_idx` int(10) unsigned default NULL,
`component_idx` int(10) unsigned NOT NULL default '0',
`aggregate_idx` int(10) unsigned default NULL,
`status` enum('ready','broken') NOT NULL default 'ready',
PRIMARY KEY (`idx`),
UNIQUE KEY `uuid` (`uuid`),
INDEX `slice_uuid` (`slice_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Table to remember the tickets that a component manager hands out.
# Table to remember tickets.
#
DROP TABLE IF EXISTS `geni_tickets`;
CREATE TABLE `geni_tickets` (
......@@ -160,10 +165,12 @@ CREATE TABLE `geni_tickets` (
`redeem_before` datetime default NULL,
`valid_until` datetime default NULL,
`component_idx` int(10) unsigned NOT NULL default '0',
`seqno` int(10) unsigned NOT NULL default '0',
`ticket_string` text,
PRIMARY KEY (`idx`),
PRIMARY KEY (`idx`),
INDEX `owner_uuid` (`owner_uuid`),
INDEX `slice_uuid` (`slice_uuid`)
INDEX `slice_uuid` (`slice_uuid`),
UNIQUE KEY `compseqno` (`component_idx`, `seqno`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
......@@ -198,12 +205,39 @@ CREATE TABLE `geni_certificates` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# A clearinghouse table to hold sshkeys associated with geni users.
# A clearinghouse table to hold keys associated with geni users.
#
DROP TABLE IF EXISTS `geni_sshkeys`;
CREATE TABLE `geni_sshkeys` (
DROP TABLE IF EXISTS `geni_userkeys`;
CREATE TABLE `geni_userkeys` (
`type` enum('ssh','password') NOT NULL default 'ssh',
`uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`sshkey` text,
PRIMARY KEY (`uuid`)
`key` text,
INDEX `uuid` (`uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# This table maps between resources and their component manager.
#
DROP TABLE IF EXISTS `geni_resources`;
CREATE TABLE `geni_resources` (
`idx` mediumint(8) unsigned NOT NULL default '0',
`resource_uuid` varchar(40) NOT NULL default '',
`resource_type` varchar(40) NOT NULL default '',
`created` datetime default NULL,
`component_idx` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`idx`),
UNIQUE KEY `resource_uuid` (`resource_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
#
# Hold the users that are bound to slices.
#
DROP TABLE IF EXISTS `geni_bindings`;
CREATE TABLE `geni_bindings` (
`slice_uuid` varchar(40) NOT NULL default '',
`user_uuid` varchar(40) NOT NULL default '',
`created` datetime default NULL,
PRIMARY KEY (`slice_uuid`,`user_uuid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -220,6 +220,33 @@ sub Delete($)
return 0;
}
#
# Look up a list of aggregates for a locally instantiated slice.
# Used by the CM.
#
sub SliceAggregates($$$)
{
my ($class, $slice, $pref) = @_;
my $slice_uuid = $slice->uuid();
my @result = ();
my $query_result =
DBQueryWarn("select idx from geni_aggregates ".
"where slice_uuid='$slice_uuid'");
return -1
if (!$query_result);
while (my ($idx) = $query_result->fetchrow_array()) {
my $aggregate = GeniAggregate->Lookup($idx);
return -1
if (!defined($aggregate));
push(@result, $aggregate);
}
@$pref = @result;
return 0;
}
#
# List of slivers for this aggregate.
#
......
......@@ -146,10 +146,10 @@ sub Resolve($)
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"No such user $uuid");
}
# Grab ssh key.
my $sshkey;
if ($user->GetSSHKey(\$sshkey) != 0) {
print STDERR "Could not get ssh key for $user\n";
# Grab keys.
my @sliverkeys;
if ($user->GetKeys(\@sliverkeys) != 0) {
print STDERR "Could not get sliver keys for $user\n";
}
# Return a blob.
......@@ -161,8 +161,8 @@ sub Resolve($)
"name" => $user->name(),
"sa_uuid" => $user->sa_uuid(),
};
$blob->{'sshkey'} = $sshkey
if (defined($sshkey));
$blob->{'sliverkeys'} = \@sliverkeys
if (@sliverkeys);
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
}
......@@ -295,7 +295,7 @@ sub Register($)
if ($type eq "User") {
my $name = $info->{'name'};
my $email = $info->{'email'};
my $sshkey= $info->{'sshkey'};
my $keys = $info->{'sliverkeys'};
if (! TBcheck_dbslot($name, "users", "usr_name",
TBDB_CHECKDBSLOT_ERROR)) {
......@@ -307,9 +307,20 @@ sub Register($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"email: ". TBFieldErrorString());
}
if (defined($sshkey) && ! ($sshkey =~ /^[\012\015\040-\176]*$/)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"sshkey: Invalid characters");
if (defined($keys)) {
foreach my $keyref (@{ $keys }) {
my $type = $keyref->{'type'};
my $key = $keyref->{'key'};
if ($type ne 'ssh') {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"key: Invalid key type");
}
if (! ($key =~ /^[\012\015\040-\176]*$/)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"key: Invalid characters");
}
}
}
#
# Need to verify the UUID is permitted for the SA making the request.
......@@ -345,7 +356,7 @@ sub Register($)
}
my $newuser = GeniUser->Create($hrn, $uid, $uuid,
$name, $email, $cert, $authority,
$sshkey);
$keys);
if (!defined($newuser)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$hrn/$email could not be registered");
......
......@@ -157,12 +157,12 @@ sub Register($$$$)
#
sub RegisterUser($$$$$)
{
my ($hrn, $name, $email, $cert, $sshkey) = @_;
my ($hrn, $name, $email, $cert, $keys) = @_;
my $info = { "name" => $name,
"email" => $email };
$info->{"sshkey"} = $sshkey
if (defined($sshkey));
$info->{"sliverkeys"} = $keys
if (defined($keys));
return Register($hrn, "User", $cert, $info);
}
......
......@@ -134,43 +134,38 @@ sub DiscoverResources($)
sub GetTicket($)
{
my ($argref) = @_;
my $slice_cert = $argref->{'slice'};
my $rspec = $argref->{'rspec'};
my $impotent = $argref->{'impotent'};
my $credential = $argref->{'credential'};
my $cred = $argref->{'credential'};
my $vtopo = $argref->{'virtual_topology'};
my $owner_uuid = $ENV{'GENIUSER'};
my $slice_uuid;
if (! defined($slice_cert)) {
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper slice");
if (! defined($cred)) {
return GeniResponse->MalformedArgsResponse();
}
if (! (defined($rspec) && ($rspec =~ /^[-\w]+$/))) {
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Improper rspec");
}
$rspec = XMLin($rspec, ForceArray => ["node", "link"]);
#print Dumper($rspec);
$impotent = 0
if (!defined($impotent));
$credential = GeniCredential->CreateFromSigned($credential);
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
GeniCertificate->CertificateInfo($slice_cert, \$slice_uuid) == 0 or
my $slice_uuid = $credential->this_uuid();
my $user_uuid = $credential->owner_uuid();
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
# The credential owner/slice has to match what was provided.
if (! ($owner_uuid eq $credential->owner_uuid() &&
$slice_uuid eq $credential->this_uuid())) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Invalid credentials for operation");
"This is not your credential!");
}
#
......@@ -196,11 +191,11 @@ sub GetTicket($)
#
# Ditto the user.
#
my $user = GeniUser->Lookup($owner_uuid);
my $user = GeniUser->Lookup($user_uuid);
if (!defined($user)) {
$user = CreateUserFromRegistry($owner_uuid);
$user = CreateUserFromRegistry($user_uuid);
if (!defined($user)) {
print STDERR "No user $owner_uuid in the ClearingHouse\n";
print STDERR "No user $user_uuid in the ClearingHouse\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get user info from ClearingHouse");
}
......@@ -238,7 +233,8 @@ sub GetTicket($)
my $pid = $experiment->pid();
my $eid = $experiment->eid();
foreach my $resource_uuid (keys(%{$rspec->{'node'}})) {
foreach my $ref (@{$rspec->{'node'}}) {
my $resource_uuid = $ref->{'uuid'};
my $node = Node->Lookup($resource_uuid);
if (!defined($node)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
......@@ -289,10 +285,9 @@ sub GetTicket($)
#
# Create a sliver.
#
sub CreateSliver($)
sub RedeemTicket($)
{
my ($argref) = @_;
my $owner_uuid = $ENV{'GENIUSER'};
my $ticket = $argref->{'ticket'};
my $impotent = $argref->{'impotent'};
my $message = "Error creating sliver/aggregate";
......@@ -311,10 +306,13 @@ sub CreateSliver($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniTicket object");
}
# The credential owner has to match what is in the ticket.
if ($owner_uuid ne $ticket->owner_uuid()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Invalid credentials for operation");
#
# Make sure the credential was issued to the caller.
#
if ($ticket->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
my $experiment = Experiment->Lookup($ticket->slice_uuid());
......@@ -338,10 +336,15 @@ sub CreateSliver($)
#
# Ditto the user.
#
my $owner_uuid = $ticket->owner_uuid();
my $owner = GeniUser->Lookup($owner_uuid);
if (!defined($owner)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No user record for $owner_uuid");
$owner = CreateUserFromRegistry($owner_uuid);
if (!defined($owner)) {
print STDERR "No user $owner_uuid in the ClearingHouse\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No user record for $owner_uuid");
}
}
#
......@@ -359,7 +362,13 @@ sub CreateSliver($)
}
foreach my $otheruuid (@userbindings) {
my $otheruser = GeniUser->Lookup($otheruuid);
if (!defined($otheruser)) {
$otheruser = CreateUserFromRegistry($otheruuid);
if (!defined($otheruser)) {
print STDERR "No user $otheruser, cannot bind to slice\n";
next;
}
}
if (!$otheruser->BindToSlice($slice) != 0) {
print STDERR "Could not bind $otheruser to $slice\n";
}
......@@ -370,21 +379,21 @@ sub CreateSliver($)
# unless there is just one node.
#
my $aggregate;
if (scalar(keys(%{$ticket->rspec()->{'node'}})) > 1) {
if (scalar(@{$ticket->rspec()->{'node'}}) > 1) {
$aggregate = GeniAggregate->Create($ticket);
if (!defined($aggregate)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniAggregate object");
}
}
#print Dumper($ticket);
#
# Now for each resource (okay, node) in the ticket create a sliver and
# add it to the aggregate.
#
my %slivers = ();
foreach my $resource_uuid (keys(%{$ticket->rspec()->{'node'}})) {
foreach my $ref (@{$ticket->rspec()->{'node'}}) {
my $resource_uuid = $ref->{'uuid'};
my $node = Node->Lookup($resource_uuid);
if (!defined($node)) {
$message = "Unknown resource_uuid in ticket: $resource_uuid";
......@@ -507,27 +516,30 @@ sub CreateSliver($)
sub StartSliver($)
{
my ($argref) = @_;
my $owner_uuid = $ENV{'GENIUSER'};
my $sliver_cert = $argref->{'sliver'};
my $credential = $argref->{'credential'};
my $sliver_uuid;
my $impotent = $argref->{'impotent'};
my $cred = $argref->{'credential'};
my $impotent = $argref->{'impotent'};
$impotent = 0
if (!defined($impotent));
if (!defined($sliver_cert) || !defined($credential)) {
if (!defined($cred)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS);
}
GeniCertificate->CertificateInfo($sliver_cert, \$sliver_uuid) == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
$credential = GeniCredential->CreateFromSigned($credential);
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
my $sliver_uuid = $credential->this_uuid();
my $user_uuid = $credential->owner_uuid();
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
my $sliver = GeniSliver->Lookup($sliver_uuid);
if (!defined($sliver)) {
# Might be an aggregate instead.
......@@ -537,13 +549,6 @@ sub StartSliver($)
"No such sliver/aggregate $sliver_uuid");
}
}
# The credential owner has to match what is in the ticket.
if (! ($owner_uuid eq $credential->owner_uuid() &&
$sliver_uuid eq $credential->this_uuid())) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Invalid credentials for operation");
}
if (!$impotent) {
$sliver->Start() == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -555,30 +560,33 @@ sub StartSliver($)
#
# Destroy a sliver/aggregate.
#
sub DestroySliver($)
sub DeleteSliver($)
{
my ($argref) = @_;
my $owner_uuid = $ENV{'GENIUSER'};
my $sliver_cert = $argref->{'sliver'};
my $credential = $argref->{'credential'};
my $sliver_uuid;
my $impotent = $argref->{'impotent'};
my $cred = $argref->{'credential'};
my $impotent = $argref->{'impotent'};
$impotent = 0
if (!defined($impotent));
if (!defined($sliver_cert) || !defined($credential)) {
if (!defined($cred)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS);
}
GeniCertificate->CertificateInfo($sliver_cert, \$sliver_uuid) == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not get uuid from Certificate");
$credential = GeniCredential->CreateFromSigned($credential);
my $credential = GeniCredential->CreateFromSigned($cred);
if (!defined($credential)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create GeniCredential object");
}
my $sliver_uuid = $credential->this_uuid();
my $user_uuid = $credential->owner_uuid();
#
# Make sure the credential was issued to the caller.
#
if ($credential->owner_uuid() ne $ENV{'GENIUUID'}) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"This is not your credential!");
}
my $sliver = GeniSliver->Lookup($sliver_uuid);
if (!defined($sliver)) {
# Might be an aggregate instead.
......@@ -588,13 +596,6 @@ sub DestroySliver($)
"No such sliver/aggregate $sliver_uuid");
}
}
# The credential owner has to match what is in the ticket.
if (! ($owner_uuid eq $credential->owner_uuid() &&
$sliver_uuid eq $credential->this_uuid())) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
"Invalid credentials for operation");
}
if (!$impotent) {
$sliver->UnProvision() == 0 or
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......@@ -628,10 +629,30 @@ sub CreateSliceFromRegistry($)
return undef;
}
}
my $slice = GeniSlice->Create($blob->{'hrn'},
$blob->{'uuid'},
$blob->{'creator_uuid'},
$blob->{'cert'}, $authority);
#
# The problem with HRNs is that people will tend to reuse them!
# So check to see if we have a slice with the hrn, and if so
# we need to check with the registry to see if its still active.
# Destroy it locally if not active.
#
my $slice = GeniSlice->Lookup($blob->{'hrn'});
if (defined($slice)) {
return $slice
if ($slice->uuid() eq $slice_uuid);
my $blob2;
if (GeniCHClient::LookupSlice($slice->uuid(), \$blob2) == 0) {
print STDERR "hrn already in use for $slice!\n";
return undef;
}
return undef
if (CleanupDeadSlice($slice) != 0);
}
$slice = GeniSlice->Create($blob->{'hrn'},
$blob->{'uuid'},
$blob->{'creator_uuid'},
$blob->{'cert'}, $authority);
return undef
if (!defined($slice));
......@@ -641,7 +662,7 @@ sub CreateSliceFromRegistry($)
if (!defined($user)) {
$user = CreateUserFromRegistry($uuid);
if (!defined($user)) {
print STDERR "No user $uuid in the ClearingHouse\n";
print STDERR "No user $uuid locally or in the ClearingHouse\n";
next;
}
}
......@@ -697,6 +718,19 @@ sub CreateUserFromRegistry($)
{
my ($uuid) = @_;
#
# Check Emulab users table first.
#
my $user = User->LookupByUUID($uuid);
if (defined($user)) {
my $geniuser = GeniUser->CreateFromLocal($user);
if (!defined($geniuser)) {
print STDERR "Could not create geniuser from $user\n";
return undef;
}
return $geniuser;
}
my $blob;
return undef
if (GeniCHClient::LookupUser($uuid, \$blob) != 0);
......@@ -709,15 +743,15 @@ sub CreateUserFromRegistry($)
return undef;
}
}
print STDERR Dumper($blob);
return GeniUser->Create($blob->{'hrn'},
$blob->{'uid'},
$blob->{'uuid'},
$blob->{'name'},
$blob->{'email'},
$blob->{'cert'},
$authority,
(exists($blob->{'sshkey'}) ?
$blob->{'sshkey'} : undef));
$authority, $blob->{'sliverkeys'});
}
#
......@@ -737,3 +771,73 @@ sub CreateAuthorityFromRegistry($)
$blob->{'cert'},
$blob->{'uuid_prefix'}, "sa");
}
#
# Cleanup a dead slice, releasing all the stuff associated with it.
#
sub CleanupDeadSlice($)
{
my ($slice) = @_;
print STDERR "Cleaning up dead slice $slice\n";
#
# Find any aggregates and tear them down.
#
my @aggregates;
if (GeniAggregate->SliceAggregates($slice, \@aggregates) != 0) {
print STDERR "Could not get dead aggregates for $slice.\n";
return -1;
}
foreach my $aggregate (@aggregates) {
if ($aggregate->UnProvision() != 0) {
print STDERR "Could not UnProvision $aggregate\n";
return -1;
}
if ($aggregate->Delete() != 0) {