Scripting How-To: Error detection

By Erdem posted 08-08-2015 18:02



The <xnm:error> element is used by Junos OS to communicate to a script that an error has occurred. This applies to SLAX version 1.0 and higher.


Finding <xnm:error> Elements in Converted Result Tree Fragment and Node-Set Results


It is important to check for the presence of this element when errors are possible and your script should react to them.  For example, anytime an op or event script performs a commit, it should be ready to handle the various errors that are possible such as a configuration load error, a database locking error, and a commit error.


However, proper care must be taken when building the location path to ensure that an error will actually be caught.  This is especially true given that the <xnm:error> element will be in a different location relative to the context node depending on whether your variable is a converted result tree fragment or was returned as a node-set.


The reason for this difference is that a result tree fragment is converted to a node-set by placing the entire XML structure under a single root node and creating a node-set that contains that root node.  For example:


1	<parent> {
2	    <child> "text";
3	}

Would be converted to the following XML structure:


1	/ {
2	    <parent> {
3	        <child> "text";
4	    }
5	}

And the node-set variable would be pointing at the /.


This behavior differs from a returned node-set.  A returned node-set will have the child of <rpc-reply> as its context node, rather than the root node of the XML structure.


Why does this matter?  Because your script might be dealing with this context node:


1	/ {
2	    <xnm:error> {
3	        <message> "jcs:load-configuration called with invalid parameters";
4	    }
5	}

Or it might be dealing with this context node:


1	<xnm:error> {
2	    <token> "This-Is-The-Bad-API-Element-I-Used";
3	    <message> "syntax error";
4	}

Or this one:


01	<commit-results> {
02	    <routing-engine junos:style='normal'> {
03	        <name> "re0";
04	        <xnm:error> {
05	            <message> "\nssh is not enabled\n";
06	        }
07	        <xnm:error> {
08	            <message> "\n1 error reported by commit scripts\n";
09	        }
10	        <xnm:error> {
11	            <message> "\ncommit script failure\n";
12	        }
13	    }
14	}

The first error message is the result of an invalid call to the jcs:load-configuration template.  The second error message was generated by sending an invalid API element to jcs:invoke().  The third error message was generated by attempting to perform a commit via jcs:invoke() that resulted in a commit failure.


Converted Result Tree Fragments


The most common reason your script will be looking for <xnm:error> in a result tree fragment is when parsing the results of the jcs:load-configuration template.  The following location path can be used to locate a <xnm:error> within the $results variable:


1	$results//xnm:error

Remember that the // is a shortcut for /descendant-or-self::node()/, and the default axis is the child axis.  So the full location path is actually:


1	$results/descendant-or-self::node()/child::xnm:error

This works great for converted result tree fragments because the context node is root, so you always want to start looking at the child of root.




However, using $results//xnm:error will not always work with node-sets.  This is because the <xnm:error> will often be the context node in node-set results.  Remember that$results//xnm:error only works if <xnm:error> is the child of the context node, not if it is the context node itself.  Converted location paths always start at root, but returned node-sets start with an actual element node.  Because of this it is necessary to move back to the context node's parent before attempting to use the // location path operator:


1	$results/..//xnm:error

The .. operator is short for parent::node(), so the full path is actually:


1	$results/parent::node()/descendant-or-self::node()/child::xnm:error

This works whether the <xnm:error> message is the context node or a descendant of the context node, so it is appropriate to use with a node-set variable.  However, it will not work with a converted result tree fragment because there is no parent of the root node so the parent::node() in the path will result in an empty result.


The Ideal Location Path?


For converted result tree fragments we have:


1	$results//xnm:error

And for returned node-sets we have:


1	$results/..//xnm:error

But isn't there a location path that could be used for both?  Actually, yes there is.  Recall that the problem with using the // operator is that the default axis is the child axis.  This prevented us from using the // with a node-set because the <xnm:error> might be the context node rather than the child of the context node.  But what if we didn't use the child axis? 


What if we used the self axis instead?


The following location path will work for both converted result tree fragments as well as node-sets whether the <xnm:error> is the context node or not:


1	$results//self::xnm:error



1	$results/descendant-or-self::node()/self::xnm:error


Why does this work?  Because the descendant-or-self axis returns all nodes that are either the context node or its descendants, and the self axis returns the current context node.  So whether <xnm:error> is the context node, or is a descendant, it will be selected by this location path, making it an ideal location path to use when searching for a <xnm::error>element in your script results.