github.com/netdata/go.d.plugin@v0.58.1/modules/dnsmasq_dhcp/collect.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package dnsmasq_dhcp
     4  
     5  import (
     6  	"bufio"
     7  	"io"
     8  	"math"
     9  	"math/big"
    10  	"net"
    11  	"os"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/netdata/go.d.plugin/pkg/iprange"
    16  )
    17  
    18  func (d *DnsmasqDHCP) collect() (map[string]int64, error) {
    19  	now := time.Now()
    20  	var updated bool
    21  
    22  	if now.Sub(d.parseConfigTime) > d.parseConfigEvery {
    23  		d.parseConfigTime = now
    24  
    25  		dhcpRanges, dhcpHosts := d.parseDnsmasqDHCPConfiguration()
    26  		d.dhcpRanges, d.dhcpHosts = dhcpRanges, dhcpHosts
    27  		updated = d.updateCharts()
    28  
    29  		d.collectV4V6Stats()
    30  	}
    31  
    32  	f, err := os.Open(d.LeasesPath)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	defer func() { _ = f.Close() }()
    37  
    38  	if !updated {
    39  		fi, err := f.Stat()
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  
    44  		if d.leasesModTime.Equal(fi.ModTime()) {
    45  			d.Debug("lease database file modification time has not changed, old data is returned")
    46  			return d.mx, nil
    47  		}
    48  
    49  		d.Debug("leases db file modification time has changed, reading it")
    50  		d.leasesModTime = fi.ModTime()
    51  	}
    52  
    53  	leases := findLeases(f)
    54  	d.collectRangesStats(leases)
    55  
    56  	return d.mx, nil
    57  }
    58  
    59  func (d *DnsmasqDHCP) collectV4V6Stats() {
    60  	d.mx["ipv4_dhcp_ranges"], d.mx["ipv6_dhcp_ranges"] = 0, 0
    61  	for _, r := range d.dhcpRanges {
    62  		if r.Family() == iprange.V6Family {
    63  			d.mx["ipv6_dhcp_ranges"]++
    64  		} else {
    65  			d.mx["ipv4_dhcp_ranges"]++
    66  		}
    67  	}
    68  
    69  	d.mx["ipv4_dhcp_hosts"], d.mx["ipv6_dhcp_hosts"] = 0, 0
    70  	for _, ip := range d.dhcpHosts {
    71  		if ip.To4() == nil {
    72  			d.mx["ipv6_dhcp_hosts"]++
    73  		} else {
    74  			d.mx["ipv4_dhcp_hosts"]++
    75  		}
    76  	}
    77  }
    78  
    79  func (d *DnsmasqDHCP) collectRangesStats(leases []net.IP) {
    80  	for _, r := range d.dhcpRanges {
    81  		d.mx["dhcp_range_"+r.String()+"_allocated_leases"] = 0
    82  		d.mx["dhcp_range_"+r.String()+"_utilization"] = 0
    83  	}
    84  
    85  	for _, ip := range leases {
    86  		for _, r := range d.dhcpRanges {
    87  			if r.Contains(ip) {
    88  				d.mx["dhcp_range_"+r.String()+"_allocated_leases"]++
    89  				break
    90  			}
    91  		}
    92  	}
    93  
    94  	for _, ip := range d.dhcpHosts {
    95  		for _, r := range d.dhcpRanges {
    96  			if r.Contains(ip) {
    97  				d.mx["dhcp_range_"+r.String()+"_allocated_leases"]++
    98  				break
    99  			}
   100  		}
   101  	}
   102  
   103  	for _, r := range d.dhcpRanges {
   104  		name := "dhcp_range_" + r.String() + "_allocated_leases"
   105  		numOfIps, ok := d.mx[name]
   106  		if !ok {
   107  			d.mx[name] = 0
   108  		}
   109  		d.mx["dhcp_range_"+r.String()+"_utilization"] = int64(math.Round(calcPercent(numOfIps, r.Size())))
   110  	}
   111  }
   112  
   113  func (d *DnsmasqDHCP) updateCharts() bool {
   114  	var updated bool
   115  	seen := make(map[string]bool)
   116  	for _, r := range d.dhcpRanges {
   117  		seen[r.String()] = true
   118  		if !d.cacheDHCPRanges[r.String()] {
   119  			d.cacheDHCPRanges[r.String()] = true
   120  			d.addDHCPRangeCharts(r.String())
   121  			updated = true
   122  		}
   123  	}
   124  
   125  	for v := range d.cacheDHCPRanges {
   126  		if !seen[v] {
   127  			delete(d.cacheDHCPRanges, v)
   128  			d.removeDHCPRangeCharts(v)
   129  			updated = true
   130  		}
   131  	}
   132  	return updated
   133  }
   134  
   135  func findLeases(r io.Reader) []net.IP {
   136  	/*
   137  		1560300536 08:00:27:61:3c:ee 2.2.2.3 debian8 *
   138  		duid 00:01:00:01:24:90:cf:5b:08:00:27:61:2e:2c
   139  		1560300414 660684014 1234::20b * 00:01:00:01:24:90:cf:a3:08:00:27:61:3c:ee
   140  	*/
   141  	var ips []net.IP
   142  	s := bufio.NewScanner(r)
   143  
   144  	for s.Scan() {
   145  		parts := strings.Fields(s.Text())
   146  		if len(parts) != 5 {
   147  			continue
   148  		}
   149  
   150  		ip := net.ParseIP(parts[2])
   151  		if ip == nil {
   152  			continue
   153  		}
   154  		ips = append(ips, ip)
   155  	}
   156  
   157  	return ips
   158  }
   159  
   160  func calcPercent(ips int64, hosts *big.Int) float64 {
   161  	h := hosts.Int64()
   162  	if ips == 0 || h == 0 || !hosts.IsInt64() {
   163  		return 0
   164  	}
   165  	return float64(ips) * 100 / float64(h)
   166  }