mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +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:
parent
158d4db6c4
commit
51dedb7b8f
12 changed files with 510 additions and 269 deletions
|
|
@ -33,6 +33,13 @@ from .serializers import (
|
|||
)
|
||||
|
||||
|
||||
def instance_action_response(action, request, pk, message):
|
||||
response = action(request, pk)
|
||||
if response.status_code >= 400:
|
||||
return response
|
||||
return Response({"status": message})
|
||||
|
||||
|
||||
class InstancesViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A simple ViewSet for listing or retrieving ALL/Compute Instances.
|
||||
|
|
@ -117,8 +124,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk, perm_type="delete")
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
instance_destroy(request, pk)
|
||||
return Response({"status": "Instance is destroyed"})
|
||||
return instance_action_response(instance_destroy, request, pk, "Instance is destroyed")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -126,8 +132,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk, perm_type="power")
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
poweron(request, pk)
|
||||
return Response({"status": "poweron command send"})
|
||||
return instance_action_response(poweron, request, pk, "poweron command send")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -135,8 +140,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk, perm_type="power")
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
poweroff(request, pk)
|
||||
return Response({"status": "poweroff command send"})
|
||||
return instance_action_response(poweroff, request, pk, "poweroff command send")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -144,8 +148,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk, perm_type="power")
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
powercycle(request, pk)
|
||||
return Response({"status": "powercycle command send"})
|
||||
return instance_action_response(powercycle, request, pk, "powercycle command send")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -153,8 +156,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk, perm_type="power")
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
force_off(request, pk)
|
||||
return Response({"status": "force off command send"})
|
||||
return instance_action_response(force_off, request, pk, "force off command send")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -162,8 +164,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk)
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
suspend(request, pk)
|
||||
return Response({"status": "suspend command send"})
|
||||
return instance_action_response(suspend, request, pk, "suspend command send")
|
||||
|
||||
@extend_schema(request=None, responses=StatusSerializer)
|
||||
@action(detail=True, methods=["post"])
|
||||
|
|
@ -171,8 +172,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
queryset = get_instance(request.user, pk)
|
||||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
resume(request, pk)
|
||||
return Response({"status": "resume command send"})
|
||||
return instance_action_response(resume, request, pk, "resume command send")
|
||||
|
||||
|
||||
class MigrateViewSet(viewsets.ViewSet):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue