The tutorials presented here are designed to help new users become familiar with the Metronome software. You will need to get an account on the UW-Madison Build & Test Lab in order to try out these examples.
This tutorial is also available as a single, printer-friendly document here.
You will need a terminal window on a UW NMI Lab submit host, and a browser which can view the B&T web status pages.
On the terminal window you will need to perform a few setup steps if you want to cut and paste directly from these pages.
bash$ export NMI_BIN=/nmi/bin
bash$ export NMI_LIB=/nmi/lib
bash$ source $NMI_BIN/config.[sh/csh]bash$ which nmi_submit
/nmi/bin/nmi_submitbash$ nmi_submit —help
Usage: /nmi/bin/nmi_submit —nmiconf= Select which NMI configuration file to use —must-match Job must match with resources before submitted —notify-fail-only Only send notification if job fails —verbose Enable verbose output —quiet Do not print job submission information —timeout= Number of seconds to wait for runid. Default is 180 —no-wait Do not wait for runid. Program returns immediately —debug Enable debug output —help Show this information
You should also point your browser to the Build & Test Overview page
As shown in the figure below, when the Build & Test Overview page is first displayed, the page list all of the submissions that have been run on the submit machines. The first thing that needs to be done is to filter the submissions down to a managable number using the search box. In this case the page displays 1109 submissions.
The submission results can be sorted by each column listed in the page that contains sort buttons. As shown in the previous picture, each column which can be sorted has two buttons which will sort the results in either descending or ascending order.
The search box is used to filter out unwanted submissions. The figure below shows the fields that can be used to filter the results:

The following picture shows an example details page for the build and test run:

