Script Infrastructure and Sample Templates

Table 1. Feature History Table

Feature Name

Release Information

Description

Contextual Script Infrastructure

Release 7.3.2

When you create and run Python scripts on the router, this feature enables a contextual interaction between the scripts, the IOS XR software, and the external servers. This context, programmed in the script, uses Cisco IOS XR Python packages, modules, and libraries to:

  • obtain operational data from the router

  • set configurations and conditions

  • detect events in the network and trigger an appropriate action

You can create Python scripts and execute the scripts on routers running Cisco IOS XR software. The software supports the Python packages, libraries and dictionaries in the software image. For more informtion about the script types and to run the scripts using CLI commands To run the same actions using NETCONF RPCs,

Cisco IOS XR, Release 7.3.2 supports creating scripts using Python version 3.5.

Cisco IOS XR, Release 7.5.1 supports creating scripts using Python version 3.9.

Cisco IOS XR Python Packages

Table 2. Feature History Table

Feature Name

Release Information

Description

Upgraded IOS XR Python from Version 3.5 to Version 3.9

Release 7.5.1

This upgrade adds new modules and capabilities to create Python scripts and execute the scripts on routers running Cisco IOS XR software. Some of the modules added as part of the upgraded IOS XR Python 3.9 are: hashlib, idna, packaging, pyparsing, six, yaml.

With on-box Python scripting, automation scripts that was run from an external controller is now run on the router. To achieve this functionality, Cisco IOS XR software provides contextual support using SDK libraries and standard protocols.

The following Python third party application packages are supported by the scripting infrastructure and can be used to create automation scripts.

Package

Description

Support Introduced in Release

appdirs

Chooses the appropriate platform-specific directories for user data.

Release 7.3.2

array

Defines an object type that can compactly represent an array of basic values: characters, integers, floating point numbers.

Release 7.3.2

asn1crypto

Parses and serializes Abstract Syntax Notation One (ASN.1) data structures.

Release 7.3.2

chardet

Universal character encoding auto-detector.

Release 7.3.2

concurrent.futures

Provides a high-level interface for asynchronously executing callables.

Release 7.3.2

ecdsa

Implements Elliptic Curve Digital Signature Algorithm (ECDSA) cryptography library to create keypairs (signing key and verifying key), sign messages, and verify the signatures.

Release 7.3.2

enum

Enumerates symbolic names (members) bound to unique, constant values.

Release 7.3.2

email

Manages email messages.

Release 7.3.2

google.protobuf

Supports language-neutral, platform-neutral, extensible mechanism for serializing structured data.

Release 7.3.2

hashlib

Implements a common interface to many different secure hash and message digest algorithms.

Release 7.5.1

idna

Supports the Internationalized Domain Names in Applications (IDNA) protocol as specified in RFC 5891.

Release 7.5.1

ipaddress

Provides capability to create, manipulate and operate on IPv4 and IPv6 addresses and networks.

Release 7.3.2

jinja2

Supports adding functionality useful for templating environments.

Release 7.3.2

json

Provides a lightweight data interchange format.

Release 7.3.2

markupsafe

Implements a text object that escapes characters so it is safe to use in HTML and XML.

Release 7.3.2

netaddr

Enables system-independent network address manipulation and processing of Layer 3 network addresses.

Release 7.3.2

packaging

Add the necessary files and structure to create the package.

Release 7.5.1

pdb

Defines an interactive source code debugger for Python programs.

Release 7.3.2

pkg_resources

Provides runtime facilities for finding, introspecting, activating and using installed distributions.

Release 7.3.2

psutil

Provides library to retrieve information on running processes and system utilization such as CPU, memory, disks, sensors and processes.

Release 7.3.2

pyasn1

Provides a collection of ASN.1 modules expressed in form of pyasn1 classes. Includes protocols PDUs definition (SNMP, LDAP etc.) and various data structures (X.509, PKCS).

Release 7.3.2

pyparsing

Provides a library of classes to construct the grammar directly in Python code.

Release 7.5.1

requests

Allows sending HTTP/1.1 requests using Python.

Release 7.3.2

shellescape

Defines the function that returns a shell-escaped version of a Python string.

Release 7.3.2

six

Provides simple utilities for wrapping over differences between Python 2 and Python 3.

Release 7.5.1

subprocess

Spawns new processes, connects to input/output/error pipes, and obtain return codes.

Release 7.3.2

urllib3

HTTP client for Python.

Release 7.3.2

xmltodict

Makes working with XML feel like you are working with JSON.

