Make VM order-able again
This commit is contained in:
		
					parent
					
						
							
								cec4263621
							
						
					
				
			
			
				commit
				
					
						a15952862a
					
				
			
		
					 3 changed files with 132 additions and 100 deletions
				
			
		|  | @ -72,6 +72,7 @@ class VMProduct(Product): | |||
|     primary_disk = models.ForeignKey('VMDiskProduct', on_delete=models.CASCADE, null=True) | ||||
| 
 | ||||
|     # Default recurring price is PER_MONTH, see uncloud_pay.models.Product. | ||||
|     @property | ||||
|     def recurring_price(self): | ||||
|         return self.cores * 3 + self.ram_in_gb * 4 | ||||
| 
 | ||||
|  | @ -153,17 +154,9 @@ class VMDiskProduct(Product): | |||
|     def description(self): | ||||
|         return "Disk for VM '{}': {}GB".format(self.vm.name, self.size_in_gb) | ||||
| 
 | ||||
|     # TODO: move magic numbers in variables | ||||
|     def recurring_price(self, recurring_period=RecurringPeriod.PER_MONTH): | ||||
|         # TODO: move magic numbers in variables | ||||
|         if recurring_period == RecurringPeriod.PER_MONTH: | ||||
|             return (self.size_in_gb / 10) * 3.5 | ||||
|         if recurring_period == RecurringPeriod.PER_YEAR: | ||||
|             return recurring_price(self, recurring_period.PER_MONTH) * 12 | ||||
|         if recurring_period == RecurringPeriod.PER_HOUR: | ||||
|             return recurring_price(self, recurring_period.PER_MONTH) / 25 | ||||
|         else: | ||||
|             raise Exception('Invalid recurring period for VM Disk Product pricing.') | ||||
|     @property | ||||
|     def recurring_price(self): | ||||
|         return (self.size_in_gb / 10) * 3.5 | ||||
| 
 | ||||
|     # Sample code for clean method | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,7 +3,9 @@ from django.contrib.auth import get_user_model | |||
| from rest_framework import serializers | ||||
| 
 | ||||
| from .models import VMHost, VMProduct, VMSnapshotProduct, VMDiskProduct, VMDiskImageProduct, VMCluster | ||||
| from uncloud_pay.models import RecurringPeriod | ||||
| from uncloud_pay.models import RecurringPeriod, BillingAddress | ||||
| 
 | ||||
| # XXX: does not seem to be used? | ||||
| 
 | ||||
| GB_SSD_PER_DAY=0.012 | ||||
| GB_HDD_PER_DAY=0.0006 | ||||
|  | @ -11,6 +13,8 @@ GB_HDD_PER_DAY=0.0006 | |||
| GB_SSD_PER_DAY=0.012 | ||||
| GB_HDD_PER_DAY=0.0006 | ||||
| 
 | ||||
| ### | ||||
| # Admin views. | ||||
| 
 | ||||
| class VMHostSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     vms = serializers.PrimaryKeyRelatedField(many=True, read_only=True) | ||||
|  | @ -26,6 +30,9 @@ class VMClusterSerializer(serializers.HyperlinkedModelSerializer): | |||
|         fields = '__all__' | ||||
| 
 | ||||
| 
 | ||||
| ### | ||||
| # Disks. | ||||
| 
 | ||||
| class VMDiskProductSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = VMDiskProduct | ||||
|  | @ -46,30 +53,6 @@ class VMDiskImageProductSerializer(serializers.ModelSerializer): | |||
|         model = VMDiskImageProduct | ||||
|         fields = '__all__' | ||||
| 
 | ||||
| class DCLVMProductSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     """ | ||||
|     Create an interface similar to standard DCL | ||||
|     """ | ||||
| 
 | ||||
|     # Custom field used at creation (= ordering) only. | ||||
|     recurring_period = serializers.ChoiceField( | ||||
|         choices=VMProduct.allowed_recurring_periods()) | ||||
| 
 | ||||
|     os_disk_uuid = serializers.UUIDField() | ||||
|     # os_disk_size = | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = VMProduct | ||||
| 
 | ||||
| class ManagedVMProductSerializer(serializers.ModelSerializer): | ||||
|     """ | ||||
|     Managed VM serializer used in ungleich_service app. | ||||
|     """ | ||||
|     primary_disk = CreateManagedVMDiskProductSerializer() | ||||
|     class Meta: | ||||
|         model = VMProduct | ||||
|         fields = [ 'cores', 'ram_in_gb', 'primary_disk'] | ||||
| 
 | ||||