This tutorial introduces the steps needed to run a B&T submission. The dissection sections explain the contents of the submit file and input file used in the exercise. The procedure and examining sections runs through the steps of the exercise. Two submit procedures are presented. The version using CVS input shows you how the build and test system retrieves code from your source repository. The version using SCP shows you how the build and test system can download input files from the submit machine. You should have already prepared for this tutorial by completing the preparations section.
This tutorial demonstrates a simple build and test run using the CVS input method. Download the attached files and move them to the working directory of your submit machine and then follow the sections below. The first two pages discuss the contents of the files you have downloaded. This is followed by the procedure to execute the run and examine the results.
The following specifies fields that identify the run so that it can be located on the Build & Test Overview page.
@project@ = tutorial
@component@ = perlHelloWorld
@component_version@ = 1.0.0
@description@ = This is a simple example
@run_type@ = build
This line points to the file perlHelloWorld.cvs for an input definition. The B&T system expects the file to be in the same directory as where nmi_submit is executed.
@inputs@ = perlHelloWorld.cvs
These lines specify that the command _“code/perlHelloWorld/helloWorld.pl Remote_Task Task”_ must be executed on each target platform.
@remote_task@ = code/perlHelloWorld/helloWorld.pl
@remote_task_args@ = Remote_Task Task
This line specifies that the run must be executed on a Fedora Core 3 system and a Sun Solaris 5.8 system.
@platforms@ = x86_fc_3, sun4u_sol_5.8
This line specifies where the B&T system should send the job completion message.
@notify@ =
The first line specifies the way the software is transferred to the submit machine.
@method@ = cvs
For the cvs method the root and module need to be specified. Note that on the target machine, the path to the software will be the same as the module name. In this case the software will be found in the directory userdir/code/perlHelloWorld.
@cvs_root@ = /nmi/tutorial-cvs
@cvs_module@ = code/perlHelloWorld
notify = < your @ email.address >
bash$ nmi_submit perlHelloWorld.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201015858_4220
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201015858_4220
Run ID: 72218
The output provided by nmi_submit shows some important information about your run. The first line gives you a unique global identifier ( _tutorial_nmi-s005.cs.wisc.edu_1201015858_4220_ ) for the job. The second line points to the directory path ( _/nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201015858_4220_ ) where the submission results are stored. The third line shows the condor run id ( 72211 )which is used by the Build & Test Overview page.
This tutorial demonstrates a simple build and test run using the SCP input method. Download the attached files and move them to the working directory of your submit machine and then follow the sections below. The first two pages discuss the contents of the files you have downloaded. This is followed by the procedure to execute the run and examine the results.
The following specifies fields that identify the run so that it can be located on the Build & Test Overview page.
@project@ = tutorial
@component@ = perlHelloWorld
@component_version@ = 1.0.0
@description@ = This is a simple example
@run_type@ = build
This line points to the file perlHelloWorld.scp for an input definition. The B&T system expects the file to be in the same directory as where nmi_submit is executed.
@inputs@ = perlHelloWorld.scp
These lines specify that the command _“helloWorld.pl Remote_Task Task”_ must be executed on each target platform.
@remote_task@ = helloWorld.pl
@remote_task_args@ = Remote_Task Task
This line specifies that the run must be executed on a Fedora Core 3 system and a Sun Solaris 5.8 system.
@platforms@ = x86_fc_3, sun4u_sol_5.8
This line specifies where the B&T system should send the job completion message.
@notify@ =
The first line specifies the way the software is transferred to the submit machine.
@method@ = scp
The scp method downloads files from the submit machine. In this case we want it to download the hello world perl script included in the attachments you downloaded into the working directory of the submit machine.
@scp_file@ = /YOUR/WORKING/DIRECTORY/helloWorld.pl
notify = < your @ email.address >
bash$ nmi_submit perlHelloWorld.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201016619_4691
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201016619_4691
Run ID: 72219
The output provided by nmi_submit shows some important information about your run. The first line gives you a unique global identifier ( _tutorial_nmi-s005.cs.wisc.edu_1201016619_4691_ ) for the job. The second line points to the directory path ( _/nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201016619_4691_ ) where the submission results are stored. The third line shows the condor run id ( 72219 )which is used by the Build & Test Overview page.
Here are two ways to look at the results of a build and test run. The first takes a look at the run results directory on the submit machine. The second way uses the build and test overview web page.
bash$ cd /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201013789_32264bash$ ls
bash$ cd userdir/bash$ lscommon sun4u_sol_5.8 x86_fc_3common nmi:sun4u_sol_5.8 nmi:x86_fc_3
bash$ cd sun4u_sol_5.8/bash$ lscode remote_task.err remote_task.out
bash$ cat remote_task.outHello World from "Remote_Task Task"
The fields for the most part match either what was in the build and test submit file or what nmi_submit returned:
This tutorial demonstrates a couple of variations of the standard fetch used by most of the other tutorials. First a tag is added to the cvs method defined in the Hello World Tutorial. Then the cvs method is replaced by the ftp method.
cvs_tag = helloBranch
bash$ nmi_submit perlHelloWorld.submit
Doing a cvs check out
U code/perlHelloWorld/.project
U code/perlHelloWorld/helloWorld.pl
system('cvs co -r helloBranch code/perlHelloWorld ') exited with value 0
= ftp
@ftp_root@ = ftp://ftp.cs.wisc.edu/condor/nmi/tutorial/
@ftp_target@ = helloWorld.tar.gz
inputs = perlHelloWorld.cvs to inputs = perlHelloWorld.ftp.bash$ nmi_submit perlHelloWorld.submit
Doing an ftp check out
system('wget ftp://ftp.cs.wisc.edu/condor/nmi/tutorials/helloWorld.tar.gz ') exited with value 0
This tutorial demonstrates the concept of tasks hooks. Tasks hooks are associated with the various stages of a build and test run. This section explains where these stages occur. This tutorial will show you how to use these hooks and how to examine the results of the tasks that are run.
The following shows the submit file used to demonstrate task hooks. The task hook commands highlighted in blue. The task hook remote_task was used in the Hello World tutorial.
@project@ = tutorial
@component@ = whereAmI
@description@ = Example to demonstrate task hooks
@run_type@ = build
@inputs@ = whereAmI.cvs
The following task hooks are executed on the submit machine
{color: blue}@pre_all@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@pre_all_args@ = Pre_All Task
{color: blue}@platform_pre@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@platform_pre_args@ = platform_Pre Task
{color: blue}@platform_post@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@platform_post_args@ = platform_Post Task
{color: blue}@post_all@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@post_all_args@ = Post_All Task
The following task hooks are executed on each platform.
{color: blue}@remote_pre_declare@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@remote_pre_declare_args@ = Remote_Pre_Declare Task
{color: blue}@remote_declare@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@remote_declare_args@ = Remote_Declare Task
{color: blue}@remote_pre@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@remote_pre_args@ = Remote_Pre Task
{color: blue}@remote_task@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@remote_task_args@ = Remote_Task Task
{color: blue}@remote_post@ = code/perlWhereAmI/whereAmI.pl
{color: blue}@remote_post_args@ = Remote_Post Task
@platforms@ = x86_fc_3, sun4u_sol_5.8
notify = < your @ email.address >notify = <your@email.address>
bash$ nmi_submit whereAmI.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201019820_7684Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201019820_7684Run ID: 72223bash$ cd /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201019820_7684/
bash$ ls platform_pre.*
platform_pre.sun4u_sol_5.8.err platform_pre.sun4u_sol_5.8.sub platform_pre.x86_fc_3.out
platform_pre.sun4u_sol_5.8.out platform_pre.x86_fc_3.err platform_pre.x86_fc_3.sub
OR
platform_pre.nmi:sun4u_sol_5.8.err platform_pre.nmi:sun4u_sol_5.8.sub platform_pre.nmi:x86_fc_3.out
platform_pre.nmi:sun4u_sol_5.8.out platform_pre.nmi:x86_fc_3.err platform_pre.nmi:x86_fc_3.sub
bash$ grep 'Time now' *.out
platform_post.sun4u_sol_5.8.out: Time now is 14:21:28
platform_post.x86_fc_3.out: Time now is 14:21:06
platform_pre.sun4u_sol_5.8.out: Time now is 14:20:27
platform_pre.x86_fc_3.out: Time now is 14:20:08
post_all.out: Time now is 14:22:02
pre_all.out: Time now is 14:19:50
OR
platform_post.nmi:sun4u_sol_5.8.out: Time now is 16:39:09
platform_post.nmi:x86_fc_3.out: Time now is 16:38:49
platform_pre.nmi:sun4u_sol_5.8.out: Time now is 16:38:01
platform_pre.nmi:x86_fc_3.out: Time now is 16:37:46
post_all.out: Time now is 16:39:38
pre_all.out: Time now is 16:37:32
bash$ cd userdir/nmi:sun4u_sol_5.8/
bash$ ls
cmdfile remote_declare.err remote_post.out remote_pre.err remote_task.out
code remote_declare.out remote_pre_declare.err remote_pre.out task_wrapper.sh
put.pl remote_post.err remote_pre_declare.out remote_task.err
bash$ grep 'Time now' *.out
remote_declare.out: Time now is 14:20:59
remote_post.out: Time now is 14:21:05
remote_pre_declare.out: Time now is 14:20:57
remote_pre.out: Time now is 14:21:01
remote_task.out: Time now is 14:21:03
OR
remote_declare.out: Time now is 16:38:44
remote_post.out: Time now is 16:38:50
remote_pre_declare.out: Time now is 16:38:42
remote_pre.out: Time now is 16:38:46
remote_task.out: Time now is 16:38:48
bash$ cd ../..
bash$ find . -name "*.out" | xargs grep 'Time now'
./userdir/x86_fc_3/remote_pre_declare.out: Time now is 14:20:38
./userdir/x86_fc_3/remote_task.out: Time now is 14:20:44
./userdir/x86_fc_3/remote_pre.out: Time now is 14:20:42
./userdir/x86_fc_3/remote_post.out: Time now is 14:20:46
./userdir/x86_fc_3/remote_declare.out: Time now is 14:20:40
./userdir/sun4u_sol_5.8/remote_pre_declare.out: Time now is 14:20:57
./userdir/sun4u_sol_5.8/remote_declare.out: Time now is 14:20:59
./userdir/sun4u_sol_5.8/remote_pre.out: Time now is 14:21:01
./userdir/sun4u_sol_5.8/remote_task.out: Time now is 14:21:03
./userdir/sun4u_sol_5.8/remote_post.out: Time now is 14:21:05
./pre_all.out: Time now is 14:19:50
./platform_pre.x86_fc_3.out: Time now is 14:20:08
./platform_pre.sun4u_sol_5.8.out: Time now is 14:20:27
./platform_post.x86_fc_3.out: Time now is 14:21:06
./platform_post.sun4u_sol_5.8.out: Time now is 14:21:28
./post_all.out: Time now is 14:22:02
OR
./userdir/nmi:x86_fc_3/remote_pre_declare.out: Time now is 16:38:20
./userdir/nmi:x86_fc_3/remote_task.out: Time now is 16:38:26
./userdir/nmi:x86_fc_3/remote_pre.out: Time now is 16:38:24
./userdir/nmi:x86_fc_3/remote_post.out: Time now is 16:38:28
./userdir/nmi:x86_fc_3/remote_declare.out: Time now is 16:38:22
./userdir/nmi:sun4u_sol_5.8/remote_pre_declare.out: Time now is 16:38:42
./userdir/nmi:sun4u_sol_5.8/remote_declare.out: Time now is 16:38:44
./userdir/nmi:sun4u_sol_5.8/remote_pre.out: Time now is 16:38:46
./userdir/nmi:sun4u_sol_5.8/remote_task.out: Time now is 16:38:48
./userdir/nmi:sun4u_sol_5.8/remote_post.out: Time now is 16:38:50
./pre_all.out: Time now is 16:37:32
./platform_pre.nmi:x86_fc_3.out: Time now is 16:37:46
./platform_pre.nmi:sun4u_sol_5.8.out: Time now is 16:38:01
./platform_post.nmi:x86_fc_3.out: Time now is 16:38:49
./platform_post.nmi:sun4u_sol_5.8.out: Time now is 16:39:09
./post_all.out: Time now is 16:39:38

