GeniCMV2.pm.in 106 KB
Newer Older
1
2
#!/usr/bin/perl -wT
#
3
# Copyright (c) 2008-2014 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.
# 
# }}}
29
30
31
32
33
34
35
36
37
38
39
#
package GeniCMV2;

#
# The server side of the CM interface on remote sites. Also communicates
# with the GMC interface at Geni Central as a client.
#
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);

40
41
@ISA    = qw(Exporter);
@EXPORT = qw();
42
43
44
45
46
47
48

# Must come after package declaration!
use GeniDB;
use GeniResponse;
use GeniTicket;
use GeniCredential;
use GeniCertificate;
49
use GeniComponent;
50
51
52
53
use GeniSlice;
use GeniAggregate;
use GeniSliver;
use GeniUtil;
54
use GeniCM;
55
use GeniHRN;
56
use GeniXML;
57
use GeniStitch;
58
use GeniStd;
59
60
61
62
63
64
65
66
use emutil;
use English;
use Data::Dumper;
use XML::Simple;
use Date::Parse;
use POSIX qw(strftime tmpnam);
use Time::Local;
use Compress::Zlib;
67
use File::Temp qw(tempfile);
68
use MIME::Base64;
69
use Errno;
70
71
72
73
74
75
76
77
78

# Configure variables
my $TB		   = "@prefix@";
my $TBOPS          = "@TBOPSEMAIL@";
my $TBAPPROVAL     = "@TBAPPROVALEMAIL@";
my $TBAUDIT   	   = "@TBAUDITEMAIL@";
my $BOSSNODE       = "@BOSSNODE@";
my $OURDOMAIN      = "@OURDOMAIN@";
my $PGENIDOMAIN    = "@PROTOGENI_DOMAIN@";
79
my $ELABINELAB     = "@ELABINELAB@";
80
my $TBBASE         = "@TBBASE@";
81
my $TBDOCBASE      = "@TBDOCBASE@";
82
83
84
85
my $CREATEEXPT     = "$TB/bin/batchexp";
my $ENDEXPT        = "$TB/bin/endexp";
my $NALLOC	   = "$TB/bin/nalloc";
my $NFREE	   = "$TB/bin/nfree";
86
my $TEVC	   = "$TB/bin/tevc";
87
88
89
90
91
92
93
94
95
96
97
98
my $AVAIL	   = "$TB/sbin/avail";
my $PTOPGEN	   = "$TB/libexec/ptopgen";
my $TBSWAP	   = "$TB/bin/tbswap";
my $SWAPEXP	   = "$TB/bin/swapexp";
my $PLABSLICE	   = "$TB/sbin/plabslicewrapper";
my $NAMEDSETUP     = "$TB/sbin/named_setup";
my $VNODESETUP     = "$TB/sbin/vnode_setup";
my $GENTOPOFILE    = "$TB/libexec/gentopofile";
my $TARFILES_SETUP = "$TB/bin/tarfiles_setup";
my $MAPPER         = "$TB/bin/mapper";
my $VTOPGEN        = "$TB/bin/vtopgen";
my $SNMPIT         = "$TB/bin/snmpit";
99
100
my $CLONEIMAGE     = "$TB/sbin/clone_image";
my $CREATEIMAGE    = "$TB/bin/create_image";
101
my $DELETEIMAGE    = "$TB/sbin/delete_image";
102
103
my $WAP            = "$TB/sbin/withadminprivs";
my $SHAREVLAN      = "$TB/sbin/sharevlan";
104
my $XMLLINT	   = "/usr/local/bin/xmllint";
105
106
my $PRERENDER      = "$TB/libexec/vis/prerender";
my $EMULAB_PEMFILE = "@prefix@/etc/genicm.pem";
107
108
# Just one of these, at Utah.
my $GENICH_PEMFILE = "@prefix@/etc/genich.pem";
109
my $WITHPROVENANCE = @IMAGEPROVENANCE@;
110
my $API_VERSION    = 2;
111
112
113
114
115
116
117
118

#
# Tell the client what API revision we support.  The correspondence
# between revision numbers and API features is to be specified elsewhere.
# No credentials are required.
#
sub GetVersion()
{
119
120
    my @input_rspec_versions = ( "0.1", "0.2", "2", "3", "PG 0.1", "PG 0.2", "PG 2" );
    my @ad_rspec_versions = ( "0.1", "0.2", "2", "3", "PG 0.1", "PG 0.2", "PG 2" );
Gary Wong's avatar
Gary Wong committed
121
122
123
    my $blob = {
	"api" => $API_VERSION,
	"level" => 1,
124
	"input_rspec" => \@input_rspec_versions,
125
	"output_rspec" => "2",
126
	"ad_rspec" => \@ad_rspec_versions
Gary Wong's avatar
Gary Wong committed
127
    };
128
    return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
129
130
131
132
133
134
135
136
}

