1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00
webvirtcloud/vrtManager/tests/test_interface_addresses.py
catborise 11348b2c37 fix(instances): Guest Info says when the agent fails and keeps what it answered
When the guest agent failed, the Guest Info section stayed empty with no
word: the request's error answer was not handled. An agent without the
time zone or host name command lost the OS details it had answered too.
The section now shows the agent's error, and a missing command leaves out
only its own details.
2026-10-26 13:49:37 +03:00

89 lines
4.2 KiB
Python

"""A running VM's addresses come from the guest agent when it answers and
from the host's ARP table, so a VM without an agent shows its IPv4 address.
The agent is asked with a timeout: interfaceAddresses() would wait for a
hung agent indefinitely, and the VM page with it."""
import json
import unittest
from unittest.mock import MagicMock, patch
from django.conf import settings
if not settings.configured:
settings.configure(MAC_OUI="52:54:10")
from libvirt import VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP, libvirtError
from vrtManager import instance as module
from vrtManager.instance import wvmInstance
MAC = "52:54:00:00:00:01"
ARP = {"vnet0": {"hwaddr": MAC, "addrs": [{"type": 0, "addr": "192.0.2.10", "prefix": 0}]}}
# guest-network-get-interfaces: the loopback has no hardware address
AGENT = [{"name": "lo", "ip-addresses": [{"ip-address-type": "ipv4", "ip-address": "127.0.0.1", "prefix": 8}]},
{"name": "eth0", "hardware-address": MAC, "ip-addresses": [
{"ip-address-type": "ipv4", "ip-address": "192.0.2.20", "prefix": 24},
{"ip-address-type": "ipv6", "ip-address": "2001:db8::20", "prefix": 64}]}]
def vm(agent):
inst = wvmInstance.__new__(wvmInstance)
inst._ip_cache = None
inst.instance = MagicMock()
inst.instance.interfaceAddresses.side_effect = lambda source: {VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP: ARP}[source]
patch.object(inst, "get_status", return_value=1).start()
patch.object(inst, "is_agent_ready", return_value=agent).start()
return inst
class InterfaceAddressesTestCase(unittest.TestCase):
def setUp(self):
self.agent = patch.object(module, "qemuAgentCommand", return_value=json.dumps({"return": AGENT})).start()
def tearDown(self):
patch.stopall()
def test_a_vm_without_an_agent_shows_its_arp_address(self):
inst = vm(agent=False)
self.assertEqual(inst.get_interface_addresses(MAC), (["192.0.2.10"], []))
self.agent.assert_not_called()
def test_the_agents_address_comes_first_and_is_asked_with_a_timeout(self):
inst = vm(agent=True)
self.assertEqual(inst.get_interface_addresses(MAC), (["192.0.2.20"], ["2001:db8::20/64"]))
self.agent.assert_called_once_with(inst.instance, '{"execute": "guest-network-get-interfaces"}', 10, 0)
self.assertEqual([c.args for c in inst.instance.interfaceAddresses.call_args_list],
[(VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP,)])
def test_an_agent_that_does_not_answer_leaves_the_arp_address(self):
self.agent.side_effect = libvirtError("guest agent didn't respond to command within '10' seconds")
self.assertEqual(vm(agent=True).get_interface_addresses(MAC), (["192.0.2.10"], []))
class OsInfoTestCase(unittest.TestCase):
def test_the_os_details_are_asked_with_a_timeout(self):
answers = {"guest-get-osinfo": {"kernel-release": "6.12"}, "guest-get-timezone": {"zone": "UTC"},
"guest-get-host-name": {"host-name": "web"}}
inst = wvmInstance.__new__(wvmInstance)
inst.instance = MagicMock()
with patch.object(module, "qemuAgentCommand",
side_effect=lambda dom, cmd, timeout, flags: json.dumps(
{"return": answers[json.loads(cmd)["execute"]]})) as agent:
self.assertEqual(inst.osinfo(), {"kernel-release": "6.12", "zone": "UTC", "host-name": "web"})
self.assertEqual({c.args[2] for c in agent.call_args_list}, {10})
def test_a_command_the_agent_lacks_leaves_out_only_its_details(self):
inst = wvmInstance.__new__(wvmInstance)
inst.instance = MagicMock()
def agent(dom, cmd, timeout, flags):
if json.loads(cmd)["execute"] == "guest-get-timezone":
raise libvirtError("The command guest-get-timezone has not been found")
return json.dumps({"return": {"guest-get-osinfo": {"id": "alma"}, "guest-get-host-name": {"host-name": "web"}}[
json.loads(cmd)["execute"]]})
with patch.object(module, "qemuAgentCommand", side_effect=agent):
self.assertEqual(inst.osinfo(), {"id": "alma", "host-name": "web"})
if __name__ == "__main__":
unittest.main()