| class VMSnapshotProductSerializer(serializers.ModelSerializer): | ||||
|     class Meta: | ||||
|         model = VMSnapshotProduct | ||||
|  | @ -93,22 +76,61 @@ class VMSnapshotProductSerializer(serializers.ModelSerializer): | |||
|     pricing['per_gb_hdd'] = 0.0006 | ||||
|     pricing['recurring_period'] = 'per_day' | ||||
| 
 | ||||
| ### | ||||
| # VMs | ||||
| 
 | ||||
| # Helper used in uncloud_service for services allocating VM. | ||||
| class ManagedVMProductSerializer(serializers.ModelSerializer): | ||||
|     """ | ||||
|     Managed VM serializer used in ungleich_service app. | ||||
|     """ | ||||
|     primary_disk = CreateManagedVMDiskProductSerializer() | ||||
|     class Meta: | ||||
|         model = VMProduct | ||||
|         fields = [ 'cores', 'ram_in_gb', 'primary_disk'] | ||||
| 
 | ||||
| class VMProductSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     # Custom field used at creation (= ordering) only. | ||||
|     recurring_period = serializers.ChoiceField( | ||||
|             choices=VMProduct.allowed_recurring_periods()) | ||||
|     primary_disk = CreateVMDiskProductSerializer() | ||||
|     snapshots = VMSnapshotProductSerializer(many=True, read_only=True) | ||||
|     disks = VMDiskProductSerializer(many=True, read_only=True) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = VMProduct | ||||
|         fields = ['uuid', 'order', 'owner', 'status', 'name', \ | ||||
|                 'cores', 'ram_in_gb', 'recurring_period', 'primary_disk', | ||||
|                 'snapshots', 'disks', 'extra_data' ] | ||||
|         fields = ['uuid', 'order', 'owner', 'status', 'name', 'cores', | ||||
|                 'ram_in_gb', 'primary_disk', 'snapshots', 'disks', 'extra_data'] | ||||
|         read_only_fields = ['uuid', 'order', 'owner', 'status'] | ||||
| 
 | ||||
|     snapshots = VMSnapshotProductSerializer(many=True, | ||||
|                                             read_only=True) | ||||
| class OrderVMProductSerializer(VMProductSerializer): | ||||
|     recurring_period = serializers.ChoiceField( | ||||
|             choices=VMProduct.allowed_recurring_periods()) | ||||
| 
 | ||||
|     disks = VMDiskProductSerializer(many=True, | ||||
|                                     read_only=True) | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super(VMProductSerializer, self).__init__(*args, **kwargs) | ||||
|         self.fields['billing_address'] = serializers.ChoiceField( | ||||
|                 choices=BillingAddress.get_addresses_for( | ||||
|                     self.context['request'].user) | ||||
|                 ) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = VMProductSerializer.Meta.model | ||||
|         fields = VMProductSerializer.Meta.fields + [ | ||||
|                 'recurring_period', 'billing_address' | ||||
|                 ] | ||||
|         read_only_fields = VMProductSerializer.Meta.read_only_fields | ||||
| 
 | ||||
| # Nico's playground. | ||||
| 
 | ||||
| class DCLVMProductSerializer(serializers.HyperlinkedModelSerializer): | ||||
|     """ | ||||
|     Create an interface similar to standard DCL | ||||
|     """ | ||||
| 
 | ||||
|     # Custom field used at creation (= ordering) only. | ||||
|     recurring_period = serializers.ChoiceField( | ||||
|         choices=VMProduct.allowed_recurring_periods()) | ||||
| 
 | ||||
|     os_disk_uuid = serializers.UUIDField() | ||||
|     # os_disk_size = | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = VMProduct | ||||
|  |  | |||
|  | @ -15,20 +15,12 @@ from uncloud_pay.models import Order | |||
| from .serializers import * | ||||
| from uncloud_pay.helpers import ProductViewSet | ||||
| 
 | ||||
| 
 | ||||
| import datetime | ||||
| 
 | ||||
| class VMHostViewSet(viewsets.ModelViewSet): | ||||
|     serializer_class = VMHostSerializer | ||||
|     queryset = VMHost.objects.all() | ||||
|     permission_classes = [permissions.IsAdminUser] | ||||
| ### | ||||
| # Generic disk image views. Do not require orders / billing. | ||||
| 
 | ||||
| class VMClusterViewSet(viewsets.ModelViewSet): | ||||
|     serializer_class = VMClusterSerializer | ||||
|     queryset = VMCluster.objects.all() | ||||
|     permission_classes = [permissions.IsAdminUser] | ||||
| 
 | ||||
| class VMDiskImageProductViewSet(viewsets.ModelViewSet): | ||||
| class VMDiskImageProductViewSet(ProductViewSet): | ||||
|     permission_classes = [permissions.IsAuthenticated] | ||||
|     serializer_class = VMDiskImageProductSerializer | ||||
| 
 | ||||
|  | @ -53,7 +45,6 @@ class VMDiskImageProductViewSet(viewsets.ModelViewSet): | |||
|         serializer.save(owner=request.user) | ||||
|         return Response(serializer.data) | ||||
| 
 | ||||
| 
 | ||||
| class VMDiskImageProductPublicViewSet(viewsets.ReadOnlyModelViewSet): | ||||
|     permission_classes = [permissions.IsAuthenticated] | ||||
|     serializer_class = VMDiskImageProductSerializer | ||||
|  | @ -61,6 +52,9 @@ class VMDiskImageProductPublicViewSet(viewsets.ReadOnlyModelViewSet): | |||
|     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 | ||||
|  | @ -92,48 +86,6 @@ class VMDiskProductViewSet(viewsets.ModelViewSet): | |||
|         serializer.save(owner=request.user, size_in_gb=size_in_gb) | ||||
|         return Response(serializer.data) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class VMProductViewSet(ProductViewSet): | ||||
|     permission_classes = [permissions.IsAuthenticated] | ||||
|     serializer_class = VMProductSerializer | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         if self.request.user.is_superuser: | ||||
|             obj = VMProduct.objects.all() | ||||
|         else: | ||||
|             obj = VMProduct.objects.filter(owner=self.request.user) | ||||
| 
 | ||||
|         return obj | ||||
| 
 | ||||
|     # 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( | ||||
|                 recurring_period=order_recurring_period, | ||||
|                 owner=request.user, | ||||
|                 starting_date=timezone.now() | ||||
|                 ) | ||||
| 
 | ||||
|         # Create disk image. | ||||
|         disk = VMDiskProduct(owner=request.user, order=order, | ||||
|                 **serializer.validated_data.pop("primary_disk")) | ||||
| 
 | ||||
|         # Create VM. | ||||
|         vm = serializer.save(owner=request.user, order=order, primary_disk=disk) | ||||
|         disk.vm = vm | ||||
|         disk.save() | ||||
| 
 | ||||
|         return Response(serializer.data) | ||||
| 
 | ||||
| 
 | ||||
| class VMSnapshotProductViewSet(viewsets.ModelViewSet): | ||||
|     permission_classes = [permissions.IsAuthenticated] | ||||
|     serializer_class = VMSnapshotProductSerializer | ||||
|  | @ -176,7 +128,72 @@ class VMSnapshotProductViewSet(viewsets.ModelViewSet): | |||
| 
 | ||||
|         return Response(serializer.data) | ||||
| 
 | ||||
| ### | ||||
| # User VMs. | ||||
| 
 | ||||
| class VMProductViewSet(ProductViewSet): | ||||
|     permission_classes = [permissions.IsAuthenticated] | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         if self.request.user.is_superuser: | ||||
|             obj = VMProduct.objects.all() | ||||
|         else: | ||||
|             obj = VMProduct.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") | ||||
|         order_billing_address = serializer.validated_data.pop("billing_address") | ||||
| 
 | ||||
|         # Create base order. | ||||
|         order = Order( | ||||
|                 recurring_period=order_recurring_period, | ||||
|                 billing_address=order_billing_address, | ||||
|                 owner=request.user, | ||||
|                 starting_date=timezone.now() | ||||
|                 ) | ||||
|         order.save() | ||||
| 
 | ||||
|         # Create disk image. | ||||
|         disk = VMDiskProduct(owner=request.user, order=order, | ||||
|                 **serializer.validated_data.pop("primary_disk")) | ||||
| 
 | ||||
|         # Create VM. | ||||
|         vm = serializer.save(owner=request.user, order=order, primary_disk=disk) | ||||
|         disk.vm = vm | ||||
|         disk.save() | ||||
| 
 | ||||
|         return Response(VMProductSerializer(vm, context={'request': request}).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 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue