1 # ihm-agent.tcl --
2 #
3 # Agent for Indiva Host Manager.
4 #
5 # Copyright (c) 1996-2002 The Regents of the University of California.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # A. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 # B. Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 # C. Neither the names of the copyright holders nor the names of its
17 # contributors may be used to endorse or promote products derived from this
18 # software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #
31 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/indiva/ihm/ihm-agent.tcl,v 1.2 2002/03/13 23:39:51 weitsang Exp $
32
33 import MashSystem
34 import ServiceCreator ServiceCreator/MeGa
35 import CoordinationBus MeGa
36 import ASCP/Manager
37 import IHMScriptLoader
38
39 #-----------------------------------------------------------------------
40 # Class:
41 # IHMAgent
42 #
43 # Description:
44 # Host Manager Agent, part of the AS1 framework
45 #
46 # Changes from HM :
47 # - chaining of hm not supported.
48 # - glunix not supported
49 #
50 # Members:
51 # uniqid_ --
52 # A unique id used to create temporary filename for downloaded
53 # servlets. Initialized to 0 and is incremented by 1 everytime
54 # a temp file is created.
55 #
56 # app_ --
57 # The HM Application.
58 #
59 # cbchannel_ --
60 # Channel ID for coordination bus.
61 #
62 # hmhosts_ --
63 # A list of other hosts eligible to run hm.
64 #-----------------------------------------------------------------------
65 Class IHMAgent
66
67 IHMAgent instproc init { app } {
68
69 $self next
70
71 $self instvar app_ cbchannel_ ascp_
72
73 $self set cbchannel_ 3
74 $self set app_ $app
75 $self set uniqid_ 0
76
77 MashLog info "Start"
78
79 set as_spec [$self get_option as_spec]
80 set as_bw [$self get_option as_bw]
81
82 $self instvar ascp_
83 set ascp_ [new ASCP/Manager $as_spec $as_bw]
84 $ascp_ start
85
86 #if { [$self get_option loadBalance] != "" } {
87 # $ascp_ start
88 # $self read_hmhosts
89 # $self init_load_check
90 #} elseif { [$self get_option targetNum] != "" } {
91 # $ascp_ start
92 # $self set trgtnum_ [$self get_option targetNum]
93 # $self read_hmhosts
94 # IHMAgent instproc timeout {} { $self target_check }
95 # $self randomize yes
96 # set a [$self get_option checkFactor]
97 # $self msched [expr $a * [$self get_option checkInterval]]
98 # } else {
99 # Modified so that hm's always announce, even if there's only 1
100 #
101 # }
102 }
103
104
105 IHMAgent public announce_launch { srv_inst } {
106 $self instvar ascp_
107 MashLog info "announce_launch $srv_inst"
108 $ascp_ announce_launch $srv_inst
109 }
110
111
112 #-----------------------------------------------------------------------
113 # Method:
114 # IHMAgent target_check
115 # Description:
116 # Checks if the total number of hm meets the desired number.
117 #-----------------------------------------------------------------------
118 IHMAgent instproc target_check {} {
119 $self instvar trgtnum_ ascp_
120 set n [$ascp_ get_num_of_hms]
121 # count ourselves
122 incr n
123
124 set r [expr [random]/double(0x7fffffff)]
125 if { $n < $trgtnum_ } {
126 # set p [expr double($trgtnum_ - $n) / $trgtnum_]
127 set p [expr double($trgtnum_ - $n) / $n]
128 # p could be > 1, but this code is correct since we want
129 # p = min(1, N/n - 1)
130 if { $r < $p } {
131 $self spawn
132 }
133 } elseif { $n > $trgtnum_ } {
134 set p [expr double($n - $trgtnum_) / $n]
135 if { $r < $p } {
136 $self doexit
137 return
138 }
139 }
140 # want the max here probably...
141 # set t [$ascp_ get_timer]
142 # $self msched [$t set interval_]
143 $self msched [$self get_option checkInterval]
144 }
145
146
147 #-----------------------------------------------------------------------
148 # Method:
149 # IHMAgent doexit
150 # Description:
151 # Tell everyone we are quiting, and then quit.
152 #-----------------------------------------------------------------------
153 IHMAgent instproc doexit {} {
154 $self instvar ascp_
155 $ascp_ announce_death
156 exit 0
157 }
158
159
160 #-----------------------------------------------------------------------
161 # Method:
162 # IHMAgent destroy
163 # Description:
164 #-----------------------------------------------------------------------
165 IHMAgent instproc destroy {} {
166 $self next
167 }
168
169
170 #-----------------------------------------------------------------------
171 # Method:
172 # IHMAgent unregister
173 # Description:
174 # Remove a handler.
175 # Arguments:
176 # aspec -- "<pid>@<hostname>", this uniquely identifies a handler.
177 # msg -- Ununsed junk.
178 #-----------------------------------------------------------------------
179 IHMAgent instproc unregister { aspec msg } {
180 $self instvar handlers_
181 if ![info exists handlers_] {
182 return
183 }
184 set i 0
185 # FIXME
186 set pid [lindex [split $aspec @] 0]
187 foreach h $handlers_ {
188 if { [$h set pid_] == $pid } {
189 delete $h
190 set handlers_ [lreplace $handlers_ $i $i]
191 return
192 }
193 incr i
194 }
195 }
196
197 #
198 # Exponential launch timer
199 # F(x) = 1/(exp(lambda) - 1) * (exp(lambda/T * x) - 1)
200 #
201 # => x = T/lambda log((exp(lambda) - 1) F(x) + 1)
202 #
203 # IHMAgent proc exp_timer { lambda T } {
204 # set r [expr [random]/double(0x7fffffff)]
205 # set o [expr ($T/$lambda) * log((exp($lambda) - 1)*$r + 1)]
206 # return [expr int($o+0.5)]
207 # }
208
209
210 #-----------------------------------------------------------------------
211 # Method:
212 # IHMAgent open_cb
213 # IHMAgent close_cb
214 # Description:
215 # Open and close a coordination bus.
216 # Arguments:
217 # cb -- Coordination bus to close.
218 # handler -- Unused junk.
219 #-----------------------------------------------------------------------
220 IHMAgent instproc close_cb cb {
221 set c [$cb set channel_]
222
223 $self instvar chanmap_
224 incr chanmap_($c) -1
225 if { $chanmap_($c) <= 0 } {
226 delete $cb
227 unset chanmap_($c)
228 }
229 }
230
231 IHMAgent instproc open_cb { handler } {
232 $self instvar cbchannel_ chanmap_
233 set cb [new CoordinationBus -channel $cbchannel_]
234 set chanmap_($cbchannel_) 1
235
236 # FIXME
237 incr cbchannel_
238
239 return $cb
240 }
241
242
243 #-----------------------------------------------------------------------
244 # Method:
245 # IHMAgent read_hmhosts
246 # Description:
247 # Read a file called "hmhosts", which contains a list of hosts hm can
248 # run on. The list of hosts are stored into a list called hmhosts_.
249 #-----------------------------------------------------------------------
250 IHMAgent instproc read_hmhosts {} {
251 $self instvar hmhosts_
252
253 set path [$self get_option execPath]
254
255 set f "$path/hmhosts"
256 if { $f == "" } {
257 MashLog warn "hm: warning: no host file - disabling load_check"
258 return
259 }
260
261 set fd [open $f r]
262 if { $fd < 0 } {
263 MashLog warn "hm: problems opening $f"
264 return
265 }
266
267 while { [gets $fd line] > 0 } {
268 if { [intoa [lookup_host_addr $line]] != [localaddr] } {
269 lappend hmhosts_ $line
270 }
271 }
272 close $fd
273 }
274
275
276 #-----------------------------------------------------------------------
277 # Method:
278 # IHMAgent init_load_check
279 # Description:
280 # Create a time to call method load_check{ } periodically.
281 #-----------------------------------------------------------------------
282 IHMAgent instproc init_load_check {} {
283
284 set low_ 0
285 set high_ 0
286
287 set t [$self get_option checkInterval]
288 after $t "$self load_check"
289 }
290
291
292 #-----------------------------------------------------------------------
293 # Method:
294 # IHMAgent load_check
295 # Description:
296 # Checks the current load on this machine. If the load is too high,
297 # Sheds some load by randomly killing of servents (see shed_load{}).
298 # If the load is too low, kill self. (Huh?).
299 #-----------------------------------------------------------------------
300 IHMAgent instproc load_check {} {
301 $self instvar app_ low_ high_ ascp_
302
303 set load [MashSystem get_load]
304 set nsamples [$self get_option loadSamples]
305 if { $load > [$self get_option highLoad] } {
306 incr high_
307 if { $high_ >= $nsamples } {
308 if { [$self spawn] != 0 } {
309 $self shed_load
310 }
311 }
312 } elseif { $load < [$self get_option lowLoad] } {
313 incr low_
314 if { $low_ >= $nsamples } {
315 # Don't die if the number of hm's is at the minimum.
316 set minhm [$self get_option minHmNum]
317 set hmnum [$ascp_ get_num_of_hms]
318 # count ourselves
319 incr hmnum
320 if { $hmnum > $minhm } {
321 $self die
322 return
323 }
324 }
325 } else {
326 set low_ 0
327 set high_ 0
328 }
329
330 set t [$self get_option checkInterval]
331 after $t "$self load_check"
332 }
333
334
335 #-----------------------------------------------------------------------
336 # Method:
337 # IHMAgent spawn
338 # Description:
339 # Spawn a copy of hm on another random host.
340 #-----------------------------------------------------------------------
341 IHMAgent instproc spawn {} {
342 $self instvar hmhosts_ app_ ascp_
343
344 # build hmhosts_ - hmlist
345 set hmlist [$ascp_ hmaddrs]
346
347 if { [$self get_option glunix] != "" } {
348 set tlist [eval exec "glustat -s l -l"]
349 } else {
350 set tlist $hmhosts_
351 }
352 # remove any local entries we may have
353 set i [lsearch -exact $tlist [localaddr]]
354 set tlist [lreplace $tlist $i $i]
355
356 foreach h $hmlist {
357 set i [lsearch -exact $tlist $h]
358 set tlist [lreplace $tlist $i $i]
359 }
360 set n [llength $tlist]
361 if { $n == 0 } {
362 # no hosts to spawn to
363 return 0
364 }
365 if { [$self get_option glunix] != "" } {
366 # glustat already sorted by load.
367 set r 0
368 } else {
369 set r [expr [random] % $n]
370 }
371 set shost [lindex $tlist $r]
372
373 $self dospawn $shost
374
375 return 1
376 }
377
378
379 #-----------------------------------------------------------------------
380 # Method:
381 # IHMAgent dospawn
382 # Description:
383 # Called by spawn{}, spawn a copy of hm on $shost.
384 #-----------------------------------------------------------------------
385 IHMAgent private dospawn { shost } {
386 $self instvar app_
387 set path [$self get_option execPath]
388 set argv [$self get_option execArgs]
389 set cmd [$self get_option execCmd]
390
391 MashLog info "eval exec $cmd $shost $path/smash $path/hm $argv >& /dev/null &"
392 if { [catch "eval exec $cmd $shost $path/smash $path/hm $argv >& /dev/null &" t] != 0 } {
393 MashLog warn "catch error: $t"
394 }
395 }
396
397
398 #-----------------------------------------------------------------------
399 # Method:
400 # IHMAgent shed_load
401 # Description:
402 # Randomly kills of servents.
403 #-----------------------------------------------------------------------
404 IHMAgent instproc shed_load {} {
405 $self instvar handlers_
406 foreach h $handlers_ {
407 set pid [$h set pid_]
408 # Shed 50% of the load randomly.
409 set r [expr [random]/double(0x7fffffff)]
410 if { $r < 0.5 } {
411 MashLog info "exec kill -9 $pid"
412 catch "eval exec kill -9 $pid"
413 }
414 }
415 }
416
417
418 #-----------------------------------------------------------------------
419 # Method:
420 # IHMAgent die
421 # Description:
422 # Schedule a callback to kill self.
423 #-----------------------------------------------------------------------
424 IHMAgent instproc die {} {
425 $self instvar dying_ app_
426
427 set t [$self get_option deathInterval]
428 set minwait [$self get_option minDeathWait]
429 set r [expr ([random] % $t) + $minwait]
430 set dying_ [after $r "$self really_die"]
431 }
432
433
434 #-----------------------------------------------------------------------
435 # Method:
436 # IHMAgent really_die
437 # Description:
438 # Really kill self.
439 #-----------------------------------------------------------------------
440 IHMAgent instproc really_die {} {
441 $self instvar app_ dying_ low_ high_ ascp_
442
443 $ascp_ announce_death
444 set minhm [$self get_option minHmNum]
445 set hmnum [$ascp_ get_num_of_hms]
446 # count ourselves
447 incr hmnum
448 # one last check
449 set load [MashSystem get_load]
450 if { $hmnum > $minhm && $load < [$self get_option lowLoad] } {
451 MashLog info "exit 0"
452 $self doexit
453 }
454 # get back in the mix!
455 unset dying_
456 set low_ 0
457 set high_ 0
458 $self load_check
459 }
460
461
462 #-----------------------------------------------------------------------
463 # Method:
464 # IHMAgent recv_death
465 # Description:
466 # If someone has already kill itself, it is possible that we do not
467 # have to kill ourself. Recheck.
468 #-----------------------------------------------------------------------
469 IHMAgent instproc recv_death {} {
470 $self instvar dying_ low_ high_
471
472 if ![info exists dying_] {
473 return
474 }
475
476 # Someone else is dying, we get a reprieve...
477 after cancel $dying_
478 unset dying_
479 set low_ 0
480 set high_ 0
481 $self load_check
482 }
483
484 #vim:ts=8:sw=4:expandtab
485
486
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.