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

fix(api): VM API answers as the VM pages do

- A VM delete reads delete_disk and delete_nvram as booleans from a JSON
  or form body. The view read them as HTML checkboxes from the form body
  only: JSON {"delete_disk": true} kept the disks while reporting success,
  and a form "delete_disk=false" deleted them. Refused options stop
  nothing; the API schema names both options.
- Power on and power cycle of a template answer 409 instead of reporting a
  start that did not happen.
- The VM details leave out the host's ISO images for non-superusers, as
  the VM page does; reading them also refreshed every pool of the host.
- Flavors take values of at least 1, as the web form does, and show their
  id for later changes; a migration needs neither live nor offline; a VM
  create on an unknown compute answers 404 instead of 500.
This commit is contained in:
catborise 2026-10-26 17:11:11 +03:00
parent 726f330dd0
commit 8d7f28482f
6 changed files with 119 additions and 6 deletions

View file

@ -10,6 +10,15 @@ class InstanceSerializer(serializers.ModelSerializer):
class InstanceDetailsSerializer(serializers.ModelSerializer):
def get_fields(self):
fields = super().get_fields()
# the host's ISO images are a superuser's, as on the VM page; reading
# them refreshes every pool of the host
request = self.context.get("request")
if not (request and request.user.is_superuser):
del fields["media_iso"]
return fields
class Meta:
model = Instance
fields = [
@ -49,7 +58,17 @@ class InstanceDetailsSerializer(serializers.ModelSerializer):
class FlavorSerializer(serializers.ModelSerializer):
class Meta:
model = Flavor
fields = ["label", "memory", "vcpu", "disk"]
fields = ["id", "label", "memory", "vcpu", "disk"]
# as the web form (instances.forms.FlavorForm): MiB, vCPUs and GiB, each at least 1
extra_kwargs = {"memory": {"min_value": 1}, "vcpu": {"min_value": 1}, "disk": {"min_value": 1}}
class DestroyOptionsSerializer(serializers.Serializer):
"""What a VM's delete takes besides the VM: from the web form's checkboxes
or an API body, JSON or form-encoded ("false" is false)."""
delete_disk = serializers.BooleanField(default=False)
delete_nvram = serializers.BooleanField(default=False)
class CreateInstanceSerializer(serializers.ModelSerializer):
@ -92,7 +111,8 @@ class CreateInstanceSerializer(serializers.ModelSerializer):
class MigrateSerializer(serializers.ModelSerializer):
instance = Instance.objects.all().prefetch_related("userinstance_set")
live = serializers.BooleanField(initial=True)
live = serializers.BooleanField(default=True)
offline = serializers.BooleanField(default=False)
# left out, also from a form-encoded request: auto converge on
autoconverge = serializers.BooleanField(default=True)
compress = serializers.BooleanField(default=False)

View file

@ -26,6 +26,7 @@ from webvirtcloud.serializers import StatusSerializer
from .serializers import (
CreateInstanceSerializer,
DestroyOptionsSerializer,
FlavorSerializer,
InstanceDetailsSerializer,
InstanceSerializer,
@ -119,7 +120,7 @@ class InstanceViewSet(viewsets.ViewSet):
return Response(serializer.data)
@extend_schema(responses=StatusSerializer)
@extend_schema(request=DestroyOptionsSerializer, responses=StatusSerializer)
def destroy(self, request, pk=None, compute_pk=None):
queryset = get_instance(request.user, pk, perm_type="delete")
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
@ -132,6 +133,8 @@ 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()
if queryset.is_template:
return Response({"status": "Templates cannot be started"}, status=status.HTTP_409_CONFLICT)
return instance_action_response(poweron, request, pk, "poweron command send")
@extend_schema(request=None, responses=StatusSerializer)
@ -148,6 +151,8 @@ 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()
if queryset.is_template:
return Response({"status": "Templates cannot be started"}, status=status.HTTP_409_CONFLICT)
return instance_action_response(powercycle, request, pk, "powercycle command send")
@extend_schema(request=None, responses=StatusSerializer)