Posts tagged ‘automation’

08/24/2014

Executing Arbitrary Junos ‘Show’ Commands with PyEZ and ncclient

by Jeff Loughridge

Still screen scraping routers with Expect scripts? It’s time to move to NETCONF, the industry standard for communicating with network infrastructure. The protocol was heavily influenced by Juniper’s XML API. Not surprisingly, Junos routers have solid NETCONF support. In this post, I’ll detail how an operator can use ncclient and PyEZ to execute arbitrary ‘show’ commands to obtain operational state. By “arbitrary”, I mean that the ‘show’ commands can be any valid Junos operational command that is not known at the time the script was written. The input could take the form of a file.

Let’s start with PyEZ, as this framework is much more friendly to network engineers who dabble in Python. To execute a ‘show’ command, the documentation recommends using the associated RPC for the command. You can map ‘show’ commands to RPCs by appending ‘| display xml rpc’. Here’s example output.

jeffl@SRX_R1> show version | display xml rpc
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/12.2I0/junos">
    <rpc>
        <get-software-information>
        </get-software-information>
    </rpc>
    <cli>
        <banner></banner>
    </cli>
</rpc-reply>

jeffl@SRX_R1>

The RPC in this example is get-software-information. This process is covered in more depth on the PyEZ wiki.

If you wanted to create a script that executed user-specified ‘show’ commands in a text file, do you see the problem? You’d need some hack to obtain the RPC for the ‘show’ command. Fortunately, PyEZ makes use of the <command> tag shortcut that is also employed by slax scripts. The method is Device.cli() (my thanks go to Kurt Bales and Nitin Kumar on the PyEZ mailing list for pointing this out to me).

The documentation rightly warns against the use of Device.cli(). The purpose of PyEZ is to get away from screen scraping and programmatically handling the router’s response. Fortunately, Device.cli() acepts an format=’xml’ parameter. This returns the RPC reply in XML for easy parsing in the language of your choosing. An example of this is op = jdev.cli(command, format=’xml’), where jdev is an instance of the Device class.

The PyEZ module is great for many automation tasks. The module is not needed for this use case, however. We can use the lower-level ncclient to achieve the same result. This requires a higher comfort level with Python and the lxml module.

The following code borrows from the Juniper examples in the ncclient github repo.

 

#!/usr/bin/env python
# Demonstrates the use of the 'command' tag to execute arbritrary 'show' commands.
# This code was inspired by Ebben Aries's command-jnpr.py at
# https://github.com/leopoul/ncclient/blob/master/examples/juniper/command-jnpr.py
#
# usage: python ncclient_demo.py <show command> <xpath expression>
# python ncclient_demo.py 'show route 2600::/64' '//rt-entry/nh'
# 
# Jeff Loughridge
# August 2014

import sys

from lxml import etree as etree
from ncclient import manager
from ncclient.xml_ import *

def connect(host, port, user, password, source):

    try:
        show_command = sys.argv[1]
    except IndexError:
        print "please specify show command as first argument."
        sys.exit(1)

    try:
        xpath_expr = sys.argv[2]
    except IndexError:
        xpath_expr=''

    conn = manager.connect(host=host,
            port=port,
            username=user,
            password=password,
            timeout=3,
            device_params = {'name':'junos'},
            hostkey_verify=False)

    try:
        result = conn.command(command=show_command, format='xml')
    except Exception, e:
        print "ncclient_demo.py: Encountered critical error"
        print e
        sys.exit(1)

    tree = etree.XML(result.tostring)

    if xpath_expr:
        filtered_tree_list = tree.xpath(xpath_expr)
        for element in filtered_tree_list:
            print etree.tostring(element)
    else:
        print etree.tostring(tree)

if __name__ == '__main__':
    connect('ROUTER', 830, 'USER', 'PASSWORD', 'candidate')

I posted this a gist.

To use this example, there are several prerequisites.

  1. You must have a Junos router configured with ‘set system services netconf’.
  2. The ROUTER, USER, and PASSWORD placeholders in the script must be filled in with a valid router IP/FQDN, user, and password.
  3. The lxml and ncclient modules must be installed. These are in PyPi and can be installed with pip.

The script supports the optional use of XPath expressions to parse the output. XPath is complex topic that is covered elsewhere. The parsing can be performed with lxml’s ElementPath or countless other python modules used to parse XML.

I hope readers find this example useful.

 

07/21/2013

Simplifying Your Junos SLAX Development Environment

by Jeff Loughridge

I’m excited by the possibilities that network programmability offers network operators. Through JUNOS SLAX scripts, Juniper has offered a simple mechanism for programming its routers for many years.

Over the last several months, I’ve been writing primarily ops scripts in SLAX. I want to share my findings on how to simplify a SLAX development environment.

JUISE/libslax – If you want to write SLAX scripts, download the JUNOS User Internet Scripting Environment (juise) for “off-the-box” scripting. juise requires libslax, another open source project by Juniper. The combination allows you to remotely execute ops and commit scripts on JUNOS devices. You don’t have to manually scp/ftp scripts from your server to the routers. libslax contains a SLAX syntax checker called slaxproc. JUISE has a built-in debugger that will make you wonder how you ever gotten anything done in SLAX without it.

refresh command – Another way to avoid manually copying SLAX scripts to routers is the use of the refresh command in JUNOS configuration mode.  I use a lightweight web server from google called mongoose to serve files from the directory in which I write the scripts. Any web server will do the trick though.

I set the sources in the config using this syntax.

set system scripts op file script1.slax source http://192.168.100.10:8080/script1.slax
set system scripts op file script2.slax source http://192.168.100.10:8080/script2.slax
set system scripts op file slax-doctor.slax source http://192.168.100.10:8080/slax-doctor.slax

Now you can execute ‘set system scripts op refresh’ and JUNOS will download the files to /var/db/script/ops. Invoking the command from configuration mode feels very un-JUNOS-like. You’ll be prompted by the CLI when exiting config mode to confirm that you want to exit with uncommitted changes.

slax-doctor – Curtis Call’s slax-doctor SLAX script is invaluable for SLAX scripting.  libslax does very limited error checking, leaving most errors undiscovered until runtime. The reporting of errors often does not identify the one unterminated string or other typo that is wreaking havoc on the script. I execute slax-doctor every time that I make non-trivial change. You can download the script here or in the collection of scripts in the junoscriptorium project on github.

op invoke-debugger cli – JUNOS 13.1 introduces official support of the ‘op invoke-debugger cli‘ operational mode command. The command is hidden is some prior versions of JUNOS. I’ve used the on-box debugger very rarely. I prefer using the debugger in juise.

If you develop SLAX scripts and want to share other tips, please include them in the comments section. For other readers who have not delved into SLAX, start reading the This Week: Applying the Junos Automation ebook and get coding today.