#
# Respond to a Resolve request. 
#
sub Resolve($)
{
    my ($argref) = @_;
137
138
    my $credentials = $argref->{'credentials'};
    my $urn         = $argref->{'urn'};
139
    my $admin       = 0;
140
    my $isauth	    = 0;
141

142
143
144
145
146
147
    if (! (defined($credentials) && defined($urn))) {
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
    if (! GeniHRN::IsValid($urn)) {
	return GeniResponse->MalformedArgsResponse("Invalid URN");
    }
148
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
149
150
151
    return $credential
	if (GeniResponse::IsResponse($credential));

152
153
154
    my ($object, $type) = LookupURN($urn);
    return $object
	if (GeniResponse::IsResponse($object));
155
156
157
158

    #
    # This is a convenience for testing. If a local user and that
    # user is an admin person, then do whatever it says. This is
159
160
    # easier then trying to do this with credential privs. But,
    # watch for credentials from authorities instead of users.
161
    #
162
163
    my (undef,$callertype,$callerid) = GeniHRN::Parse($credential->owner_urn());
    if ($callertype eq "user") {
164
	my $user = GeniCM::CreateUserFromCertificate($credential);
165
166
	if (!GeniResponse::IsResponse($user) &&
	    $user->IsLocal() && $user->admin()) {
167
168
169
	    $admin = 1;
	}
    }
170
171
    elsif ($callertype eq "authority" &&
	   ($callerid eq "cm" || $callerid eq "sa")) {
172
	$isauth = 1;
173
    }
174
175
    
    if ($type eq "node") {
176
	my $node  = $object;
177
178
179
180
181
	# Not sure about this, but I do know that Resolving a virtnode
	# is not useful right now. 
	if ($node->isvirtnode()) {
	    $node = Node->Lookup($node->phys_nodeid());
	}
182
	my $rspec = GeniCM::GetAdvertisement(0, $node->node_id(), "0.1", undef);
183
	if (! defined($rspec)) {
184
	    print STDERR "Could not get advertisement for $node!\n";
185
	    return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
186
					"Error getting advertisement");
187
	}
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	my $me = GeniAuthority->Lookup($ENV{'MYURN'});
	if (!defined($me)) {
	    print STDERR
		"Could not find local authority object for $ENV{'MYURN'}\n";
	    return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
					"Error getting advertisement");
	}
	my $myurn = GeniHRN::Generate($OURDOMAIN, "node", $node->node_id());
	my $myhrn = "${PGENIDOMAIN}." . $node->node_id();

	#
	# See if the component object exists; if not create it.
	#
	my $component = GeniComponent->Lookup($node->uuid());
	if (!defined($component)) {
	    my $certificate = GeniCertificate->Lookup($node->uuid());
	    if (!defined($certificate)) {
		$certificate =
		    GeniCertificate->Create({'urn'  => $myurn,
					     'hrn'  => $myhrn,
					     'email'=> $TBOPS,
					     'uuid' => $node->uuid(),
					     'url'  => $me->url()});
		if (!defined($certificate)) {
		    print STDERR "Could not generate certificate for $node\n";
		    return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
					    "Error getting advertisement");
		}
	    }
	    $component = GeniComponent->Create($certificate, $me);
	    if (!defined($component)) {
		print STDERR "Could not create component for $node\n";
		return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
					    "Error getting advertisement");
	    }
	}
224
	# Return a blob.
225
	my $blob = { "hrn"          => $myhrn,
226
227
		     "uuid"         => $node->uuid(),
		     "role"	    => $node->role(),
228
229
		     "hostname"     =>
			 GeniUtil::FindHostname($node->node_id()),
230
231
		     "physctrl"     => 
			 Interface->LookupControl($node->phys_nodeid())->IP(),
232
233
234
235
		     "urn"          => $myurn,
		     "rspec"        => $rspec,
		     "url"          => $me->url(),
		     "gid"          => $component->cert(),
236
237
238
239
		   };

	return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