Release 7.3.2

yaml

Provides a human-friendly format for structured data, that is both easy to write for humans and still parsable by computers.

Release 7.5.1

Cisco IOS XR Python Libraries

Cisco IOS XR software provides support for the following SDK libraries and standard protocols.

Library

Syntax

gnmi

To connect to gnmi client #
from iosxr.gnmi.gnmi_lib import GNMIClient
gnmi = GNMIClient()

For more information, see Use Automation Scripts to Interact with the Router via gNMI RPCs.

xrlog

# To generate syslogs #
from cisco.script_mgmt import xrlog

syslog = xrlog.getSysLogger('template_exec')

netconf

#To connect to netconf client #
from iosxr.netconf.netconf_lib import NetconfClient

nc = NetconfClient(debug=True)

xrclihelper

# To run native xr cli and config commands
from iosxr.xrcli.xrcli_helper import *

helper = XrcliHelper(debug = True)

config_validation

# To validate configuration #
import cisco.config_validation as xr

eem

# For EEM operations #
from iosxr import eem

precommit

# For Precommit script operations #
from cisco.script_mgmt import precommit

Sample Script Templates

Table 3. Feature History Table

Feature Name

Release Information

Description

Github Repository for Automation Scripts

Release 7.5.1

You now have access to sample scripts and templates published on the Github repository. You can leverage these samples to use the python packages and libraries developed by Cisco to build your custom automation scripts for your network

Use these sample script templates based on script type to build your custom script.

To get familiar with IOS XR Python scripts, see the samples and templates on the Cisco Devnet developer program and Github repository.

Follow these instructions to download the sample scripts from the Github repository to your router, and run the scripts:

  1. Clone the Github repository.

    $git clone https://github.com/CiscoDevNet/iosxr-ops.git
  2. Copy the Python files to the router's harddisk or a remote repository.

Precommit Script

The following example shows the template for precommit scripts

from cisco.script_mgmt import precommit

def sample_method():
    """
    Method documentation
    """

    cfg = precommit.get_target_configs()
    # cfg = precommit.get_target_configs(format="sysdb") for target config in sysdb format

    # process and verify target configs here.

    precommit.config_warning("Print a warning message in commit report")
    precommit.config_error("Print an error message in commit report and abort commit operation")


if __name__ == '__main__':
    
    sample_method()

Config Script

The following example shows a code snippet for config script. Use this snippet in your script to import the libraries required to validate configuration and also generate syslogs.
#Needed for config validation 
import cisco.config_validation as xr

#Used for generating syslogs
from cisco.script_mgmt import xrlog
syslog = xrlog.getSysLogger('Add script name here')

def check_config(root):
    #Add config validations
    pass
                        
xr.register_validate_callback([<Add config path here>],check_config)

Exec Script

Use this sample code snippet in your exec script to import Python libraries to connect to NETCONF client and also to generate syslogs.
#To connect to netconf client
from iosxr.netconf.netconf_lib import NetconfClient

#To generate syslogs
syslog = xrlog.getSysLogger('template_exec')

def test_exec():
    """
    Testcase for exec script
    """
    nc = NetconfClient(debug=True)
    nc.connect()
    #Netconf or processing operations
    nc.close()
    

if __name__ == '__main__':
    test_exec()

Process Script

Use the following sample code snippet to trigger a process script and perform various actions on the script. You can leverage this snippet to create your own custom process script. Any exec script can be used as a process script.
To trigger script
Step 1: Add and configure script as shown in README.MD

Step 2: Register the application with Appmgr

Configuraton:
appmgr process-script my-process-app
executable test_process.py
run args --threshold <threshold-value>

Step 3: Activate the registered application
appmgr process-script activate name my-process-app

Step 4: Check script status
show appmgr process-script-table

Router#show appmgr process-script-table
Name             Executable         Activated    Status     Restart Policy   Config Pending
---------------  ------------------ --------- ------------- ---------------- --------------
my-process-app   test_process.py      Yes       Running    On Failure            No

Step 5: More operations
Router#appmgr process-script ?
  activate    Activate process script
  deactivate  Deactivate process script
  kill        Kill process script
  restart     Restart process script
  start       Start process script
  stop        Stop process script
