Avoid Write-Only Code

You probably know that fantastic feeling when you think your newly-discovered tool is a Hammer of Thor, capable of solving every problem (or at least crashing through it). I guess you’re also familiar with that sinking feeling when you’re trying to use your beloved hammer to whitewash a bikeshed.

Not surprisingly, the cruder the tool is, the quicker you’ll hit its limits, like when you try to do data processing in Jinja2 (hint: don’t).

Just to give you an idea what I’m talking about: this is how I “solved” the generation of don’t care bits in IOS access lists:

{% set allone = "255.255.255.255" | ipaddr('int') %}
ip access-list extended LocalPrefixes
{% for intf in interfaces %}
  {% set netmask = intf.prefix | ipaddr('netmask') 
                               | ipaddr('int') %}
  {% set invmask = (allone - netmask) | ipaddr('address') %}
  {% set prefix = intf.prefix | ipaddr(0) %}
  permit {{prefix | regex_replace('/.*','') }} {{invmask}}
{% endfor %}

Although I explained how it worked a while ago (watch the IP Address Handling video), I would be hard-pressed to figure it out today.

Here’s another gem I received from an attendee of the Building Network Automation Solutions online course:

{% set jq = "[?name=='" + p.port + "'].{ myip: ip, peer: peer }" %}
{% set el = ports | json_query(jq) %}
{% set peer_ip = hostvars[el.0.peer] | json_query('ports[*].ip') | ipaddr(el.0.myip) %}

When reviewing his solution, I told him that…

That is write-only code. Good luck trying to remember what you did when you have to fix the template in 6 months. It might be way better to write a custom Jinja2 filter to do the lookups.

I've been programming in Perl for decades, so it’s easy for me to recognize write-only code.

Not surprisingly this is what I got back a few days later:

You are of course right. I spent good 5 minutes deciphering this today.

Long story short: whenever your Jinja2 templates or Ansible playbooks start getting complex it’s time to find someone with Python skills and ask her to write a custom Jinja2 filters or Ansible module… assuming you don’t want to open that particular can of worms. Of course, you can do it yourself if you’re so inclined.

Speaking of custom Jinja2 filters: I covered them in the Ansible for Networking Engineers webinar and online course, and Mat Wood explained how Facebook uses them in their configuration management projects in Building Network Automation Solutions online course.

6 comments:

  1. That reminds me of EWD's (if I'm not mistaken) rule of code length, along the lines: whenever you do something really ingenious that shortens your code, write a good comment to keep the size constant.
  2. Text manipulation with a templating language and filters, how revolutionary is that!
    Replies
    1. My dear Anonymous friend,

      Yet again you managed to completely miss the point of the post. That's been happening a lot to you lately... I'm worried. Are you too much in a hurry?

      All the best,
      Ivan
    2. Yes, I'm very busy at the moment reinventing the wheel. But I've done a great progress. By the way where is your new blog post so I can comment? As you might know time is valuable.
  3. To be honest I don't see the difference between embedding some logic in template and writing own filter for a single task. In both cases, after some time, one must check, what was coded and why. You may write more on less write-only code in any language and IMHO moving something from template to custom filter makes sense only if this part will be reused in other places.
    Replies
    1. "You may write write-only code in any language" << I totally agree with that, but some things (like data manipulation or complex decision trees) are better handled in some languages than others - there's the right tool for every job.

      See also http://web.mit.edu/humor/Computers/real.programmers ;)
Add comment
Sidebar