source: branches/1.6/lang/tcl/scripts/task.tcl @ 6176

Last change on this file since 6176 was 6176, checked in by gah, 8 years ago

update w/ changes to cache hit reporting

File size: 30.6 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: task - represents the executable part of a tool
4#
5#  This object is an executable version of a Rappture xml file.
6#  A tool is a task plus its graphical user interface.  Each task
7#  resides in an installation directory with other tool resources
8#  (libraries, examples, etc.).  Each task is defined by its inputs
9#  and outputs, and understands the context in which it executes
10#  (via exec, submit, mx, etc.).
11# ======================================================================
12#  AUTHOR:  Michael McLennan, Purdue University
13#  Copyright (c) 2004-2014  HUBzero Foundation, LLC
14#
15#  See the file "license.terms" for information on usage and
16#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17# ======================================================================
18package require BLT
19
20itcl::class Rappture::Task {
21    private method CheckForCachedRunFile { driverFile }
22    private method CollectUQResults {}
23    private method ExecuteSimulationCommand { cmd }
24    private method GetCommand {}
25    private method GetDriverFile {}
26    private method GetSignal { signal }
27    private method GetSimulationCommand { driverFile }
28    private method GetUQSimulationCommand { driverFile }
29    private method GetUQTemplateFile {}
30    private method IsCacheable {}
31    private method LogCachedSimulationUsage {}
32    private method LogSimulationUsage {}
33    private method LogSubmittedSimulationUsage {}
34    private method SetCpuResourceLimit {}
35
36    public variable logger ""
37    public variable jobstats Rappture::Task::MiddlewareTime
38    public variable resultdir "@default"
39
40    constructor {xmlobj installdir args} { # defined below }
41    destructor { # defined below }
42
43    public method installdir {} { return $_installdir }
44
45    public method run {args}
46    public method get_uq {args}
47    public method abort {}
48    public method reset {}
49    public method xml {args}
50    public method save {xmlobj {name ""}}
51
52    protected method OnOutput {data}
53    protected method Log {args}
54    protected method BuildSubmitCommand {cmd tfile params_file}
55    protected method GetParamsForUQ {}
56
57    private variable _xmlobj ""      ;# XML object with inputs/outputs
58    private variable _origxml ""     ;# copy of original XML (for reset)
59    private variable _installdir ""  ;# installation directory for this tool
60    private variable _outputcb ""    ;# callback for tool output
61    private common jobnum 0          ;# counter for unique job number
62    private variable _uq
63   
64    private variable _job
65   
66    # get global resources for this tool session
67    public proc resources {{option ""}}
68
69    public common _resources
70    public proc setAppName {name}   { set _resources(-appname) $name }
71    public proc setHubName {name}   { set _resources(-hubname) $name }
72    public proc setHubURL {name}    { set _resources(-huburl) $name }
73    public proc setSession {name}   { set _resources(-session) $name }
74    public proc setJobPrt {name}    { set _resources(-jobprotocol) $name }
75    public proc setResultDir {name} { set _resources(-resultdir) $name }
76    public proc setCacheHost {name} { set _resources(-cachehost) $name }
77
78    # default method for -jobstats control
79    public proc MiddlewareTime {args}
80}
81
82# must use this name -- plugs into Rappture::resources::load
83proc task_init_resources {} {
84    Rappture::resources::register \
85        application_name  Rappture::Task::setAppName \
86        application_id    Rappture::Task::setAppId \
87        hub_name          Rappture::Task::setHubName \
88        hub_url           Rappture::Task::setHubURL \
89        session_token     Rappture::Task::setSession \
90        job_protocol      Rappture::Task::setJobPrt \
91        results_directory Rappture::Task::setResultDir \
92        cache_host        Rappture::Task::setCacheHost
93}
94
95# ----------------------------------------------------------------------
96# CONSTRUCTOR
97# ----------------------------------------------------------------------
98itcl::body Rappture::Task::constructor {xmlobj installdir args} {
99    if {![Rappture::library isvalid $xmlobj]} {
100        error "bad value \"$xmlobj\": should be Rappture::Library"
101    }
102    set _xmlobj $xmlobj
103
104    # stash a copy of the original XML for later "reset" operations
105    set _origxml [Rappture::LibraryObj ::#auto "<?xml version=\"1.0\"?><run/>"]
106    $_origxml copy "" from $_xmlobj ""
107
108    if {![file exists $installdir]} {
109        error "directory \"$installdir\" doesn't exist"
110    }
111    set _installdir $installdir
112
113    eval configure $args
114}
115
116# ----------------------------------------------------------------------
117# DESTRUCTOR
118# ----------------------------------------------------------------------
119itcl::body Rappture::Task::destructor {} {
120    itcl::delete object $_origxml
121}
122
123# ----------------------------------------------------------------------
124# USAGE: resources ?-option?
125#
126# Clients use this to query information about the tool.
127# ----------------------------------------------------------------------
128itcl::body Rappture::Task::resources {{option ""}} {
129    if {$option == ""} {
130        return [array get _resources]
131    }
132    if {[info exists _resources($option)]} {
133        return $_resources($option)
134    }
135    return ""
136}
137
138itcl::body Rappture::Task::GetSignal {code} {
139    set signals {
140        xxx HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV
141        USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN
142        TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS
143        RTMIN RTMIN+1 RTMIN+2 RTMIN+3 RTMAX-3 RTMAX-2 RTMAX-1 RTMAX
144    }
145    set sigNum [expr $code - 128]
146    if { $sigNum > 0 && $sigNum < [llength $signals] } {
147        return [lindex $signals $sigNum]
148    }
149    return "unknown exit code \"$code\""
150}
151
152itcl::body Rappture::Task::get_uq {args} {
153    foreach {path val} $args {
154        if {$path == "-uq_type"} {
155            set _uq(type) $val
156        } elseif {$path == "-uq_args"} {
157            set _uq(args) $val
158        }
159    }
160    #set varlist [$_xmlobj uq_get_vars]
161    foreach {varlist num} [$_xmlobj uq_get_vars] break
162    return [Rappture::UQ ::#auto $varlist $num $_uq(type) $_uq(args)]
163}
164
165# ----------------------------------------------------------------------
166# USAGE: run ?<path1> <value1> <path2> <value2> ...? ?-output <callbk>?
167#
168# This method causes the tool to run.  A "driver.xml" file is created
169# as the input for the run.  That file is fed to the executable
170# according to the <tool><command> string, and the job is executed.
171#
172# Any "<path> <value>" arguments are used to override the current
173# settings from the GUI.  This is useful, for example, when filling
174# in missing simulation results from the analyzer.
175#
176# If the -output argument is included, then the next arg is a
177# callback command for output messages.  Any output that comes in
178# while the tool is running is sent back to the caller, so the user
179# can see progress running the tool.
180#
181# Returns a list of the form {status result}, where status is an
182# integer status code (0=success) and result is the output from the
183# simulator.  Successful output is something like {0 run1293921.xml},
184# where 0=success and run1293921.xml is the name of the file containing
185# results.
186# ----------------------------------------------------------------------
187itcl::body Rappture::Task::run {args} {
188    global env errorInfo
189
190    #
191    # Make sure that we save the proper application name.  Actually, the
192    # best place to get this information is straight from the "installtool"
193    # script, but just in case we have an older tool, we should insert the
194    # tool name from the resources config file.
195    #
196    if {[info exists _resources(-appname)] && $_resources(-appname) ne "" &&
197        [$_xmlobj get tool.name] eq ""} {
198        $_xmlobj put tool.name $_resources(-appname)
199    }
200
201    # if there are any args, use them to override parameters
202    set _outputcb ""
203    set _uq(type) ""
204    foreach {path val} $args {
205        if {$path == "-output"} {
206            set _outputcb $val
207        } elseif {$path == "-uq_type"} {
208            set _uq(type) $val
209        } elseif {$path == "-uq_args"} {
210            set _uq(args) $val
211        } else {
212            $_xmlobj put $path.current $val
213        }
214    }
215
216    # Initialize job array variables
217    array set _job {
218        control ""
219        mesg    ""
220        runfile ""
221        stderr  ""
222        stdout  ""
223        success 0
224        xmlobj  ""
225    }
226
227    SetCpuResourceLimit
228    set driverFile [GetDriverFile]
229    set cached 0
230    if { [IsCacheable] } {
231puts stderr "Cache checking: [time {
232        set cached [CheckForCachedRunFile $driverFile]
233     } ]"
234puts stderr "checking cache=$cached"
235    }
236    if { !$cached } {
237puts stderr "1._uq(type)=$_uq(type)"
238        if { $_uq(type) != "" } {
239            set _uq(tfile) [GetUQTemplateFile]
240        }
241        if { $_uq(type) == "" } {
242            set cmd [GetSimulationCommand $driverFile]
243        } else {
244            set cmd [GetUQSimulationCommand $driverFile]
245        }
246puts stderr "2. cmd=$cmd"
247        if { $cmd == "" } {
248            puts stderr "cmd is empty"
249            append mesg "There is no command specified by\n\n"
250            append mesg "    <command>\n"
251            append mesg "    </command>\n\n"
252            append mesg "in the tool.xml file."
253            return [list 1 $mesg]
254        }
255        Rappture::rusage mark
256puts stderr "3. _uq(type)=$_uq(type)"
257        if { ![ExecuteSimulationCommand $cmd] } {
258puts stderr "4. job(mesg)=$_job(mesg)"
259            return [list 1 $_job(mesg)]
260        }
261puts stderr "5. _uq(type)=$_uq(type)"
262        if { [resources -jobprotocol] == "submit" } {
263            LogSubmittedSimulationUsage
264        } else {
265            LogSimulationUsage
266        }
267    } else {
268        LogCachedSimulationUsage
269    }
270    if { $_job(success) } {
271        file delete -force -- $driverFile
272        Log run finished
273        return [list 0 $_job(xmlobj)]
274    } else {
275        # See if the job was aborted.
276        if {[regexp {^KILLED} $_job(control)]} {
277            Log run aborted
278            return [list 1 "ABORT"]
279        }
280        Log run failed [list 0 $_job(mesg)]
281        return [list 1 $_job(mesg)]
282    }
283}
284
285# ----------------------------------------------------------------------
286#  Turn the command string from tool.xml into the proper syntax to use
287#  with a submit parameter sweep with a temlate file.  Proper quoting
288# of the template file is necessary to prevent submit from being too smart
289# and converting it to a full pathname.
290# ----------------------------------------------------------------------
291itcl::body Rappture::Task::BuildSubmitCommand {cmd tfile params_file} {
292    set quote_next 0
293    set newcmd "submit --progress submit --runName=puq -l -i @:$tfile -d $params_file"
294    set cmds [split $cmd " "]
295    for {set i 0} {$i < [llength $cmds]} {incr i} {
296        set arg [lindex $cmds $i]
297        if {$quote_next == 1} {
298            set nc [string range $arg 0 0]
299            if {$nc != "\""} {
300                set arg "\"\\\"$arg\\\"\""
301            }
302        }
303        if {$arg == "--eval"} {
304            set quote_next 1
305        } else {
306            set quote_next 0
307        }
308        if {$arg == "@driver"} {
309            set arg "\"\\\"$tfile\\\"\""
310        }
311        append newcmd " " $arg
312    }
313    regsub -all @driver $newcmd $tfile newcmd
314    return $newcmd
315}
316
317# ----------------------------------------------------------------------
318# USAGE: abort
319#
320# Clients use this during a "run" to abort the current job.
321# Kills the job and forces the "run" method to return.
322# ----------------------------------------------------------------------
323itcl::body Rappture::Task::abort {} {
324    Log run abort
325    set _job(control) "abort"
326}
327
328# ----------------------------------------------------------------------
329# USAGE: reset
330#
331# Resets all input values to their defaults.  Sometimes used just
332# before a run to reset to a clean state.
333# ----------------------------------------------------------------------
334itcl::body Rappture::Task::reset {} {
335    $_xmlobj copy "" from $_origxml ""
336    foreach path [Rappture::entities -as path $_xmlobj input] {
337        if {[$_xmlobj element -as type $path.default] ne ""} {
338            set defval [$_xmlobj get $path.default]
339            $_xmlobj put $path.current $defval
340        }
341    }
342}
343
344# ----------------------------------------------------------------------
345# USAGE: xml <subcommand> ?<arg> <arg> ...?
346# USAGE: xml object
347#
348# Used by clients to manipulate the underlying XML data for this
349# tool.  The <subcommand> can be any operation supported by a
350# Rappture::library object.  Clients can also request the XML object
351# directly by using the "object" subcommand.
352# ----------------------------------------------------------------------
353itcl::body Rappture::Task::xml {args} {
354    if {"object" == $args} {
355        return $_xmlobj
356    }
357    return [eval $_xmlobj $args]
358}
359
360# ----------------------------------------------------------------------
361# USAGE: save <xmlobj> ?<filename>?
362#
363# Used by clients to save the contents of an <xmlobj> representing
364# a run out to the given file.  If <filename> is not specified, then
365# it uses the -resultsdir and other settings to do what Rappture
366# would normally do with the output.
367# ----------------------------------------------------------------------
368itcl::body Rappture::Task::save {xmlobj {filename ""}} {
369    if {$filename eq ""} {
370
371        # If there's a results_directory defined in the resources file,
372        # then move the run.xml file there for storage.
373
374        set rdir ""
375        if {$resultdir eq "@default"} {
376            if {[info exists _resources(-resultdir)]} {
377                set rdir $_resources(-resultdir)
378            } else {
379                global rapptureInfo
380                set rdir $rapptureInfo(cwd)
381            }
382        } elseif {$resultdir ne ""} {
383            set rdir $resultdir
384        }
385
386        # use the runfile name generated by the last run
387        if {$_job(runfile) ne ""} {
388            set filename [file join $rdir $_job(runfile)]
389        } else {
390            set filename [file join $rdir run.xml]
391        }
392    }
393
394    # add any last-minute metadata
395    $xmlobj put output.time [clock format [clock seconds]]
396
397    $xmlobj put tool.version.rappture.version $::Rappture::version
398    $xmlobj put tool.version.rappture.revision $::Rappture::build
399    $xmlobj put output.filename $filename
400    $xmlobj put output.version $Rappture::version
401   
402    if {[info exists ::tcl_platform(user)]} {
403        $xmlobj put output.user $::tcl_platform(user)
404    }
405
406    # save the output
407    set rdir [file dirname $filename]
408    file mkdir $rdir
409
410    set fid [open $filename w]
411    puts $fid "<?xml version=\"1.0\"?>"
412    puts $fid [$xmlobj xml]
413    close $fid
414
415    Log output saved in $filename
416}
417
418# ----------------------------------------------------------------------
419# USAGE: OnOutput <data>
420#
421# Used internally to send each bit of output <data> coming from the
422# tool onto the caller, so the user can see progress.
423# ----------------------------------------------------------------------
424itcl::body Rappture::Task::OnOutput {data} {
425    if {[string length $_outputcb] > 0} {
426        uplevel #0 $_outputcb [list $data]
427    }
428}
429
430# ----------------------------------------------------------------------
431# USAGE: Log <cmd> <arg> <arg> ...
432#
433# Used internally to log interesting events during the run.  If the
434# -logger option is set (to Rappture::Logger::log, or something like
435# that), then the arguments to this method are passed along to the
436# logger and written out to a log file.  Logging is off by default,
437# so this method does nothing unless -logger is set.
438# ----------------------------------------------------------------------
439itcl::body Rappture::Task::Log {args} {
440    if {[string length $logger] > 0} {
441        uplevel #0 $logger [list $args]
442    }
443}
444
445# ----------------------------------------------------------------------
446# USAGE: MiddlewareTime <key> <value> ...
447#
448# Used as the default method for reporting job status information.
449# Implements the old HUBzero method of reporting job status info to
450# stderr, which can then be picked up by the tool session container.
451# Most tools use the "submit" command, which talks directly to a
452# database to log job information, so this isn't really needed.  But
453# it doesn't hurt to have this and it can be useful in some cases.
454# ----------------------------------------------------------------------
455itcl::body Rappture::Task::MiddlewareTime {args} {
456    set line "MiddlewareTime:"
457    foreach {key val} $args {
458        append line " $key=$val"
459    }
460    puts stderr $line
461}
462
463itcl::body Rappture::Task::IsCacheable {} {
464    if { ![info exists _resources(-cachehost)] ||
465         $_resources(-cachehost) == "" } {
466        return 0
467    }
468if 0 {
469    set state [$_xmlobj get "tool.cache"]
470    if { $state == "" } {
471        return 0
472    }
473} else {
474    set state 1
475}
476    if { ![string is boolean $state] } {
477        return 0
478    }
479    return $state
480}
481
482#
483# Send the list of parameters to a python program so it can call PUQ
484# and get a CSV file containing the parameter values to use for the runs.
485itcl::body Rappture::Task::GetParamsForUQ {} {
486    set pid [pid]
487    # puts "puq get_params $pid $_uq(varlist) $_uq(type) $_uq(args)"
488    if {[catch {
489        exec puq get_params $pid $_uq(varlist) $_uq(type) $_uq(args)
490    } errs] != 0 } {
491        set fp [open "uq_debug.err" r]
492        set contents [read $fp]
493        close $fp
494        puts stderr "get_params.py failed: $contents"
495        error "get_params.py: $errs\n$contents"
496    }
497    return "params[pid].csv"
498}
499
500itcl::body Rappture::Task::SetCpuResourceLimit {} {
501    # Set limits for cpu time
502    set limit [$_xmlobj get tool.limits.cputime]
503    if { $limit == "unlimited" } {
504        set limit 43200;                # 12 hours
505    } else {
506        if { [scan $limit "%d" dum] != 1 } {
507            set limit 14400;            # 4 hours by default
508        } elseif { $limit > 43200 } {
509            set limit 43200;            # limit to 12 hrs.
510        } elseif { $limit < 10 } {
511            set limit 10;               # lower bound is 10 seconds.
512        }
513    }
514    Rappture::rlimit set cputime $limit
515}
516
517# Write out the driver.xml file for the tool
518itcl::body Rappture::Task::GetDriverFile {} {
519    global rapptureInfo
520    set fileName [file join $rapptureInfo(cwd) "driver[pid].xml"]
521    if { [catch {
522        set f [open $fileName w]
523        puts $f "<?xml version=\"1.0\"?>"
524        puts $f [$_xmlobj xml]
525        close $f
526    } errs] != 0 } {
527        error "can't create driver file \"$fileName\": $errs"
528    }
529    return $fileName
530}
531
532itcl::body Rappture::Task::GetCommand { } {
533    set cmd [$_xmlobj get tool.command]
534    regsub -all @tool $cmd $_installdir cmd
535    set cmd [string trimleft $cmd " "]
536    return $cmd
537}
538
539itcl::body Rappture::Task::GetSimulationCommand { driverFile } {
540    set cmd [GetCommand]
541    if { $cmd == "" } {
542        return ""
543    }
544    regsub -all @driver $cmd $driverFile cmd
545
546    switch -glob -- [resources -jobprotocol] {
547        "submit*" {
548            # if job_protocol is "submit", then use use submit command
549            set cmd "submit --local $cmd"
550        }
551        "mx" {
552            # metachory submission
553            set cmd "mx $cmd"
554        }
555        "exec" {
556            # default -- nothing special
557        }
558    }
559    return $cmd
560}
561
562itcl::body Rappture::Task::GetUQSimulationCommand { driverFile } {
563    set cmd [GetCommand]
564    if { $cmd == "" } {
565        return ""
566    }
567    set paramsFile [GetParamsForUQ]
568    set cmd [BuildSubmitCommand $cmd $_uq(tfile) $paramsFile]
569    file delete -force puq
570    return $cmd
571}
572
573itcl::body Rappture::Task::GetUQTemplateFile {} {
574    global rapptureInfo
575    # Copy xml into a new file
576    set templateFile [file join $rapptureInfo(cwd) "template[pid].xml"]
577    set fid [open $templateFile w]
578    puts $fid "<?xml version=\"1.0\"?>"
579    puts $fid [$_xmlobj xml]
580    close $fid
581
582    # Return a list of the UQ variables and their PDFs.
583    # Also turns $uq(tfile) into a template file.
584    set _uq(varlist) [lindex [$_xmlobj uq_get_vars $templateFile] 0]
585    set _uq(tfile) $templateFile
586    return $templateFile
587}
588
589itcl::body Rappture::Task::ExecuteSimulationCommand { cmd } {
590
591    set _job(runfile) ""
592    set _job(success) 0
593
594    # Step 1.  Write the command into the run file.
595    $_xmlobj put tool.execute $cmd
596
597    Log run started
598    Rappture::rusage mark
599
600    # Step 2.  Check if it is a special case "ECHO" command which always
601    #          succeeds.
602    if { [string compare -nocase -length 5 $cmd "ECHO "] == 0 } {
603        set _job(stdout) [string range $cmd 5 end]
604        set _job(success) 1
605        set _job(mesg) ""
606        return 1;                       # Success
607    }
608
609    # Step 3. Execute the command, collecting its stdout and stderr.
610    catch {
611        eval blt::bgexec [list [itcl::scope _job(control)]] \
612            -keepnewline yes \
613            -killsignal  SIGTERM \
614            -onoutput    [list [itcl::code $this OnOutput]] \
615            -output      [list [itcl::scope _job(stdout)]] \
616            -error       [list [itcl::scope _job(stderr)]] \
617            $cmd
618    } result
619puts stderr "stderr=$_job(stderr) stdout=$_job(stdout)"
620    # Step 4. Check the token and the exit code.
621    set logmesg $result
622    foreach { token pid code mesg } $_job(control) break
623    if { $token == "EXITED" } {
624        if { $code != 0 } {
625            # This means that the program exited normally but returned a
626            # non-zero exitcode.  Consider this an invalid result from the
627            # program.  Append the stderr from the program to the message.
628            if {$code > 128} {
629                set logmesg "Program signaled: signal was [GetSignal $code]"
630            } else {
631                set logmesg "Program finished: non-zero exit code is $code"
632            }
633            set _job(mesg) "$logmesg\n\n$_job(stderr)"
634            Log run failed [list $logmesg]
635            return 0;                   # Fail.
636        }
637        # Successful program termination with exit code of 0.
638    } elseif { $token == "abort" }  {
639        # The user pressed the abort button.
640       
641        set logmesg "Program terminated by user."
642        Log run failed [list $logmesg]
643        set _job(mesg) "$logmesg\n\n$_job(stdout)"
644        return 0;                       # Fail
645    } else {
646        # Abnormal termination
647       
648        set logmesg "Abnormal program termination:"
649        Log run failed [list $logmesg]
650        set _job(mesg) "$logmesg\n\n$_job(stdout)"
651        return 0;                       # Fail
652    }
653        if { $_uq(type) != "" } {
654            CollectUQResults
655        }
656    puts stderr "_job(stdout)=$_job(stdout) _job(stderr)=$_job(stderr)"
657    # Step 5. Look in stdout for the name of the run file.
658    set pattern {=RAPPTURE-RUN=>([^\n]+)}
659    if {![regexp $pattern $_job(stdout) match fileName]} {
660        set _job(mesg) "Can't find result file in output.\n"
661        append _job(mesg) "Did you call Rappture::result in your simulator?"
662        return 0;                       # Fail
663    }
664    set _job(runfile) $fileName
665    set _job(success) 1
666    set _job(mesg) $_job(stdout)
667    return 1;                           # Success
668}
669
670itcl::body Rappture::Task::LogSimulationUsage {} {
671    array set times [Rappture::rusage measure]
672
673    set toolId     [$_xmlobj get tool.id]
674    set toolVers   [$_xmlobj get tool.version.application.revision]
675    set simulation "simulation"
676    if { $toolId ne "" && $toolVers ne "" } {
677        set simulation "[pid]_${toolId}_r${toolVers}"
678    }
679   
680    # Need to save job info? then invoke the callback
681    if { [string length $jobstats] > 0} {
682        lappend args \
683            "job"      [incr jobnum] \
684            "event"    $simulation \
685            "start"    $times(start) \
686            "walltime" $times(walltime) \
687            "cputime"  $times(cputime) \
688            "status"   $_job(success)
689        uplevel #0 $jobstats $args
690    }
691       
692    #
693    # Scan through stderr channel and look for statements that
694    # represent grid jobs that were executed.  The statements look
695    # like this:
696    #
697    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
698    #
699   
700    set subjobs 0
701    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
702    while { [regexp -indices $pattern $_job(stderr) match] } {
703        foreach {p0 p1} $match break
704        if { [string index $_job(stderr) $p0] == "\n" } {
705            incr p0
706        }
707        array unset data
708        array set data {
709            job 1
710            event simulation
711            start 0
712            walltime 0
713            cputime 0
714            status 0
715        }
716        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
717            foreach {key val} [split $arg =] break
718            set data($key) $val
719        }
720        set data(job)   [expr { $jobnum + $data(job) }]
721        set data(event) "subsimulation"
722        set data(start) [expr { $times(start) + $data(start) }]
723       
724        set details ""
725        foreach key {job event start walltime cputime status} {
726            # Add required keys in a particular order
727            lappend details $key $data($key)
728            unset data($key)
729        }
730        foreach key [array names data] {
731            # Add anything else that the client gave -- venue, etc.
732            lappend details $key $data($key)
733        }
734       
735        if {[string length $jobstats] > 0} {
736            uplevel #0 $jobstats $details
737        }
738       
739        incr subjobs
740       
741        # Done -- remove this statement
742        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
743    }
744    incr jobnum $subjobs
745
746    # Add cputime info to run.xml file
747    if { [catch {
748        Rappture::library $_job(runfile)
749    } xmlobj] != 0 } {
750        error "Can't create rappture library: $xmlobj"
751    }
752    $xmlobj put output.walltime $times(walltime)
753    $xmlobj put output.cputime $times(cputime)
754    global env
755    if {[info exists env(SESSION)]} {
756        $xmlobj put output.session $env(SESSION)
757    }
758    set _job(xmlobj) $xmlobj
759}
760
761itcl::body Rappture::Task::LogSubmittedSimulationUsage {} {
762    array set times [Rappture::rusage measure]
763
764    set toolId     [$_xmlobj get tool.id]
765    set toolVers   [$_xmlobj get tool.version.application.revision]
766    set simulation "simulation"
767    if { $toolId ne "" && $toolVers ne "" } {
768        set simulation "[pid]_${toolId}_r${toolVers}"
769    }
770   
771    # Need to save job info? then invoke the callback
772    if { [string length $jobstats] > 0} {
773        lappend args \
774            "job"      [incr jobnum] \
775            "event"    $simulation \
776            "start"    $times(start) \
777            "walltime" $times(walltime) \
778            "cputime"  $times(cputime) \
779            "status"   $_job(success)
780        uplevel #0 $jobstats $args
781    }
782       
783    #
784    # Scan through stderr channel and look for statements that
785    # represent grid jobs that were executed.  The statements look
786    # like this:
787    #
788    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
789    #
790   
791    set subjobs 0
792    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
793    while { [regexp -indices $pattern $_job(stderr) match] } {
794        foreach {p0 p1} $match break
795        if { [string index $_job(stderr) $p0] == "\n" } {
796            incr p0
797        }
798        array unset data
799        array set data {
800            job 1
801            event simulation
802            start 0
803            walltime 0
804            cputime 0
805            status 0
806        }
807        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
808            foreach {key val} [split $arg =] break
809            set data($key) $val
810        }
811        set data(job)   [expr { $jobnum + $data(job) }]
812        set data(event) "subsimulation"
813        set data(start) [expr { $times(start) + $data(start) }]
814       
815        set details ""
816        foreach key {job event start walltime cputime status} {
817            # Add required keys in a particular order
818            lappend details $key $data($key)
819            unset data($key)
820        }
821        foreach key [array names data] {
822            # Add anything else that the client gave -- venue, etc.
823            lappend details $key $data($key)
824        }
825       
826        if {[string length $jobstats] > 0} {
827            uplevel #0 $jobstats $details
828        }
829       
830        incr subjobs
831       
832        # Done -- remove this statement
833        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
834    }
835    incr jobnum $subjobs
836
837    # Add cputime info to run.xml file
838    if { [catch {
839        Rappture::library $_job(runfile)
840    } xmlobj] != 0 } {
841        error "Can't create rappture library: $xmlobj"
842    }
843    set _job(xmlobj) $xmlobj
844}
845
846itcl::body Rappture::Task::LogCachedSimulationUsage {} {
847    if { [catch {
848        Rappture::library $_job(runfile)
849    } xmlobj] != 0 } {
850        error "Can't create rappture library: $xmlobj"
851    }
852    # Get the session from runfile
853    set session [$xmlobj get "output.session"]
854    if { [catch {exec submit --cache $session} result] != 0 } {
855       puts stderr "submit --cache failed: $result"
856    }
857    set _job(xmlobj) $xmlobj
858}
859
860
861itcl::body Rappture::Task::CheckForCachedRunFile { driverFile } {
862
863    # Read the driver file and collect its contents as the query.
864    set url http://$_resources(-cachehost)/cache/request
865    set f [open $driverFile "r"]
866    set query [read $f]
867    close $f
868
869    # Make the query
870    if { [catch {
871        http::geturl $url -query $query -timeout 60000 -binary yes
872    } token] != 0 } {
873        puts stderr "can't get query: token=$token"
874        return 0
875    }
876    # If the code isn't 200, we'll assume it's a cache miss.
877    if { [http::ncode $token] != 200} {
878        return 0
879    }
880    # Get contents of the run file.
881    set contents [http::data $token]
882    if { $contents == "" } {
883        return 0
884    }
885    # Create a new run.xml file and write the results into it.
886    set secs [clock seconds]
887    set millisecs [expr [clock clicks -milliseconds] % 1000]
888    set timestamp [format %d%03d%03d $secs $millisecs 0]
889
890    global rapptureInfo
891    set fileName [file join $rapptureInfo(cwd) "run${timestamp}.xml"]
892    set f [open $fileName "w"]
893    puts $f $contents
894    close $f
895    set _job(runfile) $fileName
896    set _job(success) 1
897    set _job(stderr) "Loading cached results\n"
898    OnOutput "Loading cached results\n"
899    return 1
900}
901
902# UQ. Collect data from all jobs and put it in one xml run file.
903itcl::body Rappture::Task::CollectUQResults {} {
904   puts stderr "CollectUQResult"
905    file delete -force -- "run_uq.xml"
906    global rapptureInfo
907    set puqFileName [file join $rapptureInfo(cwd) puq_[pid].hdf5]
908    if {[catch {
909        exec puq analyze $puqFileName
910    } fileName] != 0 } {
911   puts stderr "puq analyze failed: $fileName"
912        set f [open "uq_debug.err" r]
913        set rdata [read $f]
914        close $f
915        puts "PUQ analysis failed: $fileName\n$rdata"
916        error "UQ analysis failed: $fileName\n$rdata"
917    } else {
918        set _job(runfile) $fileName
919    }
920   puts stderr "runfile=$_job(runfile)"
921}
Note: See TracBrowser for help on using the repository browser.