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 }