GeniTicket.pm.in 14.1 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 131
sub uuid($)		{ return field($_[0], "slice_uuid"); }
sub slice_uuid($)	{ return field($_[0], "slice_uuid"); }
sub owner_uuid($)	{ return field($_[0], "owner_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
132 133
sub slice_cert($)	{ return field($_[0], "slice_cert"); }
sub owner_cert($)	{ return field($_[0], "owner_cert"); }
134 135
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
136
sub ticket($)		{ return field($_[0], "ticket"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
137
sub asString($)		{ return field($_[0], "ticket_string"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
138
sub ticket_string($)	{ return field($_[0], "ticket_string"); }
139
sub component_uuid($)	{ return field($_[0], "component_uuid"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
140
sub component($)	{ return field($_[0], "component"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
141 142 143 144 145 146 147 148 149 150 151 152 153 154
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";
    }
155 156 157
    my $slice_uuid = $self->slice_uuid();
    
    return "[GeniTicket: $idx, slice:$slice_uuid]";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
158
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
159 160 161 162

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    #
    # 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
187 188
    }

Leigh B. Stoller's avatar
Leigh B. Stoller committed
189 190
    # Use XML::Simple to convert to something we can mess with.
    my $parser = XML::LibXML->new;
191 192 193 194 195 196 197 198
    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
199 200 201 202 203

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

207 208 209 210 211 212 213 214 215 216 217 218 219 220
    # 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
221 222
    # 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).
223
    my ($uuid_node) = $doc->getElementsByTagName("target_uuid");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
224 225
    return undef
	if (!defined($uuid_node));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
226 227
    my $slice_cert = $uuid_node->to_literal();
    my $slice_uuid;
228
    GeniCertificate->CertificateInfo($slice_cert, \$slice_uuid) == 0
Leigh B. Stoller's avatar
Leigh B. Stoller committed
229
	or return undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
230 231 232 233 234 235 236 237 238 239 240

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

    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
251 252 253 254
    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
255

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
261
    my $self = {};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
262
    $self->{'idx'}           = undef;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
263
    $self->{'rspec'}         = $rspec;
264 265
    $self->{'ticket_cert'}   = $ticket_cert;
    $self->{'ticket_uuid'}   = $ticket_uuid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
266 267
    $self->{'slice_uuid'}    = $slice_uuid;
    $self->{'owner_uuid'}    = $owner_uuid;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
268 269
    $self->{'slice_cert'}    = $slice_cert;
    $self->{'owner_cert'}    = $owner_cert;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
270 271
    $self->{'ticket_string'} = $ticket_string;
    $self->{'xmlref'}        = $doc;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
272
    $self->{'component'}     = $component;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    $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
290 291 292 293
    bless($self, $class);

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
295
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
296 297
# 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
298 299 300 301 302 303 304 305
#
sub Delete($)
{
    my ($self) = @_;

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

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
320 321 322 323 324 325 326 327
#
# 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
328
    return -1
Leigh B. Stoller's avatar
Leigh B. Stoller committed
329 330 331 332 333 334
	if (! ref($self));

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
335
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
336 337 338
# 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
339
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
340
sub Store($)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
341
{
Leigh B. Stoller's avatar
Leigh B. Stoller committed
342
    my ($self) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
343 344
    my @insert_data  = ();

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
351 352 353 354 355
    #
    # 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
356 357 358 359
    if (!defined($idx)) {
	$idx = TBGetUniqueIndex('next_ticket', 1);
	$self->{'idx'} = $idx;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
360
    # A locally generated ticket will not have a component. Might change that.
361
    push(@insert_data, "component_uuid='" . $self->component()->uuid() . "'")
Leigh B. Stoller's avatar
Leigh B. Stoller committed
362 363
	if (defined($self->component()));

Leigh B. Stoller's avatar
Leigh B. Stoller committed
364 365 366
    # 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
367
    push(@insert_data, "seqno='$seqno'");
368
    push(@insert_data, "ticket_uuid='$ticket_uuid'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
369 370 371 372 373 374 375 376 377 378
    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
379 380
    $tickets{"$idx"}  = $self;
    $self->{'stored'} = 1;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
381 382 383
    return 0;
}

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

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

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

402 403 404 405 406 407 408 409 410 411 412 413
    #
    # 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
414 415 416 417 418
    #
    # 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
419
	"<credential xml:id=\"ref1\">\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
420
	" <type>ticket</type>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
421
	" <serial>$idx</serial>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
422
	" <owner_uuid>$owner_cert</owner_uuid>\n".
423
	" <target_uuid>$slice_cert</target_uuid>\n".
424
	" <uuid>$ticket_cert</uuid>\n".
425 426
	" <hrn>$hrn</hrn>\n".
	" <expires>2008-05-10T09:00:00</expires>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
427 428
	" <ticket>\n".
	"  <can_delegate>1</can_delegate>\n".
429
	"  <redeem_before>2008-05-10T09:00:00</redeem_before>\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
430
	"  $rspec_xml\n".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444
	" </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
445
    if (! open(SIGNER, "$SIGNCRED -c $CMCERT $filename |")) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
446 447 448 449 450 451 452
	print STDERR "Could not sign $filename\n";
	return -1;
    }
    my $ticket = "";
    while (<SIGNER>) {
	$ticket .= $_;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
453 454 455 456
    if (!close(SIGNER)) {
	print STDERR "Could not sign $filename\n";
	return -1;
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
457
    $self->{'ticket_string'} = $ticket;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
458

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

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

479 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
#
# 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
506 507
#
# Release a ticket. Need to release the nodes ...
508
# Used by the CM.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
509 510 511 512 513 514 515 516 517
#
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
518 519 520
    my $pid     = $experiment->pid();
    my $eid     = $experiment->eid();
    my @nodeids = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
521
    my @nodes   = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
522

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

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

548 549 550 551 552 553 554 555 556 557 558 559 560 561
#
# 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
562 563
# _Always_ make sure that this 1 is at the end of the file...
1;