mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-03 04:38:48 +00:00
Ceph Quincy and later (including Squid on PVE 9) dropped the monmap mons array and the mgrmap active_name/standbys arrays from ceph status output, replacing them with monmap.num_mons and mgrmap.num_standbys, with quorum membership reported at the top level of the payload. Both the host agent parser and the Proxmox API path only understood the legacy arrays, so modern clusters showed 0 monitors and undercounted managers. - hostagent: read num_mons/num_standbys and top-level quorum data, taking the largest available signal, and base the mon/mgr service rows on the same counts - pkg/proxmox: decode mgrmap num_standbys and top-level quorum_names/quorum on CephStatus - monitoring: fall back to the new fields when counting MON/MGR daemons, and log Ceph 401/403 failures at warn level with a hint to grant Sys.Audit on / instead of hiding them at debug - models: prefer the larger non-zero MON/MGR counts when merging Ceph cluster records from multiple sources Fixes #1626, Refs discussion #1290 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
84 lines
2.6 KiB
Go
84 lines
2.6 KiB
Go
package proxmox
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
// Issue #1626: PVE 9 / Ceph Squid payloads omit mgrmap num_mgrs/active_name
|
|
// and the monmap mons array. The Squid mgrmap only carries available +
|
|
// num_standbys, and quorum membership lives at the top level of the payload.
|
|
const issue1626SquidAPIPayload = `{
|
|
"data": {
|
|
"fsid": "9d4c2f0a-1626-4f6e-9b7a-squid0000001",
|
|
"health": {"status": "HEALTH_OK", "checks": {}, "mutes": []},
|
|
"election_epoch": 148,
|
|
"quorum": [0, 1, 2],
|
|
"quorum_names": ["pve1", "pve2", "pve3"],
|
|
"quorum_age": 4161,
|
|
"monmap": {
|
|
"epoch": 3,
|
|
"min_mon_release_name": "squid",
|
|
"num_mons": 3
|
|
},
|
|
"mgrmap": {
|
|
"available": true,
|
|
"num_standbys": 1,
|
|
"modules": ["balancer", "crash", "devicehealth"],
|
|
"services": {}
|
|
},
|
|
"servicemap": {"services": {}},
|
|
"osdmap": {"num_osds": 6, "num_up_osds": 6, "num_in_osds": 6},
|
|
"pgmap": {"num_pgs": 129, "bytes_total": 12002349744128, "bytes_used": 3000587436032, "bytes_avail": 9001762308096}
|
|
}
|
|
}`
|
|
|
|
func TestIssue1626GetCephStatusSquidSchema(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/cluster/ceph/status" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(issue1626SquidAPIPayload))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := &Client{baseURL: server.URL, httpClient: server.Client()}
|
|
status, err := client.GetCephStatus(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if status.MonMap.NumMons != 3 {
|
|
t.Errorf("MonMap.NumMons = %d, want 3", status.MonMap.NumMons)
|
|
}
|
|
if !status.MgrMap.Available {
|
|
t.Error("MgrMap.Available = false, want true")
|
|
}
|
|
if status.MgrMap.NumStandbys != 1 {
|
|
t.Errorf("MgrMap.NumStandbys = %d, want 1", status.MgrMap.NumStandbys)
|
|
}
|
|
if len(status.QuorumNames) != 3 {
|
|
t.Errorf("QuorumNames = %v, want 3 entries", status.QuorumNames)
|
|
}
|
|
if len(status.Quorum) != 3 {
|
|
t.Errorf("Quorum = %v, want 3 entries", status.Quorum)
|
|
}
|
|
}
|
|
|
|
// Legacy payloads with a standbys array must still populate NumStandbys.
|
|
func TestIssue1626CephMgrMapLegacyStandbysArray(t *testing.T) {
|
|
var mgrMap CephMgrMap
|
|
if err := mgrMap.UnmarshalJSON([]byte(`{"available": true, "active_name": "mgr-a", "standbys": ["mgr-b", "mgr-c"]}`)); err != nil {
|
|
t.Fatalf("UnmarshalJSON returned error: %v", err)
|
|
}
|
|
if mgrMap.NumStandbys != 2 {
|
|
t.Errorf("NumStandbys = %d, want 2 (derived from standbys array)", mgrMap.NumStandbys)
|
|
}
|
|
if len(mgrMap.Standbys) != 2 {
|
|
t.Errorf("Standbys = %v, want 2 entries", mgrMap.Standbys)
|
|
}
|
|
}
|