1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00

fix(instances): a paused VM's guest agent counts as not read

A paused guest cannot answer its agent, so the address search and the VM
report asked it nothing, but counted the VM neither as without an agent
nor as not read: the coverage line read as if every address had been
seen. It now counts among the agents not read, and the line names the
paused case.
This commit is contained in:
catborise 2026-10-24 22:49:25 +03:00
parent ecb850739f
commit a6218df6b2
4 changed files with 18 additions and 8 deletions

View file

@ -152,14 +152,14 @@ def _read_host(target, network, everything, mac):
"""The cheap first phase on one compute: leases and ARP of each running
VM in scope (with everything, the shut-off ones are listed too). Returns
the VMs' records, the observations, the (domain, vm, MACs) whose agent is
to be asked, the number of running VMs read and of those without a
connected agent; None when the compute cannot be reached (connecting may
to be asked, the number of running VMs read, of those without a
connected agent, and of paused ones whose agent cannot answer; None when the compute cannot be reached (connecting may
take the connect timeout, so this too runs on the compute's thread). A
VM that stops or leaves meanwhile is skipped. With a mac, only the VMs
with a NIC of that MAC, and only that NIC's addresses."""
if target.compute.status is not True:
return None
records, found, agents, checked, no_agent = [], [], [], 0, 0
records, found, agents, checked, no_agent, paused = [], [], [], 0, 0, 0
listing = 0 if everything else VIR_CONNECT_LIST_DOMAINS_ACTIVE
for dom in target.compute.connection.listAllDomains(listing):
try:
@ -187,9 +187,11 @@ def _read_host(target, network, everything, mac):
found += _matching(dom, vm, source, macs, network) or []
if not agent_connected(xml):
no_agent += 1
elif state == VIR_DOMAIN_RUNNING: # a paused guest cannot answer
elif state == VIR_DOMAIN_RUNNING:
agents.append((dom, vm, macs))
return records, found, agents, checked, no_agent
else:
paused += 1
return records, found, agents, checked, no_agent, paused
def _rows(observations):
"""One row per VM, MAC and address with its sources; sorted by address.
A VM is its compute and UUID: one UUID may be defined on two hosts."""
@ -236,11 +238,12 @@ def _survey(targets, network, deadline, everything, with_os=False, mac=None):
if read_host is None:
hosts_down.append(hosts[done].name)
continue
host_records, found, agents, host_checked, host_no_agent = read_host
host_records, found, agents, host_checked, host_no_agent, host_paused = read_host
records += host_records
observations += found
checked += host_checked
no_agent += host_no_agent
unread += host_paused # a paused guest cannot answer its agent
# the OS on its own thread: a hung OS read keeps the addresses read
for dom, vm, macs in agents:
agent_reads.append(pool.submit(_counted, _matching, dom, vm,

View file

@ -54,7 +54,7 @@
data-unregistered="{% trans "(not registered in the panel)" %}" data-conflict="{% trans "possible conflict" %}"
data-checked="{% trans "Running VMs checked: %(n)s." %}"
data-no-agent="{% trans "%(n)s had no connected guest agent; a static address on them cannot be seen." %}"
data-unread="{% trans "Guest agents not read (no answer in time, or an error): %(n)s." %}"
data-unread="{% trans "Guest agents not read (a paused VM, no answer in time, or an error): %(n)s." %}"
data-hosts-down="{% trans "Not reached: %(hosts)s." %}"
data-hosts-unfinished="{% trans "Not finished in time: %(hosts)s." %}"
data-read-at="{% trans "Read at %(time)s." %}"

View file

@ -171,6 +171,13 @@ class FindTests(unittest.TestCase):
result = self.find(addresses.Target(c, None))
self.assertEqual((result["checked"], result["no_agent"]), (2, 1))
def test_a_paused_vm_with_an_agent_is_an_agent_not_read(self):
# a paused guest cannot answer its agent: not "checked and empty"
paused = FakeDomain({ARP: {"e": iface(MAC1, (0, "192.0.2.10", 24))}}, state=3)
result = self.find(addresses.Target(compute("c1", paused), None))
self.assertEqual((result["checked"], result["no_agent"], result["unread"]), (1, 0, 1))
self.assertEqual([m["sources"] for m in result["matches"]], [["ARP"]])
def test_a_hung_agent_does_not_hold_the_answer(self):
hung = threading.Event()
try:

View file

@ -195,7 +195,7 @@ def vm_report(request):
f"VMs listed: {len(rows)}",
f"Running VMs read: {result['checked']}",
f"Without a connected guest agent (a static address on them cannot be seen): {result['no_agent']}",
f"Guest agents not read (no answer in time, or an error): {result['unread']}"]
f"Guest agents not read (a paused VM, no answer in time, or an error): {result['unread']}"]
if result["hosts_down"]:
notes.append("Hosts not reached: " + ", ".join(result["hosts_down"]))
if result["hosts_unfinished"]: