JUNOS commit scripts and apply-groups

As your network grows, you are bound to get more configuration in your routers and to keep that very configuration uniform and in sync with some form of design rules will become all more important to maintain the quality of your network. In line of doing so JUNOS provides some great tools; first of is the apply-groups in where you can build a basic template of some configuration and then apply it under sections of your configuration...

Here we configure a group called CORE which is to be applied to core interfaces and in this example all we do is set the MTU to 4484;

kll@STH2-CORE-1# show groups
CORE {
    interfaces {
        <*> {
            mtu 4484;
        }
    }
}

To apply this configuration, you can set the "apply-group CORE" statement under your interface:

interfaces {
    ge-0/0/0 {
        apply-groups CORE;
        description "My core facing interface";
    }
}

Or under 'interfaces':

interfaces {
    apply-groups CORE;
    ge-0/0/0 {
        description "My core facing interface";
    }
}

The latter will lead to the configuration from the group "CORE" will be applied not just to ge-0/0/0 but to all interfaces.
In my example (or in real life actually) I use the former version since not all my interfaces are core facing. What becomes a bit more tricky is when it comes to ISIS, PIM, LDP and the other protocols I want to run on my interface. Just enabling the iso family under the interface is easy, but I want to put the interface in under 'protocols isis interface ge-0/0/0' as well. Now that's not as easy with an apply-group - rather I would need something else and this is where the commit scripts come in. They are scripts written in the awkward language of XSLT (which I think is a short for some form of a brain decease) or which can be converted back and forth from SLAX which syntax is a lot easier to work with for those of you out there with a human brian.

Basically what I've done is have a script go through my interfaces and check if they, in their description, have "TYPE:CORE;" in which case certain things will happen, like adding "apply-group CORE" under the interface, under protocols isis/ldp and so forth. Since we want the operator to be aware that configuration was added (and that it was missing before), we print a small warning from the commit script, something along the lines of:

emit-change() {
    with $comment = { expr "Adding standard core interface configuration for " _ $interface _ " under [interface]"; }
...

This is all fine and dandy, but this will be generated each and everytime we commit and so I wanted to add a check that the configuration actually didn't exist before we tried adding it, otherwise we would confuse the operator. And this is where it becomes tricky. I tried doing constructs with "if (contains(../interface[apply-groups = 'CORE']))..." and so on. I think I banged my head towards the wall and keyboard for a good hour before realizing the so obvious truth - the "apply-groups CORE" statement does not exist in the configuration at the time of invocation of the commit script. The apply-group is evaluated first and then the commit script is called. This can easily be viewed with the "display commit-scripts view" command, for example:

kll@STH2-CORE-1# show
apply-groups CORE;
description "Link to some router";
unit 0 {
    apply-groups CORE;
    description "Link to some cool router TYPE:CORE;";
    family inet {
        address 1.3.3.7/31;
    }
}

[edit interfaces ge-0/0/0]
kll@STH2-CORE-1# show | display commit-scripts view
        <interfaces junos:changed="changed">
            <interface junos:changed="changed">
                <name>ge-0/0/0</name>
                <description>Link to some router</description>
                <mtu junos:group="CORE">4484</mtu>
                <unit junos:changed="changed">
                    <name>0</name>
                    <description>Link to some cool router TYPE:CORE;</description>
                    <family junos:changed="changed">
                        <inet junos:changed="changed">
                            <address>
                                <name>1.3.3.7/31</name>
                            </address>
                        </inet>
                    </family>
                </unit>
            </interface>
        </interfaces>
[edit interfaces ge-0/0/0]
kll@STH2-CORE-1#

I shortened the above output somewhat for readability, but the point is that you won't find the apply-group in the configuration when the commit script is looking at it, instead you can see the configuration statements that were added as a result of the evaluation of the apply-group, they are all marked with 'junos:group="CORE"' in my case, since my apply-group is called CORE. So, to verify that the intended apply-group indeed is configured under a interface, I simply do a search under my interface for any lines containing that XML attribute and if it's more than 0 I assume that the apply-group is correctly applied.
Searching for this (or any arbitrary) XML attributes can be done with an XPath that looks like "[@junos:group='CORE']", of course, you might want to adjust the path. For my script, since I'm in a for-each loop under the interface units, my count looks like this:

var $group = 'CORE';
if (count(../../[@junos:group=$group]) == 0) {
...
}

Bit of a hack, but hey, it works! :)

Comments