Lab-12:Using and Creating PyEZ tables

Parsing command response is a challenge when automating network elements. It was a big challenge when interfaces were cli due to complexity and variation in cli implementation among vendors. NETCONF together with Python xml libraries made life easier.  I wrote two lab-7 & lab-8 on how to parse command response  in Python. They are written for NETCONF xml response  using minidom and etree, xpath.

PyEZ table and view abstract xml parsing from user.  Under the hood it does xml parsing in xpath but as a user you don’t need to worry about it. A user simply provides the command and  the response fields they are interested in, PyEZ does the parsing and adds it in Python dictionary.  User data is formatted in yaml.
In this lab I will demonstrate below functions of PyEZ
1) How to use existing PyEZ tables. PyEZ comes with lots of pre-built tables check references links for latest tables
2) How to quickly create your own tables & views on the fly in the Python script
3) how to build external table and use it in Python script

Below construction of table & view

table name - user defined table name
rpc - rpc command, for example get-interface-information. you can get rpc command 
for a cli like this 'show interfaces | display xml rpc' 
args - default optional argument for the rpc command
args_key - optional key for the table. if this key is not set you need to specify
key name for get() like this portStatsTable(dev).get(interface_name'ge-0/0/1'). If 
key is set you can run get() like this portStatsTable(dev).get('ge-0/0/1')
item - This is the top level xpath expression in <rpc-reply>. PyEZ searches for this 
string when parsing command response
view - view table name

fields - key value pair of dictionary. Here you specify response fields you are 
interested in, you also need to create key for that field

 

Using pre-built tables

Let’s see how to use pre-built PyEZ table. I am interested in interface error stats so I will try PhyPortErrorTable. This table contains input,output received counts and input,output error counts.
Below is the snippet of yaml file for the table

PhyPortErrorTable:
  rpc: get-interface-information  
  args:                           
    extensive: True 
    interface_name: '[fgx]e*' 
  args_key: interface_name        
  item: physical-interface        
  view: PhyPortErrorView          

PhyPortErrorView:
  groups: 
    ts: traffic-statistics 
    rxerrs: input-error-list
    txerrs: output-error-list
 fields_ts:                        
    rx_bytes: { input-bytes: int }
    rx_packets: { input-packets: int }
 <truncated ....>
 fields_rxerrs:
    rx_err_input: { input-errors: int }
    rx_err_drops: { input-drops: int }
 <truncated .....>
fields_txerrs:
    tx_err_carrier-transitions: { carrier-transitions: int }
    tx_err_output: { output-errors: int }
<truncated ....>

Step-1: Start Python and import required packages

sjakhwal@rtxl3rld05:~$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from jnpr.junos import Device
>>> from jnpr.junos.utils.config import Config
>>> from lxml import etree
>>> dev = Device(host='192.168.122.35',user='root',passwd='juniper1').open()
>>> dev.facts['model']
'FIREFLY-PERIMETER'
>>>

Step-2: Import PyEZ table and get command response. PyEZ keeps command response in a Python dictionary format, it makes name field as key automatically. You can see that interface_names  are keys in the dictionary

>>>from jnpr.junos.op.phyport import PhyPortErrorTable
>>> from pprint import pprint as pp
>>> phyErrors = PhyPortErrorTable(dev).get()
>>> phyErrors.keys()
[‘ge-0/0/0’, ‘ge-0/0/1’, ‘ge-0/0/2’]
>>>
>>> pp (phyErrors[‘ge-0/0/1’].items())
[(‘tx_err_resource’, 0),
(‘rx_err_drops’, 0),
(‘rx_packets’, 562159),
(‘rx_err_l2-channel’, 0),
(‘tx_err_collisions’, 0),
(‘tx_err_drops’, 0),
(‘tx_err_fifo’, 0),
(‘rx_err_frame’, 0),
(‘rx_err_runts’, 0),
(‘tx_bytes’, 84),
(‘tx_err_output’, 0),
(‘tx_err_aged’, 0),
(‘tx_err_hs-crc’, 0),
(‘rx_err_l2-mismatch’, 0),
(‘tx_err_carrier-transitions’, 1),
(‘rx_bytes’, 29229986),
(‘rx_err_fifo’, 0),
(‘rx_err_resource’, 0),
(‘rx_err_l3-incompletes’, 0),
(‘tx_err_mtu’, 0),
(‘rx_err_discards’, 0),
(‘rx_err_input’, 0),
(‘tx_packets’, 2)]
>>>
>>> pp (phyErrors[‘ge-0/0/1’].rx_bytes)
29229986
>>>
>>> for key in phyErrors.keys():
…   for phyError in phyErrors[key].items():
…     pp (phyError)

