source: trunk/p2p/test.tcl @ 1251

Last change on this file since 1251 was 1251, checked in by mmc, 15 years ago

Added first cut of P2P network for job management. See README in this
directory for details.

File size: 20.3 KB
Line 
1# ----------------------------------------------------------------------
2#  TEST HARNESS for the P2P infrastructure
3#
4#  This script drives the test setup and visualization for the P2P
5#  infrastructure.  It launches the authority server(s) and various
6#  workers, and helps to visualize their interactions.
7# ----------------------------------------------------------------------
8#  Michael McLennan (mmclennan@purdue.edu)
9# ======================================================================
10#  Copyright (c) 2008  Purdue Research Foundation
11# ======================================================================
12package require Itcl
13
14# recognize other library files in this same directory
15set dir [file dirname [info script]]
16lappend auto_path $dir
17
18set time0 [clock seconds]
19set processes ""
20set nodes(all) ""
21set nodeRadius 15
22
23# ======================================================================
24#  SHAPES
25# ======================================================================
26itcl::class Shape {
27    private variable _canvas ""      ;# shape sits on this canvas
28    private variable _ranges ""      ;# list of time ranges for shape
29    private common _shapesOnCanvas   ;# maps canvas => list of shapes
30
31    public variable command ""  ;# command template used to create shape
32
33    constructor {canvas args} {
34        # add this shape to the list of shapes on this canvas
35        lappend _shapesOnCanvas($canvas) $this
36        set _canvas $canvas
37        eval configure $args
38    }
39    destructor {
40        # remove this shape from the list of shapes on the canvas
41        set i [lsearch $_shapesOnCanvas($_canvas) $this]
42        if {$i >= 0} {
43            set _shapesOnCanvas($_canvas) \
44                [lreplace $_shapesOnCanvas($_canvas) $i $i]
45        }
46    }
47
48    # ------------------------------------------------------------------
49    #  METHOD: addRange <time0> <time1>
50    #  Declares that this shape exists during the given time range
51    #  between <time0> and <time1>.
52    # ------------------------------------------------------------------
53    public method addRange {t0 t1} {
54        # see if there's any overlap with existing ranges
55        set ri0 -1
56        set ri1 -1
57        for {set i 0} {$i < [llength $_ranges]} {incr i} {
58            set pair [lindex $_ranges $i]
59            foreach {r0 r1} $pair break
60            if {$r0 >= $t0 && $r0 <= $t1} {
61                set ri0 $i
62            }
63            if {$r1 >= $t0 && $r1 <= $t1} {
64                set ri1 $i
65            }
66            incr i
67        }
68
69        if {$ri0 < 0 && $ri1 < 1} {
70            # doesn't overlap with anything -- insert in right place
71            for {set i 0} {$i < [llength $_ranges]} {incr i} {
72                set pair [lindex $_ranges $i]
73                foreach {r0 r1} $pair break
74                if {$t0 < $r0} break
75            }
76            set _ranges [linsert $_ranges $i [list $t0 $t1]]
77        } elseif {$ri0 >= 0 && $ri1 >= 0} {
78            # overlaps on both limits -- bridge the middle part
79            set r0 [lindex [lindex $_ranges $ri0] 0]
80            set r1 [lindex [lindex $_ranges $ri1] 1]
81            set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
82        } elseif {$ri0 >= 0} {
83            # overlaps on the lower limit
84            for {set i [expr {[llength $_ranges]-1}]} {$i >= 0} {incr i -1} {
85                set pair [lindex $_ranges $i]
86                foreach {r0 r1} $pair break
87                if {$r0 > $t0 && $r1 < $t1} {
88                    # remove any ranges completely contained in this one
89                    set _ranges [lreplace $_ranges $i $i]
90                }
91                set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
92            }
93            set pair [lindex $_ranges $ri0]
94            foreach {r0 r1} $pair break
95            set _ranges [lreplace $_ranges $ri0 $ri0 [list $r0 $t1]]
96        } else {
97            # overlaps on the upper limit
98            for {set i [expr {[llength $_ranges]-1}]} {$i >= 0} {incr i -1} {
99                set pair [lindex $_ranges $i]
100                foreach {r0 r1} $pair break
101                if {$r0 > $t0 && $r1 < $t1} {
102                    # remove any ranges completely contained in this one
103                    set _ranges [lreplace $_ranges $i $i]
104                }
105                set _ranges [lreplace $_ranges $ri0 $ri1 [list $r0 $r1]]
106            }
107            set pair [lindex $_ranges $ri1]
108            foreach {r0 r1} $pair break
109            set _ranges [lreplace $_ranges $ri1 $ri1 [list $t0 $r1]]
110        }
111    }
112
113    # ------------------------------------------------------------------
114    #  METHOD: exists <time>
115    #  Checks to see if this shape exists at the given <time>.
116    #  Returns 1 if so, and 0 otherwise.
117    # ------------------------------------------------------------------
118    public method exists {t} {
119        for {set i 0} {$i < [llength $_ranges]} {incr i} {
120            set pair [lindex $_ranges $i]
121            foreach {r0 r1} $pair break
122            if {$t >= $r0 && $t <= $r1} {
123                return 1
124            }
125        }
126        return 0
127    }
128
129    # ------------------------------------------------------------------
130    #  PROC: draw <canvas> <time>
131    #  Finds the list of shapes that exist on the given <canvas> at
132    #  the specified time, and executes all of their associated
133    #  -command templates, adding them to the canvas.
134    # ------------------------------------------------------------------
135    public proc draw {canvas time} {
136        if {[info exists _shapesOnCanvas($canvas)]} {
137            foreach obj $_shapesOnCanvas($canvas) {
138                if {[$obj exists $time]} {
139                    set cmd [$obj cget -command]
140                    regsub -all %c $cmd $canvas cmd
141                    eval $cmd
142                }
143            }
144        }
145    }
146
147    # ------------------------------------------------------------------
148    #  PROC: clear <canvas>
149    #  Removes all shapes associated with the specified <canvas>.
150    # ------------------------------------------------------------------
151    public proc clear {canvas} {
152        if {[info exists _shapesOnCanvas($canvas)]} {
153            eval itcl::delete object $_shapesOnCanvas($canvas)
154        }
155    }
156}
157
158# ======================================================================
159#  Build the main interface
160# ======================================================================
161frame .cntls
162pack .cntls -fill x
163
164button .cntls.start -text "Start" -command test_start
165pack .cntls.start -side left -padx 4 -pady 2
166
167button .cntls.stop -text "Stop" -command test_stop -state disabled
168pack .cntls.stop -side left -padx 4 -pady 2
169
170button .cntls.reload -text "Reload" -command test_reload
171pack .cntls.reload -side left -padx 4 -pady 2
172
173button .cntls.layout -text "New Layout" -command {
174    foreach key [array names nodes *-\[xy\]] {
175        unset nodes($key)
176    }
177    after idle test_reload
178}
179pack .cntls.layout -side left -padx 4 -pady 2
180
181frame .player
182pack .player -side bottom -fill x
183
184button .player.back -text "<" -command {test_frame_go -1 nudge}
185pack .player.back -side left -padx 4 -pady 2
186
187button .player.fwd -text ">" -command {test_frame_go 1 nudge}
188pack .player.fwd -side left -padx 4 -pady 2
189
190scale .player.scale -label "Frame" -orient horizontal \
191    -from 0 -to 1 -showvalue 0 -command {test_frame_go 1}
192pack .player.scale -side left -expand yes -fill x -padx 4 -pady 2
193
194frame .view
195pack .view -side bottom -anchor w
196label .view.show -text "Show:"
197grid .view.show -row 0 -column 0 -sticky e
198radiobutton .view.network -text "P2P Network" -variable view -value network -command test_view_change
199grid .view.network -row 0 -column 1 -sticky w
200radiobutton .view.traffic -text "Network traffic" -variable view -value traffic -command test_view_change
201grid .view.traffic -row 1 -column 1 -sticky w
202
203frame .diagram
204pack .diagram -expand yes -fill both
205canvas .diagram.network -width 500 -height 400
206canvas .diagram.traffic -width 500 -height 400
207
208after idle .view.traffic invoke
209
210proc test_stop {} {
211    global processes
212
213    # kill any existing processes
214    foreach job $processes {
215        exec kill $job
216    }
217    set processes ""
218
219    .cntls.stop configure -state disabled
220    .cntls.start configure -state normal
221
222    after idle test_reload
223}
224
225proc test_start {} {
226    global processes
227
228    # clean up existing log files...
229    foreach fname [glob -nocomplain /tmp/log*] {
230        file delete $fname
231    }
232
233    # launch a new authority server
234    lappend processes [exec tclsh authority.tcl &]
235
236    # launch a series of workers
237    for {set i 0} {$i < 20} {incr i} {
238        lappend processes [exec tclsh worker.tcl &]
239        after [expr {int(rand()*5000)}]
240    }
241
242    .cntls.start configure -state disabled
243    .cntls.stop configure -state normal
244}
245
246proc test_reload {} {
247    global time0 nodes actions nodeRadius
248
249    array set colors {
250        authority blue
251        worker gray
252    }
253
254    Shape::clear .diagram.network
255    Shape::clear .diagram.traffic
256    set tmax 0
257
258    #
259    # Scan through all files and generate positions for all nodes.
260    #
261    foreach fname [glob -nocomplain /tmp/log*] {
262        set fid [open $fname r]
263        set info [read $fid]
264        close $fid
265
266        # get the address for this host
267        regexp {started at port ([0-9]+)} $info match port
268        if {[regexp {options [^\n]+ ip ([^ ]+)} $info match ip]} {
269            set addr $ip:$port
270        } else {
271            set addr 127.0.0.1:$port
272        }
273
274        set lasttime ""
275        set info [split $info \n]
276        set first [lindex $info 0]
277        regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+) +(authority|worker)} $first match t0 type
278        set t0val [expr {([clock scan $t0]-$time0)*100}]
279
280        set last [lindex $info end-1]
281        regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+)} $last match t1
282        set t1val [expr {([clock scan $t1]-$time0)*100 + 99}]
283        set margin 20
284        set r $nodeRadius
285
286        set nodes($fname) $addr
287        set nodes($addr-log) $fname
288        set nodes($addr-type) $type
289        if {![info exists nodes($addr-x)]} {
290            set w [expr {[winfo width .diagram]-2*$margin}]
291            set h [expr {[winfo height .diagram]-2*$margin}]
292            set nodes($addr-x) [expr {int(rand()*$w) + $margin}]
293            set nodes($addr-y) [expr {int(rand()*$h) + $margin}]
294        }
295        set x $nodes($addr-x)
296        set y $nodes($addr-y)
297
298        foreach canv {.diagram.traffic .diagram.network} {
299            set s [Shape ::#auto $canv -command \
300                [list %c create oval [expr {$x-$r}] [expr {$y-$r}] \
301                [expr {$x+$r}] [expr {$y+$r}] \
302                -outline black -fill $colors($type) \
303                -tags [list $fname $fname-node]]]
304            $s addRange $t0val $t1val
305        }
306
307        append actions($t0val) "$type $addr online\n"
308        append actions($t1val) "$type $addr offline\n"
309
310        if {$t1val > $tmax} { set tmax $t1val }
311    }
312               
313    #
314    # Scan through files again and generate shapes for all messages
315    #
316    foreach fname [glob -nocomplain /tmp/log*] {
317        set fid [open $fname r]
318        set info [read $fid]
319        close $fid
320puts "\nscanning $fname"
321
322        catch {unset started}
323        set peerlist(addrs) ""
324        set peerlist(time) 0
325        set lasttime ""
326        set counter 0
327        foreach line [split $info \n] {
328            if {[regexp {^([0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+) (.+)$} $line match time mesg]} {
329                set tval [expr {([clock scan $time]-$time0)*100}]
330
331                if {$time == $lasttime} {
332                    set tval [expr {$tval + [incr counter]}]
333                }
334                set lasttime $time
335
336                if {$tval > $tmax} { set tmax $tval }
337
338                if {[regexp {accepted: +([^ ]+) +\((.+)\)} $mesg match addr cid]} {
339                    append actions($tval) $mesg \n
340                    set started(connect$cid-time) $tval
341                    set started(connect$cid-addr) ?
342
343                } elseif {[regexp {dropped: +([^ ]+) +\((.+)\)} $mesg match addr cid] && [info exists started(connect$cid-time)]} {
344                    append actions($tval) $mesg \n
345                    set from $nodes($fname)
346                    set x0 $nodes($from-x)
347                    set y0 $nodes($from-y)
348                    set x1 $nodes($addr-x)
349                    set y1 $nodes($addr-y)
350                    set s [Shape ::#auto .diagram.traffic -command \
351                        [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
352                    $s addRange $started(connect$cid-time) $tval
353
354                    unset started(connect$cid-time)
355                    unset started(connect$cid-addr)
356
357                } elseif {[regexp {server message from ([a-zA-Z0-9\.]+:[0-9]+) \(([a-z0-9]+)\): +(.+) => (.*)} $mesg match addr cid cmd result]} {
358                    if {![string match identity* $cmd]} {
359                        append actions($tval) $mesg \n
360                        set from $nodes($fname)
361                        set x0 $nodes($from-x)
362                        set y0 $nodes($from-y)
363                        set x1 $nodes($addr-x)
364                        set y1 $nodes($addr-y)
365                        set w [expr {[winfo width .diagram]/2}]
366
367                        set s [Shape ::#auto .diagram.traffic -command \
368                            [list %c create line [expr $x0-3] [expr $y0-3] [expr $x1-3] [expr $y1-3] -fill black -arrow first -tags transient]]
369                        $s addRange $tval $tval
370
371                        set s [Shape ::#auto .diagram.traffic -command \
372                            [list %c create text [expr {0.5*($x0+$x1)}] [expr {0.5*($y0+$y1)-1}] -width $w -fill black -anchor s -text $cmd -tags transient]]
373                        $s addRange $tval $tval
374
375                        if {"" != [string trim $result]} {
376                            if {[regexp {^ok:} $result]} {
377                                set color black
378                            } else {
379                                set color red
380                            }
381                            set s [Shape ::#auto .diagram.traffic -command \
382                                [list %c create line [expr $x0+3] [expr $y0+3] [expr $x1+3] [expr $y1+3] -fill $color -arrow last -tags transient]]
383                            $s addRange $tval $tval
384
385                            set s [Shape ::#auto .diagram.traffic -command \
386                                [list %c create text [expr {0.5*($x0+$x1)}] [expr {0.5*($y0+$y1)+1}] -width $w -fill $color -anchor n -text $result -tags transient]]
387                            $s addRange $tval $tval
388                        }
389                    }
390
391                    # no address for this client yet?  then save this info
392                    if {[info exists started(connect$cid-addr)]
393                           && $started(connect$cid-addr) == "?"} {
394                        set started(connect$cid-addr) $addr
395                    }
396
397                } elseif {[regexp {connected to peers: (.*)} $mesg match plist]} {
398                    # draw a highlight ring, indicating that peers have changed
399                    set from $nodes($fname)
400                    set x $nodes($from-x)
401                    set y $nodes($from-y)
402                    foreach canv {.diagram.traffic .diagram.network} {
403                        set s [Shape ::#auto $canv -command \
404                            [list %c create oval [expr {$x-$r-3}] [expr {$y-$r-3}] [expr {$x+$r+3}] [expr {$y+$r+3}] -outline red -fill "" -tags transient]]
405                        $s addRange $tval [expr {$tval+20}]
406                    }
407
408                    # draw lines from the previous peer list, now that
409                    # we know it's time range is done.
410                    foreach addr $peerlist(addrs) {
411                        set from $nodes($fname)
412                        set x0 $nodes($from-x)
413                        set y0 $nodes($from-y)
414                        set x1 $nodes($addr-x)
415                        set y1 $nodes($addr-y)
416                        set s [Shape ::#auto .diagram.network -command \
417                            [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
418                        $s addRange $peerlist(time) $tval
419                    }
420
421                    # save the start of this new peer list
422                    set peerlist(addrs) $plist
423                    set peerlist(time) $tval
424                }
425            }
426        }
427
428        # any connections hanging out that we haven't drawn?
429        foreach key [array names started connect*-addr] {
430            regexp {connect([^-]+)-addr} $key match cid
431            set from $nodes($fname)
432            set addr $started($key)
433            set x0 $nodes($from-x)
434            set y0 $nodes($from-y)
435            set x1 $nodes($addr-x)
436            set y1 $nodes($addr-y)
437            set s [Shape ::#auto .diagram.traffic -command \
438                [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
439            $s addRange $started(connect$cid-time) $tval
440
441            unset started(connect$cid-time)
442            unset started(connect$cid-addr)
443        }
444
445        .diagram.traffic bind $fname <Enter> "
446            %W itemconfigure $fname-node -outline red
447            %W itemconfigure $fname-cnx -fill red
448            %W raise $fname-cnx
449        "
450        .diagram.traffic bind $fname <Leave> "
451            %W itemconfigure $fname-node -outline black
452            %W itemconfigure $fname-cnx -fill gray
453            %W raise transient
454        "
455
456        foreach canv {.diagram.network .diagram.traffic} {
457            $canv bind $fname-node <ButtonPress-1> \
458                [list worker_node_click %W $fname %x %y]
459            $canv bind $fname-node <B1-Motion> \
460                [list worker_node_drag %W $fname %x %y]
461            $canv bind $fname-node <ButtonRelease-1> \
462                [list worker_node_release %W $fname %x %y]
463        }
464
465        # any peer connections that we haven't drawn?  then do it now
466        foreach addr $peerlist(addrs) {
467            set from $nodes($fname)
468            set x0 $nodes($from-x)
469            set y0 $nodes($from-y)
470            set x1 $nodes($addr-x)
471            set y1 $nodes($addr-y)
472            set s [Shape ::#auto .diagram.network -command \
473                [list %c create line $x0 $y0 $x1 $y1 -width 3 -fill gray -tags [list $fname $fname-cnx]]]
474            $s addRange $peerlist(time) $tmax
475        }
476
477        .diagram.network bind $fname <Enter> "
478            %W itemconfigure $fname-node -outline red
479            %W itemconfigure $fname-cnx -fill red
480            %W raise $fname-cnx
481        "
482        .diagram.network bind $fname <Leave> "
483            %W itemconfigure $fname-node -outline black
484            %W itemconfigure $fname-cnx -fill gray
485            %W raise transient
486        "
487    }
488    .player.scale configure -to $tmax
489
490    after cancel test_visualize
491    after idle test_visualize
492}
493
494proc worker_node_click {canv fname x y} {
495    $canv itemconfigure $fname-node -stipple gray50
496}
497
498proc worker_node_drag {canv fname x y} {
499    global nodeRadius
500    $canv itemconfigure $fname-node -stipple ""
501    $canv coords $fname-node [expr {$x-$nodeRadius}] [expr {$y-$nodeRadius}] [expr {$x+$nodeRadius}] [expr {$y+$nodeRadius}]
502}
503
504proc worker_node_release {canv fname x y} {
505    global move nodes
506
507    worker_node_drag $canv $fname $x $y
508    $canv itemconfigure $fname-node -stipple ""
509
510    set addr $nodes($fname)
511    set nodes($addr-x) $x
512    set nodes($addr-y) $y
513    after idle test_reload
514}
515
516proc test_frame_go {dir position} {
517    global actions
518
519    set tmax [.player.scale cget -to]
520    if {"nudge" == $position} {
521        set nframe [.player.scale get]
522        incr nframe $dir
523    } else {
524        set nframe $position
525    }
526
527    while {![info exists actions($nframe)] && $nframe > 0 && $nframe < $tmax} {
528        incr nframe $dir
529    }
530
531    # set the scale to this new position
532    .player.scale configure -command ""
533    .player.scale set $nframe
534    .player.scale configure -command {test_frame_go 1}
535
536    after cancel test_visualize
537    after idle test_visualize
538}
539
540proc test_visualize {} {
541    .diagram.traffic delete all
542    Shape::draw .diagram.traffic [.player.scale get]
543    .diagram.traffic raise transient
544
545    .diagram.network delete all
546    Shape::draw .diagram.network [.player.scale get]
547    .diagram.network raise transient
548}
549
550proc test_view_change {} {
551    global view
552    foreach widget [pack slaves .diagram] {
553        catch {pack forget $widget}
554    }
555    pack .diagram.$view -expand yes -fill both
556}
Note: See TracBrowser for help on using the repository browser.