Skip to content

Commit 830687c

Browse files
author
Hugo Blom
authored
feat(bgp): Neighbour path and best path count (#115)
* Add BGP neighbour path and best path count
1 parent 8d54a10 commit 830687c

8 files changed

+337
-3
lines changed

Diff for: README.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ Per-VDOM:
5757
* `fortigate_ipsec_tunnel_receive_bytes_total`
5858
* `fortigate_ipsec_tunnel_transmit_bytes_total`
5959
* `fortigate_ipsec_tunnel_up`
60-
* `fortigate_bgp_neighbor_ipv4_info`
61-
* `fortigate_bgp_neighbor_ipv6_info`
6260
* `fortigate_wifi_access_points`
6361
* `fortigate_wifi_fabric_clients`
6462
* `fortigate_wifi_fabric_max_allowed_clients`
@@ -98,6 +96,14 @@ Per-VDOM:
9896
* `fortigate_virtual_wan_bandwidth_rx_byte_per_second`
9997
* `fortigate_virtual_wan_status_change_time_seconds`
10098

99+
Per-BGP-Neighbor and VDOM:
100+
* `fortigate_bgp_neighbor_ipv4_info`
101+
* `fortigate_bgp_neighbor_ipv6_info`
102+
* `fortigate_bgp_neighbor_ipv4_paths`
103+
* `fortigate_bgp_neighbor_ipv6_paths`
104+
* `fortigate_bgp_neighbor_ipv4_best_paths`
105+
* `fortigate_bgp_neighbor_ipv6_best_paths`
106+
101107
Per-VirtualServer and VDOM:
102108
* `fortigate_lb_virtual_server_info`
103109

@@ -180,6 +186,7 @@ which means that currently you need HTTPS to be configured properly.
180186
To probe a FortiGate, do something like `curl 'localhost:9710/probe?target=https://my-fortigate'`
181187

182188
### Available CLI parameters
189+
183190
| flag | default value | description |
184191
|---|---|---|
185192
| -auth-file | fortigate-key.yaml | path to the location of the key file |
@@ -188,7 +195,7 @@ To probe a FortiGate, do something like `curl 'localhost:9710/probe?target=https
188195
| -https-timeout | 10 | timeout in seconds for establishment of HTTPS connections |
189196
| -insecure | false | allows to turn off security validation of TLS certificates |
190197
| -extra-ca-certs | (none) | comma-separated files containing extra PEMs to trust for TLS connections in addition to the system trust store |
191-
198+
| -max-bgp-paths | 10000 | Sets maximum amount of BGP paths to fetch, value is per IP stack version (IPv4 a& IPv6) |
192199
### FortiGate Configuration
193200

194201
The following example Admin Profile describes the permissions that needs to be granted

Diff for: internal/config/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type FortiExporterParameter struct {
1616
TLSTimeout *int
1717
TLSInsecure *bool
1818
TlsExtraCAs *string
19+
MaxBGPPaths *int
1920
}
2021

2122
type FortiExporterConfig struct {
@@ -25,6 +26,7 @@ type FortiExporterConfig struct {
2526
TLSTimeout int
2627
TLSInsecure bool
2728
TlsExtraCAs []LocalCert
29+
MaxBGPPaths int
2830
}
2931

3032
type AuthKeys map[Target]TargetAuth
@@ -49,6 +51,7 @@ var (
4951
TLSTimeout: flag.Int("https-timeout", 10, "TLS Handshake timeout in seconds"),
5052
TLSInsecure: flag.Bool("insecure", false, "Allow insecure certificates"),
5153
TlsExtraCAs: flag.String("extra-ca-certs", "", "comma-separated files containing extra PEMs to trust for TLS connections in addition to the system trust store"),
54+
MaxBGPPaths: flag.Int("max-bgp-paths", 10000, "How many BGP Paths to receive when counting routes, needs to be higher then the number of routes or metrics will not be generated"),
5255
}
5356

5457
savedConfig *FortiExporterConfig
@@ -69,6 +72,7 @@ func Init() error {
6972
ScrapeTimeout: *parameter.ScrapeTimeout,
7073
TLSTimeout: *parameter.TLSTimeout,
7174
TLSInsecure: *parameter.TLSInsecure,
75+
MaxBGPPaths: *parameter.MaxBGPPaths,
7276
}
7377

7478
// parse AuthKeys

Diff for: pkg/probe/bgp_neighbor_routes.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package probe
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/bluecmd/fortigate_exporter/internal/config"
8+
"github.com/bluecmd/fortigate_exporter/pkg/http"
9+
"github.com/prometheus/client_golang/prometheus"
10+
)
11+
12+
type BGPPath struct {
13+
LearnedFrom string `json:"learned_from"`
14+
IsBest bool `json:"is_best"`
15+
}
16+
17+
type BGPPaths struct {
18+
Results []BGPPath `json:"results"`
19+
VDOM string `json:"vdom"`
20+
Version string `json:"version"`
21+
}
22+
23+
type PathCount struct {
24+
Source string
25+
VDOM string
26+
}
27+
28+
func probeBGPNeighborPathsIPv4(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) {
29+
savedConfig := config.GetConfig()
30+
MaxBGPPaths := savedConfig.MaxBGPPaths
31+
32+
if MaxBGPPaths == 0 {
33+
return nil, true
34+
}
35+
36+
if meta.VersionMajor < 7 {
37+
// not supported version. Before 7.0.0 the requested endpoint doesn't exist
38+
return nil, true
39+
}
40+
var (
41+
BGPNeighborPaths = prometheus.NewDesc(
42+
"fortigate_bgp_neighbor_ipv4_paths",
43+
"Count of paths received from an BGP neighbor",
44+
[]string{"vdom", "neighbor_ip"}, nil,
45+
)
46+
BGPNeighborBestPaths = prometheus.NewDesc(
47+
"fortigate_bgp_neighbor_ipv4_best_paths",
48+
"Count of best paths for an BGP neighbor",
49+
[]string{"vdom", "neighbor_ip"}, nil,
50+
)
51+
)
52+
53+
var rs []BGPPaths
54+
55+
if err := c.Get("api/v2/monitor/router/bgp/paths", fmt.Sprintf("vdom=*&count=%d", MaxBGPPaths), &rs); err != nil {
56+
log.Printf("Error: %v", err)
57+
return nil, false
58+
}
59+
60+
m := []prometheus.Metric{}
61+
62+
srMap := make(map[PathCount]int)
63+
sr2Map := make(map[PathCount]int)
64+
for _, r := range rs {
65+
66+
if len(r.Results) >= MaxBGPPaths {
67+
log.Printf("Error: Received more BGP Paths than maximum (%d > %d) allowed, ignoring metric ...", len(r.Results), MaxBGPPaths)
68+
return nil, false
69+
}
70+
for _, route := range r.Results {
71+
sr := PathCount{
72+
Source: route.LearnedFrom,
73+
VDOM: r.VDOM,
74+
}
75+
srMap[sr] += 1
76+
if route.IsBest {
77+
sr2 := PathCount{
78+
Source: route.LearnedFrom,
79+
VDOM: r.VDOM,
80+
}
81+
sr2Map[sr2] += 1
82+
}
83+
}
84+
}
85+
86+
for neighbor, count := range srMap {
87+
m = append(m, prometheus.MustNewConstMetric(BGPNeighborPaths, prometheus.GaugeValue, float64(count), neighbor.VDOM, neighbor.Source))
88+
}
89+
for neighbor, count := range sr2Map {
90+
m = append(m, prometheus.MustNewConstMetric(BGPNeighborBestPaths, prometheus.GaugeValue, float64(count), neighbor.VDOM, neighbor.Source))
91+
}
92+
93+
return m, true
94+
}
95+
96+
func probeBGPNeighborPathsIPv6(c http.FortiHTTP, meta *TargetMetadata) ([]prometheus.Metric, bool) {
97+
savedConfig := config.GetConfig()
98+
MaxBGPPaths := savedConfig.MaxBGPPaths
99+
100+
if MaxBGPPaths == 0 {
101+
return nil, true
102+
}
103+
104+
if meta.VersionMajor < 7 {
105+
// not supported version. Before 7.0.0 the requested endpoint doesn't exist
106+
return nil, true
107+
}
108+
var (
109+
BGPNeighborPaths = prometheus.NewDesc(
110+
"fortigate_bgp_neighbor_ipv6_paths",
111+
"Count of paths received from an BGP neighbor",
112+
[]string{"vdom", "neighbor_ip"}, nil,
113+
)
114+
BGPNeighborBestPaths = prometheus.NewDesc(
115+
"fortigate_bgp_neighbor_ipv6_best_paths",
116+
"Count of best paths for an BGP neighbor",
117+
[]string{"vdom", "neighbor_ip"}, nil,
118+
)
119+
)
120+
121+
var rs []BGPPaths
122+
123+
if err := c.Get("api/v2/monitor/router/bgp/paths6", fmt.Sprintf("vdom=*&count=%d", MaxBGPPaths), &rs); err != nil {
124+
log.Printf("Error: %v", err)
125+
return nil, false
126+
}
127+
128+
m := []prometheus.Metric{}
129+
130+
srMap := make(map[PathCount]int)
131+
sr2Map := make(map[PathCount]int)
132+
for _, r := range rs {
133+
134+
if len(r.Results) >= MaxBGPPaths {
135+
log.Printf("Error: Received more BGP Paths than maximum (%d > %d) allowed, ignoring metric ...", len(r.Results), MaxBGPPaths)
136+
return nil, false
137+
}
138+
for _, route := range r.Results {
139+
sr := PathCount{
140+
Source: route.LearnedFrom,
141+
VDOM: r.VDOM,
142+
}
143+
srMap[sr] += 1
144+
if route.IsBest {
145+
sr2 := PathCount{
146+
Source: route.LearnedFrom,
147+
VDOM: r.VDOM,
148+
}
149+
sr2Map[sr2] += 1
150+
}
151+
}
152+
}
153+
154+
for neighbor, count := range srMap {
155+
m = append(m, prometheus.MustNewConstMetric(BGPNeighborPaths, prometheus.GaugeValue, float64(count), neighbor.VDOM, neighbor.Source))
156+
}
157+
for neighbor, count := range sr2Map {
158+
m = append(m, prometheus.MustNewConstMetric(BGPNeighborBestPaths, prometheus.GaugeValue, float64(count), neighbor.VDOM, neighbor.Source))
159+
}
160+
161+
return m, true
162+
}

Diff for: pkg/probe/bgp_neighbor_routes_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package probe
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/bluecmd/fortigate_exporter/internal/config"
8+
"github.com/prometheus/client_golang/prometheus"
9+
"github.com/prometheus/client_golang/prometheus/testutil"
10+
)
11+
12+
func TestBGPNeighborPathsIPv4(t *testing.T) {
13+
14+
if err := config.Init(); err != nil {
15+
t.Fatalf("config.Init failed: %+v", err)
16+
}
17+
18+
c := newFakeClient()
19+
c.prepare("api/v2/monitor/router/bgp/paths", "testdata/router-bgp-paths-v4.jsonnet")
20+
r := prometheus.NewPedanticRegistry()
21+
if !testProbe(probeBGPNeighborPathsIPv4, c, r) {
22+
t.Errorf("probeBGPNeighborPathsIPv4() returned non-success")
23+
}
24+
25+
em := `
26+
# HELP fortigate_bgp_neighbor_ipv4_best_paths Count of best paths for an BGP neighbor
27+
# TYPE fortigate_bgp_neighbor_ipv4_best_paths gauge
28+
fortigate_bgp_neighbor_ipv4_best_paths{neighbor_ip="10.0.0.1",vdom="root"} 1
29+
fortigate_bgp_neighbor_ipv4_best_paths{neighbor_ip="10.0.0.2",vdom="root"} 1
30+
# HELP fortigate_bgp_neighbor_ipv4_paths Count of paths received from an BGP neighbor
31+
# TYPE fortigate_bgp_neighbor_ipv4_paths gauge
32+
fortigate_bgp_neighbor_ipv4_paths{neighbor_ip="10.0.0.1",vdom="root"} 1
33+
fortigate_bgp_neighbor_ipv4_paths{neighbor_ip="10.0.0.2",vdom="root"} 2
34+
`
35+
36+
if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil {
37+
t.Fatalf("metric compare: err %v", err)
38+
}
39+
}
40+
41+
func TestBGPNeighborPathsIPv6(t *testing.T) {
42+
43+
if err := config.Init(); err != nil {
44+
t.Fatalf("config.Init failed: %+v", err)
45+
}
46+
47+
c := newFakeClient()
48+
c.prepare("api/v2/monitor/router/bgp/paths6", "testdata/router-bgp-paths-v6.jsonnet")
49+
r := prometheus.NewPedanticRegistry()
50+
if !testProbe(probeBGPNeighborPathsIPv6, c, r) {
51+
t.Errorf("probeBGPNeighborPathsIPv6() returned non-success")
52+
}
53+
54+
em := `
55+
# HELP fortigate_bgp_neighbor_ipv6_best_paths Count of best paths for an BGP neighbor
56+
# TYPE fortigate_bgp_neighbor_ipv6_best_paths gauge
57+
fortigate_bgp_neighbor_ipv6_best_paths{neighbor_ip="::",vdom="root"} 1
58+
fortigate_bgp_neighbor_ipv6_best_paths{neighbor_ip="fd00::1",vdom="root"} 2
59+
# HELP fortigate_bgp_neighbor_ipv6_paths Count of paths received from an BGP neighbor
60+
# TYPE fortigate_bgp_neighbor_ipv6_paths gauge
61+
fortigate_bgp_neighbor_ipv6_paths{neighbor_ip="::",vdom="root"} 1
62+
fortigate_bgp_neighbor_ipv6_paths{neighbor_ip="fd00::1",vdom="root"} 3
63+
`
64+
65+
if err := testutil.GatherAndCompare(r, strings.NewReader(em)); err != nil {
66+
t.Fatalf("metric compare: err %v", err)
67+
}
68+
}

Diff for: pkg/probe/fortigate-key.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# This file is used for running test cases
2+
"127.0.0.1":
3+
token: "asdasd"

Diff for: pkg/probe/probe.go

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ func (p *ProbeCollector) Probe(ctx context.Context, target string, hc *http.Clie
112112
probeWifiAPStatus,
113113
probeWifiClients,
114114
probeWifiManagedAP,
115+
probeBGPNeighborPathsIPv4,
116+
probeBGPNeighborPathsIPv6,
115117
} {
116118
m, ok := f(c, meta)
117119
if !ok {

Diff for: pkg/probe/testdata/router-bgp-paths-v4.jsonnet

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# api/v2/monitor/router/bgp/paths?vdom=*&count=10000
2+
[
3+
{
4+
"http_method":"GET",
5+
"results":[
6+
{
7+
"nlri_prefix":"0.0.0.0",
8+
"nlri_prefix_len":0,
9+
"learned_from":"10.0.0.1",
10+
"next_hop":"10.0.0.1",
11+
"origin":"igp",
12+
"is_best":true
13+
},{
14+
"nlri_prefix":"11.0.0.0/8",
15+
"nlri_prefix_len":0,
16+
"learned_from":"10.0.0.2",
17+
"next_hop":"10.0.0.2",
18+
"origin":"igp",
19+
"is_best":true
20+
},{
21+
"nlri_prefix":"10.0.0.0/8",
22+
"nlri_prefix_len":0,
23+
"learned_from":"10.0.0.2",
24+
"next_hop":"10.0.0.2",
25+
"origin":"igp",
26+
"is_best":false
27+
}
28+
],
29+
"vdom":"root",
30+
"path":"router",
31+
"name":"bgp",
32+
"action":"paths",
33+
"status":"success",
34+
"serial":"FGT61FT000000000",
35+
"version":"v7.0.0",
36+
"build":66
37+
}
38+
]

0 commit comments

Comments
 (0)