1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/tests/test_vm_states.py
catborise f3bdeddb0b fix(instances): a VM list poll for a host id that is not a number is 404
instances:states?compute=x raised ValueError in the lookup, a 500.
2026-11-06 13:46:27 +03:00

93 lines
5.4 KiB
Python

"""The VM lists follow libvirt: instances:states gives the states of the
listed VMs, read in one call per host; vm_states.js polls it and reloads the
list when an answer differs from the one before. A user who does not see
hosts gets only the hosts and VMs of their own."""
import json
from unittest.mock import MagicMock, PropertyMock, patch
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
from libvirt import libvirtError
from lxml import html
from accounts.models import UserInstance
from computes.models import Compute
from instances.models import Instance
UP = {"aaaaaaaa-0000-4000-8000-000000000001": ["web", 1], "aaaaaaaa-0000-4000-8000-000000000002": ["db", 5]}
class VmStatesTestCase(TestCase):
def setUp(self):
self.up = Compute.objects.create(name="up", hostname="192.0.2.70", login="u", password="p", type=1)
self.down = Compute.objects.create(name="down", hostname="192.0.2.71", login="u", password="p", type=1)
self.web = Instance.objects.create(compute=self.up, name="web", uuid="aaaaaaaa-0000-4000-8000-000000000001")
self.admin = get_user_model().objects.create_superuser("st-admin", "st@example.com", "pw")
def get(self, user, query="", read=lambda: dict(UP)):
self.client.force_login(user)
proxy = MagicMock()
proxy.get_domain_states.side_effect = read
# self.up answers, self.down cannot be reached
with patch.object(Compute, "status", property(lambda c: c.pk == self.up.pk)), \
patch.object(Compute, "proxy", new_callable=PropertyMock, return_value=proxy):
return self.client.get(reverse("instances:states") + query)
def test_an_administrator_gets_every_host_and_a_host_that_cannot_be_read(self):
response = self.get(self.admin)
self.assertEqual(response["Cache-Control"], "no-store")
self.assertEqual(response.json()["computes"], {str(self.up.pk): {"ok": True, "vms": UP},
str(self.down.pk): {"ok": False}})
def test_a_host_whose_listing_fails_cannot_be_read(self):
def fails():
raise libvirtError("Cannot recv data")
self.assertEqual(self.get(self.admin, read=fails).json()["computes"][str(self.up.pk)], {"ok": False})
def test_a_user_gets_the_hosts_and_vms_of_their_own(self):
owner = get_user_model().objects.create_user("st-owner", password="pw")
UserInstance.objects.create(user=owner, instance=self.web)
self.assertEqual(self.get(owner).json()["computes"],
{str(self.up.pk): {"ok": True, "vms": {self.web.uuid: ["web", 1]}}})
stranger = get_user_model().objects.create_user("st-stranger", password="pw")
self.assertEqual(self.get(stranger).json()["computes"], {})
def test_a_host_id_that_is_not_a_number_is_not_found(self):
self.assertEqual(self.get(self.admin, "?compute=x").status_code, 404)
def test_one_host_s_list_is_for_administrators(self):
self.assertEqual(self.get(self.admin, f"?compute={self.up.pk}").json()["computes"],
{str(self.up.pk): {"ok": True, "vms": UP}})
owner = get_user_model().objects.create_user("st-owner2", password="pw")
self.assertEqual(self.get(owner, f"?compute={self.up.pk}").status_code, 403)
def test_a_user_s_uuid_on_another_host_is_not_theirs(self):
# one UUID may be defined on two hosts: a VM is its host and UUID
other = Compute.objects.create(name="other", hostname="192.0.2.72", login="u", password="p", type=1)
Instance.objects.create(compute=other, name="mine-too", uuid="aaaaaaaa-0000-4000-8000-000000000009")
owner = get_user_model().objects.create_user("st-owner3", password="pw")
UserInstance.objects.create(user=owner, instance=self.web)
UserInstance.objects.create(user=owner, instance=Instance.objects.get(name="mine-too"))
self.client.force_login(owner)
proxy = MagicMock()
proxy.get_domain_states.return_value = dict(UP) # both hosts list the same UUIDs
with patch.object(Compute, "status", True), \
patch.object(Compute, "proxy", new_callable=PropertyMock, return_value=proxy):
computes = self.client.get(reverse("instances:states")).json()["computes"]
self.assertEqual(computes, {str(self.up.pk): {"ok": True, "vms": {self.web.uuid: ["web", 1]}},
str(other.pk): {"ok": True, "vms": {}}})
def test_the_lists_poll_it(self):
self.client.force_login(self.admin)
with patch.object(Compute, "status", property(lambda c: c.pk == self.up.pk)), \
patch.object(Compute, "proxy", new_callable=PropertyMock,
return_value=MagicMock(**{"get_domain_rows.return_value": {}})), \
patch("instances.views.utils.refr"), patch("computes.views.utils.refresh_instance_database"):
for url, states_url in ((reverse("instances:index"), reverse("instances:states")),
(reverse("instances", args=[self.up.pk]),
reverse("instances:states") + f"?compute={self.up.pk}")):
with self.subTest(url=url):
page = html.fromstring(self.client.get(url).content)
self.assertEqual(json.loads(page.get_element_by_id("vm-states-url").text), states_url)