240
    if ($type eq "slice") {
241
242
	my $slice = $object;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
243
244
245
246
	#
	# In this implementation, the caller must hold a valid slice
	# credential for the slice being looked up. 
	#
247
248
	if (! ($isauth || $admin ||
	       $slice->urn() eq $credential->target_urn())) {
249
250
	    return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
					"No permission to resolve $slice\n");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
251
252
253
254
255
256
	}
	# Return a blob.
	my $blob = { "urn"          => $urn };

	my $aggregate = GeniAggregate->SliceAggregate($slice);
	if (defined($aggregate)) {
257
	    $blob->{'sliver_urn'} = $aggregate->urn();
258
259
260
261
	    my $manifest = $aggregate->GetManifest(1);
	    if (defined($manifest)) {
		$blob->{'manifest'}   = $manifest;
	    }
262
263
264
265
266
267
268
269
270
271
272
	    # For key bindings.
	    my $slice_experiment = $slice->GetExperiment();
	    if (!defined($slice_experiment)) {
		print STDERR "*** No Experiment for $slice\n";
	    }
	    else {
		my $bindings;
		if ($slice_experiment->NonLocalUsers(\$bindings)) {
		    print STDERR "*** No bindings for $slice_experiment\n";
		}
		elsif (@{ $bindings }) {
273
		    $blob->{'users'} = $bindings;
274
275
		}
	    }
276
277
278
	    $blob->{'public_url'} =
		"$TBDOCBASE/showslicepub.php?publicid=" . $slice->publicid()
		if (defined($slice->publicid()));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
279
280
281
	}
	my $ticket = GeniTicket->SliceTicket($slice);
	if (defined($ticket)) {
282
	    $blob->{'ticket_urn'} = $ticket->urn();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
283
284
285
286
	}
	return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
    }
    if ($type eq "sliver") {
287
	my $sliver = $object;
288
289
290
	my $slice  = $sliver->GetSlice();
	return GeniResponse->Create(GENIRESPONSE_ERROR)
	    if (!defined($slice));
291

Leigh B. Stoller's avatar
Leigh B. Stoller committed
292
293
294
295
	#
	# In this implementation, the caller must hold a valid slice
	# or sliver credential for the slice being looked up. 
	#
296
	if (! ($admin || $isauth ||
297
	       $sliver->urn() eq $credential->target_urn() ||
298
299
300
301
302
	       $slice->urn() eq $credential->target_urn())) {
	    print STDERR $sliver->urn() . "\n";
	    print STDERR $slice->urn() . "\n";
	    print STDERR $credential->target_urn() . "\n";
	    print STDERR $ENV{'MYURN'} . "\n";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
303
304
	    return GeniResponse->Create(GENIRESPONSE_FORBIDDEN);
	}
305
306
	my $manifest = $sliver->GetManifest(1);
	if (!defined($manifest)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
307
308
309
310
311
312
	    return GeniResponse->Create(GENIRESPONSE_ERROR);
	}
	# Return a blob.
	my $blob = { "urn"          => $urn,
		     "manifest"     => $manifest,
		 };
313
314
315
316
	$blob->{'public_url'} =
	    "$TBDOCBASE/showslicepub.php?publicid=" . $slice->publicid()
	    if (defined($slice->publicid()));
	
317
318
319
320
321
322
323
324
325
326
327
	# For key bindings.
	my $slice_experiment = $slice->GetExperiment();
	if (!defined($slice_experiment)) {
	    print STDERR "*** No Experiment for $slice\n";
	}
	else {
	    my $bindings;
	    if ($slice_experiment->NonLocalUsers(\$bindings)) {
		print STDERR "*** No bindings for $slice_experiment\n";
	    }
	    elsif (@{ $bindings }) {
328
		$blob->{'users'} = $bindings;
329
330
	    }
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
331
332
333
	return GeniResponse->Create(GENIRESPONSE_SUCCESS, $blob);
    }
    if ($type eq "ticket") {
334
335
	my $ticket = $object;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
336
337
338
339
	#
	# In this implementation, the caller must hold a valid slice
	# or sliver credential to get the ticket.
	#
340
	my $slice = GeniSlice->Lookup($ticket->slice_urn());
Leigh B. Stoller's avatar
Leigh B. Stoller committed
341
342
343
344
	if (!defined($slice)) {
	    print STDERR "Could not find slice for $ticket\n";
	    return GeniResponse->Create(GENIRESPONSE_ERROR);
	}
345
	if (! ($admin || $slice->urn() eq $credential->target_urn())) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
346
347
348
349
350
	    #
	    # See if its the sliver credential. 
	    #
	    my $aggregate = GeniAggregate->SliceAggregate($slice);
	    if (!defined($aggregate) ||
351
		$aggregate->urn() ne $credential->target_urn()) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
352
353
354
355
356
		return GeniResponse->Create(GENIRESPONSE_FORBIDDEN());
	    }
	}
	return GeniResponse->Create(GENIRESPONSE_SUCCESS, $ticket->asString());
    }
357
358
    return GeniResponse->Create(GENIRESPONSE_UNSUPPORTED, undef,
				"Cannot resolve $type at this authority");
359
360
361
362
363
364
365
366
}

#
# Discover resources on this component, returning a resource availablity spec
#
sub DiscoverResources($)
{
    my ($argref) = @_;
367
368
369
    my $credentials = $argref->{'credentials'};
    my $available   = $argref->{'available'} || 0;
    my $compress    = $argref->{'compress'} || 0;
370
    my $version     = $argref->{'rspec_version'} || undef;
371
372
373
374

    if (! (defined($credentials))) {
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
375
    my ($credential,$speaksfor,@morecreds) = GeniStd::CheckCredentials($credentials);
376
377
    return $credential
	if (GeniResponse::IsResponse($credential));
378

379
    return GeniCM::DiscoverResourcesAux($available, $compress,
380
        $version, [$credential, @morecreds]);
381
382
383
384
385
386
387
388
}

#
# Create a Sliver.
#
sub CreateSliver($)
{
    my ($argref) = @_;
389
390
391
392
393
    my $slice_urn    = $argref->{'slice_urn'};
    my $rspecstr     = $argref->{'rspec'};
    my $credentials  = $argref->{'credentials'};
    my $keys         = $argref->{'keys'};
    my $impotent     = $argref->{'impotent'} || 0;
394
395
    require Node;
    require Experiment;
396
397
    require libtestbed;
    require libaudit;
398

399
    # For now, I am not worrying about the slice_urn argument.
400
401
    if (! (defined($credentials) &&
	   defined($slice_urn) && defined($rspecstr))) {
402
403
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
404
405
406
407
408
409
    if (! ($rspecstr =~ /^[\040-\176\012\015\011]+$/)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in rspec");
    }
    if (! GeniHRN::IsValid($slice_urn)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in URN");
    }
410
411
    my ($credential,$speaksfor,@morecreds) =
	GeniStd::CheckCredentials($credentials);
412
413
    return $credential
	if (GeniResponse::IsResponse($credential));
414

415
416
    main::AddLogfileMetaData("slice_urn", $slice_urn);
    
417
418
419
420
421
422
423
    #
    # In this implementation, the user must provide a slice credential,
    # so we ignore the slice_urn. For CreateSliver(), the slice must not
    # be instantiated.
    #
    my ($slice,$aggregate) = Credential2SliceAggregate($credential);
    if (defined($slice)) {
424
425
426
	return $slice
	    if (GeniResponse::IsResponse($slice));

427
428
429
430
	if ($slice_urn ne $slice->urn()) {
	    return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
					"Credential does not match the URN");
	}
431
	main::AddLogfileMetaDataFromSlice($slice);
432
	
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
	#
	# Watch for a placeholder slice and update it.
	#
	if ($slice->isplaceholder()) {
	    if ($slice->Lock() != 0) {
		return GeniResponse->BusyResponse();
	    }
	    #
	    # Confirm that the slice certificate is the same.
	    #
	    if ($slice->cert() ne $credential->target_cert()->cert()) {
		$slice->UnLock();
		return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
					    "Slice certificate mismatch");
	    }
	    my $user =
449
		GeniCM::CreateUserFromCertificate($credential);
450
	    if (GeniResponse::IsResponse($user)) {	    
451
		$slice->UnLock();
452
		return $user;
453
454
455
456
457
458
459
460
	    }
	    if ($slice->ConvertPlaceholder($user) != 0) {
		$slice->UnLock();
		return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
					    "Could not convert placeholder");
	    }
	    $slice->UnLock();
	}
461
462
463
464
465
	if (defined($aggregate)) {
	    return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
					"Must delete existing slice first");
	}
    }
