from django.db import transaction from django.shortcuts import render from django.utils import timezone from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 from rest_framework import viewsets, permissions from rest_framework.response import Response from rest_framework.exceptions import ValidationError from .models import VMHost, VMProduct, VMSnapshotProduct, VMDiskProduct, VMDiskImageProduct, VMCluster from uncloud_pay.models import Order, BillingAddress from .serializers import * from uncloud_pay.helpers import ProductViewSet import datetime ### # Generic disk image views. Do not require orders / billing. class VMDiskImageProductViewSet(ProductViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = VMDiskImageProductSerializer def get_queryset(self): if self.request.user.is_superuser: obj = VMDiskImageProduct.objects.all() else: obj = VMDiskImageProduct.objects.filter(owner=self.request.user) | VMDiskImageProduct.objects.filter(is_public=True) return obj def create(self, request): serializer = VMDiskImageProductSerializer(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) # did not specify size NOR import url? if not serializer.validated_data['size_in_gb']: if not serializer.validated_data['import_url']: raise ValidationError(detail={ 'error_mesage': 'Specify either import_url or size_in_gb' }) serializer.save(owner=request.user) return Response(serializer.data) class VMDiskImageProductPublicViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = VMDiskImageProductSerializer def get_queryset(self): return VMDiskImageProduct.objects.filter(is_public=True) ### # User VM disk and snapshots. class VMDiskProductViewSet(viewsets.ModelViewSet): """ Let a user modify their own VMDisks """ permission_classes = [permissions.IsAuthenticated] serializer_class = VMDiskProductSerializer def get_queryset(self): if self.request.user.is_superuser: obj = VMDiskProduct.objects.all() else: obj = VMDiskProduct.objects.filter(owner=self.request.user) return obj def create(self, request): serializer = VMDiskProductSerializer(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) # get disk size from image, if not specified if not 'size_in_gb' in serializer.validated_data: size_in_gb = serializer.validated_data['image'].size_in_gb else: size_in_gb = serializer.validated_data['size_in_gb'] if size_in_gb < serializer.validated_data['image'].size_in_gb: raise ValidationError(detail={ 'error_mesage': 'Size is smaller than original image' }) serializer.save(owner=request.user, size_in_gb=size_in_gb) return Response(serializer.data) class VMSnapshotProductViewSet(viewsets.ModelViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = VMSnapshotProductSerializer def get_queryset(self): if self.request.user.is_superuser: obj = VMSnapshotProduct.objects.all() else: obj = VMSnapshotProduct.objects.filter(owner=self.request.user) return obj def create(self, request): serializer = VMSnapshotProductSerializer(data=request.data, context={'request': request}) # This verifies that the VM belongs to the request user serializer.is_valid(raise_exception=True) vm = vm=serializer.validated_data['vm'] disks = VMDiskProduct.objects.filter(vm=vm) ssds_size = sum([d.size_in_gb for d in disks if d.image.storage_class == 'ssd']) hdds_size = sum([d.size_in_gb for d in disks if d.image.storage_class == 'hdd']) recurring_price = serializer.pricing['per_gb_ssd'] * ssds_size + serializer.pricing['per_gb_hdd'] * hdds_size recurring_period = serializer.pricing['recurring_period'] # Create order now = datetime.datetime.now() order = Order(owner=request.user, recurring_period=recurring_period) order.save() order.add_record(one_time_price=0, recurring_price=recurring_price, description="Snapshot of VM {} from {}".format(vm, now)) serializer.save(owner=request.user, order=order, gb_ssd=ssds_size, gb_hdd=hdds_size) return Response(serializer.data) ### # User VMs. class VMProductViewSet(ProductViewSet): permission_classes = [permissions.IsAuthenticated] def get_queryset(self): if self.request.user.is_superuser: obj = VMWithOSProduct.objects.all() else: obj = VMWithOSProduct.objects.filter(owner=self.request.user) return obj def get_serializer_class(self): if self.action == 'create': return OrderVMProductSerializer else: return VMProductSerializer # Use a database transaction so that we do not get half-created structure # if something goes wrong. @transaction.atomic def create(self, request): # Extract serializer data. serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) order_recurring_period = serializer.validated_data.pop("recurring_period") # Create disk image. disk = VMDiskProduct(owner=request.user, **serializer.validated_data.pop("primary_disk")) vm = VMWithOSProduct(owner=request.user, primary_disk=disk, **serializer.validated_data) disk.vm = vm # XXX: Is this really needed? # Create VM and Disk orders. vm_order = Order.from_product( vm, recurring_period=order_recurring_period, starting_date=timezone.now() ) disk_order = Order.from_product( disk, recurring_period=order_recurring_period, starting_date=timezone.now(), depends_on=vm_order ) # Commit to DB. vm.order = vm_order vm.save() vm_order.save() disk.order = disk_order disk_order.save() disk.save() return Response(VMProductSerializer(vm, context={'request': request}).data) class NicoVMProductViewSet(ProductViewSet): permission_classes = [permissions.IsAuthenticated] serializer_class = NicoVMProductSerializer def get_queryset(self): obj = VMProduct.objects.filter(owner=self.request.user) return obj def create(self, request): serializer = self.serializer_class(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) vm = serializer.save(owner=request.user) return Response(serializer.data) ### # Admin stuff. class VMHostViewSet(viewsets.ModelViewSet): serializer_class = VMHostSerializer queryset = VMHost.objects.all() permission_classes = [permissions.IsAdminUser] class VMClusterViewSet(viewsets.ModelViewSet): serializer_class = VMClusterSerializer queryset = VMCluster.objects.all() permission_classes = [permissions.IsAdminUser] ## # Nico's playground. # Also create: # - /dcl/available_os # Basically a view of public and my disk images # - class DCLCreateVMProductViewSet(ProductViewSet): """ This view resembles the way how DCL VMs are created by default. The user chooses an OS, os disk size, ram, cpu and whether or not to have a mapped IPv4 address """ permission_classes = [permissions.IsAuthenticated] serializer_class = DCLVMProductSerializer def get_queryset(self): return VMProduct.objects.filter(owner=self.request.user) # Use a database transaction so that we do not get half-created structure # if something goes wrong. @transaction.atomic def create(self, request): # Extract serializer data. serializer = VMProductSerializer(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) order_recurring_period = serializer.validated_data.pop("recurring_period") # Create base order. order = Order.objects.create( recurring_period=order_recurring_period, owner=request.user ) order.save() # Create VM. vm = serializer.save(owner=request.user, order=order) return Response(serializer.data)