Topic: tech juniper jaut prev next
tech juniper jaut > 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.
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
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...;
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
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
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";
}
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")
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.
If the script file hierarchy is marked optional
, and the script file cannot
be found, it will be silently skipped.
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).
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.