1 The times in the Start column may be off because of clock skew problems with the machines of the Build and Test System. For example, this problem could show that the tasks run on the submit machine were executed after one or more of the platform tasks.
This tutorial shows how the build and test system’s variables are set, and how user macros can be used. The build and test system publishes information about the run to user-provided scripts in the form of shell environment variables. This section explains each of the variables that the system provides. The build and test system also allows for macros to be defined by the user in the build and test specification file. These also appear to the task scripts as shell environment variables.
IAmAMacro = I am a macro bash$ nmi_submit whereAmIWithMacro.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201021357_15130Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201021357_15130Run ID: 72225
bash$ cd /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201021357_15130bash$ cd userdir/
bash$ find . -name "*.out" | xargs grep 'NMI_PLATFORM'./x86_fc_3/remote_pre_declare.out: NMI_PLATFORM=x86_fc_3./x86_fc_3/remote_task.out: NMI_PLATFORM=x86_fc_3./x86_fc_3/remote_pre.out: NMI_PLATFORM=x86_fc_3./x86_fc_3/remote_post.out: NMI_PLATFORM=x86_fc_3./x86_fc_3/remote_declare.out: NMI_PLATFORM=x86_fc_3./sun4u_sol_5.8/remote_pre_declare.out: NMI_PLATFORM=sun4u_sol_5.8./sun4u_sol_5.8/remote_declare.out: NMI_PLATFORM=sun4u_sol_5.8./sun4u_sol_5.8/remote_pre.out: NMI_PLATFORM=sun4u_sol_5.8./sun4u_sol_5.8/remote_task.out: NMI_PLATFORM=sun4u_sol_5.8./sun4u_sol_5.8/remote_post.out: NMI_PLATFORM=sun4u_sol_5.8./nmi:x86_fc_3/remote_pre_declare.out: NMI_PLATFORM=x86_fc_3./nmi:x86_fc_3/remote_task.out: NMI_PLATFORM=x86_fc_3./nmi:x86_fc_3/remote_pre.out: NMI_PLATFORM=x86_fc_3./nmi:x86_fc_3/remote_post.out: NMI_PLATFORM=x86_fc_3./nmi:x86_fc_3/remote_declare.out: NMI_PLATFORM=x86_fc_3./nmi:sun4u_sol_5.8/remote_pre_declare.out: NMI_PLATFORM=sun4u_sol_5.8./nmi:sun4u_sol_5.8/remote_declare.out: NMI_PLATFORM=sun4u_sol_5.8./nmi:sun4u_sol_5.8/remote_pre.out: NMI_PLATFORM=sun4u_sol_5.8./nmi:sun4u_sol_5.8/remote_task.out: NMI_PLATFORM=sun4u_sol_5.8./nmi:sun4u_sol_5.8/remote_post.out: NMI_PLATFORM=sun4u_sol_5.8
bash$ find . -name "*.out" | xargs grep 'NMI_remote_task'./x86_fc_3/remote_pre_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./x86_fc_3/remote_pre_declare.out: NMI_remote_task_args=Remote_Task Task./x86_fc_3/remote_task.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./x86_fc_3/remote_task.out: NMI_remote_task_args=Remote_Task Task./x86_fc_3/remote_pre.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./x86_fc_3/remote_pre.out: NMI_remote_task_args=Remote_Task Task./x86_fc_3/remote_post.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./x86_fc_3/remote_post.out: NMI_remote_task_args=Remote_Task Task./x86_fc_3/remote_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./x86_fc_3/remote_declare.out: NMI_remote_task_args=Remote_Task Task./sun4u_sol_5.8/remote_pre_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./sun4u_sol_5.8/remote_pre_declare.out: NMI_remote_task_args=Remote_Task Task./sun4u_sol_5.8/remote_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./sun4u_sol_5.8/remote_declare.out: NMI_remote_task_args=Remote_Task Task./sun4u_sol_5.8/remote_pre.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./sun4u_sol_5.8/remote_pre.out: NMI_remote_task_args=Remote_Task Task./sun4u_sol_5.8/remote_task.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./sun4u_sol_5.8/remote_task.out: NMI_remote_task_args=Remote_Task Task./sun4u_sol_5.8/remote_post.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./sun4u_sol_5.8/remote_post.out: NMI_remote_task_args=Remote_Task Task./nmi:x86_fc_3/remote_pre_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:x86_fc_3/remote_pre_declare.out: NMI_remote_task_args=Remote_Task Task./nmi:x86_fc_3/remote_task.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:x86_fc_3/remote_task.out: NMI_remote_task_args=Remote_Task Task./nmi:x86_fc_3/remote_pre.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:x86_fc_3/remote_pre.out: NMI_remote_task_args=Remote_Task Task./nmi:x86_fc_3/remote_post.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:x86_fc_3/remote_post.out: NMI_remote_task_args=Remote_Task Task./nmi:x86_fc_3/remote_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:x86_fc_3/remote_declare.out: NMI_remote_task_args=Remote_Task Task./nmi:sun4u_sol_5.8/remote_pre_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:sun4u_sol_5.8/remote_pre_declare.out: NMI_remote_task_args=Remote_Task Task./nmi:sun4u_sol_5.8/remote_declare.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:sun4u_sol_5.8/remote_declare.out: NMI_remote_task_args=Remote_Task Task./nmi:sun4u_sol_5.8/remote_pre.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:sun4u_sol_5.8/remote_pre.out: NMI_remote_task_args=Remote_Task Task./nmi:sun4u_sol_5.8/remote_task.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:sun4u_sol_5.8/remote_task.out: NMI_remote_task_args=Remote_Task Task./nmi:sun4u_sol_5.8/remote_post.out: NMI_remote_task=code/perlWhereAmI/whereAmI.pl./nmi:sun4u_sol_5.8/remote_post.out: NMI_remote_task_args=Remote_Task Task
bash$ find ../ -name "*.out" | xargs grep '_NMI_GID'./pre_all.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834./platform_pre.x86_fc_3.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834./platform_pre.sun4u_sol_5.8.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834./platform_post.x86_fc_3.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834./platform_post.sun4u_sol_5.8.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834./post_all.out: _NMI_GID=mbletzin_grandcentral.cs.wisc.edu_1151605501_23834../pre_all.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_pre.nmi:x86_fc_3.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_pre.nmi:sun4u_sol_5.8.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_post.nmi:sun4u_sol_5.8.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_post.nmi:x86_fc_3.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../post_all.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130
bash$ find . -name "*.out" | xargs grep -i 'iamamacro'./x86_fc_3/remote_pre_declare.out: NMI_IAmAMacro=Here I Am./x86_fc_3/remote_task.out: NMI_IAmAMacro=Here I Am./x86_fc_3/remote_pre.out: NMI_IAmAMacro=Here I Am./x86_fc_3/remote_post.out: NMI_IAmAMacro=Here I Am./x86_fc_3/remote_declare.out: NMI_IAmAMacro=Here I Am./sun4u_sol_5.8/remote_pre_declare.out: NMI_IAmAMacro=Here I Am./sun4u_sol_5.8/remote_declare.out: NMI_IAmAMacro=Here I Am./sun4u_sol_5.8/remote_pre.out: NMI_IAmAMacro=Here I Am./sun4u_sol_5.8/remote_task.out: NMI_IAmAMacro=Here I Am./sun4u_sol_5.8/remote_post.out: NMI_IAmAMacro=Here I Am../pre_all.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_pre.nmi:x86_fc_3.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_pre.nmi:sun4u_sol_5.8.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_post.nmi:sun4u_sol_5.8.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../platform_post.nmi:x86_fc_3.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130../post_all.out: _NMI_GID=tutorial_nmi-s005.cs.wisc.edu_1201021357_15130This tutorial explains how output files from tasks can be retrieved. Up to this point, the sole output that you have dealt with is the messages written to standard out and standard error for each task. This will also be the first tutorial for which what actual happens inside the component distribution is important. For this reason we will first take a look at the perl script that is used to generate the ouput.
The script used for this tutorial is attached. The script creates a file called “CreatedFile.txt” into which each task writes and identification message. Locate and examine the following code snippet:
if (-e 'CreatedFile.txt') {
open IN, 'CreatedFile.txt';
my lines = ;@
close IN;
print "Found created file:\n";
for my $l (lines) {
print $l;@
}
} else {
print "Created file not found\n";
}
This script checks for the existance of the file CreatedFile.txt in the current working directory. If the file exists, the script will read its contents and print them to standard out.
Immediately following this section is the section that appends an identification message to the file. This will also create the file if it does not exist:
open OUT, ">>CreatedFile.txt";
print OUT "Greetings from $taskhook. The time is $time.\n";
close OUT;
By tracking the contents of this file, we can see how the build and test system manages the contents of the component for the various tasks.
bash$ nmi_submit generateOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201088902_12633Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201088902_12633Run ID: 72308| Task | Output | |
|---|---|---|
| (dark). | pre_all | I am running on nmi-s005.cs.wisc.edu which is a i386-linux-thread-multi platform I am processing for platform local and task hook pre_all I am processing for nmi platform The time now is 11:48:53 |
| platform_pre | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| remote_pre_declare | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| remote_declare | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| Greetings from remote_pre_declare. The time is 21:14:41. | ||
| remote_pre | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| Greetings from remote_pre_declare. The time is 17:49:47. | ||
| Greetings from remote_declare. The time is 17:49:49. | ||
| remote_task | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| Greetings from remote_pre_declare. The time is 17:49:47. | ||
| Greetings from remote_declare. The time is 17:49:49. | ||
| Greetings from remote_pre. The time is 17:49:51. | ||
| remote_post | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| Greetings from remote_pre_declare. The time is 17:49:47. | ||
| Greetings from remote_declare. The time is 17:49:49. | ||
| Greetings from remote_pre. The time is 17:49:51. | ||
| Greetings from remote_task. The time is 17:49:53. | ||
| platform_post | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. | ||
| Greetings from platform_pre. The time is 11:49:23. | ||
| post_all | Found created file: | |
| Greetings from pre_all. The time is 11:48:53. |
Here are some things to notice with the various task outputs:
Lets find out where all of these instances of CreatedFile.txt are at.
bash$ cd /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201088902_12633
bash$ find -name "CreatedFile.txt"./userdir/common/CreatedFile.txt./userdir/x86_fc_3/CreatedFile.txt./userdir/x86_winnt_5.1/CreatedFile.txt./userdir/ppc_aix_5.3/CreatedFile.txt./userdir/common/CreatedFile.txt./userdir/nmi:x86_fc_3/CreatedFile.txt./userdir/nmi:x86_winnt_5.1/CreatedFile.txt./userdir/nmi:ppc_aix_5.3/CreatedFile.txt
bash$ cat userdir/common/CreatedFile.txtGreetings from pre_all. The time is 11:48:53.Greetings from post_all. The time is 11:50:54.bash$ cat userdir/x86_fc_3/CreatedFile.txtGreetings from pre_all. The time is 11:48:53.Greetings from platform_pre. The time is 11:49:08.Greetings from platform_post. The time is 11:50:08.
The following diagram illustrates the flow of the CreatedFile.txt file through the build and test run:

