diff --git a/uncloud/uncloud/urls.py b/uncloud/uncloud/urls.py index 40b3b20..02862a1 100644 --- a/uncloud/uncloud/urls.py +++ b/uncloud/uncloud/urls.py @@ -26,14 +26,27 @@ router = routers.DefaultRouter() # user / regular urls router.register(r'vm/snapshot', vmviews.VMSnapshotProductViewSet, basename='vmsnapshotproduct') +router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct') router.register(r'vm/image/mine', vmviews.VMDiskImageProductMineViewSet, basename='vmdiskimagemineproduct') router.register(r'vm/image/public', vmviews.VMDiskImageProductPublicViewSet, basename='vmdiskimagepublicproduct') +# images the provider provides :-) +# router.register(r'vm/image/official', vmviews.VMDiskImageProductPublicViewSet, basename='vmdiskimagepublicproduct') + + -#router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct') router.register(r'vm/vm', vmviews.VMProductViewSet, basename='vmproduct') +# TBD +#router.register(r'vm/disk', vmviews.VMDiskProductViewSet, basename='vmdiskproduct') + +# creates VM from os image +#router.register(r'vm/ipv6onlyvm', vmviews.VMProductViewSet, basename='vmproduct') +# ... AND adds IPv4 mapping +#router.register(r'vm/dualstackvm', vmviews.VMProductViewSet, basename='vmproduct') + +# allow vm creation from own images # Pay diff --git a/uncloud/uncloud_vm/models.py b/uncloud/uncloud_vm/models.py index f2cbf13..7aac05b 100644 --- a/uncloud/uncloud_vm/models.py +++ b/uncloud/uncloud_vm/models.py @@ -4,6 +4,16 @@ import uuid from uncloud_pay.models import Product +STATUS_CHOICES = ( + ('pending', 'Pending'), # Initial state + ('creating', 'Creating'), # Creating VM/image/etc. + ('active', 'Active'), # Is usable / active + ('disabled', 'Disabled'), # Is usable, but cannot be used for new things + ('unusable', 'Unusable'), # Has some kind of error + ('deleted', 'Deleted'), # Does not exist anymore, only DB entry as a log +) + +STATUS_DEFAULT='pending' class VMHost(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -22,13 +32,8 @@ class VMHost(models.Model): status = models.CharField(max_length=32, - choices = ( - ('pending', 'Pending'), - ('active', 'Active'), - ('unusable', 'Unusable'), - ('deleted', 'Deleted'), - ), - default='pending' + choices=STATUS_CHOICES, + default=STATUS_DEFAULT ) @@ -80,13 +85,10 @@ class VMDiskImageProduct(models.Model): default='ssd' ) - # source = models.CharField(max_length=32, - # choices = ( - # ('url', 'HDD'), - # ('ssd', 'SSD'), - # ), - # default='ssd' - # ) + status = models.CharField(max_length=32, + choices=STATUS_CHOICES, + default=STATUS_DEFAULT + ) class VMDiskProduct(models.Model): """ @@ -105,7 +107,7 @@ class VMDiskProduct(models.Model): vm = models.ForeignKey(VMProduct, on_delete=models.CASCADE) image = models.ForeignKey(VMDiskImageProduct, on_delete=models.CASCADE) - size_in_gb = models.FloatField() + size_in_gb = models.FloatField(blank=True) class VMNetworkCard(models.Model): diff --git a/uncloud/uncloud_vm/serializers.py b/uncloud/uncloud_vm/serializers.py index f8618ee..07d6c51 100644 --- a/uncloud/uncloud_vm/serializers.py +++ b/uncloud/uncloud_vm/serializers.py @@ -22,6 +22,8 @@ class VMProductSerializer(serializers.ModelSerializer): fields = '__all__' class VMDiskProductSerializer(serializers.ModelSerializer): +# vm = VMProductSerializer() + class Meta: model = VMDiskProduct fields = '__all__' diff --git a/uncloud/uncloud_vm/views.py b/uncloud/uncloud_vm/views.py index 851041e..62edaa0 100644 --- a/uncloud/uncloud_vm/views.py +++ b/uncloud/uncloud_vm/views.py @@ -5,11 +5,13 @@ 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 from uncloud_pay.models import Order -from .serializers import VMHostSerializer, VMProductSerializer, VMSnapshotProductSerializer, VMDiskImageProductSerializer +from .serializers import VMHostSerializer, VMProductSerializer, VMSnapshotProductSerializer, VMDiskImageProductSerializer, VMDiskProductSerializer import datetime @@ -27,8 +29,14 @@ class VMDiskImageProductMineViewSet(viewsets.ModelViewSet): return VMDiskImageProduct.objects.filter(owner=self.request.user) def create(self, request): - serializer = VMProductSerializer(data=request.data, context={'request': 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) @@ -40,6 +48,34 @@ class VMDiskImageProductPublicViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return VMDiskImageProduct.objects.filter(is_public=True) +class VMDiskProductViewSet(viewsets.ModelViewSet): + """ + Let a user modify their own VMDisks + """ + permission_classes = [permissions.IsAuthenticated] + serializer_class = VMDiskProductSerializer + + def get_queryset(self): + return VMDiskProduct.objects.filter(owner=self.request.user) + + 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 VMProductViewSet(viewsets.ModelViewSet): permission_classes = [permissions.IsAuthenticated]