I’m always on the lookout for ways to streamline my workflow and free up more time for the important stuff, like watching cat videos, as any other System or Network admin. So a few months ago I put some time aside and made a Pyhon script that checks the status of various services on my network infrastructure. I tried to make the tool modular so it could support any vendor that has an API but the core network that I currently support is all Arista.
To make the test output more user-friendly, I used the Rich package to format everything into a nice table with colours, line etc. If you’re not already familiar with Rich, I highly recommend checking it out – it’s a fantastic tool for formatting command line text and making it easier to read and understand.
Anyway, I made the tool, everything was going great, and I was receiving nice reports whenever I needed them. People also started using it and they liked it. Everything seemed to be going smoothly until one day, I was asked to add a new device to the tool. I thought OK, that’s not a problem. All I need to do is add the new device to the inventory, and everything should be fine. Or so I thought.
I ran the script after adding the device and I saw output for some commands are the following message for the new device:
unable to render int; a string or other renderable object is required
Where I was expecting to see something like this:
hmmm 🤔, interesting. Why? Logically, it should work because the new box is Arista and it’s running EOS. To understand what’s causing the issue, I started with comparing the raw output of the same command just to make sure at least both device produce similar output:
JSON output snippet of working device:
'169.254.96.29': {
'description': 'site-b-rt03',
'msgSent': 21111451,
'inMsgQueue': 0,
'prefixReceived': 12,
'upDownTime': 1676722090.889669,
'version': 4,
'prefixAccepted': 12,
'msgReceived': 17919108,
'peerState': 'Established',
'outMsgQueue': 0,
'underMaintenance': False,
'asn': '64512'
}
JSON output snippet of none-working device:
'169.254.96.9': {
'prefixReceived': 335,
'description': 'TOR-A',
'msgSent': 513051,
'inMsgQueue': 0,
'underMaintenance': False,
'prefixInBest': 57,
'upDownTime': 1677277045.425945,
'version': 4,
'msgReceived': 572874,
'prefixAccepted': 58,
'peerState': 'Established',
'outMsgQueue': 0,
'prefixInBestEcmp': 3,
'asn': 64512
}
Both outputs seem fine but comparing line by line, you can see 'asn'
value type for the new device is an integer but for the working device it’s a string and going back to the error message, that seems to be the culprit: the Rich module is expecting a string for the input to render and complains when it’s given an integer (asn
value is something that it renders for the output).
To make sure that this has nothing to do with the script or the Rich table module, I compared the EOS version of both devices and sure enough the new device was running on an older version of EOS, so this appears to be an API bug which has been fixed in newer EOS versions. As a workaround and also to reduce future failures when there is a similar condition, I need all the outputs to be string type before feeding them to Rich for presentation.
The proper way to implement this, could be using a powerful package such as Pydantic which the variable types could be tested and assured but for now, all I did was to put a one line list comprehension like item = [str(i) for i in item]
at the beginning of my Rich table function to get what I wanted.
The moral of the story? never trust the output, always sanitize.