GeniTicket.pm.in 14.2 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2008 University of Utah and the Flux Group.
# All rights reserved.
#
package GeniTicket;

#
# Some simple ticket stuff.
#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);

@ISA    = "Exporter";
@EXPORT = qw ( );

# Must come after package declaration!
use lib '@prefix@/lib';
use GeniDB;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22
use GeniCredential;
23
use GeniCertificate;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
24 25
use Experiment;
use libdb qw(TBGetUniqueIndex);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
26
use English;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
27 28
use XML::Simple;
use XML::LibXML;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
29 30
use Data::Dumper;
use File::Temp qw(tempfile);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
31
use overload ('""' => 'Stringify');
Leigh B. Stoller's avatar
Leigh B. Stoller committed
32 33 34 35 36 37 38 39 40

# Configure variables
my $TB		   = "@prefix@";
my $TBOPS          = "@TBOPSEMAIL@";
my $TBAPPROVAL     = "@TBAPPROVALEMAIL@";
my $TBAUDIT   	   = "@TBAUDITEMAIL@";
my $BOSSNODE       = "@BOSSNODE@";
my $OURDOMAIN      = "@OURDOMAIN@";
my $SIGNCRED	   = "$TB/sbin/signgenicred";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
41
my $VERIFYCRED	   = "$TB/sbin/verifygenicred";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
42
my $NFREE	   = "$TB/bin/nfree";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
43
my $CMCERT	   = "$TB/etc/genicm.pem";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
44

Leigh B. Stoller's avatar
Leigh B. Stoller committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
# Cache of tickets.
my %tickets = ();

