Junos Space Developer
Showing results for 
Search instead for 
Do you mean 
Reply
Juniper Employee
Posts: 4
Registered: ‎03-05-2014
0 Kudos
Accepted Solution

Conflglets: insert x before y

Good morning.

 

I know that we can use the delete and replace tags in a configlet, but how does one do a insert ... before

 

I need to add a routing policy to be executed before an existing one, so:

 

routing instances {

  foo {

    interface ge-x/y/z

    vrf-import baz

  }

}

Needs to be come:

routing instances {

  foo {

    interface ge-x/y/z

    vrf-import [bar baz]

  }

}

 

CLI would look like:

insert routing-instances foo vrf-import bar before baz

Recognized Expert
Posts: 325
Registered: ‎10-04-2012
0 Kudos

Re: Conflglets: insert x before y

This isn't possible with CLI configlets.

 

Instead an approach using a local SLAX script could be used to perform this task, leveraging the Junos Space change-management API which would allow a configuration change to be made in an XML format which would support the insert before/after capabilities.

 

I have a sample script that would demonstrate this approach, I'll upload the code once I'm able to do so.

 

Regards,

Andy

Recognized Expert
Posts: 325
Registered: ‎10-04-2012
0 Kudos

Re: Conflglets: insert x before y

As mentioned before, here is an example of a Junos Space script that can move a term.  Since the script is a local script it gets executed locally by Junos Space and therefore there is no need to stage/deploy the script on the device.

 

/* Example Junos Space Local Script to move a term via Change-Request API */
/*
 * The script must be executed from within Device Management, and _NOT_ Scripts!
 *
 * Note: there are no error checks here!
*/
version 1.1;

ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
ns str = "http://exslt.org/strings";
ns func extension = "http://exslt.org/functions";
ns curl extension = "http://xml.libslax.org/curl";
ns xutil extension = "http://xml.libslax.org/xutil";
ns jspace = "http://jspace-utils/asharp@juniper.net";

import "../import/junos.xsl";

/* Junos Space specific context, name and description */
/* @CONTEXT = "/device" */
/* @NAME = "Move Term" */
/* @DESCRIPTION = "Move Term Example" */
/* @ISLOCAL = "true" */
/* @PASSSPACEAUTHHEADER = "true" */
/* @PASSDEVICECREDENTIALS = "true" */
/* @EXECUTIONTYPE = "GROUPEDEXECUTION" */

var $curl = curl:open();
var $fabric = jspace:fabric();

var $arguments = {
    <argument> {
        <name> "FILTER";
        <description> "Name of Filter";
    }
    <argument> {
        <name> "TERM";
        <description> "Name of Term to move";
    }
    <argument> {
        <name> "POSITION";
        <description> "Position [before | after]?";
    }
    <argument> {
        <name> "TERM2";
        <description> "Name of TERM to be positioned against";
    }
}

main <op-script-results> {
    <output> {
        /* only proceed if the fabric can be identified */
        if ($fabric) {
            var $devices = jspace:credentials();

            if ($devices) {
                for-each ($devices/device) {
                    sort host;
                    var $myhost = host;
                    /* find the device using API call */
                    /*
                     * url, content-type, item-type, condition, item
                    */
                    var $findDevice = curl:perform($curl,jspace:findItem(
                        "http://" _ $fabric _ ":8080/api/space/device-management/devices",
                        "",
                        "name",
                        "eq",
                        $myhost
                    ));

                    /* get the key of the selected device */
                    var $deviceKey = $findDevice/data/devices/device/@key;

                    /* make changeRequest using the API */
                    /*
                     * url, content-type, deviceKey, orig-name, new-name, resync
                    */
                    var $results = curl:perform($curl,jspace:moveTerm(
                        "http://" _ $fabric _ ":8080/api/space/configuration-management/change-requests",
                        "application/vnd.net.juniper.space.configuration-management.change-request+xml;version=2;charset=UTF-8",
                        $deviceKey,
                        $FILTER,
                        $TERM,
                        $POSITION,
                        $TERM2,
                        "true"
                    ));

                    if ($results/headers/code == "200") {
                        /* job accepted */
                        expr "Moving term " _ $TERM _ " in " _ $FILTER _ " " _ $POSITION _ " term " _ $TERM2 _ "\n";

                        /* need to see if the commit was successful or not */
                        /* to get the XML data which is embedded within more xml needs lots of parsing... */
                        /* parse and convert the raw-data node */
                        var $parseOne = {
                            uexpr $results/raw-data/.;
                        }
                        var $parseTwo = xutil:string-to-xml($parseOne);
                        var $parseThree := {copy-of $parseTwo;}

                        /*
                           parse and convert the change-request/xmlData node
                           this step is only required if you want to display
                           the configuration that was pushed to the device.
                        */
                    /*
                        var $xmlDataOne = {
                            uexpr $parseThree/change-request/xmlData/.;
                        }
                        var $xmlDataTwo = xutil:string-to-xml($xmlDataOne);
                        var $xmlDataThree := {copy-of $xmlDataTwo;}

                        copy-of $xmlDataThree;
                    */

                        /* display the change-request/result */
                        uexpr substring-before($parseThree/change-request/result/.,"rpc-reply: ");

                    } else {
                        expr "Job failed with error code " _ $results/headers/code _ "\n";
                    }
                }
            }
        } else {
            expr "Could not identify the fabric!";
        }
    }
}

