1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00

fix(instances): a VM whose guest agent hangs no longer stops its page

The VM page read the guest's addresses with interfaceAddresses() from
the agent, and the OS details with agent commands without a timeout.
A connected agent that hangs (a frozen guest) kept the page from
loading at all and held a worker: seen still loading after 120 s.

Both now go through the 10 s guest agent command the root password and
SSH key use. The addresses come from guest-network-get-interfaces, in
the form the page's address code already reads; an agent that does not
answer leaves the ARP address. With the agent frozen the page loads in
about 10 s and the OS details answer with the agent's error (400) in
5 s, instead of a server error.
This commit is contained in:
catborise 2026-10-25 15:48:22 +03:00
parent 75fcc6f6c0
commit 400b19cf42
4 changed files with 88 additions and 60 deletions

View file

@ -15,7 +15,6 @@ try:
from libvirt import (
VIR_DOMAIN_AFFECT_CONFIG,
VIR_DOMAIN_AFFECT_LIVE,
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT,
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP,
VIR_DOMAIN_XML_SECURE,
VIR_DOMAIN_XML_INACTIVE,
@ -51,7 +50,7 @@ try:
VIR_ERR_NO_DOMAIN_BACKUP,
libvirtError,
)
from libvirt_qemu import VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT, qemuAgentCommand
from libvirt_qemu import qemuAgentCommand
except Exception:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_DOMAIN_XML_INACTIVE, VIR_MIGRATE_LIVE
@ -375,36 +374,10 @@ class wvmInstance(wvmConnect):
self.instance = self.wvm.lookupByUUIDString(uuid) if uuid else self.get_instance(vname)
def osinfo(self):
info_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-osinfo"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
timezone_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-timezone"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
hostname_results = qemuAgentCommand(
self.instance,
'{"execute":"guest-get-host-name"}',
VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT,
0,
)
info_results = json.loads(info_results).get("return")
timezone_results = json.loads(timezone_results).get("return")
hostname_results = json.loads(hostname_results).get("return")
info_results.update(timezone_results)
info_results.update(hostname_results)
return info_results
info = self._agent_command("guest-get-osinfo")
info.update(self._agent_command("guest-get-timezone"))
info.update(self._agent_command("guest-get-host-name"))
return info
def start(self, flags=0):
self.instance.createWithFlags(flags)
@ -602,6 +575,18 @@ class wvmInstance(wvmConnect):
return self.instance.interfaceAddresses(source)
return {}
def _agent_interfaces(self):
"""The guest's interfaces from its agent, in interfaceAddresses()'s
form, which waits for a hung agent indefinitely."""
interfaces = {}
for iface in self._agent_command("guest-network-get-interfaces"):
addrs = []
for address in iface.get("ip-addresses", []):
kind = 0 if address["ip-address-type"] == "ipv4" else 1
addrs.append({"type": kind, "addr": address["ip-address"], "prefix": address["prefix"]})
interfaces[iface["name"]] = {"hwaddr": iface.get("hardware-address"), "addrs": addrs}
return interfaces
def refresh_interface_addresses(self):
self._ip_cache = {"qemuga": {}, "arp": {}}
@ -609,9 +594,8 @@ class wvmInstance(wvmConnect):
return
if self.is_agent_ready():
self._ip_cache["qemuga"] = self._get_interface_addresses(
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT
)
with contextlib.suppress(libvirtError):
self._ip_cache["qemuga"] = self._agent_interfaces()
self._ip_cache["arp"] = self._get_interface_addresses(VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP)
@ -2525,12 +2509,15 @@ class wvmInstance(wvmConnect):
return util.get_xml_path(self._XMLDesc(0), func=_get_agent)
def _agent_command(self, command, arguments):
"""One guest agent command that waits AGENT_TIMEOUT seconds at most:
libvirt's own calls for it wait for a hung agent indefinitely, and
the domain's agent timeout is shared by every agent call."""
request = json.dumps({"execute": command, "arguments": arguments})
qemuAgentCommand(self.instance, request, AGENT_TIMEOUT, 0)
def _agent_command(self, command, arguments=None):
"""The answer of one guest agent command that waits AGENT_TIMEOUT
seconds at most: libvirt's own calls for it wait for a hung agent
indefinitely, and the domain's agent timeout is shared by every agent
call."""
request = {"execute": command}
if arguments is not None:
request["arguments"] = arguments
return json.loads(qemuAgentCommand(self.instance, json.dumps(request), AGENT_TIMEOUT, 0))["return"]
def set_user_password(self, user, crypted):
"""Set a user's password in the running guest through its agent;