(‘tx_err_resource’, 0)
(‘rx_err_drops’, 0)
(‘rx_packets’, 783248)
(‘rx_err_l2-channel’, 0)
(‘tx_err_collisions’, 0)
(‘tx_err_drops’, 0)
(‘tx_err_fifo’, 0)
(‘rx_err_frame’, 0)
(‘rx_err_runts’, 0)
….
<truncated>

creating table and views on the fly

Now lets create our own table and views in the Python script. I am using same table (phyPortErrorStatsTable) but updated it to parse only ‘traffic-statistics’ & ‘input-error-list’.  I have also reduced the number of error counts.
Note:Be extra cautious about yaml format, make sure all fields are aligned properly otherwise loading will fail.

Step-1: You need to load two new packages for this. Import packages and create YAML string

>>>from jnpr.junos.factory.factory_loader import FactoryLoader
>>>import yaml
>>> PortErrorStatsTableString = """
... ---
... customPortErrorStatsTable:
...   rpc: get-interface-information
...   args:
...     extensive: True
...     interface_name: '[fgx]e*'
...   args_key: interface_name
...   item: physical-interface
...   view: customPhyPortErrorView
...
... customPhyPortErrorView:
...   groups:
...     ts: traffic-statistics
...     rxerrs: input-error-list
...   fields_ts:
...     rx_packets: { input-packets: int }
...     tx_packets: { output-packets: int }
...   fields_rxerrs:
...     rx_err_drops: { input-drops: int }
...     rx_err_discards: { input-discards: int }
... """

Step-2: Load YAML string and get command response. Remaining steps are same as using pre-built tables

>> globals().update(FactoryLoader().load(yaml.load(PortErrorStatsTableString)))
>>>
>>> customErrorsTable = customPortErrorStatsTable(dev).get()
>>> print customErrorsTable
customPortErrorStatsTable:192.168.122.35: 3 items
>>> print customErrorsTable.keys()
['ge-0/0/0', 'ge-0/0/1', 'ge-0/0/2']
>>> pp (customErrorsTable.items())
[('ge-0/0/0',
  [('rx_err_drops', 0),
   ('rx_packets', 792394),
   ('tx_packets', 19432),
   ('rx_err_discards', 0)]),
 ('ge-0/0/1',
  [('rx_err_drops', 0),
   ('rx_packets', 571240),
   ('tx_packets', 2),
   ('rx_err_discards', 0)]),
 ('ge-0/0/2',
  [('rx_err_drops', 0),
   ('rx_packets', 0),
   ('tx_packets', 0),
   ('rx_err_discards', 0)])]
>>>
>>> print customErrorsTable['ge-0/0/2'].tx_packets
0
>>>

Using table and views from external file

okay so the last part is to create external table file.
create a sub-directory myPyezTable and create .yml and .py file using below steps

Step-1: create portStats.py and put these 4 statements in the file

from jnpr.junos.factory import loadyaml
from os.path import splitext
_YAML_ = splitext(__file__)[0] + '.yml
globals().update(loadyaml(_YAML_))

Step-2: create portStats.yml file and add your  yaml table in it. Note both yml and py file names are same

---
customPortErrorStatsTable:
  rpc: get-interface-information
  args:
    extensive: True
    interface_name: '[fgx]e*'
  args_key: interface_name
  item: physical-interface
  view: customPhyPortErrorView
customPhyPortErrorView:
  groups:
    ts: traffic-statistics
    rxerrs: input-error-list
  fields_ts:
    rx_packets: { input-packets: int }
    tx_packets: { output-packets: int }
  fields_rxerrs:
    rx_err_drops: { input-drops: int }
    rx_err_discards: { input-discards: int }

Step-3: add _init.py file in the sub-directory. This is an empty file

Step-4: import your table. Remaining steps are same as using pre-build PyEZ tables

