I am educating myself about network automation. As I spend a lot of time in the Juniper world, a natural place to work on automation skills is by leveraging PyEZ.
What is PyEZ?
Juniper describes it this way in their techwiki.
Junos PyEZ is a microframework for Python that enables you to remotely manage and automate devices running Junos OS.
In short, you get a bunch of classes you can act on in Python that allow you to interact with Junos devices using Python 2.7. I just started looking at this last night, so I am far from grasping the full scope of capabilities offered. At the very least, it’s possible to perform device inventories, extract information about the configuration, splice in configuration chunks, and run CLI commands. I suppose that running CLI commands is antithetical to using Python for device configuration, but I see it as a way to get any sort of output back from the device you’re managing when maybe some data isn’t available via a clever PyEZ class.
To interact with Junos using Python, you need to install Python, add the PyEZ library, and configure Junos to allow interaction via NETCONF over SSH. I am using Mac OS X 10.10.1. If you’re on Windows or *NIX, your specific steps will likely be different.
- Install Python. Your Mac already comes with Python, but AFAIK, it doesn’t have IDLE, the Python Integrated DeveLopment Environment. Or maybe it does, and I missed it. Either way, IDLE is a nice place to help you try commands, test output, read help, etc. while you develop in Python. I found it straightforward to download Python 2.7.9 from here, and install the 64-bit package for the Mac. Thereafter, a Python 2.7 folder appeared in the Applications folder, in which I found IDLE.
- Install PyEZ. There is an excellent explanation and comment thread describing the installation process here. While it’s not hard, there are some potential gotchas depending on what specific Python version you’ve installed, your specific OS X verstion, etc. For me, the process was as simple as “sudo pip install junos-eznc”.
The process immediately took off, downloading a number of packages including junos-eznc, lxml, ncclient, paramiko, scp, jinja2, PyYAML, netaddr, pycrypto, ecdsa, and markupsafe. Then the setup process ran for perhaps a minute or two on my 2013-era MacBook Pro. The same process seemed to take a bit longer on my 2013-era MacBook Air with less CPU.
There were a few warnings along the way, but nothing to halt the PyEZ installation process. The final line states,
Successfully installed junos-eznc lxml ncclient paramiko scp jinja2 PyYAML netaddr pycrypto ecdsa markupsafe
You can read the entire plaintext transcription of my PyEZ install here. Not that you need to…but just in case you’re troubleshooting an installation gone wrong and might benefit from a point of comparison.
- Enable Junos device management via NETCONF over SSH. NETCONF is the method PyEZ uses to interact with Junos. Note that if you do not enable this device management method, you will be unable to complete a device “open” in your Python code. If you run Wireshark, you’ll see that when Python attempts to open a TCP socket to port 830, the Junos device sends back an immediate reset.
I used an Olive in my ESXi lab to test PyEZ last night. I simply added the following line: “set system services netconf ssh”.
I’ll assume you’ve already got a user and password configured to manage the Junos device. You’ll need one to authenticate to Junos in your Python code, just like you need to authenticate when you log in via typical CLI session.
Now that PyEZ is installed and your Junos device ready to speak NETCONF, you can test. I followed the simple “Hello, World!” example here. I suppose you could write the code as a script and execute it. I used IDLE.
The very first thing I did in IDLE was switch the default font in Preference to Andale Mono. You can use whatever you like, but Andale Mono was the first one in the list that was monospaced that I’d worked with before. I might go for something different later.
Initial Test With Known Good Setup
Juniper’s “hello, world” example is simple. The code imports some classes that later lines will use. Then a device is defined and opened. Then “facts” are gathered about the device and output to the screen. Then the device is closed. Here’s my interaction with IDLE. Note that “>>>” is the IDLE prompt, not actual input.
>>> from pprint import pprint
>>> from jnpr.junos import Device
>>> dev = Device(host=’10.95.100.11′, user=’pyez’, password=’pyez102′ )
>>> pprint( dev.facts )
‘version_info’: junos.version_info(major=(12, 1), type=R, minor=1, build=9)}
Second Test with Bogus Password
Now, for fun, I’ll change the definition of “dev” to have a bogus password and attempt to open the device again. This is what happens.
>>> dev = Device(host=’10.95.100.11′, user=’pyez’, password=’pyez101′ )
Traceback (most recent call last):
File “<pyshell#12>”, line 1, in <module>
File “/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/jnpr/junos/device.py”, line 396, in open
In short, we get an authentication failure, as expected.
Third Test With NETCONF Over SSH Disabled
The results a little different if we shut off NETCONF over SSH on the Junos device with a “delete system services netconf ssh” command at the CLI. Assume I reset the password correctly first, and have committed the delete line above.
Traceback (most recent call last):
File “<pyshell#15>”, line 1, in <module>
File “/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/jnpr/junos/device.py”, line 409, in open
Here, we get a notification that the connection was refused. This is different from the authentication problem, and something to pay attention to if you’re having trouble getting your Python code to make a connection to your Junos device.
Fourth Test Against Unreachable Device
Finally, what happens if you try to open a device that is unreachable? In this example, I defined “dev” with a bogus IP address.
Traceback (most recent call last):
File “<pyshell#19>”, line 1, in <module>
File “/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/jnpr/junos/device.py”, line 415, in open
Again, same end result (no connection), but subtly different than the others. Here, we get a connection timeout error after many seconds of Python trying to connect.
So, What’s Next?
For me, the next steps are exploring what I can actually do with PyEZ. I don’t see PyEZ as a strange novelty that’s kind of neat of but of no real value. Rather, I see PyEZ as a building block to understanding how to interact with Junos in a way other than the CLI. Juniper’s purpose is automation, and it’s in the light of automation that my expectations shine.
I’m not sure what other value I might get out of PyEZ. I’m interested in extracting information from network devices and performing reports and doing common tasks network engineers do like L2/L3 combined traceroutes. I’m not sure if PyEZ will give me access to the data needed to do these sorts of things.
Another element of interacting with Junos is XML. Learning how to programmatically deal with XML formatted data is therefore another thing I need to get a handle on. I’ve started by reading this tutorial on XPath (recommended somewhere in the Juniper wiki on PyEZ), and I have a lot more work to do in this area. For me, it’s the same sort of data handling that I’ve had to do in any sort of programming environment I’ve ever worked in. I need the data formatted nicely and accessible via variables or tables. XPath appears to be a key part of the data parsing equation, so I need to grok it sooner rather than later.