1 # ascp-manager.tcl --
2 #
3 # AnnounceListenManager for Indiva Host Manager. It handles
4 # messages from other host managers and clients.
5 #
6 # Copyright (c) 1998-2002 The Regents of the University of California.
7 # All rights reserved.
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions are met:
11 #
12 # A. Redistributions of source code must retain the above copyright notice,
13 # this list of conditions and the following disclaimer.
14 # B. Redistributions in binary form must reproduce the above copyright notice,
15 # this list of conditions and the following disclaimer in the documentation
16 # and/or other materials provided with the distribution.
17 # C. Neither the names of the copyright holders nor the names of its
18 # contributors may be used to endorse or promote products derived from this
19 # software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
22 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #
32 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/indiva/lib/ascp-manager.tcl,v 1.3 2002/08/08 22:32:24 weitsang Exp $
33
34 import ASCP
35 import IHMAgent
36 import MashLog
37 import MashRNG
38 import MashTimer
39 import MashSystem
40 import MashSoftTable/Adaptive
41
42 #--------------------------------------------------------------------------
43 # Class:
44 # ASCP/Manager
45 # Description:
46 #
47 # Members:
48 # tid_ --
49 # An array of timers indexed by service instance.
50 #
51 # launched_ --
52 # An array of services launched, indexed by service instance.
53 #--------------------------------------------------------------------------
54 Class ASCP/Manager -superclass ASCP
55
56 ASCP/Manager instproc init { spec bw } {
57 $self next $spec $bw
58
59 $self instvar script_loader_
60 set script_loader_ [new IHMScriptLoader]
61
62 $self instvar alive_timer_
63 set alive_timer_ [new MashTimer/ConstBW 5000 "$self announce_alive" $bw]
64
65 $self instvar service_table_
66 set service_table_ [new MashSoftTable/Adaptive 5000]
67 }
68
69
70 ASCP/Manager instproc agent_type { } {
71 return "m"
72 }
73
74
75 #--------------------------------------------------------------------------
76 # Method:
77 # ASCP/Manager process_packet
78 # Description:
79 # Process a message from other alm. If we get a client message, we
80 # schedule a launch if current cpu load is less than a threshold.
81 # If we get a launch message from another hm, we cancel our scheduled
82 # launch. If we get an update message from another hm from the same
83 # host, we kill one of the hm. Ignore server messages.
84 #--------------------------------------------------------------------------
85
86 ASCP/Manager instproc process_packet { addr packet } {
87 set agent_type [$packet agent_type]
88 set operation [$packet operation]
89 set agent_id [$packet agent_id]
90 set service_instance [$packet service_instance]
91 set service_location [$packet service_location]
92 set service_type [$packet service_type]
93 set precond [$packet precondition]
94 set msg [$packet args]
95 switch $agent_type {
96 m {
97 switch $operation {
98 alive {
99 # Check if there's another hm on this host
100 if { $addr == [localaddr] } {
101 $self kill_dup_hm $agent_id
102 }
103 }
104 offer -
105 launch_ok {
106 $self suppress_timer $service_instance
107 }
108 }
109 }
110 c {
111 switch -exact -- $operation {
112 "request" {
113 MashLog info "recv request $service_location from $addr"
114 if {[$self check_precond $precond]} {
115 set load [MashSystem get_load]
116 set hiload [$self get_option highLoad]
117 if {[$self get_option noLoad] == "" && $load >= $hiload } {
118 MashLog info "HI LOAD load=$load $hiload"
119 } else {
120 # Wait a couple seconds to prevent any race conditions
121 # between gateway announcement prompted by client
122 # message and hm check.
123 after 100 "$self handle_request $service_type $service_location $service_instance [gettimeofday] $msg"
124 }
125 } else {
126 MashLog info "Precond failed: $precond"
127 }
128 }
129 "launch_now" {
130 $self instvar service_table_
131 if {[$service_table_ get $service_instance] == ""} {
132 if {[$self check_precond $precond]} {
133 set args [join [split $msg \n]]
134 $self launch $service_type $service_location $service_instance $args
135 } else {
136 # MashLog info "PRECOND FAILED $precond"
137 }
138 } else {
139 MashLog info "Already servicing $service_location [$service_table_ get $service_instance]"
140 }
141 }
142 "launch" {
143 MashLog info "recv launch $service_location from $addr"
144 if {[$self check_precond $precond]} {
145 set load [MashSystem get_load]
146 set hiload [$self get_option highLoad]
147 if {[$self get_option noLoad] == "" && $load >= $hiload } {
148 MashLog info "HI LOAD load=$load $hiload"
149 } else {
150 # Wait a couple seconds to prevent any race conditions
151 # between gateway announcement prompted by client
152 # message and hm check.
153 after 100 "$self handle_launch $service_type $service_location $service_instance [gettimeofday] $msg"
154 }
155 } else {
156 MashLog info "Precond failed: $precond"
157 }
158 }
159 "alive" {
160 # For services. Ignore.
161 }
162 default {
163 MashLog error "unrecorgnize operation $operation"
164 }
165 }
166 }
167 s {
168 $self instvar service_table_
169 switch -exact -- $operation {
170 "alive" {
171 if [$service_table_ has $service_instance] {
172 #MashLog info "$service_instance is alive"
173 $service_table_ refresh $service_instance
174 }
175 }
176 }
177 #delete $packet
178 }
179 }
180 }
181
182
183 #--------------------------------------------------------------------------
184 # Method:
185 # ASCP/Manager kill_dup_hm
186 # Description:
187 # Kill self if pid is less than the other (duplicated) hm.
188 #--------------------------------------------------------------------------
189 ASCP/Manager instproc kill_dup_hm { name } {
190 # A duplicate hm - break the tie with pids.
191 if { [string compare $name [$self agent_id]] < 0 } {
192 MashLog info "duplicate hm: $name [$self agent_id] - exiting."
193 # XXX : define doexit
194 $self doexit
195 }
196 }
197
198
199 #--------------------------------------------------------------------------
200 # Method:
201 # ASCP/Manager handle_request
202 # Description:
203 # Checks if another service for this client is running, if not schedule
204 # a launch.
205 #--------------------------------------------------------------------------
206 ASCP/Manager instproc handle_request { srv_name srv_loc srv_inst curr_time {msg ""}} {
207 if [$self is_pending $srv_inst] {
208 MashLog info "already pending $srv_inst"
209 return
210 }
211
212 $self instvar service_table_
213
214 # If we don't have a record of a gateway for this session,
215 # launch one.
216 if {[$service_table_ get $srv_inst] == ""} {
217 # Can't find a service record. Set launch timer.
218 $self sched_offer_service $srv_name $srv_loc $srv_inst $msg
219 }
220 }
221
222
223 #--------------------------------------------------------------------------
224 # Method:
225 # ASCP/Manager handle_launch
226 # Description:
227 # Checks if another service for this client is running, if not schedule
228 # a launch.
229 #--------------------------------------------------------------------------
230 ASCP/Manager instproc handle_launch { srv_name srv_loc srv_inst curr_time args} {
231 if [$self is_pending $srv_inst] {
232 MashLog info "already pending $srv_inst"
233 return
234 }
235
236 $self instvar service_table_
237
238 # If we don't have a record of a gateway for this session,
239 # launch one.
240 if {[$service_table_ get $srv_inst] == ""} {
241 # Can't find a service record. Set launch timer.
242 $self sched_launch $srv_name $srv_loc $srv_inst $args
243 }
244 }
245
246
247
248 #-----------------------------------------------------------------------
249 # Method:
250 # ASCP/Manager sched_offer_service
251 # Description:
252 # Schedule an offer announcement.
253 #-----------------------------------------------------------------------
254 ASCP/Manager public sched_offer_service { srv_name srv_loc srv_inst msg } {
255
256 set numhm [$self get_num_of_hms]
257 set T [expr $numhm * 500]
258 set max [$self get_option maxWait]
259 if { $T > $max } {
260 set T $max
261 }
262 # FIXME bias T by load.
263 set t [MashRNG integer $T]
264 MashLog info "timer $srv_name $srv_inst $t"
265
266 set tid [after $t "$self offer_service $srv_name $srv_loc $srv_inst {$msg}"]
267 $self instvar tid_
268 set tid_($srv_inst) $tid
269 }
270
271
272 ASCP/Manager instproc register { addr packet } {
273 set agent_type [$packet agent_type]
274 if { $addr != [localaddr] || $agent_type != "s" } {
275 return
276 }
277 $self cancel_timer [$packet service_instance]
278 }
279
280
281 ASCP/Manager instproc unregister { addr packet } {
282 set agent_type [$packet agent_type]
283 if { $addr != [localaddr] || $agent_type != "s" } {
284 return
285 }
286 $self unregister [$packet agent_id] [$packet args]
287 }
288
289
290 ASCP/Manager instproc announce_offer { service_instance } {
291
292 set packet [new ASCPPacket]
293 $packet agent_type [$self agent_type]
294 $packet agent_id [$self agent_id]
295 $packet operation "offer"
296 $packet service_instance $service_instance
297
298 MashLog info "offer service $service_instance"
299 $self announce [$packet to_string]
300 delete $packet
301 }
302
303
304 ASCP/Manager instproc announce_launch_ok { service_instance } {
305
306 set packet [new ASCPPacket]
307 $packet agent_type [$self agent_type]
308 $packet agent_id [$self agent_id]
309 $packet operation "launch_ok"
310 $packet service_instance $service_instance
311
312 MashLog info "launch_ok $service_instance"
313 $self announce [$packet to_string]
314 delete $packet
315
316 }
317
318
319 #
320 # We build a simple message since we only use this to announce
321 # our presence.
322 # FIXME: Since this is called periodically, we should reuse the
323 # packet instead of creating a new one everytime.
324 #
325 ASCP/Manager instproc announce_alive {} {
326 set packet [new ASCPPacket]
327 $packet agent_type [$self agent_type]
328 $packet agent_id [$self agent_id]
329 $packet operation "alive"
330
331 $self announce [$packet to_string]
332 delete $packet
333 }
334
335
336 ASCP/Manager public get_num_of_hms {} {
337 return [llength [$self hmaddrs]]
338 }
339
340
341 ASCP/Manager private hmaddrs {} {
342 # FIXME
343 $self instvar agents_
344 set aspecs [array names agents_]
345
346 foreach aspec $aspecs {
347 set addr [lindex $agents_($aspec) 0]
348 # remove duplicate entries
349 set d($addr) 1
350 }
351 return [array names d]
352 }
353
354
355 #-----------------------------------------------------------------------
356 # Method:
357 # ASCP/Manager is_pending
358 # Description:
359 # Check if there is a timer with id == $tid pending.
360 # Arguments:
361 # tid -- ID of the timer to check.
362 #-----------------------------------------------------------------------
363 ASCP/Manager instproc is_pending tid {
364 $self instvar tid_
365 return [info exists tid_($tid)]
366 }
367
368
369 #-----------------------------------------------------------------------
370 # Method:
371 # ASCP/Manager cancel_timer
372 # Description:
373 # Remove the "after" callback and information regarding a scheduled
374 # launched identified by $tid.
375 # Arguments:
376 # tid -- ID of the timer to cancel.
377 #-----------------------------------------------------------------------
378 ASCP/Manager instproc cancel_timer {tid} {
379 $self instvar tid_ launched_
380 if [info exists launched_($tid)] {
381 unset launched_($tid)
382 }
383 if { [info exists tid_($tid)] } {
384 #MashLog info "cancelled timer $tid"
385 after cancel $tid_($tid)
386 MashLog info "cancel timer for $tid"
387 unset tid_($tid)
388 }
389 }
390
391
392 #-----------------------------------------------------------------------
393 # Method:
394 # ASCP/Manager sched_launch
395 # Description:
396 # Schedule a launch by creating an "after" callback. The timeout
397 # period for launching is randomly, uniformly chosen from [0, 2000*N],
398 # where N is the estimate number of host manager currently running.
399 # Arguments:
400 # srv_name -- Name of the service to launch.
401 # srv_loc -- Where to find the executable of the service.
402 # srv_inst -- A unique ID to the service.
403 # msg -- Data for the service.
404 #-----------------------------------------------------------------------
405 ASCP/Manager public sched_launch { srv_name srv_loc srv_inst msg } {
406 set numhm [$self get_num_of_hms]
407 set T [expr $numhm * 500]
408 set max [$self get_option maxWait]
409 if { $T > $max } {
410 set T $max
411 }
412 # FIXME bias T by load.
413 set t [MashRNG integer $T]
414 MashLog info "timer $srv_name $srv_inst $t"
415
416 set tid [after $t "$self launch $srv_name $srv_loc $srv_inst {$msg}"]
417 $self instvar tid_
418 set tid_($srv_inst) $tid
419 }
420
421
422 #-----------------------------------------------------------------------
423 # Method:
424 # ASCP/Manager suppress_timer
425 # Description:
426 # Cancel our own launch, because someone else has already launch the
427 # same service.
428 # Arguments:
429 # tid -- ID to the service to cancel.
430 #-----------------------------------------------------------------------
431 ASCP/Manager public suppress_timer { sid } {
432 $self instvar tid_ launched_
433 MashLog info "suppress timer $sid"
434 if { [info exists tid_($sid)] && ![info exists launched_($sid)] } {
435 after cancel $tid_($sid)
436 unset tid_($sid)
437 }
438 }
439
440 #-----------------------------------------------------------------------
441 # Method:
442 # ASCP/Manager pending_launches
443 # Description:
444 # Return the number of launches pending.
445 #-----------------------------------------------------------------------
446 ASCP/Manager instproc pending_launches {} {
447 $self instvar launched_
448 return [llength [array names launched_]]
449 }
450
451
452 #-----------------------------------------------------------------------
453 # Method:
454 # ASCP/Manager offer_service
455 # Description:
456 # Offer a service. A service is offered only if (1) the number of
457 # current pending launches is less than maximum allowable pending
458 # launches (configurable through maxPending), (2) current CPU load
459 # is less than maximum allowable CPU load (configurable through
460 # highLoad). This method is an "after" callback scheduled by
461 # sched_offer_service {} method.
462 # Arguments:
463 # srv_name -- Name of the service to launch.
464 # srv_loc -- Location of the service's executable.
465 # srv_inst -- ?
466 # msg -- ?
467 #-----------------------------------------------------------------------
468 ASCP/Manager private offer_service { srv_name srv_loc srv_inst msg } {
469
470 if { [$self pending_launches] >= [$self get_option maxPending] } {
471 #MashLog info "BACKLOG [$self pending_launches]"
472 $self cancel_timer $srv_inst
473 return
474 }
475 set load [MashSystem get_load]
476 set hiload [$self get_option highLoad]
477 #MashLog info "LAUNCH load=$load $hiload"
478 if { [$self get_option noLoad] == "" && $load >= $hiload } {
479 $self cancel_timer $srv_inst
480 return
481 }
482
483 $self announce_offer $srv_inst
484 $self cancel_timer $srv_inst
485 return 1
486 }
487
488
489 #-----------------------------------------------------------------------
490 # Method:
491 # ASCP/Manager launch
492 # Description:
493 # Launch a service. A service is launch only if (1) the number of
494 # current pending launches is less than maximum allowable pending
495 # launches (configurable through maxPending), (2) current CPU load
496 # is less than maximum allowable CPU load (configurable through
497 # highLoad). This method is an "after" callback scheduled by
498 # schedule_launch {} method.
499 # Arguments:
500 # srv_name -- Name of the service to launch.
501 # srv_loc -- Location of the service's executable.
502 # srv_inst -- ?
503 # msg -- ?
504 #-----------------------------------------------------------------------
505 ASCP/Manager private launch { srv_type srv_loc srv_inst arguments } {
506
507 if {[$self pending_launches] >= [$self get_option maxPending]} {
508 MashLog info "BACKLOG [$self pending_launches]"
509 $self cancel_timer $srv_inst
510 return
511 }
512 set curr_load [MashSystem get_load]
513 set high_load [$self get_option highLoad]
514 if {[$self get_option noLoad] == "" && $curr_load >= $high_load} {
515 $self cancel_timer $srv_inst
516 return
517 }
518 MashLog info "Launch load=$curr_load $high_load"
519
520 # $self instvar creators_
521 # if ![info exists creators_($srv_type)] {
522 # MashLog info "ERROR unrecorgnize service $srv_type"
523 # return
524 # }
525 # set creator $creators_($srv_type)
526 # set h [$creator create_handler $srv_inst $msg]
527
528 $self instvar script_loader_ service_table_
529 set script [$script_loader_ get_script $srv_type $srv_loc]
530 if { $script != "" } {
531 $self exec $script $srv_inst $arguments
532 $service_table_ set $srv_inst [$self agent_id]
533 }
534
535 $self announce_launch_ok $srv_inst
536 }
537
538
539 ASCP/Manager instproc error {msg} {
540 MashLog error "$msg"
541 }
542
543
544 ASCP/Manager instproc exec {script service_instance arguments} {
545 # FIXME: as_spec/bw should be abstracted to somewhere else.
546
547 $self instvar as_spec_ as_bw_
548 global env
549 if [info exists env(MASHWORK)] {
550 set mashpath "$env(MASHWORK)/bin/"
551 } else {
552 set mashpath ""
553 }
554 if {[catch {eval exec ${mashpath}smash $script $arguments -as_spec $as_spec_ -as_bw $as_bw_ -sid $service_instance >& ${script}[clock clicks -milliseconds].log &} t]} {
555 MashLog info "$self exec ${mashpath}smash $script $arguments -as_spec $as_spec_ -as_bw $as_bw_ -sid $service_instance"
556 MashLog warn "ERROR: $t"
557 } else {
558 MashLog info "$self exec ${mashpath}smash $script $arguments -as_spec $as_spec_ -as_bw $as_bw_ -sid $service_instance"
559 }
560 }
561
562
563 #--------------------------------------------------------------------------
564 # Method:
565 # ASCP/Manager check_precond
566 # Description:
567 #--------------------------------------------------------------------------
568 ASCP/Manager instproc check_precond { precond } {
569 if {$precond == "-" || $precond == ""} {
570 return 1
571 }
572 set precondlist [split $precond :]
573 set prefix [lindex $precondlist 0]
574 set rest [string range $precond [expr {[string length $prefix]+1}] end]
575 switch -exact -- $prefix {
576 tcl {
577 return [eval $rest]
578 }
579 hostname {
580 return [expr [lsearch -exact $rest [info hostname]] != -1]
581 }
582 agentid {
583 if {[$self agent_id] == $rest} {
584 return 1
585 } else {
586 return 0
587 }
588 }
589 agenttype {
590 if {[$self agent_type] == $rest} {
591 return 1
592 } else {
593 return 0
594 }
595 }
596 default {
597 MashLog error "BAD PRECOND $precond"
598 return 0
599 }
600 }
601 }
602 # vim:ts=8:sw=4:expandtab
603
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.