466
    my $rspec = GeniCM::GetTicketAux($credential, $rspecstr,
467
468
				     0, $impotent, 1, 0,
				     undef, $speaksfor, @morecreds);
469
470
471
    return $rspec
	if (GeniResponse::IsResponse($rspec));

Leigh B Stoller's avatar
Leigh B Stoller committed
472
    $slice = GeniSlice->Lookup($credential->target_urn());
473
474
475
476
    if (!defined($slice)) {
	print STDERR "CreateSliver: Could not find slice for $credential\n";
	return GeniResponse->Create(GENIRESPONSE_ERROR, undef,"Internal Error");
    }
477
    main::AddLogfileMetaDataFromSlice($slice);
478

479
480
481
482
    # Make sure that the next phase sees all changes.
    Experiment->FlushAll();
    Node->FlushAll();

483
484
    my $response = GeniCM::SliverWorkAux($credential, $rspec,
					 $keys, 0, $impotent, 1, 0, $speaksfor);
485

486
487
488
489
490
491
492
    if (GeniResponse::IsError($response)) {
	#
	# We have to make sure there is nothing left over since there
	# is no actual ticket, so the resources will not get cleaned
	# up by the daemon. This is mostly cause I am reaching into
	# the V1 code, and its messy.
	#
493
	$slice = GeniSlice->Lookup($credential->target_urn());
494
	if (defined($slice)) {
495
496
497
498
499
	    if ($slice->Lock() != 0) {
		print STDERR
		    "CreateSliver: Could not lock $slice before delete\n";
		return $response;
	    }
500
501
	    GeniCM::CleanupDeadSlice($slice, 1);
	}
502
	return $response;
503
    }
504
    my ($sliver_credential) = @{ $response->{'value'} };
505

506
507
508
    #
    # Leave the slice intact on error, so we can go look at it. 
    #
509
    if ($slice->WaitForLock(30) != 0) {
510
511
512
513
	print STDERR "CreateSliver: Could not lock $slice before start\n";
	return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
				    "Internal Error");
    }
514
    $aggregate = GeniAggregate->SliceAggregate($slice);
515
516
517
518
519
    if (!defined($aggregate)) {
	print STDERR "CreateSliver: Could not find aggregate for $slice\n";
	return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
				    "Internal Error");
    }
520
521
522
    # We get the manifest from the aggregate object, so that the
    # expiration goes in.
    my $sliver_manifest = $aggregate->GetManifest(1);
523

524
525
    #
    # At this point we want to return and let the startsliver proceed
526
    # in the background. Parent never returns, just the child.
527
    #
528
    my $mypid = main::WrapperFork();
529
530
531
532
    if ($mypid) {
	return GeniResponse->Create(GENIRESPONSE_SUCCESS,
				    [$sliver_credential, $sliver_manifest]);
    }
533

534
535
536
537
    # Make sure that the next phase sees all changes.
    Experiment->FlushAll();
    Node->FlushAll();

538
539
540
541
542
543
544
    #
    # The callee might also do a wrapper fork, so remember our PID
    # to make sure we unlock properly in only the parent side of the
    # fork. Child runs with slice unlocked for now. 
    #
    $mypid = $PID;
    
545
    if ($aggregate->Start($API_VERSION) != 0) {
546
547
548
549
550
551
552
	if ($PID == $mypid) {
	    $slice->UnLock();
	    print STDERR "Could not start sliver.\n";
	}
	else {
	    print STDERR "Error waiting for nodes.\n";
	}
553
	return -1;
554
    }
555
556
557
    if ($PID == $mypid) {
	$slice->UnLock();
    }
558
    return 0;
559
560
561
562
563
564
}

