Writing a code communicator
The configuration language used in the device classes is pretty complex and so it has a lot of possibilities to specify the logic for communication with a network device. But some devices have some special cases that still are not possible to implement with the configuration language.
That is where code communicators are used. There is the possibility to define a code communicator for each device class that overwrites function from the yaml device class files. This code communicator needs to implement the communicator.Functions interface, which defines all operations Thola can perform on a network device. This interface looks like the following:
communicator.Functions
<interface>
----------------------------------------------------------------
GetVendor(ctx *context.Context) (string, error)
GetModel(ctx *context.Context) (string, error)
GetModelSeries(ctx *context.Context) (string, error)
GetSerialNumber(ctx *context.Context) (string, error)
GetOSVersion(ctx *context.Context) (string, error)
GetInterfaces(ctx *context.Context) (string, error)
GetCountInterfaces(ctx *context.Context) (string, error)
To create a new code communicator, you need to create a new file inside the code communicator directory core/communicator/codecommunicator
and create a new struct-based type for your device class. This type needs to embed the codecommunicator.codecommunicator type
to ensure it satisfies the codecommunicator.Functions interface. After that, you can write the methods you want to define as code
for this particular device class, like shown in the following example (ios):
package codecommunicator
import (
"context"
"github.com/inexio/thola/core/network"
"github.com/pkg/errors"
"strconv"
)
type iosCodeCommunicator struct {
codecommunicator
}
func (c *iosCommunicator) GetCPUComponentCPULoad(ctx context.Context) ([]float64, error) {
con, ok := network.DeviceConnectionFromContext(ctx)
if !ok || con.SNMP == nil {
return nil, errors.New("no device connection available")
}
var cpus []float64
res, _ := con.SNMP.SnmpClient.SNMPWalk(ctx, "1.3.6.1.4.1.9.9.109.1.1.1.1.5")
for _, snmpres := range res {
s, err := snmpres.GetValueString()
if err != nil {
return nil, err
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, errors.Wrap(err, "failed to parse snmp response to float64")
}
cpus = append(cpus, f)
}
return cpus, nil
}
The last thing that needs to be done in order for a code communicator to work, is assigning it to its device class.
This is done through a mapping in core/communicator/codecommunicator/code_communicator.go
. The new code communicator
needs to be included in the GetCodeCommunicator
function. It contains a switch case which maps a device class name to its
code communicator:
func getCodeCommunicator(class string) (communicator, error) {
switch class {
case "generic":
return &genericCodeCommunicator{}, nil
...
╔═══════════════════════════════════════════════════╗
║ case "ios": ║
║ return &iosCodeCommunicator{}, nil ║
╚═══════════════════════════════════════════════════╝
...
}
return nil, errors.New("no communicator found")
}
After this is done, Thola will use the newly defined code communicator for executing the operations it defines.