#
# Lookup by local idx.
#
sub Lookup($$)
{
    my ($class, $idx) = @_;

    return undef
	if (! ($idx =~ /^\d*$/));
    return $tickets{"$idx"}
        if (exists($tickets{"$idx"}));

    my $query_result =
	DBQueryWarn("select * from geni_tickets where idx='$idx'");

    return undef
	if (!defined($query_result) || !$query_result->numrows);

    my $row = $query_result->fetchrow_hashref();

    # Map the component
    my $component;
70 71
    if ($row->{'component_uuid'}) {
	$component = GeniComponent->Lookup($row->{'component_uuid'});
Leigh B. Stoller's avatar
Leigh B. Stoller committed
72 73 74 75 76 77 78 79
	return undef
	    if (!defined($component));
    }
    my $ticket = GeniTicket->CreateFromSignedTicket($row->{'ticket_string'},
						    $component, 1);
    return undef
	if (!defined($ticket));

80 81 82 83 84
    # Mark as coming from the DB.
    $ticket->{'idx'}    = $idx;
    $ticket->{'stored'} = 1;

    # Cache.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
85 86 87 88
    $tickets{"$idx"} = $ticket;
    return $ticket;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
89
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
90
# Create an unsigned ticket object, to be populated and signed and returned.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
91
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
92
sub Create($$$$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
93
{
Leigh B. Stoller's avatar
Leigh B. Stoller committed
94
    my ($class, $slice, $owner, $rspec) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
95

Leigh B. Stoller's avatar
Leigh B. Stoller committed
96 97 98
    # Every Ticket gets a new unique index (sequence number).
    my $seqno = TBGetUniqueIndex('next_ticket', 1);
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
99
    my $self = {};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
100
    $self->{'rspec'}         = $rspec;
101 102
    $self->{'ticket_cert'}   = undef;
    $self->{'ticket_uuid'}   = undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
103 104 105 106
    $self->{'slice_uuid'}    = $slice->uuid();
    $self->{'owner_uuid'}    = $owner->uuid();
    $self->{'slice_cert'}    = $slice->cert();
    $self->{'owner_cert'}    = $owner->cert();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
107
    $self->{'seqno'}         = $seqno;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
108 109
    $self->{'ticket_string'} = undef;
    $self->{'component'}     = undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
110 111 112 113 114 115 116 117 118 119
    $self->{'stored'}        = 0;	# Stored to the DB.

    #
    # Locally generated tickets need a local DB index, which can be the
    # same as the sequence number. A ticket from a remote component will
    # have it own seqno, and so we will generate a locally valid idx for
    # those when when(if) we store them in the DB.
    #
    $self->{'idx'}           = $seqno;
    
Leigh B. Stoller's avatar
Leigh B. Stoller committed
120 121 122 123 124 125
    bless($self, $class);

    return $self;
}
# accessors
sub field($$)           { return ($_[0]->{$_[1]}); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
126
sub idx($)		{ return field($_[0], "idx"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
127
sub seqno($)		{ return field($_[0], "seqno"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
128
sub rspec($)		{ return field($_[0], "rspec"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
129 130
sub uuid($)		{ return field($_[0], "slice_uuid"); }
sub slice_uuid($)	{ return field($_[0], "slice_uuid"); }
131
sub target_uuid($)	{ return field($_[0], "slice_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
132
sub owner_uuid($)	{ return field($_[0], "owner_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
133 134
sub slice_cert($)	{ return field($_[0], "slice_cert"); }
sub owner_cert($)	{ return field($_[0], "owner_cert"); }
135 136
sub ticket_cert($)	{ return field($_[0], "ticket_cert"); }
sub ticket_uuid($)	{ return field($_[0], "ticket_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
137
sub ticket($)		{ return field($_[0], "ticket"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
138
sub asString($)		{ return field($_[0], "ticket_string"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
139
sub ticket_string($)	{ return field($_[0], "ticket_string"); }
140
sub component_uuid($)	{ return field($_[0], "component_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
141
sub component($)	{ return field($_[0], "component"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
142 143 144 145 146 147 148 149 150 151 152 153 154 155
sub stored($)		{ return field($_[0], "stored"); }

#
# Stringify for output.
#
sub Stringify($)
{
    my ($self) = @_;
    
    my $idx = $self->idx();
    if (!defined($idx)) {
	my $seqno = $self->seqno();
	$idx = "S$seqno";
    }
156 157 158
    my $slice_uuid = $self->slice_uuid();
    
    return "[GeniTicket: $idx, slice:$slice_uuid]";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
159
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
160 161 162 163

#
# Create a ticket object from a signed ticket string.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
164
sub CreateFromSignedTicket($$;$$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
165
{
Leigh B. Stoller's avatar
Leigh B. Stoller committed
166
    my ($class, $ticket_string, $component, $nosig) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
167

Leigh B. Stoller's avatar
Leigh B. Stoller committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
    #
    # This flag is used to avoid verifying the signature since I do not
    # really care if the component gives me a bad ticket; I am not using
    # it locally, just passing it back to the component at some point.
    #
    $nosig = 0
	if (!defined($nosig));

    if (! $nosig) {
	my ($fh, $filename) = tempfile(UNLINK => 0);
	return undef
	    if (!defined($fh));
	print $fh $ticket_string;
	close($fh);
	system("$VERIFYCRED $filename");
	if ($?) {
	    print STDERR "Ticket in $filename did not verify\n";
	    return undef;
	}
	unlink($filename);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
188 189
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
190 191
    # Use XML::Simple to convert to something we can mess with.
    my $parser = XML::LibXML->new;
192 193 194 195 196 197 198 199
    my $doc;
    eval {
	$doc = $parser->parse_string($ticket_string);
    };
    if ($@) {
	print STDERR "Failed to parse ticket string: $@\n";
	return undef;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
200 201 202 203 204

    # Dig out the rspec.
    my ($rspec_node) = $doc->getElementsByTagName("rspec");
    return undef
	if (!defined($rspec_node));
205 206
    my $rspec = XMLin($rspec_node->toString(), ForceArray => ["node",
							      "link"]);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
207

208 209 210 211 212 213 214 215 216 217 218 219 220 221
    # Dig out the ticket uuid.
    my ($uuid_node) = $doc->getElementsByTagName("uuid");
    return undef
	if (!defined($uuid_node));
    my $ticket_cert = $uuid_node->to_literal();
    my $ticket_uuid;
    GeniCertificate->CertificateInfo($ticket_cert, \$ticket_uuid) == 0
	or return undef;

    if (! ($ticket_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
	print STDERR "Invalid uuid in ticket\n";
	return undef;
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
222 223
    # 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).
224
    ($uuid_node) = $doc->getElementsByTagName("target_uuid");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
225 226
    return undef
	if (!defined($uuid_node));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
227 228
    my $slice_cert = $uuid_node->to_literal();
    my $slice_uuid;
229
    GeniCertificate->CertificateInfo($slice_cert, \$slice_uuid) == 0
Leigh B. Stoller's avatar
Leigh B. Stoller committed
230
	or return undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
231 232 233 234 235 236 237 238 239 240 241

    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));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
242 243
    my $owner_cert = $uuid_node->to_literal();
    my $owner_uuid;
244
    GeniCertificate->CertificateInfo($owner_cert, \$owner_uuid) == 0
Leigh B. Stoller's avatar
Leigh B. Stoller committed
245
	or return undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
246 247 248 249 250 251

    if (! ($owner_uuid =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/)) {
	print STDERR "Invalid owner_uuid in ticket\n";
	return undef;
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
252 253 254 255
    my ($seqno_node) = $doc->getElementsByTagName("serial");
    return undef
	if (!defined($seqno_node));
    my $seqno = $seqno_node->to_literal();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
256

Leigh B. Stoller's avatar
Leigh B. Stoller committed
257 258 259
    if (! ($seqno =~ /^\w+$/)) {
	print STDERR "Invalid sequence number in ticket\n";
	return undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
260 261
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
262
    my $self = {};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
263
    $self->{'idx'}           = undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
264
    $self->{'rspec'}         = $rspec;
265 266
    $self->{'ticket_cert'}   = $ticket_cert;
    $self->{'ticket_uuid'}   = $ticket_uuid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
267 268
    $self->{'slice_uuid'}    = $slice_uuid;
    $self->{'owner_uuid'}    = $owner_uuid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
269 270
    $self->{'slice_cert'}    = $slice_cert;
    $self->{'owner_cert'}    = $owner_cert;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
271 272
    $self->{'ticket_string'} = $ticket_string;
    $self->{'xmlref'}        = $doc;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
273
    $self->{'component'}     = $component;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    $self->{'seqno'}         = $seqno;
    $self->{'stored'}        = 0;
    
    #
    # We save copies of the tickets we hand out; lets find that record
    # in the DB, just to verify.
    #
    if (! $nosig) {
	my $query_result =
	    DBQueryWarn("select * from geni_tickets where idx='$seqno'");
	if (!$query_result || !$query_result->numrows) {
	    print STDERR "Could not find the ticket $seqno in the DB\n";
	    return undef;
	}
	$self->{'idx'}    = $seqno;
	$self->{'stored'} = 1;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
291 292 293 294
    bless($self, $class);

    return $self;
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
295

Leigh B. Stoller's avatar
Leigh B. Stoller committed
296
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
297 298
# Might have to delete this from the DB, as with an error handing out
# a ticket.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
299 300 301 302 303 304 305 306
#
sub Delete($)
{
    my ($self) = @_;

    return -1
	if (! ref($self));

Leigh B. Stoller's avatar
Leigh B. Stoller committed
307
    if ($self->stored()) {
308 309
	my $idx  = $self->idx();
	my $uuid = $self->ticket_uuid();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
310
	
311 312
	DBQueryWarn("delete from geni_certificates where uuid='$uuid'")
	    or return -1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
313 314
	DBQueryWarn("delete from geni_tickets where idx='$idx'")
	    or return -1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
315 316
	
	delete($tickets{"$idx"});
Leigh B. Stoller's avatar
Leigh B. Stoller committed
317 318 319 320
    }
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
321 322 323 324 325 326 327 328
#
# Populate the ticket with some stuff, which right now is just the
# number of node we are willing to grant.
#
sub Grant($$)
{
    my ($self, $count) = @_;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
329
    return -1
Leigh B. Stoller's avatar
Leigh B. Stoller committed
330 331 332 333 334 335
	if (! ref($self));

    $self->{'count'} = $count;
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
336
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
337 338 339
# Store the given ticket in the DB. We only do this for signed tickets,
# so we have a record of them. We store them on the server and the client
# side.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
340
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
341
sub Store($)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
342
{
Leigh B. Stoller's avatar
Leigh B. Stoller committed
343
    my ($self) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
344 345
    my @insert_data  = ();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
346 347
    my $idx        = $self->idx();
    my $seqno      = $self->seqno();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
348 349
    my $slice_uuid = $self->slice_uuid();
    my $owner_uuid = $self->owner_uuid();
350
    my $ticket_uuid= $self->ticket_uuid();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
351

Leigh B. Stoller's avatar
Leigh B. Stoller committed
352 353 354 355 356
    #
    # For a locally created/signed ticket, seqno=idx. For a ticket from
    # another component, we have to generate a locally unique idx for
    # the DB insertion.
    #
Leigh B. Stoller's avatar
Leigh B. Stoller committed
357 358 359 360
    if (!defined($idx)) {
	$idx = TBGetUniqueIndex('next_ticket', 1);
	$self->{'idx'} = $idx;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
361
    # A locally generated ticket will not have a component. Might change that.
362
    push(@insert_data, "component_uuid='" . $self->component()->uuid() . "'")
Leigh B. Stoller's avatar
Leigh B. Stoller committed
363 364
	if (defined($self->component()));

Leigh B. Stoller's avatar
Leigh B. Stoller committed
365 366 367
    # Now tack on other stuff we need.
    push(@insert_data, "created=now()");
    push(@insert_data, "idx='$idx'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
368
    push(@insert_data, "seqno='$seqno'");
369
    push(@insert_data, "ticket_uuid='$ticket_uuid'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
370 371 372 373 374 375 376 377 378 379
    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;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
380 381
    $tickets{"$idx"}  = $self;
    $self->{'stored'} = 1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
382 383 384
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
385 386
#
# Sign the ticket before returning it. We capture the output, which is
Leigh B. Stoller's avatar
Leigh B. Stoller committed
387
# in XML. 
Leigh B. Stoller's avatar
Leigh B. Stoller committed
388 389 390 391 392 393 394
#
sub Sign($)
{
    my ($self) = @_;

    return -1
	if (!ref($self));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
395

Leigh B. Stoller's avatar
Leigh B. Stoller committed
396
    my $idx        = $self->seqno();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
397 398
    my $slice_cert = $self->slice_cert();
    my $owner_cert = $self->owner_cert();
399
    my $hrn        = "$OURDOMAIN.tickets.$idx";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
400 401
    my $rspec_xml  = XMLout($self->rspec(), "NoAttr" => 1);
    $rspec_xml =~ s/opt\>/rspec\>/g;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
402

403 404 405 406 407 408 409 410 411 412 413 414
    #
    # Every ticket/credential its own uuid (certificate).
    #
    my $certificate = GeniCertificate->Create("ticket");
    if (!defined($certificate)) {
	print STDERR "Could not generate new certificate for $self!\n";
	return -1;
    }
    $self->{'ticket_cert'}   = $certificate->cert();
    $self->{'ticket_uuid'}   = $certificate->uuid();
    my $ticket_cert = $certificate->cert();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
415 416 417 418 419
    #
    # Create a template xml file to sign.
    #
    my $template =
	"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
420
	"<credential xml:id=\"ref1\">\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
421
	" <type>ticket</type>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
422
	" <serial>$idx</serial>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
423
	" <owner_uuid>$owner_cert</owner_uuid>\n".
424
	" <target_uuid>$slice_cert</target_uuid>\n".
425
	" <uuid>$ticket_cert</uuid>\n".
426 427
	" <hrn>$hrn</hrn>\n".
	" <expires>2008-05-10T09:00:00</expires>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
428 429
	" <ticket>\n".
	"  <can_delegate>1</can_delegate>\n".
430
	"  <redeem_before>2008-05-10T09:00:00</redeem_before>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
431
	"  $rspec_xml\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445
	" </ticket>\n".	
        "</credential>\n";

    my ($fh, $filename) = tempfile(UNLINK => 0);
    return -1
	if (!defined($fh));

    print $fh $template;
    close($fh);

    #
    # Fire up the signer and capture the output. This is the signed ticket
    # that is returned. 
    #
Leigh B. Stoller's avatar
Leigh B. Stoller committed
446
    if (! open(SIGNER, "$SIGNCRED -c $CMCERT $filename |")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
447 448 449 450 451 452 453
	print STDERR "Could not sign $filename\n";
	return -1;
    }
    my $ticket = "";
    while (<SIGNER>) {
	$ticket .= $_;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
454 455 456 457
    if (!close(SIGNER)) {
	print STDERR "Could not sign $filename\n";
	return -1;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
458
    $self->{'ticket_string'} = $ticket;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
459

Leigh B. Stoller's avatar
Leigh B. Stoller committed
460
    $self->Store() == 0
Leigh B. Stoller's avatar
Leigh B. Stoller committed
461 462
	or return -1;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
463
    unlink($filename);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
    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());
}

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
#
# Look up a list of tickets for a locally instantiated slice. 
# Used by the CM.
#
sub SliceTickets($$$)
{
    my ($class, $slice, $pref) = @_;

    my $slice_uuid = $slice->uuid();
    my @result = ();

    my $query_result =
	DBQueryWarn("select idx from geni_tickets ".
		    "where slice_uuid='$slice_uuid'");
    return -1
	if (!$query_result);

    while (my ($idx) = $query_result->fetchrow_array()) {
	my $ticket = GeniTicket->Lookup($idx);
	return -1
	    if (!defined($ticket));
	push(@result, $ticket);
    }
    @$pref = @result;
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
507 508
#
# Release a ticket. Need to release the nodes ...
509
# Used by the CM.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
510 511 512 513 514 515 516 517 518
#
sub Release($)
{
    my ($self) = @_;

    return undef
	if (! ref($self));

    my $experiment = Experiment->Lookup($self->slice_uuid());
Leigh B. Stoller's avatar
Leigh B. Stoller committed
519 520 521
    my $pid     = $experiment->pid();
    my $eid     = $experiment->eid();
    my @nodeids = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
522
    my @nodes   = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
523

524 525
    foreach my $ref (@{$self->rspec()->{'node'}}) {
	my $resource_uuid = $ref->{'uuid'};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
526 527 528 529 530
	my $node = Node->Lookup($resource_uuid);
	next
	    if (!defined($node));

	my $reservation = $node->Reservation();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
531 532 533 534
	next
	    if (!defined($reservation));
	
	if ($reservation->SameExperiment($experiment)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
535
	    push(@nodeids, $node->node_id());
Leigh B. Stoller's avatar
Leigh B. Stoller committed
536
	    push(@nodes, $node);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
537 538 539
	}
    }
    if (@nodeids) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
540
	system("export NORELOAD=1; $NFREE -x -q $pid $eid @nodeids");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
541
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
542 543 544
    foreach my $node (@nodes) {
	$node->Refresh();
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
545
    $self->Delete();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
546 547 548
    return 0;
}

549 550 551 552 553 554 555 556 557 558 559 560 561 562
#
# Equality test for two tickets.
#
sub SameTicket($$)
{
    my ($self, $other) = @_;

    # Must be a real reference. 
    return 0
	if (! (ref($self) && ref($other)));

    return $self->idx() == $other->idx();
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
563 564
# _Always_ make sure that this 1 is at the end of the file...
1;