/*
 * Used to identify the fabric that the script has been executed on
 * knowing this is essential in a multi fabric environment when making
 * RESTful API calls to Junos Space and using the session cookies for
 * authentication.
*/
<func:function name="jspace:fabric"> {
    if( $CONTEXT ) {
        var $fabric = {
            if ( contains( $JSESSIONID, "server") ) {
                expr jcs:regex("\.(space-[A-Za-z0-9]+):server",$JSESSIONID)[2];
            } else {
                expr jcs:regex("\.(space-[A-Za-z0-9]+)",$JSESSIONID)[2];
            }
        }
        <func:result select="$fabric">;
    } else {
        <func:result select="false()">;
    }
}

/* findItem */
<func:function name="jspace:findItem">
{
    param $url;
    param $content-type;
    param $item-type;
    param $condition;
    param $item;
    var $curlData := {
        <method> "get";
        <insecure>;
        <url> $url;
        <header name="Cookie"> "JSESSIONID=" _ $JSESSIONID _ ";Path=/;";
        <header name="Cookie"> "JSESSIONIDSSO=" _ $JSESSIONIDSSO _ ";Path=/;";
        <param name="filter"> "(" _ $item-type _ " " _ $condition _ " '" _ $item _ "')";
        <content-type> $content-type;
        <format> "xml";
    }
    <func:result select=" $curlData ">;
}

/* changeInterfaceName */
<func:function name="jspace:moveTerm">
{
    param $url;
    param $content-type;
    param $deviceId;
    param $FILTER;
    param $TERM;
    param $POSITION;
    param $TERM2;
    param $syncAfterPush;   /* true | false */
    var $curlData := {
        <method> "post";
        <insecure>;
        <url> $url;
        <content-type> $content-type;
        <header name="Cookie"> "JSESSIONID=" _ $JSESSIONID _ ";Path=/;";
        <header name="Cookie"> "JSESSIONIDSSO=" _ $JSESSIONIDSSO _ ";Path=/;";
        <format> "xml";
        <contents> {
            uexpr '
            <change-request>
                <name>Change_move_term</name>
                <description>Change_move_term</description>
                <xmlData>
                    <![CDATA[
                        <configuration>
                            <firewall>
                                <family>
                                    <ccc>
                                        <filter>
                                            <name>' _ $FILTER _ '</name>
                                            <term insert="' _ $POSITION _ '" name="' _ $TERM2 _ '">
                                                <name>' _ $TERM _ '</name>
                                            </term>
                                        </filter>
                                    </ccc>
                                </family>
                            </firewall>
                        </configuration>
                    ]]>
                </xmlData>
                <device href="/api/space/device-management/devices/' _ $deviceId _ '"/>
                <syncAfterPush>' _ $syncAfterPush _ '</syncAfterPush>
            </change-request>
            ';
        }
    }
    <func:result select=" $curlData ">;
}

/*
 * Can be used to identify selecte devices, and their credentials.
 * Must be used with PASSDEVICECREDENTIALS  = "true" annotation for credentials.
 * Must be used with EXECUTIONTYPE = "GROUPEDEXECUTION" annotation for multiple devices
*/
<func:function name="jspace:credentials"> {
    if( $CONTEXT ) {
        var $splitCredentials = str:split( $credentials, "\\;" );
        var $targets := {
            for-each ( $splitCredentials ) {
                var $splitCredential = str:split( ., "\\:" );
                var $user-target = str:split($splitCredential[1], "\@");
                var $host = substring-before( substring-after( $deviceipmap, substring-after( $splitCredential[1], "@" ) _ "\":\"" ), "\"" );
                <device> {
                    <ipAddr> {
                        expr $user-target[2];
                    }
                    <user> {
                        expr $user-target[1];
                    }
                    <passwd> {
                        expr $splitCredential[2];
                    }
                    <host> {
                        expr $host;
                    }
                }
            }
        }
        <func:result select="$targets">;
    } else {
        <func:result select="false()">;
    }
}

Don't hesitate to ask if you have any questions.

 

Regards,

Andy

Juniper Employee
Posts: 4
Registered: ‎03-05-2014
0 Kudos

Re: Conflglets: insert x before y

This is an awesome solution, and I can adapt it to what I need to do (prepend a policy to a vrf import statement)

Recognized Expert
Posts: 325
Registered: ‎10-04-2012
0 Kudos

Re: Conflglets: insert x before y

Hi,

I did give this a bit more thought and for the requirement you have for the vrf-import which is simpler than a term move it should be possible just inside a configlet, I'll post an example once I've tested the VTL logic to recreate the statement.

Although as you can see a slax approach would also work but requires a bit more editing to match your needs...

