StateWait.pm 6.42 KB
Newer Older
1
2
3
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2003-2004 University of Utah and the Flux Group.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# All rights reserved.
#

#
# StateWait.pm
#
# A library for waiting for nodes to reach certain states.
#
# $rv = initStateWait(\@states, @nodes);
#
# Call this first. Make sure to call it _before_ performing any
# action that may trigger one or more of the states you're
# expecting to see. Each node must see the list of states in the
# proper order, ie if @states=(stateA, stateB), it will not
# complete until the first time stateB is seen after stateA has
# been seen. Returns 0 on success.
#
22
# $rv = waitForState(\@finished, \@failed[, $timeout);
23
#
24
25
26
# Do the actual waiting. Blocks until each node has either reached the
# desired state(s) or failed, or timeout is reached (if given and
# non-zero). Returns lists of nodes.
27
#
28
29
30
31
# $rv = cancelWait($node);
#
# "Nevermind..." for state waiting. Returns 0 on success.
#
32
33
34
35
36
37
38
39
40
41
# $rv = endStateWait();
#
# Stop listening for states. Call this soon after waitForState.
# This must be called before calling initStateWait again.
#

package StateWait;

use Exporter;
@ISA = "Exporter";
42
@EXPORT = qw ( initStateWait waitForState cancelWait endStateWait );
43
44

# Any 'use' or 'require' stuff must come here, after 'package'
45
46
# Note that whatever script is using us has already done a "use lib" to
# be able to use us. So we don't need to do one, or be configged.
47
use event;
48
49
use libtestbed;
use libdb;
50

51
52
53
54
# Set these to nothing here, so they can be set before calling init
# by the caller
# Don't my them, or it won't work!
$server = "";
55
$port   = TB_BOSSEVENTPORT();
56
57
$URL    = "";
$debug  = 0;
58

59
60
$handle=0;
$tuple=0;
61

62
63
64
65
my @done;
my @failures;
my $nodecount;
my %remain;
66
67
68
69
70
71

#
# Exported Sub-Routines / Functions
#

sub initStateWait( $@ ) {
72
    my $states = shift;
73
    my @nodes = @_;
74
75
76
77
    @done=();
    @failures=();
    $nodecount=0;
    %remain=();
78
    $nodecount = scalar(@nodes);
79
    # states is an arrayref
80
81
82
83
    if ($debug) {
	print "initStateWait: states=(".join(",",@$states).
	  ") nodes=(".join(",",@nodes).")\n";
    }
84
    if ($server eq "") { $server = "localhost"; }
85
86
87
88
89
90
    if ($URL eq "") { $URL = "elvin://$server"; }
    if ($port ne "") { $URL .= ":$port"; }

    # Do the subscription for the right stuff, including all the
    # states for all the nodes, and the failure event for all nodes

91
92
93
94
95
96
97
    if ($handle==0) {
	if ($debug) { print "Getting handle for $URL - "; }
	$handle = event_register($URL,0);
	if ($debug) { print "returned - "; }
	if (!$handle) { die "Unable to register with event system\n"; }
	if ($debug) { print "Success: $handle\n"; }
    }
98

99
    if ($debug) { print "Getting tuple - "; }
100
101
102
    $tuple = address_tuple_alloc();
    if (!$tuple) { die "Could not allocate an address tuple\n"; }

103
104
    %$tuple = ( objtype   => join(",",TBDB_TBEVENT_NODESTATE,
				  TBDB_TBEVENT_TBFAILED),
105
106
107
		eventtype => join(",",@$states, TBDB_COMMAND_REBOOT  ) );
		#eventtype => join(",",@$states, TBDB_COMMAND_REBOOT  ),
	        #objname   => join(",",@nodes) );
108

109
110
    if ($debug) { print "Success: $tuple\n"; }
    if ($debug > 0) {
111
112
113
114
	print "tuple = ('".join("', '",keys(%$tuple))."') => ('".
	  join("', '",values(%$tuple))."')\n";
    }

115
    if ($debug) { print "Subscribing - "; }
116
117
118
    if (!event_subscribe($handle,\&doEvent,$tuple)) {
        die "Could not subscribe to events\n";
    }
119
    if ($debug) { print "Success.\n"; }
120
121
122
123
124
125
126
127
128
129

    foreach $n (@nodes) {
	my @l = @$states;
	$remain{$n} = \@l;
    }

    if ($debug) {
	foreach $n (@nodes) { print "$n==>(".join(",",@{$remain{$n}}).")\n"; }
    }

130
131
132
    return 0;
}

