fix(vm_provision_mixin): get_vm_status hit a non-existent guest-agent path
agent.network_get_interfaces.get() built the URL path segment literally
("network_get_interfaces"), but the real Proxmox REST endpoint uses
hyphens ("network-get-interfaces") and must be reached via agent(...) as
a callable resource — the underscored attribute path 404ed silently on
every poll, so wait_for_ip always ran out the full timeout even though
the guest agent was reporting the IP to Proxmox correctly the whole time.
Also stopped assuming interfaces[0] is the real NIC — the guest agent
commonly reports "lo" first, matching the working pattern already used
in vm_mixin.py (skip "lo", require ip-address-type == "ipv4").
This commit is contained in:
@@ -537,20 +537,13 @@ class ProxmoxVMProvisionMixin:
|
||||
vmid_int = int(vmid)
|
||||
_logger.debug(f"Getting status for VM {vmid}")
|
||||
|
||||
# Get VM config to infer net0 MAC (for matching guest-agent results)
|
||||
# VM must exist / be readable before we start polling.
|
||||
try:
|
||||
config = self._node_api().qemu(vmid_int).config.get()
|
||||
self._node_api().qemu(vmid_int).config.get()
|
||||
except Exception:
|
||||
# VM may not exist yet or config not readable
|
||||
return {"status": "unknown"}
|
||||
|
||||
# Parse net0 MAC from config (if present)
|
||||
net0_line = config.get("net0", "")
|
||||
expected_mac = None
|
||||
# Example: "virtio,bridge=vmbr0,tag=10" — no explicit MAC
|
||||
# Proxmox auto-generates MACs in a deterministic pattern, but we'll
|
||||
# match by looking for the first NIC's IP in guest-agent results
|
||||
|
||||
# Polling loop
|
||||
start_time = time.time()
|
||||
while True:
|
||||
@@ -559,27 +552,34 @@ class ProxmoxVMProvisionMixin:
|
||||
raise RuntimeError(f"VM {vmid} failed to acquire IP within {timeout}s")
|
||||
|
||||
try:
|
||||
# Query guest-agent network interfaces
|
||||
agent_info = self._node_api().qemu(vmid_int).agent.network_get_interfaces.get()
|
||||
interfaces = agent_info.get("result", [])
|
||||
# Query guest-agent network interfaces. Proxmox's REST path is
|
||||
# "network-get-interfaces" (hyphens) — it must be passed as a
|
||||
# resource id via __call__, not dotted attribute access (which
|
||||
# would silently build a non-existent "network_get_interfaces"
|
||||
# path and 404 on every poll).
|
||||
agent_info = (
|
||||
self._node_api().qemu(vmid_int).agent("network-get-interfaces").get()
|
||||
)
|
||||
interfaces = (agent_info or {}).get("result", [])
|
||||
|
||||
# Find net0 (first interface with IP)
|
||||
if interfaces:
|
||||
net0_iface = interfaces[0] # Assumes net0 is first in list
|
||||
net0_mac = net0_iface.get("hardware-address", "")
|
||||
ip_addresses = net0_iface.get("ip-addresses", [])
|
||||
|
||||
if ip_addresses:
|
||||
# Found IP
|
||||
ip_info = ip_addresses[0]
|
||||
ip_addr = ip_info.get("ip-address", "")
|
||||
# The guest agent does not report interfaces in a fixed order —
|
||||
# "lo" commonly comes first. Skip it and take the first real
|
||||
# NIC that has an IPv4 address.
|
||||
for iface in interfaces:
|
||||
name = iface.get("name", "")
|
||||
if not name or name == "lo":
|
||||
continue
|
||||
for addr in iface.get("ip-addresses", []):
|
||||
if addr.get("ip-address-type") != "ipv4":
|
||||
continue
|
||||
ip_addr = addr.get("ip-address", "")
|
||||
if ip_addr:
|
||||
_logger.info(f"VM {vmid} acquired IP {ip_addr}")
|
||||
return {
|
||||
"status": "running",
|
||||
"ip_address": ip_addr,
|
||||
"hostname": net0_iface.get("name", ""),
|
||||
"mac_address": net0_mac,
|
||||
"hostname": name,
|
||||
"mac_address": iface.get("hardware-address", ""),
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user