Add HostConfig + Cluster fields to models and update docs

This commit is contained in:
Rongsong Shen 2026-04-16 09:24:22 +00:00
parent 960ef62342
commit 00c4472d6c
9 changed files with 1805 additions and 9 deletions

View file

@ -14,15 +14,18 @@ var CLUSTERSTAT = map[string]int{
var JSONBCLFields = []string{"resources", "host_types", "host_models", "cluster_admins"}
type ClusterInfo struct {
ClusterName string `json:"cluster_name"`
Version string `json:"version"`
Status int `json:"status"`
ControllerName string `json:"controller_name"`
NumServers int `json:"num_servers"`
Resources string `json:"resources"`
HostTypes string `json:"host_types"`
HostModels string `json:"host_models"`
ClusterAdmins string `json:"cluster_admins"`
ClusterName string `json:"cluster_name"`
Version string `json:"version"`
Status int `json:"status"`
ControllerName string `json:"controller_name"`
NumServers int `json:"num_servers"`
Resources string `json:"resources"`
HostTypes string `json:"host_types"`
HostModels string `json:"host_models"`
ClusterAdmins string `json:"cluster_admins"`
HeartbeatInterval int `json:"heartbeat_interval"`
Vcpus int `json:"vcpus"`
InactiveHeartbeatCount int `json:"inactive_heartbeat_count"`
}
var CLUSTER_SUPPORT_FILTERS = []string{

View file

@ -0,0 +1,125 @@
package models
import (
"encoding/json"
"testing"
)
func TestClusterInfo_NewFields(t *testing.T) {
tests := []struct {
name string
cluster ClusterInfo
field string
want interface{}
}{
{
name: "HeartbeatInterval field exists and serializes",
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 30},
field: "heartbeat_interval",
want: float64(30),
},
{
name: "Vcpus field exists and serializes",
cluster: ClusterInfo{ClusterName: "test", Vcpus: 16},
field: "vcpus",
want: float64(16),
},
{
name: "InactiveHeartbeatCount field exists and serializes",
cluster: ClusterInfo{ClusterName: "test", InactiveHeartbeatCount: 3},
field: "inactive_heartbeat_count",
want: float64(3),
},
{
name: "HeartbeatInterval zero value serializes as 0 not omitted",
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
field: "heartbeat_interval",
want: float64(0),
},
{
name: "Vcpus zero value serializes as 0 not omitted",
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
field: "vcpus",
want: float64(0),
},
{
name: "InactiveHeartbeatCount zero value serializes as 0 not omitted",
cluster: ClusterInfo{ClusterName: "test", HeartbeatInterval: 0, Vcpus: 0, InactiveHeartbeatCount: 0},
field: "inactive_heartbeat_count",
want: float64(0),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := json.Marshal(tt.cluster)
if err != nil {
t.Fatalf("json.Marshal failed: %v", err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
got, ok := m[tt.field]
if !ok {
t.Errorf("field %q not found in JSON output; available keys: %v", tt.field, mapKeys(m))
} else if got != tt.want {
t.Errorf("field %q = %v, want %v", tt.field, got, tt.want)
}
})
}
}
func TestClusterInfo_ExistingFieldsUnchanged(t *testing.T) {
c := ClusterInfo{
ClusterName: "mycluster",
Version: "1.0",
Status: 1,
ControllerName: "ctrl1",
NumServers: 5,
Resources: `{"res1":"val1"}`,
HostTypes: "LINUX64",
HostModels: "modelA",
ClusterAdmins: "admin1,admin2",
}
data, err := json.Marshal(c)
if err != nil {
t.Fatalf("json.Marshal failed: %v", err)
}
var m map[string]interface{}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("json.Unmarshal failed: %v", err)
}
// Verify all existing fields still serialize correctly
existing := map[string]string{
"cluster_name": c.ClusterName,
"version": c.Version,
"controller_name": c.ControllerName,
"resources": c.Resources,
"host_types": c.HostTypes,
"host_models": c.HostModels,
"cluster_admins": c.ClusterAdmins,
}
for jsonKey, wantVal := range existing {
got, _ := m[jsonKey].(string)
if got != wantVal {
t.Errorf("existing field %q = %q, want %q", jsonKey, got, wantVal)
}
}
if got, _ := m["status"].(float64); int(got) != c.Status {
t.Errorf("existing field status = %v, want %d", got, c.Status)
}
if got, _ := m["num_servers"].(float64); int(got) != c.NumServers {
t.Errorf("existing field num_servers = %v, want %d", got, c.NumServers)
}
}
func mapKeys(m map[string]interface{}) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}

View file

@ -18,6 +18,9 @@ var HOSTSTAT = map[string]int{
}
func HostStateStr(state int) string {
if state == 0 {
return "UNKNOWN"
}
if (state & HOSTSTAT["UNAVAIL"]) != 0 {
return "UNAVAIL"
} else if (state & HOSTSTAT["CLOSED"]) != 0 {

View file

@ -0,0 +1,61 @@
package models
import "testing"
func TestHostStateStr(t *testing.T) {
tests := []struct {
name string
state int
want string
}{
{
name: "zero value returns UNKNOWN when no worker has reported",
state: 0,
want: "UNKNOWN",
},
{
name: "UNAVAIL flag",
state: HOSTSTAT["UNAVAIL"],
want: "UNAVAIL",
},
{
name: "CLOSED flag",
state: HOSTSTAT["CLOSED"],
want: "CLOSED",
},
{
name: "BUSY flag",
state: HOSTSTAT["BUSY"],
want: "BUSY",
},
{
name: "LOCKEDU flag",
state: HOSTSTAT["LOCKEDU"],
want: "LOCKU",
},
{
name: "LOCKEDW flag",
state: HOSTSTAT["LOCKEDW"],
want: "LOCKW",
},
{
name: "no flags set returns OK for real host status",
state: 0x00000001, // some non-zero, non-flag value
want: "OK",
},
{
name: "combined UNAVAIL+CLOSED flags",
state: HOSTSTAT["UNAVAIL"] | HOSTSTAT["CLOSED"],
want: "UNAVAIL", // UNAVAIL takes priority
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := HostStateStr(tt.state)
if got != tt.want {
t.Errorf("HostStateStr(%d) = %q, want %q", tt.state, got, tt.want)
}
})
}
}