diff --git a/uncloud_django_based/uncloud/uncloud_vm/migrations/0004_vmproduct_primary_disk.py b/uncloud_django_based/uncloud/uncloud_vm/migrations/0004_vmproduct_primary_disk.py new file mode 100644 index 0000000..90c4e33 --- /dev/null +++ b/uncloud_django_based/uncloud/uncloud_vm/migrations/0004_vmproduct_primary_disk.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-03-09 12:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_vm', '0003_remove_vmhost_vms'), + ] + + operations = [ + migrations.AddField( + model_name='vmproduct', + name='primary_disk', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMDiskProduct'), + ), + ] diff --git a/uncloud_django_based/uncloud/uncloud_vm/migrations/0005_auto_20200309_1258.py b/uncloud_django_based/uncloud/uncloud_vm/migrations/0005_auto_20200309_1258.py new file mode 100644 index 0000000..0356558 --- /dev/null +++ b/uncloud_django_based/uncloud/uncloud_vm/migrations/0005_auto_20200309_1258.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.3 on 2020-03-09 12:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_pay', '0001_initial'), + ('uncloud_vm', '0004_vmproduct_primary_disk'), + ] + + operations = [ + migrations.AddField( + model_name='vmdiskproduct', + name='order', + field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'), + ), + migrations.AddField( + model_name='vmdiskproduct', + name='status', + field=models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('ACTIVE', 'Active'), ('DELETED', 'Deleted')], default='PENDING', max_length=32), + ), + ] diff --git a/uncloud_django_based/uncloud/uncloud_vm/models.py b/uncloud_django_based/uncloud/uncloud_vm/models.py index 5b80b8f..ee8eed1 100644 --- a/uncloud_django_based/uncloud/uncloud_vm/models.py +++ b/uncloud_django_based/uncloud/uncloud_vm/models.py @@ -69,6 +69,7 @@ class VMProduct(Product): cores = models.IntegerField() ram_in_gb = models.FloatField() + primary_disk = models.ForeignKey('VMDiskProduct', on_delete=models.CASCADE, null=True) def recurring_price(self, recurring_period=RecurringPeriod.PER_MONTH): # TODO: move magic numbers in variables @@ -141,7 +142,7 @@ class VMDiskImageProduct(UncloudModel): -class VMDiskProduct(UncloudModel): +class VMDiskProduct(Product): """ The VMDiskProduct is attached to a VM. @@ -150,18 +151,27 @@ class VMDiskProduct(UncloudModel): It can be enlarged, but not shrinked compared to the VMDiskImageProduct. """ - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - owner = models.ForeignKey(get_user_model(), - on_delete=models.CASCADE, - editable=False) - - vm = models.ForeignKey(VMProduct, - related_name='disks', - on_delete=models.CASCADE) + vm = models.ForeignKey(VMProduct, on_delete=models.CASCADE) image = models.ForeignKey(VMDiskImageProduct, on_delete=models.CASCADE) size_in_gb = models.FloatField(blank=True) + @property + 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.') + # Sample code for clean method # Ensures that a VMDiskProduct can only be created from a VMDiskImageProduct diff --git a/uncloud_django_based/uncloud/uncloud_vm/serializers.py b/uncloud_django_based/uncloud/uncloud_vm/serializers.py index c0cca48..dbc9692 100644 --- a/uncloud_django_based/uncloud/uncloud_vm/serializers.py +++ b/uncloud_django_based/uncloud/uncloud_vm/serializers.py @@ -31,13 +31,16 @@ class VMDiskProductSerializer(serializers.ModelSerializer): model = VMDiskProduct fields = '__all__' +class CreateVMDiskProductSerializer(serializers.ModelSerializer): + class Meta: + model = VMDiskProduct + fields = ['size_in_gb', 'image'] + class VMDiskImageProductSerializer(serializers.ModelSerializer): class Meta: model = VMDiskImageProduct fields = '__all__' - - class DCLVMProductSerializer(serializers.HyperlinkedModelSerializer): """ Create an interface similar to standard DCL @@ -84,18 +87,19 @@ class VMSnapshotProductSerializer(serializers.ModelSerializer): pricing['per_gb_hdd'] = 0.0006 pricing['recurring_period'] = 'per_day' -class VMProductSerializer(serializers.ModelSerializer): - class Meta: - model = VMProduct - fields = ['uuid', 'order', 'owner', 'status', 'name', - 'cores', 'ram_in_gb', 'recurring_period', - 'snapshots', 'disks', - 'extra_data' ] - read_only_fields = ['uuid', 'order', 'owner', 'status' ] +class VMProductSerializer(serializers.HyperlinkedModelSerializer): # Custom field used at creation (= ordering) only. recurring_period = serializers.ChoiceField( - choices=VMProduct.allowed_recurring_periods()) + choices=VMProduct.allowed_recurring_periods()) + primary_disk = CreateVMDiskProductSerializer() + + class Meta: + model = VMProduct + fields = ['uuid', 'order', 'owner', 'status', 'name', \ + 'cores', 'ram_in_gb', 'recurring_period', 'primary_disk', + 'snapshots', 'disks', 'extra_data' ] + read_only_fields = ['uuid', 'order', 'owner', 'status'] snapshots = VMSnapshotProductSerializer(many=True, read_only=True) diff --git a/uncloud_django_based/uncloud/uncloud_vm/views.py b/uncloud_django_based/uncloud/uncloud_vm/views.py index c601c5b..4efb013 100644 --- a/uncloud_django_based/uncloud/uncloud_vm/views.py +++ b/uncloud_django_based/uncloud/uncloud_vm/views.py @@ -122,10 +122,15 @@ class VMProductViewSet(ProductViewSet): 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) + vm = serializer.save(owner=request.user, order=order, primary_disk=disk) + disk.vm = vm + disk.save() return Response(serializer.data)