1 # ascp-client.tcl --
2 #
3 # Client side of ASCP protocol.
4 #
5 # Copyright (c) 1998-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/lib/ascp-client.tcl,v 1.4 2002/07/15 22:08:09 weitsang Exp $
32
33 import ASCP
34 import ASCPPacket
35 import MashLog
36 import MashTimer/ConstBW
37 import MashSoftState
38
39 Class ASCP/Client -superclass ASCP
40
41 #---------------------------------------------------------------------------
42 # Class:
43 # ASCP/Client
44 # Description:
45 # Abstraction for client side of ASCP protocol. Typically, you create
46 # an instance of this class and call "request start" to initiate a service.
47 # In some cases, you may want to intercept various stage of the ASCP
48 # protocol. To do so, you add various hooks to ASCP/Client so that
49 # they will be called when certain events happen. e.g. "on_recv_offer",
50 # "on_alive". An ASCP/client object can only launch one service.
51 # Members:
52 # request_timer_ --
53 # A Mash/Timer object that sends out request message periodically.
54 # alive_timer_ --
55 # A Mash/Timer object that sends out alive message periodically.
56 # service_ --
57 # A soft-state that remembers we are being served by a service.
58 # sev_loc_ --
59 # Service location for the requested service.
60 # precond_ --
61 # Precondition for the requested service.
62 #---------------------------------------------------------------------------
63
64 #---------------------------------------------------------------------------
65 # Method:
66 # ASCP/Client init
67 # Description:
68 # Initialize various members.
69 # Arguments:
70 # spec --
71 # addr/port specification of the active service control session.
72 # bw --
73 # maximum bandwidth of the control session
74 # srv_loc --
75 # where to find the service.
76 # precondition --
77 # precondition which service providers have to pass before launching
78 # service.
79 #---------------------------------------------------------------------------
80 ASCP/Client public init { spec bw srv_loc precond } {
81 $self next $spec $bw
82 $self instvar srv_loc_ precond_ timeout_
83 set srv_loc_ $srv_loc
84 set precond_ $precond
85 set timeout_ 10000
86
87 $self instvar callback_
88 set callback_(alive) ""
89 set callback_(expire) ""
90 set callback_(destroy) ""
91 }
92
93
94 #---------------------------------------------------------------------------
95 # Method:
96 # ASCP/Client destroy
97 # Description:
98 # Clean-up timers and soft-state.
99 #---------------------------------------------------------------------------
100 ASCP/Client instproc destroy {} {
101 $self request stop
102 $self launch stop
103 $self launch_now stop
104 $self alive stop
105
106 $self instvar service_
107 if [info exists service_] {
108 delete $service_
109 unset service_
110 }
111
112 $self instvar callback_
113 foreach cb $callback_(destroy) {
114 eval $cb
115 }
116
117 $self next
118 }
119
120
121 #---------------------------------------------------------------------------
122 # Method:
123 # ASCP/Client agent_type
124 # Description:
125 # Return the type of this agent: (c)lient/(s)ervice/(m)anager.
126 #---------------------------------------------------------------------------
127 ASCP/Client public agent_type { } {
128 return "c"
129 }
130
131
132 #---------------------------------------------------------------------------
133 # Method:
134 # ASCP/Client service_location
135 # Description:
136 # Return the service location.
137 #---------------------------------------------------------------------------
138 ASCP/Client public service_location { } {
139 $self instvar srv_loc_
140 return $srv_loc_
141 }
142
143
144 #---------------------------------------------------------------------------
145 # Method:
146 # ASCP/Client precondition.
147 # Description:
148 # Return the precondition, or set the precondition to the given args.
149 #---------------------------------------------------------------------------
150 ASCP/Client public precondition { args } {
151 $self instvar precond_
152 if {$args == ""} {
153 return $precond_
154 } else {
155 set precond_ [lindex $args 0]
156 }
157 }
158
159
160 #---------------------------------------------------------------------------
161 # Method:
162 # ASCP/Client request
163 # Description:
164 # "request start" starts the periodic timer that sends a request message.
165 # "request stop" stop the timer.
166 #---------------------------------------------------------------------------
167 ASCP/Client public request {option args} {
168 $self instvar timeout_
169 #MashLog info "Request $option [$self service_location]"
170 switch $option {
171 start {
172 set packet [new ASCPPacket]
173 $packet from_ascp "request" $self
174 set str [$packet to_string]
175 delete $packet
176
177 $self announce $str
178
179 $self instvar request_timer_
180 set request_timer_ [new MashTimer/ConstBW \
181 3000 "$self announce \{$str\}" [$self bandwidth]]
182
183 # If "-block" is specified, we block until offer is received
184 # in process_packet. Unless timeout occurs.
185 set si [$self service_instance]
186 if {[lsearch $args "-block"] != -1} {
187 global recv_offer_from_
188 set recv_offer_from_($si) 0
189 after $timeout_ "
190 set recv_offer_from_($si) TIMEOUT
191 "
192 vwait recv_offer_from_($si)
193 if {$recv_offer_from_($si) == "TIMEOUT"} {
194 $self timeout
195 }
196
197 $self request stop
198
199 return $recv_offer_from_($si)
200 }
201 }
202 stop {
203 $self instvar request_timer_
204 if {[info exists request_timer_]} {
205 delete $request_timer_
206 unset request_timer_
207 }
208 }
209 default {
210 error "unknown request option: use start/stop"
211 }
212 }
213 }
214
215
216 #---------------------------------------------------------------------------
217 # Method:
218 # ASCP/Client alive
219 # Description:
220 # "alive start" starts the periodic timer that sends an alive message.
221 # "alive stop" stop the timer. "alive pending" checks if a timer exists.
222 #---------------------------------------------------------------------------
223 ASCP/Client private alive {option} {
224 #MashLog info "Alive $option [$self service_location]"
225 $self instvar alive_timer_
226 switch $option {
227 start {
228 set packet [new ASCPPacket]
229 $packet agent_type [$self agent_type]
230 $packet agent_id [$self agent_id]
231 $packet operation "alive"
232 $packet service_instance [$self service_instance]
233 set o [$packet to_string]
234 delete $packet
235
236 $self announce $o
237
238 set alive_timer_ [new MashTimer/ConstBW \
239 1000 "$self announce \{$o\}" [$self bandwidth]]
240 $self add_timer $alive_timer_
241 }
242 stop {
243 if [info exists alive_timer_] {
244 $self del_timer $alive_timer_
245 delete $alive_timer_
246 unset alive_timer_
247 }
248 }
249 default {
250 error "unknown alive option: use start/stop/pending"
251 }
252 }
253 }
254
255
256 #---------------------------------------------------------------------------
257 # Method:
258 # ASCP/Client process_packet.
259 # Description:
260 # Handle a packet received by the protocol. This method (or methods that
261 # it calls) MUST free $packet when done.
262 #---------------------------------------------------------------------------
263 ASCP/Client public process_packet { addr packet } {
264
265 set service_instance [$packet service_instance]
266 if {$service_instance != [$self service_instance]} {
267 #delete $packet
268 return
269 }
270 $self instvar service_
271
272 set agent_type [$packet agent_type]
273 set agent_id [$packet agent_id]
274 set operation [$packet operation]
275 #delete $packet
276
277 switch -exact -- $agent_type {
278 "c" {
279 # Ignore packets from other clients.
280 }
281 "m" {
282 switch -exact -- $operation {
283 # Receive an offer from a host manager.
284 "offer" {
285 if {![info exists service_] || [$service_ is_expired]} {
286 # This is the first offer. Accept it. All other subsequent offers
287 # will be ignored.
288 MashLog info "recv offer from $agent_id for $service_instance"
289
290 $self request stop
291 global recv_offer_from_
292 set recv_offer_from_($service_instance) $agent_id
293
294 } else {
295 # Receive multiple offers.
296 MashLog info "[$self service_location] already being served by [$service_ get_value]"
297 }
298 }
299 }
300 }
301 "s" {
302 switch -exact -- $operation {
303 "alive" {
304
305 # Receive an alive message from a service. If this is the first alive, then we start
306 # a softsate timer to remember that we are being served by this service. Otherwise,
307 # we just refresh the soft-state.
308
309 $self instvar service_ launch_timer_ launch_now_timer_
310 if ![info exists service_] {
311 if [info exists launch_timer_] {
312 $self launch stop
313 } elseif [info exists launch_now_timer_] {
314 $self launch_now stop
315 }
316 set service_ [new MashSoftState/Adaptive $agent_id 5000 "$self service_expire"]
317 $self instvar alive_timer_
318 if {![info exists alive_timer_]} {
319 $self alive start
320 }
321 global recv_alive_from_
322 set recv_alive_from_($service_instance) $agent_id
323 } else {
324 $service_ refresh
325 }
326
327
328 $self instvar callback_
329 if {$callback_(alive) != ""} {
330 eval $callback_(alive)
331 }
332 }
333 "bye" {
334 if [info exists service_] {
335 delete $service_
336 }
337 $self request start
338 }
339 }
340 }
341 }
342 }
343
344
345 #---------------------------------------------------------------------------
346 # Method:
347 # ASCP/Client on_service_expire
348 # ASCP/Client on_destroy
349 # ASCP/Client on_timeout
350 # ASCP/Client on_alive
351 # Description:
352 # Define callbacks when events happen.
353 #---------------------------------------------------------------------------
354 ASCP/Client public on_service_expire {option callback} {
355 $self on_event expire $option $callback
356 }
357 ASCP/Client public on_destroy {option callback} {
358 $self on_event destroy $option $callback
359 }
360 ASCP/Client public on_timeout {option callback} {
361 $self on_event timeout $option $callback
362 }
363 ASCP/Client public on_alive {option callback} {
364 $self on_event alive $option $callback
365 }
366
367
368 #---------------------------------------------------------------------------
369 # Method:
370 # ASCP/Client on_event
371 # Description:
372 # Private method that manages the callbacks
373 #---------------------------------------------------------------------------
374 ASCP/Client private on_event {type option callback} {
375 $self instvar callback_
376 switch $option {
377 append {
378 lappend callback_($type) $callback
379 }
380 remove {
381 set pos [lsearch -glob $callback_($type) $callback]
382 set callback_($type) [lreplace $callback_($type) $pos $pos]
383 }
384 }
385 }
386
387
388
389 #---------------------------------------------------------------------------
390 # Method:
391 # ASCP/Client timeout
392 # Description:
393 # Call on_timeout callback_, if defined.
394 # Default is not to do anything
395 #---------------------------------------------------------------------------
396 ASCP/Client public timeout {} {
397 $self instvar callback_
398 if [info exists callback_(timeout)] {
399 foreach c $callback_(timeout) {
400 eval $c
401 }
402 } else {
403 $self instvar srv_loc_
404 MashLog info "TIMEOUT $srv_loc_"
405 }
406 return -code error -errorcode TIMEOUT
407 }
408
409
410
411 #---------------------------------------------------------------------------
412 # Method:
413 # ASCP/Client relaunch
414 #---------------------------------------------------------------------------
415 ASCP/Client public relaunch {{option ""}} {
416 MashLog info "RELAUNCH $self [$self service_location] [$self precondition]"
417 if {$option == ""} {
418 $self launch start
419 } else {
420 if {$option == "now"} {
421 set agent_id [$self request start -block]
422 set temp [$self precondition]
423 $self precondition "agentid:$agent_id"
424 $self launch_now start
425 $self precondition "$temp"
426 }
427 }
428 }
429
430 #---------------------------------------------------------------------------
431 # Method:
432 # ASCP/Client expire
433 #---------------------------------------------------------------------------
434 ASCP/Client public service_expire {} {
435
436 MashLog info "$self service expired"
437
438 $self instvar service_
439 delete $service_
440 unset service_
441
442 $self instvar callback_
443 if {$callback_(expire) != ""} {
444 # Make a copy of current callbacks, because $cb might
445 # append to $callback_($expire), causing infinite loop.
446 set expire_callbacks $callback_(expire)
447 foreach cb $expire_callbacks {
448 #MashLog info "$self service expired $cb"
449 eval $cb
450 }
451 }
452 #MashLog info "$self service expired DONE"
453 }
454
455
456 #---------------------------------------------------------------------------
457 # Method:
458 # ASCP/Client launch_now
459 # Description:
460 # "launch start" starts the periodic timer that sends a launch message.
461 # "launch stop" stop the timer.
462 #---------------------------------------------------------------------------
463 ASCP/Client public launch_now {option args} {
464 $self instvar timeout_
465 switch $option {
466 start {
467 set packet [new ASCPPacket]
468 $packet from_ascp "launch_now" $self
469 set str [$packet to_string]
470 delete $packet
471
472 $self announce $str
473
474 $self instvar launch_now_timer_
475 set launch_now_timer_ [new MashTimer/ConstBW \
476 3000 "$self announce \{$str\}" [$self bandwidth]]
477
478 if {[lsearch $args "-block"] != -1} {
479 global recv_alive_from_
480 set service_instance [$self service_instance]
481 set recv_alive_from_($service_instance) 0
482 after $timeout_ "
483 set recv_alive_from_($service_instance) TIMEOUT
484 "
485 vwait recv_alive_from_($service_instance)
486 if {$recv_alive_from_($service_instance) == "TIMEOUT"} {
487 $self timeout
488 }
489 return $recv_alive_from_($service_instance)
490 }
491 }
492 stop {
493 $self instvar launch_now_timer_
494 if {[info exists launch_now_timer_]} {
495 delete $launch_now_timer_
496 unset launch_now_timer_
497 }
498 }
499 default {
500 error "unknown request option: use start/stop"
501 }
502 }
503 }
504
505
506 #---------------------------------------------------------------------------
507 # Method:
508 # ASCP/Client launch
509 # Description:
510 # "launch start" starts the periodic timer that sends a launch message.
511 # "launch stop" stop the timer.
512 #---------------------------------------------------------------------------
513 ASCP/Client public launch {option args} {
514 #MashLog info "Launch $option"
515 switch $option {
516 start {
517 set packet [new ASCPPacket]
518 $packet from_ascp "launch" $self
519 set str [$packet to_string]
520 delete $packet
521
522 $self announce $str
523
524 $self instvar launch_timer_
525 set launch_timer_ [new MashTimer/ConstBW \
526 3000 "$self announce \{$str\}" [$self bandwidth]]
527
528 if {[lsearch $args "-block"] != -1} {
529 global recv_alive_from_
530 set service_instance [$self service_instance]
531 set recv_alive_from_($service_instance) 0
532 $self instvar timeout_
533 after $timeout_ "
534 set recv_alive_from_($service_instance) TIMEOUT
535 "
536 vwait recv_alive_from_($service_instance)
537 if {$recv_alive_from_($service_instance) == "TIMEOUT"} {
538 $self timeout
539 }
540 return $recv_alive_from_($service_instance)
541 }
542 }
543 stop {
544 $self instvar launch_timer_
545 if {[info exists launch_timer_]} {
546 delete $launch_timer_
547 unset launch_timer_
548 }
549 }
550 default {
551 error "unknown request option: use start/stop"
552 }
553 }
554 }
555
556 ASCP/Client public service_instance {} {
557 return "[pid]@[info hostname]:$self"
558 }
559
560
561 ASCP/Client public served_by {} {
562 $self instvar service_
563 if [info exists service_] {
564 return [$service_ get_value]
565 } else {
566 return ""
567 }
568 }
569
570
571 ASCP/Client public service_type {} {
572 return "Generic"
573 }
574 #vim:ts=8:sw=4:expandtab
575
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.