Display Valid Command Option and Configuration Statement Values in the CLI for Custom YANG Modules
Certain Junos devices enable you to load custom YANG modules on the device to add data
models that are not natively supported by Junos OS. When you add custom YANG data models to
a device, you must also supply an action or translation script that handles the translation
logic between the YANG data model and Junos OS. Although the script logic can ensure that a
user supplies valid values for a given command option or configuration statement, that
logic is not always transparent to the user. Starting in Junos OS Release 19.2R1, the CLI
displays the set of possible values for certain command options or configuration statements
in a custom YANG data model when you include the action-expand
extension
statement in the option or statement definition and reference a script that handles the
logic.
Understanding Context-Sensitive Help for Custom YANG Modules
The Junos CLI provides context-sensitive help whenever you type a question mark (?) in operational or configuration mode. When you execute a command or configure a statement, the CLI’s context-sensitive help displays the valid options and option values for a command or the valid configuration statements and leaf statement values in the configuration statement hierarchy. Additionally, context-sensitive help shows the possible completions for incomplete option names, statement names, and their values.
The CLI can also display the values that are valid for certain command options or configuration statements in a custom YANG data model. The CLI can display all possible values or a subset of values that match on partial input from the user. For example:
user@host> show host-status hostip ? Possible completions: <hostip> Host IP address 10.10.10.1 IPv4 address 10.10.10.2 IPv4 address 172.16.0.1 IPv4 address 198.51.100.1 IPv4 address 198.51.100.10 IPv4 address 2001:db8::1 IPv6 address (DC 1...128) 2001:db8::fdd2 IPv6 address (DC 1...128)
user@host> show host-status hostip 198? Possible completions: <hostip> Host IP address 198.51.100.1 IPv4 address 198.51.100.10 IPv4 address
To display the set of valid values for a given command option or configuration statement in a custom YANG module:
-
Define the
action-expand
andscript
extension statements under the appropriate input parameter or configuration statement in the YANG module as described in Defining the YANG Module. -
Create a Python script that checks for user input, calculates the possible values of the command option or configuration statement, and sends the appropriate output to the CLI, as described in Creating the CLI Expansion Script.
Note:The CLI expansion script only displays the valid values in the CLI. The module’s translation script or action script must still include the logic that ensures that only valid values are accepted and processed.
-
Load the YANG module, any translation or action scripts, and the CLI expansion script as part of a custom YANG package on the device as described in Loading the YANG Package.
Note:Junos devices process CLI expansion scripts as another kind of action script, but we refer to CLI expansion script to avoid any confusion.
Defining the YANG Module
To define a custom YANG module that displays the set of valid values for a given command option or configuration statement when the user requests context-sensitive help in the CLI, your module must:
For example, in the following module, the RPC defines the hostip
input parameter, which calls the hostip-expand.py
Python script when
the user requests context-sensitive help for the hostip
argument in
the CLI. The script implements the custom logic that displays the valid values for
that argument in the CLI.
module rpc-host-status { namespace "http://yang.juniper.net/examples/rpc-cli"; prefix jrpc; import junos-extension-odl { prefix junos-odl; } import junos-extension { prefix junos; } 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 "Host IP address"; type string; junos:action-expand { junos:script "hostip-expand.py"; } } leaf level { type enumeration { enum brief { description "Display brief output"; } enum detail { description "Display detailed output"; } } } } output { ... } } }
Creating the CLI Expansion Script
When you define the action-expand
statement and
script
substatement for a command option or configuration
statement in a custom YANG module and you request context-sensitive help for that
option or statement value in the CLI, the device invokes the referenced Python
script. The script must contain the custom logic that calculates and displays all
possible values for that parameter or displays a subset of values that match on
partial input from the user.
For example, the following command should display all valid values for the
hostip
argument:
user@host> show host-status hostip ?
And the following command should display all valid values that start with "198":
user@host> show host-status hostip 198?
To display the valid values for a command option or configuration statement in the CLI, the Python script should perform the following functions:
-
Import the
jcs
library along with any other required Python libraries. -
Retrieve and process any user input.
If you specify partial input for an option or statement value in the CLI, the script’s command-line arguments include the
symbol
argument, which is a string containing the user input.Note:Starting in Junos OS Release 21.2R1 and Junos OS Evolved Release 21.2R1, the script's command-line arguments include the
--symbol
argument instead of thesymbol
argument. -
Define or calculate the valid values for the parameter.
-
Call the
jcs.expand()
function for each value to display on the command line.
The script must call the jcs:expand()
function for each option or
statement value to display in the CLI. The syntax for the
jcs:expand()
function is:
jcs.expand(value, description, <units>, <range>)
Where:
value |
String defining a valid value for the given command option or configuration statement. |
description |
String that describes the value. |
units |
(Optional) String that defines the units for the corresponding value. |
range |
(Optional) String that defines the range for the corresponding value. |
For each call to the jcs.expand()
function, the script emits the
value, description, units, and range that are provided in the function arguments in
the CLI. For example, given the following call to jcs.expand()
in
the script:
jcs.expand("2001:db8:4136::fdd2", "IPv6 address", "DC", "1...128")
The corresponding CLI output is:
Possible completions: <hostip> Host IP address 2001:db8:4136::fdd2 IPv6 address (DC 1...128)
The following sample scripts first check for the presence of symbol
in the script’s command-line arguments, and if present, set the corresponding
variable equal to the user’s input. The scripts then calculate the set of valid
values for the parameter based on the user’s input. Finally, the scripts call the
jcs.expand()
function for each value to display in the CLI.
We provide two versions of the script, which appropriately handle the script's
symbol
argument for the different releases. The following sample
script, which is valid on devices running Junos OS Release 21.2R1 or later, uses the
argparse
library to parse the --symbol
argument.
#!/usr/bin/python3 # Junos OS Release 21.2R1 and later import jcs import argparse parser = argparse.ArgumentParser(description='This is a demo script.') parser.add_argument('--symbol', required=False, default='') args = parser.parse_args() description_ipv4 = "IPv4 address" description_ipv6 = "IPv6 address" expand_colon = ":" expand_units = "DC" expand_range = "1...128" item = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] for ip in item: if ip.startswith(args.symbol) or not args.symbol: if not expand_colon in ip: jcs.expand(ip, description_ipv4) else: jcs.expand(ip, description_ipv6, expand_units, expand_range)
Similarly, the following sample script, which is valid on devices running Junos OS
Release 21.1 or earlier, checks for symbol
in the
sys.argv
list.
#!/usr/bin/python # Junos OS Release 21.1 and earlier import sys import jcs symbol = "" # Retrieve user input in symbol argument and store the value if "symbol" in sys.argv: index = sys.argv.index("symbol") symbol = sys.argv[index+1] description_ipv4 = "IPv4 address" description_ipv6 = "IPv6 address" expand_colon = ":" expand_units = "DC" expand_range = "1...128" item = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] for ip in item: if ip.startswith(symbol) or not symbol: if not expand_colon in ip: jcs.expand(ip, description_ipv4) else: jcs.expand(ip, description_ipv6, expand_units, expand_range)
The CLI expansion script only displays the valid values, units, and ranges for the command option or configuration statement in the CLI. The module’s translation script or action script must ensure that only valid values are accepted and processed.
Loading the YANG Package
When you load a YANG package on a Junos device, include any CLI expansion scripts in the list of action scripts for that package. Junos OS automatically copies the script to the /var/db/scripts/action directory.
To load a new package and include custom CLI expansion scripts:
Example: Displaying Context-Sensitive Help for a Command Option
This example presents a custom YANG module that uses the action-expand
extension statement and a custom script to display the set of possible values for one of
the command options when a user requests context-sensitive help in the CLI for that
option.
- Requirements
- Overview
- YANG Module and Action Scripts
- Configuration
- Verifying the Context-Sensitive Help
Requirements
This example uses the following hardware and software components:
-
Device running Junos OS Release 19.2R1 or later that supports loading custom YANG data models.
Overview
The YANG module in this example defines a custom RPC to ping the specified host and
return the result. The YANG module rpc-host-status
is saved in the
rpc-host-status.yang file. The module imports the Junos OS
extension modules, which provide the extensions required to execute custom RPCs on
the device and to customize the output and context-sensitive help in the CLI.
The module defines the get-host-status
RPC. The
junos:command
statement defines the command that is used to
execute the RPC in the CLI, which in this case is show host-status
.
The junos:action-execute
and junos:script
statements define the action script that is invoked when you execute the 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"; } }
The hostip
input parameter includes the
junos:action-expand
and junos:script
statements,
which define the script that is invoked when the user requests context-sensitive help
in the CLI for that input parameter.
input { leaf hostip { description "Host IP address"; type string; junos:action-expand { junos:script "hostip-expand.py"; } } ... }
The hostip-expand.py script processes the user’s input, which is
passed to the script as the argument symbol
or
--symbol
, depending on the release. The script then calculates
and displays the set of values that the user can enter for that command option.
Starting in Junos OS Release 21.2R1 and Junos OS Evolved Release 21.2R1, when the device passes command-line arguments to a Python action script (including CLI expansion scripts), it prefixes a single hyphen (-) to single-character argument names and prefixes two hyphens (--) to multi-character argument names.
The expansion script displays the valid values for hostip
in the
CLI. The action script implements the logic that determines if the provided value is
valid. This example adds the YANG module and the action scripts to the device as part
of a new YANG package named rpc-host-status
.
YANG Module and Action Scripts
YANG Module
The YANG module, rpc-host-status.yang, defines the RPC, the command used to execute the RPC in the CLI, the name of the action script to invoke when you execute the RPC, and the name of the CLI expansion script to invoke when the user requests context-sensitive help for the corresponding input parameter.
/* * Copyright (c) 2019 Juniper Networks, Inc. * All rights reserved. */ module rpc-host-status { namespace "http://yang.juniper.net/examples/rpc-cli"; prefix jrpc; import junos-extension-odl { prefix junos-odl; } import junos-extension { prefix junos; } organization "Juniper Networks, Inc."; description "Junos OS YANG module for RPC example"; 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 "Host IP address"; type string; junos:action-expand { junos:script "hostip-expand.py"; } } leaf level { type enumeration { enum brief { description "Display brief output"; } enum detail { description "Display detailed output"; } } } } output { container host-status-information { leaf hostip { type string; description "Host IP"; } leaf status { type string; description "Operational status"; } leaf date { type string; description "Date information"; } junos-odl:style brief { junos-odl:format host-status-information-format-brief { junos-odl:header "Brief output\n"; junos-odl:picture "@<<<<<<<<<<<< @"; junos-odl:space; junos-odl:line { junos-odl:field "hostip"; junos-odl:field "status"; } } } junos-odl:style detail { junos-odl:format host-status-information-format-detail { junos-odl:header "Detail output\n"; junos-odl:picture "@<<<<<<<<<<<< @<<<<<<<<<<<< @"; junos-odl:space; junos-odl:line { junos-odl:field "hostip"; junos-odl:field "status"; junos-odl:field "date"; } } } } } } }
Action Script
The corresponding action script is rpc-host-status.py. This example provides two versions of the action script, which appropriately handle the script's command-line arguments for the different releases.
Action Script (Junos OS Release 21.2R1 and later)
#!/usr/bin/python3 # Junos OS Release 21.2R1 and later import os 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('--rpc_name', required=True) args = parser.parse_args() valid_addresses = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] f = os.popen('date') now = f.read() # Ping target host and set the status if args.hostip in valid_addresses: response = os.system('ping -c 1 ' + args.hostip + ' > /dev/null') if response == 0: pingstatus = "Host is Active" else: pingstatus = "Host is Inactive" else: pingstatus = "Invalid host" # Print RPC XML for the given style print ("<host-status-information>") print ("<{}>".format(args.level)) print ("<hostip>{}</hostip>".format(args.hostip)) print ("<status>{}</status>".format(pingstatus)) if args.level == "detail": print ("<date>{}</date>".format(now)) print ("</{}>".format(args.level)) print ("</host-status-information>")
Action Script (Junos OS Release 21.1 and earlier)
#!/usr/bin/python # Junos OS Release 21.1 and earlier import sys import os args = {'hostip': None, 'level': 'brief'} valid_addresses = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] # 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] f = os.popen('date') now = f.read() # Ping target host and set the status if args['hostip'] in valid_addresses: response = os.system('ping -c 1 ' + args['hostip'] + ' > /dev/null') if response == 0: pingstatus = "Host is Active" else: pingstatus = "Host is Inactive" else: pingstatus = "Invalid host" # Print RPC XML for the given style print ("<host-status-information>") print ("<{}>".format(args['level'])) print ("<hostip>{}</hostip>".format(args['hostip'])) print ("<status>{}</status>".format(pingstatus)) if args['level'] == "detail": print ("<date>{}</date>".format(now)) print ("</{}>".format(args['level'])) print ("</host-status-information>")
CLI Expansion Script
The action script that handles the logic to display the valid values for
hostip
in the CLI is hostip-expand.py.
This example provides two versions of the script, which appropriately handle the
script's arguments for the different releases.
- CLI expansion script (Junos OS Release 21.2R1 and later)
- CLI expansion script (Junos OS Release 21.1 and earlier)
CLI expansion script (Junos OS Release 21.2R1 and later)
#!/usr/bin/python3 # Junos OS Release 21.2R1 and later import jcs import argparse parser = argparse.ArgumentParser(description='This is a demo script.') parser.add_argument('--symbol', required=False, default='') args = parser.parse_args() description_ipv4 = "IPv4 address" description_ipv6 = "IPv6 address" expand_colon = ":" expand_units = "DC" expand_range = "1...128" item = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] for ip in item: if ip.startswith(args.symbol) or not args.symbol: if not expand_colon in ip: jcs.expand(ip, description_ipv4) else: jcs.expand(ip, description_ipv6, expand_units, expand_range)
CLI expansion script (Junos OS Release 21.1 and earlier)
#!/usr/bin/python # Junos OS Release 21.1 and earlier import sys import jcs symbol = "" # Retrieve user input in symbol argument and store the value if "symbol" in sys.argv: index = sys.argv.index("symbol") symbol = sys.argv[index+1] description_ipv4 = "IPv4 address" description_ipv6 = "IPv6 address" expand_colon = ":" expand_units = "DC" expand_range = "1...128" item = ["10.10.10.1", "10.10.10.2", "2001:db8::1", "172.16.0.1", "198.51.100.1", "198.51.100.10", "2001:db8::fdd2"] for ip in item: if ip.startswith(symbol) or not symbol: if not expand_colon in ip: jcs.expand(ip, description_ipv4) else: jcs.expand(ip, description_ipv6, expand_units, expand_range)
Configuration
Enable Execution of Python Scripts
To enable the device to execute unsigned Python scripts:
-
Configure the
language python
orlanguage python3
statement, as appropriate for the Junos OS release.[edit] user@host# set system scripts language (python | python3)
Note: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.
-
Commit the configuration.
[edit] user@host# commit and-quit
Load the YANG Module and Scripts on the Device
To add the YANG module and scripts to the Junos device:
-
Download the YANG module and scripts to the Junos device.
-
Ensure that the Python scripts meet the following requirements:
-
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 the appropriate interpreter directive line as outlined in Create Action Scripts for YANG RPCs on Junos Devices.
-
-
(Optional) Validate the syntax for the YANG module and action scripts.
user@host> request system yang validate module /var/tmp/rpc-host-status.yang action-script [ /var/tmp/rpc-host-status.py /var/tmp/hostip-expand.py ] YANG modules validation : START YANG modules validation : SUCCESS Scripts syntax validation : START Scripts syntax validation : SUCCESS
-
Add the YANG module and scripts to a new YANG package.
user@host> request system yang add package rpc-host-status module /var/tmp/rpc-host-status.yang action-script [ /var/tmp/rpc-host-status.py /var/tmp/hostip-expand.py ] YANG modules validation : START YANG modules validation : SUCCESS Scripts syntax validation : START Scripts syntax validation : SUCCESS TLV generation: START TLV generation: SUCCESS Building schema and reloading /config/juniper.conf.gz ... mgd: commit complete Restarting mgd ...
-
When the system prompts you to restart the Junos OS CLI, press
Enter
to accept the default value ofyes
, or type yes and pressEnter
.WARNING: cli has been replaced by an updated version: ... Restart cli using the new version ? [yes,no] (yes) yes Restarting cli ...
Verifying the Context-Sensitive Help
Purpose
Verify that the CLI expansion script works as expected.
Action
From operational mode, request context-sensitive help in the CLI by issuing the
command defined by the junos:command
statement in the RPC
definition, and include the hostip
input argument and a question
mark (?).
user@host> show host-status hostip ? Possible completions: <hostip> Host IP address 10.10.10.1 IPv4 address 10.10.10.2 IPv4 address 172.16.0.1 IPv4 address 198.51.100.1 IPv4 address 198.51.100.10 IPv4 address 2001:db8::1 IPv6 address (DC 1...128) 2001:db8::fdd2 IPv6 address (DC 1...128)
Perform the same operation with partial user input and verify that the displayed values correctly match the input.
user@host> show host-status hostip 198? Possible completions: <hostip> Host IP address 198.51.100.1 IPv4 address 198.51.100.10 IPv4 address
Meaning
When context-sensitive help is requested for the hostip
value,
the device invokes the hostip-expand.py
script. The script
processes the user’s input, if provided, and prints the valid completions in the
CLI. If no user input is given, the script prints all possible values. When user
input is provided, the script prints only matching values.
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.