1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
webvirtcloud/storages/api/viewsets.py
catborise 3b8869c72f fix(api): typed URLs and accurate response schemas
- Routers use path converters (use_regex_path=False): computes, instances
  and flavors take <int:...> ids, so non-numeric ids get 404 and
  drf-spectacular derives the parameter types itself.
- Each method declares what it really returns: detail serializers,
  a shared StatusSerializer for status-only replies, no request body for
  actions. Instance properties used by the details serializer get return
  type hints.
- The create-instance API drops the firmware field. Its only accepted
  value was "" (BIOS), which produced an empty, invalid enum, and the
  UEFI branch behind it was unreachable. Clients that still send it are
  unaffected; DRF ignores unknown fields.

The schema now passes spectacular --validate --fail-on-warn; a test
keeps it that way.
2026-10-04 01:08:11 +03:00

203 lines
6.9 KiB
Python

from admin.permissions import IsSuperUser
from appsettings.settings import app_settings
from computes.models import Compute
from django.shortcuts import get_object_or_404
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from vrtManager.storage import wvmStorage, wvmStorages
from webvirtcloud.serializers import StatusSerializer
from .serializers import StorageSerializer, StoragesSerializer, VolumeSerializer
class StorageViewSet(viewsets.ViewSet):
"""
A viewset for listing retrieving storages.
"""
permission_classes = [IsSuperUser]
serializer_class = StoragesSerializer
lookup_value_converter = "str"
def list(self, request, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorages(
compute.hostname, compute.login, compute.password, compute.type
)
queryset = conn.get_storages_info()
serializer = StoragesSerializer(
queryset, many=True, context={"request": request}
)
return Response(serializer.data)
@extend_schema(responses=StorageSerializer)
def retrieve(self, request, pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, pk
)
infoset = {
"state": conn.is_active(),
"size": conn.get_size()[0],
"free": conn.get_size()[1],
"status": conn.get_status(),
"path": conn.get_target_path(),
"type": conn.get_type(),
"autostart": conn.get_autostart(),
"volumes": conn.update_volumes(),
}
serializer = StorageSerializer(
infoset, many=False, context={"request": request}
)
return Response(serializer.data)
@extend_schema(request=None, responses=StatusSerializer)
@action(detail=True, methods=["post"])
def start(self, request, pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, pk
)
ret = conn.start()
conn.close()
return Response({"status": "Pool start command send: " + str(ret)})
@extend_schema(request=None, responses=StatusSerializer)
@action(detail=True, methods=["post"])
def stop(self, request, pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, pk
)
ret = conn.stop()
conn.close()
return Response({"status": "Pool stop command send: " + str(ret)})
@extend_schema(request=None, responses=StatusSerializer)
@action(detail=True, methods=["post"])
def refresh(self, request, pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, pk
)
ret = conn.refresh()
conn.close()
return Response({"status": "Pool refresh command send: " + str(ret)})
@extend_schema(
request=None,
responses=inline_serializer("PoolXML", {"return": serializers.CharField()}),
)
@action(detail=True, methods=["post"])
def XML_description(self, request, pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, pk
)
ret = conn._XMLDesc(0)
conn.close()
return Response({"return": str(ret)})
class VolumeViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving Storage Volumes.
"""
permission_classes = [IsSuperUser]
serializer_class = VolumeSerializer
lookup_value_converter = "str"
def list(self, request, storage_pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, storage_pk
)
state = conn.is_active()
if state:
conn.refresh()
volume_queryset = conn.update_volumes()
else:
volume_queryset = None
conn.close()
serializer = VolumeSerializer(
volume_queryset, many=True, context={"request": request}
)
return Response(serializer.data)
def retrieve(self, request, storage_pk=None, compute_pk=None, pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, storage_pk
)
state = conn.is_active()
volume_queryset = conn.get_volume_details(pk) if state else None
conn.close()
serializer = VolumeSerializer(
volume_queryset, many=False, context={"request": request}
)
return Response(serializer.data)
@extend_schema(responses=StatusSerializer)
def create(self, request, storage_pk=None, compute_pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, storage_pk
)
serializer = VolumeSerializer(data=request.data)
if serializer.is_valid():
state = conn.is_active()
if state:
conn.refresh()
ret = conn.create_volume(
serializer.validated_data["name"],
serializer.validated_data["size"],
serializer.validated_data["type"],
serializer.validated_data["meta_prealloc"],
int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID),
int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID),
)
conn.close()
return Response({"status": "Volume: " + ret + " is created"})
else:
return Response({"status": "Pool is not active"})
else:
return Response({"status": "Data is not right for create volume"})
@extend_schema(responses=StatusSerializer)
def destroy(self, request, storage_pk=None, compute_pk=None, pk=None):
compute = get_object_or_404(Compute, pk=compute_pk)
conn = wvmStorage(
compute.hostname, compute.login, compute.password, compute.type, storage_pk
)
if conn.is_active():
conn.del_volume(pk)
conn.close()
return Response({"status": "Volume: " + pk + " is deleted"})
else:
return Response({"status": "Pool is not active"})