From 96ec04726f8ddc6867f3d5c5d491cd1162c61bea Mon Sep 17 00:00:00 2001 From: Joel Norberg Date: Fri, 11 Oct 2024 16:12:46 +0200 Subject: [PATCH] [feat] CentralManagement Status metrics. Replaces Fortimanager Status which is deprecated in FortiOS 7.6+ --- README.md | 3 + pkg/probe/probe.go | 1 + pkg/probe/system_central_management.go | 88 +++++++++++++++++++ pkg/probe/system_central_management_test.go | 47 ++++++++++ pkg/probe/system_fortimanager.go | 5 ++ .../system-central-management-status.jsonnet | 39 ++++++++ 6 files changed, 183 insertions(+) create mode 100644 pkg/probe/system_central_management.go create mode 100755 pkg/probe/system_central_management_test.go create mode 100644 pkg/probe/testdata/system-central-management-status.jsonnet diff --git a/README.md b/README.md index 82a0118..184d940 100755 --- a/README.md +++ b/README.md @@ -74,6 +74,9 @@ Per-VDOM: * _System/Fortimanager/Status_ * `fortigate_fortimanager_connection_status` * `fortigate_fortimanager_registration_status` + * _System/CentralManagement/Status_ + * `fortigate_central_management_connection_status` + * `fortigate_central_management_status` * _System/Interface_ * `fortigate_interface_link_up` * `fortigate_interface_speed_bps` diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go index c79a7f6..6ca1e91 100644 --- a/pkg/probe/probe.go +++ b/pkg/probe/probe.go @@ -129,6 +129,7 @@ func (p *ProbeCollector) Probe(ctx context.Context, target map[string]string, hc {"Log/DiskUsage", probeLogCurrentDiskUsage}, {"System/AvailableCertificates", probeSystemAvailableCertificates}, {"System/Fortimanager/Status", probeSystemFortimanagerStatus}, + {"System/CentralManagement/Status", probeSystemCentralManagementStatus}, {"System/HAStatistics", probeSystemHAStatistics}, {"System/Interface", probeSystemInterface}, {"System/LinkMonitor", probeSystemLinkMonitor}, diff --git a/pkg/probe/system_central_management.go b/pkg/probe/system_central_management.go new file mode 100644 index 0000000..a5b7e3b --- /dev/null +++ b/pkg/probe/system_central_management.go @@ -0,0 +1,88 @@ +package probe + +import ( + "log" + + "github.com/bluecmd/fortigate_exporter/pkg/http" + "github.com/prometheus/client_golang/prometheus" +) + +type SystemCentralManagementResults struct { + Mode string `json:"mode"` + Status string `json:"status"` + Registration string `json:"registration_status"` +} + +type SystemCentralManagementStatus struct { + Results SystemCentralManagementResults `json:"results"` + VDOM string `json:"vdom"` +} + +func probeSystemCentralManagementStatus(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) { + // New API in FortiOS v7.4.0+ + if meta.VersionMajor < 7 || (meta.VersionMajor == 7 && meta.VersionMinor < 4) { + log.Printf("Unsupported probe System/CentralManagement/Status - requires minimum FortiOS v7.4.0") + return []prometheus.Metric{}, false + } + var ( + FortimanStat_id = prometheus.NewDesc( + "fortigate_central_management_connection_status", + "Fortimanager status", + []string{"vdom", "mode", "status"}, nil, + ) + FortimanReg_id = prometheus.NewDesc( + "fortigate_central_management_registration_status", + "Fortimanager registration status", + []string{"vdom", "mode", "status"}, nil, + ) + ) + + var res []SystemCentralManagementStatus + if err := c.Get("api/v2/monitor/system/central-management/status", "vdom=*", &res); err != nil { + log.Printf("Error: %v", err) + return nil, false + } + + m := []prometheus.Metric{} + for _, r := range res { + StatusDown, StatusHandshake, StatusUp := 0.0, 0.0, 0.0 + switch r.Results.Status { + case "down": + // No management Tunnel + StatusDown = 1.0 + case "handshake": + // Management tunnel establishment in progress + StatusHandshake = 1.0 + case "up": + // Management tunnel is establised + StatusUp = 1.0 + } + + RegistrationUnknown, RegistrationInProgress, RegistrationRegistered, RegistrationUnregistered := 0.0, 0.0, 0.0, 0.0 + switch r.Results.Registration { + case "unknown": + // FMG does not know about the device + RegistrationUnknown = 1.0 + case "in_progress": + // FMG does know the device, but it is not yet fully saved in the list of unregistered devices + RegistrationInProgress = 1.0 + case "registered": + // FMG does know the device, and device is authorized + RegistrationRegistered = 1.0 + case "unregistered ": + // FMG does know the device, but it is not yet authorized + RegistrationUnregistered = 1.0 + } + + m = append(m, prometheus.MustNewConstMetric(FortimanStat_id, prometheus.GaugeValue, StatusDown, r.VDOM, r.Results.Mode, "down")) + m = append(m, prometheus.MustNewConstMetric(FortimanStat_id, prometheus.GaugeValue, StatusHandshake, r.VDOM, r.Results.Mode, "handshake")) + m = append(m, prometheus.MustNewConstMetric(FortimanStat_id, prometheus.GaugeValue, StatusUp, r.VDOM, r.Results.Mode, "up")) + + m = append(m, prometheus.MustNewConstMetric(FortimanReg_id, prometheus.GaugeValue, RegistrationUnknown, r.VDOM, r.Results.Mode, "unknown")) + m = append(m, prometheus.MustNewConstMetric(FortimanReg_id, prometheus.GaugeValue, RegistrationInProgress, r.VDOM, r.Results.Mode, "inprogress")) + m = append(m, prometheus.MustNewConstMetric(FortimanReg_id, prometheus.GaugeValue, RegistrationRegistered, r.VDOM, r.Results.Mode, "registered")) + m = append(m, prometheus.MustNewConstMetric(FortimanReg_id, prometheus.GaugeValue, RegistrationUnregistered, r.VDOM, r.Results.Mode, "unregistered")) + } + + return m, true +} diff --git a/pkg/probe/system_central_management_test.go b/pkg/probe/system_central_management_test.go new file mode 100755 index 0000000..fcebc41 --- /dev/null +++ b/pkg/probe/system_central_management_test.go @@ -0,0 +1,47 @@ +package probe + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" +) + +func TestSystemCentralManagementStatus(t *testing.T) { + c := newFakeClient() + c.prepare("api/v2/monitor/system/central-management/status", "testdata/system-central-management-status.jsonnet") + r := prometheus.NewPedanticRegistry() + meta := &TargetMetadata{ + VersionMajor: 7, + VersionMinor: 4, + } + if !testProbeWithMetadata(probeSystemCentralManagementStatus, c, meta, r) { + t.Errorf("probeSystemCentralManagementStatus() returned non-success") + } + + em := ` + # HELP fortigate_central_management_connection_status Fortimanager status + # TYPE fortigate_central_management_connection_status gauge + fortigate_central_management_connection_status{mode="",status="down",vdom="test1"} 0 + fortigate_central_management_connection_status{mode="",status="down",vdom="root"} 0 + fortigate_central_management_connection_status{mode="",status="handshake",vdom="test1"} 1 + fortigate_central_management_connection_status{mode="",status="handshake",vdom="root"} 0 + fortigate_central_management_connection_status{mode="",status="up",vdom="test1"} 0 + fortigate_central_management_connection_status{mode="",status="up",vdom="root"} 1 + # HELP fortigate_central_management_registration_status Fortimanager registration status + # TYPE fortigate_central_management_registration_status gauge + fortigate_central_management_registration_status{mode="",status="inprogress",vdom="test1"} 0 + fortigate_central_management_registration_status{mode="",status="inprogress",vdom="root"} 0 + fortigate_central_management_registration_status{mode="",status="registered",vdom="test1"} 0 + fortigate_central_management_registration_status{mode="",status="registered",vdom="root"} 1 + fortigate_central_management_registration_status{mode="",status="unknown",vdom="test1"} 1 + fortigate_central_management_registration_status{mode="",status="unknown",vdom="root"} 0 + fortigate_central_management_registration_status{mode="",status="unregistered",vdom="test1"} 0 + fortigate_central_management_registration_status{mode="",status="unregistered",vdom="root"} 0 + ` + + if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil { + t.Fatalf("metric compare: err %v", err) + } +} diff --git a/pkg/probe/system_fortimanager.go b/pkg/probe/system_fortimanager.go index 7ebcec6..eaee470 100644 --- a/pkg/probe/system_fortimanager.go +++ b/pkg/probe/system_fortimanager.go @@ -19,6 +19,11 @@ type SystemFortimanagerStatus struct { } func probeSystemFortimanagerStatus(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) { + // Deprecated API call in FortiOS v7.6.0 + if meta.VersionMajor > 7 || (meta.VersionMajor == 7 && meta.VersionMinor >= 6) { + log.Printf("Deprecated probe in FortiOS 7.6+ System/Fortimanager/Status - replaced by System/CentralManagement/Status") + return []prometheus.Metric{}, false + } var ( FortimanStat_id = prometheus.NewDesc( "fortigate_fortimanager_connection_status", diff --git a/pkg/probe/testdata/system-central-management-status.jsonnet b/pkg/probe/testdata/system-central-management-status.jsonnet new file mode 100644 index 0000000..0c38efd --- /dev/null +++ b/pkg/probe/testdata/system-central-management-status.jsonnet @@ -0,0 +1,39 @@ +# api/v2/monitor/system/central-management/status?vdom=* +[ + { + "http_method":"GET", + "results":{ + "status":"up", + "registration_status":"registered", + "sn":"F2K60FTK00000000", + "mgmt_ip":"::ffff:172.25.150.122", + "mgmt_port":443 + }, + "vdom":"root", + "path":"system", + "name":"central-management", + "action":"status", + "status":"success", + "serial":"F2K60FTK00000000", + "version":"v7.4.5", + "build":2702 + }, + { + "http_method":"GET", + "results":{ + "status":"handshake", + "registration_status":"unknown", + "sn":"F2K60FTK00000000", + "mgmt_ip":"::ffff:172.25.150.122", + "mgmt_port":443 + }, + "vdom":"test1", + "path":"system", + "name":"central-management", + "action":"status", + "status":"success", + "serial":"F2K60FTK00000000", + "version":"v7.4.5", + "build":2702 + } +]