Parsing Command Output in Nornir with JC

In my last couple of posts we learned how to parse linux command output in Ansible and Saltstack using jc. In this post we’ll do something similar with Nornir.

Nornir is a popular automation framework that allows you to use native python to control hosts and network devices. Many times it would be nice to be able to parse the output of remotely-run commands and use that information elsewhere in your scripts. jc allows you to do this automatically – no regex/looping/slicing/etc. required to get to the data you want!

Since jc is both a command line tool and a python library, it is easy to use inside a Nornir script to automate the boring work of command output parsing.

For more information on the motivations for creating jc, see my blog post.

Installation

To use jc in a Nornir script, simply install it and import one or more parsers.

Installing jc:

$ pip3 install jc

Import the jc library:

import jc

Now we are ready to use the jc in our Nornir script!

Syntax

To use the jc parser, call the parse function with the parser name and command output arguments. For example, to automatically parse a uname -a output string:

uname_obj = jc.parse('uname', uname_command_output_string)

Now you can use whatever uname field you would like in the rest of your code:

print(uname_obj['node_name'])

A Simple Example

Below we have a small Nornir script using Netmiko to call a few commands on a linux host. (uname, date, ifconfig, and uptime) I used the nornir-netmiko package to simplify the connection to the linux host:

from nornir import InitNornir
from nornir_netmiko.tasks import netmiko_send_command
import jc

nr = InitNornir(config_file='config.yaml')

def run_commands(task, command_list):
    for cmd in command_list:
        task.run(
            task=netmiko_send_command,
            command_string=cmd,
            name=cmd
        )

commands = ['uname -a', 'date', 'ifconfig', 'uptime']

result = nr.run(
    task=run_commands,
    command_list=commands
)

uname_result_string = result['host1'][1].result
uname_result_obj = jc.parse('uname', uname_result_string)
hostname = uname_result_obj['node_name']
kernel_version = uname_result_obj['kernel_version']

date_result_string = result['host1'][2].result
date_result_obj = jc.parse('date', date_result_string)
timezone = date_result_obj['timezone']

ifconfig_result_string = result['host1'][3].result
ifconfig_result_obj = jc.parse('ifconfig', ifconfig_result_string)
ipv4_addr = ifconfig_result_obj[1]['ipv4_addr']

uptime_result_string = result['host1'][4].result
uptime_result_obj = jc.parse('uptime', uptime_result_string)
uptime = uptime_result_obj['uptime']

print(f'hostname: {hostname}')
print(f'kernel version: {kernel_version}')
print(f'timezone: {timezone}')
print(f'ip address: {ipv4_addr}')
print(f'uptime: {uptime}')

Script output:

$ python3 nornir_with_jc.py 
hostname: my-ubuntu
kernel version: #113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020
timezone: UTC
ip address: 192.168.1.239
uptime: 47 min

Here you can see we have run a few tasks and assigned the results to some variables. Let’s go over the uname -a output:

uname_result_string = result['host1'][1].result

Above, we are grabbing the string result output attribute from the uname -a command (the first command in the commands list) and are assigning it to uname_result_string. There are cleaner ways of getting the result info from Nornir, but this way we can see the structure of the result object.

uname_result_obj = jc.parse('uname', uname_result_string)

Next, we have run uname_result_string through the jc uname parser and assigned the resulting dictionary object to the uname_result_obj variable.

hostname = uname_result_obj['node_name']
kernel_version = uname_result_obj['kernel_version']

Then, we created a couple of variables that we can use in our script called hostname and kernel_version so we can grab just the object attributes we are interested in. jc returns standard dictionary objects, so they are easy to use.

print(f'hostname: {hostname}')
print(f'kernel version: {kernel_version}')

Finally, we use our variables in a print function, but we could have used these objects anywhere else in the script.

Nice! Instead of parsing the STDOUT text manually, we used jc to automatically parse the command output, providing us a convenient object to use elsewhere in our script. No more need to regex or loop and slice your way through the output to get what you are looking for!

For a complete list of jc parsers available and their associated schemas, see the parser documentation.

Happy parsing!

Published by kellyjonbrazil

I'm a cybersecurity and cloud computing nerd.

Leave a Reply

%d