Skip to content

Commit 4f7d51e

Browse files
authored
Merge pull request #131 from PureStorage-OpenConnect/63-new-metric-proposal-controller-uptime-in-seconds-purefa_hw_controller_uptime_sec
[New metric] controller uptime in seconds & disable go debug metrics
2 parents de967d6 + 8bead70 commit 4f7d51e

25 files changed

+611
-410
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package collectors
2+
3+
import (
4+
client "purestorage/fa-openmetrics-exporter/internal/rest-client"
5+
6+
"github.com/prometheus/client_golang/prometheus"
7+
)
8+
9+
type ControllersCollector struct {
10+
ControllersModeSinceDesc *prometheus.Desc
11+
ControllersInfoDesc *prometheus.Desc
12+
Client *client.FAClient
13+
}
14+
15+
func (c *ControllersCollector) Describe(ch chan<- *prometheus.Desc) {
16+
prometheus.DescribeByCollect(c, ch)
17+
}
18+
19+
func (c *ControllersCollector) Collect(ch chan<- prometheus.Metric) {
20+
cl := c.Client.GetControllers()
21+
if len(cl.Items) == 0 {
22+
return
23+
}
24+
for _, ctl := range cl.Items {
25+
if ctl.ModeSince != 0 {
26+
ch <- prometheus.MustNewConstMetric(
27+
c.ControllersModeSinceDesc,
28+
prometheus.GaugeValue,
29+
// OpenMetrics timestamps MUST be in seconds, divide as an float64 to keep precision
30+
(float64(ctl.ModeSince) / 1000),
31+
ctl.Mode, ctl.Model, ctl.Name, ctl.Status, ctl.Type, ctl.Version,
32+
)
33+
}
34+
ch <- prometheus.MustNewConstMetric(
35+
c.ControllersInfoDesc,
36+
prometheus.GaugeValue,
37+
1,
38+
ctl.Mode, ctl.Model, ctl.Name, ctl.Status, ctl.Type, ctl.Version,
39+
)
40+
41+
}
42+
}
43+
44+
func NewControllersCollector(fa *client.FAClient) *ControllersCollector {
45+
return &ControllersCollector{
46+
ControllersModeSinceDesc: prometheus.NewDesc(
47+
"purefa_hw_controller_mode_since_timestamp_seconds",
48+
"FlashArray controller mode since change timestamp in seconds since UNIX epoch",
49+
[]string{"mode", "model", "name", "status", "type", "version"},
50+
prometheus.Labels{},
51+
),
52+
ControllersInfoDesc: prometheus.NewDesc(
53+
"purefa_hw_controller_info",
54+
"FlashArray controller info",
55+
[]string{"mode", "model", "name", "status", "type", "version"},
56+
prometheus.Labels{},
57+
),
58+
Client: fa,
59+
}
60+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package collectors
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"net/http/httptest"
8+
"os"
9+
"regexp"
10+
"strings"
11+
"testing"
12+
13+
client "purestorage/fa-openmetrics-exporter/internal/rest-client"
14+
)
15+
16+
func TestArrayControllersCollector(t *testing.T) {
17+
18+
rdr, _ := os.ReadFile("../../test/data/controllers.json")
19+
vers, _ := os.ReadFile("../../test/data/versions.json")
20+
var contl client.ControllersList
21+
json.Unmarshal(rdr, &contl)
22+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23+
url := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/controllers$`)
24+
if r.URL.Path == "/api/api_version" {
25+
w.Header().Set("Content-Type", "application/json")
26+
w.WriteHeader(http.StatusOK)
27+
w.Write([]byte(vers))
28+
} else if url.MatchString(r.URL.Path) {
29+
w.Header().Set("x-auth-token", "faketoken")
30+
w.Header().Set("Content-Type", "application/json")
31+
w.WriteHeader(http.StatusOK)
32+
w.Write([]byte(rdr))
33+
}
34+
}))
35+
endp := strings.Split(server.URL, "/")
36+
e := endp[len(endp)-1]
37+
want := make(map[string]bool)
38+
for _, ctl := range contl.Items {
39+
want[fmt.Sprintf("label:{name:\"mode\" value:\"%s\"} label:{name:\"model\"value:\"%s\"} label:{name:\"name\" value:\"%s\"} label:{name:\"status\" value:\"%s\"} label:{name:\"type\" value:\"%s\"} label:{name:\"version\" value:\"%s\"} gauge:{value:\"%g\"}", ctl.Mode, ctl.Model, ctl.Name, ctl.Status, ctl.Type, ctl.Version, (float64(ctl.ModeSince)/1000))] = true
40+
}
41+
42+
c := client.NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
43+
dc := NewControllersCollector(c)
44+
metricsCheck(t, dc, want)
45+
server.Close()
46+
}

internal/openmetrics-exporter/collector.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,29 @@ import (
55
client "purestorage/fa-openmetrics-exporter/internal/rest-client"
66

77
"github.com/prometheus/client_golang/prometheus"
8-
"github.com/prometheus/client_golang/prometheus/collectors"
8+
//"github.com/prometheus/client_golang/prometheus/collectors"
99
)
1010

1111
func Collector(ctx context.Context, metrics string, registry *prometheus.Registry, faclient *client.FAClient) bool {
1212

1313
arrayscoll := NewArraysCollector(faclient)
1414
registry.MustRegister(
15-
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
16-
collectors.NewGoCollector(),
15+
//collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
16+
//collectors.NewGoCollector(),
1717
arrayscoll,
1818
)
1919
if metrics == "all" || metrics == "array" {
2020
alertscoll := NewAlertsCollector(faclient)
2121
arrayperfcoll := NewArraysPerformanceCollector(faclient)
2222
arrayspacecoll := NewArraySpaceCollector(faclient)
2323
hwcoll := NewHardwareCollector(faclient)
24+
controllercol := NewControllersCollector(faclient)
2425
drcoll := NewDriveCollector(faclient)
2526
nicsperfcoll := NewNetworkInterfacesPerformanceCollector(faclient)
2627
portscoll := NewPortsCollector(faclient)
2728
interfacecoll := NewNetworkInterfacesCollector(faclient)
2829
registry.MustRegister(
30+
controllercol,
2931
alertscoll,
3032
arrayperfcoll,
3133
arrayspacecoll,
Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package client
22

3-
43
import (
5-
"testing"
6-
"regexp"
7-
"strings"
4+
"encoding/json"
85
"net/http"
96
"net/http/httptest"
10-
"encoding/json"
117
"os"
8+
"regexp"
9+
"strings"
10+
"testing"
1211

1312
"github.com/google/go-cmp/cmp"
1413
)
@@ -23,41 +22,41 @@ func TestAlerts(t *testing.T) {
2322
json.Unmarshal(ropen, &aopen)
2423
json.Unmarshal(rall, &aall)
2524
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26-
urlall := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts$`)
27-
urlopen := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts\?filter=state%3D%27open%27$`)
28-
if r.URL.Path == "/api/api_version" {
29-
w.Header().Set("Content-Type", "application/json")
30-
w.WriteHeader(http.StatusOK)
25+
urlall := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts$`)
26+
urlopen := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/alerts\?filter=state%3D%27open%27$`)
27+
if r.URL.Path == "/api/api_version" {
28+
w.Header().Set("Content-Type", "application/json")
29+
w.WriteHeader(http.StatusOK)
3130
w.Write([]byte(vers))
32-
} else if urlopen.MatchString(r.URL.Path + "?" + r.URL.RawQuery) {
31+
} else if urlopen.MatchString(r.URL.Path + "?" + r.URL.RawQuery) {
3332
w.Header().Set("x-auth-token", "faketoken")
3433
w.Header().Set("Content-Type", "application/json")
3534
w.WriteHeader(http.StatusOK)
3635
w.Write([]byte(ropen))
37-
} else if urlall.MatchString(r.URL.Path) {
36+
} else if urlall.MatchString(r.URL.Path) {
3837
w.Header().Set("x-auth-token", "faketoken")
3938
w.Header().Set("Content-Type", "application/json")
4039
w.WriteHeader(http.StatusOK)
4140
w.Write([]byte(rall))
4241
}
43-
}))
44-
endp := strings.Split(server.URL, "/")
45-
e := endp[len(endp)-1]
46-
t.Run("alerts_open", func(t *testing.T) {
47-
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
48-
al := c.GetAlerts("state='open'")
49-
if diff := cmp.Diff(al.Items, aopen.Items); diff != "" {
50-
t.Errorf("Mismatch (-want +got):\n%s", diff)
51-
server.Close()
52-
}
53-
})
54-
t.Run("alerts_all", func(t *testing.T) {
55-
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
56-
al := c.GetAlerts("")
57-
if diff := cmp.Diff(al.Items, aall.Items); diff != "" {
58-
t.Errorf("Mismatch (-want +got):\n%s", diff)
59-
server.Close()
60-
}
61-
})
62-
server.Close()
42+
}))
43+
endp := strings.Split(server.URL, "/")
44+
e := endp[len(endp)-1]
45+
t.Run("alerts_open", func(t *testing.T) {
46+
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
47+
al := c.GetAlerts("state='open'")
48+
if diff := cmp.Diff(al.Items, aopen.Items); diff != "" {
49+
t.Errorf("Mismatch (-want +got):\n%s", diff)
50+
server.Close()
51+
}
52+
})
53+
t.Run("alerts_all", func(t *testing.T) {
54+
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
55+
al := c.GetAlerts("")
56+
if diff := cmp.Diff(al.Items, aall.Items); diff != "" {
57+
t.Errorf("Mismatch (-want +got):\n%s", diff)
58+
server.Close()
59+
}
60+
})
61+
server.Close()
6362
}
Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package client
22

3-
43
import (
5-
"testing"
6-
"regexp"
7-
"strings"
4+
"encoding/json"
85
"net/http"
96
"net/http/httptest"
10-
"encoding/json"
11-
"os"
7+
"os"
8+
"regexp"
9+
"strings"
10+
"testing"
1211

1312
"github.com/google/go-cmp/cmp"
1413
)
@@ -17,29 +16,29 @@ func TestArraysPerformance(t *testing.T) {
1716

1817
res, _ := os.ReadFile("../../test/data/arrays_performance.json")
1918
vers, _ := os.ReadFile("../../test/data/versions.json")
20-
var arrsp ArraysPerformanceList
21-
json.Unmarshal(res, &arrsp)
19+
var arrsp ArraysPerformanceList
20+
json.Unmarshal(res, &arrsp)
2221
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23-
valid := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/arrays/performance$`)
24-
if r.URL.Path == "/api/api_version" {
25-
w.Header().Set("Content-Type", "application/json")
26-
w.WriteHeader(http.StatusOK)
27-
w.Write([]byte(vers))
28-
} else if valid.MatchString(r.URL.Path) {
22+
valid := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/arrays/performance$`)
23+
if r.URL.Path == "/api/api_version" {
24+
w.Header().Set("Content-Type", "application/json")
25+
w.WriteHeader(http.StatusOK)
26+
w.Write([]byte(vers))
27+
} else if valid.MatchString(r.URL.Path) {
2928
w.Header().Set("x-auth-token", "faketoken")
3029
w.Header().Set("Content-Type", "application/json")
3130
w.WriteHeader(http.StatusOK)
3231
w.Write([]byte(res))
3332
}
34-
}))
35-
endp := strings.Split(server.URL, "/")
36-
e := endp[len(endp)-1]
37-
t.Run("arrays_performance_1", func(t *testing.T) {
38-
defer server.Close()
39-
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
40-
apl := c.GetArraysPerformance()
41-
if diff := cmp.Diff(apl.Items[0], arrsp.Items[0]); diff != "" {
42-
t.Errorf("Mismatch (-want +got):\n%s", diff)
43-
}
44-
})
33+
}))
34+
endp := strings.Split(server.URL, "/")
35+
e := endp[len(endp)-1]
36+
t.Run("arrays_performance_1", func(t *testing.T) {
37+
defer server.Close()
38+
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
39+
apl := c.GetArraysPerformance()
40+
if diff := cmp.Diff(apl.Items[0], arrsp.Items[0]); diff != "" {
41+
t.Errorf("Mismatch (-want +got):\n%s", diff)
42+
}
43+
})
4544
}
Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package client
22

3-
43
import (
5-
"testing"
6-
"regexp"
7-
"strings"
4+
"encoding/json"
85
"net/http"
96
"net/http/httptest"
10-
"encoding/json"
117
"os"
8+
"regexp"
9+
"strings"
10+
"testing"
1211

1312
"github.com/google/go-cmp/cmp"
1413
)
@@ -20,26 +19,26 @@ func TestConnections(t *testing.T) {
2019
var conns ConnectionsList
2120
json.Unmarshal(res, &conns)
2221
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23-
valid := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/connections$`)
24-
if r.URL.Path == "/api/api_version" {
25-
w.Header().Set("Content-Type", "application/json")
26-
w.WriteHeader(http.StatusOK)
22+
valid := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/connections$`)
23+
if r.URL.Path == "/api/api_version" {
24+
w.Header().Set("Content-Type", "application/json")
25+
w.WriteHeader(http.StatusOK)
2726
w.Write([]byte(vers))
28-
} else if valid.MatchString(r.URL.Path) {
27+
} else if valid.MatchString(r.URL.Path) {
2928
w.Header().Set("x-auth-token", "faketoken")
3029
w.Header().Set("Content-Type", "application/json")
3130
w.WriteHeader(http.StatusOK)
3231
w.Write([]byte(res))
3332
}
34-
}))
35-
endp := strings.Split(server.URL, "/")
36-
e := endp[len(endp)-1]
37-
t.Run("connections_1", func(t *testing.T) {
38-
defer server.Close()
39-
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
40-
cl := c.GetConnections()
41-
if diff := cmp.Diff(cl.Items, conns.Items); diff != "" {
42-
t.Errorf("Mismatch (-want +got):\n%s", diff)
43-
}
44-
})
33+
}))
34+
endp := strings.Split(server.URL, "/")
35+
e := endp[len(endp)-1]
36+
t.Run("connections_1", func(t *testing.T) {
37+
defer server.Close()
38+
c := NewRestClient(e, "fake-api-token", "latest", "test-user-agent-string", false)
39+
cl := c.GetConnections()
40+
if diff := cmp.Diff(cl.Items, conns.Items); diff != "" {
41+
t.Errorf("Mismatch (-want +got):\n%s", diff)
42+
}
43+
})
4544
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package client
2+
3+
type Controllers struct {
4+
Name string `json:"name"`
5+
Mode string `json:"mode"`
6+
Model string `json:"model"`
7+
Status string `json:"status"`
8+
Type string `json:"type"`
9+
Version string `json:"version"`
10+
ModeSince int64 `json:"mode_since"`
11+
}
12+
13+
type ControllersList struct {
14+
ContinuationToken string `json:"continuation_token"`
15+
TotalItemCount int32 `json:"total_item_count"`
16+
MoreItemsRemaining bool `json:"more_items_remaining"`
17+
Items []Controllers `json:"items"`
18+
}
19+
20+
func (fa *FAClient) GetControllers() *ControllersList {
21+
uri := "/controllers"
22+
result := new(ControllersList)
23+
res, _ := fa.RestClient.R().
24+
SetResult(&result).
25+
Get(uri)
26+
if res.StatusCode() == 401 {
27+
fa.RefreshSession()
28+
fa.RestClient.R().
29+
SetResult(&result).
30+
Get(uri)
31+
}
32+
return result
33+
}

0 commit comments

Comments
 (0)