proxmox: Create by cloning a template, seed upload, config, start
Create validates placement, then under the per-name lock adopts the VM a previous attempt left (by name, or at a taken VMID after the ownership check of spec entry 2) or full-clones the template onto the UID's deterministic VMID with the mark in the clone call. It then uploads the seed over any existing file, writes cores, memory, agent, tag, ide2 and net0 when configured, grows the boot disk when diskGiB is larger, starts, and waits for running/running. waitUnlocked no longer waits on a suspended lock: a hibernated VM is judged at once, so ours is Permanent and a foreign one is probed past. The template is checked only on the allocation path, and its shape (net0, boot disk size) is refused before any clone exists. Spec entries 2, 4 and 10 updated to match. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,9 @@ func (p *Provider) waitTask(ctx context.Context, log logr.Logger, upid string, b
|
||||
}
|
||||
|
||||
// waitUnlocked polls a VM's config until no task holds its lock and returns
|
||||
// that config, for Create's ownership check (spec entry 2). A VM that
|
||||
// that config, for Create's ownership check (spec entry 2). A suspended lock
|
||||
// is returned at once: it marks a hibernated VM, not a task, and never
|
||||
// clears on its own, so waiting would only burn the clone budget. A VM that
|
||||
// disappears meanwhile, as a failed clone does, returns the does-not-exist
|
||||
// error for the caller to judge.
|
||||
func (p *Provider) waitUnlocked(ctx context.Context, node string, vmid int, budget time.Duration) (vmConfig, error) {
|
||||
@@ -100,10 +102,27 @@ func (p *Provider) waitUnlocked(ctx context.Context, node string, vmid int, budg
|
||||
return false, fmt.Errorf("reading config of VM %d: %w", vmid, err)
|
||||
}
|
||||
cfg = c
|
||||
return c["lock"] == "", nil
|
||||
return c["lock"] == "" || c["lock"] == lockSuspended, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// waitRunning polls status/current after the start task until QEMU runs
|
||||
// the guest (spec entry 10, step 9). A VM that is stopped again is
|
||||
// Transient at once: it will not come up on its own, and the next attempt
|
||||
// adopts and starts it.
|
||||
func (p *Provider) waitRunning(ctx context.Context, node string, vmid int) error {
|
||||
return pollUntil(ctx, p.budgets.poll, p.budgets.running, fmt.Sprintf("VM %d to run", vmid), func() (bool, error) {
|
||||
st, err := p.api.VMStatus(ctx, node, vmid)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("reading status of VM %d: %w", vmid, err)
|
||||
}
|
||||
if st.Status == "stopped" {
|
||||
return false, transientf("VM %d stopped right after its start", vmid)
|
||||
}
|
||||
return st.Status == "running" && st.QMPStatus == "running", nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user