>>> from myPyezTable.portStats import customPortErrorStatsTable
>>> portErrors = customPortErrorStatsTable(dev).get()
>>> pp (portErrors.items())
[('ge-0/0/0',
  [('rx_err_drops', 0),
   ('rx_packets', 793354),
   ('tx_packets', 19525),
   ('rx_err_discards', 0)]),
 ('ge-0/0/1',
  [('rx_err_drops', 0),
   ('rx_packets', 572104),
   ('tx_packets', 2),
   ('rx_err_discards', 0)]),
 ('ge-0/0/2',
  [('rx_err_drops', 0),
   ('rx_packets', 0),
   ('tx_packets', 0),
   ('rx_err_discards', 0)])]
>>> pp (portErrors.keys())
['ge-0/0/0', 'ge-0/0/1', 'ge-0/0/2']
>>>

View of my sub-directory & external files

sjakhwal@rtxl3rld05:~/myPyezTable$ pwd
/home/sjakhwal/myPyezTable
sjakhwal@rtxl3rld05:~/myPyezTable$ ls
__init__.py  __init__.pyc  portStats.py  portStats.pyc  portStats.yml
sjakhwal@rtxl3rld05:~/myPyezTable$ cat __init__.py
sjakhwal@rtxl3rld05:~/myPyezTable$ cat portStats.py
from jnpr.junos.factory import loadyaml
from os.path import splitext
_YAML_ = splitext(__file__)[0] + '.yml'
globals().update(loadyaml(_YAML_))
sjakhwal@rtxl3rld05:~/myPyezTable$ cat portStats.yml
---
customPortErrorStatsTable:
  rpc: get-interface-information
  args:
    extensive: True
    interface_name: '[fgx]e*'
  args_key: interface_name
  item: physical-interface
  view: customPhyPortErrorView

customPhyPortErrorView:
  groups:
    ts: traffic-statistics
    rxerrs: input-error-list
  fields_ts:
    rx_packets: { input-packets: int }
    tx_packets: { output-packets: int }
  fields_rxerrs:
    rx_err_drops: { input-drops: int }
    rx_err_discards: { input-discards: int }

 

References:

Junos PyEZ Developer Guide

 

Lab-10:Python for network automation -using Jinja2 template and YAML file

Jinja2 is a powerful templating language for Python. If you are in network automation business you are going to love it I guarantee it. Using Jinja2 you can build a template which uses variables instead of hard coded values, Jinja2 then automagically renders template using variable values. Variable values either comes from Python dictionary or yaml file. Jinja2 also allows you to add control statements like ‘for’ loop & ‘If’ statement to create logic in template file.

Some common Jinja2 statements:

