master
go 91 lines 2.59 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 //go:build linux || freebsd || openbsd || netbsd || dragonfly
4
5 package isc_dhcpd
6
7 import (
8 "os"
9 )
10
11 /*
12 dhcpd.leases db (file), see details: https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases#dhcpdleases
13
14 Every time a lease is acquired, renewed or released, its new value is recorded at the end of the lease file.
15 So if more than one declaration appears for a given lease, the last one in the file is the current one.
16
17 In order to prevent the lease database from growing without bound, the file is rewritten from time to time.
18 First, a temporary lease database is created and all known leases are dumped to it.
19 Then, the old lease database is renamed DBDIR/dhcpd.leases~.
20 Finally, the newly written lease database is moved into place.
21
22 In order to process both DHCPv4 and DHCPv6 messages you will need to run two separate instances of the dhcpd process.
23 Each of these instances will need its own lease file.
24 */
25
26 func (c *Collector) collect() (map[string]int64, error) {
27 fi, err := os.Stat(c.LeasesPath)
28 if err != nil {
29 return nil, err
30 }
31
32 if c.leasesModTime.Equal(fi.ModTime()) {
33 c.Debugf("leases file is not modified, returning cached metrics ('%s')", c.LeasesPath)
34 return c.collected, nil
35 }
36
37 c.leasesModTime = fi.ModTime()
38
39 leases, err := parseDHCPdLeasesFile(c.LeasesPath)
40 if err != nil {
41 return nil, err
42 }
43
44 activeLeases := removeInactiveLeases(leases)
45 c.Debugf("found total/active %d/%d leases ('%s')", len(leases), len(activeLeases), c.LeasesPath)
46
47 for _, pool := range c.pools {
48 collectPool(c.collected, pool, activeLeases)
49 }
50 c.collected["active_leases_total"] = int64(len(activeLeases))
51
52 return c.collected, nil
53 }
54
55 const precision = 100
56
57 func collectPool(collected map[string]int64, pool ipPool, leases []leaseEntry) {
58 n := calcPoolActiveLeases(pool, leases)
59 collected["dhcp_pool_"+pool.name+"_active_leases"] = n
60 collected["dhcp_pool_"+pool.name+"_utilization"] = int64(calcPoolUtilizationPercentage(pool, n) * precision)
61 }
62
63 func calcPoolActiveLeases(pool ipPool, leases []leaseEntry) (num int64) {
64 for _, l := range leases {
65 if pool.addresses.Contains(l.addr) {
66 num++
67 }
68 }
69 return num
70 }
71
72 func calcPoolUtilizationPercentage(pool ipPool, leases int64) float64 {
73 size := pool.addresses.Size()
74 if leases == 0 || !size.IsInt64() {
75 return 0
76 }
77 if size.Int64() == 0 {
78 return 100
79 }
80 return float64(leases) / float64(size.Int64()) * 100
81 }
82
83 func removeInactiveLeases(leases []leaseEntry) (active []leaseEntry) {
84 active = leases[:0]
85 for _, l := range leases {
86 if l.bindingState == "active" {
87 active = append(active, l)
88 }
89 }
90 return active
91 }