mirror of
https://github.com/retspen/webvirtcloud
synced 2025-07-31 12:41:08 +00:00
Rest framework (#24)
* Add rest framework for API: First Commit * modify some shell scripts to make variable references safer; modify some python scripts to reduce the code complexity and cyclomatic complexity of functions. * Add REST API for some webvirtcloud functions. Instance list/delete/create, compute list/delete/create, storages-network list/retrieve. Add swagger and redoc for API interface * update requirements Co-authored-by: herengui <herengui@uniontech.com>
This commit is contained in:
parent
92254401dc
commit
cfce71ec2b
42 changed files with 1170 additions and 348 deletions
100
instances/api/serializers.py
Normal file
100
instances/api/serializers.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from instances.models import Flavor, Instance, MigrateInstance, CreateInstance
|
||||
|
||||
|
||||
class InstanceSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Instance
|
||||
fields = ['id', 'compute', 'name', 'uuid', 'is_template', 'created', 'drbd']
|
||||
|
||||
|
||||
class InstanceDetailsSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Instance
|
||||
fields = [
|
||||
'id',
|
||||
'compute',
|
||||
'status',
|
||||
'uuid',
|
||||
'name',
|
||||
'title',
|
||||
'description',
|
||||
'is_template',
|
||||
'created',
|
||||
'drbd',
|
||||
'arch',
|
||||
'machine',
|
||||
'vcpu',
|
||||
'memory',
|
||||
'firmware',
|
||||
'nvram',
|
||||
'bootmenu',
|
||||
'boot_order',
|
||||
'disks',
|
||||
'media',
|
||||
'media_iso',
|
||||
'snapshots',
|
||||
'networks',
|
||||
'console_type',
|
||||
'console_port',
|
||||
'console_keymap',
|
||||
'console_listener_address',
|
||||
'video_model',
|
||||
'guest_agent_ready',
|
||||
'autostart']
|
||||
|
||||
|
||||
class FlavorSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Flavor
|
||||
fields = ['label', 'memory', 'vcpu', 'disk']
|
||||
|
||||
|
||||
class CreateInstanceSerializer(serializers.ModelSerializer):
|
||||
firmware_choices = (
|
||||
('', 'BIOS'),
|
||||
#('UEFI', 'UEFI'),
|
||||
)
|
||||
firmware = serializers.ChoiceField(choices = firmware_choices)
|
||||
graphics = serializers.CharField(initial='vnc')
|
||||
video = serializers.CharField(initial='vga')
|
||||
storage = serializers.CharField(initial='default')
|
||||
cache_mode = serializers.CharField(initial='none')
|
||||
virtio = serializers.BooleanField(initial=True)
|
||||
qemu_ga = serializers.BooleanField(initial=True)
|
||||
|
||||
class Meta:
|
||||
model = CreateInstance
|
||||
fields = [
|
||||
'name',
|
||||
'firmware',
|
||||
'vcpu',
|
||||
'vcpu_mode',
|
||||
'memory',
|
||||
'networks',
|
||||
'mac',
|
||||
'nwfilter',
|
||||
'storage',
|
||||
'hdd_size',
|
||||
'cache_mode',
|
||||
'meta_prealloc',
|
||||
'virtio',
|
||||
'qemu_ga',
|
||||
'console_pass',
|
||||
'graphics',
|
||||
'video',
|
||||
'listener_addr'
|
||||
]
|
||||
|
||||
|
||||
class MigrateSerializer(serializers.ModelSerializer):
|
||||
instance = Instance.objects.all().prefetch_related("userinstance_set")
|
||||
live = serializers.BooleanField(initial=True)
|
||||
xml_del = serializers.BooleanField(initial=True)
|
||||
class Meta:
|
||||
model = MigrateInstance
|
||||
fields = ['instance', 'target_compute', 'live', 'xml_del', 'offline', 'autoconverge', 'compress', 'postcopy', 'unsafe']
|
||||
224
instances/api/viewsets.py
Normal file
224
instances/api/viewsets.py
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from appsettings.settings import app_settings
|
||||
from computes.models import Compute
|
||||
from computes import utils
|
||||
from instances.models import Flavor, Instance
|
||||
from instances.views import get_instance
|
||||
from instances.utils import migrate_instance
|
||||
from instances.views import poweron, powercycle, poweroff, force_off, suspend, resume, destroy as instance_destroy
|
||||
|
||||
from rest_framework import status, viewsets, permissions
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from vrtManager import util
|
||||
|
||||
from vrtManager.create import wvmCreate
|
||||
from .serializers import FlavorSerializer, InstanceSerializer, InstanceDetailsSerializer, MigrateSerializer, CreateInstanceSerializer
|
||||
|
||||
class InstancesViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A simple ViewSet for listing or retrieving ALL/Compute Instances.
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
def list(self, request):
|
||||
|
||||
if request.user.is_superuser or request.user.has_perm("instances.view_instances"):
|
||||
queryset = Instance.objects.all().prefetch_related("userinstance_set")
|
||||
else:
|
||||
queryset = Instance.objects.filter(userinstance__user=request.user).prefetch_related("userinstance_set")
|
||||
serializer = InstanceSerializer(queryset, many=True, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request, pk=None, compute_pk=None):
|
||||
queryset = get_instance(request.user, pk)
|
||||
serializer = InstanceSerializer(queryset, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class InstanceViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A simple ViewSet for listing or retrieving Compute Instances.
|
||||
"""
|
||||
#serializer_class = CreateInstanceSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
def list(self, request, compute_pk=None):
|
||||
compute = get_object_or_404(Compute, pk=compute_pk)
|
||||
|
||||
utils.refresh_instance_database(compute)
|
||||
|
||||
queryset = Instance.objects.filter(compute=compute).prefetch_related("userinstance_set")
|
||||
serializer = InstanceSerializer(queryset, many=True, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def retrieve(self, request, pk=None, compute_pk=None):
|
||||
queryset = get_instance(request.user, pk)
|
||||
serializer = InstanceDetailsSerializer(queryset, context={'request': request})
|
||||
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
def destroy(self, request, pk=None, compute_pk=None):
|
||||
instance_destroy(request, pk)
|
||||
return Response({'status': 'Instance is destroyed'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def poweron(self, request, pk=None):
|
||||
poweron(request, pk)
|
||||
return Response({'status': 'poweron command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def poweroff(self, request, pk=None):
|
||||
poweroff(request, pk)
|
||||
return Response({'status': 'poweroff command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def powercycle(self, request, pk=None):
|
||||
powercycle(request, pk)
|
||||
return Response({'status': 'powercycle command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def forceoff(self, request, pk=None):
|
||||
force_off(request, pk)
|
||||
return Response({'status': 'force off command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def suspend(self, request, pk=None):
|
||||
suspend(request, pk)
|
||||
return Response({'status': 'suspend command send'})
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def resume(self, request, pk=None):
|
||||
resume(request, pk)
|
||||
return Response({'status': 'resume command send'})
|
||||
|
||||
|
||||
class MigrateViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A viewset for migrating instances.
|
||||
"""
|
||||
serializer_class = MigrateSerializer
|
||||
queryset = ""
|
||||
|
||||
def create(self, request):
|
||||
serializer = MigrateSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
instance = serializer.validated_data['instance']
|
||||
target_host = serializer.validated_data['target_compute']
|
||||
live = serializer.validated_data['live']
|
||||
unsafe = serializer.validated_data['unsafe']
|
||||
xml_del = serializer.validated_data['xml_del']
|
||||
offline = serializer.validated_data['offline']
|
||||
autoconverge = serializer.validated_data['autoconverge']
|
||||
postcopy = serializer.validated_data['postcopy']
|
||||
compress = serializer.validated_data['compress']
|
||||
|
||||
migrate_instance(target_host, instance, request.user, live, unsafe, xml_del, offline, autoconverge, compress, postcopy)
|
||||
|
||||
return Response({'status': 'instance migrate is started'})
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
|
||||
class FlavorViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint that allows flavor to be viewed.
|
||||
"""
|
||||
queryset = Flavor.objects.all().order_by('id')
|
||||
serializer_class = FlavorSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
class CreateInstanceViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
A viewset for creating instances.
|
||||
"""
|
||||
serializer_class = CreateInstanceSerializer
|
||||
queryset = ""
|
||||
|
||||
def create(self, request, compute_pk=None, arch=None, machine=None):
|
||||
serializer = CreateInstanceSerializer(data=request.data,
|
||||
context = {'compute_pk': compute_pk,
|
||||
'arch': arch,
|
||||
'machine': machine
|
||||
})
|
||||
if serializer.is_valid():
|
||||
volume_list = []
|
||||
default_bus = app_settings.INSTANCE_VOLUME_DEFAULT_BUS
|
||||
default_io = app_settings.INSTANCE_VOLUME_DEFAULT_IO
|
||||
default_discard = app_settings.INSTANCE_VOLUME_DEFAULT_DISCARD
|
||||
default_zeroes = app_settings.INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
|
||||
default_scsi_disk_model = app_settings.INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
|
||||
default_disk_format = app_settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
||||
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
||||
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
||||
compute = Compute.objects.get(pk=compute_pk)
|
||||
conn = wvmCreate(
|
||||
compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type,
|
||||
)
|
||||
|
||||
path = conn.create_volume(
|
||||
serializer.validated_data['storage'],
|
||||
serializer.validated_data['name'],
|
||||
serializer.validated_data['hdd_size'],
|
||||
default_disk_format,
|
||||
serializer.validated_data['meta_prealloc'],
|
||||
default_disk_owner_uid,
|
||||
default_disk_owner_gid,
|
||||
)
|
||||
volume = {}
|
||||
firmware = {}
|
||||
volume["device"] = "disk"
|
||||
volume["path"] = path
|
||||
volume["type"] = conn.get_volume_format_type(path)
|
||||
volume["cache_mode"] = serializer.validated_data["cache_mode"]
|
||||
volume["bus"] = default_bus
|
||||
if volume["bus"] == "scsi":
|
||||
volume["scsi_model"] = default_scsi_disk_model
|
||||
volume["discard_mode"] = default_discard
|
||||
volume["detect_zeroes_mode"] = default_zeroes
|
||||
volume["io_mode"] = default_io
|
||||
|
||||
volume_list.append(volume)
|
||||
|
||||
if "UEFI" in serializer.validated_data['firmware']:
|
||||
firmware["loader"] = serializer.validated_data['firmware'].split(":")[1].strip()
|
||||
firmware["secure"] = "no"
|
||||
firmware["readonly"] = "yes"
|
||||
firmware["type"] = "pflash"
|
||||
if "secboot" in firmware["loader"] and machine != "q35":
|
||||
machine = "q35"
|
||||
firmware["secure"] = "yes"
|
||||
|
||||
ret = conn.create_instance(
|
||||
serializer.validated_data['name'],
|
||||
serializer.validated_data['memory'],
|
||||
serializer.validated_data['vcpu'],
|
||||
serializer.validated_data['vcpu_mode'],
|
||||
util.randomUUID(),
|
||||
arch,
|
||||
machine,
|
||||
firmware,
|
||||
volume_list,
|
||||
serializer.validated_data['networks'],
|
||||
serializer.validated_data['nwfilter'],
|
||||
serializer.validated_data['graphics'],
|
||||
serializer.validated_data['virtio'],
|
||||
serializer.validated_data['listener_addr'],
|
||||
serializer.validated_data['video'],
|
||||
serializer.validated_data['console_pass'],
|
||||
serializer.validated_data['mac'],
|
||||
serializer.validated_data['qemu_ga'],
|
||||
)
|
||||
msg = f"Instance {serializer.validated_data['name']} is created"
|
||||
return Response({'status': msg })
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
Loading…
Add table
Add a link
Reference in a new issue