Variable substitution:

  • Jinja2 variable substituting is done by enclosing it in ‘{{‘
    • example: {{ interface }}

For loop:

  • for statement begins with {% for statement %} and end with {% end %}
    • example: {% for interface in interfaces %}  … {% endfor %}

 If statement:

  • If statement begins with {% if statement %} and end with {% endif %}
    • example: {% if interface == ‘ge-0/0/2’  %}  …  {% endif %}

Comment:

  • comment starts with ‘{#’ and ends with ‘#}
    • example: {# set description only for interface ge-0/0/2 #}

 

 Pre-condition:

  • Install jinja2
    • pip install jinja2

Procedure:

  • Step-1: Create a Jinja2 template file, interfaces.j2. In this template interface name and description tags are created as variable and a for loop added to iterate thru multiple interfaces. I have created xml format file but it can be any format
<interfaces>
 {% for interface in interfaces %}
      <interface>{{interface}} 
          <description> {{description}} </description>
      </interface>
 {% endfor %}
</interfaces>
  • Step-2: Import packages, set environment and load jinja2 template by using get_template API. Here I am using ‘interfaces.j2’ template created in step-1

sjakhwal@rtxl3rld05:~$ python
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import jinja2
>>> import yaml
>>> import os
>>> from lxml import etree
>>> templateFilePath = jinja2.FileSystemLoader(os.getcwd())
>>> jinjaEnv = jinja2.Environment(loader=templateFilePath,trim_blocks=True,lstrip_blocks=True)
>>> jTemplate = jinjaEnv.get_template(‘interfaces.j2’)

  • Step-3: Build variable dictionary in python. Here I am creating a dictionary for interfaces and description

config = {
‘interfaces’: [‘ge-0/0/1′,’ge-0/0/2′,’ge-0/0/3’],
‘description’: ‘interface configured using jinja2 template’
}

>>> print config
{‘interfaces’: [‘ge-0/0/1’, ‘ge-0/0/2’, ‘ge-0/0/3’], ‘description’: ‘interface configured using jinja2 template’}

  • Step-4: Last step is to render jinja2 template using dictionary . you can store new configuration to a file or push it to your device by writing simple Python code.

> print jTemplate.render(config)
<interfaces>
<nterface>ge-0/0/1
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/2
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/3
<description> interface configured using jinja2 template </description>
</interface>
</interfaces>

  • This is another way of achieving same result. Here instead of building dictionary of variables you pass them while rendering template. Note: This could be an issue when you have too many variables to pass

>> print jTemplate.render(interfaces = {‘ge-0/0/1′,’ge-0/0/2′},description=’interface configured using jinja2 template’)
<interfaces>
<nterface>ge-0/0/2
<description> interface configured using jinja2 template </description>
</interface>
<nterface>ge-0/0/1
<description> interface configured using jinja2 template </description>
</interface>
</interfaces>

  • Using yaml file for variable values is a clean way of separating variables from Python code. User can change variable values without understanding of Python code.
  • Create yaml file, filename:interface.yaml. Yaml file always begins with ‘—‘ and ‘-‘ represents list element
  • Here I am creating values for two variables (interfaces and description) for jinja2 template ‘interfaces.j2’
---
interfaces:
- ge-0/0/1
- ge-0/0/2
- ge-0/0/3
description:
 "configured by using jinja2 & yaml"
  • Read yaml file in your Python

>>>templateVars = yaml.load(open(‘interfaces.yaml’).read())

  • Render jinja2 template using yaml file.

>>> templateVars = yaml.load(open(‘interfaces.yaml’).read())
>>> print jTemplate.render(templateVars)
<interfaces>
 <nterface>ge-0/0/1
 <description> configured by using jinja2 & yaml </description>
 </interface>
 <nterface>ge-0/0/2
 <description> configured by using jinja2 & yaml </description>
 </interface>
 <nterface>ge-0/0/3
 <description> configured by using jinja2 & yaml </description>
 </interface>
</interfaces>
>>>

  • Now lets try If control statement. Update ‘interfaces.j2’ file to add ‘if’ statement and comment statement. I added logic  to set description  only for interface ge-0/0/2

<interfaces>
 {% for interface in interfaces %}
{# set description only for interface ge-0/0/2 #}
 {% if interface == ‘ge-0/0/2’ %}
 <nterface>{{interface}}
 <description> {{description}} </description>
 </interface>
 {% endif %}
 {% endfor %}
</interfaces>

  • Render template and you see only ge-0/0/2 printed

>>> jTemplate = jinjaEnv.get_template(‘interfaces.j2’)
>>> print jTemplate.render(templateVars)
<interfaces>
<nterface>ge-0/0/2
<description> configured by using jinja2 & yaml </description>
</interface>
</interfaces>
>>>

  • There is a shortcut if you need to create template on the fly inside Python code. Here I am building same template by using jinja2 Template API

>>> from jinja2 import Template

>>> template = Template(‘<interfaces>{% for interface in interfaces %} <interface> \
… {{interface}} <description>{{description}}</description> </interface> {% endfor %} </interfaces>’)
>>> print template.render(templateVars)
<interfaces> <interface> ge-0/0/1 <description>configured by using jinja2 & yaml</description> </interface> <interface> ge-0/0/2 <description>configured by using jinja2 & yaml</description> </interface> <interface> ge-0/0/3 <description>configured by using jinja2 & yaml</description> </interface> </interfaces>
>>>

 

References:

  1. http://jinja.pocoo.org/docs/dev/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Lab-8:Python for network automation – XML parsing

continuing from Lab-7 below is an example of XML parsing using etree.

Precondition:

  • Juniper srx with NETCONF enabled
    • set system services netconf ssh
  • NETCONF ncclient installed
    • sudo pip install ncclient

Procedure:

  • This script follows these steps
    • connect to Juniper srx using NETCONF
    • get interface counts for interface ge-0/0/0 using this command ‘show interface ge-0/0/0 detail statistics’
    • search for stats group ‘traffic-statistics’, ‘input-error-list’, ‘output-error-list’ and print individual stats under the group also store them in Python dictionary
  •  Python file (get_interface_stats.py) located here

 

Output:

sjakhwal@rtxl3rld05:~/scripts$ ./get_interface_statistics.py
================= Traffic Statistics ================
input-bytes =
32690246
input-bps =
200
output-bytes =
4419133
output-bps =
0
input-packets =
615902
input-pps =
0
output-packets =
16620
output-pps =
0
================ Input Error Statistics =============
input-errors =
0
input-drops =
0
framing-errors =
0
input-runts =
0
input-discards =
0
input-l3-incompletes =
0
input-l2-channel-errors =
0
input-l2-mismatch-timeouts =
0
input-fifo-errors =
0
input-resource-errors =
0
================ Output Error Statistics ===========
carrier-transitions =
1
output-errors =
0
output-collisions =
0
output-drops =
0
aged-packets =
0
mtu-errors =
0
hs-link-crc-errors =
0
output-fifo-errors =
0
output-resource-errors =
0
sjakhwal@rtxl3rld05:~/scripts

Lab-7:Python for network automation – XML parsing

Knowledge to XML parsing is important for network automation, these days  many modern interfaces generate XML formatted output. I have written a small Python script which parses XML using Minidom and creates Python dictionary. You can download script (parse_route_table.py) from here.

Pre-condition:

  • Juniper srx with NETCONF enabled
    • set system services netconf ssh
  • NETCONF ncclient installed
    • sudo pip install ncclient

Procedure:

Script follows these steps

  • Connect to srx using NETCONF
  • Generate ‘show route | display xml’ in xml format
  • Parse xml and generate a Python dictionary for destination and outgoing interface.

Output

sjakhwal@rtxl3rld05:~/scripts$ python parse_routing_table.py
=== Route table name =inet.0, Number of active routes =9 ====
destination-ip=0.0.0.0/0,via=ge-0/0/0.0
destination-ip=11.11.11.0/24,via=ge-0/0/1.0
destination-ip=11.11.11.1/32,via=ge-0/0/1.0
destination-ip=192.168.47.5/32,via=lo0.0
destination-ip=192.168.47.6/32,via=lo0.0
destination-ip=192.168.47.7/32,via=lo0.0
destination-ip=192.168.47.8/32,via=lo0.0
destination-ip=192.168.122.0/24,via=ge-0/0/0.0
destination-ip=192.168.122.35/32,via=ge-0/0/0.0

 

Note: Below sample XML
root> show route | display xml
<rpc-reply xmlns:junos=”http://xml.juniper.net/junos/12.1X47/junos”&gt;
<route-information xmlns=”http://xml.juniper.net/junos/12.1X47/junos-routing”&gt;
<!– keepalive –>
<route-table>
<table-name>inet.0</table-name>
<destination-count>9</destination-count>
<total-route-count>9</total-route-count>
<active-route-count>9</active-route-count>
<holddown-route-count>0</holddown-route-count>
<hidden-route-count>0</hidden-route-count>
<rt junos:style=”brief”>
<rt-destination>0.0.0.0/0</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Access-internal</protocol-name>
<preference>12</preference>
<age junos:seconds=”1096554″>1w5d 16:35:54</age>
<nh>
<selected-next-hop/>
<to>192.168.122.1</to>
<via>ge-0/0/0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>11.11.11.0/24</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”686008″>1w0d 22:33:28</age>
<nh>
<selected-next-hop/>
<via>ge-0/0/1.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>11.11.11.1/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Local</protocol-name>
<preference>0</preference>
<age junos:seconds=”686008″>1w0d 22:33:28</age>
<nh-type>Local</nh-type>
<nh>
<nh-local-interface>ge-0/0/1.0</nh-local-interface>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.47.5/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096716″>1w5d 16:38:36</age>
<nh>
<selected-next-hop/>
<via>lo0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.47.6/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096716″>1w5d 16:38:36</age>
<nh>
<selected-next-hop/>
<via>lo0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.47.7/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096716″>1w5d 16:38:36</age>
<nh>
<selected-next-hop/>
<via>lo0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.47.8/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096716″>1w5d 16:38:36</age>
<nh>
<selected-next-hop/>
<via>lo0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.122.0/24</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Direct</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096554″>1w5d 16:35:54</age>
<nh>
<selected-next-hop/>
<via>ge-0/0/0.0</via>
</nh>
</rt-entry>
</rt>
<rt junos:style=”brief”>
<rt-destination>192.168.122.35/32</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Local</protocol-name>
<preference>0</preference>
<age junos:seconds=”1096554″>1w5d 16:35:54</age>
<nh-type>Local</nh-type>
<nh>
<nh-local-interface>ge-0/0/0.0</nh-local-interface>
</nh>
</rt-entry>
</rt>
</route-table>
</route-information>
<cli>
<banner></banner>
</cli>
</rpc-reply>

Lab-5:Python for network automation – NETCONF client

In this lab I will demonstrate how to create NETCONF connection using ncclient package and Juniper PyEZ NETCONF clients

Component required:

NETCONF capable device. I am using Juniper virtual SRX

Pre-condition:

  • Install ncclient package
    • pip install ncclient
  • Install Juniper PyEZ package
    • pip install junos-eznc
  • Enable NETCONF on device. In my case it is srx
    • configure>edit system services netconf ssh

Procedure:

NETCONF connection using Juniper PyEZ NETCONF client

root@rtxl:/home/sjakhwal/scripts# python

Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>> import sys
>>> from jnpr.junos import Device
>>> import json
>>>from jnpr.junos import Device
>>>from lxml import etree
>>>from jnpr.junos.utils.config import Config

>>> dev = Device(host=’192.168.122.35′,user=’root’,passwd=’juniper1′)
>>> dev.open()

Device(192.168.122.35)

>>> print dev.facts

{'domain': None, 'hostname': '', 'ifd_style': 'CLASSIC', 'version_info': junos.version_info(major=(12, 1), type=X, minor=(47, 'D', 20), build=7), '2RE': False, 'serialnumber': 'f9bf7cca7bfa', 'fqdn': '', 'virtual'

>>> dir(dev)

['ON_JUNOS', 'Template', '__class__', '__delattr__', '__dict__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_auth_password', '_auth_user', '_auto_probe', '_conf_auth_user', '_conf_ssh_private_key_file', '_conn', '_facts', '_gather_facts', '_hostname', '_j2ldr', '_manages', '_nc_transform', '_norm_transform', '_normalize', '_port', '_ssh_config', '_ssh_private_key_file', '_sshconf_lkup', '_sshconf_path', 'auto_probe', 'bind', 'cli', 'close', 'connected', 'display_xml_rpc', 'execute', 'facts', 'facts_refresh', 'hostname', 'logfile', 'manages', 'open', 'password', 'probe', 'rpc', 'timeout', 'transform', 'user']

>> print dev.hostname

192.168.122.35

>> print json.dumps(dev.facts)

{"domain": null, "hostname": "", "ifd_style": "CLASSIC", "version_info": {"major": [12, 1], "type": "X", "build": 7, "minor": [47, "D", 20]}, "2RE": false, "serialnumber": "f9bf7cca7bfa", "fqdn": "", "virtual": true, "switch_style": "NONE", "version

>>>print dev.cli(‘show route’)

/usr/local/lib/python2.7/dist-packages/jnpr/junos/device.py:652: RuntimeWarning: CLI command is for debug use only! warnings.warn("CLI command is for debug use only!", RuntimeWarning) inet.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both
0.0.0.0/0         *[Access-internal/12] 6d 23:51:27
> to 192.168.122.1 via ge-0/0/0.0 11.11.11.0/24     
*[Direct/0] 2d 05:49:01 > via ge-0/0/1.0

>>> print dev.display_xml_rpc(‘show route’,format=’text’)

<get-route-information> </get-route-information>

>>>print etree.tostring(dev.rpc.get_route_information({‘format’:’xml’}))

 <route-information>
 <!-- keepalive -->
 <route-table>
 <table-name>inet.0</table-name>
 <destination-count>9</destination-count>
 <total-route-count>9</total-route-count>
 <active-route-count>9</active-route-count>
 <holddown-route-count>0</holddown-route-count>
 <hidden-route-count>0</hidden-route-count>
 <rt style="brief">
 <rt-destination>0.0.0.0/0</rt-destination>
 <rt-entry>
 <active-tag>*</active-tag>
 <current-active/>
 <last-active/>
 <protocol-name>Access-internal</protocol-name>
 <preference>12</preference>
 <age seconds="660641">1w0d 15:30:41</age>
 <nh>
 <selected-next-hop/>
 <to>192.168.122.1</to>
 <via>ge-0/0/0.0</via>
 </nh>
 </rt-entry>
 </rt>
 <rt style="brief">
…..<truncated>…

>>> print dev.display_xml_rpc(‘show interfaces’)

<Element get-interface-information at 0x7fa541ade7a0>

>>> print etree.tostring(dev.rpc.get_interface_information(terse=True))

<interface-information style="terse"> <physical-interface> 
<name> ge-0/0/0 </name> <admin-status> up </admin-status> 
<oper-status>up </oper-status> <logical-interface> 
<name> ge-0/0/0.0
….. <truncated>….

>>>print etree.tostring(dev.rpc.get_interface_information   (interface_name=’ge-0/0/0′))

<interface-information style="normal"> <physical-interface> <name> ge-0/0/0 
</name> <admin-status format="Enabled"> up </admin-status> <oper-status> up 
</oper-status> 
<local-index> 134 </local-index> 
<snmp-index> 507 </snmp-index> 
<link-level-type>


 NETCONF connection using ncclient NETCONF client

Here is another way to create NETCONF connection to your device if it doesn’t provide Python library like Juniper does

sjakhwal@rtxl3rld05:~$ python

Python 2.7.6 (default, Mar 22 2014, 22:59:56)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from ncclient import manager

>>> dev = manager.connect(host=’192.168.122.35′,username=’root’,password=’juniper1′,hostket_verify=False,timeout=10)

>> dir(dev)

['_Manager__set_async_mode', '_Manager__set_raise_mode', 
'_Manager__set_timeout', '__class__', '__delattr__', '__dict__', '__doc__', 
'__enter__', '__exit__', '__format__', '__getattr__', '__getattribute__', 
'__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', '__weakref__', '_async_mode', '_device_handler', 
'_raise_mode', '_session', '_timeout', 'async_mode', 'channel_id', 'channel_name', 
'client_capabilities', 'close_session', 'command', 'commit', 
'compare_configuration', 'connected', 'copy_config', 'delete_config', 
'discard_changes', 'dispatch', 'edit_config', 'execute', 'get', 'get_config', 
'get_configuration', 'get_schema', 'halt', 'kill_session', 'load_configuration', 'lock',
'locked', 'poweroff_machine', 'raise_mode', 'reboot', 'reboot_machine', 'rpc', 'scp',
'server_capabilities', 'session', 'session_id', 'timeout', 'unlock', 'validate']

>>> dev.command(command=’show route’,format=’xml’)

<ncclient.xml_.NCElement object at 0x7fdff06d6c90>

>>> print dev.command(command=’show route’,format=’xml’)

<rpc-reply message-id="urn:uuid:896eb964-e7a2-11e5-a0a3-6431501eb7ad">
<route-information>
<!-- keepalive -->
<route-table>
<table-name>inet.0</table-name>
<destination-count>9</destination-count>
<total-route-count>9</total-route-count>
<active-route-count>9</active-route-count>
<holddown-route-count>0</holddown-route-count>
<hidden-route-count>0</hidden-route-count>
<rt style="brief">
<rt-destination>0.0.0.0/0</rt-destination>
<rt-entry>
<active-tag>*</active-tag>
<current-active/>
<last-active/>
<protocol-name>Access-internal</protocol-name>
<preference>12</preference>
<age seconds="748256">1w1d 15:50:56</age>
<nh>
<selected-next-hop/>
<to>192.168.122.1</to>
<via>ge-0/0/0.0</via>
</nh>
</rt-entry>
</rt>

>>> print dev.get_configuration(‘running’)

<rpc-reply message-id="urn:uuid:ddd4e5b4-e7a2-11e5-a0a3-6431501eb7ad">
<configuration changed-seconds="1457375295" changed-localtime="2016-03-07 18:28:15 UTC">
<version>12.1X47-D20.7</version>
<system>
<root-authentication>
<encrypted-password>$1$KhbpR2sm$x9rV5uZSS/Q1a4YRculZ//</encrypted-password>
 </root-authentication>
<login>
<user>
 <name>admin</name>
<uid>2000</uid>
 <class>super-user</class>
<authentication>
<encrypted-password>$1$5QcrZTKt$fqisDA5ZAXQiBOeTN4fH6.</encrypted-password>
</authentication>
</user>
</login>
<services>
<ssh>
</ssh>