Use Junos PyEZ to Execute RPCs on Junos Devices
SUMMARY Use the Device
rpc
property to execute operational RPCs on Junos devices.
You can use Junos PyEZ to execute remote procedure calls (RPCs) on demand on Junos devices. 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 Junos device. 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.
Map 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.
Execute 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')
Specify 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. 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 supported starting in Junos OS Release 14.2R1.
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:~$ python3 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:~$ python3 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'}]}, ...
Specify the Scope of Data to Return
You can use Junos PyEZ to execute an RPC to retrieve operational information from Junos devices. 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(result, encoding='unicode'))
When you execute the script, it displays each physical interface’s name element.
user@server:~$ python3 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>
Specify 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. 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)
Normalize 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. 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.
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'