#
# Delete a Sliver.
#
sub DeleteSliver($)
565
566
567
568
569
570
571
572
573
574
575
576
{
    my ($argref) = @_;
    my $sliver_urn   = $argref->{'sliver_urn'};
    my $credentials  = $argref->{'credentials'};
    my $impotent     = $argref->{'impotent'} || 0;

    if (! (defined($credentials) && defined($sliver_urn))) {
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
    if (! GeniHRN::IsValid($sliver_urn)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in URN");
    }
577
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
578
579
580
581
582
583
584
585
    return $credential
	if (GeniResponse::IsResponse($credential));

    #
    # In this implementation, the user must provide a slice or sliver
    # credential
    #
    my ($slice, $aggregate) = Credential2SliceAggregate($credential);
586
587
588
    return $slice
	if (defined($slice) && GeniResponse::IsResponse($slice));
    
589
590
591
592
593
594
595
596
597
    if (! (defined($slice) && defined($aggregate))) {
	return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
				    "Sliver does not exist");
    }
    if ($sliver_urn ne $aggregate->urn()) {
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
				    "Credential does not match the URN");
    }

598
599
600
601
    # If a monitor process is running, we are "busy".
    if ($slice->monitor_pid()) {
	return GeniResponse->MonitorResponse();
    }
602
603
604
605
606
607
608
609
    if ($slice->Lock() != 0) {
	return GeniResponse->BusyResponse();
    }
    # If any slivers are imaging, then we are busy as well.
    if ($aggregate->CheckSliverStates("imaging")) {
	$slice->UnLock();
	return GeniResponse->BusyResponse();
    }
610
611
612
613
    
    main::AddLogfileMetaData("sliver_urn", $sliver_urn);
    main::AddLogfileMetaDataFromSlice($slice);
    
614
615
616
617
618
    #
    # We need this below to sign the ticket.
    #
    my $authority = GeniCertificate->LoadFromFile($EMULAB_PEMFILE);
    if (!defined($authority)) {
619
	print STDERR " Could not load $EMULAB_PEMFILE\n";
620
621
622
623
624
625
	return GeniResponse->Create(GENIRESPONSE_ERROR);
	
    }
    #
    # We need the user to sign the new ticket to. 
    #
626
    my $user = GeniCM::CreateUserFromCertificate($credential);
627
628
    return $user
	if (GeniResponse::IsResponse($user));
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    
    my $response = GeniCM::DeleteSliverAux($credential, $impotent, 1);
    return $response
	if (GeniResponse::IsResponse($response));

    #
    # In the v2 API, return a new ticket for the resources
    # (which were not released). As with all tickets, it will
    # expire very quickly. 
    #
    #
    # Create a new ticket from the manifest.
    #
    my $manifest = $aggregate->GetManifest(0);
    if (!defined($manifest)) {
	print STDERR "No manifest found for $aggregate\n";
	$response = GeniResponse->Create(GENIRESPONSE_ERROR);
	goto bad;
    }
648
649
    my $ticket = GeniTicket->Create($authority, $user,
				    GeniXML::Serialize($manifest));
650
651
652
653
654
    if (!defined($ticket)) {
	print STDERR "Could not create new ticket for $slice\n";
	$response = GeniResponse->Create(GENIRESPONSE_ERROR);
	goto bad;
    }
655
    $ticket->SetSlice($slice);
656
657
    $ticket->SetSpeaksFor($speaksfor)
	if (defined($speaksfor));
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
    
    if ($ticket->Sign()) {
	$ticket->Delete();
	print STDERR "Could not sign new ticket $ticket\n";
	$response = GeniResponse->Create(GENIRESPONSE_ERROR);
	goto bad;
    }
    if ($ticket->Store()) {
	$ticket->Delete();
	print STDERR "Could not store new ticket $ticket\n";
	$response = GeniResponse->Create(GENIRESPONSE_ERROR);
	goto bad;
    }
    my $slice_uuid = $slice->uuid();
    DBQueryWarn("delete from geni_manifests ".
		"where slice_uuid='$slice_uuid'");
    $slice->UnLock();
    return GeniResponse->Create(GENIRESPONSE_SUCCESS, $ticket->asString());

  bad:
    if (GeniCM::CleanupDeadSlice($slice) != 0) {
	print STDERR "Could not cleanup slice\n";
    }
    return $response;
}

#
# Delete a Slice
#
sub DeleteSlice($)
688
689
{
    my ($argref) = @_;
690
691
692
693
    my $slice_urn    = $argref->{'slice_urn'};
    my $credentials  = $argref->{'credentials'};
    my $impotent     = $argref->{'impotent'} || 0;

694
    if (! (defined($credentials) && defined($slice_urn))) {
695
696
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
697
698
699
    if (! GeniHRN::IsValid($slice_urn)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in URN");
    }
700
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
701
702
    return $credential
	if (GeniResponse::IsResponse($credential));
703

704
705
706
707
    #
    # In this implementation, the user must provide a slice credential.
    #
    my ($slice, $aggregate) = Credential2SliceAggregate($credential);
708
709
710
    return $slice
	if (defined($slice) && GeniResponse::IsResponse($slice));

711
712
713
714
    if (! defined($slice)) {
	return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
				    "No such slice here");
    }
715
    main::AddLogfileMetaDataFromSlice($slice);
716
    
717
718
719
720
721
722
723
    if ($slice_urn ne $slice->urn()) {
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
				    "Credential does not match the URN");
    }
    if ($slice->Lock() != 0) {
	return GeniResponse->BusyResponse();
    }
724
725

    #
