Monday, July 6, 2020

scrapli - python sync/aysnc telnet/ssh/netconf driver

I've spent an obscene amount of time working on my project called scrapli -- a silly name that is "scrape cli" squished together. As the name/title implies, scrapli is a screen scraping library built in python, but theres a bit more to it than that, and as of this past weekend scrapli encompasses more than just "screen scraping" -- it also can handle NETCONF connections (get/get-config/edit-config/etc. -- though not 100% RFC support yet)!

TL;DR - scrapli is wicked fast, has zero requirements*, supports 100% of normal OpenSSH behavior*, is well typed/tested/documented, sync and asyncio with the same API, and provides a consistent look/feel across telnet/SSH/NETCONF (over SSH).

* if using "system" transport (you can ready about what that means in the README!)

Before going too deep into things, here are some links that may be useful:

I won't bother too much talking about what scrapli is and how its built as I've written extensively about it in the README on the GitHub page (first link above), as well as gone into tons of detail with Dmitry on his stream. Instead, I'll outline some reasons you may be interested in scrapli.

  • scrapli is super fast - it supports multiple different flavors of transports depending on your needs, so speed may vary a bit from transport to transport, but scrapli is fast with any of them! If you really feel the need for speed the ssh2-python (wrapper around libssh2 C library) plugin is about as fast as you'll get in python!
  • You care about typing! I know for me personally I really enjoy libraries with good type hinting -- its useful for IDE auto complete/Intellisense stuff, and being strict with typing can help stop problems before they rear their head (and has with scrapli!).
  • You enjoy long walks on the beach and reading ridiculously long README docs.
  • You want to write asyncio code. Not for everyone for sure, and I'm not advocating for asyncio necessarily, but "right tool for the right job" -- scrapli provides the exact same API for both sync and aysncio.
  • There is a nornir plugin for scrapli -- the current version on pypi works with nornir 2.x, but an improved version is ready to be pushed whenever nornir 3.x is "officially" released.
  • You have telnet/SSH and NETCONF devices that you interact with -- the NETCONF support is built right on top of scrapli "core", so you interact with scrapli in exactly the same way regardless of the type of connection you are making. Same host setup (the arguments you pass to scrapli to make a connection), same look and feel of the API, same result objects, etc.
  • You appreciate possibly too many tests. I have spent a goodly portion of that obscene amount of time making sure scrapli is well tested! There are of course unit tests, but there are also "functional" tests that run against virtual devices (and info in the README on how you can setup a lab to match if you wanted to test scrapli or contribute to it), as well as a mocked SSH server for ensuring that even without the "functional" tests scrapli can be tested in the most real way possible -- ensuring its doing what its supposed to do.
  • Additional platforms can easily be added by simply defining the privilege levels and on_open/on_close functions. I'm also working on a scrapli_community setup so that folks can add more device support. I am pretty adamant about not having a billion classes to maintain, and instead just having things be fairly pluggable by passing in args/callables to the "NetworkDriver" (or AsyncNetworkDriver of course)
  • There is seeming to be a lack of love for paramiko lately -- I don't want to get into any of that, but if you do want to get away from paramiko, you can use the system, ssh2, or asyncssh transport plugins in scrapli!
  • You, like me, don't really love ncclient, but want to NETCONF all the things. I personally find ncclient a bit obtuse, there is a fair bit of "magic" going on with dynamic attributes (getattr for vendor methods and things like that) and such that make it (at least for me) not super intutive. While scrapli_netconf is the latest addition to the scrapli family and does not have 100% feature parity with ncclient, it covers all the basics, and I am happy to add features if folks need them (and will over time anyway I'm sure!)

Why you may not want to use scrapli:

  • You are not working with one of the "core" platforms (IOSXE, IOSXR, Junos, NXOS, EOS) and aren't ready/interested to jump into a little bit of DIY (I promise its not hard!) to get your platform working (but you can still try out the GenericDriver which may be enough for whatever you've got going on!).
  • You are already doing all the things with RESTCONF or some other HTTP based thing -- yep, no need for scrapli then!
  • You are using Ansible/Salt/something else and don't really need/want to do low-level stuff like this because there are modules for that already.
  • ???

I may as well bring up the big question since I'm sure it will get asked: why not use netmiko? If netmiko is working for you then by all means keep on keepin' on! Kirk and I are friends and if it weren't for netmiko and all the amazing work Kirk has done for the community I wouldn't ever have built any of this anyway! To address the question though; asyncio is the most obvious differentiator. 100% of OpenSSH config support could be another big selling point to make a move to trying out scrapli (would mean using system transport). Not wanting to use paramiko (for whatever reasons), and lastly speed speed speed!

You probably would prefer to stick to using netmiko if you are a windows user (scrapli with ssh2/paramiko/telnet transports should work on windows but I don't windows so not sure whats up there), or you have non-core platforms and don't want to do a bit of DIY to make it work in scrapli (see above).

And now for a quick intro/example to scrapli!

from scrapli.driver.core import IOSXEDriver my_device = { "host": "172.31.254.1", "ssh_config_file": True, "auth_strict_key": False } with IOSXEDriver(**my_device) as conn: print("Gathering 'show run'!") show_run_response = conn.send_command(command="show run") print(f"Show run complete in {show_run_response.elapsed_time} seconds, successful: {not show_run_response.failed}") print("Gathering 'show tech'!") show_tech_response = conn.send_command(command="show tech") print(f"Show run complete in {show_tech_response.elapsed_time} seconds, successful: {not show_tech_response.failed}") print(f"Show tech was {len(show_tech_response.result.splitlines())} lines long!! AHHHHHHHH!!!!") 

And a quick asciinema example!

asciicast



No comments:

Post a Comment