Topic: tech juniper jaut prev next
tech juniper jaut > Module 07: Junos Op Scripts
Can be executed manually from the CLI, upon login, or as part of another script or a NETCONF session. Here is a ‘hello world’ script. The ‘ns’ declarations are boilerplate and will be excluded from future examples.
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 / {
<op-script-results> {
<output> "Hello, world!";
}
}
Copy the script to /var/db/scripts/op. Configure the op script at the [edit system scripts] hierarchy.
set system scripts op file helloworld.slax
show system scripts
op {
file helloworld.slax
}
Run the script with the ‘op’ command, omitting the extension for .slax, .xsl, and .py files.
op helloworld
The following arguments are available to every script:
$product
$user
$hostname
$localtime
$localtime_iso
$script
The SLAX ‘param’ statement is used to process arguments. If parameters are not specified in the CLI, their value will be passed as empty.
version 1.0;
import "../imports/junos.xsl";
param $message;
match / {
<op-script-results> {
<output> $message;
}
}
Values can be passed as follows;
op helloworld message "Hello, world!"
Provide context-sensitive help on how to use the script.
var $arguments = {
<argument> {
<name> "message";
<description> "The message that should be printed.";
}
}
The jcs:invoke(rpc)
function invokes an RPC on the device. It takes and
returns XML.
To find the corrent RPC from the CLI, use;
show interfaces | display xml rpc
Alternatively, consult the documentation.
var $intoutput = jcs:invoke('get-interface-information');
for-each ($intoutput/physical-interface) {
expr jcs:outptu(name);
}
XML input can also be passed to jcs:invoke().
var $interface-request = <get-interface-information. {
<interface-name> "ge-1/0/0";
<extensive>
}
var $intoutput = jcs:invoke($interface-request);
View the op script log with;
show log op.log
XMLRPC requests can return errors. The error will be enclosed in an xnm:error tag. An error is detected by the presence of this tag.
var $ping-request = <ping> {
<host> "10.0.20.1";
<routing-instance> "soinos";
}
var $ping-output = jcs:invoke($ping-request);
if ($ping-output//xnm:error) {
<output> "Ping command returned an error: " _ $ping-output//xnm:error/message;
}
These two options result in the same output.
<output> {
expr "The IP address is ";
expr $ip-address;
}
<output> "The IP address is " _ $ip-address;
The CLI already translates XMLRPC responses into CLI output when the user runs a command. If an XML tag recognised by the CLI is output from an op script, the output is automatically translated into CLI format. Alternatively, output can be formatted manually using the tag.
param $autonomous-system;
var $arguments = {
<argument> {
<name> "autonomous-system";
<description> "The AS to display results for.";
}
}
match / {
<op-script-results> {
var $bgp-info = jcs:invoke('get-bgp-summary-information');
<output> "header";
<bgp-information> {
if (jcs:empty($autonomous-system)) {
for-each ($bgp-info/bgp-peer) {
<bgp-peer junos:style="terse"> {
copy-of ./*;
}
}
} else {
for-each ($bgp-info/bgp-peer[peer-as=$autonomous-system]) {
<bgp-peer junos:style="terse"> {
copy-of ./*;
}
}
}
}
}
}
param $interface
var $arguments = {
<argument> {
<name> "interface";
<description> "The interface to disable & re-enable.";
}
}
match / {
<op-script-results> {
if (jcs:empty($interface$)) {
<xnm:error> {
<message> "Required parameter 'interface' missing.";
}
} else {
var $con = jcs:open();
if (not($con)) {
<xnm:error> {
<message> "Unable to connect to local mgd.";
}
}
else {
var $disable-xml = {
<configuration> {
<interfaces> {
<interface> {
<name> $interface;
<disable>;
}
}
}
}
expr jcs:output("Disabling interface...");
var $disable-results := {
call jcs:load-configurtion($connection=$con, $configuration=$disable-xml);
}
<output> "Results of commit to disable the interface:";
copy-of $disable-results;
if ($disable-results//xnm:error) {
<xnm:error> {
<output> "Script aborted due to errors while disabling the interface.";
}
} else {
expr jcs:output("Interface ", $interface, " disabled.");
expr jcs:sleep(5,0);
}
var $enable-xml = {
<configuration> {
<interfaces> {
<interface> {
<name> $interface;
<disable delete="delete">;
}
}
}
}
expr jcs:output("Enabling interface...");
var $enable-results := {
call jcs:load-configurtion($connection=$con, $configuration=$enable-xml);
}
<output> "Results of commit to enable the interface:";
copy-of $enable-results;
if ($enable-results//xnm:error) {
<xnm:error> {
<message> "Error enabling the interface.";
}
} else {
expr jcs:output("Interface ", $interface, " enabled.");
}
}
expr jcs:close($con);
}
}
}
Junos does not execute unsigned Python scripts by default. Set ‘system scripts language python’ to enable execution of unsigned Python scripts.
show system scripts
op {
file hello-python.py;
}
language python;
Save scripts to the appropriate location.
Set ‘system scripts load-scripts-from-flash’ to enable flash storage.
Hard drive storage Flash storage
OP /var/db/scripts/op /config/scripts/op
Commit /var/db/scripts/commit /config/scripts/commit
Event /var/db/scripts/event /config/scripts/event
SNMP /var/db/scripts/snmp /config/scripts/snmp
For unsigned scripts to run: * The script owner is either root or a member of the super-user class. * Only the script owner has write permission for the file. * The script executer has read permission for the file.
The only notable difference from an off-box script is that Device()
requires
no parameters to represent the local device.
from jnpr.junos import Device
if __name__ == "__main__":
with Device() as dev:
print(dev.facts)
Place arguments in a dictionary. The dictionary must be called ‘arguments’ and contain key-value pairs. argparse only parses the arguments when the script is running.
import argparse
arguments = {
"interface": "Interface to display"
}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
for key in arguments:
parser.add_argument('-' + key, required=True, help=arguments[key])
args = parser.parse_args()
print(args)
with Device() as dev:
res = dev.rpc.get_interface_information(interface_name=args.interface, terse=True, normalize=True)
print args.interface + " status: " + res.findtext("logical-interface/oper-status")
XPATH = "//address-family[address-family-name=$family]/interface-address/ifa-local"
# Copy the 'family' variable into the XPath context.
for elem in res.xpath(XPATH, family=args.family):
print args.family + " address " + elem.text
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
from jnpr.junos.exception import U
import argparse
from time import sleep
arguements = {
"interface": "",
"delay": ""
}
def change_config(dev_cfg, set_cmds, op_descr):
print op_descr + " : Locking the configuration"
try:
dev_cfg.lock()
except LockError:
print "Error: unable to lock"
return False
print op_descr + " : Loading configuration changes"
try:
dev_cfg.load(set_cmds, format="set")
except ConfigLoadError as err:
print "Unable to load coniguration changes: \n" + err
print Unlocking the configuration
try:
dev_cfg.unlock()
except UnlockError:
print "Error: Unable to unlock configuration"
return False
print op_descr + " : Committing the configuration"
try:
dev_cfg.commit()
except CommitError:
print "Error: Unable to commit the configuration"
print "Unlocking the configuration"
try:
dev_cfg.unlock()
except UnlockError:
print "Error: Unable to unlock configuration"
return False
print op_descr + " : Unlocking the configuration"
try:
dev_cfg.unlock()
except UnlockError:
print "Error: Unable to unlock the configuration"
return False
return True
if __name__ == "__main__":
parser = argparse.ArgumentParser()
for key in arguments:
parser.add_argument('-' + key, required=True, help=arguments[key])
args = parser.parse_args()
with Device() as dev:
with Config(dev) as conf;
if change_config(conf, "set interfaces " + args.interface + " disable", "Disabling interface"):
print "Waiting " + args.delay + " seconds"
sleep(float(args.delay))
if change_config(conf, "delete interfaces " + args.interface + " disable", "Enabling interface"):
print "Interface bounced"
else:
print "Error re-enabling"
else:
print "Error disabling interface"
Install the script by configuring:
set system scripts op file bounce.py
Run the script with;
op bounce.py interface ge-0/0/0 delay 5
A description, command and remote source can be configured for a script;
set system scripts op file hello.py source http://..../hello.py
set system scripts op file hello.py command pyhello
set system scripts op file hello.py description "My happy script"
Scripts can be refreshed from their remote source. The command is effective immediately and does not enter the configuration, nor require a commit;
set system scripts op file hello.py refresh
Global trace options can be configured;
set system scripts op traceoptions file opscript
set system scripts op traceoptions flag all
Script arguments can also be configured in the configuration, though configuring them in the script itself is probably preferred.
set system scripts op file hello.py arguments interface description "..."
Allow additional commands to be run by the script, even if the user running the script doesn’t have those permissions. Note that this only works for local scripts and not remote scripts.
set system scripts op file hello.py allow-commands restart
Scripts in other locations can be run without having been configured;
op url /var/home/lab/hello.py
SLAX scripts can be executed in this way by default. To execute Python scripts;
set system scripts op allow-url-for-python
To increase security, an SHA256 hash can be specified;
op url http://... key ...
Or, remote op scripts can be disabled entirely;
set system scripts op no-allow-url
In SLAX, use jcs:progress()
to print a message only if the detail flag is
set. Use jcs:trace()
to log a message only if traceoptions is enabled.
Similarly, in Python, use jcs.progress()
and jcs.trace()
.