""" 

#To connect to netconf client
from iosxr.netconf.netconf_lib import NetconfClient

#To generate syslogs
syslog = xrlog.getSysLogger('template_exec')

def test_process():
    """
    Testcase for process script
    """
    nc = NetconfClient(debug=True)
    nc.connect()
    #Netconf or any other operations
    nc.close()
    

if __name__ == '__main__':
    test_process()

EEM Script

You can leverage the following sample code to import Python libraries to create your custom eem script and also generate syslogs.
Required configuration:
User and AAA configuration

event manager event-trigger <trigger-name>
type syslog pattern "PROC_RESTART_NAME"

event manager action <action-name>
username <user>
type script script-name <script-name> checksum sha256 <checksum>

event manager policy-map policy1
trigger event <trigger-name>
action <action-name>

To verify:
Check for syslog EVENT SCRIPT EXECUTED: User restarted <process-name>

"""
#Needed for eem operations
from iosxr import eem

#Used to generate syslogs
from cisco.script_mgmt import xrlog
syslog = xrlog.getSysLogger(<add your script name here>)

# event_dict consists of details of the event
rc, event_dict = eem.event_reqinfo()

#You can process the information as needed and take action for example: generate a syslog.
#Syslog type can be emergency, alert, critical, error, exception, warning, notification, info, debug

syslog.info(<Add you syslog here>)

Use Automation Scripts to Interact with the Router via gNMI RPCs

Table 4. Feature History Table

Feature Name

Release Information

Description

Automation Scripts for gNMI RPCs

Release 7.5.2

You can create automation scripts to connect to the gRPC Network Management Interface (gNMI) server and interact with the router using gNMI services. Based on gNMI-defined RPCs, you can use the automation script to connect to the gNMI server, manage the configuration of network devices, and query the operational data.

gRPC Network Management Interface (gNMI) is developed by Google. gNMI provides the mechanism to install, manipulate, and delete the configuration of network devices, and also to view operational data. The content provided through gNMI can be modeled using YANG. The supported operations are based on the gNMI defined RPCs:

from iosxr.gnmi.gnmi_lib import GNMIClient
gnmi = GNMIClient()

#Connect
gnmi.connect()

#Capabilities
cap = gnmi.capabilities()

#Get
get = gnmi.get(get_request)

#Set
set = gnmi.set(set_request)

#Disconnect
gnmi.disconnect()
  • gNMI Capabilities RPC: This RPC allows the client to retrieve the gNMI capabilities that is supported by the target (router). This allows the target to validate the service version that is implemented and retrieve the set of models that the target supports. The models can then be specified in subsequent RPCs to restrict the set of data that is utilized. The CapabilityRequest RPC returns a response CapabilityResponse RPC.

  • gNMI GET RPC: This RPC specifies how to retrieve one or more of the configuration attributes, state attributes or all attributes associated with a supported mode from a date tree. A GetRequest RPC is sent from a client to the target to retrieve values from the data tree. A GetResponse RPC is sent in response to the request.

  • gNMI SET RPC: This RPC specifies how to set one or more configurable attributes associated with a supported model. A SetRequest RPC is sent from a client to a target to update the values in the data tree. The actions contained in a SetRequest RPC is treated as a single transaction. If any element of the transaction fails, the entire transaction fails and is rolled back. A SetResponse RPC is sent in response to the request.

  • gNMI Connect RPC: This RPC specifies how to initiaize a connection to the client.

  • gNMI Disconnect RPC: This RPC specifies how to end the connection with the client.

Restrictions for the gNMI Protocol

The following restrictions apply to the gNMI protocol:

  • Subscribe RPC services are not supported.

  • Only JSON_IETF encoding for GET and SET requests is supported

  • CLI over GNMI is not supported

Follow the procedure to use automation scripts to interact with the router via gNMI services:

Procedure


Step 1

Create script using the GNMIClient python module.

Example:

In this example, you create a script to connect with the router using gNMI capabilities.
from iosxr.gnmi.gnmi_lib import GNMIClient

gnmi = GNMIClient()
gnmi.connect()
print("Getting capabilities")
cap = gnmi.capabilities()
print("Get")
get_req = """
path: {
    elem: {
        name: "network-instances"
    }
    elem: {
        name: "network-instance"
        key: {
            key: "name"
            value: "vrf_1"
        }
    }
    origin: "openconfig-network-instance"
}
type: CONFIG
encoding: JSON_IETF
"""
get = gnmi.get(get_req)
print("Set")
set_req = """
prefix: <
    origin:"openconfig-interfaces"
>
update: <
path: <
            elem: <
                name: "interfaces"
            >
            elem: <
                name: "interface"
                key: <
                    key: "name"
                    value: "MgmtEth0/RP0/CPU0/0"
                >
            >
            elem: <
                name: "config"
            >
        >
        val: <
            json_ietf_val: '{"description":"Testing failover case: testrole200"}'
        >
>
"""
set = gnmi.set(set_req)
import pdb;pdb.set_trace()

