Exec Mode

../_images/exec-mode.png

Command Execution Mode

The Exec Mode is the normal way for Invoking yangcli-pro Commands.

Builtin YANG-Based Commands

All of the built-in commands are defined as 'RPC' operations in the yangcli-pro.yang module.

Server YANG-Based Commands

All of the RPC operations advertised by a server (within a session) are available as commands in Exec Mode.

  • All server commands can be suppressed by setting the --server-commands parameter to 'false'.

  • A server command is shown with a '*' character appended when then tab key is pressed.

    Example: tab key pressed after the 'd' key

    • Commands such as 'delete' are yangcli-pro built-in commands

    • Commands such as 'delete-backup' are server commands

    andy@localhost> d
    delete                delete-backup*        delete-subscription*  devices-cfg
    delete-all            delete-config*        device-cfg            discard-changes*
    andy@localhost> d
    

Hiding Exec Commands

The --hide-command CLI parameter can be used to hide specific commands from the user.

Example:

> yangcli-pro --hide-command=delete-backup

Now when the tab key is pressed during a session, the 'delete-backup' command is not shown:

andy@localhost> d
delete                delete-config*        device-cfg            discard-changes*
delete-all            delete-subscription*  devices-cfg
andy@localhost> d

Script Integration

The yangcli-pro Exec Mode can be used to integrate your own scripts. The scripts can be installed anywhere in the --runpath and it will be found.

Enabling Script Usage

Permission to access scripts is handled differently for yangcli-pro and yp-shell.

In both cases, the --allow-shell parameter must be 'true' to invoke the run-shell command directly. The default is 'false' Custom commands are allowed even if this parameter is 'false'.

  • In yangcli-pro, the '$$allow-shell' system variable can be changed at run-time.

  • In yp-shell, the '$$allow-shell' system variable cannot be changed at run-time. It is a read-only variable in this mode.

The run command is always allowed for yangcli-pro. For yp-shell, the --ypshell-exec-ok parameter must be set to 'true' or both the run and run-shell commands will fail with an error:

user@server1> run foo

Error: The run command is not allowed in this mode

The --shell-command and --python-command CLI parameters are used for custom commands or the run-shell command. There is no special 'run-python' command. Instead, the script suffix is used to pick the script type (see Script Types).

Script Integration Modes

There are 2 ways to use scripts:

  • Manual: Built in run or run-shell command used to invoke a script directly.

    • The script name does not appear as a built-in command.

    • There is no YANG representation of the script input parameters, just the 'run-shell' parameters ('P1', 'P2', etc.).

    • There is no tab-key or help-key support for the script parameters.

    • The --allow-shell parameter must be 'true' to use this mode.

    • There is no vendor control over script selection except making sure no unwanted scripts are present in the --runpath.

  • Automatic: Custom Commands are installed with the --custom-command CLI parameter. They are defined in YANG modules as 'RPC' operations.

    • The script name does appear as a built-in command.

    • There is a full YANG representation of the script input parameters.

    • There is full tab-key and help-key support for the script parameters.

    • The --allow-shell parameter is ignored in this mode.

    • There is full vendor control over script selection so no unwanted scripts are present as commands.

Script Types

There are 3 types of scripts supported. For custom commands, the script name is derived from the RPC operation name.

Script Type

Suffix

Description

Python

.py

Script invoked with the --python-command

Shell

.sh

Script invoked with the --shell-command

yangcli

none

Script invoked with the run command

Custom Commands

A custom command is a user or vendor-added command added to the Exec Mode command set. It is defined with a YANG 'rpc' statement like most other commands.

The --custom-command CLI parameter is used to specify the YANG module name and optionally the specific RPC operation to install as a custom command.

Warning

Custom commands are enabled automatically since they require explicit configuration to add to the command parser.

The --allow-shell and --ypshell-exec-ok CLI parameters do not affect custom command access.

Custom Command YANG Usage

A YANG 'rpc' statement is used to define a custom command.

  • The YANG 'rpc' name identifies the command name

  • The YANG 'rpc' input statement defines the command input parameters.

  • The script is expected to process the parameters (if any) and print messages to the 'stdout' device (if any).

  • No output parameters are returned and the YANG output statement is ignored if present.

  • YANG field validation is done for each input parameter

  • YANG 'must' and 'when' statements are ignored and not validated.

  • YANG augments are not supported. Only the input parameters listed in the specified 'rpc' statement are supported.

Custom Command Usage Restrictions

  • The custom commands are loaded at boot-time and cannot be updated or changed at run-time.

  • If the wildcard '*' form is used for a module, then no other --custom-command parameters are allowed for that module,

  • The custom command name cannot be the same as any yangcli-pro built-in command or the same name as any other custom command.

  • It is not possible to replace a built-in command with a custom command instead.

Custom Command CLI Syntax

The --custom-command parameter is a structured string with 3 parts:

module-name ':' rpc-name [ '/' scope-id ]

Example: Load all the RPC statements in the 'test-commands' YANG module as top-level commands.

yangcli-pro --custom-command=test-commands:*

Fields:

The module name must be provided. It is expected to contain at least one RPC statement to define the custom command.

The rpc name is the identifier for the RPC statement.

  • The value '*' can be used to indicate that all the RPC statements found in the module should be used as custom commands.

    • If the '*' wildcard is used then a warning is printed if no RPC statements are found in that module.

  • Otherwise this string is the name of an RPC operation in the specified YANG module.

    • An error is printed if the specific RPC statement is not found. The custom command will not be loaded but the program will not exit.

The scope-id is a 1 letter code identifying the RPC command scope.

  • The letter T is used to identify a top-level command that is always available.

  • The letter S is used to identify a session-level command that is only available in a connected session.

  • If the scope ID is missing then the default is T for yangcli-pro or yangcli-gw.

  • This field is ignored for yp-shell, and the value used is always S. The scope ID is not relevant for yp-shell sessions since it is always connected to the local server.

Custom Command Script Parameters

When the command is executed, the command parser will look in the --runpath for a Python, Shell, or yangcli script (in that order).

If a script is found, it will be invoked with the following parameters. The same 5 parameters are always passed to a custom command script.

  • Script Identifier

    • Parameter 0

    • Full filespec path for the script

    • Example: /home/user1/scripts/command1.sh

  • RPC Identifier

    • Parameter 1

    • modname:version:rpc for the YANG source

    • Example: test-commands@2025-05-30:command1

    • The version field will be none if the module does not have a revision date.

  • Session Identifier

    • Parameter 2

    • username@server-address OR none

    • Example: user1@192,168.0.10

    • This parameter value will be the string none if the yangcli session is not connected to a server.

  • YANG Input Parameters

    • Parameter 3

    • Full input filespec OR none

    • Input parameters are encoded in JSON similar to a RESTCONF POST request.

    • The JSON object is for the 'input' container for the specified 'rpc' operation.

    • The RFC 7951 syntax is followed except there is no module-name prefix used.

    • YANG defaults are added to the input parameters if any are defined.

    • Example: /home/user1/.yumapro/tmp/20250605013521171838/command1-input-1.json

    • Example script file contents:

    {
      "input": {
        "parm1":"test2",
        "parm2":18
      }
    }
    
    • This parameter value will be the string none if the script does not define any input parameters of none were provided when the command was invoked.

    • If no session is active, the temporary input file will be created within the 'tmp' directory for the program instance that is running.

      Example: /home/user1/.yumapro/tmp/20250605013521171838/

    • If a session is active, the temporary input file will be created within the 'tmp' directory for the session, in the program instance that is running.

      Example: /home/user1/.yumapro/tmp/20250605013521171838/12

    • The last number in the filename is the command invocation sequence which will increment each time any custom command is invoked. It is shared across all commands, not the sequence number for that specific command.

  • Current Log Level

    • Parameter 4

    • Current log level

    • Example: info

    • This parameter will contain the string none if some error occurred and the real --log-level value cannot be determined.

Custom Command Usage Example

This example is intentionally simple but it shows all the steps of setting up and using custom commands.

Example YANG Module

A YANG module is needed to define the RPC operations. In this example 'test-commands.yang' is used. The module used must be found in one of the directories specified in the --modpath (or default search path).

 module test-commands {
     yang-version 1.1;
     namespace "urn:yumaworks:params:xml:ns:yang:test-commands";
     prefix tc;

     revision "2025-05-30";

     rpc command1 {
       description
        "Custom CLI command 1";
        input {
          leaf parm1 {
            type string;
            description "Parameter 1";
          }
          leaf parm2 {
            type uint32;
            description "Parameter 2";
          }
        }
     }

     rpc command2 {
       description
        "Custom CLI command 2";
        input {
          leaf parm3 {
            type string;
            description "Parameter 3";
          }
          leaf parm4 {
            type int32;
            description "Parameter 4";
          }
        }
     }

}

Example Bash Command Script

This simple script just echos the input parameters and prints the contents of the JSON input parameters file if present.

#
# yangcli-pro Custom Command Script
# Bash script for the test-commands:command1 RPC operation
#

echo ""
echo "Script (P0)= $0"
echo "RPC (P1)= $1"
echo "Session (P2)= $2"
echo "Input (P3)= $3"
echo "Log Level (P4)= $4"

if [[ "$3" != "none" && "$3" != "" ]]; then
    echo "Input File Contents:";
    cat $3;
fi

Example Python Command Script

This simple script just echos the input parameters and loads the JSON input file as a JSON object.

# yangcli-pro Custom Command Script
# Python script for the test-commands:command2 RPC operation
#

import argparse
import json

def load_and_parse_json(file_path):
    """
    Loads a JSON file, parses it, and returns the JSON object.

    Args:
        file_path (str): The path to the JSON file.

    Returns:
        dict: The parsed JSON object as a Python dictionary,
        or None if an error occurs.
    """
    try:
        with open(file_path, 'r') as file:
            data = json.load(file)
        return data
    except FileNotFoundError:
        print(f"Error: File not found at {file_path}")
        return None
    except json.JSONDecodeError:
        print(f"Error: Invalid JSON format in {file_path}")
        return None
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return None

def main():
    """
    Loads the Custom Command input parameters
    P1 = RPC identifier
    P2 = Session Info
    P3 = Input Parameter File
    P4 = Log Level
    """

    parser = argparse.ArgumentParser(description="Command2 Help")
    parser.add_argument("rpc", type=str, help="The operation being invoked.")
    parser.add_argument("session", type=str, help="The session information.")
    parser.add_argument("input", type=str, help="The input parameters file.")
    parser.add_argument("loglevel", type=str, help="The current log-level.")
    args = parser.parse_args()

    print()
    print(f"RPC (P1)= {args.rpc}")
    print(f"Session (P2)= {args.session}")
    print(f"Input (P3)= {args.input}")
    print(f"Log Level (P4)= {args.loglevel}")

    if args.input is not None and args.input != 'none':

        data = load_and_parse_json(args.input)

        if data:
            print(f"Got JSON Input {data}")

            if "input" in data:
                topdata = data["input"]

                if "parm3" in topdata:
                    parm3 = topdata["parm3"]
                    print(f"Parm 3: {parm3}")
                else:
                    print(f"Parm 3: none")

                if "parm4" in topdata:
                    parm4 = topdata["parm4"]
                    print(f"Parm 4: {parm4}")
                else:
                    print(f"Parm 4: none")


if __name__ == "__main__":
    main()

Example Tab and Help Key

The 'command1' and 'command2' keywords are automatically supported within the context sensitive help keys.

Example: tab key pressed after the letter 'c'

> c
callhome-server  cd               collector        command2
callhome-user    clear            command1         connect

Example: help '?' key pressed after the letter 'c'

> c

  callhome-server   Access and control the CallHome server.
  callhome-user     Access and configure the CallHome Users.
  cd                Change the current working directory.
  clear             Clear the screen in interactive mode.
  collector         Access and control the UDP-Notif Collector.
  command1          Custom CLI command 1
  command2          Custom CLI command 2
  connect           Connect to a NETCONF server.

> c

Example Command Invocations

Example: command1 invoked without any session connection:

> command1 parm1=test1 parm2=17

Script (P0)= command1.sh
RPC (P1)= test-commands@2025-05-30:command1
Session (P2)= none
Input (P3)= /home/andy/.yumapro/tmp/20250606155131395334/command1-input-1.json
Log Level (P4)= info
Input File Contents:
{
  "input": {
    "parm1":"test1",
    "parm2":17
  }
}

>

Example: command1 invoked with a session connected:

andy@localhost> command1 parm1=test2 parm2=18

Script (P0)= command1.sh
RPC (P1)= test-commands@2025-05-30:command1
Session (P2)= andy@localhost
Input (P3)= /home/andy/.yumapro/tmp/20250606155131395334/11/command1-input-2.json
Log Level (P4)= info
Input File Contents:
{
  "input": {
    "parm1":"test2",
    "parm2":18
  }
}

andy@localhost>

Example: command1 invoked with no parameters

andy@localhost> command1

Script (P0)= command1.sh
RPC (P1)= test-commands@2025-05-30:command1
Session (P2)= andy@localhost
Input (P3)= none
Log Level (P4)= info

andy@localhost>

Example: command2 invoked without any session connection:

> command2 parm3=test parm4=3

RPC (P1)= test-commands@2025-05-30:command2
Session (P2)= none
Input (P3)= /home/andy/.yumapro/tmp/20250607175808652315/command2-input-4.json
Log Level (P4)= info
Got JSON Input {'input': {'parm3': 'test', 'parm4': 3}}
Parm 3: test
Parm 4: 3

>

Example: command2 invoked with a session connected:

andy@localhost> command2 parm3=test parm4=3

RPC (P1)= test-commands@2025-05-30:command2
Session (P2)= andy@localhost
Input (P3)= /home/andy/.yumapro/tmp/20250607175808652315/11/command2-input-5.json
Log Level (P4)= info
Got JSON Input {'input': {'parm3': 'test', 'parm4': 3}}
Parm 3: test
Parm 4: 3

andy@localhost>

Example: command2 invoked with no parameters

andy@localhost> command2

RPC (P1)= test-commands@2025-05-30:command2
Session (P2)= andy@localhost
Input (P3)= none
Log Level (P4)= info

andy@localhost>

Example Error If Script Not Found

An error will be printed if the script execution fails for some reason.

Example: script not installed

> command1 parm1=test1 parm2=17

Error: Error getting script for custom command 'command1' (not found)

>