DevOps Within

Stories from a man against the machines.

Where do we go(lang) now?

It feels good to be back home!

I've been a Python guy since I started programming consciously and I got used to all those things which make Python easy to start with. Especially dynamic typing. But golang is more like C/C++, which are the languages that I have begun my programming adventures with. This post is going to cover my first steps with i-must-go application and golang.

Slow but steady wins the race

It was always the easiest for me to learn a new technology by doing some specific tasks which interested me instead of following some tutorials on the web. I have tried 'a tour of go' and gave up after a few lessons because of that. So I jumped in to i-must-go with a little more than zero knowledge of the language with first goal taken off the kanban board: Application should be able to crawl network using LLDP protocol.

Since the application is going to use SNMP to get information from the devices on the network, I needed to be able to send and receive SNMP packets using golang. There is no library for that in the basic packages, so I have decided to use gosnmp:

GoSNMP is an SNMP client library fully written in Go. It provides Get, GetNext, GetBulk, Walk, BulkWalk, Set and Traps. It supports IPv4 and IPv6, using SNMPv2c or SNMPv3.

This seems like more than I would ever need in this application, so I settled with it. To query LLDP information using SNMP, I had to find proper OIDs (Object IDentifiers). For this I have used two webpages: one of many mib databases and one with lldp-specific info. Unfortunately, I didn't find any library that could use readily available MIBs, so I'm doing many things manually. As a first step, I have decided to just scan single device (starting node of the network) and learn how to use the tools that I have.

Step 1: Specify what you need

Using snmpwalk I have defined what kind of data is important to me:

iso.0.8802.1.1.2.1.4.1.1.4.0.3.1 = INTEGER: 4
iso.0.8802.1.1.2.1.4.1.1.4.0.12.1 = INTEGER: 4
(...)
iso.0.8802.1.1.2.1.4.1.1.5.0.3.1 = Hex-STRING: 1C 1B 0D 22 56 FB 
(...)
iso.0.8802.1.1.2.1.4.1.1.6.0.3.1 = INTEGER: 3
iso.0.8802.1.1.2.1.4.1.1.6.0.12.1 = INTEGER: 3
(...)

From this, I have identified parts of the OID that are really important to me:

iso.0.8802.1.1.2.1.4.1.1.<lldpObjectId>.0.<localPortId>.1

To somehow try and make the code easier to work with from the beginning, I have created an enum and a struct which represent LLDP information (lldpObjectId) that I can get using SNMP:

Step 2: Go get it

(pun intended.)

Knowing that I can walk the OID tree starting with 1.0.8802.1.1.2.1.4.1.1, I have created a basic function, which checks what is the current OID and assigns gathered data to a global list of ports. Then just print out what we've got for now: And the output looks like this:

[ `./i-must-go` | done: 1.178614094s ]
  Port 12:
  Remote:
   Chassis: 5407b613be63
   Port: 0 ()
   System: 
  Port 26:
  Remote:
   Chassis: 001a80edc93f
   Port: 0 ()
   System: 
  Port 40:
  Remote:
   Chassis: 24b6fdd2812
   Port: 0 ()
   System: 
  Port 47:
  Remote:
   Chassis: 9cb654c9f5c0
   Port: 0 (38)
   System: xyz-fgh
  Port 48:
  Remote:
   Chassis: 9cb6546d74c0
   Port: 0 (37)
   System: xyz-fgh
  Port 3:
  Remote:
   Chassis: 1c1b0d28b6fb
   Port: 0 ()
   System:

So, what do we have here? For now I have created a list of remote devices connected to local ports of the network entrypoint. That's today's goal, achieved.

Next steps

In the next step, I'll try to get IP address from MAC (ChassisId) of a remote end and then crawl it for the same information. Questions that have arisen for now are:

  • Can I 'namespace' my consts, so that ChassisIdSubtype becomes lldp.ChassisIdSubtype without creating a package?
  • How long can I go without a proper IDE? I'm using sublime text - the support is basic, but it seems to be working for now.