GeniSliver.pm.in 36.5 KB
Newer Older
Leigh Stoller's avatar
Leigh Stoller committed
1 2
#!/usr/bin/perl -wT
#
3
# Copyright (c) 2008-2011, 2013 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# 
# {{{GENIPUBLIC-LICENSE
# 
# GENI Public License
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
# 
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
# 
# }}}
Leigh Stoller's avatar
Leigh Stoller committed
29 30 31 32 33 34 35 36 37 38 39 40
#
package GeniSliver;

#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);

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

use GeniDB;
Leigh Stoller's avatar
Leigh Stoller committed
41
use GeniComponent;
Leigh Stoller's avatar
Leigh Stoller committed
42
use GeniSlice;
Leigh Stoller's avatar
Leigh Stoller committed
43
use GeniCredential;
44
use GeniCertificate;
Leigh Stoller's avatar
Leigh Stoller committed
45
use GeniAggregate;
46
use GeniUsage;
47
use GeniHRN;
48
use GeniXML;
49
use emutil;
50
use Node;
Leigh Stoller's avatar
Leigh Stoller committed
51
use English;
Leigh Stoller's avatar
Leigh Stoller committed
52
use XML::Simple;
Leigh Stoller's avatar
Leigh Stoller committed
53 54
use Data::Dumper;
use File::Temp qw(tempfile);
Leigh Stoller's avatar
Leigh Stoller committed
55
use overload ('""' => 'Stringify');
Leigh Stoller's avatar
Leigh Stoller committed
56 57 58 59 60 61 62 63

# Configure variables
my $TB		   = "@prefix@";
my $TBOPS          = "@TBOPSEMAIL@";
my $TBAPPROVAL     = "@TBAPPROVALEMAIL@";
my $TBAUDIT   	   = "@TBAUDITEMAIL@";
my $BOSSNODE       = "@BOSSNODE@";
my $OURDOMAIN      = "@OURDOMAIN@";
64
my $PGENIDOMAIN    = "@PROTOGENI_DOMAIN@";
Leigh Stoller's avatar
Leigh Stoller committed
65
my $SIGNCRED	   = "$TB/sbin/signgenicred";
Leigh Stoller's avatar
Leigh Stoller committed
66 67 68
my $AVAIL	   = "$TB/sbin/avail";
my $NALLOC	   = "$TB/bin/nalloc";
my $NFREE	   = "$TB/bin/nfree";
Leigh Stoller's avatar
Leigh Stoller committed
69
my $NODEREBOOT	   = "$TB/bin/node_reboot";
70
my $NAMEDSETUP     = "$TB/sbin/named_setup";
Leigh Stoller's avatar
Leigh Stoller committed
71 72
my $PLABNODE       = "$TB/sbin/plabnodewrapper";
my $VNODESETUP     = "$TB/sbin/vnode_setup";
73
my $GENTOPOFILE    = "$TB/libexec/gentopofile";
74
my $POWER          = "$TB/bin/power";
Leigh Stoller's avatar
Leigh Stoller committed
75 76 77

# Cache of instances to avoid regenerating them.
my %slivers      = ();
78
BEGIN { use GeniUtil; GeniUtil::AddCache(\%slivers); }
Leigh Stoller's avatar
Leigh Stoller committed
79 80

#
81
# Lookup by URN, idx, or uuid.
Leigh Stoller's avatar
Leigh Stoller committed
82 83 84 85 86
#
sub Lookup($$)
{
    my ($class, $token) = @_;
    my $query_result;
Leigh Stoller's avatar
Leigh Stoller committed
87
    my $idx;
Leigh Stoller's avatar
Leigh Stoller committed
88

89 90 91
    if (GeniHRN::IsValid($token)) {
	my ($authority, $type, $id) = GeniHRN::Parse($token);
	return undef if $type ne "sliver";
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

	if( GeniHRN::Authoritative($token, "@OURDOMAIN@") ) {
	    # Very simple: we put the index of our own slivers right
	    # in the name.
	    $idx = $id;
	} else {
	    # Look up the slivers's certificate.
	    $token = GeniHRN::Normalise( $token );
	    $query_result = DBQueryWarn(
		"SELECT geni_slivers.idx FROM geni_slivers, " .
		"geni_certificates WHERE geni_slivers.uuid = " .
		"geni_certificates.uuid AND " .
		"geni_certificates.urn='$token';" );

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

	    ($idx) = $query_result->fetchrow_array();
	}
110 111
    }
    elsif ($token =~ /^\d+$/) {
Leigh Stoller's avatar
Leigh Stoller committed
112
	$idx = $token;
Leigh Stoller's avatar
Leigh Stoller committed
113 114 115
    }
    elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
	$query_result =
Leigh Stoller's avatar
Leigh Stoller committed
116
	    DBQueryWarn("select idx from geni_slivers ".
Leigh Stoller's avatar
Leigh Stoller committed
117
			"where uuid='$token'");
Leigh Stoller's avatar
Leigh Stoller committed
118 119 120 121
	    return undef
		if (! $query_result || !$query_result->numrows);

	    ($idx) = $query_result->fetchrow_array();
Leigh Stoller's avatar
Leigh Stoller committed
122 123 124 125
    }
    else {
	return undef;
    }
Leigh Stoller's avatar
Leigh Stoller committed
126 127 128 129 130 131 132 133

    # Look in cache first
    return $slivers{"$idx"}
        if (exists($slivers{"$idx"}));

    $query_result = DBQueryWarn("select * from geni_slivers ".
				"where idx='$idx'");

Leigh Stoller's avatar
Leigh Stoller committed
134 135 136
    return undef
	if (!$query_result || !$query_result->numrows);

137 138 139 140 141 142
    my $self               = {};
    $self->{'SLIVER'}      = $query_result->fetchrow_hashref();
    $self->{'SLICE'}       = undef;	# server
    $self->{'AGGREGATE'}   = undef;	# server
    $self->{'RSPEC'}       = undef;	# server
    $self->{'CERTIFICATE'} = undef;
Leigh Stoller's avatar
Leigh Stoller committed
143 144 145

    my $rspec_string = $self->{'SLIVER'}->{'rspec_string'};
    if (defined($rspec_string) && $rspec_string ne "") {
146 147
	my $rspec = GeniXML::Parse($rspec_string);
	if (!defined($rspec)) {
148 149 150
	    return undef;
	}
	$self->{'RSPEC'} = $rspec;
Leigh Stoller's avatar
Leigh Stoller committed
151
    }
152 153 154 155 156

    #
    # Grab the certificate, since we will probably want it.
    #
    my $uuid = $self->{'SLIVER'}->{'uuid'};
157
    if (0) {
158 159 160 161 162 163
    my $certificate = GeniCertificate->Lookup($uuid);
    if (!defined($certificate)) {
	print STDERR "Could not find certificate for sliver $idx ($uuid)\n";
	return undef;
    }
    $self->{'CERTIFICATE'} = $certificate;
164
    }