At first glance it looks like the bit bucket behaviour illustrated by the previous diagram is a design flaw. However, this is a case were implementation realities intrude on the design. The build and test system is designed to build software. One characteristic of these kinds of jobs is that the output is usually orders of magnitude larger than the input, leading to system scalability problems. For this reason, the system forces the user to choose what output is retrieved from a remote platform rather than retrieving everything as a default. The next section demonstrates how this works.
As was discussed previously, a results.tar.gz file needs to be created by one or more of scripts called by the remote task hooks in the build and test specification file. In our example, all of the task hooks call the same generateOutput.pl script. Locate and examine the following code snippet1:
if (defined $genresultfile) {
print "Creating results.tar.gz file\n";
system("tar czvf results.tar.gz CreatedFile.txt");
}
The $genresultfile flag that is set by the Getopt::Long module. This snippet assumes that a tar program which supports compression is visible. For this particular program this assumption is correct because the build and test system has to support the creation of the results.tar.gz. Other programs may require prerequisites which will be discussed later.
1 A copy of the script is attached on this page.
remote_post_args = -taskhook=remote_post to remote_post_args = -taskhook=remote_post -genresult.bash$ nmi_submit generateOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201098668_29904Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201098668_29904Run ID: 72315

bash$ cd /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201098668_29904
bash$ find . -name results.tar.gz./userdir/nmi:x86_fc_3/results.tar.gz./userdir/nmi:x86_winnt_5.1/results.tar.gz./userdir/nmi:ppc_aix_5.3/results.tar.gz
bash$ tar xzvf ./userdir/nmi:x86_winnt_5.1/results.tar.gzCreatedFile.txtbash$ cat CreatedFile.txtGreetings from pre_all . The time is 14:31:40.Greetings from platform_pre . The time is 14:32:10.Greetings from remote_pre_declare . The time is 14:32:35.Greetings from remote_declare . The time is 14:32:37.Greetings from remote_pre . The time is 14:32:39.Greetings from remote_task . The time is 14:32:41.Greetings from remote_post . The time is 14:32:44.Here is the flow diagram again with the results.tar.gz file. Notice that once the results file is created, it is automatically copied to the submit machines run directory:

