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

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