165 166 167 168

    # Bless into sub package if called for.
    my $resource_type = $self->{'SLIVER'}->{'resource_type'};
    if (defined($resource_type) && $resource_type ne "") {
169
	bless($self, $class . "::" . $resource_type);
170 171 172 173
    }
    else {
	bless($self, $class);
    }
Leigh Stoller's avatar
Leigh Stoller committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    
    # 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]";
}

#
Leigh Stoller's avatar
Leigh Stoller committed
195
# Create a sliver record in the DB. On the client side we save the credential
Leigh Stoller's avatar
Leigh Stoller committed
196
# that allows control of it, for later operations.
Leigh Stoller's avatar
Leigh Stoller committed
197
#
198
sub Create($$$$$$$$)
Leigh Stoller's avatar
Leigh Stoller committed
199
{
200
    my ($class, $slice, $owner, 
201
	$resource_uuid, $resource_type, $resource_id,
202
	$hrn, $nickname, $rspec) = @_;
Leigh Stoller's avatar
Leigh Stoller committed
203 204
    my @insert_data = ();

205 206 207 208 209 210 211 212 213 214 215 216
    my $urn;
    # Get sliver id from the ticket if available.
    if (defined($rspec)) {
	$urn = GeniXML::GetSliverId($rspec);
    }
    # Otherwise, generate a fresh one.
    if (! defined($urn)) {
	# Every sliver gets a new unique index.
	my $sliver_idx = TBGetUniqueIndex('next_sliver', 1);
	$urn = GeniHRN::Generate("@OURDOMAIN@", "sliver", $sliver_idx);
    }
    my (undef, undef, $idx) = GeniHRN::Parse($urn);
217

218 219
    # Sanity check.
    my $certificate = GeniCertificate->Lookup($urn);
220
    if (0) {
221 222 223 224
    if (defined($certificate)) {
	print STDERR "GeniSliver::Create: ".
	    "Already have a certificate for $hrn/$urn\n";
	return undef;
Leigh Stoller's avatar
Leigh Stoller committed
225
    }
226 227 228
    $certificate = GeniCertificate->Create({'urn'  => $urn,
					    'hrn'  => $hrn,
					    'email'=> $TBOPS});
229 230
    if (!defined($certificate)) {
	print STDERR "GeniSliver::Create: ".
231
	    "Could not generate new certificate for $hrn/$urn\n";
232
	return undef;
Leigh Stoller's avatar
Leigh Stoller committed
233
    }
234
    }
Leigh Stoller's avatar
Leigh Stoller committed
235
    my $slice_uuid     = $slice->uuid();
236
    my $owner_uuid     = $owner->uuid();
237 238
    my $sliver_uuid    = (defined($certificate) ?
			  $certificate : GeniUtil::NewUUID());
Leigh Stoller's avatar
Leigh Stoller committed
239 240 241 242

    # Now tack on other stuff we need.
    push(@insert_data, "created=now()");
    push(@insert_data, "idx='$idx'");
243 244 245
    push(@insert_data, "hrn=" . DBQuoteSpecial($hrn));
    push(@insert_data, "nickname=" . DBQuoteSpecial($nickname))
	if (defined($nickname));
246
    push(@insert_data, "uuid='$sliver_uuid'");
247 248
    push(@insert_data, "resource_uuid='$resource_uuid'");
    push(@insert_data, "resource_type='$resource_type'");
249
    push(@insert_data, "resource_id='$resource_id'");
Leigh Stoller's avatar
Leigh Stoller committed
250 251
    push(@insert_data, "creator_uuid='$owner_uuid'");
    push(@insert_data, "slice_uuid='$slice_uuid'");
252 253
    # Start out new slivers, as new.
    push(@insert_data, "state='new'");
Leigh Stoller's avatar
Leigh Stoller committed
254

Leigh Stoller's avatar
Leigh Stoller committed
255
    if (defined($rspec)) {
256
	my $rspec_string = GeniXML::Serialize($rspec);
Leigh Stoller's avatar
Leigh Stoller committed
257 258 259 260 261
	my $safe_rspec   = DBQuoteSpecial($rspec_string);

	push(@insert_data, "rspec_string=$safe_rspec");
    }

Leigh Stoller's avatar
Leigh Stoller committed
262
    # Insert into DB.
263 264
    if (!DBQueryWarn("insert into geni_slivers set " .
		     join(",", @insert_data))) {
265 266
	$certificate->Delete()
	    if (defined($certificate));
Leigh Stoller's avatar
Leigh Stoller committed
267 268 269
	return undef;
    }

Leigh Stoller's avatar
Leigh Stoller committed
270 271 272
    my $sliver = GeniSliver->Lookup($idx);
    return undef
	if (!defined($sliver));
273 274 275 276 277

    if (GeniUsage->NewSliver($sliver, $slice, $owner)) {
	print STDERR
	    "GeniSliver::Create: GeniUsage->NewSliver($sliver) failed\n";
    }
Leigh Stoller's avatar
Leigh Stoller committed
278
    $sliver->{'AGGREGATE'} = undef;
Leigh Stoller's avatar
Leigh Stoller committed
279
    $sliver->{'SLICE'}     = undef;
Leigh Stoller's avatar
Leigh Stoller committed
280 281

    return $sliver;
Leigh Stoller's avatar
Leigh Stoller committed
282 283 284 285 286
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'SLIVER'}->{$_[1]}); }
sub idx($)		{ return field($_[0], "idx"); }
sub uuid($)		{ return field($_[0], "uuid"); }
287 288
sub hrn($)		{ return field($_[0], "hrn"); }
sub nickname($)		{ return field($_[0], "nickname"); }
Leigh Stoller's avatar
Leigh Stoller committed
289 290 291
sub slice_uuid($)	{ return field($_[0], "slice_uuid"); }
sub creator_uuid($)	{ return field($_[0], "creator_uuid"); }
sub created($)		{ return field($_[0], "created"); }
Leigh Stoller's avatar
Leigh Stoller committed
292
sub registered($)	{ return field($_[0], "registered"); }
Leigh Stoller's avatar
Leigh Stoller committed
293
sub credential_idx($)	{ return field($_[0], "credential_idx"); }
Leigh Stoller's avatar
Leigh Stoller committed
294
sub resource_uuid($)	{ return field($_[0], "resource_uuid"); }
295
sub resource_id($)	{ return field($_[0], "resource_id"); }
296
sub resource_type($)	{ return field($_[0], "resource_type"); }
Leigh Stoller's avatar
Leigh Stoller committed
297 298 299
sub component_uuid($)	{ return field($_[0], "component_uuid"); }
sub aggregate_uuid($)	{ return field($_[0], "aggregate_uuid"); }
sub rspec_string($)     { return field($_[0], "rspec_string"); }
Leigh Stoller's avatar
Leigh Stoller committed
300
sub status($)		{ return field($_[0], "status"); }
301
sub state($)		{ return field($_[0], "state"); }
302
sub ErrorLog($)		{ return field($_[0], "errorlog"); }
303
sub cert($)		{ return GetCertificate($_[0])->cert(); }
Leigh Stoller's avatar
Leigh Stoller committed
304
sub rspec($)            { return $_[0]->{'RSPEC'}; }
305 306 307 308 309 310 311 312
# Watch for slivers that no longer get a certificate.
sub GetCertificate($)
{
    if (!defined($_[0]->{'CERTIFICATE'})) {
	print STDERR "*** No certificate for sliver: " . $_[0] . "\n";
    }
    return $_[0]->{'CERTIFICATE'};
}
Leigh Stoller's avatar
Leigh Stoller committed
313