This tutorial explains how to tell the build and test system to subdivide the remote_task. It also demonstrates how the system handles failures. Before starting this tutorial you should review the failure handling reference here.
The key here is the generation of the file tasklist.nmi. As before we will start by examining the script that generates this file.
The script we will examine is attached. Rather than create a different script for each task we wish to define, we’ll use a single script, userTasks.pl, capable of performing each step, depending on its arguments.
The main execution flow of the script is contained within the hash %table. The hash contains a list of task names which are associated with a subroutine. The following snippet shows which tasks associate with what subroutines:
my %table = (
default => \&default,
failure => \&failure,
timeout_failure => \&timeout_failed,
remote_declare => \&generate_tasklist_nmi,
remote_post => \&generate_results_file,
);
The table has entries for the remote_declare and remote_post tasks. The remote_declare task calls the function which creates the tasklist.nmi file. The remote_post task calls the function which generates the results.tar.gz file. It should be noted that these files can be generated during other tasks. Generating the files during these tasks follows the model that the system was designed for.
The script expects two flags, -taskhook and -fail. The remaining arguments to the script are assumed to be user defined task names that are passed as a list to the associated function in %table. The only function that uses this list is generate_tasklist_nmi.
The last thing to note is the following:
my $name = $taskhook;
$name = $ENV{'_NMI_TASKNAME'} if defined $ENV{'_NMI_TASKNAME'};
As this snippet shows, the variable $name can either contain the name of a taskhook or the name of a user defined task. The script treats taskhook tasks the same as user defined tasks.
The function for generating tasklist.nmi is below:
sub generate_tasklist_nmi {
my ($taskhook,list) = _;
open LIST, ">tasklist.nmi";
print "Generating tasklist.nmi for $taskhook\n";
for my $l (list) {
print LIST “$l 1\n”;@
print "$l 1\n";
}
close LIST;
}
Each taskname in @list a timeout of 1 minute. The function assumes that the script is being executed in the top level directory where the tasklist.nmi needs to reside.
The script has two functions that generate a failure. The first failure executes a perl die command which ends the script with a non zero return. Then second timeout_failure hangs the script for 80 seconds which exceeds the 1 minute timeout specified in the generated tasklist.nmi file. The functions are shown below:
sub failure {
my ($taskhook) = _;@
print "Running failure with $taskhook\n";
die "Task $taskhook has failed\n";
}
sub timeout_failed {
my ($taskhook) = _;@
print "Running timeout failed with $taskhook\n";
sleep 80;
print "Timeout is done\n";
}
The var_dump function records the shell environment a bit differently than what the previous tutorials have done. The function is as follows:
sub var_dump {
my ($taskhook) = _;@
mkdir 'CreatedDir' unless -d 'CreatedDir';
my $filename = File::Spec->catfile('CreatedDir',$taskhook . "VarDump.txt");
print "Creating $filename file\n";
open OUT, ">$filename";
for my $e (sort keys %ENV) {
next unless $e=~ m!CONDOR! or
$e=~ m!NMI_! or
$e eq 'PATH';
print OUT "\t\t$e=" . $ENV{$e} . "\n";
}
close OUT;
}
As can be seen, the function creates a dump file specific to the task name under the directory CreatedDir. This means that the shell environment for each tasks will be stored in a seperate file.
Note that the variables _NMI_TASKNAME and _NMI_STEP_FAILED are also dump to standard out in the main script. The importance of these variables will be discussed later.
The function generate_results_file creates the file results.tar.gz. Unlike the previous tutorial where only one file was archived, the function archives two files (_tasklist.nmi_ and CreatedFile.txt) and a directory (_CreatedDir_). The function is as follows:
sub generate_results_file {
my ($taskhook) = _;@
print "Creating results.tar.gz file for $taskhook\n";
if (-e 'tasklist.nmi') {
print "Including tasklist.nmi\n";
system("tar czvf results.tar.gz CreatedFile.txt CreatedDir tasklist.nmi");
} else {
system("tar czvf results.tar.gz CreatedFile.txt CreatedDir");
}
}
= code/perlUserTasks/userTasks.pl= -taskhook=remote_declare customTaskOne customTaskTwo customTaskThree= code/perlUserTasks/userTasks.pl= -taskhook=pre_all
bash$ nmi_submit userTasks.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201099302_30463Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201099302_30463Run ID: 72316
customTaskOne 1customTaskTwo 1customTaskThree 1remote_declare = code/perlUserTasks/userTasks.pl
remote_declare_args = -taskhook=remote_declare customTaskOne failure customTaskTwo customTaskThree
bash$ nmi_submit userTasks.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201100669_2545
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201100669_2545
Run ID: 72321
Task failure has failed which was generated by the perl die command.remote_declare = code/perlUserTasks/userTasks.pl
remote_declare_args = -taskhook=remote_declare customTaskOne failure customTaskTwo timeout_failure customTaskThree
bash$ nmi_submit userTasks.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201101119_2843
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201101119_2843
Run ID: 72322
remote_pre = code/perlUserTasks/userTasks.pl
remote_pre_args = -taskhook=remote_pre -fail
bash$ nmi_submit userTasks.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201101416_3804
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201101416_3804
Run ID: 72325
bash$ nmi_submit userTasks.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201101812_5132
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201101812_5132
Run ID: 72326
This tutorial explains how to specify prereqs (short for prerequisites) to the build and test system. Your software may depend on one or more external tools or libraries in order to build or test. Often these components are not bundled as part of the “stock” operating system you are targetting. The build and test system, by default, provides a “clean” environment with only the default tools that are provided by the OS in question.
This need for external tools can be addressed in two different ways:
bash$ nmi_submit prereqCoreUtils.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201102326_5467Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201102326_5467Run ID: 72327
system_wrapper: running system(/Users/condor/execute/dir_10129/task_wrapper.sh environment.source code/perlPrereqs/prereqTest.pl)...md5sum does not exist at /Users/condor/execute/dir_10129/userdir/code/perlPrereqs/prereqTest.pl line 8.system_wrapper: system() has returned.
= tutorial= prereqCoreUtils= Demonstrates prereqs= build= prereqs.cvs= code/perlPrereqs/prereqTest.pl= x86_macos_10.4= bgietzelcs.wisc.edu@
bash$ nmi_submit prereqCoreUtils.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201102587_5684Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201102587_5684Run ID: 72328bash$ nmi_submit prereqCoreUtils.submitWARNING: no machines were found that matched your requirement of: '(nmi_platform == "x86_macos_10.4" && Arch != "" && OpSys != "" && @Memory >= 0) && (has_coreutilsOOPS_5_2_1 =!= Undefined)’ at /usr/local/nmi-2.1.5/bin/nmi_submit line 680.@
PATH=/prereq/coreutils-5.2.1/bin:/bin:/usr/bin:/Users/condor/execute/dir_10247/userdir
_CONDOR_ANCESTOR_10247=10249:1154729035:3503905792
_CONDOR_ANCESTOR_12083=10247:1154729034:1340517094
_CONDOR_ANCESTOR_346=12083:1153930469:1192128401
_CONDOR_SCRATCH_DIR=/Users/condor/execute/dir_10247
_NMI_PREREQ_coreutils_5_2_1_ROOT=/prereq/coreutils-5.2.1
_NMI_TASKNAME=remote_task68b37b75d0d1aba037dcaaf558fe1985 TestFile.txtThis tutorial explains how to tell the build and test system to use an existing build as a starting point for a build and test run. The tutorial re-uses the example demonstrated in the retrieving output here. The first part of the tutorial will also show how to save a run using nmi_pin
bash$ nmi_pin --runid=72315
UPDATE Run SET archive_results_until='2008-03-23 21:50:45' WHERE (((runid=72315)) AND (archived=1))
= build= generateOutput.cvs, generateMoreOutput.nmi= code/perlGenerateOutput/generateMoreOutput.pl= -taskhook=pre_all MoreOutput
input_runids = 72315
bash$ nmi_submit generateMoreOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201103817_6182Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201103817_6182Run ID: 72330
Found created file CreatedFile.txt:Greetings from pre_all . The time is 14:31:40.Greetings from platform_pre . The time is 14:31:55.Greetings from remote_pre_declare . The time is 14:32:36.Greetings from remote_declare . The time is 14:32:38.Greetings from remote_pre . The time is 14:32:40.Greetings from remote_task . The time is 14:32:43.Greetings from remote_post . The time is 14:32:45.
pre_all = code/perlGenerateOutput/generateMoreOutput.plpre_all_args = -taskhook=pre_all EvenMoreOutputplatform_pre = code/perlGenerateOutput/generateMoreOutput.plplatform_pre_args = -taskhook=platform_pre EvenMoreOutputplatform_post = code/perlGenerateOutput/generateMoreOutput.plplatform_post_args = -taskhook=platform_post EvenMoreOutputpost_all = code/perlGenerateOutput/generateMoreOutput.plpost_all_args = -taskhook=post_all EvenMoreOutputremote_pre_declare = code/perlGenerateOutput/generateMoreOutput.plremote_pre_declare_args = -taskhook=remote_pre_declare EvenMoreOutputremote_declare = code/perlGenerateOutput/generateMoreOutput.plremote_declare_args = -taskhook=remote_declare EvenMoreOutputremote_pre = code/perlGenerateOutput/generateMoreOutput.plremote_pre_args = -taskhook=remote_pre EvenMoreOutputremote_task = code/perlGenerateOutput/generateMoreOutput.plremote_task_args = -taskhook=remote_task EvenMoreOutputremote_post = code/perlGenerateOutput/generateMoreOutput.plremote_post_args = -taskhook=remote_post EvenMoreOutput -genresult
input_runids = 72315, 72330
bash$ nmi_submit generateMoreOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201104281_8698Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201104281_8698Run ID: 72331
Found created file CreatedFile.txt:Greetings from pre_all . The time is 14:31:40.Greetings from platform_pre . The time is 14:32:10.Greetings from remote_pre_declare . The time is 20:32:37.Greetings from remote_declare . The time is 20:32:39.Greetings from remote_pre . The time is 20:32:41.Greetings from remote_task . The time is 20:32:43.Greetings from remote_post . The time is 20:32:46.Found created file MoreOutput-CreatedFile.txt:Greetings from pre_all MoreOutput. The time is 15:57:32.Greetings from platform_pre MoreOutput. The time is 15:58:02.Greetings from remote_pre_declare MoreOutput. The time is 21:58:38.Greetings from remote_declare MoreOutput. The time is 21:58:40.Greetings from remote_pre MoreOutput. The time is 21:58:42.Greetings from remote_task MoreOutput. The time is 21:58:44.Greetings from remote_post MoreOutput. The time is 21:58:46.
This output should be present in all of the tasks except pre_all and post_all.
1 If your favorite editor is vi then you can do a global search and replace using the following command :1,$s/ MoreOutput/ EvenMoreOutput/. Be sure to preserve the space before each string otherwise you will rename the script generateEvenMoreOutput.pl which will cause the run to fail.
bash$ nmi_submit generateMoreOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201105749_9782Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201105749_9782Run ID: 72333
Found created file EvenMoreOutput-CreatedFile.txt:Greetings from pre_all EvenMoreOutput. The time is 16:29:45.bash$ nmi_submit generateMoreOutput.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201106123_15159Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201106123_15159Run ID: 72334
Found created file EvenMoreOutput-CreatedFile.txt:Greetings from pre_all EvenMoreOutput. The time is 16:05:17.Greetings from platform_pre EvenMoreOutput. The time is 16:05:34.Greetings from remote_pre_declare EvenMoreOutput. The time is 16:06:01.Greetings from remote_declare EvenMoreOutput. The time is 16:06:03.Greetings from remote_pre EvenMoreOutput. The time is 16:06:05.Greetings from remote_post EvenMoreOutput. The time is 16:06:09.This tutorial applies the build and test concepts learned in the previous tutorials to the an actual software application; Zsh. We will start by reviewing how one might normally build zsh by hand, and then walk through the process of automating this inside the NMI Build & Test System. We will start with a simple automation, and then introduce more advanced features and show how they add flexibility and power to the build or test process.
make check. You may have some problems with some of the tests hanging. For example the Y0 test hangs on Mac OS X 10.4. The tests can also be run individually by using the TESTNUM argument. For example make TESTNUM=B check runs the B* set of tests.
./configure. You may have had to add some flags dependending on your local machine.make.make check.In order to repeat this every night, or on multiple remote platforms, these steps will need to be automated. The way to do this is by adding what is commonly known as a glue script.
This first cut demonstrates a minimal build and test framework that shows you how easily a traditional build process can be added into the build and test system. Download the attachments and put them into your working directory on the submit machine.
Several files need to be created to run the ZShell build on the system. A build/test specification file and input specification file are always required. These files will be called zshell.ftp and Simple.submit. Also needed is a build script (also called a glue script) that changes the working directory to the top of the ZShell source directory and runs the build command. The build script in this case will be called run.sh.
Finally, another input specification is needed to retrieve the build script itself. Although this script is code itself and should really be kept a reliable repository like CVS to ensure the repeatability of this build in the future, for now we’re just going to use the scp input method to “retrieve” it from a directory on the local submission host. This input specification file will be called glue.scp.
The input method is ftp to get the tar archive from the FTP site and untar it into the run directory:
@method@ = ftp
@ftp_root@ = ftp://ftp.cs.wisc.edu/condor/nmi/tutorials/
@ftp_target@ = zsh-4.3.2.tar.gz
@untar@ = true
This is the quick and dirty version of a submit file. As seen below, we simply put our three build steps in three predefined remote tasks that we know will be run in the correct sequence.
@project@ = tutorial
@component@ = ZShell Simple
@description@ = ZShell Build and Test Simple Example
@run_type@ = build
The following shows the two input files being referenced:
@inputs@ = zshell.ftp, glue.scp
Here is the configure step. Notice how the glue script is what is actually called and the configure command is passed in as an argument:
@remote_pre@ = run.sh
@remote_pre_args@ = ./configure --without-tcsetpgrp
Here are the rest of the build steps and the platform list:
@remote_task@ = run.sh
@remote_task_args@ = make
@remote_post@ = run.sh
@remote_post_args@ = make check
@platforms@ = ia64_rhas_3, x86_64_fc_4
The glue.scp calls the scp method.
run.sh is a two line script that changes to the zsh-4.3.2 directory and then runs whatever build command is provided as an argument.
bash$ nmi_submit Simple.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201179662_15953
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201179662_15953
Run ID: 72428
The simple build and test run is sufficient for one-off tests. However, the real strength of the build and test system is running automated builds that are constantly repeated. Because of this, it makes sense to improve the submission files so that build and test results can be understood more easily by the casual user. In this tutorial, we will start by moving the build steps into explicitly-named user defined tasks, so that the build steps can easily be identified on the details page. To accomplish this we need to defined an NMI_TASKLIST file as we did in the user tasks tutorial.
To start this tutorial, download the attachments into your working directory on the submit machine.
For this version of the submission files we adapt the script used in the user tasks tutorial. The submit file, tasks.submit, calls two tasks remote_declare and remote_task. Just as in the user tasks tutorial, remote_declare is called to create the tasklist.nmi file and remote_task is called repeatedly for each user defined task. The zshell.ftp file is the same as the simple example earlier. The file glue.scp is also similar except that it retrieves the script glue.pl instead of run.sh.
This script is adapted from the userTasks.pl script used in the user tasks tutorial. The tasknames and their associated functions have be replaced by the following:
The tasklist is hard-coded in the _generate_tasklist_nmi_ function with each task getting a timeout of 10 minutes.
bash$ nmi_submit tasks.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201179264_15130
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201179264_15130
Run ID: 72423
In the previous section we introduced the idea that the build and test system should be used to run continuously repeated tasks. This kind of use can be part of software development practices such as continuous integration. However as with other internal tools, a good design is needed up front to keep developers from falling into the trap of spending more time fixing internal tools than working on the software deliverables1. In this tutorial we show some of the tricks that can be used to make the build and test system a vital but invisible part of your development process. We will demonstrate the following design principles:
We will use four features of the build and test system.
1 This trap is humorously explained in the book The Peter Pyramid. In the book, Dr. Peter explains that one of the symptoms of an organization suffering from to much bureaucracy is when it spends more time on external processes then in talking to its customers.
bash$ wget ftp.cs.wisc.edu/condor/nmi/tutorials/zsh-scripts.tar.gz--10:42:37-- http://ftp.cs.wisc.edu/condor/nmi/tutorials/zsh-scripts.tar.gz=> `zsh-scripts.tar.gz'Resolving ftp.cs.wisc.edu... 128.105.2.28Connecting to ftp.cs.wisc.edu|128.105.2.28|:21... connected.Logging in as anonymous ... Logged in!==> SYST ... done. > PWD ... done.@
@> TYPE I ... done. > CWD /condor/nmi/tutorials ... done.@
@> PASV ... done. > RETR zsh-scripts.tar.gz ... done.@
@Length: 2,497 (2.4K) (unauthoritative)@
@100%[======================================================================>] 2,497 --.--K/s 10:42:36 (1.03 MB/s) - `zsh-scripts.tar.gz' saved [2497]
bash$ tar xzvf zsh-scripts.tar.gzdrwxr-xr-x tutorial/tutorial 0 2006-08-14 10:33:31 zshell/-rwxr-xr-x tutorial/tutorial 3583 2006-08-13 11:29:04 zshell/glue.pl-rw-r--r-- tutorial/tutorial 45 2006-08-12 21:01:43 zshell/glue.scp-rw-r--r-- tutorial/tutorial 80 2006-08-12 19:33:14 zshell/submit_env.sh-rwxr-xr-x tutorial/tutorial 856 2006-08-13 11:29:05 zshell/test.sh-rw-r--r-- tutorial/tutorial 110 2006-08-12 21:38:25 zshell/zshell.ftp-rw-r--r-- tutorial/tutorial 443 2006-08-13 09:14:07 zshell/zshell.submitdrwxr-xr-x tutorial/tutorial 0 2006-08-12 20:25:21 zshell/srctest/-rw-r--r-- tutorial/tutorial 76 2006-08-12 19:33:14 zshell/srctest/Makefile-rwxr-xr-x tutorial/tutorial 37 2006-08-12 19:33:14 zshell/srctest/configurebash$ cd zshell/Take a look at the file glue.pl. This is the script that automates the build steps you did earlier. The script is larger than it could be because it implements some of the design principles discussed in the subsection.
The first section to note is the lookup table which lays out the tasks that the glue script is expecting to run under (this is similar to the script used in the user tasks tutorial). The script is expecting the build and test of zsh to be broken up into configure, build, and test tasks. In addition, the script expects the remote_declare task to generate the tasklist file. Finally the script has a scan task available which will print out all of the CONDOR and NMI variables found.
The second section is a comment block that describes the macros that the script is expecting. Only _NMI_TASKNAME is provided by the build and test system. All of the others are user macros which need to be defined in the build and test specification file as demonstrated here. Remember that the NMI_ prefix is added by the build and test system. And so for example, the macro NMI_TASKLIST would be defined as TASKLIST = task1... in the build and test specification file. You should also notice the convention used with the NMI_ARGS and NMI_TIMEOUT macros. These macros indicate a specific task and optional platform by including the labels in the macro name. For example NMI_ARGS_configure_x86_fc_4 specifies arguments for the configure tasks run on the x86_fc_4 platform which NMI_ARGS_build specify arguments for the build task for all platforms. See the subroutine assemble for the perl logic behind this convention.
The third thing to note is the for loop which contains the actual lookup code. Unlike the previous script which expects to match tasks names exactly, this script looks for the task name as a prefix. So for example test1, test2, and testFinal would all execute the test task. This is done so that a task can be executed multiple times with different arguments and timeouts.
The last thing you should examine is the functions which execute the tasks. For example, you should see that the test command actually adds its arguments between make and check.
One of the traps that you can fall into when you run a task a remote batch is to keep the same debugging habits that you used when you ran on your local machine. Since build and test runs take much longer then a local execution, you will find your time wasting away if you let simple mistakes such as syntax errors creep into your builds. To prevent this from happening you should follow some simple design principles with your glue scripts:
The file zshell.submit contains the start of what is needed for the Zsh build and test. This is the file where we expect all of the changes that were discussed here will be made. Note the macros section where the macros TASKNAME and SRCDIR are set. In the tasknames version of the submission files, the task list and source directory were hard-coded in the glue script. The TASKNAME macro contains only the scan task so that the run environment can be seen. During this tutorial you will be adding more stuff to this section.
This file tells the build and test system where to fetch the glue script. For this tutorial the glue script is assumed to be on the machine and path that you run nmi_submit from. The script can also be include within the source distribution or as a seperate archive or CVS module.
To fetch the glue script from the submit directory we will need to use the scp method. We also need to use variable substitution as discussed here.
@method@ = scp
@scp_file@ = $(SUBMITDIR)/glue.pl
The file _submit_env.sh_ should be sourced before the run is submitted so that SUBMITDIR is set to the proper value. This file set all of the environmental variables used by the submission files as shown below:
export _NMI_HOME=$HOME
export _NMI_HOSTNAME=$HOSTNAME
export _NMI_SUBMITDIR=$PWD
The file is used with Borne shell. If you prefer a different shell you will need to create your own version of the file.
bash# . submit_env.shbash# set | grep _NMI__NMI_HOME=/home/tutorial_NMI_HOSTNAME=nmi-s005.cs.wisc.edu_NMI_SUBMITDIR=/home/tutorial/zshell
bash# nmi_submit zshell.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201270245_29836Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201270245_29836Run ID: 72525
NMI_SRCDIR=zsh-4.3.2NMI_TASKLIST=scan
TASKLIST = scan,configure,build,test
bash# nmi_submit zshell.submitGlobal ID: tutorial_nmi-s005.cs.wisc.edu_1201384769_17678Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201384769_17678Run ID: 72610
system_wrapper: running system(/home/condor/execute/dir_25030/task_wrapper.sh environment.source glue.pl -taskhook=remote_task)...configure: error: no controlling ttyTry running configure with --with-tcsetpgrp or --without-tcsetpgrpTask configure has failed at /home/condor/execute/dir_25030/userdir/glue.pl line 58.ARGS_configure = --with-tcsetpgrp
bash# nmi_submit zshell.submit
Global ID: tutorial_nmi-s005.cs.wisc.edu_1201385114_20490
Run Directory: /nmi/run/tutorial/2008/01/tutorial_nmi-s005.cs.wisc.edu_1201385114_20490
Run ID: 72613
Things To Look Out For: