netdata/netdata

View on GitHub
src/go/collectors/go.d.plugin/modules/dnsmasq_dhcp/collect.go

Summary

Maintainability
A
0 mins
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package dnsmasq_dhcp

import (
    "bufio"
    "io"
    "math"
    "math/big"
    "net"
    "os"
    "strings"
    "time"

    "github.com/netdata/netdata/go/go.d.plugin/pkg/iprange"
)

func (d *DnsmasqDHCP) collect() (map[string]int64, error) {
    now := time.Now()
    var updated bool

    if now.Sub(d.parseConfigTime) > d.parseConfigEvery {
        d.parseConfigTime = now

        dhcpRanges, dhcpHosts := d.parseDnsmasqDHCPConfiguration()
        d.dhcpRanges, d.dhcpHosts = dhcpRanges, dhcpHosts
        updated = d.updateCharts()

        d.collectV4V6Stats()
    }

    f, err := os.Open(d.LeasesPath)
    if err != nil {
        return nil, err
    }
    defer func() { _ = f.Close() }()

    if !updated {
        fi, err := f.Stat()
        if err != nil {
            return nil, err
        }

        if d.leasesModTime.Equal(fi.ModTime()) {
            d.Debug("lease database file modification time has not changed, old data is returned")
            return d.mx, nil
        }

        d.Debug("leases db file modification time has changed, reading it")
        d.leasesModTime = fi.ModTime()
    }

    leases := findLeases(f)
    d.collectRangesStats(leases)

    return d.mx, nil
}

func (d *DnsmasqDHCP) collectV4V6Stats() {
    d.mx["ipv4_dhcp_ranges"], d.mx["ipv6_dhcp_ranges"] = 0, 0
    for _, r := range d.dhcpRanges {
        if r.Family() == iprange.V6Family {
            d.mx["ipv6_dhcp_ranges"]++
        } else {
            d.mx["ipv4_dhcp_ranges"]++
        }
    }

    d.mx["ipv4_dhcp_hosts"], d.mx["ipv6_dhcp_hosts"] = 0, 0
    for _, ip := range d.dhcpHosts {
        if ip.To4() == nil {
            d.mx["ipv6_dhcp_hosts"]++
        } else {
            d.mx["ipv4_dhcp_hosts"]++
        }
    }
}

func (d *DnsmasqDHCP) collectRangesStats(leases []net.IP) {
    for _, r := range d.dhcpRanges {
        d.mx["dhcp_range_"+r.String()+"_allocated_leases"] = 0
        d.mx["dhcp_range_"+r.String()+"_utilization"] = 0
    }

    for _, ip := range leases {
        for _, r := range d.dhcpRanges {
            if r.Contains(ip) {
                d.mx["dhcp_range_"+r.String()+"_allocated_leases"]++
                break
            }
        }
    }

    for _, ip := range d.dhcpHosts {
        for _, r := range d.dhcpRanges {
            if r.Contains(ip) {
                d.mx["dhcp_range_"+r.String()+"_allocated_leases"]++
                break
            }
        }
    }

    for _, r := range d.dhcpRanges {
        name := "dhcp_range_" + r.String() + "_allocated_leases"
        numOfIps, ok := d.mx[name]
        if !ok {
            d.mx[name] = 0
        }
        d.mx["dhcp_range_"+r.String()+"_utilization"] = int64(math.Round(calcPercent(numOfIps, r.Size())))
    }
}

func (d *DnsmasqDHCP) updateCharts() bool {
    var updated bool
    seen := make(map[string]bool)
    for _, r := range d.dhcpRanges {
        seen[r.String()] = true
        if !d.cacheDHCPRanges[r.String()] {
            d.cacheDHCPRanges[r.String()] = true
            d.addDHCPRangeCharts(r.String())
            updated = true
        }
    }

    for v := range d.cacheDHCPRanges {
        if !seen[v] {
            delete(d.cacheDHCPRanges, v)
            d.removeDHCPRangeCharts(v)
            updated = true
        }
    }
    return updated
}

func findLeases(r io.Reader) []net.IP {
    /*
        1560300536 08:00:27:61:3c:ee 2.2.2.3 debian8 *
        duid 00:01:00:01:24:90:cf:5b:08:00:27:61:2e:2c
        1560300414 660684014 1234::20b * 00:01:00:01:24:90:cf:a3:08:00:27:61:3c:ee
    */
    var ips []net.IP
    s := bufio.NewScanner(r)

    for s.Scan() {
        parts := strings.Fields(s.Text())
        if len(parts) != 5 {
            continue
        }

        ip := net.ParseIP(parts[2])
        if ip == nil {
            continue
        }
        ips = append(ips, ip)
    }

    return ips
}

func calcPercent(ips int64, hosts *big.Int) float64 {
    h := hosts.Int64()
    if ips == 0 || h == 0 || !hosts.IsInt64() {
        return 0
    }
    return float64(ips) * 100 / float64(h)
}