firehol/netdata

View on GitHub
src/go/plugin/go.d/modules/snmp/charts.go

Summary

Maintainability
A
2 hrs
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package snmp

import (
    "fmt"
    "strings"

    "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
)

const (
    prioNetIfaceTraffic = module.Priority + iota
    prioNetIfaceUnicast
    prioNetIfaceMulticast
    prioNetIfaceBroadcast
    prioNetIfaceErrors
    prioNetIfaceDiscards
    prioNetIfaceAdminStatus
    prioNetIfaceOperStatus
    prioSysUptime
)

var netIfaceChartsTmpl = module.Charts{
    netIfaceTrafficChartTmpl.Copy(),
    netIfacePacketsChartTmpl.Copy(),
    netIfaceMulticastChartTmpl.Copy(),
    netIfaceBroadcastChartTmpl.Copy(),
    netIfaceErrorsChartTmpl.Copy(),
    netIfaceDiscardsChartTmpl.Copy(),
    netIfaceAdminStatusChartTmpl.Copy(),
    netIfaceOperStatusChartTmpl.Copy(),
}

var (
    netIfaceTrafficChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_traffic",
        Title:    "SNMP device network interface traffic",
        Units:    "kilobits/s",
        Fam:      "traffic",
        Ctx:      "snmp.device_net_interface_traffic",
        Priority: prioNetIfaceTraffic,
        Type:     module.Area,
        Dims: module.Dims{
            {ID: "net_iface_%s_traffic_in", Name: "received", Algo: module.Incremental},
            {ID: "net_iface_%s_traffic_out", Name: "sent", Mul: -1, Algo: module.Incremental},
        },
    }

    netIfacePacketsChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_unicast",
        Title:    "SNMP device network interface unicast packets",
        Units:    "packets/s",
        Fam:      "packets",
        Ctx:      "snmp.device_net_interface_unicast",
        Priority: prioNetIfaceUnicast,
        Dims: module.Dims{
            {ID: "net_iface_%s_ucast_in", Name: "received", Algo: module.Incremental},
            {ID: "net_iface_%s_ucast_out", Name: "sent", Mul: -1, Algo: module.Incremental},
        },
    }
    netIfaceMulticastChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_multicast",
        Title:    "SNMP device network interface multicast packets",
        Units:    "packets/s",
        Fam:      "packets",
        Ctx:      "snmp.device_net_interface_multicast",
        Priority: prioNetIfaceMulticast,
        Dims: module.Dims{
            {ID: "net_iface_%s_mcast_in", Name: "received", Algo: module.Incremental},
            {ID: "net_iface_%s_mcast_out", Name: "sent", Mul: -1, Algo: module.Incremental},
        },
    }
    netIfaceBroadcastChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_broadcast",
        Title:    "SNMP device network interface broadcast packets",
        Units:    "packets/s",
        Fam:      "packets",
        Ctx:      "snmp.device_net_interface_broadcast",
        Priority: prioNetIfaceBroadcast,
        Dims: module.Dims{
            {ID: "net_iface_%s_bcast_in", Name: "received", Algo: module.Incremental},
            {ID: "net_iface_%s_bcast_out", Name: "sent", Mul: -1, Algo: module.Incremental},
        },
    }

    netIfaceErrorsChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_errors",
        Title:    "SNMP device network interface errors",
        Units:    "errors/s",
        Fam:      "errors",
        Ctx:      "snmp.device_net_interface_errors",
        Priority: prioNetIfaceErrors,
        Dims: module.Dims{
            {ID: "net_iface_%s_errors_in", Name: "inbound", Algo: module.Incremental},
            {ID: "net_iface_%s_errors_out", Name: "outbound", Mul: -1, Algo: module.Incremental},
        },
    }

    netIfaceDiscardsChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_discards",
        Title:    "SNMP device network interface discards",
        Units:    "discards/s",
        Fam:      "discards",
        Ctx:      "snmp.device_net_interface_discards",
        Priority: prioNetIfaceDiscards,
        Dims: module.Dims{
            {ID: "net_iface_%s_discards_in", Name: "inbound", Algo: module.Incremental},
            {ID: "net_iface_%s_discards_out", Name: "outbound", Mul: -1, Algo: module.Incremental},
        },
    }

    netIfaceAdminStatusChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_admin_status",
        Title:    "SNMP device network interface administrative status",
        Units:    "status",
        Fam:      "status",
        Ctx:      "snmp.device_net_interface_admin_status",
        Priority: prioNetIfaceAdminStatus,
        Dims: module.Dims{
            {ID: "net_iface_%s_admin_status_up", Name: "up"},
            {ID: "net_iface_%s_admin_status_down", Name: "down"},
            {ID: "net_iface_%s_admin_status_testing", Name: "testing"},
        },
    }
    netIfaceOperStatusChartTmpl = module.Chart{
        ID:       "snmp_device_net_iface_%s_oper_status",
        Title:    "SNMP device network interface operational status",
        Units:    "status",
        Fam:      "status",
        Ctx:      "snmp.device_net_interface_oper_status",
        Priority: prioNetIfaceOperStatus,
        Dims: module.Dims{
            {ID: "net_iface_%s_oper_status_up", Name: "up"},
            {ID: "net_iface_%s_oper_status_down", Name: "down"},
            {ID: "net_iface_%s_oper_status_testing", Name: "testing"},
            {ID: "net_iface_%s_oper_status_unknown", Name: "unknown"},
            {ID: "net_iface_%s_oper_status_dormant", Name: "dormant"},
            {ID: "net_iface_%s_oper_status_notPresent", Name: "not_present"},
            {ID: "net_iface_%s_oper_status_lowerLayerDown", Name: "lower_layer_down"},
        },
    }
)

var (
    uptimeChart = module.Chart{
        ID:       "snmp_device_uptime",
        Title:    "SNMP device uptime",
        Units:    "seconds",
        Fam:      "uptime",
        Ctx:      "snmp.device_uptime",
        Priority: prioSysUptime,
        Dims: module.Dims{
            {ID: "uptime", Name: "uptime"},
        },
    }
)

func (s *SNMP) addNetIfaceCharts(iface *netInterface) {
    charts := netIfaceChartsTmpl.Copy()

    for _, chart := range *charts {
        chart.ID = fmt.Sprintf(chart.ID, cleanIfaceName(iface.ifName))
        chart.Labels = []module.Label{
            {Key: "vendor", Value: s.sysInfo.organization},
            {Key: "sysName", Value: s.sysInfo.name},
            {Key: "ifDescr", Value: iface.ifDescr},
            {Key: "ifName", Value: iface.ifName},
            {Key: "ifType", Value: ifTypeMapping[iface.ifType]},
        }
        for _, dim := range chart.Dims {
            dim.ID = fmt.Sprintf(dim.ID, iface.ifName)
        }
    }

    if err := s.Charts().Add(*charts...); err != nil {
        s.Warning(err)
    }
}

func (s *SNMP) removeNetIfaceCharts(iface *netInterface) {
    px := fmt.Sprintf("snmp_device_net_iface_%s_", cleanIfaceName(iface.ifName))
    for _, chart := range *s.Charts() {
        if strings.HasPrefix(chart.ID, px) {
            chart.MarkRemove()
            chart.MarkNotCreated()
        }
    }
}

func (s *SNMP) addSysUptimeChart() {
    chart := uptimeChart.Copy()
    chart.Labels = []module.Label{
        {Key: "vendor", Value: s.sysInfo.organization},
        {Key: "sysName", Value: s.sysInfo.name},
    }
    if err := s.Charts().Add(chart); err != nil {
        s.Warning(err)
    }
}

func cleanIfaceName(name string) string {
    r := strings.NewReplacer(".", "_", " ", "_")
    return r.Replace(name)
}

func newUserInputCharts(configs []ChartConfig) (*module.Charts, error) {
    charts := &module.Charts{}
    for _, cfg := range configs {
        if len(cfg.IndexRange) == 2 {
            cs, err := newUserInputChartsFromIndexRange(cfg)
            if err != nil {
                return nil, err
            }
            if err := charts.Add(*cs...); err != nil {
                return nil, err
            }
        } else {
            chart, err := newUserInputChart(cfg)
            if err != nil {
                return nil, err
            }
            if err = charts.Add(chart); err != nil {
                return nil, err
            }
        }
    }
    return charts, nil
}

func newUserInputChartsFromIndexRange(cfg ChartConfig) (*module.Charts, error) {
    var addPrio int
    charts := &module.Charts{}
    for i := cfg.IndexRange[0]; i <= cfg.IndexRange[1]; i++ {
        chart, err := newUserInputChartWithOIDIndex(i, cfg)
        if err != nil {
            return nil, err
        }
        chart.Priority += addPrio
        addPrio += 1
        if err = charts.Add(chart); err != nil {
            return nil, err
        }
    }
    return charts, nil
}

func newUserInputChartWithOIDIndex(oidIndex int, cfg ChartConfig) (*module.Chart, error) {
    chart, err := newUserInputChart(cfg)
    if err != nil {
        return nil, err
    }

    chart.ID = fmt.Sprintf("%s_%d", chart.ID, oidIndex)
    chart.Title = fmt.Sprintf("%s %d", chart.Title, oidIndex)
    for _, dim := range chart.Dims {
        dim.ID = fmt.Sprintf("%s.%d", dim.ID, oidIndex)
    }

    return chart, nil
}

func newUserInputChart(cfg ChartConfig) (*module.Chart, error) {
    chart := &module.Chart{
        ID:       cfg.ID,
        Title:    cfg.Title,
        Units:    cfg.Units,
        Fam:      cfg.Family,
        Ctx:      fmt.Sprintf("snmp.%s", cfg.ID),
        Type:     module.ChartType(cfg.Type),
        Priority: cfg.Priority,
    }

    if chart.Title == "" {
        chart.Title = "Untitled chart"
    }
    if chart.Units == "" {
        chart.Units = "num"
    }
    if chart.Priority < module.Priority {
        chart.Priority += module.Priority
    }

    seen := make(map[string]struct{})
    var a string
    for _, cfg := range cfg.Dimensions {
        if cfg.Algorithm != "" {
            seen[cfg.Algorithm] = struct{}{}
            a = cfg.Algorithm
        }
        dim := &module.Dim{
            ID:   strings.TrimPrefix(cfg.OID, "."),
            Name: cfg.Name,
            Algo: module.DimAlgo(cfg.Algorithm),
            Mul:  cfg.Multiplier,
            Div:  cfg.Divisor,
        }
        if err := chart.AddDim(dim); err != nil {
            return nil, err
        }
    }
    if len(seen) == 1 && a != "" && len(chart.Dims) > 1 {
        for _, d := range chart.Dims {
            if d.Algo == "" {
                d.Algo = module.DimAlgo(a)
            }
        }
    }

    return chart, nil
}