Regards,
Andy
Highlighted
Recognized Expert
Posts: 325
Registered: ‎10-04-2012
0 Kudos

Re: Conflglets: insert x before y

OK, so an approach using a CLI Configlet would be something along the following lines:

 

<?xml version="1.0" encoding="iso-8859-1"?><cli-configlets>
<cli-configlet>
<!-- mandatory --><name>Insert vrf-export</name>
<category>JNET</category>
<context>/device</context>
<!-- mandatory --><device-family>ACX/J/M/MX/T/TX/PTX/EX92xx</device-family>
<description>Example configlet to insert a vrf-export entry before another.</description>
<!-- mandatory --><execution-type>Grouped</execution-type>
<preview-show-parameters>true</preview-show-parameters>
<preview-show-configuration>true</preview-show-configuration>
<postview-show-parameters>true</postview-show-parameters>
<postview-show-configuration>true</postview-show-configuration>
<cli-configlet-pages><!-- At least one configlet page required -->
<cli-configlet-page>
<page-number>1</page-number>
<!-- mandatory --><cli-text>routing-instances {
    $routing-instance.get(0) {
        replace: vrf-export [ #foreach ($export in $list-vrf-exports)#if ($export == $vrf-export.get(0))$new-vrf-export $vrf-export.get(0) #else${export} #end#end];
    }
}
</cli-text>
</cli-configlet-page>
</cli-configlet-pages>
<cli-configlet-params>
<cli-configlet-param>
<!-- mandatory --><parameter>routing-instance</parameter>
<!-- mandatory --><display-name>routing-instance</display-name>
<!-- mandatory --><parameter-type>Selection Field</parameter-type>
<description>Select a routing-instance</description>
<parameter-scope>Device Specific</parameter-scope>
<configured-value-xpath/>
<default-value/>
<selection-values-xpath>/device/configuration/routing-instances/instance/name/text()</selection-values-xpath>
<selection-values/>
<!-- mandatory --><parameter-order>1</parameter-order>
</cli-configlet-param>
<cli-configlet-param>
<!-- mandatory --><parameter>new-vrf-export</parameter>
<!-- mandatory --><display-name>New VRF-EXPORT</display-name>
<!-- mandatory --><parameter-type>Text Field</parameter-type>
<description>Enter a new vrf-export entry</description>
<parameter-scope>Device Specific</parameter-scope>
<regex-value/>
<configured-value-xpath/>
<default-value/>
<!-- mandatory --><parameter-order>2</parameter-order>
</cli-configlet-param>
<cli-configlet-param>
<!-- mandatory --><parameter>vrf-export</parameter>
<!-- mandatory --><display-name>vrf-export</display-name>
<!-- mandatory --><parameter-type>Selection Field</parameter-type>
<description>Insert new vrf-export before this entry</description>
<parameter-scope>Device Specific</parameter-scope>
<configured-value-xpath/>
<default-value/>
<selection-values-xpath>/device/configuration/routing-instances/instance[name=&apos;$routing-instance.get(0)&apos;]/vrf-export/text()</selection-values-xpath>
<selection-values/>
<!-- mandatory --><parameter-order>3</parameter-order>
</cli-configlet-param>
<cli-configlet-param>
<!-- mandatory --><parameter>export</parameter>
<!-- mandatory --><display-name>export</display-name>
<!-- mandatory --><parameter-type>Invisible Field</parameter-type>
<description>temporary variable</description>
<parameter-scope>Device Specific</parameter-scope>
<configured-value-xpath/>
<default-value/>
<!-- mandatory --><parameter-order>99</parameter-order>
</cli-configlet-param>
<cli-configlet-param>
<!-- mandatory --><parameter>list-vrf-exports</parameter>
<!-- mandatory --><display-name>list-vrf-exports</display-name>
<!-- mandatory --><parameter-type>Invisible Field</parameter-type>
<description>temporary variable to store a list of current vrf-exports</description>
<parameter-scope>Device Specific</parameter-scope>
<configured-value-xpath>/device/configuration/routing-instances/instance[name=&apos;$routing-instance.get(0)&apos;]/vrf-export/text()</configured-value-xpath>
<default-value/>
<!-- mandatory --><parameter-order>99</parameter-order>
</cli-configlet-param>
</cli-configlet-params>
</cli-configlet>
</cli-configlets>

With this approach, you have 3 parameters to provide values for.

1. Routing-Instance :  a selection list where you can select the routing-instance to modify. e.g. "foo"

2. New VRF-EXPORT : the new export entry that you wish to add e.g. "bar"

3. vrf-export : the existing entry that you wish to insert "bar" before.  e.g. "baz".

 

This would then produce a configuration as follows:

routing-instances {
    foo {
        replace: vrf-export [ bar baz ];
    }
}

 

This is possible using a configlet approach, because the vrf-export statement is just rebuilt with the relevant order created.  With terms it's not so practical hence the script approach is better suited.  But hopefully the configlet and VTL should be easy enough to follow.

 

Regards,

Andy