R-1192 follow-up: report host swap vs the guests' swap allowance (log once, hub alarm, local API); R-1194: no OOB stanza before the first felhom-sshd pass; unreleased
gates / gates (push) Successful in 1m56s
gates / gates (push) Successful in 1m56s
This commit is contained in:
@@ -57,6 +57,10 @@ type MemoryInfo struct {
|
||||
MaxMB int64 `json:"max_mb"`
|
||||
FloorMB int64 `json:"floor_mb"`
|
||||
Running bool `json:"running"`
|
||||
// HostSwapMB / GuestSwapMB (R-1192 follow-up): the host's swap and this guest's swap allowance, so the controller
|
||||
// can tell the household when the host has too little swap behind the guest (its install page). nil = not read.
|
||||
HostSwapMB *int64 `json:"host_swap_mb,omitempty"`
|
||||
GuestSwapMB *int64 `json:"guest_swap_mb,omitempty"`
|
||||
}
|
||||
|
||||
// memoryResizeRequest is the POST /guest/memory body.
|
||||
@@ -94,6 +98,8 @@ type memoryBounds struct {
|
||||
maxMB int64
|
||||
floorMB int64
|
||||
running bool
|
||||
hostSwapMB int64
|
||||
guestSwapMB int64
|
||||
}
|
||||
|
||||
func (b memoryBounds) refusal(code string) memoryRefusal {
|
||||
@@ -104,7 +110,9 @@ func (b memoryBounds) refusal(code string) memoryRefusal {
|
||||
}
|
||||
|
||||
func (b memoryBounds) info(vmid int) MemoryInfo {
|
||||
hostSwap, guestSwap := b.hostSwapMB, b.guestSwapMB
|
||||
return MemoryInfo{
|
||||
HostSwapMB: &hostSwap, GuestSwapMB: &guestSwap,
|
||||
VMID: vmid, AllocatedMB: b.allocatedMB, UsageMB: b.usageMB, HostTotalMB: b.hostTotalMB,
|
||||
MinMB: b.minMB, MaxMB: b.maxMB, FloorMB: b.floorMB, Running: b.running,
|
||||
}
|
||||
@@ -139,6 +147,8 @@ func (s *Server) readMemoryBounds(ctx context.Context, vmid int) (memoryBounds,
|
||||
hostTotalMB: node.Memory.Total / mib, // bytes → MB, floor (conservative for max)
|
||||
minMB: minGuestMemoryMB,
|
||||
running: st.Status == "running",
|
||||
hostSwapMB: node.Swap.Total / mib, // R-1192 follow-up: the kernel's SwapTotal, as Proxmox reports it
|
||||
guestSwapMB: cfg.Swap, // PVE config swap is MB
|
||||
}
|
||||
b.maxMB = b.hostTotalMB - hostReserveMB
|
||||
b.floorMB = b.usageMB + shrinkUsageMarginMB
|
||||
|
||||
@@ -25,12 +25,14 @@ type fakeMemory struct {
|
||||
setUPID string
|
||||
setErr error
|
||||
applyReflect bool
|
||||
hostSwapB int64 // R-1192 follow-up: node swap total
|
||||
guestSwapMB int64
|
||||
}
|
||||
|
||||
func (f *fakeMemory) GuestConfig(_ context.Context, _ int) (proxmox.GuestConfig, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
return proxmox.GuestConfig{Memory: f.allocMB}, nil
|
||||
return proxmox.GuestConfig{Memory: f.allocMB, Swap: f.guestSwapMB}, nil
|
||||
}
|
||||
func (f *fakeMemory) GuestStatus(_ context.Context, vmid int) (proxmox.Guest, error) {
|
||||
f.mu.Lock()
|
||||
@@ -42,6 +44,7 @@ func (f *fakeMemory) NodeStatus(_ context.Context) (proxmox.NodeStatus, error) {
|
||||
defer f.mu.Unlock()
|
||||
var ns proxmox.NodeStatus
|
||||
ns.Memory.Total = f.hostTotalB
|
||||
ns.Swap.Total = f.hostSwapB
|
||||
return ns, nil
|
||||
}
|
||||
func (f *fakeMemory) SetConfig(_ context.Context, _ int, params map[string]string) (string, error) {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package localapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-1192 follow-up (night 2026-10-21): GET /guest/memory carries the host's swap and this guest's swap allowance, so
|
||||
// the controller's install page can say when the host has no swap behind the guest. 0 is a real answer (no swap);
|
||||
// absent means an older agent.
|
||||
// RED-PROOF: without the two MemoryInfo fields the keys are missing → fails on „host_swap_mb absent".
|
||||
func TestR1192_GuestMemoryCarriesHostAndGuestSwap(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
hostB, guestMB int64
|
||||
wantHost, wantG int64
|
||||
}{
|
||||
{"installer host (7.75 GB swap), guest swap = RAM", 7935 * mib, 8192, 7935, 8192},
|
||||
{"a host with NO swap", 0, 8192, 0, 8192},
|
||||
} {
|
||||
f := stdFixture()
|
||||
f.hostSwapB, f.guestSwapMB = c.hostB, c.guestMB
|
||||
w := do(t, memServer(t, f).Handler(), "GET", "/guest/memory", "A", "")
|
||||
var m struct {
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hs, ok := m.Data["host_swap_mb"].(float64)
|
||||
if !ok {
|
||||
t.Fatalf("%s: host_swap_mb absent: %s", c.name, w.Body.String())
|
||||
}
|
||||
gs, _ := m.Data["guest_swap_mb"].(float64)
|
||||
if int64(hs) != c.wantHost || int64(gs) != c.wantG {
|
||||
t.Fatalf("%s: host %v guest %v, want %d / %d", c.name, hs, gs, c.wantHost, c.wantG)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user