GeniAggregate.pm.in 77.6 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
#!/usr/bin/perl -wT
#
3
# Copyright (c) 2008-2016 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 B. Stoller's avatar
Leigh B. Stoller committed
29
30
31
32
#
package GeniAggregate;

#
33
# Some simple aggregate stuff.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
34
35
36
#
use strict;
use Exporter;
37
use vars qw(@ISA @EXPORT @EXPORT_OK $ACTION_FLAGS_SYNCVLANS);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
38

39
40
@ISA    = qw(Exporter);
@EXPORT = qw();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
41
42
43
44
45
46

# Must come after package declaration!
use GeniDB;
use GeniCredential;
use GeniCertificate;
use GeniSliver;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
47
use GeniSlice;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
48
use GeniRegistry;
49
use GeniUtil;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
50
use GeniUser;
51
use GeniComponent;
52
use GeniHRN;
53
use GeniEvent;
54
use GeniXML;
55
use emutil;
56
use EmulabConstants;
57
use Node;
58
59
use Logfile;
use libtestbed;
60
use Data::Dumper;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
61
62
63
use English;
use overload ('""' => 'Stringify');
use XML::Simple;
64
65
66
use POSIX qw(strftime);
use Time::Local;
use Date::Parse;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
67
68
69
70
71
72
73
74

# Configure variables
my $TB		   = "@prefix@";
my $TBOPS          = "@TBOPSEMAIL@";
my $TBAPPROVAL     = "@TBAPPROVALEMAIL@";
my $TBAUDIT   	   = "@TBAUDITEMAIL@";
my $BOSSNODE       = "@BOSSNODE@";
my $OURDOMAIN      = "@OURDOMAIN@";
75
76
my $MAINSITE       = @TBMAINSITE@;
my $ELABINELAB     = @ELABINELAB@;
77
my $PGENIDOMAIN    = "@PROTOGENI_DOMAIN@";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
78
79
my $SIGNCRED	   = "$TB/sbin/signgenicred";
my $VERIFYCRED	   = "$TB/sbin/verifygenicred";
80
my $NODEREBOOT	   = "$TB/bin/node_reboot";
81
my $EVENTSYS	   = "$TB/bin/eventsys_control";
82
my $VNODESETUP     = "$TB/sbin/vnode_setup";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
83
my $POWER          = "$TB/bin/power";
84
my $OSLOAD         = "$TB/bin/os_load";
85
my $SNMPIT         = "$TB/bin/snmpit_test";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
86
87
88
my $NAMEDSETUP     = "$TB/sbin/named_setup";
my $EXPORTS_SETUP  = "$TB/sbin/exports_setup";
my $GENTOPOFILE    = "$TB/libexec/gentopofile";
89
my $GENELISTS      = "$TB/sbin/genelists";
90
my $IMAGE_SETUP    = "$TB/sbin/image_setup";
Leigh B Stoller's avatar
Leigh B Stoller committed
91
my $ARPLOCKDOWN    = "$TB/sbin/arplockdown";
92
my $TARFILES_SETUP = "$TB/bin/tarfiles_setup";
93
my $ELAB_SETUP     = "$TB/sbin/elabinelab";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
94
95
96

