Using Junos PyEZ to Execute RPCs on Devices Running Junos OS
You can use Junos PyEZ to execute remote procedure
calls (RPCs) on demand on devices running Junos OS. After creating
an instance of the Device
class, you can
execute RPCs as a property of the Device
instance. You can perform most of the same operational commands
using Junos PyEZ that you can execute in the CLI.
The Junos XML API is an XML representation of Junos OS configuration statements and operational mode commands. It defines an XML equivalent for all statements in the Junos OS configuration hierarchy and many of the commands that you issue in CLI operational mode. Each operational mode command with a Junos XML counterpart maps to a request tag element and, if necessary, a response tag element. Request tags are used in RPCs within NETCONF or Junos XML protocol sessions to request information from a device running Junos OS. The server returns the response using Junos XML elements enclosed within the response tag element.
When you use Junos PyEZ to execute RPCs, you map the request tag name to a method name. This topic outlines how to map CLI commands to Junos PyEZ RPCs, how to execute RPCs using Junos PyEZ, and how to customize the data returned in the RPC reply.
Mapping Junos OS Commands to Junos PyEZ RPCs
All operational commands that have Junos XML counterparts are listed in the Junos XML API Explorer. You can also display the Junos XML request tag element for any operational mode command that has a Junos XML counterpart either on the CLI or using Junos PyEZ. Once you obtain the request tag, you can map it to the Junos PyEZ RPC method name.
To display the Junos XML request tag for a command in the CLI, include the | display xml rpc option after the command. The following example displays the request tag for the show route command:
user@router> show route | display xml rpc
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/15.1R1/junos"> <rpc> <get-route-information> </get-route-information> </rpc> </rpc-reply>
You can also display the Junos XML request tag for a
command using Junos PyEZ. To display the request tag, call the Device
instance display_xml_rpc()
method, and include the command string and format='text'
as arguments. For example:
from jnpr.junos import Device
with Device(host='router.example.com') as dev:
print (dev.display_xml_rpc('show route', format='text'))
Executing the program returns the request tag for the show route command.
<get-route-information> </get-route-information>
You can map the request tags for an operational command to a
Junos PyEZ RPC method name. To derive the RPC method name, replace
any hyphens in the request tag with underscores (_) and remove the
enclosing angle brackets. For example, the <get-route-information>
request tag maps to the get_route_information()
method name.
Executing RPCs as a Property of the Device Instance
Each instance of Device
has
an rpc
property that enables you to execute
any RPC available through the Junos XML API. In a Junos PyEZ application,
after establishing a connection with the device, you can execute the
RPC by appending the rpc
property and RPC
method name to the device instance as shown in the following example:
from jnpr.junos import Device
from lxml import etree
with Device(host='dc1a.example.com') as dev:
#invoke the RPC equivalent to "show version"
sw = dev.rpc.get_software_information()
print(etree.tostring(sw, encoding='unicode'))
The return value is an XML object starting at the first
element under the <rpc-reply>
tag. In
this case, the get_software_information()
RPC returns the <software-information>
element.
<software-information> <host-name>dc1a</host-name> ... </software-information>
Junos OS commands can have fixed-form options that do not have a value. For example, the Junos XML equivalent for the show interfaces terse command indicates that terse is an empty element.
user@router> show interfaces terse | display xml rpc <rpc-reply xmlns:junos="http://xml.juniper.net/junos/14.1R1/junos"> <rpc> <get-interface-information> <terse/> </get-interface-information> </rpc> </rpc-reply>
To execute an RPC and include a command option that does
not take a value, add the option to the RPC method’s argument
list, change any dashes in the option name to underscores, and set
it equal to True. The following code executes the Junos PyEZ RPC equivalent
of the show interfaces terse
command:
rsp = dev.rpc.get_interface_information(terse=True)
Junos OS commands can also have options that require a value. For example, in the following output, the interface-name element requires a value, which is the name of the interface for which you want to return information:
user@router> show interfaces ge-0/0/0 | display xml rpc <rpc-reply xmlns:junos="http://xml.juniper.net/junos/14.1R1/junos"> <rpc> <get-interface-information> <interface-name>ge-0/0/0</interface-name> </get-interface-information> </rpc> </rpc-reply>
To execute an RPC and include a command option that requires
a value, add the option to the RPC method’s argument list, change
any dashes in the option name to underscores, and then set it equal
to the appropriate value. The following example executes the Junos
PyEZ RPC equivalent of the show interfaces ge-0/0/0
command:
rsp = dev.rpc.get_interface_information(interface_name='ge-0/0/0')
Specifying the Format of the RPC Output
By default, the RPC return value is an XML object starting at
the first element under the <rpc-reply>
tag. Starting in Junos PyEZ Release 1.2.3, you can also return the
RPC output in text or JavaScript Object Notation (JSON) format by
including either the {'format':'text'}
or {'format':'json'}
dictionary as the RPC method’s
first argument.
RPC output in JSON format is only supported on devices running Junos OS Release 14.2 and later releases.
The following example returns the output of the get_software_information()
RPC in text format, which
is identical to the output emitted for the show version command in the CLI, except that the RPC output is enclosed within
an <output>
element.
from jnpr.junos import Device
from lxml import etree
with Device(host='router1.example.com') as dev:
sw_info_text = dev.rpc.get_software_information({'format':'text'})
print(etree.tostring(sw_info_text))
user@server:~$ python junos-pyez-rpc-text-format.py
<output> Hostname: router1 Model: mx104 Junos: 18.3R1.9 JUNOS Base OS boot [18.3R1.9] JUNOS Base OS Software Suite [18.3R1.9] JUNOS Crypto Software Suite [18.3R1.9] JUNOS Packet Forwarding Engine Support (TRIO) [18.3R1.9] JUNOS Web Management [18.3R1.9] JUNOS Online Documentation [18.3R1.9] JUNOS SDN Software Suite [18.3R1.9] JUNOS Services Application Level Gateways [18.3R1.9] JUNOS Services COS [18.3R1.9] JUNOS Services Jflow Container package [18.3R1.9] JUNOS Services Stateful Firewall [18.3R1.9] JUNOS Services NAT [18.3R1.9] JUNOS Services RPM [18.3R1.9] JUNOS Services Captive Portal and Content Delivery Container package [18.3R1.9] JUNOS Macsec Software Suite [18.3R1.9] JUNOS Services Crypto [18.3R1.9] JUNOS Services IPSec [18.3R1.9] JUNOS DP Crypto Software Software Suite [18.3R1.9] JUNOS py-base-powerpc [18.3R1.9] JUNOS py-extensions-powerpc [18.3R1.9] JUNOS jsd [powerpc-18.3R1.9-jet-1] JUNOS Kernel Software Suite [18.3R1.9] JUNOS Routing Software Suite [18.3R1.9] <output>
The following example returns the output of the get_software_information()
RPC in JSON format.
from jnpr.junos import Device
from pprint import pprint
with Device(host='router1.example.com') as dev:
sw_info_json = dev.rpc.get_software_information({'format':'json'})
pprint(sw_info_json)
user@server:~$ python junos-pyez-rpc-json-format.py
{u'software-information': [{u'host-name': [{u'data': u'router1'}], u'junos-version': [{u'data': u'18.3R1.9'}], u'package-information': [{u'comment': [{u'data': u'JUNOS Base OS boot [18.3R1.9]'}], u'name': [{u'data': u'junos'}]}, {u'comment': [{u'data': u'JUNOS Base OS Software Suite [18.3R1.9]'}], u'name': [{u'data': u'jbase'}]}, ...
Specifying the Scope of Data to Return
You can use Junos PyEZ to execute an RPC to retrieve operational information from devices running Junos OS. Starting in Junos PyEZ Release 2.3.0, when you request XML output, you can filter the reply to return only specific elements. Filtering the output is beneficial when you have extensive operational output, but you only need to work with a subset of the data.
To filter the RPC reply to return only specific tags, include
the RPC method’s filter_xml
argument.
The filter_xml
parameter takes a string
containing the subtree filter that selects the elements to return.
The subtree filter returns the data that matches the selection criteria.
The following Junos PyEZ example executes the <get-interface-information>
RPC and filters the
output to retrieve just the <name>
element
for each <physical-interface>
element
in the reply:
from jnpr.junos import Device
from lxml import etree
with Device(host='router.example.com', use_filter=True) as dev:
filter = '<interface-information><physical-interface><name/></physical-interface></interface-information>'
result = dev.rpc.get_interface_information(filter_xml=filter)
print (etree.tostring(reply, encoding='unicode'))
When you execute the script, it displays each physical interface’s name element.
user@server:~$ python junos-pyez-get-interface-names.py
<interface-information style="normal"><physical-interface><name> lc-0/0/0 </name></physical-interface><physical-interface><name> pfe-0/0/0 </name></physical-interface><physical-interface><name> pfh-0/0/0 </name></physical-interface><physical-interface><name> xe-0/0/0 </name></physical-interface><physical-interface><name> xe-0/1/0 </name></physical-interface><physical-interface><name> ge-1/0/0 </name></physical-interface> ... </interface-information>
Specifying the RPC Timeout
RPC execution time can vary considerably depending on the RPC
and the device. By default, NETCONF RPCs time out after 30 seconds.
Starting in Junos PyEZ Release 1.2, you can extend the timeout value
by including the dev_timeout=seconds
argument when you execute the RPC to ensure that the
RPC does not time out during execution. dev_timeout
adjusts the device timeout only for that single RPC operation.
dev.rpc.get_route_information(table='inet.0', dev_timeout=55)
Normalizing the XML RPC Reply
When you execute an RPC, the RPC reply can include data that is wrapped in newlines or contains other superfluous whitespace. Unnecessary whitespace can make it difficult to parse the XML and find information using text-based searches. Starting in Junos PyEZ Release 1.2, you can normalize an RPC reply, which strips out all leading and trailing whitespace and replaces sequences of internal whitespace characters with a single space.
Table 1 compares a default RPC reply to the normalized version. The default RPC reply includes many newlines that are not present in the normalized reply.
Table 1: Comparison of a Default and Normalized RPC Reply
Default RPC Reply | Normalized RPC Reply |
---|---|
<interface-information style="terse"> <logical-interface> <name>\nge-0/0/0.0\n</name> <admin-status>\nup\n</admin-status> <oper-status>\nup\n</oper-status> <filter-information>\n</filter-information> <address-family> <address-family-name>\ninet\n</address-family-name> <interface-address> <ifa-local emit="emit">\n198.51.100.1/24\n</ifa-local> </interface-address> </address-family> </logical-interface> </interface-information> | <interface-information style="terse"> <logical-interface> <name>ge-0/0/0.0</name> <admin-status>up</admin-status> <oper-status>up</oper-status> <filter-information/> <address-family> <address-family-name>inet</address-family-name> <interface-address> <ifa-local emit="emit">198.51.100.1/24</ifa-local> </interface-address> </address-family> </logical-interface> </interface-information> |
You can enable normalization for the duration of a session
with a device, or you can normalize an individual RPC reply when you
execute the RPC. To enable normalization for the entire device session,
include normalize=True
in the argument
list either when you create the device instance or when you connect
to the device using the open()
method.
dev = Device(host='router1.example.com', user='root', normalize=True)
# or
dev.open(normalize=True)
To normalize an individual RPC reply, include normalize=True
in the argument list for that RPC method.
dev.rpc.rpc_method(normalize=True)
For example:
rsp = dev.rpc.get_interface_information(interface_name='ge-0/0/0.0', terse=True, normalize=True)
If you do not normalize the RPC reply, you must account for any whitespace when using XPath expressions that reference a specific node or value. The following example selects the IPv4 address for a logical interface. In the XPath expression, the predicate specifying the inet family must account for the additional whitespace in order for the search to succeed. The resulting value includes leading and trailing newlines.
rsp = dev.rpc.get_interface_information(interface_name='ge-0/0/0.0', terse=True)
print (rsp.xpath(".// \
address-family[normalize-space(address-family-name)='inet']/ \
interface-address/ifa-local")[0].text)
'\n198.51.100.1/24\n'
When you normalize the RPC reply, any leading and trailing whitespace is removed, which makes text-based searches much more straightforward.
rsp = dev.rpc.get_interface_information(interface_name='ge-0/0/0.0', terse=True, normalize=True)
print (rsp.xpath(".//address-family[address-family-name='inet']/ \
interface-address/ifa-local")[0].text)
'198.51.100.1/24'