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

fix(instances): disk edits keep their XML; changes lock per VM

Disk edits:
- edit_disk built a new <disk> from the form and replaced the old one,
  losing what the form does not show (e.g. encryption and virtio
  driver options); a bus change detached and re-attached the disk.
  The disk of the persistent definition is now edited in place and the
  domain defined once; a bus change picks a free device name and drops
  the old address and virtio-only options. Pending persistent changes of
  a running VM are kept.
- Clone omits empty disk devices, refuses a disk without a supported
  source and destination, and checks the name before allocating storage.

Locks:
- Every VM change held its compute's exclusive lock, so one slow
  operation blocked all VMs on that compute. A VM change now holds its
  own VM lock plus a shared compute lock; reconciliation, migration and
  cloning hold the compute exclusively (two clones of different VMs
  must not pass the name check together).
- Quota-checked changes of one user (CPU, memory and disk resize,
  clone) run one at a time under a per-user lock, also across computes,
  so concurrent requests cannot all pass the check against the old
  allocation. It is the outermost lock; superusers, who have no quota,
  skip it.
- A busy lock answers 409 to the API and a message in the web UI. API
  power and destroy actions return the view's error instead of success.
This commit is contained in:
catborise 2026-10-03 19:11:44 +03:00
parent 158d4db6c4
commit 51dedb7b8f
12 changed files with 510 additions and 269 deletions

View file

@ -175,3 +175,15 @@ class APISecurityTestCase(TestCase):
self.client.force_login(self.staff_user)
res = self.client.get(f"/api/v1/computes/{self.compute.id}/storages/")
self.assertEqual(res.status_code, 403)
def test_busy_instance_api_does_not_report_success(self):
self.client.force_login(self.regular_user)
with patch("instances.views.libvirt_instance_lock", side_effect=TimeoutError("busy")), patch(
"instances.models.wvmInstance"
) as proxy:
response = self.client.post(
f"/api/v1/computes/{self.compute.pk}/instances/{self.instance1.pk}/poweron/", {}
)
self.assertEqual(response.status_code, 409)
self.assertIn("busy", response.json()["detail"])
proxy.assert_not_called()