summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/modules/isc_dhcpd
diff options
context:
space:
mode:
Diffstat (limited to '')
l---------src/go/collectors/go.d.plugin/modules/isc_dhcpd/README.md1
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/charts.go57
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/collect.go89
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/config_schema.json70
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/init.go88
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/integrations/isc_dhcp.md193
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd.go121
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd_test.go345
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/metadata.yaml135
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/parse.go92
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.json10
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.yaml5
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_empty (renamed from fluent-bit/lib/jansson-e23f558/test/suites/invalid/empty/input)0
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4370
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_backup39
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_inactive370
-rw-r--r--src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv667
17 files changed, 2052 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/README.md b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/README.md
new file mode 120000
index 000000000..3385a00a4
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/README.md
@@ -0,0 +1 @@
+integrations/isc_dhcp.md \ No newline at end of file
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/charts.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/charts.go
new file mode 100644
index 000000000..7165bbffb
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/charts.go
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+const (
+ prioLeasesTotal = module.Priority + iota
+
+ prioDHCPPoolUtilization
+ prioDHCPPoolActiveLeases
+)
+
+var activeLeasesTotalChart = module.Chart{
+ ID: "active_leases_total",
+ Title: "Active Leases Total",
+ Units: "leases",
+ Fam: "summary",
+ Ctx: "isc_dhcpd.active_leases_total",
+ Priority: prioLeasesTotal,
+ Dims: module.Dims{
+ {ID: "active_leases_total", Name: "active"},
+ },
+}
+
+var dhcpPoolChartsTmpl = module.Charts{
+ dhcpPoolActiveLeasesChartTmpl.Copy(),
+ dhcpPoolUtilizationChartTmpl.Copy(),
+}
+
+var (
+ dhcpPoolUtilizationChartTmpl = module.Chart{
+ ID: "dhcp_pool_%s_utilization",
+ Title: "DHCP Pool Utilization",
+ Units: "percent",
+ Fam: "pools",
+ Ctx: "isc_dhcpd.dhcp_pool_utilization",
+ Priority: prioDHCPPoolUtilization,
+ Type: module.Area,
+ Dims: module.Dims{
+ {ID: "dhcp_pool_%s_utilization", Name: "utilization"},
+ },
+ }
+ dhcpPoolActiveLeasesChartTmpl = module.Chart{
+ ID: "dhcp_pool_%s_active_leases",
+ Title: "DHCP Pool Active Leases",
+ Units: "leases",
+ Fam: "pools",
+ Ctx: "isc_dhcpd.dhcp_pool_active_leases",
+ Priority: prioDHCPPoolActiveLeases,
+ Dims: module.Dims{
+ {ID: "dhcp_pool_%s_active_leases", Name: "active"},
+ },
+ }
+)
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/collect.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/collect.go
new file mode 100644
index 000000000..08716a108
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/collect.go
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ "os"
+)
+
+/*
+dhcpd.leases db (file), see details: https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases#dhcpdleases
+
+Every time a lease is acquired, renewed or released, its new value is recorded at the end of the lease file.
+So if more than one declaration appears for a given lease, the last one in the file is the current one.
+
+In order to prevent the lease database from growing without bound, the file is rewritten from time to time.
+First, a temporary lease database is created and all known leases are dumped to it.
+Then, the old lease database is renamed DBDIR/dhcpd.leases~.
+Finally, the newly written lease database is moved into place.
+
+In order to process both DHCPv4 and DHCPv6 messages you will need to run two separate instances of the dhcpd process.
+Each of these instances will need its own lease file.
+*/
+
+func (d *DHCPd) collect() (map[string]int64, error) {
+ fi, err := os.Stat(d.LeasesPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if d.leasesModTime.Equal(fi.ModTime()) {
+ d.Debugf("leases file is not modified, returning cached metrics ('%s')", d.LeasesPath)
+ return d.collected, nil
+ }
+
+ d.leasesModTime = fi.ModTime()
+
+ leases, err := parseDHCPdLeasesFile(d.LeasesPath)
+ if err != nil {
+ return nil, err
+ }
+
+ activeLeases := removeInactiveLeases(leases)
+ d.Debugf("found total/active %d/%d leases ('%s')", len(leases), len(activeLeases), d.LeasesPath)
+
+ for _, pool := range d.pools {
+ collectPool(d.collected, pool, activeLeases)
+ }
+ d.collected["active_leases_total"] = int64(len(activeLeases))
+
+ return d.collected, nil
+}
+
+const precision = 100
+
+func collectPool(collected map[string]int64, pool ipPool, leases []leaseEntry) {
+ n := calcPoolActiveLeases(pool, leases)
+ collected["dhcp_pool_"+pool.name+"_active_leases"] = n
+ collected["dhcp_pool_"+pool.name+"_utilization"] = int64(calcPoolUtilizationPercentage(pool, n) * precision)
+}
+
+func calcPoolActiveLeases(pool ipPool, leases []leaseEntry) (num int64) {
+ for _, l := range leases {
+ if pool.addresses.Contains(l.ip) {
+ num++
+ }
+ }
+ return num
+}
+
+func calcPoolUtilizationPercentage(pool ipPool, leases int64) float64 {
+ size := pool.addresses.Size()
+ if leases == 0 || !size.IsInt64() {
+ return 0
+ }
+ if size.Int64() == 0 {
+ return 100
+ }
+ return float64(leases) / float64(size.Int64()) * 100
+}
+
+func removeInactiveLeases(leases []leaseEntry) (active []leaseEntry) {
+ active = leases[:0]
+ for _, l := range leases {
+ if l.bindingState == "active" {
+ active = append(active, l)
+ }
+ }
+ return active
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/config_schema.json b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/config_schema.json
new file mode 100644
index 000000000..e357fd86d
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/config_schema.json
@@ -0,0 +1,70 @@
+{
+ "jsonSchema": {
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "ISC DHCP collector configuration.",
+ "type": "object",
+ "properties": {
+ "update_every": {
+ "title": "Update every",
+ "description": "Data collection interval, measured in seconds.",
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ },
+ "leases_path": {
+ "title": "Leases file",
+ "description": "File path to the ISC DHCP server's lease database.",
+ "type": "string",
+ "default": "/var/lib/dhcp/dhcpd.leases",
+ "pattern": "^$|^/"
+ },
+ "pools": {
+ "title": "IP pools",
+ "description": "A list of IP pools to monitor. Each pool consists of a descriptive name and corresponding IP ranges.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "title": "IP pool",
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "name": {
+ "title": "Name",
+ "description": "A descriptive name for the IP pool.",
+ "type": "string"
+ },
+ "networks": {
+ "title": "Networks",
+ "description": "A space-separated list of [IP ranges](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/iprange#supported-formats) for the pool.",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "networks"
+ ]
+ },
+ "minItems": 1,
+ "uniqueItems": true,
+ "additionalItems": false
+ }
+ },
+ "required": [
+ "leases_path",
+ "pools"
+ ],
+ "additionalProperties": false,
+ "patternProperties": {
+ "^name$": {}
+ }
+ },
+ "uiSchema": {
+ "uiOptions": {
+ "fullPage": true
+ }
+ }
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/init.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/init.go
new file mode 100644
index 000000000..861ded398
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/init.go
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+ "github.com/netdata/netdata/go/go.d.plugin/pkg/iprange"
+)
+
+type ipPool struct {
+ name string
+ addresses iprange.Pool
+}
+
+func (d *DHCPd) validateConfig() error {
+ if d.Config.LeasesPath == "" {
+ return errors.New("'lease_path' parameter not set")
+ }
+ if len(d.Config.Pools) == 0 {
+ return errors.New("'pools' parameter not set")
+ }
+ for i, cfg := range d.Config.Pools {
+ if cfg.Name == "" {
+ return fmt.Errorf("'pools[%d]->pool.name' parameter not set", i+1)
+ }
+ if cfg.Networks == "" {
+ return fmt.Errorf("'pools[%d]->pool.networks' parameter not set", i+1)
+ }
+ }
+ return nil
+}
+
+func (d *DHCPd) initPools() ([]ipPool, error) {
+ var pools []ipPool
+
+ for i, cfg := range d.Pools {
+ ipRange, err := iprange.ParseRanges(cfg.Networks)
+ if err != nil {
+ return nil, fmt.Errorf("parse pools[%d]->pool.networks '%s' ('%s'): %v", i+1, cfg.Name, cfg.Networks, err)
+ }
+ if len(ipRange) == 0 {
+ continue
+ }
+
+ pool := ipPool{name: cfg.Name, addresses: ipRange}
+ pools = append(pools, pool)
+ }
+
+ return pools, nil
+}
+
+func (d *DHCPd) initCharts(pools []ipPool) (*module.Charts, error) {
+ charts := &module.Charts{}
+
+ if err := charts.Add(activeLeasesTotalChart.Copy()); err != nil {
+ return nil, err
+ }
+
+ for _, pool := range pools {
+ poolCharts := dhcpPoolChartsTmpl.Copy()
+
+ for _, chart := range *poolCharts {
+ chart.ID = fmt.Sprintf(chart.ID, cleanPoolNameForChart(pool.name))
+ chart.Labels = []module.Label{
+ {Key: "dhcp_pool_name", Value: pool.name},
+ }
+ for _, dim := range chart.Dims {
+ dim.ID = fmt.Sprintf(dim.ID, pool.name)
+ }
+ }
+
+ if err := charts.Add(*poolCharts...); err != nil {
+ return nil, err
+ }
+ }
+
+ return charts, nil
+}
+
+func cleanPoolNameForChart(name string) string {
+ name = strings.ReplaceAll(name, " ", "_")
+ name = strings.ReplaceAll(name, ".", "_")
+ return name
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/integrations/isc_dhcp.md b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/integrations/isc_dhcp.md
new file mode 100644
index 000000000..29d657c8d
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/integrations/isc_dhcp.md
@@ -0,0 +1,193 @@
+<!--startmeta
+custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/isc_dhcpd/README.md"
+meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/isc_dhcpd/metadata.yaml"
+sidebar_label: "ISC DHCP"
+learn_status: "Published"
+learn_rel_path: "Collecting Metrics/DNS and DHCP Servers"
+most_popular: False
+message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
+endmeta-->
+
+# ISC DHCP
+
+
+<img src="https://netdata.cloud/img/isc.png" width="150"/>
+
+
+Plugin: go.d.plugin
+Module: isc_dhcpd
+
+<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
+
+## Overview
+
+This collector monitors ISC DHCP lease usage by reading the DHCP client lease database (dhcpd.leases).
+
+
+
+
+This collector is supported on all platforms.
+
+This collector supports collecting metrics from multiple instances of this integration, including remote instances.
+
+
+### Default Behavior
+
+#### Auto-Detection
+
+This integration doesn't support auto-detection.
+
+#### Limits
+
+The default configuration for this integration does not impose any limits on data collection.
+
+#### Performance Impact
+
+The default configuration for this integration is not expected to impose a significant performance impact on the system.
+
+
+## Metrics
+
+Metrics grouped by *scope*.
+
+The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
+
+
+
+### Per ISC DHCP instance
+
+These metrics refer to the entire monitored application.
+
+This scope has no labels.
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| isc_dhcpd.active_leases_total | active | leases |
+
+### Per ISC DHCP instance
+
+These metrics refer to the DHCP pool.
+
+Labels:
+
+| Label | Description |
+|:-----------|:----------------|
+| dhcp_pool_name | The DHCP pool name defined in the collector configuration. |
+
+Metrics:
+
+| Metric | Dimensions | Unit |
+|:------|:----------|:----|
+| isc_dhcpd.dhcp_pool_utilization | utilization | percent |
+| isc_dhcpd.dhcp_pool_active_leases | active | leases |
+
+
+
+## Alerts
+
+There are no alerts configured by default for this integration.
+
+
+## Setup
+
+### Prerequisites
+
+No action required.
+
+### Configuration
+
+#### File
+
+The configuration file name for this integration is `go.d/isc_dhcpd.conf`.
+
+
+You can edit the configuration file using the `edit-config` script from the
+Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
+
+```bash
+cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
+sudo ./edit-config go.d/isc_dhcpd.conf
+```
+#### Options
+
+The following options can be defined globally: update_every, autodetection_retry.
+
+
+<details open><summary>Config options</summary>
+
+| Name | Description | Default | Required |
+|:----|:-----------|:-------|:--------:|
+| update_every | Data collection frequency. | 1 | no |
+| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no |
+| leases_path | Path to DHCP client lease database. | /var/lib/dhcp/dhcpd.leases | no |
+| pools | List of IP pools to monitor. | | yes |
+
+##### pools
+
+List of IP pools to monitor.
+
+- IP range syntax: see [supported formats](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/iprange#supported-formats).
+- Syntax:
+
+```yaml
+pools:
+ - name: "POOL_NAME1"
+ networks: "SPACE SEPARATED LIST OF IP RANGES"
+ - name: "POOL_NAME2"
+ networks: "SPACE SEPARATED LIST OF IP RANGES"
+```
+
+
+</details>
+
+#### Examples
+
+##### Basic
+
+A basic example configuration.
+
+<details open><summary>Config</summary>
+
+```yaml
+jobs:
+ - name: local
+ pools:
+ - name: lan
+ networks: "192.168.0.0/24 192.168.1.0/24 192.168.2.0/24"
+ - name: wifi
+ networks: "10.0.0.0/24"
+
+```
+</details>
+
+
+
+## Troubleshooting
+
+### Debug Mode
+
+To troubleshoot issues with the `isc_dhcpd` collector, run the `go.d.plugin` with the debug option enabled. The output
+should give you clues as to why the collector isn't working.
+
+- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
+ your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
+
+ ```bash
+ cd /usr/libexec/netdata/plugins.d/
+ ```
+
+- Switch to the `netdata` user.
+
+ ```bash
+ sudo -u netdata -s
+ ```
+
+- Run the `go.d.plugin` to debug the collector:
+
+ ```bash
+ ./go.d.plugin -d -m isc_dhcpd
+ ```
+
+
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd.go
new file mode 100644
index 000000000..c51abc75b
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd.go
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ _ "embed"
+ "errors"
+ "time"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+)
+
+//go:embed "config_schema.json"
+var configSchema string
+
+func init() {
+ module.Register("isc_dhcpd", module.Creator{
+ JobConfigSchema: configSchema,
+ Defaults: module.Defaults{
+ UpdateEvery: 1,
+ },
+ Create: func() module.Module { return New() },
+ Config: func() any { return &Config{} },
+ })
+}
+
+func New() *DHCPd {
+ return &DHCPd{
+ Config: Config{
+ LeasesPath: "/var/lib/dhcp/dhcpd.leases",
+ },
+
+ collected: make(map[string]int64),
+ }
+}
+
+type (
+ Config struct {
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
+ LeasesPath string `yaml:"leases_path" json:"leases_path"`
+ // TODO: parse config file to extract configured pool
+ Pools []PoolConfig `yaml:"pools" json:"pools"`
+ }
+ PoolConfig struct {
+ Name string `yaml:"name" json:"name"`
+ Networks string `yaml:"networks" json:"networks"`
+ }
+)
+
+type DHCPd struct {
+ module.Base
+ Config `yaml:",inline" json:""`
+
+ charts *module.Charts
+
+ pools []ipPool
+ leasesModTime time.Time
+ collected map[string]int64
+}
+
+func (d *DHCPd) Configuration() any {
+ return d.Config
+}
+
+func (d *DHCPd) Init() error {
+ err := d.validateConfig()
+ if err != nil {
+ d.Errorf("config validation: %v", err)
+ return err
+ }
+
+ pools, err := d.initPools()
+ if err != nil {
+ d.Errorf("ip pools init: %v", err)
+ return err
+ }
+ d.pools = pools
+
+ charts, err := d.initCharts(pools)
+ if err != nil {
+ d.Errorf("charts init: %v", err)
+ return err
+ }
+ d.charts = charts
+
+ d.Debugf("monitoring leases file: %v", d.Config.LeasesPath)
+ d.Debugf("monitoring ip pools: %v", d.Config.Pools)
+
+ return nil
+}
+
+func (d *DHCPd) Check() error {
+ mx, err := d.collect()
+ if err != nil {
+ d.Error(err)
+ return err
+ }
+ if len(mx) == 0 {
+ return errors.New("no metrics collected")
+ }
+ return nil
+}
+
+func (d *DHCPd) Charts() *module.Charts {
+ return d.charts
+}
+
+func (d *DHCPd) Collect() map[string]int64 {
+ mx, err := d.collect()
+ if err != nil {
+ d.Error(err)
+ }
+
+ if len(mx) == 0 {
+ return nil
+ }
+
+ return mx
+}
+
+func (d *DHCPd) Cleanup() {}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd_test.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd_test.go
new file mode 100644
index 000000000..d91dfca15
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/isc_dhcpd_test.go
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ "os"
+ "testing"
+
+ "github.com/netdata/netdata/go/go.d.plugin/agent/module"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+var (
+ dataConfigJSON, _ = os.ReadFile("testdata/config.json")
+ dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
+)
+
+func Test_testDataIsValid(t *testing.T) {
+ for name, data := range map[string][]byte{
+ "dataConfigJSON": dataConfigJSON,
+ "dataConfigYAML": dataConfigYAML,
+ } {
+ require.NotNil(t, data, name)
+ }
+}
+
+func TestDHCPd_ConfigurationSerialize(t *testing.T) {
+ module.TestConfigurationSerialize(t, &DHCPd{}, dataConfigJSON, dataConfigYAML)
+}
+
+func TestDHCPd_Cleanup(t *testing.T) {
+ assert.NotPanics(t, New().Cleanup)
+}
+
+func TestDHCPd_Init(t *testing.T) {
+ tests := map[string]struct {
+ config Config
+ wantFail bool
+ }{
+ "default": {
+ wantFail: true,
+ config: New().Config,
+ },
+ "'leases_path' not set": {
+ wantFail: true,
+ config: Config{
+ LeasesPath: "",
+ Pools: []PoolConfig{
+ {Name: "test", Networks: "10.220.252.0/24"},
+ },
+ },
+ },
+ "'pools' not set": {
+ wantFail: true,
+ config: Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4",
+ },
+ },
+ "'pools->pool.networks' invalid syntax": {
+ wantFail: true,
+ config: Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4",
+ Pools: []PoolConfig{
+ {Name: "test", Networks: "10.220.252./24"},
+ },
+ }},
+ "ok config ('leases_path' and 'pools' are set)": {
+ config: Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4",
+ Pools: []PoolConfig{
+ {Name: "test", Networks: "10.220.252.0/24"},
+ },
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ dhcpd := New()
+ dhcpd.Config = test.config
+
+ if test.wantFail {
+ assert.Error(t, dhcpd.Init())
+ } else {
+ assert.NoError(t, dhcpd.Init())
+ }
+ })
+ }
+}
+
+func TestDHCPd_Check(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() *DHCPd
+ wantFail bool
+ }{
+ "lease db not exists": {prepare: prepareDHCPdLeasesNotExists, wantFail: true},
+ "lease db is an empty file": {prepare: prepareDHCPdLeasesEmpty},
+ "lease db ipv4": {prepare: prepareDHCPdLeasesIPv4},
+ "lease db ipv4 with only inactive leases": {prepare: prepareDHCPdLeasesIPv4Inactive},
+ "lease db ipv4 with backup leases": {prepare: prepareDHCPdLeasesIPv4Backup},
+ "lease db ipv6": {prepare: prepareDHCPdLeasesIPv6},
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ dhcpd := test.prepare()
+ require.NoError(t, dhcpd.Init())
+
+ if test.wantFail {
+ assert.Error(t, dhcpd.Check())
+ } else {
+ assert.NoError(t, dhcpd.Check())
+ }
+ })
+ }
+}
+
+func TestDHCPd_Charts(t *testing.T) {
+ dhcpd := New()
+ dhcpd.LeasesPath = "leases_path"
+ dhcpd.Pools = []PoolConfig{
+ {Name: "name", Networks: "192.0.2.0/24"},
+ }
+ require.NoError(t, dhcpd.Init())
+
+ assert.NotNil(t, dhcpd.Charts())
+}
+
+func TestDHCPd_Collect(t *testing.T) {
+ tests := map[string]struct {
+ prepare func() *DHCPd
+ wantCollected map[string]int64
+ }{
+ "lease db not exists": {
+ prepare: prepareDHCPdLeasesNotExists,
+ wantCollected: nil,
+ },
+ "lease db is an empty file": {
+ prepare: prepareDHCPdLeasesEmpty,
+ wantCollected: map[string]int64{
+ "active_leases_total": 0,
+ "dhcp_pool_net1_active_leases": 0,
+ "dhcp_pool_net1_utilization": 0,
+ "dhcp_pool_net2_active_leases": 0,
+ "dhcp_pool_net2_utilization": 0,
+ "dhcp_pool_net3_active_leases": 0,
+ "dhcp_pool_net3_utilization": 0,
+ "dhcp_pool_net4_active_leases": 0,
+ "dhcp_pool_net4_utilization": 0,
+ "dhcp_pool_net5_active_leases": 0,
+ "dhcp_pool_net5_utilization": 0,
+ "dhcp_pool_net6_active_leases": 0,
+ "dhcp_pool_net6_utilization": 0,
+ },
+ },
+ "lease db ipv4": {
+ prepare: prepareDHCPdLeasesIPv4,
+ wantCollected: map[string]int64{
+ "active_leases_total": 5,
+ "dhcp_pool_net1_active_leases": 2,
+ "dhcp_pool_net1_utilization": 158,
+ "dhcp_pool_net2_active_leases": 1,
+ "dhcp_pool_net2_utilization": 39,
+ "dhcp_pool_net3_active_leases": 0,
+ "dhcp_pool_net3_utilization": 0,
+ "dhcp_pool_net4_active_leases": 1,
+ "dhcp_pool_net4_utilization": 79,
+ "dhcp_pool_net5_active_leases": 0,
+ "dhcp_pool_net5_utilization": 0,
+ "dhcp_pool_net6_active_leases": 1,
+ "dhcp_pool_net6_utilization": 39,
+ },
+ },
+ "lease db ipv4 with only inactive leases": {
+ prepare: prepareDHCPdLeasesIPv4Inactive,
+ wantCollected: map[string]int64{
+ "active_leases_total": 0,
+ "dhcp_pool_net1_active_leases": 0,
+ "dhcp_pool_net1_utilization": 0,
+ "dhcp_pool_net2_active_leases": 0,
+ "dhcp_pool_net2_utilization": 0,
+ "dhcp_pool_net3_active_leases": 0,
+ "dhcp_pool_net3_utilization": 0,
+ "dhcp_pool_net4_active_leases": 0,
+ "dhcp_pool_net4_utilization": 0,
+ "dhcp_pool_net5_active_leases": 0,
+ "dhcp_pool_net5_utilization": 0,
+ "dhcp_pool_net6_active_leases": 0,
+ "dhcp_pool_net6_utilization": 0,
+ },
+ },
+ "lease db ipv4 with backup leases": {
+ prepare: prepareDHCPdLeasesIPv4Backup,
+ wantCollected: map[string]int64{
+ "active_leases_total": 2,
+ "dhcp_pool_net1_active_leases": 1,
+ "dhcp_pool_net1_utilization": 79,
+ "dhcp_pool_net2_active_leases": 0,
+ "dhcp_pool_net2_utilization": 0,
+ "dhcp_pool_net3_active_leases": 0,
+ "dhcp_pool_net3_utilization": 0,
+ "dhcp_pool_net4_active_leases": 1,
+ "dhcp_pool_net4_utilization": 79,
+ "dhcp_pool_net5_active_leases": 0,
+ "dhcp_pool_net5_utilization": 0,
+ "dhcp_pool_net6_active_leases": 0,
+ "dhcp_pool_net6_utilization": 0,
+ },
+ },
+ "lease db ipv6": {
+ prepare: prepareDHCPdLeasesIPv6,
+ wantCollected: map[string]int64{
+ "active_leases_total": 6,
+ "dhcp_pool_net1_active_leases": 6,
+ "dhcp_pool_net1_utilization": 5454,
+ "dhcp_pool_net2_active_leases": 0,
+ "dhcp_pool_net2_utilization": 0,
+ },
+ },
+ }
+
+ for name, test := range tests {
+ t.Run(name, func(t *testing.T) {
+ dhcpd := test.prepare()
+ require.NoError(t, dhcpd.Init())
+
+ collected := dhcpd.Collect()
+
+ assert.Equal(t, test.wantCollected, collected)
+ if len(collected) > 0 {
+ ensureCollectedHasAllChartsDimsVarsIDs(t, dhcpd, collected)
+ }
+ })
+ }
+}
+
+func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, dhcpd *DHCPd, collected map[string]int64) {
+ for _, chart := range *dhcpd.Charts() {
+ if chart.Obsolete {
+ continue
+ }
+ for _, dim := range chart.Dims {
+ _, ok := collected[dim.ID]
+ assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID)
+ }
+ for _, v := range chart.Vars {
+ _, ok := collected[v.ID]
+ assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID)
+ }
+ }
+}
+
+func prepareDHCPdLeasesNotExists() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_not_exists",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "192.168.3.0/25"},
+ {Name: "net2", Networks: "10.254.251.0/24"},
+ {Name: "net3", Networks: "10.254.252.0/24"},
+ {Name: "net4", Networks: "10.254.253.0/25"},
+ {Name: "net5", Networks: "10.254.254.0/25"},
+ {Name: "net6", Networks: "10.254.255.0/24"},
+ },
+ }
+ return dhcpd
+}
+
+func prepareDHCPdLeasesEmpty() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_empty",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "192.168.3.0/25"},
+ {Name: "net2", Networks: "10.254.251.0/24"},
+ {Name: "net3", Networks: "10.254.252.0/24"},
+ {Name: "net4", Networks: "10.254.253.0/25"},
+ {Name: "net5", Networks: "10.254.254.0/25"},
+ {Name: "net6", Networks: "10.254.255.0/24"},
+ },
+ }
+ return dhcpd
+}
+
+func prepareDHCPdLeasesIPv4() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "192.168.3.0/25"},
+ {Name: "net2", Networks: "10.254.251.0/24"},
+ {Name: "net3", Networks: "10.254.252.0/24"},
+ {Name: "net4", Networks: "10.254.253.0/25"},
+ {Name: "net5", Networks: "10.254.254.0/25"},
+ {Name: "net6", Networks: "10.254.255.0/24"},
+ },
+ }
+ return dhcpd
+}
+
+func prepareDHCPdLeasesIPv4Backup() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4_backup",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "192.168.3.0/25"},
+ {Name: "net2", Networks: "10.254.251.0/24"},
+ {Name: "net3", Networks: "10.254.252.0/24"},
+ {Name: "net4", Networks: "10.254.253.0/25"},
+ {Name: "net5", Networks: "10.254.254.0/25"},
+ {Name: "net6", Networks: "10.254.255.0/24"},
+ },
+ }
+ return dhcpd
+}
+
+func prepareDHCPdLeasesIPv4Inactive() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv4_inactive",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "192.168.3.0/25"},
+ {Name: "net2", Networks: "10.254.251.0/24"},
+ {Name: "net3", Networks: "10.254.252.0/24"},
+ {Name: "net4", Networks: "10.254.253.0/25"},
+ {Name: "net5", Networks: "10.254.254.0/25"},
+ {Name: "net6", Networks: "10.254.255.0/24"},
+ },
+ }
+ return dhcpd
+}
+
+func prepareDHCPdLeasesIPv6() *DHCPd {
+ dhcpd := New()
+ dhcpd.Config = Config{
+ LeasesPath: "testdata/dhcpd.leases_ipv6",
+ Pools: []PoolConfig{
+ {Name: "net1", Networks: "2001:db8::-2001:db8::a"},
+ {Name: "net2", Networks: "2001:db8:0:1::-2001:db8:0:1::a"},
+ },
+ }
+ return dhcpd
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/metadata.yaml b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/metadata.yaml
new file mode 100644
index 000000000..e6e11d72e
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/metadata.yaml
@@ -0,0 +1,135 @@
+plugin_name: go.d.plugin
+modules:
+ - meta:
+ id: collector-go.d.plugin-isc_dhcpd
+ plugin_name: go.d.plugin
+ module_name: isc_dhcpd
+ monitored_instance:
+ name: ISC DHCP
+ link: https://www.isc.org/dhcp/
+ categories:
+ - data-collection.dns-and-dhcp-servers
+ icon_filename: isc.png
+ keywords:
+ - dhcpd
+ - dhcp
+ most_popular: false
+ info_provided_to_referring_integrations:
+ description: ""
+ related_resources:
+ integrations:
+ list: []
+ overview:
+ data_collection:
+ metrics_description: |
+ This collector monitors ISC DHCP lease usage by reading the DHCP client lease database (dhcpd.leases).
+ method_description: ""
+ default_behavior:
+ auto_detection:
+ description: ""
+ limits:
+ description: ""
+ performance_impact:
+ description: ""
+ additional_permissions:
+ description: ""
+ multi_instance: true
+ supported_platforms:
+ include: []
+ exclude: []
+ setup:
+ prerequisites:
+ list: []
+ configuration:
+ file:
+ name: go.d/isc_dhcpd.conf
+ options:
+ description: |
+ The following options can be defined globally: update_every, autodetection_retry.
+ folding:
+ title: Config options
+ enabled: true
+ list:
+ - name: update_every
+ description: Data collection frequency.
+ default_value: 1
+ required: false
+ - name: autodetection_retry
+ description: Recheck interval in seconds. Zero means no recheck will be scheduled.
+ default_value: 0
+ required: false
+ - name: leases_path
+ description: Path to DHCP client lease database.
+ default_value: /var/lib/dhcp/dhcpd.leases
+ required: false
+ - name: pools
+ description: List of IP pools to monitor.
+ default_value: ""
+ required: true
+ detailed_description: |
+ List of IP pools to monitor.
+
+ - IP range syntax: see [supported formats](https://github.com/netdata/netdata/tree/master/src/go/collectors/go.d.plugin/pkg/iprange#supported-formats).
+ - Syntax:
+
+ ```yaml
+ pools:
+ - name: "POOL_NAME1"
+ networks: "SPACE SEPARATED LIST OF IP RANGES"
+ - name: "POOL_NAME2"
+ networks: "SPACE SEPARATED LIST OF IP RANGES"
+ ```
+ examples:
+ folding:
+ title: Config
+ enabled: true
+ list:
+ - name: Basic
+ description: A basic example configuration.
+ config: |
+ jobs:
+ - name: local
+ pools:
+ - name: lan
+ networks: "192.168.0.0/24 192.168.1.0/24 192.168.2.0/24"
+ - name: wifi
+ networks: "10.0.0.0/24"
+ troubleshooting:
+ problems:
+ list: []
+ alerts: []
+ metrics:
+ folding:
+ title: Metrics
+ enabled: false
+ description: ""
+ availability: []
+ scopes:
+ - name: global
+ description: These metrics refer to the entire monitored application.
+ labels: []
+ metrics:
+ - name: isc_dhcpd.active_leases_total
+ description: Active Leases Total
+ unit: leases
+ chart_type: line
+ dimensions:
+ - name: active
+ - name: global
+ description: These metrics refer to the DHCP pool.
+ labels:
+ - name: dhcp_pool_name
+ description: The DHCP pool name defined in the collector configuration.
+ metrics:
+ - name: isc_dhcpd.dhcp_pool_utilization
+ description: DHCP Pool Utilization
+ unit: percent
+ chart_type: area
+ dimensions:
+ - name: utilization
+ - name: isc_dhcpd.dhcp_pool_active_leases
+ description: Active Leases Total
+ unit: leases
+ chart_type: line
+ dimensions:
+ - name: active
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/parse.go b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/parse.go
new file mode 100644
index 000000000..cb4161745
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/parse.go
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package isc_dhcpd
+
+import (
+ "bufio"
+ "bytes"
+ "net"
+ "os"
+)
+
+/*
+Documentation (v4.4): https://kb.isc.org/docs/en/isc-dhcp-44-manual-pages-dhcpdleases
+
+DHCPv4 prepare declaration:
+ prepare ip-address {
+ statements...
+ }
+
+DHCPv6 prepare declaration:
+ ia_ta IAID_DUID {
+ cltt date;
+ iaaddr ipv6-address {
+ statements...
+ }
+ }
+ ia_na IAID_DUID {
+ cltt date;
+ iaaddr ipv6-address {
+ statements...
+ }
+ }
+ ia_pd IAID_DUID {
+ cltt date;
+ iaprefix ipv6-address/prefix-length {
+ statements...
+ }
+ }
+*/
+
+type leaseEntry struct {
+ ip net.IP
+ bindingState string
+}
+
+func (l leaseEntry) hasIP() bool { return l.ip != nil }
+func (l leaseEntry) hasBindingState() bool { return l.bindingState != "" }
+
+func parseDHCPdLeasesFile(filepath string) ([]leaseEntry, error) {
+ f, err := os.Open(filepath)
+ if err != nil {
+ return nil, err
+ }
+ defer func() { _ = f.Close() }()
+
+ leasesSet := make(map[string]leaseEntry)
+ l := leaseEntry{}
+ sc := bufio.NewScanner(f)
+
+ for sc.Scan() {
+ bs := bytes.TrimSpace(sc.Bytes())
+ switch {
+ case !l.hasIP() && bytes.HasPrefix(bs, []byte("lease")):
+ // "lease 192.168.0.1 {" => "192.168.0.1"
+ s := string(bs)
+ l.ip = net.ParseIP(s[6 : len(s)-2])
+ case !l.hasIP() && bytes.HasPrefix(bs, []byte("iaaddr")):
+ // "iaaddr 1985:470:1f0b:c9a::001 {" => "1985:470:1f0b:c9a::001"
+ s := string(bs)
+ l.ip = net.ParseIP(s[7 : len(s)-2])
+ case l.hasIP() && !l.hasBindingState() && bytes.HasPrefix(bs, []byte("binding state")):
+ // "binding state active;" => "active"
+ s := string(bs)
+ l.bindingState = s[14 : len(s)-1]
+ case bytes.HasPrefix(bs, []byte("}")):
+ if l.hasIP() && l.hasBindingState() {
+ leasesSet[l.ip.String()] = l
+ }
+ l = leaseEntry{}
+ }
+ }
+
+ if len(leasesSet) == 0 {
+ return nil, nil
+ }
+
+ leases := make([]leaseEntry, 0, len(leasesSet))
+ for _, l := range leasesSet {
+ leases = append(leases, l)
+ }
+ return leases, nil
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.json b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.json
new file mode 100644
index 000000000..945f8865e
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.json
@@ -0,0 +1,10 @@
+{
+ "update_every": 123,
+ "leases_path": "ok",
+ "pools": [
+ {
+ "name": "ok",
+ "networks": "ok"
+ }
+ ]
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.yaml
new file mode 100644
index 000000000..a33defc55
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/config.yaml
@@ -0,0 +1,5 @@
+update_every: 123
+leases_path: "ok"
+pools:
+ - name: "ok"
+ networks: "ok"
diff --git a/fluent-bit/lib/jansson-e23f558/test/suites/invalid/empty/input b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_empty
index e69de29bb..e69de29bb 100644
--- a/fluent-bit/lib/jansson-e23f558/test/suites/invalid/empty/input
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_empty
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4 b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4
new file mode 100644
index 000000000..08e0e3f20
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4
@@ -0,0 +1,370 @@
+# The format of this file is documented in the dhcpd.leases(5) manual page.
+# This lease file was written by isc-dhcp-4.3.1
+
+lease 10.254.252.2 {
+ starts 3 2014/07/23 07:32:16;
+ ends 3 2014/07/23 09:12:16;
+ tstp 3 2014/07/23 09:12:16;
+ cltt 3 2014/07/23 07:32:16;
+ binding state free;
+ hardware ethernet f0:de:f1:89:24:1f;
+ uid "\001\360\336\361\211$\037";
+}
+lease 10.254.252.3 {
+ starts 5 2014/11/28 05:49:01;
+ ends 5 2014/11/28 07:29:01;
+ tstp 5 2014/11/28 07:29:01;
+ cltt 5 2014/11/28 05:49:01;
+ binding state free;
+ hardware ethernet c0:4a:00:00:f5:fa;
+ uid "\001\300J\000\000\365\372";
+}
+lease 10.254.252.4 {
+ starts 5 2016/03/11 01:03:59;
+ ends 5 2016/03/11 02:33:20;
+ tstp 5 2016/03/11 02:33:20;
+ cltt 5 2016/03/11 01:12:33;
+ binding state free;
+ hardware ethernet 00:1c:c0:7a:38:3f;
+ uid "\001\000\034\300z8?";
+ set vendor-class-identifier = "MSFT 5.0";
+}
+lease 10.254.252.5 {
+ starts 1 2016/09/05 23:53:19;
+ ends 2 2016/09/06 01:33:19;
+ tstp 2 2016/09/06 01:33:19;
+ cltt 1 2016/09/05 23:53:19;
+ binding state free;
+ hardware ethernet 28:28:5d:65:30:ef;
+ uid "\001((]e0\357";
+}
+lease 10.254.252.6 {
+ starts 4 2016/09/29 01:41:23;
+ ends 4 2016/09/29 03:21:23;
+ tstp 4 2016/09/29 03:21:23;
+ cltt 4 2016/09/29 01:41:23;
+ binding state free;
+ hardware ethernet 04:bf:6d:94:1b:0d;
+ uid "\001\004\277m\224\033\015";
+}
+lease 10.254.252.7 {
+ starts 1 2016/10/03 08:23:14;
+ ends 1 2016/10/03 10:03:14;
+ tstp 1 2016/10/03 10:03:14;
+ cltt 1 2016/10/03 08:23:14;
+ binding state free;
+ hardware ethernet ec:22:80:f7:3f:44;
+ uid "\001\354\"\200\367?D";
+}
+lease 10.254.252.8 {
+ starts 5 2016/10/07 05:43:11;
+ ends 5 2016/10/07 05:58:31;
+ tstp 5 2016/10/07 05:58:31;
+ cltt 5 2016/10/07 05:43:11;
+ binding state free;
+ hardware ethernet 70:62:b8:bf:b5:b3;
+ uid "\001pb\270\277\265\263";
+}
+lease 192.168.3.15 {
+ starts 2 2019/01/08 06:29:58;
+ ends 2 2019/01/08 08:09:58;
+ tstp 2 2019/01/08 08:09:58;
+ cltt 2 2019/01/08 06:29:58;
+ binding state free;
+ hardware ethernet a8:f9:4b:20:99:9c;
+ uid "\001\250\371K \231\234";
+}
+lease 192.168.3.18 {
+ starts 2 2020/03/10 01:46:07;
+ ends 2 2020/03/10 03:22:21;
+ tstp 2 2020/03/10 03:22:21;
+ cltt 2 2020/03/10 01:46:08;
+ binding state free;
+ hardware ethernet 04:bf:6d:0d:e2:35;
+ uid "\001\004\277m\015\3425";
+ set vendor-class-identifier = "ndhcpc";
+}
+lease 192.168.3.11 {
+ starts 6 2020/10/03 07:52:36;
+ ends 6 2020/10/03 09:32:36;
+ cltt 6 2020/10/03 07:52:36;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 60:a4:4c:3f:6e:78;
+ uid "\001`\244L?nx";
+}
+lease 192.168.3.10 {
+ starts 6 2020/10/03 08:18:50;
+ ends 6 2020/10/03 09:58:50;
+ cltt 6 2020/10/03 08:18:50;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 70:62:b8:bf:b5:b3;
+ uid "\001pb\270\277\265\263";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.251.101 {
+ starts 0 2017/03/12 22:11:59;
+ ends 0 2017/03/12 23:51:58;
+ tstp 0 2017/03/12 23:51:58;
+ cltt 0 2017/03/12 22:11:59;
+ binding state free;
+ hardware ethernet b4:ce:f6:01:83:73;
+ set vendor-class-identifier = "dhcpcd-5.5.6";
+}
+lease 10.254.251.102 {
+ starts 5 2017/05/19 06:07:39;
+ ends 5 2017/05/19 07:47:39;
+ tstp 5 2017/05/19 07:47:39;
+ cltt 5 2017/05/19 06:07:39;
+ binding state free;
+ hardware ethernet 34:51:c9:4c:40:c9;
+ uid "\0014Q\311L@\311";
+}
+lease 10.254.251.103 {
+ starts 2 2018/04/24 13:18:00;
+ ends 2 2018/04/24 14:58:00;
+ tstp 2 2018/04/24 14:58:00;
+ cltt 2 2018/04/24 13:18:00;
+ binding state free;
+ hardware ethernet 70:8a:09:da:74:d0;
+ set vendor-class-identifier = "dhcpcd-5.5.6";
+}
+lease 10.254.251.104 {
+ starts 2 2018/04/24 12:54:27;
+ ends 3 2018/04/25 06:47:20;
+ tstp 3 2018/04/25 06:47:20;
+ cltt 2 2018/04/24 12:54:28;
+ binding state free;
+ hardware ethernet 78:a3:e4:e8:12:1f;
+ uid "\001x\243\344\350\022\037";
+}
+lease 10.254.251.100 {
+ starts 6 2020/10/03 07:58:45;
+ ends 6 2020/10/03 09:38:45;
+ cltt 6 2020/10/03 07:58:45;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 74:ea:3a:a6:a9:c7;
+ uid "\001t\352:\246\251\307";
+ set vendor-class-identifier = "MSFT 5.0";
+ client-hostname "TL-WR741N";
+}
+lease 10.254.255.104 {
+ starts 1 2017/07/10 09:35:24;
+ ends 1 2017/07/10 09:37:24;
+ tstp 1 2017/07/10 09:37:24;
+ cltt 1 2017/07/10 09:35:24;
+ binding state free;
+ hardware ethernet 50:85:69:11:b6:ff;
+ uid "\001P\205i\021\266\377";
+}
+lease 10.254.255.102 {
+ starts 3 2017/08/16 22:01:09;
+ ends 3 2017/08/16 23:41:09;
+ tstp 3 2017/08/16 23:41:09;
+ cltt 3 2017/08/16 22:01:09;
+ binding state free;
+ hardware ethernet c8:d3:a3:54:31:3a;
+ uid "\001\310\323\243T1:";
+}
+lease 10.254.255.103 {
+ starts 0 2018/12/16 00:54:07;
+ ends 0 2018/12/16 02:34:07;
+ tstp 0 2018/12/16 02:34:07;
+ cltt 0 2018/12/16 00:54:07;
+ binding state free;
+ hardware ethernet 08:c6:b3:01:e8:18;
+ uid "\001\010\306\263\001\350\030";
+ set vendor-class-identifier = "QTCH-QBR1041WUV2";
+}
+lease 10.254.255.100 {
+ starts 2 2018/12/18 09:21:24;
+ ends 2 2018/12/18 10:32:36;
+ tstp 2 2018/12/18 10:32:36;
+ cltt 2 2018/12/18 09:21:30;
+ binding state free;
+ hardware ethernet 70:62:b8:c3:51:a3;
+ uid "\001pb\270\303Q\243";
+}
+lease 10.254.255.105 {
+ starts 5 2019/03/22 07:42:55;
+ ends 5 2019/03/22 09:22:55;
+ tstp 5 2019/03/22 09:22:55;
+ cltt 5 2019/03/22 07:42:55;
+ binding state free;
+ hardware ethernet 58:d5:6e:95:88:30;
+ uid "\001X\325n\225\2100";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.255.101 {
+ starts 6 2020/10/03 07:29:24;
+ ends 6 2020/10/03 09:09:24;
+ cltt 6 2020/10/03 07:29:24;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 28:3b:82:58:f4:58;
+ uid "\001(;\202X\364X";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.253.104 {
+ starts 4 2018/03/15 12:01:12;
+ ends 4 2018/03/15 12:34:35;
+ tstp 4 2018/03/15 12:34:35;
+ cltt 4 2018/03/15 12:02:58;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3d;
+ uid "\001Pd+O\375=";
+ set vendor-class-identifier = "udhcp 1.19.4";
+}
+lease 10.254.253.105 {
+ starts 4 2018/03/15 12:39:46;
+ ends 4 2018/03/15 14:17:39;
+ tstp 4 2018/03/15 14:17:39;
+ cltt 4 2018/03/15 12:39:47;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3d;
+ set vendor-class-identifier = "udhcp 1.19.4";
+}
+lease 10.254.253.101 {
+ starts 5 2018/03/16 11:00:43;
+ ends 5 2018/03/16 12:40:15;
+ tstp 5 2018/03/16 12:40:15;
+ cltt 5 2018/03/16 11:00:43;
+ binding state free;
+ hardware ethernet d0:66:7b:8b:e5:ff;
+ uid "\001\320f{\213\345\377";
+ set vendor-class-identifier = "udhcp 1.14.3-VD Linux VDLinux.1.2.1.x";
+}
+lease 10.254.253.102 {
+ starts 5 2018/03/16 11:26:21;
+ ends 5 2018/03/16 13:06:21;
+ tstp 5 2018/03/16 13:06:21;
+ cltt 5 2018/03/16 11:26:21;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3f;
+ uid "\001Pd+O\375?";
+}
+lease 10.254.253.100 {
+ starts 2 2018/08/21 05:48:43;
+ ends 2 2018/08/21 07:23:13;
+ tstp 2 2018/08/21 07:23:13;
+ cltt 2 2018/08/21 05:48:44;
+ binding state free;
+ hardware ethernet 20:cf:30:ef:8e:a4;
+ uid "\001 \3170\357\216\244";
+ set vendor-class-identifier = "udhcp 0.9.8-asus";
+}
+lease 10.254.253.103 {
+ starts 6 2020/10/03 08:07:02;
+ ends 6 2020/10/03 09:47:02;
+ cltt 6 2020/10/03 08:07:02;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 34:ce:00:03:08:57;
+ uid "\0014\316\000\003\010W";
+ set vendor-class-identifier = "udhcp 1.24.2";
+}
+lease 10.254.254.103 {
+ starts 3 2015/11/11 09:03:11;
+ ends 3 2015/11/11 09:05:11;
+ tstp 3 2015/11/11 09:05:11;
+ cltt 3 2015/11/11 09:03:11;
+ binding state free;
+ hardware ethernet 74:d0:2b:0e:9b:d6;
+}
+lease 10.254.254.104 {
+ starts 0 2017/12/03 15:57:29;
+ ends 0 2017/12/03 17:37:29;
+ tstp 0 2017/12/03 17:37:29;
+ cltt 0 2017/12/03 15:57:29;
+ binding state free;
+ hardware ethernet ac:22:0b:78:00:78;
+ uid "\377\3139\012\307\000\002\000\000\253\021(CC\252e\021\000\017";
+}
+lease 10.254.254.105 {
+ starts 2 2018/06/26 12:30:04;
+ ends 2 2018/06/26 13:09:10;
+ tstp 2 2018/06/26 13:09:10;
+ cltt 2 2018/06/26 12:30:04;
+ binding state free;
+ hardware ethernet cc:2d:e0:3f:bc:5c;
+ uid "\001\314-\340?\274\\";
+}
+lease 10.254.254.101 {
+ starts 3 2018/07/25 09:33:10;
+ ends 3 2018/07/25 11:13:10;
+ tstp 3 2018/07/25 11:13:10;
+ cltt 3 2018/07/25 09:33:10;
+ binding state free;
+ hardware ethernet 74:d0:2b:0e:9b:d6;
+ uid "\001t\320+\016\233\326";
+ set vendor-class-identifier = "MSFT 5.0";
+}
+lease 10.254.254.100 {
+ starts 2 2020/09/22 11:19:29;
+ ends 2 2020/09/22 11:21:29;
+ cltt 2 2020/09/22 11:19:29;
+ binding state free;
+ hardware ethernet 30:45:96:6a:f3:de;
+ uid "\0010E\226j\363\336";
+ client-hostname "Honor_7C-bb23201389a3c44";
+}
+lease 10.254.254.102 {
+ starts 2 2020/09/22 11:25:14;
+ ends 2 2020/09/22 11:27:14;
+ cltt 2 2020/09/22 11:25:14;
+ binding state free;
+ hardware ethernet c8:3d:dc:be:d2:cf;
+ uid "\001\310=\334\276\322\317";
+ client-hostname "Redmi7A-Redmi";
+}
+lease 10.254.255.101 {
+ starts 6 2020/10/03 08:19:24;
+ ends 6 2020/10/03 09:59:24;
+ cltt 6 2020/10/03 08:19:24;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 28:3b:82:58:f4:58;
+ uid "\001(;\202X\364X";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.251.100 {
+ starts 6 2020/10/03 08:48:45;
+ ends 6 2020/10/03 10:28:45;
+ cltt 6 2020/10/03 08:48:45;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 74:ea:3a:a6:a9:c7;
+ uid "\001t\352:\246\251\307";
+ set vendor-class-identifier = "MSFT 5.0";
+ client-hostname "TL-WR741N";
+}
+lease 10.254.253.103 {
+ starts 6 2020/10/03 08:57:02;
+ ends 6 2020/10/03 10:37:02;
+ cltt 6 2020/10/03 08:57:02;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 34:ce:00:03:08:57;
+ uid "\0014\316\000\003\010W";
+ set vendor-class-identifier = "udhcp 1.24.2";
+}
+lease 192.168.3.11 {
+ starts 6 2020/10/03 09:01:22;
+ ends 6 2020/10/03 10:41:22;
+ cltt 6 2020/10/03 09:01:22;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 60:a4:4c:3f:6e:78;
+ uid "\001`\244L?nx";
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_backup b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_backup
new file mode 100644
index 000000000..e822ca846
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_backup
@@ -0,0 +1,39 @@
+# The format of this file is documented in the dhcpd.leases(5) manual page.
+# This lease file was written by isc-dhcp-4.4.2
+
+# authoring-byte-order entry is generated, DO NOT DELETE
+authoring-byte-order little-endian;
+
+lease 10.254.253.103 {
+ starts 6 2020/10/03 08:57:02;
+ ends 6 2020/10/03 10:37:02;
+ cltt 6 2020/10/03 08:57:02;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 34:ce:00:03:08:57;
+ uid "\0014\316\000\003\010W";
+ set vendor-class-identifier = "udhcp 1.24.2";
+}
+lease 192.168.3.1 {
+ starts 6 2018/02/17 01:13:21;
+ tsfp 6 2018/02/17 01:13:21;
+ atsfp 6 2018/02/17 01:13:21;
+ binding state backup;
+}
+lease 192.168.3.11 {
+ starts 6 2020/10/03 09:01:22;
+ ends 6 2020/10/03 10:41:22;
+ cltt 6 2020/10/03 09:01:22;
+ binding state active;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 60:a4:4c:3f:6e:78;
+ uid "\001`\244L?nx";
+}
+lease 192.168.3.2 {
+ starts 6 2018/02/17 01:13:21;
+ tsfp 6 2018/02/17 01:13:21;
+ atsfp 6 2018/02/17 01:13:21;
+ binding state backup;
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_inactive b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_inactive
new file mode 100644
index 000000000..c5aed080f
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv4_inactive
@@ -0,0 +1,370 @@
+# The format of this file is documented in the dhcpd.leases(5) manual page.
+# This lease file was written by isc-dhcp-4.3.1
+
+lease 10.254.252.2 {
+ starts 3 2014/07/23 07:32:16;
+ ends 3 2014/07/23 09:12:16;
+ tstp 3 2014/07/23 09:12:16;
+ cltt 3 2014/07/23 07:32:16;
+ binding state free;
+ hardware ethernet f0:de:f1:89:24:1f;
+ uid "\001\360\336\361\211$\037";
+}
+lease 10.254.252.3 {
+ starts 5 2014/11/28 05:49:01;
+ ends 5 2014/11/28 07:29:01;
+ tstp 5 2014/11/28 07:29:01;
+ cltt 5 2014/11/28 05:49:01;
+ binding state free;
+ hardware ethernet c0:4a:00:00:f5:fa;
+ uid "\001\300J\000\000\365\372";
+}
+lease 10.254.252.4 {
+ starts 5 2016/03/11 01:03:59;
+ ends 5 2016/03/11 02:33:20;
+ tstp 5 2016/03/11 02:33:20;
+ cltt 5 2016/03/11 01:12:33;
+ binding state free;
+ hardware ethernet 00:1c:c0:7a:38:3f;
+ uid "\001\000\034\300z8?";
+ set vendor-class-identifier = "MSFT 5.0";
+}
+lease 10.254.252.5 {
+ starts 1 2016/09/05 23:53:19;
+ ends 2 2016/09/06 01:33:19;
+ tstp 2 2016/09/06 01:33:19;
+ cltt 1 2016/09/05 23:53:19;
+ binding state free;
+ hardware ethernet 28:28:5d:65:30:ef;
+ uid "\001((]e0\357";
+}
+lease 10.254.252.6 {
+ starts 4 2016/09/29 01:41:23;
+ ends 4 2016/09/29 03:21:23;
+ tstp 4 2016/09/29 03:21:23;
+ cltt 4 2016/09/29 01:41:23;
+ binding state free;
+ hardware ethernet 04:bf:6d:94:1b:0d;
+ uid "\001\004\277m\224\033\015";
+}
+lease 10.254.252.7 {
+ starts 1 2016/10/03 08:23:14;
+ ends 1 2016/10/03 10:03:14;
+ tstp 1 2016/10/03 10:03:14;
+ cltt 1 2016/10/03 08:23:14;
+ binding state free;
+ hardware ethernet ec:22:80:f7:3f:44;
+ uid "\001\354\"\200\367?D";
+}
+lease 10.254.252.8 {
+ starts 5 2016/10/07 05:43:11;
+ ends 5 2016/10/07 05:58:31;
+ tstp 5 2016/10/07 05:58:31;
+ cltt 5 2016/10/07 05:43:11;
+ binding state free;
+ hardware ethernet 70:62:b8:bf:b5:b3;
+ uid "\001pb\270\277\265\263";
+}
+lease 192.168.3.15 {
+ starts 2 2019/01/08 06:29:58;
+ ends 2 2019/01/08 08:09:58;
+ tstp 2 2019/01/08 08:09:58;
+ cltt 2 2019/01/08 06:29:58;
+ binding state free;
+ hardware ethernet a8:f9:4b:20:99:9c;
+ uid "\001\250\371K \231\234";
+}
+lease 192.168.3.18 {
+ starts 2 2020/03/10 01:46:07;
+ ends 2 2020/03/10 03:22:21;
+ tstp 2 2020/03/10 03:22:21;
+ cltt 2 2020/03/10 01:46:08;
+ binding state free;
+ hardware ethernet 04:bf:6d:0d:e2:35;
+ uid "\001\004\277m\015\3425";
+ set vendor-class-identifier = "ndhcpc";
+}
+lease 192.168.3.11 {
+ starts 6 2020/10/03 07:52:36;
+ ends 6 2020/10/03 09:32:36;
+ cltt 6 2020/10/03 07:52:36;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 60:a4:4c:3f:6e:78;
+ uid "\001`\244L?nx";
+}
+lease 192.168.3.10 {
+ starts 6 2020/10/03 08:18:50;
+ ends 6 2020/10/03 09:58:50;
+ cltt 6 2020/10/03 08:18:50;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 70:62:b8:bf:b5:b3;
+ uid "\001pb\270\277\265\263";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.251.101 {
+ starts 0 2017/03/12 22:11:59;
+ ends 0 2017/03/12 23:51:58;
+ tstp 0 2017/03/12 23:51:58;
+ cltt 0 2017/03/12 22:11:59;
+ binding state free;
+ hardware ethernet b4:ce:f6:01:83:73;
+ set vendor-class-identifier = "dhcpcd-5.5.6";
+}
+lease 10.254.251.102 {
+ starts 5 2017/05/19 06:07:39;
+ ends 5 2017/05/19 07:47:39;
+ tstp 5 2017/05/19 07:47:39;
+ cltt 5 2017/05/19 06:07:39;
+ binding state free;
+ hardware ethernet 34:51:c9:4c:40:c9;
+ uid "\0014Q\311L@\311";
+}
+lease 10.254.251.103 {
+ starts 2 2018/04/24 13:18:00;
+ ends 2 2018/04/24 14:58:00;
+ tstp 2 2018/04/24 14:58:00;
+ cltt 2 2018/04/24 13:18:00;
+ binding state free;
+ hardware ethernet 70:8a:09:da:74:d0;
+ set vendor-class-identifier = "dhcpcd-5.5.6";
+}
+lease 10.254.251.104 {
+ starts 2 2018/04/24 12:54:27;
+ ends 3 2018/04/25 06:47:20;
+ tstp 3 2018/04/25 06:47:20;
+ cltt 2 2018/04/24 12:54:28;
+ binding state free;
+ hardware ethernet 78:a3:e4:e8:12:1f;
+ uid "\001x\243\344\350\022\037";
+}
+lease 10.254.251.100 {
+ starts 6 2020/10/03 07:58:45;
+ ends 6 2020/10/03 09:38:45;
+ cltt 6 2020/10/03 07:58:45;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 74:ea:3a:a6:a9:c7;
+ uid "\001t\352:\246\251\307";
+ set vendor-class-identifier = "MSFT 5.0";
+ client-hostname "TL-WR741N";
+}
+lease 10.254.255.104 {
+ starts 1 2017/07/10 09:35:24;
+ ends 1 2017/07/10 09:37:24;
+ tstp 1 2017/07/10 09:37:24;
+ cltt 1 2017/07/10 09:35:24;
+ binding state free;
+ hardware ethernet 50:85:69:11:b6:ff;
+ uid "\001P\205i\021\266\377";
+}
+lease 10.254.255.102 {
+ starts 3 2017/08/16 22:01:09;
+ ends 3 2017/08/16 23:41:09;
+ tstp 3 2017/08/16 23:41:09;
+ cltt 3 2017/08/16 22:01:09;
+ binding state free;
+ hardware ethernet c8:d3:a3:54:31:3a;
+ uid "\001\310\323\243T1:";
+}
+lease 10.254.255.103 {
+ starts 0 2018/12/16 00:54:07;
+ ends 0 2018/12/16 02:34:07;
+ tstp 0 2018/12/16 02:34:07;
+ cltt 0 2018/12/16 00:54:07;
+ binding state free;
+ hardware ethernet 08:c6:b3:01:e8:18;
+ uid "\001\010\306\263\001\350\030";
+ set vendor-class-identifier = "QTCH-QBR1041WUV2";
+}
+lease 10.254.255.100 {
+ starts 2 2018/12/18 09:21:24;
+ ends 2 2018/12/18 10:32:36;
+ tstp 2 2018/12/18 10:32:36;
+ cltt 2 2018/12/18 09:21:30;
+ binding state free;
+ hardware ethernet 70:62:b8:c3:51:a3;
+ uid "\001pb\270\303Q\243";
+}
+lease 10.254.255.105 {
+ starts 5 2019/03/22 07:42:55;
+ ends 5 2019/03/22 09:22:55;
+ tstp 5 2019/03/22 09:22:55;
+ cltt 5 2019/03/22 07:42:55;
+ binding state free;
+ hardware ethernet 58:d5:6e:95:88:30;
+ uid "\001X\325n\225\2100";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.255.101 {
+ starts 6 2020/10/03 07:29:24;
+ ends 6 2020/10/03 09:09:24;
+ cltt 6 2020/10/03 07:29:24;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 28:3b:82:58:f4:58;
+ uid "\001(;\202X\364X";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.253.104 {
+ starts 4 2018/03/15 12:01:12;
+ ends 4 2018/03/15 12:34:35;
+ tstp 4 2018/03/15 12:34:35;
+ cltt 4 2018/03/15 12:02:58;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3d;
+ uid "\001Pd+O\375=";
+ set vendor-class-identifier = "udhcp 1.19.4";
+}
+lease 10.254.253.105 {
+ starts 4 2018/03/15 12:39:46;
+ ends 4 2018/03/15 14:17:39;
+ tstp 4 2018/03/15 14:17:39;
+ cltt 4 2018/03/15 12:39:47;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3d;
+ set vendor-class-identifier = "udhcp 1.19.4";
+}
+lease 10.254.253.101 {
+ starts 5 2018/03/16 11:00:43;
+ ends 5 2018/03/16 12:40:15;
+ tstp 5 2018/03/16 12:40:15;
+ cltt 5 2018/03/16 11:00:43;
+ binding state free;
+ hardware ethernet d0:66:7b:8b:e5:ff;
+ uid "\001\320f{\213\345\377";
+ set vendor-class-identifier = "udhcp 1.14.3-VD Linux VDLinux.1.2.1.x";
+}
+lease 10.254.253.102 {
+ starts 5 2018/03/16 11:26:21;
+ ends 5 2018/03/16 13:06:21;
+ tstp 5 2018/03/16 13:06:21;
+ cltt 5 2018/03/16 11:26:21;
+ binding state free;
+ hardware ethernet 50:64:2b:4f:fd:3f;
+ uid "\001Pd+O\375?";
+}
+lease 10.254.253.100 {
+ starts 2 2018/08/21 05:48:43;
+ ends 2 2018/08/21 07:23:13;
+ tstp 2 2018/08/21 07:23:13;
+ cltt 2 2018/08/21 05:48:44;
+ binding state free;
+ hardware ethernet 20:cf:30:ef:8e:a4;
+ uid "\001 \3170\357\216\244";
+ set vendor-class-identifier = "udhcp 0.9.8-asus";
+}
+lease 10.254.253.103 {
+ starts 6 2020/10/03 08:07:02;
+ ends 6 2020/10/03 09:47:02;
+ cltt 6 2020/10/03 08:07:02;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 34:ce:00:03:08:57;
+ uid "\0014\316\000\003\010W";
+ set vendor-class-identifier = "udhcp 1.24.2";
+}
+lease 10.254.254.103 {
+ starts 3 2015/11/11 09:03:11;
+ ends 3 2015/11/11 09:05:11;
+ tstp 3 2015/11/11 09:05:11;
+ cltt 3 2015/11/11 09:03:11;
+ binding state free;
+ hardware ethernet 74:d0:2b:0e:9b:d6;
+}
+lease 10.254.254.104 {
+ starts 0 2017/12/03 15:57:29;
+ ends 0 2017/12/03 17:37:29;
+ tstp 0 2017/12/03 17:37:29;
+ cltt 0 2017/12/03 15:57:29;
+ binding state free;
+ hardware ethernet ac:22:0b:78:00:78;
+ uid "\377\3139\012\307\000\002\000\000\253\021(CC\252e\021\000\017";
+}
+lease 10.254.254.105 {
+ starts 2 2018/06/26 12:30:04;
+ ends 2 2018/06/26 13:09:10;
+ tstp 2 2018/06/26 13:09:10;
+ cltt 2 2018/06/26 12:30:04;
+ binding state free;
+ hardware ethernet cc:2d:e0:3f:bc:5c;
+ uid "\001\314-\340?\274\\";
+}
+lease 10.254.254.101 {
+ starts 3 2018/07/25 09:33:10;
+ ends 3 2018/07/25 11:13:10;
+ tstp 3 2018/07/25 11:13:10;
+ cltt 3 2018/07/25 09:33:10;
+ binding state free;
+ hardware ethernet 74:d0:2b:0e:9b:d6;
+ uid "\001t\320+\016\233\326";
+ set vendor-class-identifier = "MSFT 5.0";
+}
+lease 10.254.254.100 {
+ starts 2 2020/09/22 11:19:29;
+ ends 2 2020/09/22 11:21:29;
+ cltt 2 2020/09/22 11:19:29;
+ binding state free;
+ hardware ethernet 30:45:96:6a:f3:de;
+ uid "\0010E\226j\363\336";
+ client-hostname "Honor_7C-bb23201389a3c44";
+}
+lease 10.254.254.102 {
+ starts 2 2020/09/22 11:25:14;
+ ends 2 2020/09/22 11:27:14;
+ cltt 2 2020/09/22 11:25:14;
+ binding state free;
+ hardware ethernet c8:3d:dc:be:d2:cf;
+ uid "\001\310=\334\276\322\317";
+ client-hostname "Redmi7A-Redmi";
+}
+lease 10.254.255.101 {
+ starts 6 2020/10/03 08:19:24;
+ ends 6 2020/10/03 09:59:24;
+ cltt 6 2020/10/03 08:19:24;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 28:3b:82:58:f4:58;
+ uid "\001(;\202X\364X";
+ set vendor-class-identifier = "dslforum.org";
+}
+lease 10.254.251.100 {
+ starts 6 2020/10/03 08:48:45;
+ ends 6 2020/10/03 10:28:45;
+ cltt 6 2020/10/03 08:48:45;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 74:ea:3a:a6:a9:c7;
+ uid "\001t\352:\246\251\307";
+ set vendor-class-identifier = "MSFT 5.0";
+ client-hostname "TL-WR741N";
+}
+lease 10.254.253.103 {
+ starts 6 2020/10/03 08:57:02;
+ ends 6 2020/10/03 10:37:02;
+ cltt 6 2020/10/03 08:57:02;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 34:ce:00:03:08:57;
+ uid "\0014\316\000\003\010W";
+ set vendor-class-identifier = "udhcp 1.24.2";
+}
+lease 192.168.3.11 {
+ starts 6 2020/10/03 09:01:22;
+ ends 6 2020/10/03 10:41:22;
+ cltt 6 2020/10/03 09:01:22;
+ binding state free;
+ next binding state free;
+ rewind binding state free;
+ hardware ethernet 60:a4:4c:3f:6e:78;
+ uid "\001`\244L?nx";
+}
diff --git a/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv6 b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv6
new file mode 100644
index 000000000..3a4f1520e
--- /dev/null
+++ b/src/go/collectors/go.d.plugin/modules/isc_dhcpd/testdata/dhcpd.leases_ipv6
@@ -0,0 +1,67 @@
+# The format of this file is documented in the dhcpd.leases(5) manual page.
+# This lease file was written by isc-dhcp-4.3.6b1
+
+# authoring-byte-order entry is generated, DO NOT DELETE
+authoring-byte-order little-endian;
+
+server-duid "\000\001\002\003!\004\005\006\007\008)^6\257";
+
+ia-na "'\000\010\016\000\001\000\001!\320\263\003\010\000'\327\337\354" {
+ cltt 0 2017/12/24 10:53:29;
+ iaaddr 2001:db8:: {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 10:53:29;
+ }
+}
+
+ia-na "#\2340\000\000\000\000\000!\300\021]0\234#e\212\261" {
+ cltt 6 2017/12/23 23:59:58;
+ iaaddr 2001:db8::1 {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 23:59:58;
+ }
+}
+
+ia-na "\000\000\000\000\000\001\000\000 \000\301\267xOCl\313\310" {
+ cltt 0 2017/12/24 02:11:08;
+ iaaddr 2001:db8::2 {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 02:11:08;
+ }
+}
+
+ia-na "'\000\000\000\000\000\000\001\027.\010\225\010\000'C8\353" {
+ cltt 0 2017/12/24 00:48:39;
+ iaaddr 2001:db8::3 {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 18:48:39;
+ }
+}
+
+ia-na "\000\000\000\000\000\000\000\265H\006n\305F\351\270i\014\326q\023J\347" {
+ cltt 0 2017/12/24 01:53:15;
+ iaaddr 2001:db8::4 {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 14:53:15;
+ }
+}
+
+ia-na "\000\000\000\000\000\000\000\000 \010\351\267xOCl\313\310" {
+ cltt 0 2017/12/24 11:33:17;
+ iaaddr 2001:db8::5 {
+ binding state active;
+ preferred-life 604800;
+ max-life 2592000;
+ ends 2 2020/09/30 11:33:17;
+ }
+}