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

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

fix new puq handling routine in task.tcl

File size: 30.3 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 } {
237        if { $_uq(type) != "" } {
238            set _uq(tfile) [GetUQTemplateFile]
239        }
240        if { $_uq(type) == "" } {
241            set cmd [GetSimulationCommand $driverFile]
242        } else {
243            set cmd [GetUQSimulationCommand $driverFile]
244        }
245        if { $cmd == "" } {
246            puts stderr "cmd is empty"
247            append mesg "There is no command specified by\n\n"
248            append mesg "    <command>\n"
249            append mesg "    </command>\n\n"
250            append mesg "in the tool.xml file."
251            return [list 1 $mesg]
252        }
253        Rappture::rusage mark
254        if { ![ExecuteSimulationCommand $cmd] } {
255            return [list 1 $_job(mesg)]
256        }
257        if { [resources -jobprotocol] == "submit" } {
258            LogSubmittedSimulationUsage
259        } else {
260            LogSimulationUsage
261        }
262    } else {
263        LogCachedSimulationUsage
264    }
265    if { $_job(success) } {
266        file delete -force -- $driverFile
267        Log run finished
268        return [list 0 $_job(xmlobj)]
269    } else {
270        # See if the job was aborted.
271        if {[regexp {^KILLED} $_job(control)]} {
272            Log run aborted
273            return [list 1 "ABORT"]
274        }
275        Log run failed [list 0 $_job(mesg)]
276        return [list 1 $_job(mesg)]
277    }
278}
279
280# ----------------------------------------------------------------------
281#  Turn the command string from tool.xml into the proper syntax to use
282#  with a submit parameter sweep with a temlate file.  Proper quoting
283# of the template file is necessary to prevent submit from being too smart
284# and converting it to a full pathname.
285# ----------------------------------------------------------------------
286itcl::body Rappture::Task::BuildSubmitCommand {cmd tfile params_file} {
287    set quote_next 0
288    set newcmd "submit --progress submit --runName=puq -l -i @:$tfile -d $params_file"
289    set cmds [split $cmd " "]
290    for {set i 0} {$i < [llength $cmds]} {incr i} {
291        set arg [lindex $cmds $i]
292        if {$quote_next == 1} {
293            set nc [string range $arg 0 0]
294            if {$nc != "\""} {
295                set arg "\"\\\"$arg\\\"\""
296            }
297        }
298        if {$arg == "--eval"} {
299            set quote_next 1
300        } else {
301            set quote_next 0
302        }
303        if {$arg == "@driver"} {
304            set arg "\"\\\"$tfile\\\"\""
305        }
306        append newcmd " " $arg
307    }
308    regsub -all @driver $newcmd $tfile newcmd
309    return $newcmd
310}
311
312# ----------------------------------------------------------------------
313# USAGE: abort
314#
315# Clients use this during a "run" to abort the current job.
316# Kills the job and forces the "run" method to return.
317# ----------------------------------------------------------------------
318itcl::body Rappture::Task::abort {} {
319    Log run abort
320    set _job(control) "abort"
321}
322
323# ----------------------------------------------------------------------
324# USAGE: reset
325#
326# Resets all input values to their defaults.  Sometimes used just
327# before a run to reset to a clean state.
328# ----------------------------------------------------------------------
329itcl::body Rappture::Task::reset {} {
330    $_xmlobj copy "" from $_origxml ""
331    foreach path [Rappture::entities -as path $_xmlobj input] {
332        if {[$_xmlobj element -as type $path.default] ne ""} {
333            set defval [$_xmlobj get $path.default]
334            $_xmlobj put $path.current $defval
335        }
336    }
337}
338
339# ----------------------------------------------------------------------
340# USAGE: xml <subcommand> ?<arg> <arg> ...?
341# USAGE: xml object
342#
343# Used by clients to manipulate the underlying XML data for this
344# tool.  The <subcommand> can be any operation supported by a
345# Rappture::library object.  Clients can also request the XML object
346# directly by using the "object" subcommand.
347# ----------------------------------------------------------------------
348itcl::body Rappture::Task::xml {args} {
349    if {"object" == $args} {
350        return $_xmlobj
351    }
352    return [eval $_xmlobj $args]
353}
354
355# ----------------------------------------------------------------------
356# USAGE: save <xmlobj> ?<filename>?
357#
358# Used by clients to save the contents of an <xmlobj> representing
359# a run out to the given file.  If <filename> is not specified, then
360# it uses the -resultsdir and other settings to do what Rappture
361# would normally do with the output.
362# ----------------------------------------------------------------------
363itcl::body Rappture::Task::save {xmlobj {filename ""}} {
364    if {$filename eq ""} {
365
366        # If there's a results_directory defined in the resources file,
367        # then move the run.xml file there for storage.
368
369        set rdir ""
370        if {$resultdir eq "@default"} {
371            if {[info exists _resources(-resultdir)]} {
372                set rdir $_resources(-resultdir)
373            } else {
374                global rapptureInfo
375                set rdir $rapptureInfo(cwd)
376            }
377        } elseif {$resultdir ne ""} {
378            set rdir $resultdir
379        }
380
381        # use the runfile name generated by the last run
382        if {$_job(runfile) ne ""} {
383            set filename [file join $rdir $_job(runfile)]
384        } else {
385            set filename [file join $rdir run.xml]
386        }
387    }
388
389    # add any last-minute metadata
390    $xmlobj put output.time [clock format [clock seconds]]
391
392    $xmlobj put tool.version.rappture.version $::Rappture::version
393    $xmlobj put tool.version.rappture.revision $::Rappture::build
394    $xmlobj put output.filename $filename
395    $xmlobj put output.version $Rappture::version
396   
397    if {[info exists ::tcl_platform(user)]} {
398        $xmlobj put output.user $::tcl_platform(user)
399    }
400
401    # save the output
402    set rdir [file dirname $filename]
403    file mkdir $rdir
404
405    set fid [open $filename w]
406    puts $fid "<?xml version=\"1.0\"?>"
407    puts $fid [$xmlobj xml]
408    close $fid
409
410    Log output saved in $filename
411}
412
413# ----------------------------------------------------------------------
414# USAGE: OnOutput <data>
415#
416# Used internally to send each bit of output <data> coming from the
417# tool onto the caller, so the user can see progress.
418# ----------------------------------------------------------------------
419itcl::body Rappture::Task::OnOutput {data} {
420    if {[string length $_outputcb] > 0} {
421        uplevel #0 $_outputcb [list $data]
422    }
423}
424
425# ----------------------------------------------------------------------
426# USAGE: Log <cmd> <arg> <arg> ...
427#
428# Used internally to log interesting events during the run.  If the
429# -logger option is set (to Rappture::Logger::log, or something like
430# that), then the arguments to this method are passed along to the
431# logger and written out to a log file.  Logging is off by default,
432# so this method does nothing unless -logger is set.
433# ----------------------------------------------------------------------
434itcl::body Rappture::Task::Log {args} {
435    if {[string length $logger] > 0} {
436        uplevel #0 $logger [list $args]
437    }
438}
439
440# ----------------------------------------------------------------------
441# USAGE: MiddlewareTime <key> <value> ...
442#
443# Used as the default method for reporting job status information.
444# Implements the old HUBzero method of reporting job status info to
445# stderr, which can then be picked up by the tool session container.
446# Most tools use the "submit" command, which talks directly to a
447# database to log job information, so this isn't really needed.  But
448# it doesn't hurt to have this and it can be useful in some cases.
449# ----------------------------------------------------------------------
450itcl::body Rappture::Task::MiddlewareTime {args} {
451    set line "MiddlewareTime:"
452    foreach {key val} $args {
453        append line " $key=$val"
454    }
455    puts stderr $line
456}
457
458itcl::body Rappture::Task::IsCacheable {} {
459    if { ![info exists _resources(-cachehost)] ||
460         $_resources(-cachehost) == "" } {
461        return 0
462    }
463if 0 {
464    set state [$_xmlobj get "tool.cache"]
465    if { $state == "" } {
466        return 0
467    }
468} else {
469    set state 1
470}
471    if { ![string is boolean $state] } {
472        return 0
473    }
474    return $state
475}
476
477#
478# Send the list of parameters to a python program so it can call PUQ
479# and get a CSV file containing the parameter values to use for the runs.
480itcl::body Rappture::Task::GetParamsForUQ {} {
481    set pid [pid]
482    # puts "puq.sh get_params $pid $_uq(varlist) $_uq(type) $_uq(args)"
483    if {[catch {
484        exec puq.sh get_params $pid $_uq(varlist) $_uq(type) $_uq(args)
485    } errs] != 0 } {
486        set contents {}
487        if { [file exists "uq_debug.err"] } {
488            set f [open "uq_debug.err" r]
489            set contents [read $f]
490            close $f
491        }
492        puts stderr "get_params.py failed: $contents"
493        error "get_params.py failed: $errs\n$contents"
494    }
495    return "params[pid].csv"
496}
497
498itcl::body Rappture::Task::SetCpuResourceLimit {} {
499    # Set limits for cpu time
500    set limit [$_xmlobj get tool.limits.cputime]
501    if { $limit == "unlimited" } {
502        set limit 43200;                # 12 hours
503    } else {
504        if { [scan $limit "%d" dum] != 1 } {
505            set limit 14400;            # 4 hours by default
506        } elseif { $limit > 43200 } {
507            set limit 43200;            # limit to 12 hrs.
508        } elseif { $limit < 10 } {
509            set limit 10;               # lower bound is 10 seconds.
510        }
511    }
512    Rappture::rlimit set cputime $limit
513}
514
515# Write out the driver.xml file for the tool
516itcl::body Rappture::Task::GetDriverFile {} {
517    global rapptureInfo
518    set fileName [file join $rapptureInfo(cwd) "driver[pid].xml"]
519    if { [catch {
520        set f [open $fileName w]
521        puts $f "<?xml version=\"1.0\"?>"
522        puts $f [$_xmlobj xml]
523        close $f
524    } errs] != 0 } {
525        error "can't create driver file \"$fileName\": $errs"
526    }
527    return $fileName
528}
529
530itcl::body Rappture::Task::GetCommand { } {
531    set cmd [$_xmlobj get tool.command]
532    regsub -all @tool $cmd $_installdir cmd
533    set cmd [string trimleft $cmd " "]
534    return $cmd
535}
536
537itcl::body Rappture::Task::GetSimulationCommand { driverFile } {
538    set cmd [GetCommand]
539    if { $cmd == "" } {
540        return ""
541    }
542    regsub -all @driver $cmd $driverFile cmd
543
544    switch -glob -- [resources -jobprotocol] {
545        "submit*" {
546            # if job_protocol is "submit", then use use submit command
547            set cmd "submit --local $cmd"
548        }
549        "mx" {
550            # metachory submission
551            set cmd "mx $cmd"
552        }
553        "exec" {
554            # default -- nothing special
555        }
556    }
557    return $cmd
558}
559
560itcl::body Rappture::Task::GetUQSimulationCommand { driverFile } {
561    set cmd [GetCommand]
562    if { $cmd == "" } {
563        return ""
564    }
565    set paramsFile [GetParamsForUQ]
566    set cmd [BuildSubmitCommand $cmd $_uq(tfile) $paramsFile]
567    file delete -force puq
568    return $cmd
569}
570
571itcl::body Rappture::Task::GetUQTemplateFile {} {
572    global rapptureInfo
573    # Copy xml into a new file
574    set templateFile "template[pid].xml"
575    set f [open $templateFile w]
576    puts $f "<?xml version=\"1.0\"?>"
577    puts $f [$_xmlobj xml]
578    close $f
579
580    # Return a list of the UQ variables and their PDFs.
581    # Also turns $uq(tfile) into a template file.
582    set _uq(varlist) [lindex [$_xmlobj uq_get_vars $templateFile] 0]
583    set _uq(tfile) $templateFile
584    return $templateFile
585}
586
587itcl::body Rappture::Task::ExecuteSimulationCommand { cmd } {
588
589    set _job(runfile) ""
590    set _job(success) 0
591
592    # Step 1.  Write the command into the run file.
593    $_xmlobj put tool.execute $cmd
594
595    Log run started
596    Rappture::rusage mark
597
598    # Step 2.  Check if it is a special case "ECHO" command which always
599    #          succeeds.
600    if { [string compare -nocase -length 5 $cmd "ECHO "] == 0 } {
601        set _job(stdout) [string range $cmd 5 end]
602        set _job(success) 1
603        set _job(mesg) ""
604        return 1;                       # Success
605    }
606
607    # Step 3. Execute the command, collecting its stdout and stderr.
608    catch {
609        eval blt::bgexec [list [itcl::scope _job(control)]] \
610            -keepnewline yes \
611            -killsignal  SIGTERM \
612            -onoutput    [list [itcl::code $this OnOutput]] \
613            -output      [list [itcl::scope _job(stdout)]] \
614            -error       [list [itcl::scope _job(stderr)]] \
615            $cmd
616    } result
617
618    # Step 4. Check the token and the exit code.
619    set logmesg $result
620    foreach { token pid code mesg } $_job(control) break
621    if { $token == "EXITED" } {
622        if { $code != 0 } {
623            # This means that the program exited normally but returned a
624            # non-zero exitcode.  Consider this an invalid result from the
625            # program.  Append the stderr from the program to the message.
626            if {$code > 128} {
627                set logmesg "Program signaled: signal was [GetSignal $code]"
628            } else {
629                set logmesg "Program finished: non-zero exit code is $code"
630            }
631            set _job(mesg) "$logmesg\n\n$_job(stderr)"
632            Log run failed [list $logmesg]
633            return 0;                   # Fail.
634        }
635        # Successful program termination with exit code of 0.
636    } elseif { $token == "abort" }  {
637        # The user pressed the abort button.
638       
639        set logmesg "Program terminated by user."
640        Log run failed [list $logmesg]
641        set _job(mesg) "$logmesg\n\n$_job(stdout)"
642        return 0;                       # Fail
643    } else {
644        # Abnormal termination
645       
646        set logmesg "Abnormal program termination:"
647        Log run failed [list $logmesg]
648        set _job(mesg) "$logmesg\n\n$_job(stdout)"
649        return 0;                       # Fail
650    }
651    if { $_uq(type) != "" } {
652        CollectUQResults
653    }
654
655    # Step 5. Look in stdout for the name of the run file.
656    set pattern {=RAPPTURE-RUN=>([^\n]+)}
657    if {![regexp $pattern $_job(stdout) match fileName]} {
658        set _job(mesg) "Can't find result file in output.\n"
659        append _job(mesg) "Did you call Rappture::result in your simulator?"
660        return 0;                       # Fail
661    }
662    set _job(runfile) $fileName
663    set _job(success) 1
664    set _job(mesg) $_job(stdout)
665    return 1;                           # Success
666}
667
668itcl::body Rappture::Task::LogSimulationUsage {} {
669    array set times [Rappture::rusage measure]
670
671    set toolId     [$_xmlobj get tool.id]
672    set toolVers   [$_xmlobj get tool.version.application.revision]
673    set simulation "simulation"
674    if { $toolId ne "" && $toolVers ne "" } {
675        set simulation "[pid]_${toolId}_r${toolVers}"
676    }
677   
678    # Need to save job info? then invoke the callback
679    if { [string length $jobstats] > 0} {
680        lappend args \
681            "job"      [incr jobnum] \
682            "event"    $simulation \
683            "start"    $times(start) \
684            "walltime" $times(walltime) \
685            "cputime"  $times(cputime) \
686            "status"   $_job(success)
687        uplevel #0 $jobstats $args
688    }
689       
690    #
691    # Scan through stderr channel and look for statements that
692    # represent grid jobs that were executed.  The statements look
693    # like this:
694    #
695    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
696    #
697   
698    set subjobs 0
699    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
700    while { [regexp -indices $pattern $_job(stderr) match] } {
701        foreach {p0 p1} $match break
702        if { [string index $_job(stderr) $p0] == "\n" } {
703            incr p0
704        }
705        array unset data
706        array set data {
707            job 1
708            event simulation
709            start 0
710            walltime 0
711            cputime 0
712            status 0
713        }
714        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
715            foreach {key val} [split $arg =] break
716            set data($key) $val
717        }
718        set data(job)   [expr { $jobnum + $data(job) }]
719        set data(event) "subsimulation"
720        set data(start) [expr { $times(start) + $data(start) }]
721       
722        set details ""
723        foreach key {job event start walltime cputime status} {
724            # Add required keys in a particular order
725            lappend details $key $data($key)
726            unset data($key)
727        }
728        foreach key [array names data] {
729            # Add anything else that the client gave -- venue, etc.
730            lappend details $key $data($key)
731        }
732       
733        if {[string length $jobstats] > 0} {
734            uplevel #0 $jobstats $details
735        }
736       
737        incr subjobs
738       
739        # Done -- remove this statement
740        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
741    }
742    incr jobnum $subjobs
743
744    # Add cputime info to run.xml file
745    if { [catch {
746        Rappture::library $_job(runfile)
747    } xmlobj] != 0 } {
748        error "Can't create rappture library: $xmlobj"
749    }
750    $xmlobj put output.walltime $times(walltime)
751    $xmlobj put output.cputime $times(cputime)
752    global env
753    if {[info exists env(SESSION)]} {
754        $xmlobj put output.session $env(SESSION)
755    }
756    set _job(xmlobj) $xmlobj
757}
758
759itcl::body Rappture::Task::LogSubmittedSimulationUsage {} {
760    array set times [Rappture::rusage measure]
761
762    set toolId     [$_xmlobj get tool.id]
763    set toolVers   [$_xmlobj get tool.version.application.revision]
764    set simulation "simulation"
765    if { $toolId ne "" && $toolVers ne "" } {
766        set simulation "[pid]_${toolId}_r${toolVers}"
767    }
768   
769    # Need to save job info? then invoke the callback
770    if { [string length $jobstats] > 0} {
771        lappend args \
772            "job"      [incr jobnum] \
773            "event"    $simulation \
774            "start"    $times(start) \
775            "walltime" $times(walltime) \
776            "cputime"  $times(cputime) \
777            "status"   $_job(success)
778        uplevel #0 $jobstats $args
779    }
780       
781    #
782    # Scan through stderr channel and look for statements that
783    # represent grid jobs that were executed.  The statements look
784    # like this:
785    #
786    # MiddlewareTime: job=1 event=simulation start=3.001094 ...
787    #
788   
789    set subjobs 0
790    set pattern {(^|\n)MiddlewareTime:( +[a-z]+=[^ \n]+)+(\n|$)}
791    while { [regexp -indices $pattern $_job(stderr) match] } {
792        foreach {p0 p1} $match break
793        if { [string index $_job(stderr) $p0] == "\n" } {
794            incr p0
795        }
796        array unset data
797        array set data {
798            job 1
799            event simulation
800            start 0
801            walltime 0
802            cputime 0
803            status 0
804        }
805        foreach arg [lrange [string range $_job(stderr) $p0 $p1] 1 end] {
806            foreach {key val} [split $arg =] break
807            set data($key) $val
808        }
809        set data(job)   [expr { $jobnum + $data(job) }]
810        set data(event) "subsimulation"
811        set data(start) [expr { $times(start) + $data(start) }]
812       
813        set details ""
814        foreach key {job event start walltime cputime status} {
815            # Add required keys in a particular order
816            lappend details $key $data($key)
817            unset data($key)
818        }
819        foreach key [array names data] {
820            # Add anything else that the client gave -- venue, etc.
821            lappend details $key $data($key)
822        }
823       
824        if {[string length $jobstats] > 0} {
825            uplevel #0 $jobstats $details
826        }
827       
828        incr subjobs
829       
830        # Done -- remove this statement
831        set _job(stderr) [string replace $_job(stderr) $p0 $p1]
832    }
833    incr jobnum $subjobs
834
835    # Add cputime info to run.xml file
836    if { [catch {
837        Rappture::library $_job(runfile)
838    } xmlobj] != 0 } {
839        error "Can't create rappture library: $xmlobj"
840    }
841    set _job(xmlobj) $xmlobj
842}
843
844itcl::body Rappture::Task::LogCachedSimulationUsage {} {
845    if { [catch {
846        Rappture::library $_job(runfile)
847    } xmlobj] != 0 } {
848        error "Can't create rappture library: $xmlobj"
849    }
850    # Get the session from runfile
851    set session [$xmlobj get "output.session"]
852    if { [catch {exec submit --cache $session} result] != 0 } {
853       puts stderr "submit --cache failed: $result"
854    }
855    set _job(xmlobj) $xmlobj
856}
857
858
859itcl::body Rappture::Task::CheckForCachedRunFile { driverFile } {
860
861    # Read the driver file and collect its contents as the query.
862    set url http://$_resources(-cachehost)/cache/request
863    set f [open $driverFile "r"]
864    set query [read $f]
865    close $f
866
867    # Make the query
868    if { [catch {
869        http::geturl $url -query $query -timeout 60000 -binary yes
870    } token] != 0 } {
871        puts stderr "error performing cache query: token=$token"
872        return 0
873    }
874    # If the code isn't 200, we'll assume it's a cache miss.
875    if { [http::ncode $token] != 200} {
876        return 0
877    }
878    # Get contents of the run file.
879    set contents [http::data $token]
880    if { $contents == "" } {
881        return 0
882    }
883
884    # Create a new run.xml file and write the results into it.
885    set secs [clock seconds]
886    set millisecs [expr [clock clicks -milliseconds] % 1000]
887    set timestamp [format %d%03d%03d $secs $millisecs 0]
888
889    global rapptureInfo
890    set fileName [file join $rapptureInfo(cwd) "run${timestamp}.xml"]
891    set f [open $fileName "w"]
892    puts $f $contents
893    close $f
894    set _job(runfile) $fileName
895    set _job(success) 1
896    set _job(stderr) "Loading cached results\n"
897    OnOutput "Loading cached results\n"
898    return 1
899}
900
901# UQ. Collect data from all jobs and put it in one xml run file.
902itcl::body Rappture::Task::CollectUQResults {} {
903    file delete -force -- "run_uq.xml"
904    set hdfFile puq_[pid].hdf5
905    if { [catch {
906        exec puq.sh analyze $hdfFile
907    } results] != 0 } {
908        set rdata {}
909        if { [file exists "uq_debug.err" ] } {
910            set f [open "uq_debug.err" r]
911            set rdata [read $f]
912            close $f
913        }
914        puts stderr "UQ analysis failed: $results\n$rdata"
915        error "UQ analysis failed: $results\n$rdata"
916    } else {
917        set _job(stdout) $results
918    }
919}
Note: See TracBrowser for help on using the repository browser.