726
727
728
729
730
731
732
733
    # If a monitor process is running, then the slice is busy.
    # This might mean that the user will not be able to delete
    # the slice for a long time, but we are having problems with
    # users canceling slices before they finish setting up, and
    # the XEN client side is not handling this very well. Note that
    # the cleanupslice script calls GeniCM::CleanupDeadSlice()
    # directly, which *does* kill the monitor, so admin cleanup
    # is not affected.
734
735
    #
    if ($slice->monitor_pid()) {
736
737
	$slice->UnLock();
	return GeniResponse->MonitorResponse();
738
    }
739
    # If any slivers are imaging, then we are busy as well.
Leigh B Stoller's avatar
Leigh B Stoller committed
740
741
    if (defined($aggregate) &&
	$aggregate->CheckSliverStates("imaging")) {
742
743
744
	$slice->UnLock();
	return GeniResponse->BusyResponse();
    }
745
746
747
748
749
750
751
752
753

    #
    # Proceed in the background. No reason to make the caller wait,
    # it takes too long.
    #
    my $mypid = main::WrapperFork();
    if ($mypid) {
	return GeniResponse->Create(GENIRESPONSE_SUCCESS);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
754
    if (GeniCM::CleanupDeadSlice($slice, 1) != 0) {
755
756
757
758
	#
	# We may need to send email here. 
	#
	return -1;
759
    }
760
    return 0;
761
762
763
764
765
766
767
768
}

#
# Get a Sliver (credential)
#
sub GetSliver($)
{
    my ($argref) = @_;
769
770
    my $slice_urn    = $argref->{'slice_urn'};
    my $credentials  = $argref->{'credentials'};
771

772
    if (! (defined($credentials) && defined($slice_urn))) {
773
774
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
775
776
777
    if (! GeniHRN::IsValid($slice_urn)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in URN");
    }
778
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
779
780
781
    return $credential
	if (GeniResponse::IsResponse($credential));

782
783
784
785
    #
    # In this implementation, the user must provide a slice credential.
    #
    my ($slice, $aggregate) = Credential2SliceAggregate($credential);
786
787
788
    return $slice
	if (defined($slice) && GeniResponse::IsResponse($slice));

789
    if (! (defined($slice) && defined($aggregate))) {
790
	return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
791
792
793
794
795
796
				    "No slice or aggregate here");
    }
    if ($slice_urn ne $slice->urn()) {
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
				    "Credential does not match the URN");
    }
797
    return GeniCM::GetSliverAux($credential);
798
799
800
}

#
801
# Start a sliver (not sure what this means yet, so reboot for now).
802
#
803
sub StartSliver($)
804
805
{
    my ($argref) = @_;
806
    my $slice_urn    = $argref->{'slice_urn'};
807
    my $sliver_urns  = $argref->{'sliver_urns'} || $argref->{'component_urns'};
808
    my $credentials  = $argref->{'credentials'};
809
    my $manifest     = $argref->{'manifest'};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
810
    
811
812
    return SliverAction("start",
			$slice_urn, $sliver_urns, $credentials, $manifest);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
813
814
815
816
817
818
}

sub StopSliver($)
{
    my ($argref) = @_;
    my $slice_urn    = $argref->{'slice_urn'};
819
    my $sliver_urns  = $argref->{'sliver_urns'} || $argref->{'component_urns'};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
820
821
    my $credentials  = $argref->{'credentials'};

822
823
    return SliverAction("stop",
			$slice_urn, $sliver_urns, $credentials, undef);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
824
825
826
827
828
829
}

sub RestartSliver($)
{
    my ($argref) = @_;
    my $slice_urn    = $argref->{'slice_urn'};
830
    my $sliver_urns  = $argref->{'sliver_urns'} || $argref->{'component_urns'};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
831
    my $credentials  = $argref->{'credentials'};
832
    my $manifest     = $argref->{'manifest'};
Leigh B. Stoller's avatar
Leigh B. Stoller committed
833

834
835
    return SliverAction("restart",
			$slice_urn, $sliver_urns, $credentials, $manifest);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
836
}
837

838
839
840
841
842
843
844
845
846
847
848
sub ReloadSliver($)
{
    my ($argref) = @_;
    my $slice_urn    = $argref->{'slice_urn'};
    my $sliver_urns  = $argref->{'sliver_urns'} || $argref->{'component_urns'};
    my $credentials  = $argref->{'credentials'};

    return SliverAction("reload",
			$slice_urn, $sliver_urns, $credentials, undef);
}

