From 63aba5d9c91c28ab153d2a27ad3c80817c3bd6f5 Mon Sep 17 00:00:00 2001 From: catborise Date: Mon, 26 Oct 2026 14:18:33 +0300 Subject: [PATCH] fix(storages): pool pages work while a volume is cloned, failed pools are undefined - libvirt refuses to refresh a pool while a volume is cloned in it. The pool page, the volume list of the VM and create pages, upload planning and the API no longer fail on that refusal; they use the list libvirt has. - A new pool that does not start (unreachable NFS server, a device LVM cannot use) is undefined again, so the name is free for another try. - A pool reporting no size, such as an iSCSI pool without LUNs, shows its page instead of a division error. - Each clone dialog shows its own format and metadata rows, so a format is never shown without Convert ticked and silently ignored. - The new-volume size is labelled in GiB and is at least 1; an inactive pool shows no "None" volume count. --- storages/api/viewsets.py | 7 ++++-- storages/forms.py | 2 +- storages/jobs.py | 5 +++- storages/templates/storage.html | 25 ++++++-------------- storages/templates/storages.html | 2 +- storages/tests/test_pool_page.py | 40 ++++++++++++++++++++++++++++++++ storages/tests/test_pool_xml.py | 14 ++++++++++- storages/views.py | 7 +++--- vrtManager/storage.py | 31 +++++++++++-------------- 9 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 storages/tests/test_pool_page.py diff --git a/storages/api/viewsets.py b/storages/api/viewsets.py index 3af5d0c6..177ee761 100644 --- a/storages/api/viewsets.py +++ b/storages/api/viewsets.py @@ -1,9 +1,12 @@ +import contextlib + from admin.permissions import IsSuperUser from appsettings.settings import app_settings from computes.models import Compute from computes.utils import libvirt_compute_lock from django.shortcuts import get_object_or_404 from drf_spectacular.utils import extend_schema, inline_serializer +from libvirt import libvirtError from logs.audit import audit, failed from rest_framework import serializers, status, viewsets from rest_framework.decorators import action @@ -136,7 +139,6 @@ class VolumeViewSet(viewsets.ViewSet): state = conn.is_active() if state: - conn.refresh() volume_queryset = conn.update_volumes() else: volume_queryset = None @@ -176,7 +178,8 @@ class VolumeViewSet(viewsets.ViewSet): subject, action = f"Storage pool {storage_pk}", f"add volume {serializer.validated_data['name']}" state = conn.is_active() if state: - conn.refresh() + with contextlib.suppress(libvirtError): # refused while a volume is cloned in the pool + conn.refresh() with audit(request, compute, subject, action) as entry: ret = conn.create_volume( serializer.validated_data["name"], diff --git a/storages/forms.py b/storages/forms.py index d316acd3..05e10729 100644 --- a/storages/forms.py +++ b/storages/forms.py @@ -73,7 +73,7 @@ class CreateVolumeForm(forms.Form): required=True, choices=(("qcow2", "qcow2 (recommended)"), ("qcow", "qcow"), ("raw", "raw")), ) - size = forms.IntegerField() + size = forms.IntegerField(label=_("Size (GiB)"), min_value=1) meta_prealloc = forms.BooleanField(required=False) def clean_name(self): diff --git a/storages/jobs.py b/storages/jobs.py index 124c9ff4..9f693e4f 100644 --- a/storages/jobs.py +++ b/storages/jobs.py @@ -1,10 +1,12 @@ """Jobs of the storages app (operations framework).""" +import contextlib import logging import os from django.core.exceptions import PermissionDenied from django.utils.translation import gettext_noop +from libvirt import libvirtError from vrtManager import util from vrtManager.storage import wvmStorage @@ -109,7 +111,8 @@ class UploadVolume(Kind): def plan(self, user, params): compute, stg = _storage(params) - stg.refresh() + with contextlib.suppress(libvirtError): # refused while a volume is cloned in the pool + stg.refresh() if params["name"] in stg.get_volumes(): raise FileExistsError(f"The pool has a volume named {params['name']}") pool = claims.pool_key(compute.pk, params["pool"]) diff --git a/storages/templates/storage.html b/storages/templates/storage.html index bf5839cb..a9598b98 100644 --- a/storages/templates/storage.html +++ b/storages/templates/storage.html @@ -130,7 +130,7 @@ -
+
@@ -200,24 +200,13 @@ {% job_panel_script job_resource %}