mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
fix(instances): two owners added at once keep a VM to one owner
With one owner per VM, the VM page and a user's page counted the VM's owners and then added one in separate steps, so two owners added at the same moment both found the VM without one, and it got two. The check and the insert now run in one transaction that holds the ownership lock (PostgreSQL's advisory lock; SQLite's IMMEDIATE transactions already serialize them): the second waits for the first and is refused. The audit row is written after the transaction, so a failure keeps its row.
This commit is contained in:
parent
5e93920d78
commit
cd2dd517b7
4 changed files with 107 additions and 19 deletions
|
|
@ -18,7 +18,7 @@ from appsettings.models import AppSettings
|
|||
from appsettings.settings import app_settings
|
||||
from computes.models import Compute
|
||||
from computes.validators import validate_hostname, validate_login
|
||||
from computes.utils import libvirt_compute_lock, libvirt_instance_lock, user_quota_lock
|
||||
from computes.utils import libvirt_compute_lock, libvirt_instance_lock, lock_ownership, user_quota_lock
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
|
|
@ -26,6 +26,7 @@ from django.contrib.auth.models import User
|
|||
from django.core import signing
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.http import Http404, HttpResponse, JsonResponse
|
||||
from django.db import transaction
|
||||
from django.db.models import Prefetch, prefetch_related_objects
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
|
|
@ -2077,22 +2078,22 @@ def add_owner(request, pk):
|
|||
raise Http404
|
||||
user = get_object_or_404(User, pk=user_id)
|
||||
|
||||
check_inst = 0
|
||||
|
||||
if app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == "False":
|
||||
check_inst = UserInstance.objects.filter(instance=instance).count()
|
||||
|
||||
action = f"add owner {user.username}"
|
||||
if check_inst > 0:
|
||||
failed_vm(request, instance, action, "only one owner is allowed")
|
||||
messages.error(
|
||||
request, _("Only one owner is allowed and the one already added")
|
||||
)
|
||||
else:
|
||||
with audit_vm(request, instance, action) as entry:
|
||||
# the check and the insert in one step: two owners added at once must not
|
||||
# both find the VM without one; the row is written after the transaction
|
||||
with audit_vm(request, instance, action) as entry, transaction.atomic():
|
||||
lock_ownership()
|
||||
taken = (app_settings.ALLOW_INSTANCE_MULTIPLE_OWNER == "False"
|
||||
and UserInstance.objects.filter(instance=instance).exists())
|
||||
if taken:
|
||||
entry.skip = True
|
||||
else:
|
||||
entry.skip = not UserInstance.objects.get_or_create(instance=instance, user=user)[1]
|
||||
if entry.skip:
|
||||
messages.warning(request, _("%(user)s is already an owner") % {"user": user})
|
||||
if taken:
|
||||
failed_vm(request, instance, action, "only one owner is allowed")
|
||||
messages.error(request, _("Only one owner is allowed and the one already added"))
|
||||
elif entry.skip:
|
||||
messages.warning(request, _("%(user)s is already an owner") % {"user": user})
|
||||
return _back(request, pk, "users")
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue