Added cluster mode metric
Build Docker image on push / docker (push) Successful in 22s
Build and push Docker image on tag / docker (push) Successful in 22s

This commit is contained in:
2026-08-17 21:29:09 +02:00
parent c4287c37bc
commit e5a5e6416b
4 changed files with 59 additions and 4 deletions
+4 -1
View File
@@ -58,6 +58,9 @@ metrics:
sdn: true # Enable collection of software-defined network (SDN) metrics. sdn: true # Enable collection of software-defined network (SDN) metrics.
``` ```
The cluster state collector exposes `pve_cluster_mode`, where `1` means that
Proxmox VE is configured as a cluster and `0` means that it is a standalone node.
## Build ## Build
To build the Docker image for PVE Exporter, use the following command: To build the Docker image for PVE Exporter, use the following command:
@@ -101,4 +104,4 @@ Contributions are welcome! Please open an issue or submit a pull request if you
## Support ## Support
If you encounter any issues or have questions, please open an issue on the project's GitHub repository. If you encounter any issues or have questions, please open an issue on the project's GitHub repository.
+16 -3
View File
@@ -12,8 +12,9 @@ type PveClusterStateCollector struct {
apiClient *proxmox.PveApiClient // PVE API client instance. apiClient *proxmox.PveApiClient // PVE API client instance.
registry *TTLRegistry // TTL metrics registry. registry *TTLRegistry // TTL metrics registry.
nodes *TTLGaugeVec // Count of nodes prometheus gauge. nodes *TTLGaugeVec // Count of nodes prometheus gauge.
quorate *TTLGaugeVec // Cluster quorum state prometheus gauge. quorate *TTLGaugeVec // Cluster quorum state prometheus gauge.
clusterMode *TTLGaugeVec // PVE cluster mode prometheus gauge.
} }
// Create new instance of PVE cluster state collector. // Create new instance of PVE cluster state collector.
@@ -42,6 +43,17 @@ func NewPveClusterStateCollector(apiClient *proxmox.PveApiClient, registry *TTLR
5*time.Minute, 5*time.Minute,
) )
c.registry.Register(c.quorate) c.registry.Register(c.quorate)
// PVE cluster mode gauge.
c.clusterMode = NewTTLGaugeVec(
prometheus.GaugeOpts{
Name: "pve_cluster_mode",
Help: "PVE cluster mode (1 = cluster, 0 = standalone node).",
},
[]string{"cluster"},
5*time.Minute,
)
c.registry.Register(c.clusterMode)
c.registry.StartCleanupLoop(5 * time.Second) c.registry.StartCleanupLoop(5 * time.Second)
return &c return &c
@@ -54,9 +66,10 @@ func (c *PveClusterStateCollector) CollectMetrics() error {
return err return err
} }
l := prometheus.Labels{"cluster": cluster.Name} l := prometheus.Labels{"cluster": cluster.GetClusterName()}
c.nodes.With(l).Set(float64(cluster.Nodes)) c.nodes.With(l).Set(float64(cluster.Nodes))
c.quorate.With(l).Set(float64(cluster.Quorate)) c.quorate.With(l).Set(float64(cluster.Quorate))
c.clusterMode.With(l).Set(cluster.GetClusterModeNumeric())
return nil return nil
} }
+9
View File
@@ -486,6 +486,15 @@ func (r *PveQemuStatus) IsRunning() bool {
return false return false
} }
// GetClusterModeNumeric returns whether the PVE installation is configured as a cluster.
// The cluster status API includes an object with type "cluster" only in cluster mode.
func (r *PveClusterStatus) GetClusterModeNumeric() float64 {
if r.Type == "cluster" {
return 1
}
return 0
}
// GetClusterName returns name of cluster for labeling purposes. // GetClusterName returns name of cluster for labeling purposes.
// If there is no cluster then return standalone node with node name. Otherwise, it returns cluster name. // If there is no cluster then return standalone node with node name. Otherwise, it returns cluster name.
func (r *PveClusterStatus) GetClusterName() string { func (r *PveClusterStatus) GetClusterName() string {
+30
View File
@@ -0,0 +1,30 @@
package proxmox
import "testing"
func TestPveClusterStatusGetClusterModeNumeric(t *testing.T) {
tests := []struct {
name string
status PveClusterStatus
want float64
}{
{
name: "cluster mode",
status: PveClusterStatus{Type: "cluster"},
want: 1,
},
{
name: "standalone node",
status: PveClusterStatus{},
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.status.GetClusterModeNumeric(); got != tt.want {
t.Fatalf("GetClusterModeNumeric() = %v, want %v", got, tt.want)
}
})
}
}