849
sub SliverAction($$$$$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
850
{
851
    my ($action, $slice_urn, $sliver_urns, $credentials, $manifest) = @_;
852
    my $response;
853
    my $isasync = 0;
854

855
856
    if (! (defined($credentials) &&
	   (defined($slice_urn) || defined($sliver_urns)))) {
857
858
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
859
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
860
861
862
863
864
865
866
867
    return $credential
	if (GeniResponse::IsResponse($credential));

    $credential->HasPrivilege( "pi" ) or
	$credential->HasPrivilege( "info" ) or
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
				    "Insufficient privilege");

868
869
870
871
872
873
874
875
876
    if (defined($manifest)) {
	$manifest = GeniXML::Parse($manifest);
	if (!defined($manifest)) {
	    print STDERR "Error reading manifest\n";
	    return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
					"Bad manifest");
	}
    }
    
877
878
879
    #
    # For now, only allow top level aggregate or the slice
    #
880
    my ($slice, $aggregate) = Credential2SliceAggregate($credential);
881
882
    return $slice
	if (defined($slice) && GeniResponse::IsResponse($slice));
Srikanth's avatar
Srikanth committed
883

884
885
886
887
    #
    # I think this allows the CM to perform an action without
    # the slice credential?
    #
888
889
890
891
892
    if ( (!defined($slice)) && 
          ($credential->target_urn() =~ /\+authority\+cm$/)) {
          # administrative credentials are presented.
          my $cm_urn = GeniHRN::Generate($OURDOMAIN, "authority", "cm");
          if ($cm_urn != $credential->target_urn()) {
Srikanth's avatar
Srikanth committed
893
            return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
894
895
896
897
                      "Credential target does not match CM URN");
          }

      if (!defined($slice_urn)) {
Srikanth's avatar
Srikanth committed
898
899
          return GeniResponse->MalformedArgsResponse("Missing arguments");
      }       
900
901
902
903
904
905
906
907
      $slice = GeniSlice->Lookup($slice_urn);
      return GeniResponse->Create(GENIRESPONSE_ERROR, undef, 
                "No Slice with urn $slice_urn here")
          if (!defined($slice));
      $aggregate = GeniAggregate->SliceAggregate($slice);
      return GeniResponse->Create(GENIRESPONSE_ERROR, undef, 
                      "No Aggregate here")
          if (!defined($aggregate));
Srikanth's avatar
Srikanth committed
908
    } 
909

910
911
912
913
    if (! (defined($slice) && defined($aggregate))) {
	return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
				    "No slice or aggregate here");
    }
914
915
916
917
918
919
920
    main::AddLogfileMetaDataFromSlice($slice);

    # If a monitor process is running, we are "busy".
    if ($slice->monitor_pid()) {
	return GeniResponse->MonitorResponse();
    }
    
921
922
    if (defined($slice_urn)) {
	if (! GeniHRN::IsValid($slice_urn)) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
923
924
	    return
		GeniResponse->MalformedArgsResponse("Bad characters in URN");
925
	}
926
927
928
	if ($slice_urn ne $slice->urn()) {
	    return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
					"Credential does not match the URN");
929
	}
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
    }
    if ($slice->Lock() != 0) {
	return GeniResponse->BusyResponse();
    }
    # Shutdown slices get nothing.
    if ($slice->shutdown()) {
	$slice->UnLock();
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
				    "Slice has been shutdown");
    }
    if ($aggregate->ComputeState()) {
	$slice->UnLock();
	print STDERR "Could not determine current state\n";
	return GeniResponse->Create(GENIRESPONSE_ERROR);
    }
    my $CheckState = sub {
	my ($object, $action) = @_;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
948
	if ($action eq "start") {
949
950
	    if ($object->state() ne "stopped" && $object->state() ne "new"
		&& $object->state() ne "mixed") {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
951
952
953
954
955
		return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
					    "Sliver is not stopped (yet)");
	    }
	}
	elsif ($action eq "stop") {
956
	    if ($object->state() ne "started" && $object->state() ne "mixed") {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
957
958
959
960
961
		return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
					    "Sliver is not started (yet)");
	    }
	}
	elsif ($action eq "restart") {
962
	    if ($object->state() ne "started" && $object->state() ne "mixed") {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
963
964
965
		return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
					    "Sliver is not started (yet)");
	    }
966
	}
967
968
969
970
971
972
	elsif ($action eq "reload") {
	    if ($object->state() ne "started" && $object->state() ne "stopped"){
		return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
				    "Sliver is not started or stopped (yet)");
	    }
	}
973
974
975
976
977
	return 0;
    };
    my $PerformAction = sub {
	my ($object, $action) = @_;

978
979
	my $exitval = 0;

980
	if ($action eq "start") {
981
	    $exitval = $object->Start($API_VERSION);
982
	}
983
	elsif ($action eq "stop") {
984
	    $exitval = $object->Stop($API_VERSION);
985
986
	}
	elsif ($action eq "restart") {
987
988
989
990
	    $exitval = $object->Restart($API_VERSION);
	}
	elsif ($action eq "reload") {
	    $exitval = $object->Reload($API_VERSION);
991
	}
992
993
994
995
	return GeniResponse->Create(GENIRESPONSE_ERROR, 
				    "Could not $action sliver")
	    if ($exitval);
	
996
997
998
	return 0;
    };

999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
    my $user = GeniCM::CreateUserFromCertificate($credential);
    return $user
	if (GeniResponse::IsResponse($user));

    my $realuser = GeniCM::FlipToUser($slice, $user);
    if (! (defined($realuser) && $realuser)) {
	print STDERR "Error flipping to real user\n";
	return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
				    "FlipToUser Error");
    }

