JUISE is a environment for writing, debugging, and executing SLAX scripts. This environment is tailored to the needs of JUNOS-oriented scripts, including op scripts and commit scripts.
In addition, there are tools to integrate scripts with web servers.
The JUISE project (the JUNOS User Interface Scripting Environment) allows scripts to be written, debugged, and executed outside the normal JUNOS environment. Users can develop scripts in their desktop environment, performing the code/test/debug cycle in a more natural way. Tools for developers are available, including a debugger, a profiler, a call-flow tracing mechanism, and trace files. The JUNOS-specific extension functions are available for scripts, allowing access to JUNOS XML data using the normal jcs:invoke and jcs:execute functions. Commit scripts can be tested against a JUNOS device's configuration and the results of those script tested on that device.
JUISE scripts are typically written in SLAX, an alternative syntax for XSLT. For information about SLAX, refer to libslax.googlecode.com. Additional reference material is available under the "JUNOS Automation" section of http://junos.juniper.net.
The JUISE software can be used to execute scripts independent of a JUNOS box, allowing the use of SLAX scripts in the off-box world. Scripts can communicate with zero or more devices, allowing a centralized manipulation of multiple devices.
JUISE includes a rudimentary NETCONF server, allowing JUNOS devices to invoke RPCs to unix devices. JUNOS devices can perform NETCONF or JUNOScript RPCs to a JUISE box, and invoke scripts or programs on that device.
JUISE includes "mod_juise", a plug-in module for lighttpd, allowing the execution of scripts via HTTP. At this point, the portion of JUISE is completely experimental.
The juise command is normally invoked with the name of the script to be run, a set of options are used to tailor the execution of the script, and a set of name/value parameter pairs that are passed as parameters to the script, as they are for the JUNOS "op" command.
If the first non-option argument contains an '@' and the --target option is not present, the argument will be taken as the default target. This value will be used as the "local" device for configuration retrieval and for any jcs:open() call that does not provide an explicit target. This argument can be either "user@device" or "@device" (with the current login name used as the user name).
Commit scripts were introduced in JUNOS-7.2 as a means of adding custom rules to the "commit check" process, allowing customers to define additional validation rules which the incoming configuration must satisfy. Commit scripts can find and report configuration statements that do not follow these rules.
Commit script can also change the configuration in two distinct ways. Normal changes are added to the candidate configuration before it is passed to the rest of the JUNOS subcomponents for validation. These changes are normal parts of the configuration data set.
In contrast, transient changes are not part of the normal configuration data set, since they are not visible in the candidate configuration nor are they saved in rollback data sets. But they are exposed to JUNOS subcomponents during validation, allowing the commit script to create second order configuration information based on the first order data in the candidate configuration.
The typical commit script relies heavily upon XPath to find errant configuration. They typically follow the pattern:
if (xpath/to/find/bad/config) {
report bad config;
}
or:
for-each (some/list/that/needs/to/be/checked) {
if (xpath/to/find/bad/config) {
report bad config;
}
}
Consider this example:
version 1.1;
import "../import/junos.xsl";
match configuration {
var $ldp = protocols/ldp;
var $isis = protocols/isis;
var $ospf = protocols/ospf;
for-each ($isis/interface/name | $ospf/area/interface/name) {
var $ifname = .;
if (not($ldp/interface[name == $ifname])) {
<xnm:error> {
call jcs:edit-path();
call jcs:statement();
<message> "ldp not enabled for this interface";
}
}
}
}
This script looks for interfaces listed under [protocols isis] and [protocols ospf] to find interfaces not listed under [protocols ldp]. When such interfaces are found, an error is generated. The UI sees this error and stops the configuration from being validated.
juise can be used to test commit scripts by using the "‑c" option. The target device can be given with either the --target (-T) option. If no target is given and the first argument contains an '@', then this is used as the target.
% juise -c me@my-router my-script.slax
To execute the commit script, juise tries to reproduce the environment which the script would run under JUNOS. juise starts by downloading the configuration in the same format used by on-box commit scripts, with expanded inheritance and group and change attributes. This configuration is wrapped in an input document (the <op‑script‑input> element) to be passed to the script. The wrapper is identical to the JUNOS wrapper, complete with the <junos‑context> element.
The <op‑script‑input> element is passed as input to the script, which runs normally, inspecting the configuration and generating, <xnm:error>, <xnm:warning>, <syslog>, <change>, and <transient> elements as needed.
The results of the script are inspected, and any <change> and <transient> elements are uploaded to a "edit private" session. Any errors are reported to the user.
The results of these interactions with the device are displayed in XML to aid with debugging scripts.
If no errors have occurred, then any changes to the configuration can be retrieved and reported to the user, who can verify that the changes are those desired. Use the "--output-format <style>" option to retrieve the results in any of the following formats:
Style
Description
compare
Display in "show compare" format (default)
none
Do not display changes
text
Display in "show configuration" format
xml
Display in JUNOS XML format
% cat fix-domain.slax
version 1.1;
import "../import/junos.xsl";
match configuration {
<change> {
<system> {
<domain-name> "new.example.com";
}
}
}
% juise -c --output-format compare @dent fix-domain.slax
Results from edit private:
<xnm:warning>
<message>
uncommitted changes will be discarded on exit
</message>
</xnm:warning>
Results from load change:
<load-configuration-results>
<load-success/>
</load-configuration-results>
Results from load change:
<load-configuration-results>
<load-success/>
</load-configuration-results>
Results from commit check:
<commit-results>
<load-success/>
<routing-engine junos:style="normal">
<name>re0</name>
<commit-check-success/>
</routing-engine>
</commit-results>
Results from script:
<configuration-information>
<configuration-output>
[edit system]
- domain-name juniper.net;
+ domain-name new.example.com;
</configuration-output>
</configuration-information>
The libslax debugger (sdb) can be used to debug commit scripts. Use the "‑d" option to trigger the debugger.
% juise -d -c me@my-router my-script.slax
sdb: The SLAX Debugger (version 0.12.2)
Type 'help' for help
(sdb) help
List of commands:
break [loc] Add a breakpoint at [file:]line or template
callflow [val] Enable call flow tracing
continue [loc] Continue running the script
delete [num] Delete all (or one) breakpoints
finish Finish the current template
help Show this help message
info Showing info about the script being debugged
list [loc] List contents of the current script
next Execute the next instruction, stepping over
over Execute the current instruction hierarchy
print <xpath> Print the value of an XPath expression
profile [val] Turn profiler on or off
reload Reload the script contents
run Restart the script
step Execute the next instruction, stepping into
verbose Turn on verbose (-v) output logging
where Show the backtrace of template calls
quit Quit debugger
Command name abbreviations are allowed
(sdb)
Op scripts were introduced in JUNOS-7.5 as a means of adding custom commands to the JUNOS CLI.
Op scripts are not passed a normal document as input. An <op‑script‑input> element is passed in which contains the <junos‑context> similar to operation of op scripts in JUNOS.
Command line parameters (if any) are passed as SLAX parameters. They are also recorded under the <arguments> element inside the <junos‑context>.
Op scripts can generate output using the "message" statements and "jcs:output" calls, as well as by the XML document which the script generates. The XML document is displayed only when script execution is complete.
juise can also be used to develop scripts that are not intended to run under JUNOS. The off-box environment gives a number of distinct advantages for scripts that operate like "op" scripts. These scripts join the simplicity and flexibility of SLAX to the unix environment.
The SLAX parser supports the "#!" mechanism which is a normal part of the unix environment. This allows scripts beginning with the line "#!/usr/bin/slax" to be executed directly from the command line.
#!/usr/local/bin/slax
match / { message "this works"; }
If this script file is given the appropriate "executable" permission bit (such as "chmod a+x works"), then the command "works" will work.
Event scripts are not yet functional. JUNOS currently lacks an effective mechanism for getting event data from the device to a transient listener like juise.
juise can perform as a NETCONF server, accepting NETCONF RPCs and returning XML data. RPCs are serviced by invoking local scripts on the server machine (aka "juise box").
The server can be invoked in two ways. The more traditional invocation uses the "‑R" or "‑‑run‑server" option. The most common way to run a NETCONF server is to make an sshd_config file listing the service. In JUNOS, we do this as follows:
The alternative means of running NETCONF is to use the "xml‑mode" command over a normal ssh connection. This is non-standard, but allows the deploying of NETCONF with no configuration and much less impact.
juise will make a symbolic link from "juise" to "xml‑mode", so a client can open a normal ssh command with the "xml‑mode" command and get a NETCONF connection. We refer to this a "junos‑netconf" and the juise client supports it via the "‑P" option and the "junos‑netconf" <protocol> option for jcs:open().
juise puts few limit limitations on the RPCs invoked on the server. The RPC method name is used to locate a local script or binary which implements that RPC. The script or binary is executed with the RPC content as standard input, and any output from the script is passed back to the client.
The JUISE_SCRIPT_DIR environment variable and the "‑D" option are used to provide a set of search directories. The default path is the directory $prefix/share/juise/scripts/, or the value given for --with-script-dir=DIR to the "configure" script at build time.
For each directory in the search path, juise looks for a file with the name of the RPC method and a suitable extension. The list of extensions is "slax", "xsl", "xslt", "sh", "pl", and "" (none).
This section contains reference material for the juise command, including command line options, environment variables, build options, and other information.
This section provides a complete listing of available juise options.
--agent OR -A
Enable forwarding of ssh-agent credentials, allowing remote ssh sessions to connect to the local ssh-agent process for authentication information. See "man ssh-agent(1)" for details.
--debug OR -d Start the libslax debugger, allowing the user to
interactively debug script execution. Refer to the debugger documentation in the libslax distribution for more information (libslax.googlecode.com).
--directory <dir> OR -D <dir>
Use the given directory as the location for server scripts. This directory can also be set using the JUISE_SCRIPT_DIR environment variable.
--include <dir> OR -I <dir>
Add the given directory to the search path for files that are referenced via the SLAX "include" or "import" statements.
--indent OR -g
Make indented output as if the script contained:
output-method {
indent "yes";
}
(The "‑g" is for "good-looking output").
--input <file> OR -i <file>
Use given file for input.
--junoscript OR -J
Default to using the older JUNOScript XML API instead of the NETCONF API for jcs:open() connections.
--lib <dir> OR -L <dir>
Add the given directory to the list of directories searched for dynamic extension libraries. Refer to libslax for more information.
--no-randomize
Avoid initializing the random number generator so script execution can be predictable (e.g. during debugging).
--param <name> <value> OR -a <name> <value>
An alternative method of giving parameters to a script.
--protocol <name> OR -P <name>
Use the given protocol as the default protocol for jcs:open() connections.
--run-server OR -R
Run juise in server mode, where it accepts incoming RPCs and executes scripts given by the RPC name. See NETCONF Server section for details.
--script <name> OR -S <name>
An alternative method of giving the script name.
--target <name> OR -T <name>
An alternative method of giving the default target name.
--trace <file> OR -t <file>
Save trace data to the given file.
--user <name> OR -u <name>
An alternative method of giving the user name for API connections.
--verbose OR -v
Enable debugging output. Any calls to slaxLog() will be displayed.
--version OR -V
Display any version information, including the versions of libslax, libxslt, and libxml2. After displaying this information, juise will exit.
--wait <seconds>
After starting and parsing arguments, juise will wait for the specified number of seconds, allowing the user to attach to the process with "gdb" for additional debugging.
The values of certain environment variables and the command line arguments to the "configure" script will affect the operation of JUISE. This section details these settings.
JUNOS scripts typically import junos.xsl using the following statement:
import "../import/junos.xsl";
JUISE installs this file, along with other import files, in the $prefix/share/juise/import directory.
In addition, JUISE will look at the environment variables JUISEPATH and SLAXPATH. These variables list a set of colon-separated directories to be searched for import and include files.
Additional paths may be passed in on the command line using the "‑I" flag.
The "‑‑with‑juise‑dir" argument of the configure script will also affect this. The contents of this option are appended with "/import" and added to the initial search path.
The JUISE project includes a plug-in module for the "lighttpd" (pronounced "lightey") web server. This plug-in allows scripts to be run directly from lighttpd while allowing SSH credentials established using "ssh‑agent" to be inherited by the SLAX script. SSH connections, including those from the jcs:open() call, can then be opened without prompting for a passphrase.
A suitable lighttpd.conf file is provided with juise, installed in the $prefix/share/juise/web/ directory. lighttpd can be started by using the "‑f" option and this file:
lighttpd -f $prefix/share/juise/web/lighttpd.conf
To add mod_juise to an existing lighttpd server, add the following line to your lighttpd.conf file:
When a file ending in ".slax" is requested, lighttpd will pass the request to mod_juise which will invoke juise using the "juise.cgi" driver. This driver will fetch the CGI-defined variables and make them available to the script as both global parameters and as elements inside the "$cgi" global parameter. Scripts are free to use either method to retrieve these values. The following table list the CGI parameter name and the $cgi element name.
Parameter Name
Element Name
CONTENT_LENGTH
content-length
DOCUMENT_ROOT
document-root
GATEWAY_INTERFACE
gateway-interface
HTTPS
https
LD_LIBRARY_PATH
ld-library-path
LD_PRELOAD
ld-preload
PATH_INFO
path-info
QUERY_STRING
query-string
REDIRECT_STATUS
redirect-status
REMOTE_ADDR
remote-addr
REMOTE_PORT
remote-port
REMOTE_USER
remote-user
REQUEST_METHOD
request-method
REQUEST_URI
request-uri
SCRIPT_FILENAME
script-filename
SCRIPT_NAME
script-name
SERVER_ADDR
server-addr
SERVER_NAME
server-name
SERVER_PORT
server-port
SERVER_PROTOCOL
server-protocol
SERVER_SOFTWARE
server-software
SYSTEMROOT
systemroot
The following lines are functionally equivalent:
expr "User is " _ $REMOTE_USER;
expr "User is " _ $cgi/remote-user;
In addition $cgi has an element named "parameters" which has the fields of $QUERY_STRING broken into individual elements. These are also available as global parameters. The following lines are functionally equivalent:
expr "'from' parameter is " _ $cgi/parameters/from;
expr "'from' parameter is " _ $from;
The full set of parameters can be accessed using this parameters element:
When SLAX scripts are invoked via mod_juise, the script can choose to emit a top level element named <cgi>. This element is used to supply directions to the HTTP server.
Any non-namespace attributes given on the <cgi> element is converted into a header field in the HTTP reply message. The attribute name is the header field and the attribute value is the header value.
Additional attributes and elements may be defined under the <cgi> element. For future proofing, script should avoid attributes and elements whose names being with "junos", "cgi", or "juise".
The MIXER daemon provides persistent SSH connections, allowing multiple jcs:open calls to return more quickly by avoiding most of the SSH connection establishment and negotiation expense.
MIXER can be used to forward normal SSH connections, but also gateways WebSocket calls into NETCONF connections, allowing browser-based software access to NETCONF services.
The JUISE project includes the jcs extension library which allows invocation of NETCONF RPCs. This library is identical to the jcs library available under JUNOS. This section details the extension functions and element of those libraries.
note::
The jcs extension library is not a dynamic extension library and is only available when using the juise command. This needs to change, but is low priority.
The "jcs" namespace is used for JUNOS-specific extension function, but many of the functions are no longer JUNOS specific. "jcs" was originally for "JUNOS Commit Scripts", but these functions can be used for other sorts of scripting as well.
In addition, many of the extension functions in the "slax" namespace were originally in the "jcs" namespace, and are now available in both namespaces. The following table lists these dual-homed functions.
The jcs:open() function creates a connection to either the local machine (for on-box JUNOS scripts) or a remote device using the NETCONF or JUNOScript API. This connection can be passed to jcs:execute() to invoke RPCs and to jcs:close() to close the connection.
Three protocols can be used for network access:
netconf: IETF standard (RFC6241) over the standard netconf port with the netconf subsystem
junoscript: the original JUNOScript API accessed over the standard ssh port using the JUNOS CLI
junos-netconf: the NETCONF protocol accessed over the standard ssh port using the JUNOS CLI
"junoscript" and "junos‑netconf" have the advantage of not requiring additional configuration, where "netconf" has the advantage of being the full standard mechanism.
The behavior of jcs:open varies with the number of arguments given:
If no arguments are passed and the script is running under JUNOS, a connection to the local device is created. RPCs are processed within the device and no network login is needed.
If no arguments are passed and the script is not running under JUNOS, the arguments to the juise program are using to build a connection. The "‑‑target", "‑‑user", and "‑‑protocol" options, along with the "[user]@target" argument (if provided), identify the target of the connection.
If one argument is provided, the argument is the name of the device to connect with. The name may be in either "target" or "user@target" format.
If two arguments are provided, the first argument is the name of the device to connect with, and the second argument is a node-set with the following members.
Element Name
Value
connection-timeout
Seconds before connect fails
method
netconf, junos-netconf, or junoscript
passphrase
SSH passphrase
password
User password
port
Transport port number
timeout
Connection timeout (in seconds)
username
User login name
If three arguments are provided, the first argument is the name of the device to connect with, the second is the username, and the third is the password.
The jcs:execute() function invokes RPCs using a connection returned by jcs:open(). RPCs are invoked and their results returned using a persistent connection, allowing stateful RPCs, such as locking the configuration database.
The first argument is the connection, as returned by jcs:open(). If the second argument is a string, it is the name of an RPC operation. Otherwise the second argument is a node-set containing the operation to be invoked.
SYNTAX::
node-set jcs:execute(conn, method);
node-set jcs:execute(conn, rpc);
EXAMPLE::
var $rpc = <get-interface-information> {
<interface-name> "fe-0/0/0";
<statistics>;
}
var $res = jcs:execute($conn, $rpc);
var $sw = jcs:execute($conn, "get-software-information");
The jcs:invoke() function invokes RPCs but does not use or require a persistent connection. RPCs are stateless, since any connection needed is temporary, so operations like locking the configuration database should be avoided since the lock will be immediately released.
If the argument to jcs:invoke is a string, it is the name of an RPC operation. Otherwise the argument is a node-set containing the operation to be invoked.
SYNTAX::
node-set jcs:invoke(method);
node-set jcs:invoke(rpc);
EXAMPLE::
var $rpc = <get-interface-information> {
<interface-name> "fe-0/0/0";
<statistics>;
}
var $res = jcs:invoke($rpc);
var $sw = jcs:invoke("get-software-information");