# Cache of instances to avoid regenerating them.
my %aggregates     = ();
97
BEGIN { use GeniUtil; GeniUtil::AddCache(\%aggregates); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
98

99
100
101
102
103
# Action() flags.
$ACTION_FLAGS_SYNCVLANS	= 0x01;

@EXPORT_OK = qw($ACTION_FLAGS_SYNCVLANS);

Leigh B. Stoller's avatar
Leigh B. Stoller committed
104
#
105
# Lookup by URN, idx, or uuid.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
106
107
108
109
110
111
112
#
sub Lookup($$)
{
    my ($class, $token) = @_;
    my $query_result;
    my $idx;

113
114
115
    if (GeniHRN::IsValid($token)) {
	my ($authority, $type, $id) = GeniHRN::Parse($token);
	return undef if $type ne "sliver";
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

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

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

	    ($idx) = $query_result->fetchrow_array();
	}
134
135
    }
    elsif ($token =~ /^\d+$/) {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
	$idx = $token;
    }
    elsif ($token =~ /^\w+\-\w+\-\w+\-\w+\-\w+$/) {
	$query_result =
	    DBQueryWarn("select idx from geni_aggregates ".
			"where uuid='$token'");
	    return undef
		if (! $query_result || !$query_result->numrows);

	    ($idx) = $query_result->fetchrow_array();
    }
    else {
	return undef;
    }
    # Look in cache first
    return $aggregates{"$idx"}
        if (exists($aggregates{"$idx"}));

    $query_result =
	DBQueryWarn("select * from geni_aggregates where idx='$idx'");
    
    return undef
	if (!$query_result || !$query_result->numrows);

160
161
162
163
164
165
    my $self               = {};
    $self->{'AGGREGATE'}   = $query_result->fetchrow_hashref();
    $self->{'CREDENTIAL'}  = undef;
    $self->{'SLICE'}       = undef;
    $self->{'PARENT'}      = undef;
    $self->{'CERTIFICATE'} = undef;
166
167
168

    # Bless into sub package if called for.
    my $type = $self->{'AGGREGATE'}->{'type'};
169
    if (defined($type) && $type ne "" && $type ne "Aggregate") {
170
171
172
173
174
	bless($self, $class . "::" . $type);
    }
    else {
	bless($self, $class);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
175
176
177
178
179
180
181

    #
    # Grab the certificate, since we will probably want it.
    #
    my $uuid = $self->{'AGGREGATE'}->{'uuid'};
    my $certificate = GeniCertificate->Lookup($uuid);
    if (!defined($certificate)) {
182
183
184
185
186
187
188
189
	if ($type eq "Aggregate") {
	    print STDERR
		"Could not find certificate for aggregate $idx ($uuid)\n";
	    return undef;
	}
    }
    else {
	$self->{'CERTIFICATE'} = $certificate;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
    }
    
    # Add to cache. 
    $aggregates{$self->{'AGGREGATE'}->{'idx'}} = $self;
    
    return $self;
}

#
# Stringify for output.
#
sub Stringify($)
{
    my ($self) = @_;
    
    my $uuid = $self->uuid();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
206
    my $hrn  = $self->hrn();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
207
208
    my $idx  = $self->idx();

Leigh B. Stoller's avatar
Leigh B. Stoller committed
209
    return "[GeniAggregate: $hrn, IDX: $idx]";
Leigh B. Stoller's avatar
Leigh B. Stoller committed
210
211
}

Leigh B Stoller's avatar
Leigh B Stoller committed
212
213
214
215
216
217
218
219
220
221
#
# Flush from our little cache, as for the expire daemon.
#
sub Flush($)
{
    my ($self) = @_;

    delete($aggregates{$self->idx()});
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
222
223
224
225
226
#
# Create a Geni aggregate in the DB. This happens on the server side only
# for now. The client side does not actually know its an aggregate, at
# least not yet.
#
227
sub Create($$$$$$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
228
{
229
    my ($class, $slice, $owner, $aggregate_type, $hrn, $nickname) = @_;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
230
    my @insert_data = ();
231
    my $certificate;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
232
233

    # Every aggregate gets a new unique index.
234
    my $idx = TBGetUniqueIndex('next_sliver', 1);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
235
236

    # Create a cert pair, which gives us a new uuid.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
237
    my $urn = GeniHRN::Generate( "@OURDOMAIN@", "sliver", $idx );
238
239
240
241
242
243
244
245
246
247
248

    if ($aggregate_type eq "Aggregate") {
	$certificate = GeniCertificate->Create({'urn'   => $urn,
						'hrn'   => $hrn,
						'email' => $TBOPS});

	if (!defined($certificate)) {
	    print STDERR "GeniAggregate::Create: ".
		"Could not generate new certificate and UUID for $hrn\n";
	    return undef;
	}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
249
    }
250
251
    my $uuid        = (defined($certificate) ?
		       $certificate->uuid() : GeniUtil::NewUUID());
252
253
    my $slice_uuid  = $slice->uuid();
    my $owner_uuid  = $owner->uuid();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
254
255
256
    $aggregate_type = "Aggregate"
    	if (! defined($aggregate_type));

Leigh B. Stoller's avatar
Leigh B. Stoller committed
257
258
259
    # Now tack on other stuff we need.
    push(@insert_data, "created=now()");
    push(@insert_data, "idx='$idx'");
260
    push(@insert_data, "hrn=" . DBQuoteSpecial($hrn));
261
    push(@insert_data, "nickname=" . DBQuoteSpecial($nickname));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
262
263
264
    push(@insert_data, "uuid='$uuid'");
    push(@insert_data, "creator_uuid='$owner_uuid'");
    push(@insert_data, "slice_uuid='$slice_uuid'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
265
    push(@insert_data, "type='$aggregate_type'");
266
267
    # Start out new aggregates, as new.
    push(@insert_data, "state='new'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
268
269
270
271

    # Insert into DB.
    if (!DBQueryWarn("insert into geni_aggregates set " .
		     join(",", @insert_data))) {
272
273
	$certificate->Delete()
	    if (defined($certificate));
Leigh B. Stoller's avatar
Leigh B. Stoller committed
274
275
	return undef;
    }
276
277
278
279
280
    my $aggregate = GeniAggregate->Lookup($idx);
    return undef
	if (!defined($aggregate));

    return $aggregate;
Leigh B. Stoller's avatar
Leigh B. Stoller committed
281
282
283
284
285
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'AGGREGATE'}->{$_[1]}); }
sub idx($)		{ return field($_[0], "idx"); }
sub uuid($)		{ return field($_[0], "uuid"); }
286
sub nickname($)		{ return field($_[0], "nickname"); }
287
sub resource_id($)      { return $_[0]->nickname(); }
288
sub type($)		{ return field($_[0], "type"); }
Leigh B. Stoller's avatar
Leigh B. 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 B. Stoller's avatar
Leigh B. Stoller committed
292
sub registered($)	{ return field($_[0], "registered"); }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
293
sub credential_idx($)	{ return field($_[0], "credential_idx"); }
294
sub aggregate_idx($)	{ return field($_[0], "aggregate_idx"); }
295
296
sub speaksfor_uuid($)	{ return field($_[0], "speaksfor_uuid"); }
sub speaksfor_urn($)	{ return field($_[0], "speaksfor_urn"); }
297
sub status($)		{ return field($_[0], "status"); }
298
sub state($)		{ return field($_[0], "state"); }
299
sub boot_failure($)     { return field($_[0], "boot_failure"); }
300
sub ErrorLog($)		{ return field($_[0], "errorlog"); }
301
302
303
304
305
306
307
308
309
sub cert($)		{ return GetCertificate($_[0])->cert(); }
# Watch for aggregates that no longer get a certificate.
sub GetCertificate($)
{
    if (!defined($_[0]->{'CERTIFICATE'})) {
	print STDERR "*** No certificate for aggregate: " . $_[0] . "\n";
    }
    return $_[0]->{'CERTIFICATE'};
}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
310

311
312
313
# An alias so that slivers look like aggregates.
sub resource_type($)	{ return field($_[0], "type"); }

314
315
316
317
318
319
320
321
322
323
324
# A place to stash a temporary rspec.
sub rspec($;$)
{
    my ($self, $rspec) = @_;

    if (defined($rspec)) {
	$self->{'RSPEC'} = $rspec;
    }
    return $self->{'RSPEC'};
}

325
326
327
328
329
330
331
# Return the URN.
sub urn($)
{
    my ($self) = @_;

    return GeniHRN::Generate("@OURDOMAIN@", "sliver", $self->idx());
}
332
333
# Name compat.
sub sliver_urn($)	{ return $_[0]->urn(); }
334

335
336
337
338
#
# Destroy all the slivers in the aggregate, and then the aggregate if there
# is nothing in it. Leave it around if something goes wrong.
#
339
sub Delete($$)
340
{
341
    my ($self, $purge) = @_;
342
343
344
345
346
347
348
349
350
351
352
353
    my $broken = 0;

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

    my @slivers = ();
    if ($self->SliverList(\@slivers) != 0) {
	print STDERR "Could not get sliver list for $self\n";
	return -1;
    }
    foreach my $sliver (@slivers) {
	if ($sliver->status() eq "broken") {
354
	    print STDERR "Could not delete 'broken' $sliver from $self\n";
355
	    $broken++;
356
	    last;
357
	}
358
	if ($sliver->Delete($purge) != 0) {
359
360
361
	    print STDERR "Could not delete $sliver from $self\n";
	    $sliver->SetStatus("broken");
	    $broken++;
362
	    last;
363
364
365
366
367
	}
    }
    return -1
	if ($broken);
    
368
369
370
371
    if (GeniUsage->DestroyAggregate($self, $purge)) {
	print STDERR "GeniAggregate::Delete: ".
	    "GeniUsage->DestroyAggregate($self) failed\n";
    }
372
373
374
375
376
377
378
379
380
381
    my $idx  = $self->idx();
    my $uuid = $self->uuid();

    DBQueryWarn("delete from geni_credentials where this_uuid='$uuid'")
	or return -1;
    DBQueryWarn("delete from geni_certificates where uuid='$uuid'")
	or return -1;
    DBQueryWarn("delete from geni_aggregates where idx='$idx'")
	or return -1;
    
382
383
384
    # Delete from cache. 
    delete($aggregates{$idx});

385
386
387
    return 0;
}

388
389
390
391
392
393
394
#
# Cons up an hrn.
#
sub hrn($)
{
    my ($self) = @_;

395
396
397
398
399
    my $hrn = field($self, "hrn");

    if (defined($hrn) && $hrn ne "") {
	return $hrn;
    }
400
    return "${PGENIDOMAIN}.aggregate_" . $self->idx();
401
402
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#
# Look up toplevel aggregate for a locally instantiated slice. 
#
sub SliceAggregate($$)
{
    my ($class, $slice) = @_;

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

    my $query_result =
	DBQueryWarn("select idx from geni_aggregates ".
		    "where slice_uuid='$slice_uuid' and type='Aggregate'");
    return undef
	if (!$query_result);
    return undef
	if ($query_result->numrows != 1);

    my ($idx) = $query_result->fetchrow_array();
    my $aggregate = GeniAggregate->Lookup($idx);
    return undef
	if (!defined($aggregate));

    return $aggregate;
}

429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
#
# Look up a list of aggregates for a locally instantiated slice. 
# Used by the CM.
#
sub SliceAggregates($$$)
{
    my ($class, $slice, $pref) = @_;

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

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

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

Leigh B. Stoller's avatar
Leigh B. Stoller committed
456
457
458
459
460
461
462
463
464
465
466
#
# List of slivers for this aggregate.
#
sub SliverList($$)
{
    my ($self, $pref) = @_;
    my @result = ();
    
    return -1
	if (! (ref($self) && ref($pref)));

467
468
    my $idx  = $self->idx();
    my $uuid = $self->uuid();
Leigh B. Stoller's avatar
Leigh B. Stoller committed
469
    my $query_result =
470
471
	DBQueryWarn("select idx from geni_slivers ".
		    "where aggregate_uuid='$uuid'");
Leigh B. Stoller's avatar
Leigh B. Stoller committed
472
473
474
475
476
477
478
479
480
481
482
    return -1
	if (!$query_result);

    while (my ($sliver_idx) = $query_result->fetchrow_array()) {
	my $sliver = GeniSliver->Lookup($sliver_idx);
	if (!defined($sliver)) {
	    print STDERR "Could not find sliver object for $sliver_idx\n";
	    return -1;
	}
	push(@result, $sliver);
    }
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501

    #
    # And any aggregates that are children.
    #
    $query_result =
	DBQueryWarn("select idx from geni_aggregates ".
		    "where aggregate_idx='$idx'");
    return -1
	if (!$query_result);

    while (my ($aggregate_idx) = $query_result->fetchrow_array()) {
	my $aggregate = GeniAggregate->Lookup($aggregate_idx);
	if (!defined($aggregate_idx)) {
	    print STDERR
		"Could not find aggregate object for $aggregate_idx\n";
	    return -1;
	}
	push(@result, $aggregate);
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
502
503
504
505
506
    @$pref = @result;
    return 0;
    
}

507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
#
# Look for any slivers with provided state.
#
sub CheckSliverStates($$)
{
    my ($self, $state) = @_;

    my @slivers = ();
    if ($self->SliverList(\@slivers) != 0) {
	print STDERR "Could not get sliver list for $self\n";
	return -1;
    }
    foreach my $sliver (@slivers) {
	next
	    if (ref($sliver) ne "GeniSliver::Node");

	return 1
	    if ($sliver->state() eq $state);
    }
    return 0;
}

529
530
531
532
533
534
535
536
537
538
#
# Set the aggregate for an aggregate.
#
sub SetAggregate($$)
{
    my ($self, $aggregate) = @_;

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

539
540
541
    my $idx      = $self->idx();
    my $agg_idx  = $aggregate->idx();
    my $agg_uuid = $aggregate->uuid();
542
543
544
545
546
547
548
549
550
551
552
553

    return -1
	if (!DBQueryWarn("update geni_aggregates set ".
			 "  aggregate_idx='$agg_idx' ".
			 "where idx='$idx'"));
    
    $self->{'AGGREGATE'}->{'aggregate_idx'} = $agg_idx;
    $self->{'PARENT'} = $aggregate;
    return 0;
}

#
554
# Get the aggregate for an aggregate.
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
#
sub GetAggregate($)
{
    my ($self) = @_;

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

    return $self->{'PARENT'} if (defined($self->{'PARENT'}));
    return undef
	if (!defined($self->aggregate_idx()));

    my $aggregate = GeniAggregate->Lookup($self->aggregate_idx());
    if (!defined($aggregate)) {
	print STDERR "Could not get aggregate object associated with $self\n";
	return undef;
    }
    $self->{'PARENT'} = $aggregate;
    return $aggregate;
}

576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
#
# Is object in the aggregate.
#
sub IsMember($$)
{
    my ($self, $object) = @_;

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

    my $aggregate = $object->GetAggregate();
    return 0
	if (!$aggregate);
    return -1
	if ($self->idx() != $aggregate->idx());
    return 1;
}

594
#
595
596
# Set the state and status for the aggregate. The goal here is to send
# just a single event when both state/status are being updated.
597
#
598
sub SetStateStatus($$$)
599
{
600
    my ($self, $state, $status) = @_;
601
602
603
604
605

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

    my $idx = $self->idx();
606
607
608
609
610
611

    my $query_result =
	DBQueryWarn("update geni_aggregates set ".
		    "  status='$status',state='$state' ".
		    "where idx='$idx' and ".
		    "      (status!='$status' or state!='$state')");
612
    return -1
613
	if (!defined($query_result));
614
    
615
    $self->{'AGGREGATE'}->{'state'}  = $state;
616
    $self->{'AGGREGATE'}->{'status'} = $status;
617
618
619
620
621

    # Any change, send the event.
    $self->SendStatusEvent()
	if ($self->type() eq "Aggregate" && $query_result->affectedrows);

622
623
624
    return 0;
}

625
626
627
628
629
630
631
632
633
634
#
# Set the status for the aggregate.
#
sub SetStatus($$)
{
    my ($self, $status) = @_;

    return $self->SetStateStatus($self->state(), $status);
}

635
636
637
638
639
640
641
#
# Set the state for the aggregate
#
sub SetState($$)
{
    my ($self, $state) = @_;

642
643
    return $self->SetStateStatus($state, $self->status());
}
644

645
646
647
648
649
sub SendStatusEvent($)
{
    my ($self) = @_;

    my $slice = $self->GetSlice();
650
    return -1
651
	if (!defined($slice));
652
    
653
654
655
656
657
658
659
660
661
    my $blob = {
	"state"   => $self->state(),
	"status"  => $self->status(),
	"utc"     => time(),
    };
    GeniEvent->SendEvent({"type"    => "SLICESTATUS",
			  "slice"   => $slice->urn(),
			  "urn"     => $slice->urn(),
			  "details" => $blob});
662
663
664
    return 0;
}

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
#
# 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_aggregates set ".
			 "  errorlog=$safe_log ".
			 "where idx='$idx'"));
    
    $self->{'AGGREGATE'}->{'errorlog'} = $log;
    return 0;
}

687
688
sub SetBootFailure($)
{
689
690
    my ($self) = @_;

691
692
693
694
695
696
697
698
699
700
701
702
703
704
    return undef
	if (! ref($self));

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

    return -1
	if (!DBQueryWarn("update geni_aggregates set ".
			 "  boot_failure='1' ".
			 "where idx='$idx'"));
    return 0;
}

sub ClearBootFailure($)
{
705
706
    my ($self) = @_;

707
708
709
710
711
712
713
714
715
716
717
718
    return undef
	if (! ref($self));

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

    return -1
	if (!DBQueryWarn("update geni_aggregates set ".
			 "  boot_failure='0' ".
			 "where idx='$idx'"));
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
#
# Set the registered datetime for the aggregate
#
sub SetRegistered($$)
{
    my ($self, $yesno) = @_;

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

    my $idx = $self->idx();
    my $val = ($yesno ? "now()" : "NULL");
    
    return -1
	if (!DBQueryWarn("update geni_aggregates set ".
			 "  registered=$val ".
			 "where idx='$idx'"));
    
    return 0;
}

740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
#
# Set the speaksfor stuff.
#
sub SetSpeaksFor($$)
{
    my ($self, $speaksfor) = @_;
    my $idx = $self->idx();
    my $safe_speaksfor_uuid = DBQuoteSpecial($speaksfor->owner_uuid());
    my $safe_speaksfor_urn  = DBQuoteSpecial($speaksfor->owner_urn());

    print "GeniAggregate->SetSpeaksFor($self, $speaksfor)\n";

    return -1
	if (!DBQueryWarn("update geni_aggregates set ".
			 "  speaksfor_uuid=$safe_speaksfor_uuid ".
			 "where idx='$idx'"));

    $self->{'AGGREGATE'}->{'speaksfor_urn'}  = $speaksfor->owner_urn();
    $self->{'AGGREGATE'}->{'speaksfor_uuid'} = $speaksfor->owner_uuid();

    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
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
#
# Get the slice for the aggregate.
#
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;
}

788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
#
# The expiration time for an aggregate 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();
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
#
# Get the creator for the aggregate.
#
sub GetCreator($)
{
    my ($self) = @_;

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

    if (!defined($self->creator_uuid())) {
	print STDERR "No creator associated with $self\n";
	return undef;
    }
    return GeniUser->Lookup($self->creator_uuid(), 1);
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
#
# Create a signed credential for this aggregate, issued to the provided user.
# The credential will grant all permissions for now.
#
# Should we store these credentials in the DB, recording what we hand out?
#
sub NewCredential($$)
{
    my ($self, $owner) = @_;

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

    my $credential = GeniCredential->Create($self, $owner);
    if (!defined($credential)) {
	print STDERR "Could not create credential for $self, $owner\n";
	return undef;
    }
841
    # Bump expiration to avoid race with aggregate about to expire.
842
    $credential->SetExpiration(time() + (24 * 3600));
843
	
844
845
846
    if (defined($self->nickname())) {
	$credential->AddExtension("nickname", $self->nickname());
    }
Leigh B. Stoller's avatar
Leigh B. Stoller committed
847
848
849
850
851
852
853
    if ($credential->Sign($self->GetCertificate()) != 0) {
	print STDERR "Could not sign credential for $self, $owner\n";
	return undef;
    }
    return $credential;
}

854
855
856
#
# Get the manifest for an aggregate. Returns the XML string.
#
857
sub GetManifest($$)
858
{
859
    my ($self, $asxml) = @_;
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878

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

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

    my $query_result =
	DBQueryWarn("select manifest from geni_manifests ".
		    "where slice_uuid='$slice_uuid'");
    
    if (!$query_result || !$query_result->numrows) {
	print STDERR "GetManifest: Could not locate manifest for $self\n";
	return undef;
    }
    my ($xml) = $query_result->fetchrow_array();

879
880
    my $manifest = GeniXML::Parse($xml);
    if (!defined($manifest)) {
881
882
	return undef;
    }
883

884
885
886
    #
    # Update the manifest ticket to reflect the current expiration time.
    #
887
    my $valid_date = POSIX::strftime("20%y-%m-%dT%H:%M:%SZ",
888
				     gmtime(str2time($slice->expires())));
Jonathon Duerig's avatar
Jonathon Duerig committed
889
890
891
892
893
    if (GeniXML::IsVersion0($manifest)) {
	GeniXML::SetText("valid_until", $manifest, $valid_date);
    } else {
	GeniXML::SetText("expires", $manifest, $valid_date);
    }
894

895
896
897
    return $manifest
	if (!$asxml);
    
898
    $xml = GeniXML::Serialize($manifest);
899
900
901
    return $xml;
}

902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
#
# Store a manifest back.
#
sub UpdateManifest($$)
{
    my ($self, $manifest) = @_;
    my $slice_uuid = $self->slice_uuid();
    my $manifest_string = DBQuoteSpecial(GeniXML::Serialize($manifest));

    #
    # We need the current idx. 
    #
    my $query_result =
	DBQueryWarn("select idx from geni_manifests ".
		    "where slice_uuid='$slice_uuid'");
    return -1
	if (!$query_result);

    if ($query_result->numrows) {
	my ($idx) = $query_result->fetchrow_array();
    
	DBQueryWarn("update geni_manifests set ".
		    "  manifest=$manifest_string " .
		    "where idx=$idx")
	    or return -1;
    }
    else {
	DBQueryWarn("replace into geni_manifests set ".
		    "  manifest=$manifest_string, " .
		    "  idx=NULL, slice_uuid='$slice_uuid', created=now()")
	    or return -1;
    }
    return 0;
}

Leigh B. Stoller's avatar
Leigh B. Stoller committed
937
#
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
# Process the manifest. Just hand off to the slivers.
#
sub ProcessManifest($$)
{
    my ($self, $manifest) = @_;

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

    my @slivers = ();
    if ($self->SliverList(\@slivers) != 0) {
	print STDERR "Could not get sliver list for $self\n";
	return -1;
    }

    foreach my $sliver (@slivers) {
	next
	    if (ref($sliver) ne "GeniSliver::Node");

	if ($sliver->ProcessManifest($manifest) != 0) {
	    return -1;
	}
    }
    return 0;
}

964
sub Start($$;$)
965
{
966
    my ($self, $version, $flags) = @_;
967

968
    return $self->Action($version, "start", $flags);
969
970
}

971
sub Restart($$;$)
972
{
973
    my ($self, $version, $flags) = @_;
974

975
    return $self->Action($version, "restart", $flags);
976
977
}

978
sub Reload($$;$)
979
{
980
    my ($self, $version, $flags) = @_;
981

982
    return $self->Action($version, "reload", $flags);
983
984
}

985
#
986
987
988
989
# Start/Restart/reload all the slivers in the aggregate. Start is
# special since it sorta means reboot, and the only thing we reboot
# are nodes. And, since we might have multiple vnodes on a pnode, we
# want to be efficient about it.
990
991
992
#
# XXX Is is assumed that there is a single toplevel aggregate for the
# slice, so we can get all the nodes.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
993
#
994
sub Action($$$;$)
Leigh B. Stoller's avatar
Leigh B. Stoller committed
995
{
996
    my ($self, $version, $action, $flags) = @_;
997
    $self->ClearBootFailure();
998
    my $msg = "Internal Error: ";
999
1000
    my $restart = ($action eq "restart" ? 1 : 0);
    my $reload  = ($action eq "reload" ? 1 : 0);
For faster browsing, not all history is shown. View entire blame