Topic: tech juniper jaut prev next

tech juniper jaut > Module 08: Junos Commit Scripts

Module 08: Junos Commit Scripts

Commit scripts can evaluate the configuration and display custom warning messages, log custom system log messages, change the configuration or generate a commit error and halt the commit.

Changes made by commit scripts are persistent or transient. Persistent changes are made to the candidate configuration and then saved to the active configuration and stored configuration. Transient changes are made to the checkout configuration before being copied to the active configuration. Transient changes are not saved to the candidate configuration or juniper.conf and are removed if the commit script is ever deleted.

Configuration Groups

groups {
    MTU {
     interfaces {
        <*> {
            mtu 1500;
        }
    }
}
...
interfaces {
    ge-0/0/0 {
        apply-groups MTU;
        ...

When making a commit operation, the group information is evaluated. The post-inheritence configuration is seen by the commit scripts.

The post-inheritence configuration can be viewed with;

show | display commit-scripts view

SLAX for Commit Scripts

Recommended template for commit scripts.

version 1.0;

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";

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

match configuration {
    ...
}

...

Define errors with <xnm:error>. Errors halt the commit. Define warnings with <xnm:warning>.

<xnm:warning>
    <edit-path>[edit interfaces interface ge-0/0/2 unit 0]</edit-path>
    <message>'family mpls' is configured on this interface</message>
</xnm:warning>

The jcs:edit-path([$dot=dot]) template converts an XPath expression for the current hierarchy into Juniper hierarchy representation.

If the edit-path expression is not used, it defaults to the current hierarchy being evaluated in the template.

A commit script makes a persistent change with <change> and a transient change with <transient-change>. The easiest way to make a change is with the jcs:emit-change template.

jcs:emit-change(
    [$dot=dot][,$tag=tag][,$message=message][,$content=content]
    )

dot is the XPath expression of the hierarchy to make the change. The default is the current location. tag is change or transient-change, the default is change. message is sent to the user and content is the content of the change.

match configuration {
    for-each (interfaces/interface[name != 'lo0']/unit) {
        /* The context (dot location) moves to the unit. */
        if (not(family/mpls)) {
            call jcs:emit-change() {
                with $content = {
                    <family> {
                        <mpls>;
                    }
                }
                with $message = "Added 'family mpls'";
        }
    }
}

To enable a commit script, place it in /var/db/scripts/commit directory. Configure it in [edit system scripts commit];

set system scripts commit file persistent.slax

An additional command must be added to enable transient changes;

set system scripts commit allow-transients

Traceoptions can be enabled to help debug scripts;

set system scripts commit traceoptions file commit.log
set system scripts commit traceoptions flag all

By default, changes are added to the configuration. They can instead be deleted, activated, or deactivated by adding XML attributes.

<address delete="delete"> {
    <name>192...;
<address active="active"> {
    <name>192...;
<address inactive="inactive"> {
    <name>192...;

Hierarchies can be renamed, using the old name as an attribute;

<address rename="rename" name="..."> {
    <name>192...;

Hierarchies can be replaced;

<system> {
    <services replace="replace">

Hierarchies can be inserted before existing hierarchies. Again, name references the target to insert before;

<address insert="before" name="..."> {
    <name>192...;

Python Commit Scripts

Similar to op scripts. Use the following functions to output configuration and messages;

jcs.emit_warning()
jcs.emit_error()
jcs.syslog()
jcs.emit_change(content, tag, format)

As with op scripts, set the language python statement in the configuration to allow Python scripts to run. Enable individual scripts in the configuration. Script permissions must meet the requirements for op scripts.

For example, to check that the fxp0 interface has an IP address;

fxp0_interface_ip = root.xpath(
    "./interfaces/interface[name='fxp0']/unit[name='0']/family/inet/address/name"
    )
if not(fxp0_interface_ip):
    jcs.emit_error("fxp0 must have an IP address")

In calls to jcs.emit_change(), content is the full configuration path. Relative dot location paths, like in SLAX, will not work. format is the format of the configuration data. The only supported format is XML.

from junos import Junos_Configuration
import jcs

unit_list = Junos_Configuration.xpath("interfaces/interface[name!='lo0']/unit")
for unit in unit_list:
    if unit.find("family/mpls") is None:
        interface_name = unit.findText("../name")
        unit_name = unit.findText("name")
        config = """
        <interfaces>
            <interface>
                <name>{0}</name>
                <unit>
                    <name>{1}</name>
                    <family><mpls></family>
                </unit>
            </interface>
        </interfaces>
        """.format(interface_name, unit_name)
        jcs.emit_change(config, "change", "xml")
        jcs.emit_warning("Added 'family mpls' to {} {}".format(interface_name, unit_name))

‘apply-macro’ Configuration Element

apply-macro elements can be added at any level in the configuration. Commit scripts process macros.

Here is an example application of a macro with parameters;

apply-macro example {
    enable;
    count 10;
}

edit family inet address 10.0.22.1/30 apply-macro create-bgp
[edit family inet address 10.0.22.1/30 apply-macro create-bgp]
set asn 65111
set routes full-routes
set customer-id ab21332

SLAX ‘apply-macro’ Example

Use jcs:parse-ip function to extract the IP address and jcs:split to split the address at the decimal points; or, use jcs:regex.

Parameter extraction and error checking are skipped;

var $localaddr=jcs:regex(
    '([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)(\/[0-9]+\)',
    ../name
    );
var $remoteaddr=$localaddr[2] _ ($localaddr[3] + 1);

call jcs:emit-change() {
    with $dot=../../../../../../../protocols/bgp;
    with $content= {
        <group> {
            <name>$routes;
            <neighbor> {
                <name>$remoteaddr;
                <peer-as>$asn;
                <import>$customer-id _ '_' _ $asn;
            }
        }
    }
    with $tag="transient-change";
}

Python ‘apply-macro’ Example

def main():
    macro_list = Junos_Configuration.xpath(
        'interfaces/interface/unit/family/inet/address/apply-macro[name="create-bgp"]'
        )
    for macro in macro_list:
        asn = macro.findtext('data[name="asn"]/value')
        local_ip = macro.fintext('../name')
        m = re.match(
            '([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)(\/[0-9]+\)',
            local_ip
            )
        remote_ip = m.group(1) + str(int(m.group(2)) + 1)
        change_xml = """
            <protocols>
                <bgp>
                    <group>
                        <name>{0}</name>
                        <neighbor>
                            <name>{1}</name>
                            <peer-as>{2}</peer-as>
                            <import{3}_{2}</import>
                        </neighbor>
                    </group>
                </bgp>
            </protocols>
            """.format(routes, remote_ip, asn, cid).strip()
        jcs.emit_change(change_xml, "transient-change", "xml")

Simplifying Configuration Storage

Using transient configuration changes simplifies configuration storage, possibly making it easier to display and read. It also makes managing configuration changes that must be propagated in multiple places, because the configuration is propagated every time the script is run and configurations regenerated.

When using persistent configuration changes, the ‘apply-macro’ statement must be deleted after making the change to prevent the change being made mutiple times.

Optional Scripts

If the script file hierarchy is marked optional, and the script file cannot be found, it will be silently skipped.

Rollback Behaviour

rollback does not restore transient changes included in the last commit. rollback loads a candidate configuration that includes the instruction to run a commit script. After the rollback is performed with a commit, the commit scripts included in the rollback configuration will be perfomed, making any transient or permanent changes again.

If the commit script has changed since the previous commit (when the rollback configuration was first committed), unintended results can occur (the actual, committed configuration may be different to the actual, committed configuration the first time around).

Commit Script Checksums

A checksum verifies the integrity of the script and prevents the script from running if it has been changed.

run file checksum sha-256 /var/db/scripts/commit/hello.py
set file hello.py checksum sha-256 ...

Only SHA256 checksums are supported for commit scripts.