Create Action Scripts for YANG RPCs on Junos Devices
You can add YANG data models that define custom remote procedure calls (RPCs) on supported
Junos devices. When you add a nonnative YANG RPC to a device, you must also supply an action
script that serves as the RPC’s handler. The RPC definition references the action script,
which is invoked when the RPC is executed. The action script performs the operations and
retrieves the information required by the RPC and returns any necessary XML output elements as
defined in the RPC output
statement.
Action scripts can be written in Stylesheet Language Alternative SyntaX (SLAX) or Python.
SLAX action scripts are similar to SLAX op scripts and can perform any function available
through the RPCs supported by the Junos XML management protocol and the Junos XML API. Python
action scripts can leverage all of the features and constructs in the Python language, which
provides increased flexibility over SLAX scripts. In addition, Python action scripts support
Junos PyEZ APIs, which facilitate executing RPCs and performing operational and
configuration tasks on Junos devices. Python scripts can also leverage the
lxml
library, which simplifies XPath handling. Also, starting in Junos OS
Release 19.3R1, devices running Junos OS with Upgraded FreeBSD support using IPv6 in Python
action scripts.
This topic discusses how to create an action script, including how to parse the RPC input arguments, access operational and configuration data in the script, emit the XML output, and validate and load the script on a device.
Action Script Boilerplate
SLAX Script Boilerplate
SLAX action scripts must include the necessary boilerplate for both basic script
functionality as well as any optional functionality used within the script such as the
Junos OS extension functions and named templates. In addition, the script must declare all RPC input parameters
using the param
statement. The SLAX action script boilerplate is as
follows:
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 "/var/db/scripts/import/junos.xsl"; param $input-param1; param $input-param2; match / { <action-script-results> { /* insert your code here */ } }
Python Script Boilerplate
Python action scripts must include an interpreter directive line that specifies the Python version used to execute the script. Table 1 outlines the interpreter directive lines you can use in the different releases.
Python Version | Interpreter Directive Lines | Supported Releases |
---|---|---|
Python 3 |
|
Junos OS Release 20.2R1 and later Junos OS Evolved Release 21.1R1 and later |
Python 2.7 |
|
Junos OS Release 20.1 and earlier Junos OS Evolved Release 22.2 and earlier |
Starting in Junos OS Release 20.2R1 and Junos OS Evolved Release 22.3R1, the device uses Python 3 to execute YANG action and translation scripts. In earlier releases, Junos OS only uses Python 2.7 to execute these scripts, and Junos OS Evolved uses Python 2.7 by default to execute the scripts.
In addition, Python action scripts should import any libraries, modules, or objects that are used in the script. For example, in addition to standard Python libraries, Python action scripts might import the following:
-
jcs
library—Enables the script to use Junos OS extension functions and Junos OS named template functionality in the script. -
jnpr.junos
module and classes—Enables the script to use Junos PyEZ. -
lxml
library—Simplifies XPath handling.
For example:
#!/usr/bin/python3 import jcs from jnpr.junos import Device from lxml import etree
Parsing RPC Input Arguments
Input Argument Overview
An RPC can define input parameters using the optional input
statement.
When you execute an RPC and provide input arguments, Junos OS invokes the RPC’s action
script and passes those arguments to the script. In a Python or SLAX action script, you
can access the RPC input arguments in the same manner as you would access command-line
arguments for a normal Python script or a Junos OS SLAX op script, respectively.
Consider the following input
statement for the
get-host-status
RPC:
rpc get-host-status { description "RPC example to retrieve host status"; junos:command "show host-status" { junos:action-execute { junos:script "rpc-host-status.py"; } } input { leaf hostip { description "IP address of the target host"; type string; } leaf level { type enumeration { enum brief { description "Display brief output"; } enum detail { description "Display detailed output"; } } } leaf test { description "empty argument"; type empty; } } ...
The RPC can be executed in the CLI or through a NETCONF or Junos XML protocol session. For example, you might execute the following command in the CLI:
user@host> show host-status hostip 198.51.100.1 level detail test
Similarly, you might execute the following RPC in a remote session:
<rpc> <get-host-status> <hostip>198.51.100.1</hostip> <level>detail</level> <test/> </get-host-status> </rpc>
When you execute the command or RPC, the device invokes the action script and passes in the arguments. The following sections discuss how to process the arguments in the SLAX or Python action script.
Starting in Junos OS Release 19.2R1, custom YANG RPCs support input parameters of type
empty
when executing the RPC’s command in the Junos OS CLI, and the
value passed to the action script is the parameter name. In earlier releases, input
parameters of type empty
are only supported when executing the RPC in a
NETCONF or Junos XML protocol session, and the value passed to the action script is the
string 'none'
.
SLAX Script Input Arguments
In SLAX action scripts, you must declare input parameters using the
param
statement. The parameter names must be identical to the parameter
names defined in the YANG module.
When invoked, the script assigns the value for each argument to the corresponding
parameter, which you can then reference throughout the script. You must include the dollar
sign ($) symbol both when you declare the parameter and when you access its value. If a
parameter is type empty
, the parameter name is passed in as its value.
param $hostip; param $level; param $test;
For more information about SLAX parameters, see SLAX Parameters Overview in the Automation Scripting User Guide.
Python Script Input Arguments
For Python action scripts, the arguments are passed to the script as follows:
-
The first argument is always the action script's file path.
-
The next arguments in the list are the name and value for each input parameter supplied by the user.
The argument name is passed in as follows:
-
In Junos OS Release 21.1 and earlier, the device passes in the name of the argument.
-
In Junos OS Release 21.2R1 and later, the device prefixes a single hyphen (-) to single-character argument names and prefixes two hyphens (--) to multi-character argument names.
Note:When you execute the RPC’s command in the CLI, the arguments are passed to the script in the order given on the command line. In a NETCONF or Junos XML protocol session, the order of arguments in the XML is arbitrary, so the arguments are passed to the script in the order that they are declared in the RPC
input
statement. -
-
The last two arguments in the list, which are supplied by the system and not the user, are
'rpc_name'
and the name of the RPC.
The following sections discuss how to handle the arguments that are passed to Python action scripts in the different releases.
Python Action Scripts (21.2R1 or later)
Starting in Junos OS Release 21.2R1 and Junos OS Evolved Release 21.2R1, when the device passes the input argument names to the Python action script, it prefixes a single hyphen (-) to single-character argument names and prefixes two hyphens (--) to multi-character argument names. This enables you to use standard command-line parsing libraries to handle the arguments.
For the previous YANG RPC example, the action script's sys.argv
input
argument list is:
['/var/db/scripts/action/rpc-host-status.py', '--hostip', '198.51.100.1', '--level', 'detail', '--test', 'test', '--rpc_name', 'get-host-status']
The following sample Python code uses the argparse
library to handle
the arguments. In this case, the parser must also account for the
rpc_name
argument that the system passes to the script.
#!/usr/bin/python3 import argparse parser = argparse.ArgumentParser(description='This is a demo script.') parser.add_argument('--hostip', required=True) parser.add_argument('--level', required=False, default='brief') parser.add_argument('--test', required=False) parser.add_argument('--rpc_name', required=True) args = parser.parse_args() # access argument values by using args.hostip, args.level, and args.test
Python Action Scripts (21.1 and earlier)
In Junos OS Release 21.1 and earlier, the device passes the input argument names to the
Python action script exactly as they are given in the command or RPC. You can access the
input arguments through the sys.argv
list.
For the previous YANG RPC example, the action script's sys.argv
input
argument list is:
['/var/db/scripts/action/rpc-host-status.py', 'hostip', '198.51.100.1', 'level', 'detail', 'test', 'test', 'rpc_name', 'get-host-status']
The following sample Python code demonstrates one way to extract the value for each
argument from the sys.arv
list for the example RPC. The example first
defines a dictionary containing the possible argument names as keys and a default value
for each argument. The code then checks for each key in the sys.argv
list and retrieves the index of the argument name in the list, if it is present. The
code then extracts the argument’s value at the adjacent index position, and stores it in
the dictionary for the appropriate key. This method ensures that if the arguments are
passed to the script in a different order during execution, the correct value is
retrieved for a given argument.
import sys # Define default values for arguments args = {'hostip': None, 'level': 'brief', 'test': None} # Retrieve user input and store the values in the args dictionary for arg in args.keys(): if arg in sys.argv: index = sys.argv.index(arg) args[arg] = sys.argv[index+1]
Retrieving Operational and Configuration Data
Action scripts can retrieve operational and configuration data from a device running
Junos OS and then parse the data for necessary information. SLAX action scripts can
retrieve information from the device by executing RPCs supported by the Junos XML
management protocol and the Junos XML API. Python action scripts can retrieve operational
and configuration information by using Junos PyEZ APIs or by using the cli -c
'command'
to execute CLI commands in the action script as
you would from the shell. To retrieve operational information with the cli
-c
method, include the desired operational command. To retrieve configuration
information, use the show configuration
command.
The following SLAX snippet executes the show interfaces
command on the
local device by using the equivalent <get-interface-information>
request tag:
var $rpc = <get-interface-information>; var $out = jcs:invoke($rpc); /* parse for relevant information and return as XML tree for RPC output */
The following Python code uses Junos PyEZ to execute the
get_interface_information
RPC, which is equivalent to the show
interfaces
CLI command:
#!/usr/bin/python3 from jnpr.junos import Device from lxml import etree with Device() as dev: res = dev.rpc.get_interface_information() # parse for relevant information and return as XML tree for RPC output
For information about using Junos PyEZ to execute RPCs on devices running Junos OS, see Using Junos PyEZ to Execute RPCs on Devices Running Junos OS.
The following Python code executes the show interfaces | display xml
command and converts the string output into an XML tree that can be parsed for the
required data using XPath constructs:
#!/usr/bin/python3 import subprocess from lxml import etree cmd = ['cli', '-c', 'show interfaces | display xml'] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) tmp = proc.stdout.read() root = etree.fromstring(tmp.strip()) # parse for relevant information and return as XML tree for RPC output
Emitting the RPC XML Output
An RPC can define output elements using the optional output
statement.
The action script must define and emit any necessary XML elements for the RPC output. The
XML hierarchy emitted by the script should reflect the tree defined by the containers and
leaf statements in the definition of the RPC output
statement. To return
the XML output, the action script must emit the RPC output hierarchy, and only the output
hierarchy. SLAX scripts must use the copy-of
statement to emit the XML,
and Python scripts can use print
statements.
For example, consider the following YANG RPC output
statement:
output { container host-status-information { container host-status-info { leaf host { type string; description "Host IP"; } leaf status { type string; description "Host status"; } leaf date { type string; description "Date and time"; } } } }
The action script must generate and emit the corresponding XML output, for example:
<host-status-information> <host-status-info> <host>198.51.100.1</host> <status>Active</status> <date>2016-10-10</date> </host-status-info> <host-status-info> <host>198.51.100.2</host> <status>Inactive</status> <date>2016-10-10</date> </host-status-info> </host-status-information>
After retrieving the values for the required output elements, a Python script might emit the XML output hierarchy by using the following code:
from lxml import etree ... xml = ''' <host-status-information> <host-status-info> <host>{0}</host> <status>{1}</status> <date>{2}</date> </host-status-info> </host-status-information> '''.format(hostip, pingstatus, now) tree = etree.fromstring(xml) print (etree.tostring(tree))
Similarly, a SLAX action script might use the following:
var $node = { <host-status-information> { <host-status-info> { <host> $ip; <status> $pingstatus; <date> $date; } } } copy-of $node;
Validating and Loading Action Scripts on a Device
In your YANG RPC definition, you specify the RPC’s action script by including the
junos:command
and junos:action-execute
statements and
the junos:script
substatement, which takes the action script’s filename
as its value. You must define one and only one action script for each RPC. For
example:
rpc rpc-name { ... junos:command "show sw-info" { junos:action-execute { junos:script "sw-info.py"; } } ... }
Starting in Junos OS Release 17.3, the action-execute
statement is a
substatement to command
. In earlier releases, the
action-execute
and command
statements are placed at
the same level, and the command
statement is optional.
YANG modules that define RPCs for devices running Junos OS must import the Junos OS DDL extensions module.
Python action scripts must meet the following requirements before you can execute the scripts on devices running Junos OS.
-
File owner is either root or a user in the Junos OS
super-user
login class. -
Only the file owner has write permission for the file.
-
Script includes an interpreter directive line as outlined in Action Script Boilerplate.
-
The
language python
orlanguage python3
statement is configured at the[edit system scripts]
hierarchy level to enable the execution of unsigned Python scripts.
Starting in Junos OS Release 20.2R1 and Junos OS Evolved Release 22.3R1, the device uses Python 3 to execute YANG action and translation scripts. In earlier releases, Junos OS only uses Python 2.7 to execute these scripts, and Junos OS Evolved uses Python 2.7 by default to execute the scripts.
Users can only execute unsigned Python scripts on devices running Junos OS when the script's file permissions include read permission for the first class that the user falls within, in the order of user, group, or others.
You can validate the syntax of an action script in the CLI by issuing the request
system yang validate action-script
command and providing the path to the
script. For example:
user@host> request system yang validate action-script /var/tmp/sw-info.py Scripts syntax validation : START Scripts syntax validation : SUCCESS
To use an action script, you must load it onto the device with the YANG module that
contains the corresponding RPC. You use the request system yang add
or
request system yang update
commands to add YANG modules and their
associated action scripts to a new or existing YANG package on the device. After you add
the modules and action scripts to the device, you can execute your custom RPCs. When you
execute an RPC, the device invokes the referenced script.
Troubleshooting Action Scripts
By default, action scripts log informational trace messages when the script executes. You can view the trace messages to verify that the RPC invoked the script and that the script executed correctly. If the script fails for any reason, the errors are logged to the trace file.
Junos OS
To view action script trace messages on Junos OS devices running, view the contents of the action.log trace file.
user@host> show log action.log
Junos OS Evolved
To view action script trace messages on Junos OS Evolved devices, view the cscript application trace messages, which include trace data for all script types.
user@host> show trace application cscript
Change History Table
Feature support is determined by the platform and release you are using. Use Feature Explorer to determine if a feature is supported on your platform.
empty
when executing the RPC’s command in the Junos OS
CLI, and the value passed to the action script is the parameter name.action-execute
statement is a substatement to command
.