1010
1011
1012
1013
1014
    if (defined($slice_urn)) {
	$response = &$CheckState($aggregate, $action);
	goto bad
	    if (GeniResponse::IsResponse($response));
	    
1015
	if ($action eq "start" || $action eq "restart" || $action eq "reload"){
1016
1017
	    if (defined($manifest) &&
		$aggregate->ProcessManifest($manifest)) {
1018
1019
1020
1021
1022
		$response = GeniResponse->Create(GENIRESPONSE_ERROR,
						 undef,
						 "Error processing manifest");
		goto bad;
	    }
1023
1024
1025
1026
	    #
	    # At this point we want to return and let the startsliver proceed
	    # in the background
	    #
1027
	    my $mypid = main::WrapperFork();
1028
1029
1030
	    if ($mypid) {
		return GeniResponse->Create(GENIRESPONSE_SUCCESS);
	    }
1031
1032
	    # Remember our pid in case callee wrapper forks again.
	    $isasync = $PID;
1033
	}
1034
1035
1036
1037
	$response = &$PerformAction($aggregate, $action);
	goto bad
	    if (GeniResponse::IsResponse($response));

1038
1039
1040
	if (!$isasync || $isasync == $PID) {
	    $slice->UnLock();
	}
1041
1042
	return ($isasync ? GENIRESPONSE_SUCCESS :
		GeniResponse->Create(GENIRESPONSE_SUCCESS));
1043
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1044
    else {
1045
	my @slivers = ();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1046

1047
1048
1049
1050
1051
	#
	# Sanity check all arguments before doing anything.
	#
	foreach my $urn (@{ $sliver_urns }) {
	    my $sliver = GeniSliver->Lookup($urn);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1052
1053
1054
1055
1056
1057
	    if (!defined($sliver)) {
		$response = GeniResponse->Create(GENIRESPONSE_SEARCHFAILED,
						 undef,
						 "Nothing here by that name");
		goto bad;
	    }
1058
1059
1060
1061
1062
1063
1064
1065
	    
	    $response = &$CheckState($sliver, $action);
	    goto bad
		if (GeniResponse::IsResponse($response));

	    push(@slivers, $sliver);
	}
	foreach my $sliver (@slivers) {
1066
1067
1068
1069
1070
1071
1072
1073
	    if ($action eq "start" && defined($manifest)) {
		if ($sliver->ProcessManifest($manifest)) {
		    $response = GeniResponse->Create(GENIRESPONSE_ERROR,
				     undef,
				     "Error processing manifest for $sliver");
		    goto bad;
		}
	    }
1074
1075
1076
1077
1078
1079
	    $response = &$PerformAction($sliver, $action);
	    goto bad
		if (GeniResponse::IsResponse($response));
	}
	$slice->UnLock();
	return GeniResponse->Create(GENIRESPONSE_SUCCESS);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1080
    }
1081
1082
  bad:
    $slice->UnLock();
1083
    return ($isasync ? $response->{'code'} : $response);
1084
1085
1086
1087
1088
1089
1090
}

#
# Get sliver status
#
sub SliverStatus($)
{
1091
    my ($argref)     = @_;
1092
1093
1094
1095
1096
1097
1098
1099
1100
    my $slice_urn    = $argref->{'slice_urn'};
    my $credentials  = $argref->{'credentials'};

    if (! (defined($credentials) && defined($slice_urn))) {
	return GeniResponse->MalformedArgsResponse("Missing arguments");
    }
    if (! GeniHRN::IsValid($slice_urn)) {
	return GeniResponse->MalformedArgsResponse("Bad characters in URN");
    }
1101
    my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
    return $credential
	if (GeniResponse::IsResponse($credential));

    $credential->HasPrivilege( "pi" ) or
	$credential->HasPrivilege( "info" ) or
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN, undef,
				    "Insufficient privilege");

    #
    # For now, only allow top level aggregate or the slice
    #
    my ($slice, $aggregate) = Credential2SliceAggregate($credential);
1114
1115
1116
    return $slice
	if (defined($slice) && GeniResponse::IsResponse($slice));

1117
    if (! (defined($slice) && defined($aggregate))) {
1118
	return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
1119
1120
				    "No slice or aggregate here");
    }
1121
    main::AddLogfileMetaDataFromSlice($slice);
1122
    
1123
1124
1125
    if ($slice_urn ne $slice->urn()) {
	return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
				    "Credential does not match the URN");
1126
1127
1128
1129
    }
    if ($slice->Lock() != 0) {
	return GeniResponse->BusyResponse();
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1130
1131
1132
1133
1134
    if ($aggregate->ComputeState()) {
	print STDERR "SliverStatus: Could not compute state for $aggregate\n";
	$slice->UnLock();
	return GeniResponse->Create(GENIRESPONSE_ERROR);
    }
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145

    #
    # Grab all the slivers for this slice, and then
    # look for just the nodes.
    #
    my @slivers    = ();
    if ($aggregate->SliverList(\@slivers) != 0) {
	print STDERR "SliverStatus: Could not get slivers for $aggregate\n";
	$slice->UnLock();
	return GeniResponse->Create(GENIRESPONSE_ERROR);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1146
1147
1148

    my $blob = {
	"state"   => $aggregate->state(),
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1149
	"status"  => $aggregate->status(),
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1150
1151
	"details" => {},
    };
1152
1153
1154
    $blob->{'public_url'} =
	"$TBBASE/showslicepub.php?publicid=" .	$slice->publicid()
	if (defined($slice->publicid()));
Leigh B Stoller's avatar
Leigh B Stoller committed
1155
    $blob->{"error"} = $aggregate->ErrorLog() || "";
1156
    
1157
    foreach my $sliver (@slivers) {
1158
1159
1160
1161
1162
1163
1164
1165
	if ($sliver->isa("GeniAggregate")) {
	    next
		if (! (ref($sliver) eq "GeniAggregate::Link" ||
		       ref($sliver) eq "GeniAggregate::Tunnel"));
	}
	elsif ($sliver->resource_type() ne "Node") {
	    next;
	}