Step 2

Configure gRPC.

Example:

Router#config
Router(config)#grpc
Router(config-grpc)#local connection
Router(config-grpc)#no-tls
Router(config-grpc)#commit

Step 3

Copy the script to the router.

Step 4

Verify that the script is available on the router.

Example:

Router#show script status detail
Tue Apr 12 23:10:50.453 UTC
================================================================================================
 Name                     | Type     | Status           | Last Action | Action Time
-------------------------------------------------------------------------------------------------
 gnmi-sample-script.py    | exec     | Config Checksum  | NEW         | Tue Apr 12 10:18:23 2021
 ================================================================================================
Script Name        : gnmi-sample-scripy.py
Checksum           : 94336f3997521d6e1aec0ee6faab0233562d53d4de7b0092e80b53caed58414b
Script Description : View gNMI capabilities
History:
 --------
 1.   Action       : NEW
      Time         : Tue Apr 12 05:03:41 2021
      Description  : User action IN_CLOSE_WRITE
===========================================================================================
Router(config)#exit

Step 5

Add the script to the script management repository.

Example:

Router#script add <type> <location> <name>
In this example, you add an Exec script gnmi-sample-script.py to the router.
Router#script add exec /harddisk\: gnmi-sample-scripy.py
Tue Apr 18 16:16:46.427 UTC
Copying script from /harddisk:/gnmi-sample-scripy.py
gnmi-sample-scripy.py has been added to the script repository

Step 6

Configure the checksum.

Example:

Router(config)#script <type> <name> checksum SHA 256 <checksum>
In this example, you configure the checksum for the Exec script gnmi-sample-script.py to the router.

Example:

Router(config)#script exec gnmi-sample-script.py checksum SHA 256 
94336f3997521d6e1aec0ee6faab0233562d53d4de7b0092e80b53caed58414b
Router(config)#commit
Router(config)#end

Step 7

Run the script.

Example:

Router#script run gnmi-sample-script.py
Tue Apr 18 16:17:46.427 UTC
Script run scheduled: gnmi-sample-script.py. Request ID: 1634055439
Getting capabilities
..................................

The following example shows the output of the gNMI get operation:

notification: <
  timestamp: 1649917466577514766
  update: <
    path: <
      origin: "openconfig-interfaces"
      elem: <
        name: "interfaces"
      >
      elem: <
        name: "interface"
        key: <
          key: "name"
          value: "TenGigE0/0/0/0"
        >
      >
    >
    val: <
      json_ietf_val: "{\n \"config\": {\n  \"name\": \"TenGigE0/0/0/0\",\n  \"type\": 
\"iana-if-type:ethernetCsmacd\",\n  \"enabled\": false\n },\n \"openconfig-if-ethernet:
ethernet\": {\n  \"config\": {\n   \"auto-negotiate\": false\n  }\n }\n}\n"
    >
  >
  update: <
    path: <
      origin: "openconfig-interfaces"
      elem: <
        name: "interfaces"
      >
      elem: <
        name: "interface"
        key: <
          key: "name"
          value: "TenGigE0/0/0/1"
        >
      >
    >
    val: <
      json_ietf_val: "{\n \"config\": {\n  \"name\": \"TenGigE0/0/0/1\",\n  \"type\": 
\"iana-if-type:ethernetCsmacd\",\n  \"enabled\": false\n },\n \"openconfig-if-ethernet:
ethernet\": {\n  \"config\": {\n   \"auto-negotiate\": false\n  }\n }\n}\n"
    >
------------------------------- Output truncated for brevity ----------------------------------------- 

Xrcli_helper Python Module

Overview of xrcli_helper Python Module

The XrcliHelper is a utility class designed to facilitate the execution of IOS-XR CLI commands and configuration changes programmatically. It provides methods to:

  • Execute native IOS-XR commands.

  • Apply configurations from files or strings.

Prerequisites of xrcli_helper Python Module

  • Python 2.7 or higher.

  • Ensure that you are on Cisco IOS XR Release 7.4.x or higher.

  • Access to Cisco IOS XR device with AAA Authorization enabled. Use the aaa authorization exec default group tacacs+ local command to enable AAA Authorization.

  • Ensure that the iosxr.xrcli.xrcli_helper module is available in your Python environment.

Import Library Information

