Added ZFS state metrics
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"lostak.dev/pve-exporter/proxmox"
|
||||
)
|
||||
|
||||
// PveNodeZfsCollector collects ZFS pool and component health metrics.
|
||||
type PveNodeZfsCollector struct {
|
||||
apiClient *proxmox.PveApiClient
|
||||
registry *TTLRegistry
|
||||
|
||||
state *TTLGaugeVec
|
||||
readErrors *TTLGaugeVec
|
||||
writeErrors *TTLGaugeVec
|
||||
checksumErrors *TTLGaugeVec
|
||||
}
|
||||
|
||||
// zfsMetricComponent is a flattened entry from the recursive ZFS topology.
|
||||
type zfsMetricComponent struct {
|
||||
proxmox.PveZfsComponent
|
||||
Path string
|
||||
}
|
||||
|
||||
// NewPveNodeZfsCollector creates a ZFS metrics collector.
|
||||
func NewPveNodeZfsCollector(apiClient *proxmox.PveApiClient, registry *TTLRegistry) *PveNodeZfsCollector {
|
||||
c := PveNodeZfsCollector{apiClient: apiClient, registry: registry}
|
||||
labelNames := []string{"cluster", "node", "pool", "component", "path", "leaf"}
|
||||
|
||||
c.state = NewTTLGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "pve_node_zfs_state",
|
||||
Help: "ZFS pool component state (1 = ONLINE, 0 = any other state).",
|
||||
},
|
||||
labelNames,
|
||||
5*time.Minute,
|
||||
)
|
||||
c.registry.Register(c.state)
|
||||
|
||||
c.readErrors = NewTTLGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "pve_node_zfs_read_errors",
|
||||
Help: "ZFS pool component read error count.",
|
||||
},
|
||||
labelNames,
|
||||
5*time.Minute,
|
||||
)
|
||||
c.registry.Register(c.readErrors)
|
||||
|
||||
c.writeErrors = NewTTLGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "pve_node_zfs_write_errors",
|
||||
Help: "ZFS pool component write error count.",
|
||||
},
|
||||
labelNames,
|
||||
5*time.Minute,
|
||||
)
|
||||
c.registry.Register(c.writeErrors)
|
||||
|
||||
c.checksumErrors = NewTTLGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "pve_node_zfs_checksum_errors",
|
||||
Help: "ZFS pool component checksum error count.",
|
||||
},
|
||||
labelNames,
|
||||
5*time.Minute,
|
||||
)
|
||||
c.registry.Register(c.checksumErrors)
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
// CollectMetrics implements PveMetricsCollector.
|
||||
func (c *PveNodeZfsCollector) CollectMetrics() error {
|
||||
cluster, err := c.apiClient.GetClusterStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, node := range cluster.NodeStatuses {
|
||||
pools, err := c.apiClient.GetNodeZfsPools(node.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pool := range *pools {
|
||||
status, err := c.apiClient.GetNodeZfsPoolStatus(node.Name, pool.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, component := range flattenZfsComponents(status) {
|
||||
labels := prometheus.Labels{
|
||||
"cluster": cluster.GetClusterName(),
|
||||
"node": node.Name,
|
||||
"pool": pool.Name,
|
||||
"component": component.Name,
|
||||
"path": component.Path,
|
||||
"leaf": strconv.FormatBool(component.Leaf == 1),
|
||||
}
|
||||
|
||||
if component.State != "" {
|
||||
c.state.With(labels).Set(zfsStateNumeric(component.State))
|
||||
}
|
||||
if component.Read != nil {
|
||||
c.readErrors.With(labels).Set(float64(*component.Read))
|
||||
}
|
||||
if component.Write != nil {
|
||||
c.writeErrors.With(labels).Set(float64(*component.Write))
|
||||
}
|
||||
if component.Checksum != nil {
|
||||
c.checksumErrors.With(labels).Set(float64(*component.Checksum))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetName implements PveMetricsCollector.
|
||||
func (c *PveNodeZfsCollector) GetName() string {
|
||||
return "Node ZFS"
|
||||
}
|
||||
|
||||
func flattenZfsComponents(status *proxmox.PveZfsPoolStatus) []zfsMetricComponent {
|
||||
components := make([]zfsMetricComponent, 0)
|
||||
appendZfsComponent(&components, status.PveZfsComponent, nil)
|
||||
return components
|
||||
}
|
||||
|
||||
func appendZfsComponent(components *[]zfsMetricComponent, component proxmox.PveZfsComponent, parents []string) {
|
||||
pathParts := append(append([]string{}, parents...), component.Name)
|
||||
*components = append(*components, zfsMetricComponent{
|
||||
PveZfsComponent: component,
|
||||
Path: strings.Join(pathParts, " > "),
|
||||
})
|
||||
|
||||
for _, child := range component.Children {
|
||||
appendZfsComponent(components, child, pathParts)
|
||||
}
|
||||
}
|
||||
|
||||
func zfsStateNumeric(state string) float64 {
|
||||
if strings.EqualFold(state, "ONLINE") {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user