Skip to content

Commit 3e0fd5f

Browse files
authored
Merge pull request #91 from PureStorage-OpenConnect/add_drives
Add purefa_drive metric
2 parents ca5991d + f063b37 commit 3e0fd5f

File tree

8 files changed

+214
-8
lines changed

8 files changed

+214
-8
lines changed

internal/openmetrics-exporter/collector.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ func Collector(ctx context.Context, metrics string, registry *prometheus.Registr
2020
arrayperfcoll := NewArraysPerformanceCollector(faclient)
2121
arrayspacecoll := NewArraySpaceCollector(faclient)
2222
hwcoll := NewHardwareCollector(faclient)
23+
drcoll := NewDriveCollector(faclient)
2324
nicsperfcoll := NewNetworkInterfacesPerformanceCollector(faclient)
2425
registry.MustRegister(
2526
alertscoll,
2627
arrayperfcoll,
2728
arrayspacecoll,
2829
hwcoll,
30+
drcoll,
2931
nicsperfcoll,
3032
)
3133
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package collectors
2+
3+
4+
import (
5+
"github.com/prometheus/client_golang/prometheus"
6+
"purestorage/fa-openmetrics-exporter/internal/rest-client"
7+
)
8+
9+
type DriveCollector struct {
10+
CapacityDesc *prometheus.Desc
11+
Client *client.FAClient
12+
}
13+
14+
func (c *DriveCollector) Describe(ch chan<- *prometheus.Desc) {
15+
prometheus.DescribeByCollect(c, ch)
16+
}
17+
18+
func (c *DriveCollector) Collect(ch chan<- prometheus.Metric) {
19+
drl := c.Client.GetDrives()
20+
if len(drl.Items) == 0 {
21+
return
22+
}
23+
for _, d := range drl.Items {
24+
ch <- prometheus.MustNewConstMetric(
25+
c.CapacityDesc,
26+
prometheus.GaugeValue,
27+
d.Capacity,
28+
d.Name, d.Type, d.Status, d.Protocol,
29+
)
30+
}
31+
}
32+
33+
func NewDriveCollector(fa *client.FAClient) *DriveCollector {
34+
return &DriveCollector{
35+
CapacityDesc: prometheus.NewDesc(
36+
"purefa_drive_capacity_bytes",
37+
"FlashArray drive capacity in bytes",
38+
[]string{"component_name", "component_type", "component_status", "component_protocol"},
39+
prometheus.Labels{},
40+
),
41+
Client: fa,
42+
}
43+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package collectors
2+
3+
4+
import (
5+
"fmt"
6+
"testing"
7+
"regexp"
8+
"strings"
9+
"net/http"
10+
"net/http/httptest"
11+
"encoding/json"
12+
"os"
13+
14+
"purestorage/fa-openmetrics-exporter/internal/rest-client"
15+
)
16+
17+
func TestDriveCollector(t *testing.T) {
18+
19+
rdr, _ := os.ReadFile("../../test/data/drives.json")
20+
vers, _ := os.ReadFile("../../test/data/versions.json")
21+
var drl client.DriveList
22+
json.Unmarshal(rdr, &drl)
23+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
24+
url := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/drive$`)
25+
if r.URL.Path == "/api/api_version" {
26+
w.Header().Set("Content-Type", "application/json")
27+
w.WriteHeader(http.StatusOK)
28+
w.Write([]byte(vers))
29+
} else if url.MatchString(r.URL.Path) {
30+
w.Header().Set("x-auth-token", "faketoken")
31+
w.Header().Set("Content-Type", "application/json")
32+
w.WriteHeader(http.StatusOK)
33+
w.Write([]byte(rdr))
34+
}
35+
}))
36+
endp := strings.Split(server.URL, "/")
37+
e := endp[len(endp)-1]
38+
want := make(map[string]bool)
39+
for _, d := range drl.Items {
40+
want[fmt.Sprintf("label:{name:\"component_name\" value:\"%s\"} label:{name:\"component_status\" value:\"%s\"} label:{name:\"component_type\" value:\"%s\"} label:{name:\"component_type\" value:\"%s\"} gauge:{value:\"%s\"}", d.Name, d.Type, d.Status, d.Protocol, d.Capacity)] = true
41+
}
42+
}
43+
c := client.NewRestClient(e, "fake-api-token", "latest", false)
44+
dc := NewDriveCollector(c)
45+
metricsCheck(t, dc, want)
46+
server.Close()
47+
}

internal/rest-client/drives.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package client
2+
3+
4+
type Drive struct {
5+
Name string `json:"name"`
6+
Details string `json:"details"`
7+
Capacity float64 `json:"capacity"`
8+
Protocol string `json:"protocol"`
9+
Status string `json:"status"`
10+
Type string `json:"type"`
11+
}
12+
13+
type DriveList struct {
14+
ContinuationToken string `json:"continuation_token"`
15+
TotalItemCount int `json:"total_item_count"`
16+
MoreItemsRemaining bool `json:"more_items_remaining"`
17+
Items []Drive `json:"items"`
18+
}
19+
20+
func (fa *FAClient) GetDrives() *DriveList {
21+
uri := "/drives"
22+
result := new(DriveList)
23+
res, err := fa.RestClient.R().
24+
SetResult(&result).
25+
Get(uri)
26+
if err != nil {
27+
fa.Error = err
28+
}
29+
if res.StatusCode() == 401 {
30+
fa.RefreshSession()
31+
}
32+
res, err = fa.RestClient.R().
33+
SetResult(&result).
34+
Get(uri)
35+
if err != nil {
36+
fa.Error = err
37+
}
38+
39+
return result
40+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package client
2+
3+
4+
import (
5+
"testing"
6+
"regexp"
7+
"strings"
8+
"net/http"
9+
"net/http/httptest"
10+
"encoding/json"
11+
"os"
12+
13+
"github.com/google/go-cmp/cmp"
14+
)
15+
16+
func TestDrive(t *testing.T) {
17+
18+
res, _ := os.ReadFile("../../test/data/drive.json")
19+
vers, _ := os.ReadFile("../../test/data/versions.json")
20+
var dr DriveList
21+
json.Unmarshal(res, &dr)
22+
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23+
valid := regexp.MustCompile(`^/api/([0-9]+.[0-9]+)?/drives$`)
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) {
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(res))
33+
}
34+
}))
35+
endp := strings.Split(server.URL, "/")
36+
e := endp[len(endp)-1]
37+
t.Run("drive_1", func(t *testing.T) {
38+
defer server.Close()
39+
c := NewRestClient(e, "fake-api-token", "latest", false)
40+
dl := c.GetDrives()
41+
if diff := cmp.Diff(dl.Items, dr.Items); diff != "" {
42+
t.Errorf("Mismatch (-want +got):\n%s", diff)
43+
}
44+
})
45+
}

internal/rest-client/flasharray_client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Client interface {
2020
GetHostsPerformance() *HostsPerformanceList
2121
GetHostsBalance() *HostsBalanceList
2222
GetHardware() *HardwareList
23+
GetDrive() *DriveList
2324
GetPods() *PodsList
2425
GetPodsPerformance() *PodsPerformanceList
2526
GetVolumes() *VolumesList

specification/metrics/purefa-metrics.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This document describes the semantic conventions for Pure FlashArray Metrics.
1313
- [`purefa_array` - FlashArray metrics](#purefa_array---flasharray-metrics)
1414
- [`purefa_directory` - FlashArray File Directory metrics](#purefa_directory---flasharray-file-directory-metrics)
1515
- [`purefa_host` - Host metrics](#purefa_host---host-metrics)
16+
- [`purefa_drive` - FlashArray Drive metrics](#purefa_drive---flasharray-drive-metrics)
1617
- [`purefa_hw` - Hardware metrics](#purefa_hw---hardware-metrics)
1718
- [`purefa_network` - Network metrics](#purefa_network---network-metrics)
1819
- [`purefa_pod` - Pod metrics](#purefa_pod---pod-metrics)
@@ -22,14 +23,14 @@ This document describes the semantic conventions for Pure FlashArray Metrics.
2223

2324
## Collections by Endpoint
2425

25-
| Endpoint | Description | Metrics Instruments collected |
26-
| -------------------- | ------------------------ | ----------------------------------------------------------------------------- |
27-
| /metrics | Full array metrics | all |
28-
| /metrics/array | Array only metrics | `purefa_info`, `purefa_alerts`, `purefa_array`, `purefa_hw`, `purefa_network` |
29-
| /metrics/directories | Directories only metrics | `purefa_info`, `purefa_directory` |
30-
| /metrics/hosts | Hosts only metrics | `purefa_info`, `purefa_host` |
31-
| /metrics/pods | Pods only metrics | `purefa_info`, `purefa_pod` |
32-
| /metrics/volumes | Volumes only metrics | `purefa_info`, `purefa_volume` |
26+
| Endpoint | Description | Metrics Instruments collected |
27+
| -------------------- | ------------------------ | --------------------------------------------------------------------------------------------- |
28+
| /metrics | Full array metrics | all |
29+
| /metrics/array | Array only metrics | `purefa_info`, `purefa_alerts`, `purefa_array`, `purefa_hw`, `purefa_network`, `purefa_drive` |
30+
| /metrics/directories | Directories only metrics | `purefa_info`, `purefa_directory` |
31+
| /metrics/hosts | Hosts only metrics | `purefa_info`, `purefa_host` |
32+
| /metrics/pods | Pods only metrics | `purefa_info`, `purefa_pod` |
33+
| /metrics/volumes | Volumes only metrics | `purefa_info`, `purefa_volume` |
3334

3435
## Metric Statuses
3536

@@ -114,6 +115,18 @@ This document describes the semantic conventions for Pure FlashArray Metrics.
114115
| Available | purefa_host_space_data_reduction_ratio | FlashArray host space data reduction | ratio | Gauge | Double | `host` | (host name) |
115116

116117

118+
### `purefa_drive` - FlashArray Drive metrics
119+
120+
**Description:** FlashArray drive metrics
121+
122+
| Status | Name | Description | Units | Metric Type ([*](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#metric-types)) | Value Type | Attribute Key | Attribute Values |
123+
| --------- | --------------------------- | ---------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------- | ---------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
124+
| Available | purefa_drive_capacity_bytes | FlashArray drive capacity in bytes | bytes | Gauge | Double | `component_name` | (name) |
125+
| | | | | | | `component_protocol` | `NVMe`, `SAS` |
126+
| | | | | | | `component_type` | `cache`, `NVRAM`, `SSD`, `virtual` |
127+
| | | | | | | `component_status` | `empty`, `failed`, `healthy`, `identifying`, `missing`, `recovering`, `unadmitted`, `unhealthy`, `unrecognized`, `updating` |
128+
129+
117130
### `purefa_hw` - Hardware metrics
118131

119132
**Description:** FlashArray hardware metrics

test/data/drives.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"continuation_token": null,
3+
"items": [
4+
{
5+
"name": "SH0.BAY10",
6+
"capacity": 1099511627776,
7+
"details": null,
8+
"protocol": "SAS",
9+
"status": "healthy",
10+
"type": "SSD"
11+
}
12+
],
13+
"more_items_remaining": false,
14+
"total_item_count": null
15+
}

0 commit comments

Comments
 (0)