Merge remote-tracking branch 'origin/fnux-stable'

Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
This commit is contained in:
Nico Schottelius 2020-03-03 11:23:29 +01:00
commit a50095f873
49 changed files with 1326 additions and 102 deletions

View file

@ -16,17 +16,6 @@ class Migration(migrations.Migration):
model_name='vmsnapshotproduct',
name='vm_uuid',
),
migrations.AddField(
model_name='vmproduct',
name='order',
field=models.ForeignKey(default=0, editable=False, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'),
preserve_default=False,
),
migrations.AddField(
model_name='vmproduct',
name='status',
field=models.CharField(choices=[('pending', 'Pending'), ('being_created', 'Being created'), ('active', 'Active'), ('deleted', 'Deleted')], default='pending', max_length=256),
),
migrations.AddField(
model_name='vmsnapshotproduct',
name='vm',

View file

@ -0,0 +1,25 @@
# Generated by Django 3.0.3 on 2020-02-27 15:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('uncloud_pay', '0004_auto_20200227_1532'),
('uncloud_vm', '0004_vmsnapshotproduct'),
]
operations = [
migrations.AddField(
model_name='vmproduct',
name='order',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'),
),
migrations.AlterField(
model_name='vmsnapshotproduct',
name='order',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order'),
),
]

View file

@ -0,0 +1,14 @@
# Generated by Django 3.0.3 on 2020-02-28 13:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('uncloud_vm', '0005_auto_20200227_1532'),
('uncloud_vm', '0005_auto_20200227_1230'),
]
operations = [
]

View file

@ -0,0 +1,23 @@
# Generated by Django 3.0.3 on 2020-02-28 13:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_vm', '0006_merge_20200228_1303'),
]
operations = [
migrations.AddField(
model_name='vmnetworkcard',
name='ip_address',
field=models.GenericIPAddressField(blank=True, null=True),
),
migrations.AddField(
model_name='vmproduct',
name='status',
field=models.CharField(choices=[('pending', 'Pending'), ('being_created', 'Being created'), ('active', 'Active'), ('deleted', 'Deleted')], default='pending', max_length=256),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-02-28 14:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_vm', '0007_auto_20200228_1344'),
]
operations = [
migrations.AddField(
model_name='vmproduct',
name='name',
field=models.CharField(blank=True, max_length=32),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-02-28 14:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('uncloud_vm', '0008_vmproduct_name'),
]
operations = [
migrations.AlterField(
model_name='vmproduct',
name='name',
field=models.CharField(max_length=32),
),
]

View file

@ -6,7 +6,8 @@ from django.contrib.auth import get_user_model
# Uncomment if you override model's clean method
# from django.core.exceptions import ValidationError
from uncloud_pay.models import Product
from uncloud_pay.models import Product, RecurringPeriod
import uncloud_pay.models as pay_models
STATUS_CHOICES = (
('pending', 'Pending'), # Initial state
@ -45,9 +46,31 @@ class VMProduct(Product):
VMHost, on_delete=models.CASCADE, editable=False, blank=True, null=True
)
# VM-specific. The name is only intended for customers: it's a pain te
# remember IDs (speaking from experience as ungleich customer)!
name = models.CharField(max_length=32)
cores = models.IntegerField()
ram_in_gb = models.FloatField()
def recurring_price(self, recurring_period=RecurringPeriod.PER_MONTH):
# TODO: move magic numbers in variables
if recurring_period == RecurringPeriod.PER_MONTH:
return self.cores * 3 + self.ram_in_gb * 2
elif recurring_period == RecurringPeriod.PER_HOUR:
return self.cores * 4.0/(30 * 24) + self.ram_in_gb * 3.0/(30* 24)
else:
raise Exception('Invalid recurring period for VM Product pricing.')
@property
def description(self):
return "Virtual machine '{}': {} core(s), {}GB memory".format(
self.name, self.cores, self.ram_in_gb)
@staticmethod
def allowed_recurring_periods():
return list(filter(
lambda pair: pair[0] in [RecurringPeriod.PER_MONTH, RecurringPeriod.PER_HOUR],
RecurringPeriod.choices))
class VMWithOSProduct(VMProduct):
pass

View file

@ -1,7 +1,9 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
from .models import VMHost, VMProduct, VMSnapshotProduct, VMDiskProduct, VMDiskImageProduct
from uncloud_pay.models import RecurringPeriod
GB_SSD_PER_DAY=0.012
GB_HDD_PER_DAY=0.0006
@ -16,14 +18,7 @@ class VMHostSerializer(serializers.ModelSerializer):
fields = '__all__'
class VMProductSerializer(serializers.ModelSerializer):
class Meta:
model = VMProduct
fields = '__all__'
class VMDiskProductSerializer(serializers.ModelSerializer):
# vm = VMProductSerializer()
class Meta:
model = VMDiskProduct
fields = '__all__'
@ -33,6 +28,25 @@ class VMDiskImageProductSerializer(serializers.ModelSerializer):
model = VMDiskImageProduct
fields = '__all__'
class VMProductSerializer(serializers.HyperlinkedModelSerializer):
# Custom field used at creation (= ordering) only.
recurring_period = serializers.ChoiceField(
choices=VMProduct.allowed_recurring_periods())
class Meta:
model = VMProduct
fields = ['uuid', 'order', 'owner', 'status', 'name', \
'cores', 'ram_in_gb', 'recurring_period']
read_only_fields = ['uuid', 'order', 'owner', 'status']
class ManagedVMProductSerializer(serializers.ModelSerializer):
"""
Managed VM serializer used in ungleich_service app.
"""
class Meta:
model = VMProduct
fields = [ 'cores', 'ram_in_gb']
class VMSnapshotProductSerializer(serializers.ModelSerializer):
class Meta:
model = VMSnapshotProduct

View file

@ -1,3 +1,4 @@
from django.db import transaction
from django.shortcuts import render
from django.contrib.auth.models import User
@ -7,11 +8,11 @@ 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, VMDiskProductSerializer
from uncloud_pay.helpers import ProductViewSet
import datetime
@ -77,27 +78,37 @@ class VMDiskProductViewSet(viewsets.ModelViewSet):
class VMProductViewSet(viewsets.ModelViewSet):
class VMProductViewSet(ProductViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = VMProductSerializer
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)
# Create order
now = datetime.datetime.now()
order = Order(owner=request.user,
creation_date=now,
starting_date=now,
recurring_price=20,
one_time_price=0,
recurring_period="per_month")
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()
serializer.save(owner=request.user, order=order)
# Create VM.
vm = serializer.save(owner=request.user, order=order)
# Add Product record to order (VM is mutable, allows to keep history in order).
# XXX: Move this to some kind of on_create hook in parent Product class?
order.add_record(vm.setup_fee,
vm.recurring_price(order.recurring_period), vm.description)
return Response(serializer.data)