Automation
Automation

Scripting How-To: Useset:difference() in SLAX to quickly figure out what's different

by Cordelia on ‎08-08-2015 06:14 AM - edited on ‎09-18-2017 02:52 PM by Administrator Administrator (957 Views)

Overview


How do you examine two sets of XML and figure out what's different? This applies to SLAX version 1.0 and higher.

Feature


Sometimes you need to be able to compare two node-sets and figure out if they're exactly the same, or if they're different, which items are different?   The exslt "set" extension is just the thing for this.  However, its usage in SLAX is non-obvious (at least it was to me).


An obvious use case for this application of "set:difference()" is examining data sets from before and after something like a configuration change.  And that is precisely the use case that ultimately led Jeremy to develop JSNAP, which obviates the need for any additional SLAX coding.   Another use case might be calculating the lowest allocatable item from a sparsely-populated data range  An example of this could be something like allocating an OID index, a VLAN ID or user ID from a specified data range which has pre-existing (and probably non-consecutive) entries.


To that end, below is an example of some SLAX code for detecting a difference between two XML data sets, which you may find useful.  This example  uses "set:difference()"  to find the first available VLAN from a pool of VLAN IDs.


Script in a nutshell:

 

  • The script initializes a data range that defines the allowable range of VLANS that can be assigned on the device ($vlan-range).
  • The script then scans the configuration and builds up a list of the VLANs that are already in use in the configuration ($user-vlans).
  • At that point, we can then derive the pool of available VLAN IDs ($available-vlans) by taking the difference of $vlan-range and $user-vlans.

The line that actually uses the set:difference() function is highlighted in red, below.  Since this op script is just for demonstration, it simply displays the lowest available VLAN that you can use.   A "real" script would do something with that data.

Have fun.

/doug

 

 

001	version 1.0;
002	/*
003	* File: vlan.slax
004	* Description: use set:difference to find 1st available item from a "pool"
005	*/
006	 
007	ns junos = "http://xml.juniper.net/junos/*/junos";
008	ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
009	ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
010	ns func extension = "http://exslt.org/functions";
011	ns set = "http://exslt.org/sets";
012	 
013	import "../import/junos.xsl";
014	 
015	var $top-vlan = 110;  /* upper end of vlan range */
016	var $bottom-vlan = 96; /* lower end of vlan range */
017	 
018	match / {
019	    <op-script-results> {
020	 
021	        /*
022	         * First, build a node-set that describes the range of ALL VLANs that
023	         * we are allowed to allocate from.  (used later)
024	         */
025	        var $vlan-range := {
026	            call create-vlan-range($item = $bottom-vlan);
027	        }
028	        expr jcs:output("Allowable VLAN ID range: ",$bottom-vlan,"-",$top-vlan);
029	 
030	        /*
031	         * Open mgd session and get configuration.
032	         */
033	        var $con = jcs:open();
034	        if (not($con)) {
035	            expr jcs:output("Error connecting to mgd.");
036	        }
037	        var $rpc-config-req =  <get-configuration database="committed">;
038	        var $configuration = jcs:execute($con,$rpc-config-req);
039	 
040	        /*
041	         * Collect ALL VLAN IDs assigned in the interfaces stanza and save into a node-set.
042	         */
043	        var $used-vlans := {
044	            for-each ($configuration/interfaces//vlan-id) {
045	                <vlan> {
046	                    <id> .;
047	                }
048	            }
049	        }
050	       
051	        /*
052	         * Just for fun: show the count of VLANs IDs in use and display this list.
053	         */
054	        var $vlan-count = count($used-vlans/vlan);
055	        expr jcs:output("There are ", $vlan-count, " VLANs currently in use in the config:");
056	        for-each ($used-vlans/vlan) {
057	            expr jcs:output("\t", id);
058	        }
059	 
060	       /*
061	         * Now we can derive the set of "available VLANs" by getting the
062	         * difference of $vlan-range and $used-vlans.  For that we will use the
063	         * exslt set:difference function.   Usage is a little 'non-obvious',
064	         * since we have to compare references to the nodes, not not the node
065	         * values.  It's a little weird but it works, is fast and only takes 3 steps:
066	         */
067	         
068	        var $ref-vlan-range = $vlan-range/vlan;
069	        var $ref-used-vlans = $vlan-range/vlan[(id == $used-vlans/vlan/id)];var $available-vlans = set:difference($ref-vlan-range, $ref-used-vlans);
070	        /*
071	         * Just for fun: dump out the list of VLAN IDs still available
072	         */
073	        expr jcs:output("These are, then, the VLAN IDs still available:");
074	        for-each ($available-vlans) {
075	            expr jcs:output("\t", . );
076	        }
077	 
078	        /*
079	         * Now all we have to do is select the 1st one in the list (should
080	         * already be sorted).  That way, we're always allocaing "from the
081	         * bottom up"
082	         * Just for fun, show the user which one you've picked:
083	         */
084	        var $next-vlan = $available-vlans[position() == 1];
085	        expr jcs:output("We will use ID ", $next-vlan, " for our next VLAN.");
086	    }
087	}
088	/*********************************************************************************************************
089	* End of main.  Templates, functions below here.
090	*/
091	 
092	/*********************************************************************************************************/
093	template create-vlan-range($item) {
094	/*
095	* Creates an xml node-set that describes the allowable VLAN range
096	*/
097	    if ($item <= $top-vlan) {
098	        <vlan> {
099	            <id> $item;
100	        }
101	        call create-vlan-range($item = $item + 1);
102	    }
103	}
104	/*********************************************************************************************************/

 

Source


Original from Douglas McPherson blog post March 19, 2013. Released to TechWiki with permission.