proxmox: Get, ListByTag and task polling

Get follows spec entry 9's check order (lock and unknown status before
the name, suspended is Stopped after the name and mark checks), reads
status/current so paused is Stopped, and maps the three agent messages
of entry 7. ListByTag reads one config per candidate and fails whole
only on the resources call or an ended ctx. waitTask polls the UPID's
node every budgets.poll, logs failed tasks at V(1) and counts
"WARNINGS: <n>" as success; waitUnlocked serves Create's ownership check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:11:20 +00:00
co-authored by Claude Opus 5.5
parent e05d994974
commit 803716459a
12 changed files with 1497 additions and 25 deletions
+17 -5
View File
@@ -52,9 +52,12 @@ type fakeAPI struct {
type fakeVM struct {
node, name, status, qmpstatus, lock string
template bool
config map[string]string
ifaces []agentInterface
// kind is the resource type; empty is "qemu". Any other kind, such as
// "lxc", is listed but invisible to the qemu endpoints.
kind string
template bool
config map[string]string
ifaces []agentInterface
}
type fakeTask struct {
@@ -125,9 +128,15 @@ func (f *fakeAPI) addTemplate(vmid int, name string) *fakeVM {
}
func (f *fakeAPI) failWith(method string, err error) {
f.hookOn(method, func() error { return err })
}
// hookOn runs fn at the start of every call to method, without f.mu held;
// a non-nil result fails the call. A hook that changes state takes f.mu.
func (f *fakeAPI) hookOn(method string, fn func() error) {
f.mu.Lock()
defer f.mu.Unlock()
f.hooks[method] = func() error { return err }
f.hooks[method] = fn
}
func (f *fakeAPI) blockOn(method string) chan struct{} {
@@ -217,7 +226,7 @@ func (f *fakeAPI) enter(ctx context.Context, method string) error {
// node has no config file on this one.
func (f *fakeAPI) vm(node string, vmid int) (*fakeVM, error) {
vm, ok := f.vms[vmid]
if !ok || vm.node != node {
if !ok || vm.node != node || vm.kind != "" {
return nil, errVMMissing(node, vmid)
}
return vm, nil
@@ -279,6 +288,9 @@ func (f *fakeAPI) ClusterResources(ctx context.Context) ([]vmResource, error) {
for _, vmid := range slices.Sorted(maps.Keys(f.vms)) {
vm := f.vms[vmid]
r := vmResource{VMID: vmid, Name: vm.name, Node: vm.node, Type: "qemu", Status: vm.status, Lock: vm.lock, Tags: vm.config["tags"]}
if vm.kind != "" {
r.Type = vm.kind
}
if vm.template {
r.Template = 1
}