314 315 316 317 318 319 320 321
# Return the sliver URN. 
sub sliver_urn($)
{
    my ($self)  = @_;

    return GeniHRN::Generate("@OURDOMAIN@", "sliver", $self->idx());
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335
#
# Equality test. Not strictly necessary in perl, but good form.
#
sub SameSliver($$)
{
    my ($self, $other) = @_;

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

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

Leigh Stoller's avatar
Leigh Stoller committed
336 337 338
#
# Delete the sliver. The sliver should not be provisioned when this done.
#
339
sub Delete($$)
Leigh Stoller's avatar
Leigh Stoller committed
340
{
341
    my ($self, $purge) = @_;
Leigh Stoller's avatar
Leigh Stoller committed
342 343 344 345

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

346 347
    my $idx  = $self->idx();
    my $uuid = $self->uuid();
Leigh Stoller's avatar
Leigh Stoller committed
348

349 350 351 352
    if (GeniUsage->DestroySliver($self, $purge)) {
	print STDERR
	    "GeniSliver::Delete: GeniUsage->DestroySliver($self) failed\n";
    }
353 354
    DBQueryWarn("delete from geni_credentials where this_uuid='$uuid'")
	or return -1;
355 356
    DBQueryWarn("delete from geni_certificates where uuid='$uuid'")
	or return -1;
Leigh Stoller's avatar
Leigh Stoller committed
357 358
    DBQueryWarn("delete from geni_slivers where idx='$idx'")
	or return -1;
359

360 361 362
    # Delete from cache. 
    delete($slivers{$idx});
    
Leigh Stoller's avatar
Leigh Stoller committed
363 364 365
    return 0;
}

366 367 368 369 370 371 372 373 374
#
# Annotate the rspec with the proper information to become
# a manifest. Returns undef because type is unknown
#
sub AnnotateManifest($)
{
    return undef;
}

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
#
# Get the manifest for an aggregate. Returns the XML string.
#
sub GetManifest($$)
{
    my ($self, $asxml) = @_;

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

    my $manifest = $self->rspec();

    return $manifest
	if (!$asxml);
    
390
    my $xml = GeniXML::Serialize($self->rspec());
391 392 393
    return $xml;
}

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
#
# Store the rspec/manifest string.
#
sub UpdateRspec($$)
{
    my ($self, $rspec) = @_;

    my $idx          = $self->idx();
    my $rspec_string = GeniXML::Serialize($rspec);
    my $safe_rspec   = DBQuoteSpecial($rspec_string);

    return -1
	if (!DBQueryWarn("update geni_slivers set ".
			 "  rspec_string=$safe_rspec ".
			 "where idx='$idx'"));
    
    $self->{'RSPEC'} = $rspec;
411
    $self->{'SLIVER'}->{'rspec_string'} = $rspec_string;
412 413 414
    return 0;
}

Leigh Stoller's avatar
Leigh Stoller committed
415 416 417 418 419 420 421 422 423 424
#
# Set the aggregate for a sliver.
#
sub SetAggregate($$)
{
    my ($self, $aggregate) = @_;

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

Leigh Stoller's avatar
Leigh Stoller committed
425 426
    my $idx      = $self->idx();
    my $agg_uuid = $aggregate->uuid();
Leigh Stoller's avatar
Leigh Stoller committed
427 428 429

    return -1
	if (!DBQueryWarn("update geni_slivers set ".
Leigh Stoller's avatar
Leigh Stoller committed
430
			 "  aggregate_uuid='$agg_uuid' ".
Leigh Stoller's avatar
Leigh Stoller committed
431 432
			 "where idx='$idx'"));
    
433 434 435 436 437 438 439
    if (!DBQueryWarn("update sliver_history set ".
		     "  aggregate_uuid='$agg_uuid' ".
		     "where idx='$idx'")) {
	print STDERR "GeniSliver::SetAggregate: ".
	    "Failed to update sliver_history for $self\n";
    }

Leigh Stoller's avatar
Leigh Stoller committed
440
    $self->{'SLIVER'}->{'aggregate_uuid'} = $agg_uuid;
Leigh Stoller's avatar
Leigh Stoller committed
441 442 443 444
    $self->{'AGGREGATE'} = $aggregate;
    return 0;
}

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
#
# And clear the aggregate.
#
sub ClearAggregate($$)
{
    my ($self) = @_;

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

    my $idx      = $self->idx();

    return -1
	if (!DBQueryWarn("update geni_slivers set ".
			 "  aggregate_uuid=NULL ".
			 "where idx='$idx'"));
    
    $self->{'SLIVER'}->{'aggregate_uuid'} = undef;
    $self->{'AGGREGATE'} = undef;
    return 0;
}

Leigh Stoller's avatar
Leigh Stoller committed
467 468 469 470 471 472 473 474 475 476 477 478
#
# Get the aggregate for a sliver.
#
sub GetAggregate($)
{
    my ($self) = @_;

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

    return $self->{'AGGREGATE'} if (defined($self->{'AGGREGATE'}));
    return undef
Leigh Stoller's avatar
Leigh Stoller committed
479
	if (!defined($self->aggregate_uuid()));
Leigh Stoller's avatar
Leigh Stoller committed
480

Leigh Stoller's avatar
Leigh Stoller committed
481
    my $aggregate = GeniAggregate->Lookup($self->aggregate_uuid());
Leigh Stoller's avatar
Leigh Stoller committed
482 483 484 485 486 487 488 489
    if (!defined($aggregate)) {
	print STDERR "Could not get aggregate object associated with $self\n";
	return undef;
    }
    $self->{'AGGREGATE'} = $aggregate;
    return $aggregate;
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
#
# Set the status for the sliver.
#
sub SetStatus($$)
{
    my ($self, $status) = @_;

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

    my $idx = $self->idx();
    
    return -1
	if (!DBQueryWarn("update geni_slivers set ".
			 "  status='$status' ".
			 "where idx='$idx'"));
    
    $self->{'SLIVER'}->{'status'} = $status;
    return 0;
}

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
#
# Set the state for the sliver.
#
sub SetState($$)
{
    my ($self, $state) = @_;

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

    my $idx = $self->idx();
    
    return -1
	if (!DBQueryWarn("update geni_slivers set ".
			 "  state='$state' ".
			 "where idx='$idx'"));
    
    $self->{'SLIVER'}->{'state'} = $state;
    return 0;
}

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
#
# And the ErrorLog. These are intended to be short ...
#
sub SetErrorLog($$)
{
    my ($self, $log) = @_;
    my $safe_log = DBQuoteSpecial($log);

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

    my $idx = $self->idx();
    
    return -1
	if (!DBQueryWarn("update geni_slivers set ".
			 "  errorlog=$safe_log ".
			 "where idx='$idx'"));
    
    $self->{'SLIVER'}->{'errorlog'} = $log;
    return 0;
}

Leigh Stoller's avatar
Leigh Stoller committed
554 555 556 557 558 559
#
# Get the experiment for the slice this sliver belongs to.
#
sub GetExperiment($)
{
    my ($self) = @_;
560
    require Experiment;
Leigh Stoller's avatar
Leigh Stoller committed
561 562 563 564 565 566 567

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

    return Experiment->Lookup($self->slice_uuid());
}

Leigh Stoller's avatar
Leigh Stoller committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
#
# Get the slice for the sliver.
#
sub GetSlice($)
{
    my ($self) = @_;

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

    return $self->{'SLICE'} if (defined($self->{'SLICE'}));

    if (!defined($self->slice_uuid())) {
	print STDERR "No slice associated with $self\n";
	return undef;
    }
    my $slice = GeniSlice->Lookup($self->slice_uuid());
    if (!defined($slice)) {
	print STDERR "Could not get slice object associated with $self\n";
	return undef;
    }
    $self->{'SLICE'} = $slice;
    return $slice;
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
#
# The expiration time for a sliver is when the slice expires.
# The DB field is ignored.
#
sub expires($)
{
    my ($self) = @_;

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

    my $slice = $self->GetSlice();
    return undef
	if (!defined($slice));

    return $slice->expires();
}

611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
#
# Look up a list of slivers for a locally instantiated slice. 
# Used by the CM.
#
sub SliceSlivers($$$)
{
    my ($class, $slice, $pref) = @_;

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

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

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

638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
#
# Find slivers "dependent" on this sliver, as for interfaces on nodes.
#
sub DependentSlivers($$)
{
    my ($self, $pref) = @_;

    return -1
	if (! (ref($self) && ref($pref)));
    @$pref = ();

    my $idx = $self->idx();
    my $resource_uuid = $self->resource_uuid();

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

    my @result = ();
    while (my ($idx) = $query_result->fetchrow_array()) {
	my $sliver = GeniSliver->Lookup($idx);
	if (!defined($sliver)) {
	    print STDERR "Could not get sliver object for $idx\n";
	    return -1;
	}
	push(@result, $sliver);
    }
    @$pref = @result;
    return 0;
}

671 672 673 674 675 676 677 678 679 680 681 682 683
############################################################################
#
# The server side methods are in packages which inherit from above.
#
package GeniSliver::Node;
use vars qw(@ISA);
@ISA = "GeniSliver";

use GeniDB;
use GeniComponent;
use GeniSlice;
use GeniCredential;
use GeniCertificate;
684
use GeniUtil;
685
use emutil;
Leigh Stoller's avatar
Leigh Stoller committed
686
use XML::Simple;
687
use libdb qw(TBDB_ALLOCSTATE_RES_INIT_DIRTY TBDB_NODESTATE_SHUTDOWN
688 689
	     TBResolveNextOSID TBDB_NODESTATE_ISUP TBDB_NODESTATE_TBFAILED
	     TBDB_NODESTATE_PXEWAIT);
690

691 692
# Error log for local physical node. This overrides the default method above,
# since it is stored in the node.
693 694 695 696 697
sub ErrorLog($)
{
    my ($self)  = @_;
    my $bootlog = "";

698
    my $node = Node->Lookup($self->resource_id());
699 700 701 702 703 704 705 706 707 708
    if (!defined($node)) {
	print STDERR "Could not map node $self to its object\n";
	return "";
    }
    if ($node->GetBootLog(\$bootlog)) {
	print STDERR "Could not get bootlog for $self ($node)\n";
	return "";
    }
    return $bootlog;
}
709

710 711 712
# Return the resource URN. This is how a resource is resolved.
# Resource urns may differ from component_urns (pcvm201-1 rather than pc201).
sub resource_urn($)
713 714 715 716 717 718
{
    my ($self)  = @_;

    return GeniHRN::Generate("@OURDOMAIN@", "node", $self->resource_id())
}

719
sub Create($$$$$$)
720
{
721
    # $rspec is a LibXML element representing a single node.
Leigh Stoller's avatar
Leigh Stoller committed
722
    my ($class, $slice, $user, $node, $rspec) = @_;
723
    my $virtualization_type = GeniXML::GetVirtualizationType($rspec);
724
    if (!defined($virtualization_type)) {
725 726 727
	print STDERR "Node does not contain a virtualization_type\n";
	return undef;
    }
Leigh Stoller's avatar
Leigh Stoller committed
728 729 730 731

    my $experiment = $slice->GetExperiment();
    if (!defined($experiment)) {
	print STDERR "Could not map $slice to its experiment\n";
732
	return undef;
Leigh Stoller's avatar
Leigh Stoller committed
733
    }
734

735 736
    #
    # An artifact of Emulab is that for shared/remote nodes, the physical
737
    # node is already allocated, but not to the current experiment.
738 739 740
    #
    if (! ($node->sharing_mode() ||
	   ($node->isremotenode() && $node->isvirtnode()))) {
741 742 743 744 745 746
	my $reservation = $node->Reservation();
	if (!defined($reservation)) {
	    print STDERR "$node was already released from $slice\n";
	    return undef;
	}
	if (! $reservation->SameExperiment($experiment)) {
747
	    print STDERR "$node is reserved to another, not $experiment\n";
748 749 750
	    # Signal error so we can look at what happened.
	    return undef;
	}
Leigh Stoller's avatar
Leigh Stoller committed
751
    }
752
    my $nickname = GeniXML::GetVirtualId($rspec);
753
    if (!defined($nickname)) {
754 755 756
	print STDERR "Node does not contain a virtual_id\n";
	return undef;
    }
Leigh Stoller's avatar
Leigh Stoller committed
757 758 759
    my $resource_uuid = $node->uuid();
    my $resource_id   = $node->node_id();
    my $hrn           = "${PGENIDOMAIN}." . $node->node_id();
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

    my $sliver = GeniSliver->Create($slice, $user, $resource_uuid, "Node",
				    $resource_id, $hrn, $nickname, $rspec);
    return undef
	if (!defined($sliver));

    return $sliver;
}

#
# Annotate the rspec of this node with the proper information to become
# a manifest. Returns LibXML tree or undef on failure.
#
sub AnnotateManifest($)
{
    my ($self) = @_;
    my $rspec = GeniXML::Parse($self->rspec_string());
    if (! defined($rspec)) {
	print STDERR "Could not parse manifest rspec for node $self\n";
	return undef;
    }
    my $node = Node->Lookup($self->resource_id());
    if (!defined($node)) {
	print STDERR "Could not map node $self to its object\n";
	return undef;
    }
    my $user = GeniUser->Lookup($self->creator_uuid(), 1);
    if (!defined($user)) {
	print STDERR "Could not map sliver $self to its creator\n";
	return undef;
    }
791 792 793 794 795
    my $experiment = $self->GetExperiment();
    if (!defined($experiment)) {
	print STDERR "Could not lookup experiment for sliver $self\n";
	return undef;
    }
796

Leigh Stoller's avatar
Leigh Stoller committed
797 798
    my $sshdport      = 22;
    my $hostname      = GeniUtil::FindHostname($node->node_id());
799
    my $sshdhost      = $hostname;
800
    my $vhostname     = GeniUtil::FindVHostname($experiment, $node);
801
    my $component_urn = GeniHRN::Generate("@OURDOMAIN@", "node",
802
					  $node->phys_nodeid());
803
    
Leigh Stoller's avatar
Leigh Stoller committed
804
    if ($node->isvirtnode()) {
805 806 807 808 809 810 811 812 813 814 815 816 817
	my $vname = $node->vname();
	my $public_control;
	$experiment->GetVirtNodeAttribute( $vname, "routable_control_ip",
					   \$public_control );
	if( defined( $public_control ) && $public_control eq "true" ) {
	    # we've assigned a public address to this one
	    $sshdhost = $hostname;
	} else {
	    # this node has no public control address of its own -- give the
	    # sshd port on the physical host's address
	    $sshdport = $node->sshdport();
	    $sshdhost = GeniUtil::FindHostname($node->phys_nodeid());
	}
818 819 820 821 822
    }

    #
    # Add this stuff to the rspec (which becomes the manifest).
    #
823
    if (GeniXML::IsVersion0($rspec)) {
824
	GeniXML::SetText("hostname", $rspec, $sshdhost);
825 826
	GeniXML::SetText("sshdport", $rspec, $sshdport)
	    if (defined($sshdport));
827 828
	GeniXML::SetText("sliver_uuid", $rspec, $self->uuid());
	GeniXML::SetText("sliver_urn", $rspec, $self->sliver_urn());
829
	GeniXML::SetText("component_urn", $rspec, $self->resource_urn());
830 831
    } else {
	GeniXML::SetText("sliver_id", $rspec, $self->sliver_urn());
832
	GeniXML::SetText("component_id", $rspec, $component_urn);
833 834 835 836 837
	my $host = GeniXML::FindFirst("n:host", $rspec);
	if (! defined($host)) {
	    $host = GeniXML::AddElement("host", $rspec);
	}
	GeniXML::SetText("name", $host, $vhostname);
838
    }
839 840 841 842 843 844 845 846 847 848 849

    if ($experiment->HasNonLocalUsers()) {
	my $services = GeniXML::FindFirst("n:services", $rspec);
	if (! defined($services)) {
	    $services = GeniXML::AddElement("services", $rspec);
	}
	my $login = GeniXML::FindFirst("n:login", $services);
	if (! defined($login)) {
	    $login = GeniXML::AddElement("login", $services);
	}
	GeniXML::SetText("authentication", $login, "ssh-keys");
850
	GeniXML::SetText("hostname", $login, $sshdhost);
851 852 853
	GeniXML::SetText("port", $login, $sshdport);
	GeniXML::SetText("username", $login, $user->uid());
    }
854

855 856 857 858
    if ($self->UpdateRspec($rspec)) {
	print STDERR "Could not insert annotated manifest for node $self ".
	    "into database";
	return undef;
859
    }
860 861

    return $rspec;
862 863
}

Leigh Stoller's avatar
Leigh Stoller committed
864 865
#
# Provision a slice. We actually did this when the ticket was requested.
Leigh Stoller's avatar
Leigh Stoller committed
866
# We fill in some virt table stuff so that tbswap will work.
Leigh Stoller's avatar
Leigh Stoller committed
867
#
Leigh Stoller's avatar
Leigh Stoller committed
868
sub Provision($;$)
Leigh Stoller's avatar
Leigh Stoller committed
869
{
Leigh Stoller's avatar
Leigh Stoller committed
870
    my ($self, $extraargs) = @_;
Leigh Stoller's avatar
Leigh Stoller committed
871 872 873 874 875 876 877

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

    #
    # the node is already allocated to the sliver, but still need to enter
    # a virt_nodes entry, and possibly more virt table entries, so that the
Leigh Stoller's avatar
Leigh Stoller committed
878
    # node will boot properly, and is otherwise controllable.
Leigh Stoller's avatar
Leigh Stoller committed
879
    #
880
    my $experiment = $self->GetExperiment();
Leigh Stoller's avatar
Leigh Stoller committed
881 882 883 884
    if (!defined($experiment)) {
	print STDERR "Could not map $self to its experiment\n";
	return -1;
    }
885
    my $node_id = $self->resource_id();
Leigh Stoller's avatar
Leigh Stoller committed
886
    return 0
887 888
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
Leigh Stoller's avatar
Leigh Stoller committed
889
    if (!defined($node)) {
890
	print STDERR "Could not map node $node_id to its object\n";
Leigh Stoller's avatar
Leigh Stoller committed
891 892 893 894
	return -1;
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
895
	print STDERR "$node was already released from slice\n";
Leigh Stoller's avatar
Leigh Stoller committed
896 897
	return -1;
    }
898
    if (! $reservation->SameExperiment($experiment)) {
899
	print STDERR "$node is reserved to another, not $experiment\n";
900 901 902 903 904
	# Signal error so we can look at what happened.
	return -1;
    }
    my $pid = $experiment->pid();
    my $eid = $experiment->eid();
905

906
    my $virt_type = GeniXML::GetVirtualizationType($self->rspec());
907
    if (!$node->isremotenode() &&
908
	defined($virt_type) &&
909
	$virt_type eq "emulab-vnode") {
910 911 912 913
	my $pnode = Node->Lookup($node->phys_nodeid());
	if (!defined($pnode)) {
	    print STDERR "Could not get pnode object for $node\n";
	    return -1;
Leigh Stoller's avatar
Leigh Stoller committed
914
	}
915

916 917
	# Mark
	$node->ModifyReservation({"genisliver_idx" => $self->idx()});
918

919
	my $subtype = GeniXML::GetVirtualizationSubtype($self->rspec());
920
	if (defined($subtype)) {
Leigh Stoller's avatar
Leigh Stoller committed
921 922 923
	    if (!$pnode->sharing_mode()) {
		$pnode->ModifyReservation({"genisliver_idx" => $self->idx()});
	    }
924
	}
Leigh Stoller's avatar
Leigh Stoller committed
925 926
    }
    else {
927 928 929 930 931 932 933 934
	#
	# For a "raw" node, there is no vnode, so this is the pnode
	# we need to mark for tmcd.
	#
	if ($node->ModifyReservation({"genisliver_idx" => $self->idx()})) {
	    return -1;
	}
    }
Leigh Stoller's avatar
Leigh Stoller committed
935 936 937 938 939 940
    return 0;
}

#
# Unprovision a sliver. 
#
941
sub UnProvision($;$)
Leigh Stoller's avatar
Leigh Stoller committed
942
{
943
    my ($self, $nophysfree) = @_;
Leigh Stoller's avatar
Leigh Stoller committed
944 945 946

    return -1
	if (! ref($self));
947 948
    $nophysfree = 0
	if (!defined($nophysfree));
Leigh Stoller's avatar
Leigh Stoller committed
949

950
    my $experiment = $self->GetExperiment();
Leigh Stoller's avatar
Leigh Stoller committed
951 952
    if (!defined($experiment)) {
	print STDERR "Could not map $self to its experiment\n";
Leigh Stoller's avatar
Leigh Stoller committed
953
	return 0;
Leigh Stoller's avatar
Leigh Stoller committed
954
    }
955
    my $node_id = $self->resource_id();
Leigh Stoller's avatar
Leigh Stoller committed
956
    return 0
957 958
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
Leigh Stoller's avatar
Leigh Stoller committed
959
    if (!defined($node)) {
960 961
	# Lets call this nonfatal since it might be a virtnode that is gone.
	print STDERR "Could not map node $node_id to its object\n";
Leigh Stoller's avatar
Leigh Stoller committed
962
	return 0;
Leigh Stoller's avatar
Leigh Stoller committed
963 964 965 966 967 968 969
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
	print STDERR "$node was already released from $self\n";
	return 0;
    }
    if ($reservation->SameExperiment($experiment)) {
Leigh Stoller's avatar
Leigh Stoller committed
970
	my $node_id = $node->node_id();
Leigh Stoller's avatar
Leigh Stoller committed
971 972
	my $pid = $experiment->pid();
	my $eid = $experiment->eid();
Leigh Stoller's avatar
Leigh Stoller committed
973

974
	if ($node->isremotenode() && $node->isvirtnode()) {
Leigh Stoller's avatar
Leigh Stoller committed
975
	    system("$VNODESETUP -p -q -m -k $pid $eid $node_id");
976
	    if ($?) {
Leigh Stoller's avatar
Leigh Stoller committed
977
		print STDERR "$VNODESETUP -k failed on $node_id\n";
978 979 980
		return -1;
	    }
	}
Leigh Stoller's avatar
Leigh Stoller committed
981 982 983 984 985 986
	elsif ($node->sharing_mode()) {
	    system("$VNODESETUP -j -q -m -k $pid $eid $node_id");
	    if ($?) {
		print STDERR "$VNODESETUP -k failed on $node_id\n";
		return -1;
	    }
987
	}
Leigh Stoller's avatar
Leigh Stoller committed
988

989
	my $virt_type = GeniXML::GetVirtualizationType($self->rspec());
990
	my $sub_type  = GeniXML::GetVirtualizationSubtype($self->rspec());
991
	if (!$node->isremotenode() && $node->isvirtnode()) {
Leigh Stoller's avatar
Leigh Stoller committed
992 993 994 995 996 997
	    my $pnode_id = $node->phys_nodeid();
	    my $pnode = Node->Lookup($pnode_id);
	    if (!defined($pnode)) {
		print STDERR "Could not get pnode object for $pnode_id\n";
		return -1;
	    }
998 999 1000

	    #
	    # If this is the last virtnode on the physnode, release the
Leigh Stoller's avatar
Leigh Stoller committed
1001 1002
	    # physnode too. Unless its a shared host, in which case just
	    # deallocate the virtnode.
1003
	    #
Leigh Stoller's avatar
Leigh Stoller committed
1004 1005 1006 1007 1008 1009
	    my @vnodes = ();
	    
	    if ($pnode->sharing_mode()) {
		$nophysfree = 1;
	    }
	    elsif ($pnode->VirtualNodes(\@vnodes) != 0) {
1010
		print STDERR "Could not get vnode list for $pnode\n";
Leigh Stoller's avatar
Leigh Stoller committed
1011 1012
		return -1;
	    }
1013

1014
	    if (scalar(@vnodes) > 1 || $nophysfree) {
1015
		system("$NFREE -q $pid $eid $node_id");
1016 1017 1018
		# Unincorporate the node. Bogus, needs fixing.
		$pnode->ModifyReservation({"genisliver_idx" => 0})
		    if (! ($pnode->sharing_mode() || scalar(@vnodes) > 1));
1019 1020 1021 1022 1023
	    }
	    else {
		system("$NFREE -x -q $pid $eid $pnode_id");
		$pnode->Refresh();
	    }
Leigh Stoller's avatar
Leigh Stoller committed
1024 1025
	}
	else {
1026 1027 1028 1029 1030
	    if ($nophysfree) {
		# Unincorporate the node. Bogus, needs fixing.
		$node->ModifyReservation({"genisliver_idx" => 0});
		goto skip;
	    }
1031 1032 1033 1034 1035 1036 1037 1038
	    #
	    # Look to see if local physical node was stopped (powered off).
	    #
	    if (!$node->isvirtnode() &&
		!$node->isremotenode() &&
		$self->state() eq "stopped") {
		system("$POWER on $node_id");
	    }
1039
	    system("$NFREE -q $pid $eid $node_id");
Leigh Stoller's avatar
Leigh Stoller committed
1040
	}
Leigh Stoller's avatar
Leigh Stoller committed
1041 1042
	if ($?) {
	    print STDERR "Could not deallocate $node from $self\n";
Leigh Stoller's avatar
Leigh Stoller committed
1043
	    $node->Refresh();
Leigh Stoller's avatar
Leigh Stoller committed
1044 1045
	    return -1;
	}
1046
      skip:
Leigh Stoller's avatar
Leigh Stoller committed
1047
	$node->Refresh();
Leigh Stoller's avatar
Leigh Stoller committed
1048 1049 1050 1051
    }
    else {
	print STDERR "$node is reserved to another, not $self\n";
	# Signal error so we can look at what happened.
1052
	return 0;
Leigh Stoller's avatar
Leigh Stoller committed
1053 1054 1055 1056
    }
    return 0;
}

1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
#
# Process a manifest.
#
sub ProcessManifest($$)
{
    my ($self, $manifest) = @_;

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

1067
    my $experiment = $self->GetExperiment();
1068 1069 1070 1071 1072 1073 1074
    if (!defined($experiment)) {
	print STDERR "Could not map $self to its experiment\n";
	return -1;
    }
    my $pid = $experiment->pid();
    my $eid = $experiment->eid();
    
1075
    my $node_id = $self->resource_id();
1076
    return 0
1077 1078
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
1079
    if (!defined($node)) {
1080
	print STDERR "Could not map node $node_id to its object\n";
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
	return -1;
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
	print STDERR "$node was already released from $self\n";
	return -1;
    }
    if ($reservation->SameExperiment($experiment)) {
	#
	# Find the corresponding node in the manifest.
	#
1092
	foreach my $ref (GeniXML::FindNodes("n:node",
1093
					    $manifest)->get_nodelist()) {
1094
	    my $sliver_urn = GeniXML::GetSliverId($ref);
1095
	    if (defined($sliver_urn) && $sliver_urn eq $self->sliver_urn()) {
1096
		# startup command.
1097
		my $startupcmd = GeniXML::GetStartupCommand($ref);
1098
		if (defined($startupcmd)) {
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
		    if (! TBcheck_dbslot($startupcmd, "virt_nodes",
					 "startupcmd",
					 TBDB_CHECKDBSLOT_WARN|
					 TBDB_CHECKDBSLOT_ERROR)) {
			print STDERR "Invalid startup command '$startupcmd'\n";
			return -1;
		    }
		    $node->Update({"startupcmd" => $startupcmd});
		}
		return 0;
	    }
	}
    }
    else {
	print STDERR "$node is reserved to another, not $self\n";
	# Signal error so we can look at what happened.
	return -1;
    }
    return 0;
}

Leigh Stoller's avatar
Leigh Stoller committed
1120
#
1121
# Start (or restart) a node. Basically, a reboot.
Leigh Stoller's avatar
Leigh Stoller committed
1122
#
1123
sub Start($$$)
Leigh Stoller's avatar
Leigh Stoller committed
1124
{
1125
    my ($self, $version, $restart) = @_;
Leigh Stoller's avatar
Leigh Stoller committed
1126 1127 1128 1129

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

1130
    my $experiment = $self->GetExperiment();
Leigh Stoller's avatar
Leigh Stoller committed
1131 1132 1133 1134
    if (!defined($experiment)) {
	print STDERR "Could not map $self to its experiment\n";
	return -1;
    }
Leigh Stoller's avatar
Leigh Stoller committed
1135 1136 1137
    my $pid = $experiment->pid();
    my $eid = $experiment->eid();
    
1138
    my $node_id = $self->resource_id();
Leigh Stoller's avatar
Leigh Stoller committed
1139
    return 0
1140 1141
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
Leigh Stoller's avatar
Leigh Stoller committed
1142
    if (!defined($node)) {
1143
	print STDERR "Could not map node $node_id to its object\n";
Leigh Stoller's avatar
Leigh Stoller committed
1144 1145 1146 1147 1148 1149 1150 1151
	return -1;
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
	print STDERR "$node was already released from $self\n";
	return -1;
    }
    if ($reservation->SameExperiment($experiment)) {
Leigh Stoller's avatar
Leigh Stoller committed
1152
	my $node_id = $node->node_id();
Leigh Stoller's avatar
Leigh Stoller committed
1153

Leigh Stoller's avatar
Leigh Stoller committed
1154 1155 1156
	#
	# Reboot and wait?
	#
Leigh Stoller's avatar
Leigh Stoller committed
1157 1158 1159 1160 1161 1162
	if ($node->isvirtnode() && $node->sharing_mode()) {
	    if ($node->eventstate() eq TBDB_NODESTATE_SHUTDOWN()) {
		system("$VNODESETUP -j -q -m $pid $eid $node_id");
	    }
	}
	else {
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
	    #
	    # Look to see if local physical node was stopped (powered off).
	    #
	    if (!$node->isvirtnode() &&
		!$node->isremotenode() &&
		$self->state() eq "stopped") {
		system("$POWER on $node_id");
	    }
	    else {
		system("$NODEREBOOT -s $node_id");
	    }
Leigh Stoller's avatar
Leigh Stoller committed
1174
	}
Leigh Stoller's avatar
Leigh Stoller committed
1175 1176
	return -1
	    if ($?);
Leigh Stoller's avatar
Leigh Stoller committed
1177 1178 1179 1180 1181
    }
    else {
	print STDERR "$node is reserved to another, not $self\n";
	# Signal error so we can look at what happened.
	return -1;
Leigh Stoller's avatar
Leigh Stoller committed
1182
    }
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
    $self->SetState("started");
    return 0;
}
# And stop.
sub Stop($$)
{
    my ($self, $version) = @_;

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

1194
    my $experiment = $self->GetExperiment();
1195 1196 1197 1198 1199 1200 1201
    if (!defined($experiment)) {
	print STDERR "Could not map $self to its experiment\n";
	return -1;
    }
    my $pid = $experiment->pid();
    my $eid = $experiment->eid();
    
1202
    my $node_id = $self->resource_id();
1203
    return 0
1204 1205
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
1206
    if (!defined($node)) {
1207
	print STDERR "Could not map node $node_id to its object\n";
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
	return -1;
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
	print STDERR "$node was already released from $self\n";
	return -1;
    }
    if ($reservation->SameExperiment($experiment)) {
	my $node_id = $node->node_id();

	#
	# Reboot and wait?
	#
	if ($node->isvirtnode() && $node->sharing_mode()) {
	    if ($node->eventstate() eq TBDB_NODESTATE_SHUTDOWN()) {
		system("$VNODESETUP -j -k -q -m $pid $eid $node_id");
	    }
	}
	else {
	    system("$POWER off $node_id");
	}
	return -1
	    if ($?);
    }
    else {
	print STDERR "$node is reserved to another, not $self\n";
	# Signal error so we can look at what happened.
	return -1;
    }
    $self->SetState("stopped");
    return 0;
}

sub ComputeStatus($$)
{
    my ($self, $pref) = @_;
    my $status = undef;

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

Leigh Stoller's avatar
Leigh Stoller committed
1249 1250 1251
    #
    # If the sliver is "broken" then call it failed.
    #
1252
    if ($self->status() eq "broken" || $self->status() eq "failed") {
Leigh Stoller's avatar
Leigh Stoller committed
1253 1254 1255 1256
	$$pref = "failed";
	return 0;
    }

1257
    my $node_id = $self->resource_id();
1258
    return -1
1259 1260
	if (!defined($node_id));
    my $node       = Node->Lookup($node_id);
1261
    if (!defined($node)) {
1262
	print STDERR "Could not map node $node_id to its object\n";
1263 1264 1265 1266 1267 1268 1269
	return -1;
    }
    my $reservation = $node->Reservation();
    if (!defined($reservation)) {
	print STDERR "$node was already released from $self\n";
	return -1;
    }
Leigh Stoller's avatar
Leigh Stoller committed
1270
    #
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
    # Special state for updating user accounts. We never go into the
    # state unless the sliver was started/ready, so we know we can go
    # back into the started state when it is done. 
    #
    if ($self->state() eq "updating_users") {
	if ($node->IsUpdated() == 0) {
	    $status = "changing";
	    goto done;
	}
	else {
	    # return to normal started state and continue below.
	    $self->SetState("started");
	}
    }
    #
Leigh Stoller's avatar
Leigh Stoller committed
1286 1287
    # Emulab does not return "unknown" ... we always know ...
    #
1288 1289 1290 1291 1292 1293 1294
    my $eventstate = $node->eventstate();
    if ($eventstate eq TBDB_NODESTATE_ISUP()) {
	$status = "ready";
    }
    elsif ($eventstate eq TBDB_NODESTATE_TBFAILED()) {
	$status = "failed";
    }
1295 1296
    elsif ($eventstate eq TBDB_NODESTATE_SHUTDOWN() ||
	   $eventstate eq TBDB_NODESTATE_PXEWAIT()) {
1297 1298
	$status = "notready";
    }
Leigh Stoller's avatar
Leigh Stoller committed
1299 1300 1301
    else {
	$status = "changing";
    }
1302
  done:
1303 1304
    $self->SetStatus($status);
    $$pref = $status;
Leigh Stoller's avatar
Leigh Stoller committed
1305 1306
    return 0;
}
Leigh Stoller's avatar
Leigh Stoller committed
1307

1308
##########################################################################
1309
#
1310 1311 1312 1313 1314 1315 1316 1317 1318
package GeniSliver::Interface;
use vars qw(@ISA);
@ISA = "GeniSliver";

use GeniDB;
use GeniComponent;
use GeniSlice;
use GeniCredential;
use GeniCertificate;
1319
use GeniUtil;
1320

1321 1322 1323 1324 1325
# Return the component URN. This is how a resource is resolved.
sub component_urn($)
{
    my ($self)  = @_;

1326 1327 1328 1329 1330 1331 1332 1333 1334
    my ($linkname,$nodeid,$iface) = split('\.', $self->nickname());
    if (! (defined($nodeid) && defined($iface))) {
	print STDERR "Could not parse nickname for interface $self\n";
	return undef;
    }
    my $node = Node->Lookup($nodeid);
    my $phys_nodeid = $node->phys_nodeid();
    return GeniHRN::GenerateInterface($OURDOMAIN, $phys_nodeid,
				      $iface);
1335 1336
}

1337 1338
sub Create()
{
1339
    my ($class, $slice, $user, $nodeid, $iface, $linkname, $rspec) = @_;
1340

1341 1342 1343 1344 1345
    # These are silly; uuids should go away for interfaces. 
    my $interface_uuid = GeniUtil::NewUUID();
    my $nickname       = "$linkname.$nodeid.$iface";
    my $hrn            = "${PGENIDOMAIN}.$nodeid.$iface";
    my $resource_id    = "$nodeid//$iface";
1346

1347 1348 1349 1350 1351
    my $sliver = GeniSliver->Create($slice, $user, $interface_uuid,
				    "Interface", $resource_id,
				    $hrn, $nickname, $rspec);
    return undef
	if (!defined($sliver));
1352

1353
    return $sliver;
1354 1355
}

1356 1357 1358 1359
#
# Annotate the rspec of this interface with the proper information to become
# a manifest. Returns LibXML tree or undef on error.
#
1360
sub AnnotateManifest($$)
1361
{
1362 1363
    my ($self, $rspec) = @_;

1364
    my ($linkname,$nodeid,$iface) = split('\.', $self->nickname());
1365 1366 1367 1368 1369 1370
    if (! (defined($nodeid) && defined($iface))) {
	print STDERR "Could not parse nickname for interface $self\n";
	return undef;
    }

    if (GeniXML::IsVersion0($rspec)) {
1371
	GeniXML::SetText("component_urn", $rspec, $self->component_urn());
1372 1373 1374
	GeniXML::SetText("sliver_uuid", $rspec, $self->uuid());
    }
    else {
1375
	GeniXML::SetText("component_id", $rspec, $self->component_urn());
1376 1377 1378 1379 1380 1381 1382 1383
	GeniXML::SetText("sliver_id", $rspec, $self->sliver_urn());
    }

    if ($self->UpdateRspec($rspec)) {
	print STDERR "Could not insert annotated manifest for $self\n";
	return undef;
    }
    return $rspec;
1384 1385
}

1386 1387 1388 1389
sub Provision($)
{
    my ($self) = @_;

1390 1391 1392 1393
    #
    # This is actually implemented in GeniAggregate since currently "link"
    # is the smallest entity; you cannot operate on an individual interface.
    #
1394 1395 1396 1397 1398 1399 1400
    return -1
	if (! ref($self));

    return 0;
}

#
1401
# Unprovision a single interface from a link/lan. 
1402 1403 1404 1405 1406 1407 1408 1409
#
sub UnProvision($)
{
    my ($self) = @_;

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

1410 1411 1412 1413 1414 1415
    my $aggregate = $self->GetAggregate();
    if (!defined($aggregate)) {
	print STDERR "Could not find aggregate for $self\n";
	return -1;
    }

1416 1417 1418 1419 1420 1421 1422
    #
    # Skip this for Tunnel aggregates; nothing to to, and the code below
    # only applies for vlan links.
    #
    return 0
	if ($aggregate->type() eq "Tunnel");

1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
    #
    # This is terrible; we need an interface to remove ports form vlans,
    # so that we do not have to tear down the entire vlan and recreate.
    #
    if ($aggregate->UnProvision() != 0) {
	print STDERR "Could not unprovision $aggregate\n";
	return -1;
    }
    $self->ClearAggregate();
    if ($aggregate->Provision() != 0) {
	print STDERR "Could not provision $aggregate\n";
	return -1;
    }
1436 1437 1438 1439
    return 0;
}

#
1440
# Start a sliver.
1441
#
1442
sub Start($$)
1443
{
1444
    my ($self, $version) = @_;
1445

1446 1447 1448 1449
    #
    # This is actually implemented in GeniAggregate since currently "link"
    # is the smallest entity; you cannot operate on an individual interface.
    #
1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
    return -1
	if (! ref($self));

    $self->SetState("started");
    return 0;
}

#
# Stop
#
sub Stop($$)
{
    my ($self, $version) = @_;

1464 1465 1466 1467 1468
    return -1
	if (! ref($self));

    return 0;
}
Leigh Stoller's avatar
Leigh Stoller committed
1469

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