diff --git a/README.md b/README.md index 82a0118b..67933b27 100755 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Global: * _WebUI/State_ * `fortigate_last_reboot_seconds` * `fortigate_last_snapshot_seconds` + * _System/HAPeerStatus_ + * `fortigate_ha_member_info` Per-VDOM: diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go index c79a7f6f..3cacfb00 100644 --- a/pkg/probe/probe.go +++ b/pkg/probe/probe.go @@ -149,6 +149,7 @@ func (p *ProbeCollector) Probe(ctx context.Context, target map[string]string, hc {"Wifi/ManagedAP", probeWifiManagedAP}, {"Switch/ManagedSwitch", probeManagedSwitch}, {"OSPF/Neighbors", probeOSPFNeighbors}, + {"System/HAPeer", probeSystemHAPeer}, } { wanted := false diff --git a/pkg/probe/system_ha_peer.go b/pkg/probe/system_ha_peer.go new file mode 100644 index 00000000..586039f1 --- /dev/null +++ b/pkg/probe/system_ha_peer.go @@ -0,0 +1,55 @@ +package probe + +import ( + "log" + "strconv" + + "github.com/bluecmd/fortigate_exporter/pkg/http" + "github.com/prometheus/client_golang/prometheus" +) + +func probeSystemHAPeer(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) { + var ( + memberInfo = prometheus.NewDesc( + "fortigate_ha_peer_info", + "Info metrics regarding cluster HA peers", + []string{"vdom", "hostname", "serial", "priority", "vcluster_id", "primary"}, nil, + ) + ) + + type HAResults struct { + Hostname string `json:"hostname"` + SerialNo string `json:"serial_no"` + VclusterID int `json:"vcluster_id"` + Priority int `json:"priority"` + Primary bool `json:"primary"` // Set to true if primary node in FortiOS 7.4+ + } + + type HAResponse struct { + HTTPMethod string `json:"http_method"` + Results []HAResults `json:"results"` + VDOM string `json:"vdom"` + Path string `json:"path"` + Name string `json:"name"` + Status string `json:"status"` + Serial string `json:"serial"` + Version string `json:"version"` + Build int64 `json:"build"` + } + var r HAResponse + + if err := c.Get("api/v2/monitor/system/ha-peer", "", &r); err != nil { + log.Printf("Error: %v", err) + return nil, false + } + + m := []prometheus.Metric{} + for _, result := range r.Results { + strPrimary := strconv.FormatBool(result.Primary) + if meta.VersionMajor < 7 || (meta.VersionMajor == 7 && meta.VersionMinor < 4) { + strPrimary = "Unsupported" + } + m = append(m, prometheus.MustNewConstMetric(memberInfo, prometheus.GaugeValue, 1, r.VDOM, result.Hostname, result.SerialNo, strconv.Itoa(result.Priority), strconv.Itoa(result.VclusterID), strPrimary)) + } + return m, true +} diff --git a/pkg/probe/system_ha_peer_test.go b/pkg/probe/system_ha_peer_test.go new file mode 100644 index 00000000..eb9a2181 --- /dev/null +++ b/pkg/probe/system_ha_peer_test.go @@ -0,0 +1,57 @@ +package probe + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" +) + +func TestHAPeer(t *testing.T) { + c := newFakeClient() + c.prepare("api/v2/monitor/system/ha-peer", "testdata/ha-peer.jsonnet") + r := prometheus.NewPedanticRegistry() + meta := &TargetMetadata{ + VersionMajor: 7, + VersionMinor: 0, + } + if !testProbeWithMetadata(probeSystemHAPeer, c, meta, r) { + t.Errorf("probeSystemHAPeer() returned non-success") + } + + em := ` + # HELP fortigate_ha_peer_info Info metrics regarding cluster HA peers + # TYPE fortigate_ha_peer_info gauge + fortigate_ha_peer_info{hostname="member-name-1",primary="Unsupported",priority="200",serial="FGT61E4QXXXXXXXX1",vcluster_id="0",vdom="root"} 1 + fortigate_ha_peer_info{hostname="member-name-2",primary="Unsupported",priority="100",serial="FGT61E4QXXXXXXXX2",vcluster_id="0",vdom="root"} 1 + ` + + if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil { + t.Fatalf("metric compare: err %v", err) + } +} + +func TestHAPeer74(t *testing.T) { + c := newFakeClient() + c.prepare("api/v2/monitor/system/ha-peer", "testdata/ha-peer-74+.jsonnet") + r := prometheus.NewPedanticRegistry() + meta := &TargetMetadata{ + VersionMajor: 7, + VersionMinor: 4, + } + if !testProbeWithMetadata(probeSystemHAPeer, c, meta, r) { + t.Errorf("probeSystemHAPeer() returned non-success") + } + + em := ` + # HELP fortigate_ha_peer_info Info metrics regarding cluster HA peers + # TYPE fortigate_ha_peer_info gauge + fortigate_ha_peer_info{hostname="member-name-1",primary="true",priority="200",serial="FGT61E4QXXXXXXXX1",vcluster_id="0",vdom="root"} 1 + fortigate_ha_peer_info{hostname="member-name-2",primary="false",priority="100",serial="FGT61E4QXXXXXXXX2",vcluster_id="0",vdom="root"} 1 + ` + + if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil { + t.Fatalf("metric compare: err %v", err) + } +} diff --git a/pkg/probe/testdata/ha-peer-74+.jsonnet b/pkg/probe/testdata/ha-peer-74+.jsonnet new file mode 100644 index 00000000..a9828bb7 --- /dev/null +++ b/pkg/probe/testdata/ha-peer-74+.jsonnet @@ -0,0 +1,27 @@ +# api/v2/monitor/system/ha-peer +{ + "http_method":"GET", + "results":[ + { + "serial_no":"FGT61E4QXXXXXXXX1", + "vcluster_id":0, + "priority":200, + "hostname":"member-name-1", + "primary":true + }, + { + "serial_no":"FGT61E4QXXXXXXXX2", + "vcluster_id":0, + "priority":100, + "hostname":"member-name-2" + } + ], + "vdom":"root", + "path":"system", + "name":"ha-peer", + "action":"", + "status":"success", + "serial":"FGT61E4QXXXXXXXX1", + "version":"v7.4.0", + "build":700 +} \ No newline at end of file diff --git a/pkg/probe/testdata/ha-peer.jsonnet b/pkg/probe/testdata/ha-peer.jsonnet new file mode 100644 index 00000000..0ebae689 --- /dev/null +++ b/pkg/probe/testdata/ha-peer.jsonnet @@ -0,0 +1,26 @@ +# api/v2/monitor/system/ha-peer +{ + "http_method":"GET", + "results":[ + { + "serial_no":"FGT61E4QXXXXXXXX1", + "vcluster_id":0, + "priority":200, + "hostname":"member-name-1" + }, + { + "serial_no":"FGT61E4QXXXXXXXX2", + "vcluster_id":0, + "priority":100, + "hostname":"member-name-2" + } + ], + "vdom":"root", + "path":"system", + "name":"ha-peer", + "action":"", + "status":"success", + "serial":"FGT61E4QXXXXXXXX1", + "version":"v7.0.12", + "build":523 +} \ No newline at end of file