Topic: tech juniper jaut prev next
tech juniper jaut > Module 04: Advanced Junos PyEZ
PyEZ is a micro framework. PyEZ is used by the Juniper Ansible and Salt modules and by JSNAPy. Aside, Puppet and Chef use RubyEZ.
PyEZ works with any Junos device running Junos version 11.4 or later. It supports NETCONF, telnet and serial connections. From Junos 16.1, PyEZ is available for on-box scripting.
[edit system services]
set netconf ssh
A user account must be mapped to a class with the correct permissions (the same permissions as to execute instructions through the API).
>>> from jnpr.junos.version import VERSION
>>> VERSION
'2.2.0'
pip list | grep junos-eznc
NETCONF over SSH;
Device(host=..., user=..., passwd=...)
Telnet connection;
Device(host=..., user=..., passwd=..., mode='telnet', port='23')
Telnet can be used to connect via a console server. The port number will usually be non-standard.
Direct serial console connection;
Device(host=..., user=..., passwd=..., mode='serial', port='/dev/ttyUSB0')
Serial can be used to configure new devices out of the box. The console provides root login with no password.
SSH connections through a console server;
Device(host=..., user=..., passwd=..., cs_user=..., cs_passwd=...))
scp .ssh/id_rsa.pub lab@...:/tmp
edit system login user lab
set class super-user
set authentication load-key-file /tmp/id_rsa.pub
commit
ssh-add ssh/id_rsa
The ‘passwd’ argument contains the key passphrase, not the login password. The ‘sshprivatekey_file’ parameter to ‘Device’ can be used to specify a different key.
Display the RPC needed to execute the ‘show’ command;
show route table inet.0 | display xml rpc
Display the output of the command in XML format;
show route table inet.0 | display xml
This can also be done from Python;
>>> print(dev.display_xml_rpc('show interfaces terse', format='text'))
For example, the RPC;
<get-interface-information>
<interface-name>lo0</interface-name>
<terse/>
</get-interface-information>
Translates into;
dev.rpc.get_interface_information(interface_name='lo0', terse=True)
XPath can be used to process the result of an RPC query.
find() finds the first match. findall() returns a list of all matches. findtext() returns the .text content of the first match (shortcut to find()). xpath() implements full XPath functionality. Full XPath functionality is only available when using the xpath() function.
r1 = xml.find('interface')
dump(r1)
r3 = xml.findtext('interface/name')
print(r3)
Extraneous whitespace can make searching the RPC reply tricky. Pass ‘normalize=True’ to the RPC function call to normalise the reply.
Python library developed by Juniper. Converts XML to Python data structures and back.
import jxmlease
parser = jxmlease.EtreeParser()
route_lxml_element = dev.rpc.get_route_information(table='inet.0')
ezxml = parser(route_lxml_element)
print(ezxml)
root = jxmlease.parse(route_lxml_element)
root.prettyprint()
Unstructured changes, consisting of ASCII text as XML or ‘set’ commands. Lock the configuration using ‘lock()’, load in the new changes using ‘load()’, then commit the changes and unlock using ‘commit()’ and ‘unlock()’.
with Config(dev, mode='private') as cu:
cu.load('...', format='set')
cu.commit()
Arguments to load():
Lock the configuration. This is the same as running ‘configure exclusive’. Load the prepared config data in text format. Print a diff of the candidate configuration against the active configuration. Check that the config is acceptable and commit it, otherwise, rollback.
conf.lock()
conf.load(data, format='text')
conf.pdiff()
if conf.commit_check():
conf.commit()
else:
conf.rollback()
conf.unlock()
Note that dev.open needn’t be called as StartShell opens its own connection. ShartShell also has ‘open()’ and ‘close()’ methods.
with StartShell(dev) as ss:
res, text = ss.run('ls -la ~lab/important.txt')
print(res)
print(text)
To execute non-returning commands, use the ‘timeout’ argument and set ‘this’ equal to ‘None’. The default time is 30 seconds.
ss.run('cli -r "monitor traffic fxp0"', this=None, timeout=15)
if dev.facts['junos_info']['re0']['text'] == TARGET_VERSION:
print('no upgrade requried')
exit(1)
fs = FS(dev)
bytes_free = fs.storage_usage()['/dev/gpt/junos']['avail_block'] * 512
file_size = os.stat(IMAGE_FILE).st_size
if bytes_free < file_size:
print('not enough free space')
exit(1)
with SCP(dev, progress=True) as scp:
scp.put(IMAGE_FILE, remote_path=REMOTE_PATH)
if not sw.install(
package=REMOTE_PATH+IMAGE_FILE,
no_copy=True,
validate=False
):
print('installation error')
exit(1)
sw.reboot()
for _ in range(20):
print('waiting for device')
sleep(50)
try:
dev.open(auto_probe=10)
ver = dev.facts['junos_info']['re0']['text']
except (ProbeError, ConnectError):
continue
dev.close()
break
else:
print('device did not reboot in time')
if ver == TARGET_VERSION...
if __name__ == '__main__':
main()
A view is a set of fields from a table. This table is defined in the jnpr.junos.op.arp module.
---
ArpTable:
rpc: get-arp-table-information
item: arp-table-entry
key: mac-address
view: ArpView
ArpView:
fields:
mac_address: mac-address
ip_address: ip-address
interface_name: interface-name
Use the following to retrieve data from a table;
with Device(...) as dev:
arp = ArpTable(dev)
arp.get()
for mac in arp:
print(
"{}: {} {}".format(
mac.mac_address,
mac.ip_address,
mac.interface_name
)
)
Access can be read-write using the ‘set’ property.
To modify the user configuration, use the XPath ‘system/login/user’. The user name is the key field.
The YAML to allow setting the configuration. The ‘set’ mapping is required to modify the configuration.
UserAccountTable:
set: system/login/user
key-field: username
view: UserAccountView
UserAccountView:
fields:
username: name
fullname: full-name
‘userclass’ is stored in the ‘class’ tag.
userclass:
class:
default: unauthorized
‘uid’ is stored in the ‘uid’ element.
uid:
uid:
type: int
default: 1001
minValue: 100
maxValue: 64000
groups:
auth: authentication
Because ‘password’ is defined under the ‘auth’ group (here, ‘fieldsauth’), its actual path will be ‘authentication/encryptedpassword’.
fields_auth:
password: encrypted-password
Save the YAML files in a new directory, for example, ‘myTables’. Save both the table and the view in the same file, with the extension ‘.yml’. Save the following in a file with the same name but with a ‘.py’ extension;
from jnpr.junos.factory import loadyaml
from os.path import splitext
# Generate the .yml filename matching the name of this file.
_YAML_ = splitext(__file__)[0] + '.yml'
# Build classes based on the .yml file and export them.
globals().update(loadyaml(_YAML_))
Create a blank file called ‘init.py’. This tells the Python interpreter to look for modules in this directory.
Finally, write the script that will modify the configuration.
ua = UserAccountTable(dev)
ua.username = 'bob'
ua.userclass = 'super-user'
# use 'import crypt'
ua.password = crypt.crypt('lab123')
# Generate and store the configuration data in the internal LXML object.
ua.append()
# Lock the DB, make then change then unlock the DB.
ua.set(merge=True, comment="Commit")
An alternative is to lock, load, commit and unlock in separate steps.
ua.lock()
ua.load(merge=True)
ua.commit(comment="Commit")
ua.unlock()
The result can be verified using the same table and view.
ua.get()
for account in ua:
print(...)