133
134
135
136
137
138
139
140
141
142
143
sub doEvent( $$$ ) {
    my ($handle,$event) = @_;

    my $time      = time();
    my $site      = event_notification_get_site($handle, $event);
    my $expt      = event_notification_get_expt($handle, $event);
    my $group     = event_notification_get_group($handle, $event);
    my $host      = event_notification_get_host($handle, $event);
    my $objtype   = event_notification_get_objtype($handle, $event);
    my $objname   = event_notification_get_objname($handle, $event);
    my $eventtype = event_notification_get_eventtype($handle, $event);
144
    if ($debug ) {
145
146
147
148
	print "Event: $time $site $expt $group $host $objtype $objname " .
	  "$eventtype\n";
    }
    my $n = $objname;
149
150
151
152
153
154
    if (defined($remain{$n}) && $objtype eq TBDB_TBEVENT_TBFAILED) {
	# It is a failed boot... add it to the failures list.
	if ($debug) { print "Got $eventtype failure for $n... Aborting!\n"; }
	push(@failures,$n);
	delete($remain{$n});
    }
Mac Newbold's avatar
Mac Newbold committed
155
    if (defined($remain{$n}) && @{$remain{$n}}[0] eq $eventtype) {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
	# this is the next state we were waiting for
	if ($debug) { print "Got $eventtype for $n\n" };
	shift(@{$remain{$n}});
	if ($debug) { print "$n==>(".join(",",@{$remain{$n}}).")\n"; }
	if (scalar(@{$remain{$n}}) == 0) {
	    if ($debug) { print "Done with $n!\n"; }
	    push(@done,$n);
	    delete($remain{$n});
	}
    }
}

sub waitForState( $$;$ ) {
    my ($finished,$failed,$timeout) = @_;
    if (!defined($timeout)) { $timeout=0; }
    my $now = ($timeout > 0 ? time() : 0);
    my $deadline = ($timeout > 0 ? $now + $timeout : 1);
    my $total = scalar(@done) + scalar(@failures);
174
    while ($total < $nodecount && $now < $deadline) {
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
	if ($timeout > 0) {
	    if ($debug) {
		print "waitForState: done=(".join(",",@done)."), ".
		  "failures=(".join(",",@failures).")\n";
		print "Polling for ".($deadline-$now)." seconds\n";
	    }
	    event_poll_blocking($handle,($deadline-$now)*1000);
	    $now = time();
	} else {
	    event_poll_blocking($handle,0);
	}
	$total = scalar(@done) + scalar(@failures);
    }
    # finished and failed are arrayrefs
    @$finished=@done;
    @$failed=@failures;
    if ($debug) {
	print "waitForState: finished=(".join(",",@$finished)."), ".
193
194
195
	  "failed=(".join(",",@$failed)."), ".
	    "notdone=(".join(",",keys %remain).")\n";
	print "  Wait for state returning now...\n"
196
    }
197
198
199
    return 0;
}

200
201
202
203
204
205
206
207
208
209
210
211
sub cancelWait( $ ) {
    # Important thing to remember: This will never get called while
    # we're in the middle of a waitForState call, so we just need to
    # make sure things are consistent next time they call it.
    my $node = @_;
    if (!defined($remain{$node})) {
	return 1;
    }
    delete $remain{$node};
    $nodecount--;
    return 0;
}
212

213
214
sub endStateWait() {
    %remain = ();
215
    $tuple = address_tuple_free($tuple);
216
    if ($debug) { print "endStateWait\n"; }
217
218
219
220
221
222
    return 0;
}

# Always end a package successfully!
1;

223
END {
224
225
    if ($handle!=0 && event_unregister($handle) == 0) {
        warn "Unable to unregister with event system\n";
226
    }
227
    1;
228
}