From 9da7dd6041f347c2f18f6c27fa64386480105094 Mon Sep 17 00:00:00 2001
From: Gianni Stubbe <github@frai.se>
Date: Mon, 14 Aug 2023 20:17:11 +0200
Subject: [PATCH] feat(probe): support for ippool info (#240)

* feat(ippool): Added support for ippool info

This merge adds support for ippool information. This closes #231

* fix(naming): Updated metric names to be in line with prometheus conventions

* test(ippool): Added tests for the firewall ippool feature

* style: Updated tests and readme to align for new naming

* style: Changed metric name to match other percentage metrics

* fix(ippool): Updated value percentage to be 0-1.0

---------

Co-authored-by: Gianni Stubbe <gianni.stubbe@citymesh.com>
---
 README.md                            |  8 +++
 pkg/probe/firewall_ippool.go         | 91 ++++++++++++++++++++++++++++
 pkg/probe/firewall_ippool_test.go    | 42 +++++++++++++
 pkg/probe/probe.go                   |  1 +
 pkg/probe/testdata/fw-ippool.jsonnet | 30 +++++++++
 5 files changed, 172 insertions(+)
 create mode 100644 pkg/probe/firewall_ippool.go
 create mode 100644 pkg/probe/firewall_ippool_test.go
 create mode 100644 pkg/probe/testdata/fw-ippool.jsonnet

diff --git a/README.md b/README.md
index 0e4cc46..628a446 100755
--- a/README.md
+++ b/README.md
@@ -63,6 +63,13 @@ Per-VDOM:
    * `fortigate_policy_bytes_total`
    * `fortigate_policy_hit_count_total`
    * `fortigate_policy_packets_total`
+ * _Firewall/IpPool_
+   * `fortigate_ippool_available_ratio`
+   * `fortigate_ippool_used_ips`
+   * `fortigate_ippool_total_ips`
+   * `fortigate_ippool_clients`
+   * `fortigate_ippool_used_items`
+   * `fortigate_ippool_total_items`
  * _System/Fortimanager/Status_
    * `fortigate_fortimanager_connection_status`
    * `fortigate_fortimanager_registration_status`
@@ -386,6 +393,7 @@ To improve security, limit permissions to required ones only (least privilege pr
 |BGP/NeighborPaths/IPv6       | netgrp.route-cfg   |api/v2/monitor/router/bgp/paths6 |
 |BGP/Neighbors/IPv4           | netgrp.route-cfg   |api/v2/monitor/router/bgp/neighbors |
 |BGP/Neighbors/IPv6           | netgrp.route-cfg   |api/v2/monitor/router/bgp/neighbors6 |
+|Firewall/IpPool              | fwgrp.policy       |api/v2/monitor/firewall/ippool |
 |Firewall/LoadBalance         | fwgrp.others       |api/v2/monitor/firewall/load-balance |
 |Firewall/Policies            | fwgrp.policy       |api/v2/monitor/firewall/policy/select<br>api/v2/monitor/firewall/policy6/select<br>api/v2/cmdb/firewall/policy<br>api/v2/cmdb/firewall/policy6 |
 |License/Status               | *any*              |api/v2/monitor/license/status/select |
diff --git a/pkg/probe/firewall_ippool.go b/pkg/probe/firewall_ippool.go
new file mode 100644
index 0000000..2ff7203
--- /dev/null
+++ b/pkg/probe/firewall_ippool.go
@@ -0,0 +1,91 @@
+package probe
+
+import (
+	"log"
+
+	"github.com/bluecmd/fortigate_exporter/pkg/http"
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+type IpPool struct {
+	Name      string  `json:"name"`
+	IPTotal   int     `json:"natip_total"`
+	IPInUse   int     `json:"natip_in_use"`
+	Clients   int     `json:"clients"`
+	Available float64 `json:"available"`
+	Used      int     `json:"used"`
+	Total     int     `json:"total"`
+}
+
+type IpPoolResponse struct {
+	Results map[string]IpPool `json:"results"`
+	VDOM    string            `json:"vdom"`
+	Version string            `json:"version"`
+}
+
+func probeFirewallIpPool(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) {
+	var (
+		mAvailable = prometheus.NewDesc(
+			"fortigate_ippool_available_ratio",
+			"Percentage available in ippool (0 - 1.0)",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+	var (
+		mIpUsed = prometheus.NewDesc(
+			"fortigate_ippool_used_ips",
+			"Ip addresses in use in ippool",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+	var (
+		mIpTotal = prometheus.NewDesc(
+			"fortigate_ippool_total_ips",
+			"Ip addresses total in ippool",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+	var (
+		mClients = prometheus.NewDesc(
+			"fortigate_ippool_clients",
+			"Amount of clients using ippool",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+	var (
+		mUsed = prometheus.NewDesc(
+			"fortigate_ippool_used_items",
+			"Amount of items used in ippool",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+	var (
+		mTotal = prometheus.NewDesc(
+			"fortigate_ippool_total_items",
+			"Amount of items total in ippool",
+			[]string{"vdom", "name"}, nil,
+		)
+	)
+
+	var rs []IpPoolResponse
+
+	if err := c.Get("api/v2/monitor/firewall/ippool", "vdom=*", &rs); err != nil {
+		log.Printf("Error: %v", err)
+		return nil, false
+	}
+
+	m := []prometheus.Metric{}
+
+	for _, r := range rs {
+		for _, ippool := range r.Results {
+			m = append(m, prometheus.MustNewConstMetric(mAvailable, prometheus.GaugeValue, ippool.Available/100, r.VDOM, ippool.Name))
+			m = append(m, prometheus.MustNewConstMetric(mIpUsed, prometheus.GaugeValue, float64(ippool.IPInUse), r.VDOM, ippool.Name))
+			m = append(m, prometheus.MustNewConstMetric(mIpTotal, prometheus.GaugeValue, float64(ippool.IPTotal), r.VDOM, ippool.Name))
+			m = append(m, prometheus.MustNewConstMetric(mClients, prometheus.GaugeValue, float64(ippool.Clients), r.VDOM, ippool.Name))
+			m = append(m, prometheus.MustNewConstMetric(mUsed, prometheus.GaugeValue, float64(ippool.Used), r.VDOM, ippool.Name))
+			m = append(m, prometheus.MustNewConstMetric(mTotal, prometheus.GaugeValue, float64(ippool.Total), r.VDOM, ippool.Name))
+		}
+	}
+
+	return m, true
+}
diff --git a/pkg/probe/firewall_ippool_test.go b/pkg/probe/firewall_ippool_test.go
new file mode 100644
index 0000000..dbce34e
--- /dev/null
+++ b/pkg/probe/firewall_ippool_test.go
@@ -0,0 +1,42 @@
+package probe
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/prometheus/client_golang/prometheus"
+	"github.com/prometheus/client_golang/prometheus/testutil"
+)
+
+func TestFirewallIpPool(t *testing.T) {
+	c := newFakeClient()
+	c.prepare("api/v2/monitor/firewall/ippool", "testdata/fw-ippool.jsonnet")
+	r := prometheus.NewPedanticRegistry()
+	if !testProbe(probeFirewallIpPool, c, r) {
+		t.Errorf("probeFirewallIpPool() returned non-success")
+	}
+
+	em := `
+	# HELP fortigate_ippool_available_ratio Percentage available in ippool (0 - 1.0)
+	# TYPE fortigate_ippool_available_ratio gauge
+	fortigate_ippool_available_ratio{name="ippool_name",vdom="FG-traffic"} 1
+	# HELP fortigate_ippool_clients Amount of clients using ippool
+	# TYPE fortigate_ippool_clients gauge
+	fortigate_ippool_clients{name="ippool_name",vdom="FG-traffic"} 0
+	# HELP fortigate_ippool_total_ips Ip addresses total in ippool
+	# TYPE fortigate_ippool_total_ips gauge
+	fortigate_ippool_total_ips{name="ippool_name",vdom="FG-traffic"} 1
+	# HELP fortigate_ippool_total_items Amount of items total in ippool
+	# TYPE fortigate_ippool_total_items gauge
+	fortigate_ippool_total_items{name="ippool_name",vdom="FG-traffic"} 472
+	# HELP fortigate_ippool_used_ips Ip addresses in use in ippool
+	# TYPE fortigate_ippool_used_ips gauge
+	fortigate_ippool_used_ips{name="ippool_name",vdom="FG-traffic"} 0
+	# HELP fortigate_ippool_used_items Amount of items used in ippool
+	# TYPE fortigate_ippool_used_items gauge
+	fortigate_ippool_used_items{name="ippool_name",vdom="FG-traffic"} 0
+	`
+	if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil {
+		t.Fatalf("metric compare: err %v", err)
+	}
+}
diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go
index a44ab69..7104283 100644
--- a/pkg/probe/probe.go
+++ b/pkg/probe/probe.go
@@ -122,6 +122,7 @@ func (p *ProbeCollector) Probe(ctx context.Context, target map[string]string, hc
 		{"BGP/Neighbors/IPv6", probeBGPNeighborsIPv6},
 		{"Firewall/LoadBalance", probeFirewallLoadBalance},
 		{"Firewall/Policies", probeFirewallPolicies},
+		{"Firewall/IpPool", probeFirewallIpPool},
 		{"License/Status", probeLicenseStatus},
 		{"Log/Fortianalyzer/Status", probeLogAnalyzer},
 		{"Log/Fortianalyzer/Queue", probeLogAnalyzerQueue},
diff --git a/pkg/probe/testdata/fw-ippool.jsonnet b/pkg/probe/testdata/fw-ippool.jsonnet
new file mode 100644
index 0000000..cc23978
--- /dev/null
+++ b/pkg/probe/testdata/fw-ippool.jsonnet
@@ -0,0 +1,30 @@
+# api/v2/monitor/firewall/ippool?vdom=*
+
+[
+    {
+        "http_method": "GET",
+        "results": {
+            "ippool_name": {
+                "name": "ippool_name",
+                "blocks": 8,
+                "block_size": 128,
+                "fixed_port": false,
+                "pba_per_ip": 472,
+                "used": 0,
+                "total": 472,
+                "available": 100.0,
+                "clients": 0,
+                "natip_in_use": 0,
+                "natip_total": 1
+            }
+        },
+        "vdom":"FG-traffic",
+        "path":"firewall",
+        "name":"ippool",
+        "action":"",
+        "status":"success",
+        "serial":"FGVMEVZFNTS3OAC8",
+        "version":"v7.0.11",
+        "build":489
+    }
+]