June 26, 2008
Document Owners: Sharon Lucas, David Bender
eMail Addresses: lucass@us.ibm.com, bdavid@us.ibm.com
Using Python for Expression Evaluation
Installation and Configuration
Generating STAX Function Documentation
Events Generated by STAX that Provide Job Status
Appendix A: STAX XML Document Examples
Appendix B: STAX Error Code Reference
Appendix C: STAX Document Type Definition (DTD)
Appendix D: STAX Extensions Document Type Definition (DTD)
Appendix F: Jython and CPython Differences
Appendix G: Licenses and Acknowledgements
STAX accepts job definitions, in the form of XML documents. Fundamentally, these job definitions allow you to specify the processes and STAF commands necessary to perform the job. STAX provides a wealth of expressive functionality on top of this, making it easy to implement, manage, track, and monitor your jobs.
STAX uses the Python scripting language for variable and expression evaluation. The Python code is executed by Jython, a version of Python written entirely in Java. This allows STAX to take advantage of the powerful and easy-to-use features of Python.
The sections that follow describe the basic concepts behind STAX, explain
the STAX XML language used to define your jobs, and detail the commands
externalized by the STAX service. Read on to find out more about the exciting
new world of STAX.
Processes and stafcmds may be put into sequential and/or parallel wrappers which can be nested.
For example, instead of hardcoding the name of the machines where processes and commands are executed during the job, a Python variable can be assigned the name of the machine and specified for the location element. Python variables also be provided at the time the job is requested to be executed. A Python variable may also be assigned a list of machine names.
After STAX processes some elements (e.g. process and stafcmd), the return code and result (if applicable) are accessible via STAX variables. These variables can be referenced by other STAX elements (e.g. via the if element's expression attribute) to determine logic flow within the job.
Whenever a new STAX-Thread is created, existing variables are cloned from the parent STAX-Thread. To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section. STAX elements that can create STAX-Threads include the following: <parallel>, <paralleliterate>, <process-action>, <job-action>, and <function> elements with a local scope.
The STAX Service also maintains an individual job log for each submitted job. These job-specific logs record information such as testcase status and job execution tracing. If the "log" element is used within the STAX Job Definition, then a user log is also created for the submitted job.
Alternatively, if you want your STAX job to use a queue on the STAX
service side to send and receive messages, you can create your own
STAF handle and use it's queue. Refer to Sample
STAX Job 3 - Creating a STAF Handle and Using it's Queue for an example
of how to create a STAF handle within a STAX job and use it's queue to get
messages.
STAX uses XML (Extensible Markup Language) to describe STAX job definitions. XML is a language defined by the World Wide Web Consortium (W3C), the body that sets the standards for the Web. It is called extensible because it is not a fixed format like HTML (a single, predefined markup language). Instead, XML is actually a 'metalanguage' -- a language for describing other languages -- which lets you design your own customized markup languages for limitless different types of documents. This section reviews some XML fundamentals. Refer to the "References" section for where to get more information about XML.
Both markup and text in an XML document are case-sensitive. All XML processing instructions start with <? and end with ?>. XML comments start with <!-- and end with -->. In XML, tags always start with < and end with >. The names that can be used for a tag are defined by the DTD (Document Type Definition).
XML documents are made up of XML elements. Much like in HTML, you create XML elements with an opening tag, such as <stax>, followed by the element content (if any), such as text or other elements, and ending with the matching closing tag that starts with </, such as </stax>. It's necessary to enclose the entire document, except for processing instructions, in one element, called the root element -- that's the <stax> element here:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE stax SYSTEM "stax.dtd"> <stax> . . . </stax>
<nop/>
If the attribute value contains both single and double quotes, you can use the XML-defined entity ' for a single quote and " for double quotes.
An example of a defaultcall element with a function attribute is:
<defaultcall function="MainFunction"/>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE stax SYSTEM "stax.dtd">
<stax>
<defaultcall function="FunctionA"/>
<function name="FunctionA">
...
</function>
<function name="FunctionB">
...
</function>
<function name="FunctionC">
...
</function>
</stax>
The function element can contain a single task element as defined by the STAX DTD. Here, I added a process element to FunctionA, a stafcmd element to FunctionB, and a log element to functionC. A process element can contain other elements. In this case I added location, command, and parms. A stafcmd element can contain other elements. In this case I added location, service, and request.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE stax SYSTEM "stax.dtd">
<stax>
<defaultcall function="FunctionA"/>
<function name="FunctionA">
<process>
<location>'local'</location>
<command>'java'</command>
<parms>'com.ibm.staf.service.stax.TestProcess 2 4 0'
</process>
</function>
<function name="FunctionB">
<stafcmd>
<location>'local'</location>
<service>'misc'</service>
<request>'version'</request>
</stafcmd>
</function>
<function name="FunctionC">
<log>'This function logs this message'</log>
</function>
</stax>
For example, the STAX DTD allows you to describe a STAF Command which has a name attribute and contains location, service, and request elements. The relevant part of the STAX DTD contains:
<!ELEMENT stafcmd (location, service, request)>
<!ATTLIST stafcmd
name CDATA #IMPLIED
>
<!ELEMENT location (#PCDATA)>
<!ELEMENT service (#PCDATA)>
<!ELEMENT request (#PCDATA)>
This defines a stafcmd as an element type containing location, service, and request elements; and it defines
location, service, and request as element types containing just plain text (Parsed Character Data or PCDATA)
and defines name as an attribute type containing just plain text (Character Data or CDATA).
Validating parsers read the DTD before they read your document so that they can identify where every element type ought
to come and how each relates to the other, so that applications which need to know this in advance (such as the
STAX service) can set themselves up correctly. The example above lets
you create STAF commands like:
<stafcmd name="'Delay'">
<location>'local'</location>
<service>'delay'</service>
<request>'delay 5000'</request>
</stafcmd>
A DTD provides applications with advance notice of what names and structures can be used in a
particular document type. Using a DTD when editing files means you can be certain that all documents
which belong to a particular type will be constructed and named in a consistent and conformant manner.
The STAX service parses an XML document to break it down into its component parts and then handles the
resulting data. STAX uses the XML Parser for Java which is a validating XML parser.
Refer to the References section for where to get more information about the XML Parser for Java.
You can use your favorite text editor to create a STAX XML document, or you can use an XML editor such as Cooktop. If you use an XML editor, you'll probably want to get the STAX DTD file so that the XML editor can use it to validate the XML syntax. The stax.dtd file is not provided with the STAX service because its contents can vary because you can extend it by registering STAX service extensions. You can get the stax.dtd file by running the following from a command prompt on your STAX service machine:
set STAF_QUIET_MODE=1 (or if on Unix: export STAF_QUIET_MODE=1) STAF local STAX GET DTD > stax.dtd set STAF_QUIET_MODE= (or if on Unix: unset STAF_QUIET_MODE)This creates a stax.dtd file in the current directory. Or, see Appendix D: STAX Extensions Document Type Definition (DTD) for the contents of the STAX DTD (without any extensions).
STAX uses the Python for variable and expression evaluation. STAX uses Jython to execute the Python code. Jython is an implementation of the Python scripting language written in 100% pure Java that runs under any compliant Java Virtual Machine (JVM). Using Jython, you can write Python code that interacts with any Java code.
STAX variable names must follow the Python variable naming conventions. In Python, variable names come into existence when you assign values to them, but there are a few rules to follow when picking names for variables.
Note: Python lets you use the names of Python built-in functions as variable names. However, we recommend that you don't use the name of a Python built-in function as a variable name because you may want to use the Python built-in function at some point in your STAX job. Following are names of Python built-in functions: abs, basestring, bool, callable, chr, classmethod, cmp, compile, complex, delattr, dict, dir, divmod, enumerate, eval, execfile, file, filter, float, getattr, globals, hasattr, hash, help, hex, id, input, int, isinstance, issubclass, iter, len, locals, long, map, max, min, object, oct, open, ord, pow, property, range, raw_input, reduce, reload, repr, round, setattr, slice, staticmethod, str, sum, super, tuple, type, unichr, unicode, vars, xrange, zip.
For example, the following two lines do exactly the same thing in a STAX XML document. They assign a string constant (literal) "CoolTest" to the value of a variable named testName.
<script>testName = "CoolTest1"</script> <script>testName = 'CoolTest1'</script>However, the following line is not the same. It assigns the value of a variable named CoolTest1 to the value of a variable named testName. If this was not what you intended and a variable named CoolTest1 does not exist, a STAXPythonEvaluationError signal is raised.
<script>testName = CoolTest1</script>
For elements and attributes whose values are evaluated via Python, we need to distinguish between literals and variables.
For example, the following request element's value contains a string constant which is concatenated with the value of a variable named machName. So, if the value of variable machName is 'testA.austin.ibm.com', after being evaluated by Python, the request element's value would be: 'RELEASE POOL ClientMachPool ENTRY testA.austin.ibm.com'.
<request>'RELEASE POOL ClientMachPool ENTRY ' + machName</request>Another way to do this is:
<request>'RELEASE POOL ClientMachPool ENTRY %s' % (machName)</request>where the %s indicates a String format (and can also be used for decimal format, etc.), and where the value of the machName variable would replace the %s marker.
Also, note that the following two lines do exactly the same thing in a STAX XML document. They assign a string constant (literal) "VerifyRC" to the function attribute's value.
<call function="'VerifyRC'"/> <call function='"VerifyRC"'/>However, the following line is not the same. It assigns the value of a variable named VerifyRC to the function attribute's value. If a variable named VerifyRC does not exist, a STAXPythonEvaluationError signal is raised.
<call function="VerifyRC"/>Also, note that XML processors assume that < always starts a tag and that & always starts an entity reference, so you should avoid using those characters for anything else. You must use the entity reference < instead of < and entity reference & instead of & or else you'll get an XML parsing error. This can be difficult sometimes as the < character is used as the less-than operator in Python, as in this example, where RC < 0 is being assigned to the expression attribute.
<if expr="RC < 0">Here's another example that shows a <script> element that contains Python code using the regular expression (re) module to look for pattern <pass> anywhere in a string.
<script> import re # Look for <pass> anywhere in the string. # Note have to use < to represent a < in the pattern. matchstr = r'.*?<pass>.*?' matchFlag = re.match(matchstr, STAFResult) </script>Refer to the "References" section for where to get more information about Jython and Python.
If you are already a CPython programmer, or are hoping to use CPython
code under Jython, refer to the
"Jython and CPython Differences" section for information about
differences in the two implementations of Python.
The first line in an XML document should start with an XML declaration. This indicates the document is written in XML and specifies the XML version, the language encoding for the document, and indicates that the document refers to an external DTD (standalone="no").
The second line in an XML document should be the document type declaration. This is used to indicate the DTD used for the document. It defines the name of the root element (stax), and the DTD to be used. STAX checks the syntax of XML documents using a validating XML parser to verify that the document complies with the DTD. Note that DTDs are all about specifying the structure and syntax of XML documents (not their content).
So, the first two lines in a STAX XML document should look like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE stax SYSTEM "stax.dtd">This section describes the elements that can be used in a STAX XML document.
To ease the description of the elements, some elements will be grouped as follows so that they can be referenced as a group and will be shown in bold italics:
Reference Elements
task process | stafcmd | nop | sequence | parallel | paralleliterate | call | call-with-list | call-with-map | return | import | if | loop | iterate | break | continue | try | throw | rethrow | signalhandler | raise | hold | release | terminate | testcase | tcstatus | script | block | timer | log | messageNotes:
Also, some examples of the usage of elements use "..." for brevity to represent that additional XML would be included in place of the "...".
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE stax SYSTEM "stax.dtd"> <stax> <defaultcall function="FunctionA"/> <script>machName = "test1.austin.ibm.com"</script> <function name="FunctionA"> ... </function> <function name="FunctionB"> ... </function> ... </stax>
All script elements contained in the root stax element are initialized at the beginning of the job, as each is encountered in sequential order, regardless of their placement within the stax element, and are accessible throughout the job (like global variables). All script elements contained in elements other than the stax element (e.g. such as the sequence element) are assigned as each is encountered and are accessible within their scope and are inherited from parent STAX-Threads.
Whenever a new STAX-Thread is created, existing variables are cloned from the parent STAX-Thread. To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section. STAX elements that can create STAX-Threads include the following: parallel, paralleliterate, process-action, job-action, and function elements with a local scope.
<script>testName = "CoolTest1"</script>Goal: Create a variable named machName and assign it the value of the STAFResult variable.
<script>machName = STAFResult</script>Goal: Create a list called machList containing 10 machine names by running the STAF Resource Pool Request command in a loop ten times, each time adding the STAF Result value from the RESPOOL REQUEST POOL command (which contains a machine name) to the list. Note that this example first creates an empty list and then adds a machine name to the list 10 times.
<script>machList = []</script> <script>clientPool = 'ClientMachinePool'</script> <loop var="i" from="1" to="10"> <sequence> <stafcmd> <location>'server1.austin.ibm.com'</location> <service>'RESPOOL'</service> <request>'REQUEST POOL %s' % (clientPool)</request> </stafcmd> <script>machList.append(STAFResult)</script> </sequence> </loop>Goal: Create a list called allMachList by combining lists named unallocMachList and allocMachList. New list allMachList contains ['MachA','MachB','MachC','MachD','AllocMachA','AllocMachB'].
<script>unallocMachList = ['MachA','MachB','MachC','MachD']</script> <script>allocMachList = ['AllocMachA','AllocMachB']</script> <script>allMachList = unallocMachList + allocMachList</script>Goal: Generate a random number (which could be used to randomly select which function to call) using the random module provided by Python. Note that in Python, you can use a semicolon to separate multiple statements on the same line.
<script>from random import random; r=random()*100</script>Goal: Use a Java class, com.ibm.staf.STAFUtil (provided in JSTAF.jar), and it's wrapData() method to turn strings containing spaces into the colon-length-colon form needed for submitting a STAF Notify request. Note that the Java class STAFUtil needs to be imported in order to use its wrapData() method. This example also shows that for statements that are too long to fit on one line, Python lets you continue typing the statement on the next line, if you're coding something enclosed in (), {}, or [] pairs. Continuation lines can start at any indentation level.
<script>
NotifyProfile = 'Jane Smith'
Message = 'STAX Job ID %s failed.' % (STAXJobID)
from com.ibm.staf import STAFUtil
Request = ('NOTIFY PROFILE %s LEVEL NORMAL MESSAGE %s' %
(STAFUtil.wrapData(NotifyProfile), STAFUtil.wrapData(Message)))
</script>
<message>NotifyRequest</message>
The message element would display something like:
NOTIFY PROFILE :10:Jane Smith LEVEL NORMAL MESSAGE :21:STAX Job ID 6 failed.Goal: Create a Python class object, Server, and generate three instance objects from the class and create a list of these Server objects. Then iterate through the server list, logging information about each server object in the server list.
<script>
# Define Server class
class Server:
def __init__(self, hostname, dir):
self.hostname = hostname
self.dir = dir
def __repr__(self):
return "<Server: hostname=%s, directory=%s>" % (self.hostname, self.dir)
def getHostname(self):
return self.hostname
def getDir(self):
return self.dir
# Create an array of 3 Server objects
serverList = [
Server('myServer.austin.ibm.com', 'C:/install'),
Server('serverA.portland.ibm.com', 'D:/install'),
Server('linuxServer.austin.ibm.com', '/usr/local/install')
]
</script>
<iterate var="server" in="serverList" indexvar="i">
<log>
'Server #%s: hostname=%s, directory=%s' % (i+1, server.getHostname(), server.getDir())
</log>
</iterate>
After a process has completed (or if it could not be started) the following variables are set and can be referenced by the job:
[ [<File 1 rc>, <File 1 data>], [<File 2 rc>, <File 2 data> ], ... ]Each entry in the list represents a returned file and consists of a 2-element list as follows:
Files will be returned in the order of standard output, then standard error, then any files specified with the returnfile(s) element(s).
For example, assume that the standard output of a process was simply "This is stdout data", and that the standard error of a process was "This is stderr data", and that you asked for both of these to be returned. STAXResult would look like the following.
[ [0, 'This is stdout data'], [0, 'This is stderr data'] ]
The process element contains two required elements (location and command) with many optional elements. The location element must be specified first, followed by the command element. The rest of the elements are optional.
Note that the process elements are equivalent to the options allowed for the STAF Process Service's START request (except where noted), so see the STAF User's Guide for more information.
Required process elements:
This element has the following optional attributes:
Optional process elements:
Here are some important notes about optional process elements:
Using the if attribute provides a convenient shortcut to deal with many variations of optional process elements that, otherwise, would have to be specified using the if/elseif elements and multiple different process elements.
The process element may contain any of the following optional elements in the order listed (with some variations):
This element has the following required attribute (in addition to the if attribute):
This element has the following required attribute (in addition to the if attribute):
This element has the following attribute (in addition to the if attribute):
This element has the following required attribute (in addition to the if attribute):
This element has the following required attribute (in addition to the optional if attribute):
Note: This element was added in STAX V3.1.3.
Note: To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section.
Notes:
<sequence> <process name="'TestProcess'"> <location>'local'</location> <command>'java'</command> <parms>'com.ibm.staf.service.stax.TestProcess 5 1 0'</parms> <title>'Test Process'</title> </process> <if expr="RC != 0"> <raise signal="'NonZeroRCError'"/> </if> </sequence>In the following example of a process element, a ping command is executed as though you were at a shell prompt. The ping is executed within a loop contained within a timer. If the ping command does not complete successfully (indicated by RC 0) within 30 seconds, a failure message is sent.
<sequence>
<timer duration="'30s'">
<loop until="RC == 0">
<process name="'Ping'">
<location>'local'</location>
<command mode="'shell'">'ping -n 1 -w 1 %s' % machName</command>
</process>
</loop>
</timer>
<if expr="RC != 0">
<message>'Ping of machine %s failed' % machName</message>
</if>
</sequence>
The following example of a process element shows many of the
optional elements that a process can contain and shows the use of the
if element to specify whether an optional element should be
used based on an expression evaluated while the job is running.
<script>
machName = 'local'
opSys = 'Win32'
className = 'com.ibm.staf.service.stax.TestProcess'
commonEnvVarList = ['COMMON_ENV_VAR_1=value1','COMMON_ENV_VAR_2=value2']
</script>
<process name="'aProcess'">
<location>machName</location>
<command>'java'</command>
<parms if="opSys != 'Linux'">
'%s 2 15 100' % className
</parms>
<title>'Title example for process with many elements'</title>
<vars if="opSys == 'Win32'">
['tempPath=C:/temp', 'winRunPath=C:/temp/processa']
</vars>
<vars if="opSys == 'Linux'">
['tempPath=/test/temp']
</vars>
<var>
'commonMachName=%s' % (machName)
</var>
<envs if="opSys == 'Win32'">
['TEMP_DIR=C:/temp']
</envs>
<envs>commonEnvVarList</envs>
<useprocessvars if="opSys == 'Win32'"/>
<disabledauth if="opSys == 'Win32'" action="'ignore'"/>
<stdout mode="'replace'">
'c:/temp/aProcess.out'
</stdout>
<stderr mode="'append'">
'c:/temp/aProcess.err'
</stderr>
<console if="opSys == 'Win32'" use="'same'"/>
<focus if="opSys == 'Win32'" mode="'minimized'"/>
</process>
In the following example of a process element, a command which writes to stdout and
stderr and produces a couple of files (C:\process1.inf and C:\process2.inf) is run.
The contents of the stdout file and the two additional files are returned
in STAXResult when the process completes.
Note that the stdout file also contains stderr output because <stderr> specified
mode 'stdout' instead of specifying a different file name.
Then the contents all returned files are written to one central place, the STAX Job User Log.
<sequence>
<process>
<location>machName</location>
<command>cmd</command>
<stdout>'C:/temp.out'</stdout>
<stderr mode="'stdout'"/>
<returnstdout/>
<returnfiles>['C:/process1.inf', 'C:/process2.inf']</returnfiles>
</process>
<if expr="RC != 0">
<log level="'error'">
'Process failed with RC=%s, Result=%s' % (RC, STAFResult)
</log>
<elseif expr="STAXResult != None">
<iterate var="fileInfo" in="STAXResult" indexvar="i">
<if expr="fileInfo[0] == 0">
<sequence>
<log level="'info'">fileInfo[1]</log>
</sequence>
<else>
<log level="'error'">
'Retrieval of file %s contents failed with RC=%s' % (i, fileInfo[0])
</log>
</else>
</if>
</iterate>
</elseif>
<else>
<log level="'info'">'STAXResult is None'</log>
</else>
</if>
</sequence>
In the following example of a process element, the following shell-style command
is executed, "grep 'Node Count = ' /tests/cli.out | awk '{print $8}'" redirecting its
standard output and standard error to /tests/awk.out and returning the output.
Note the use of the caret (^) as an escape character for "{" so that it doesn't try to
resolve a variable named "print $8".
<process name="'Grep_and_awk_numClustNodes'">
<location>machName</location>
<command mode="'shell'">
"/bin/grep 'Node Count = ' /tests/cli.out | awk '{print $8}'"
</command>
<stdout mode="'replace'" >'tests/awk.out'</stdout>
<stderr mode="'stdout'"/>
<returnstdout/>
</process>
In the following example of a process element, the command is started in a
separate Cygwin shell on Windows, redirecting its standard output and standard error
to D:/temp/copy.out and returning the output (if any).
<process name="'copyFiles'">
<location>machName</location>
<command mode="'shell' shell="'D:/Cygwin/bin/bash -c %C'">
'cp -fr D:/tests/test1/*.java D:/output/test1'
</command>
<stdout mode="'replace'" >'D:/temp/copy.out'</stdout>
<stderr mode="'stdout'"/>
<returnstdout/>
</process>
In the following example of a process element, a process-action element is specified. The process-action task will be executed after the process starts.
<script>
machName = 'local'
className = 'com.ibm.staf.service.stax.TestProcess'
msg = 'Data being sent to the Process Queue after it starts executing'
</script>
<process name="'aProcess'">
<location>machName</location>
<command>'java'</command>
<parms>className</command
<process-action>
<sequence>
<stafcmd>
<location>machName</location>
<service>'QUEUE'</service>
<request>'QUEUE HANDLE %s %s' % (STAXProcessHandle, msg)</request>
</stafcmd>
</sequence>
</process-action>
</process>
Note: If you want to start a process and wait for it to complete (e.g. if you want to submit a START request to the PROCESS service and wait for the process to complete), you should use the process element instead of the stafcmd element.
After the STAF command has completed, the following variables are set and can be referenced by the job:
The stafcmd element has one attribute:
The stafcmd element contains the following required elements. These elements must be specified in the order listed here:
<sequence> <script>resPoolServer = "server1.austin.ibm.com"</script> <script>clientPool = "clientMachinePool"</script> <stafcmd name="'Respool Request Pool'"> <location>resPoolServer</location> <service>'RESPOOL'</service> <request>'REQUEST POOL %s' % (clientPool)</request> </stafcmd> <if expr="RC == STAFRC.STAFOk"> <script>machName = STAFResult</script> <else> <log message="1">'RC=%s STAFResult=%s' % (RC, STAFResult)</log> </else> </if> </sequence>
If you want to submit a START request to the PROCESS service and wait for the process to complete, you should use the process element, not the stafcmd element. However, if you want to submit a START request to the PROCESS service and not wait for it to complete (e.g. start the process asynchronously) before continuing to the next element in the STAX job, then you can use the stafcmd element. Here's an example of starting notepad on a Windows machine:
<sequence> <stafcmd name="'Start Notepad'"> <location>'client1.company.com'</location> <service>'PROCESS'</service> <request>'START COMMAND notepad'</request> </stafcmd> <if expr="RC != STAFRC.STAFOk"> <log message="1"> 'Starting Notepad failed with RC=%s STAFResult=%s' % (RC, STAFResult) </log> </if> </sequence>
In the following example of a stafcmd element, a "SEND MESSAGE" request is submitted to the Email service on machine server1.company.com to send message "STAX Job ID 6 failed" to Jane Smith@company.com.
Note that STAFUtil's wrapData() method is used to turn strings containing spaces into the colon-length-colon form needed for submitting a SEND MESSAGE request to the Email service. Java class STAFUtil needs to be imported from com.ibm.staf in order to use its wrapData() method.
<sequence>
<script>
from com.ibm.staf import STAFUtil
emailServiceMachine = 'server1.company.com'
message = 'STAX Job ID %s failed.' % (STAXJobID)
subject = 'STAX Job Failed'
address = 'JaneSmith@company.com'
request = 'SEND MESSAGE %s' % (STAFUtil.wrapData(message))
request += ' SUBJECT %s' % (STAFUtil.wrapData(subject))
request += ' TO %s' % (address)
</script>
<stafcmd>
<location>emailServiceMachine</location>
<service>'Email'</service>
<request>request</request>
</stafcmd>
<if expr="RC != 0">
<log message="1">
'Email request failed with RC=%s STAFResult=%s' % (RC, STAFResult)
</log>
</if>
</sequence>
In the following example of a stafcmd element which executes a FS QUERY ENTRY request on the local machine. A FS QUERY ENTRY request returns a PyDictionary (aka Map) if successful containing information about the file such as its name, type, size, and timestamp that it was last modified. Log the file information in a verbose format by specifying the STAFResultContext variable:
<sequence>
<stafcmd>
<location>'local'</location>
<service>'FS'</service>
<request>'QUERY ENTRY C:/tmp/testA.exe'</request>
</stafcmd>
<if expr="RC == STAFRC.Ok">
<log message="1">STAFResultContext</log>
<else>
<log message="1">'FS QUERY failed with RC=%s STAFResult=%s' % (RC, STAFResult)</log>
</else>
</if>
</sequence>
The message logged in verbose mode could look like:
{
Name : C:/tmp/testA.exe
Type : F
Upper 32-bit Size : 0
Lower 32-bit Size : 12505
Modified Date-Time: 20030506-19:14:40
}
In the following example of a stafcmd element which executes a FS LIST DIRECTORY LONG DETAILS request on the local machine. A FS LIST DIRECTORY LONG DETAILS request returns a PyList of PyDictionary (aka Map) if successful, where each PyDictionary represents an entry in the specified directory and has keys such as 'name', 'lowerSize', 'type', and 'lastModifiedTimestamp'. It then checks for entries in the directory which are files with a size > 500000 bytes and logs a message containing the names of all the files meeting this criteria, along with their size and the timestamp that they were last modified.
<sequence>
<stafcmd>
<location>'local'</location>
<service>'FS'</service>
<request>'LIST DIRECTORY C:/tmp LONG DETAILS'</request>
</stafcmd>
<script>
msg = ''
if RC == STAFRC.Ok:
for entryMap in STAFResult:
# Check if the entry is a file whose size is greater than 500000 bytes
if entryMap['type'] == 'F' and int(entryMap['lowerSize']) > 500000:
# Print the name, size, and last Modified Timestamp for the entry:
msg += 'Name: %s, Size: %s, Timestamp: %s\n' % \
(entryMap['name'], entryMap['lowerSize'], entryMap['lastModifiedTimestamp'])
else:
msg = 'FS LIST ENTRY failed with RC=%s Result=%s' % (RC, STAFResult)
</script>
<log message="1">msg</log>
</sequence>
The message logged could look like:
Name: en_platformsdk_win2003.exe, Size: 340488704, Timestamp: 20040512-18:07:52 Name: project-docs.tar, Size: 1239040, Timestamp: 20040517-11:57:10
Notes:
After a sub-job has completed (or if it could not be started) the following variables are set and can be referenced by the job:
The job element has the following optional attributes:
The job element contains the following elements in the order listed (with some variations). Refer to the "STAX Document Type Definition (DTD)" section to see the DTD for the job element.
Note that these elements are equivalent to the options allowed for the EXECUTE request (except where noted), so refer to the "EXECUTE" section for more information.
The job element must contain either a job-file or job-data element as follows:
The job element has the following optional elements. Each of these optional elements may specify an if attribute. The if attribute must evaluate via Python to a true or false value. If it does not evaluate to a true value, the element is ignored. The default value for the if attribute is 1, a true value. Note that in Python, true means any nonzero number or nonempty object; false means not true, such as a zero number, an empty object, or None. Comparisons and equality tests return 1 or 0 (true or false).
Specifying only one script file could look like either:
'C:/stax/scriptfiles/scriptfile1.py' or ['C:/stax/scriptfiles/scriptfile1.py']Specifying a list containing three script files could look like:
['C:/stax/scriptfiles/scriptfile1.py', 'C:/stax/scriptfiles/scriptfile2.py', 'C:/stax/scriptfiles/scriptfile3.py']The job-scriptfile element has the following optional attribute:
Note: To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section.
<job name="'Job 2'" monitor="1"> <job-file>'C:/stax/xml/myJob2.xml'</job-file> </job>In the following example of a job element, a sub-job defined by an XML file named tests/testB/xml located on machine myMachine is executed and given a job name of 'Test B'. The job is started by calling function 'Main' and passing this function an argument list of [1, 'server']. In addition, two scriptfiles are specified as well as a couple of script elements. This sub-job is similar to the following STAX EXECUTE request:
EXECUTE FILE /tests/testB.xml MACHINE myMachine JOBNAME "Test B" CLEARLOGS
FUNCTION Main ARGS "[1, 'server1']" SCRIPTFILEMACHINE myMachine
SCRIPTFILE /tests/testB1.py SCRIPTFILE /tests/testB2.py
SCRIPT "MachineList = ['machA', 'machB'] SCRIPT "maxTime = '1h'"
WAIT RETURNRESULT
In addition, a job-action element is run in parallel with
the sub-job after the sub-job has been started. In this example, it
simply logs a message containing the job ID for the sub-job being
executed.
<job name="'Test B'" clearlogs="'Enabled'">
<job-file machine="'myMachine'">'/tests/testB.xml'</job-file>
<job-function>'Main'</job-function>
<job-function-args>[1, 'server1']</job-function-args>
<job-scriptfiles machine="'myMachine'">['/tests/testB1.py', '/tests/testB2.py']</job-scriptfiles>
<job-script>machineList = ['machA', 'machB']<job-script>
<job-script>maxTime = '1h'</job-script>
<job-action>
<log>'Started sub-job %s' % (STAXSubJobID)</log>
</job-action>
</job>
<if expr="RC == 0">
<log message="1">
'Sub-job %s completed. Status: %s Result: %s' % (STAXSubJobID, STAXSubJobStatus, STAXResult)
</log>
<else>
<log message="1" level="'Error'">
'Sub-job could not be started. RC: %s Result: %s' % (RC, STAFResult)
</log>
</else>
</if>
In the following example of a job element, a sub-job defined
by an XML file named C:/tests/Scenario01.xml located on machine myMachine
is executed and given a job name of 'Scenario 01'.
The option to clear the job logs is enabled, and all of the testcase
logging options are enabled as well, and the python output is being
redirected to both the STAX Job User Log and to the STAX Monitor's
Messages panel and Python stdout is logged to the STAX Job User Log
using logging level 'User1'.
EXECUTE FILE C:/tests/Scenario01.xml MACHINE myMachine JOBNAME "Scenario 01"
CLEARLOGS Enabled LOGTCELAPSEDTIME Enabled LOGTCNUMSTARTS Enabled
LOGTCSTARTSTOP Enabled PYTHONOUTPUT JobUserLogAndMsg PYTHONLOGLEVEL User1
<job name="'Scenario 01'" clearlogs="'Enabled'"
logtcelapsedtime="'Enabled'" logtcnumstarts="'Enabled'" logtcstartstop="'Enabled'"
pythonoutput="'JobUserLogAndMsg'" pythonloglevel="'User'">
<job-file machine="'myMachine'">'C:/tests/Scenario01.xml'</job-file>
</job>
<if expr="RC == 0"> <nop/> <else> <call function="'ErrorRoutine'"/> </else> </if>
<sequence> <script>server1 = "machine1.test.austin.ibm.com"</script> <stafcmd> ... </stafcmd> <process> ... </process> <call function="'VerifyRC'"/> </sequence>
Note: To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section.
<parallel> <stafcmd> ... </stafcmd> <process> ... </process> <call function="'VerifyRC'"/> </parallel>
The paralleliterate element contains a single task element. The paralleliterate element performs the task for each value in a list. The iterations of the contained task element are executed in parallel (unlike the iterate element whose tasks are performed serially). Each iteration will be executed on a separate STAX-Thread and existing variables are cloned for each thread. The paralleliterate element is considered to be complete when all its iterations of the task element have completed.
Note: To create a global variable that can be accessed across STAX-Threads, use the STAXGlobal class described in "STAXGlobal Class" section.
The paralleliterate element has the following attributes:
<script>machList = ['machA','machB','machC','machD']</script> <paralleliterate var="machName" in="machList"> <process> <location>machName</location> <command>'ProcessA'</command> </process> </paralleliterate>The following example of a paralleliterate element submits a STAF request to the PING service to ping each machine in a list. If the ping fails, the name of the machine which could not be pinged is added to a list. The STAXGlobal class was used to store this list so that it can be accessed across STAX-Threads that are running in parallel.
<script>
machineList = ['machA', 'machB', 'machC' ]
gPingFailList = STAXGlobal([])
</script>
<paralleliterate var="machName" in="machineList">
<sequence>
<stafcmd>
<location>machName</location>
<service>'PING'</service>
<request>'PING'</request>
</stafcmd>
<if expr="RC != 0">
<script>gPingFailList.append(machName)</script>
</if>
</sequence>
</paralleliterate>
<if expr="len(gPingFailList) != 0">
<message>
'Could not ping the following machines: %s' % (gPingFailList.get())
</message>
</if>
The function element defines a named task and contains a single task element. A function element may only be defined within the root stax element.
The first function called when a job is started is determined by the defaultcall element or by the FUNCTION parameter of an EXECUTE request. Functions are called within a job definition file using the call, call-with-list, or call-with-map elements.
The function element has the following attributes:
The function element can also optionally contain the following elements, in the order listed, before the task element:
Note: This information is not used by STAX in any way and is completely ignored. In particular, this value is never passed to the Python interpreter, and thus, it should be a literal, not a quoted string. If you want to use standard HTML markup such as <p>, <b>, and <ol> in the description, then enclose the text in a CDATA section.
Note: This information is not used by STAX in any way and is completely ignored. In particular, this value is never passed to the Python interpreter, and thus, it should be a literal, not a quoted string. If you want to use standard HTML markup such as <p>, <b>, and <ol> in the description, then enclose the text in a CDATA section.
If more arguments are passed to the function when called than are defined (assuming a <function-other-args> element, or a <function-arg def> with a "type" attribute set to "other", is not specified) or if not all required arguments are passed to the function when called, a STAXFunctionArgValidate signal is raised to indicate the failure and the function is not executed.
A function that does not define its arguments is implicitly defined as:
<function-single-arg> <function-optional-arg name="STAXArg" default="None"/> <function-single-arg>Note that the function argument elements (function-required-arg, function-optional-arg, and function-other-args) can contain a description of the argument. This information, along with the values of the function-prolog element (or the deprecated function-description element) and the function-epilog element, can be used in conjunction with an XSLT stylesheet to generate a nicely formatted HTML file documentating functions and their associated arguments specified in a STAX job. Refer to the "Generating STAX Function Documentation" section for more information on how to generate HTML documentation for your STAX functions.
The function-arg-def element can also optionally contain the following elements, in the order listed:
The function-arg-property element has the following required attributes:
The function-arg-property element can also optionally contain the following elements, in the order listed:
The function-arg-property-data element has the following required attributes:
Goal: Define a simple function containing a sequence element (which can then contain any number of other elements).
<function name="FunctionA"> <sequence> ... </sequence> </function>Goal: Define a function which you intend to import into other STAX XML job files. The requires attribute defines the two additional functions it requires so that they will be automatically imported as well when FunctionB is imported.
<function name="FunctionB" requires="FunctionC FunctionD"> <sequence> ... <call function="'FunctionC'"/> ... <call function="'FunctionD'"/> ... </sequence> </function>Goal: Illustrate the use of local function scope and the STAXGlobal class. Note that only changes to globalVar (which is an instance of the STAXGlobal class) are visible after function Bar completes. Also, all existing variables are visible inside functions with "local" scope. Thus, variables localVar and globalVar are visible inside function Bar, even though function Bar has "local" scope and had not defined them. The following messages are displayed in the STAX Monitor when this example is run:
Before Bar: localVar=[1, 2], globalVar=[1, 2] After Bar: localVar=[1, 2], globalVar=[1, 2, 3]
<stax>
<script>
localVar = [1, 2]
globalVar = STAXGlobal([1, 2])
</script>
<defaultcall function="Main"/>
<function name="Main" scope="local">
<sequence>
<message>
'Before Bar: localVar=%s, globalVar=%s' % (localVar, globalVar)
</message>
<call function="'Bar'"/>
<message>
'After Bar: localVar=%s, globalVar=%s' % (localVar, globalVar)
</message>
</sequence>
</function>
<function name="Bar" scope="local">
<script>
localVar.append(3)
globalVar.append(3)
</script>
</function>
</stax>
Goal: Illustrate the specification of a function which does not allow any arguments to be
passed to it. If any arguments are passed to it when called, a STAXFunctionArgValidate signal
is raised and the function is not run.
<function name="NoArgsFunction">
<function-no-args/>
<sequence>
...
</sequence>
</function>
Goal: Illustrate the specification of a function which requires one argument, duration, to be
passed to it. If zero or more than one argument is passed to it when called,
a STAXFunctionArgValidate signal is raised and the function is not run.
<function name="OneRequiredArgFunction" scope="local">
<function-single-arg>
<function-required-arg name="duration"/>
</function-single-arg>
<timer duration="duration">
<loop>
...
</loop>
</timer>
</function>
This function could be called in any of the following ways with the same result:
<call function="'OneRequiredArgFunction'">'24h'</call>
<call-with-list function="'OneRequiredArgFunction'">
<call-list-arg>'24h'</call-list-arg>
</call-with-list>
Goal: Illustrate the specification of a function which requires two map arguments (returnCode
and result) and has one optional argument (msg). If the two required arguments are
not passed to it when called, a STAXFunctionArgValidate signal is raised and the function is
not run. A function prolog element is provided to describe what this function does
and descriptions of the arguments passed to the function are also provided.
<function name="Check-STAFCmd-RC" scope="local">
<function-prolog>
Checks if a STAFCmd was successful and updates testcase status
</function-prolog>
<function-map-args>
<function-required-arg name="returnCode">
Return Code from a STAF Command
</function-required-arg>
<function-required-arg name="result">
Result from a STAF Command
</function-required-arg>
<function-optional-arg name="msg" default="''">
Message to display if an error occurs
</function-optional-arg>
</function-map-args>
<if expr="RC == 0">
<tcstatus result="'pass'"/>
<else>
<tcstatus result="'fail'">
'%s; RC=%s, Result=%s' % (msg, returnCode, result)
</tcstatus>
</else>
</if>
</function>
This function could be called in any of the following ways with the same result:
<call function="'Check-STAFCmd-RC'">
{ 'returnCode': RC, 'result': STAFResult, 'msg': 'This is the error message' }
</call>
<call-with-map function="'Check-STAFCmd-RC'">
<call-map-arg name="'result'">STAFResult</call-map-arg>
<call-map-arg name="'returnCode'">RC</call-map-arg>
<call-map-arg name="'msg'">'This is the error message'<call-map-arg>
</call-with-map>
Goal: Illustrate the specification of a function which requires a list argument (machName)
and may have any number of additional arguments which will be stored in a list called
testList. This example also shows the use of a STAXGlobal variable which is updated
across STAX-Threads.
<function name="RunTests" scope="local">
<function-list-args>
<function-required-arg name="machName"/>
<function-other-args name="testList"/>
</function-list-args>
<sequence>
<script>
testsRun = STAXGlobal([0]) # Number of tests run
</script>
<paralleliterate var="testName" in="testList">
<sequence>
<process>
<location>machName</location>
<command mode="'shell'">testName</command>
</process>
<script>testsRun[0] += 1</script>
</sequence>
</paralleliterate>
<message>'Ran %s tests' % testsRun[0]</message>
</sequence>
</function>
This function could be called in any of the following ways with the same result:
<call function="'RunTests'">
'local', 'ping machineA', 'dir C:\ > C:\out'
</call>
<call function="'RunTests'">
[ 'local', 'ping machineA', 'dir C:\ > C:\out' ]
</call>
<call-with-list function="'RunTests'">
<call-list-arg>'local'</call-list-arg>
<call-list-arg>'ping machineA'</call-list-arg>
<call-list-arg>'dir C:\ > C:\out'<call-list-arg>
</call-with-list>
Goal: Illustrate the specification of a function that includes a complete desription of
the function using the function-prolog and function-epilog elements. These
elements utilize a CDATA section so that the text can include standard HTML markup so that
when transformed via an XSLT processor (or by using the STAXDoc tool), the text is easily readable.
Note this function is actually provided in the sample STAXUtil.xml file provides in the
STAX zip/tar file.
<function name="STAXUtilLogAndMsg" scope="local">
<function-prolog>
<![CDATA[
<p>
Logs a message and sends the message to the STAX Monitor.
It's a shortcut for specifying the <message> and <log> elements
for the same message.
</p>
]]>
</function-prolog>
<function-epilog>
<![CDATA[
<h4>Returns:</h4>
<p>Nothing. That is, STAXResult = None.</p>
<h4>Example:</h4>
<pre>
<call function="'STAXUtilLogAndMsg'">'Here is my message'</call></pre>
]]>
</function-epilog>
<function-list-args>
<function-required-arg name="message">
The message you want to log in the STAX Job User log and to send to
the STAX Monitor.
</function-required-arg>
<function-optional-arg name="level" default="'info'">
The level of the message to be logged in the STAX Job User log.
</function-optional-arg>
</function-list-args>
<sequence>
<message>message</message>
<log level="level">message</log>
</sequence>
</function>
Goal: Illustrate the specification of a function that accepts a map of
<function-arg-def> elements. It also demonstrates how function arguments
can be denoted as containing private data, as well as defining an enumerated
list of values for a function argument. This example includes an argument
named "color" that allows values of "red" (which would be the default
selection in any form of graphical selection), "blue", and "green".
<function name="RunCommand">
<function-map-args>
<function-arg-def name="command">
<function-arg-description>
A command to execute
</function-arg-description>
</function-arg-def>
<function-arg-def name="user" type="optional" default="'anonymous'">
<function-arg-description>
The user id to run the command under
</function-arg-description>
</function-arg-def>
<function-arg-def name="password" type="optional">
<function-arg-description>
The password for the user id
</function-arg-description>
<function-arg-private/>
</function-arg-def>
<function-arg-def name="numTimes" type="optional" default="1">
<function-arg-description>
The number of times to run the command
</function-arg-description>
<function-arg-property name="type" value="int"/>
</function-arg-def>