To use the XrcliHelper class in your Python script, you need to import it from the appropriate module. The import statement provided allows you to bring the XrcliHelper class into your script so you can create instances of it and use its methods.

from iosxr.xrcli.xrcli_helper import XrcliHelper

Library/API Initialization

By initializing the XrcliHelper class, you establish the environment needed to execute IOS-XR commands and apply configurations programmatically. This serves as the initial step in automating network management tasks, enabling you to utilize the class's methods to efficiently interact with your IOS-XR devices.

<object name> = XrcliHelper([debug=True/False(default)])

This example shows how to initialize Xrclihelper class.

helper = XrcliHelper()

Xrcli_helper Script APIs

xrcli_exec

The xrcli_exec API executes IOS-XR exec commands to obtain the output.

Parameter

cmd: A String representing the IOS- XR exec command to be executed.

Result

The result of the xrcli_exec API is a dictionary containing:

  • status: Indicates whether the command execution was error or success.

  • output: The output of the executed command.

Example

The following example shows the sample output of xrcli_exec API:

>>> result = helper.xrcli_exec("show filesystem ")
 
>>> print(result)
{'output': '\n'
           '------------------------------- show filesystem '
           '-------------------------------\n'
           'File Systems:\n'
           '\n'
           '      Size(b)      Free(b)        Type  Flags  Prefixes\n'
           '   4275265536   4274974720  flash-disk     rw  disk0:\n'
           '  67301322752  67266158592    harddisk     rw  harddisk:\n'
           '            0            0     network     rw  ftp:\n'
           '  60264796160  51056054272       flash     rw  /misc/config\n'
           '            0            0     network     rw  tftp:\n',
'status': 'success'}

xr_apply_config_file

The xr_apply_config_file API applies configuration to IOS-XR using a file.

Parameter

  • filename: Path to a configuration file containing XR config commands with the following structure:

    !
    XR config command
    !
    end
  • comment: A comment for the configuration commit, which will be visible in the output of show configuration commit list detail .

Result

The result of xr_apply_config_file is a dictionary specifying the effect of the configuration change:

  • status: Indicates whether the configuration application was error or success.

  • output:

    • If status is error: use the show configuration failed command.

    • If status is success: use the show configuration commit changes last 1 command.

Example

The following example shows sample output of xr_apply_config_file API.

[node0_RP0_CPU0:~]$more /harddisk:/noshut_int.cfg
!
interface hundredGigE 0/0/0/24 
no shutdown
interface hundredGigE 0/0/0/25
no shutdown
!
end
 
>>> result = helper.xr_apply_config_file("/harddisk:/noshut_int.cfg")
>>> print(result)
{'output': '\n'
           '------------------ show configuration commit changes last 1 '
           '-------------------\n'
           '!! Building configuration...\n'
           '!! IOS XR Configuration x.y.z \n'
           'interface HundredGigE0/0/0/24\n'
           ' no shutdown\n'
           '!\n'
           'interface HundredGigE0/0/0/25\n'
           ' no shutdown\n'
           '!\n'
           'end\n'
           '\n',
'status': 'success'}
>>> 

xr_apply_config_string

The xr_apply_config_string applies configuration to XR using a single line string.

Parameter

cmd: Single line string representing an XR config command.

comment:Reason for the config commit, visible in show configuration commit list detail .

Result

The result of xr_apply_config_string is a dictionary specifying the effect of the configuration change:

  • status: Indicates whether the configuration application was error or success.

  • output:

    • If status is error: use the show configuration failed command.

    • If status is success: use the show configuration commit changes last 1 command.

Example

The following example shows sample output of xr_apply_config_file API.

>>> cmd = """
... interface HundredGigE0/0/0/25
... description "shut down by scriptx"
... shut
... """
>>> result = helper.xr_apply_config_string(cmd)
>>> print(result)
{'output': '\n'
           '------------------ show configuration commit changes last 1 '
           '-------------------\n'
           '!! Building configuration...\n'
           '!! IOS XR Configuration x.y.z\n'
           'interface HundredGigE0/0/0/25\n'
           ' description "shut down by scriptx"\n'
           ' shutdown\n'
           '!\n'
           'end\n'
           '\n',
'status': 'success'
>>>

user

The user is an XrcliHelper Object Attribute (not API) which contains the username to authorize the XR commands.

Example

The following example shows sample output of user.

Example:
 
>>> helper.user  
'cisco'

toggle_debug

The toggle_debug enables or disables debug logging.

Example

The following example shows sample output of toggle_debug.

>>> helper.toggle_debug(True)
>>>