Move django-based uncloud to top-level

This commit is contained in:
fnux 2020-05-07 12:12:35 +02:00
commit 95d43f002f
265 changed files with 0 additions and 0 deletions

View file

3
uncloud_service/admin.py Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
uncloud_service/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class UngleichServiceConfig(AppConfig):
name = 'ungleich_service'

View file

@ -0,0 +1,36 @@
# Generated by Django 3.0.5 on 2020-04-13 09:38
from django.conf import settings
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('uncloud_pay', '0005_auto_20200413_0924'),
('uncloud_vm', '0010_auto_20200413_0924'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='MatrixServiceProduct',
fields=[
('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='PENDING', max_length=32)),
('domain', models.CharField(default='domain.tld', max_length=255)),
('order', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')),
('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('vm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMProduct')),
],
options={
'abstract': False,
},
),
]

View file

@ -0,0 +1,41 @@
# Generated by Django 3.0.5 on 2020-04-18 06:41
from django.conf import settings
import django.contrib.postgres.fields.jsonb
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('uncloud_pay', '0005_auto_20200413_0924'),
('uncloud_service', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='matrixserviceproduct',
name='status',
field=models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='AWAITING_PAYMENT', max_length=32),
),
migrations.CreateModel(
name='GenericServiceProduct',
fields=[
('extra_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, editable=False, null=True)),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('status', models.CharField(choices=[('PENDING', 'Pending'), ('AWAITING_PAYMENT', 'Awaiting payment'), ('BEING_CREATED', 'Being created'), ('SCHEDULED', 'Scheduled'), ('ACTIVE', 'Active'), ('MODIFYING', 'Modifying'), ('DELETED', 'Deleted'), ('DISABLED', 'Disabled'), ('UNUSABLE', 'Unusable')], default='AWAITING_PAYMENT', max_length=32)),
('custom_description', models.TextField()),
('custom_recurring_price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
('custom_one_time_price', models.DecimalField(decimal_places=2, default=0.0, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])),
('order', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_pay.Order')),
('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
]

View file

64
uncloud_service/models.py Normal file
View file

@ -0,0 +1,64 @@
import uuid
from django.db import models
from uncloud_pay.models import Product, RecurringPeriod, AMOUNT_MAX_DIGITS, AMOUNT_DECIMALS
from uncloud_vm.models import VMProduct, VMDiskImageProduct
from django.core.validators import MinValueValidator
class MatrixServiceProduct(Product):
monthly_managment_fee = 20
description = "Managed Matrix HomeServer"
# Specific to Matrix-as-a-Service
vm = models.ForeignKey(
VMProduct, on_delete=models.CASCADE
)
domain = models.CharField(max_length=255, default='domain.tld')
# Default recurring price is PER_MONT, see Product class.
def recurring_price(self, recurring_period=RecurringPeriod.PER_MONTH):
return self.monthly_managment_fee
@staticmethod
def base_image():
# TODO: find a way to safely reference debian 10 image.
return VMDiskImageProduct.objects.get(uuid="93e564c5-adb3-4741-941f-718f76075f02")
@staticmethod
def allowed_recurring_periods():
return list(filter(
lambda pair: pair[0] in [RecurringPeriod.PER_MONTH],
RecurringPeriod.choices))
@property
def one_time_price(self):
return 30
class GenericServiceProduct(Product):
custom_description = models.TextField()
custom_recurring_price = models.DecimalField(default=0.0,
max_digits=AMOUNT_MAX_DIGITS,
decimal_places=AMOUNT_DECIMALS,
validators=[MinValueValidator(0)])
custom_one_time_price = models.DecimalField(default=0.0,
max_digits=AMOUNT_MAX_DIGITS,
decimal_places=AMOUNT_DECIMALS,
validators=[MinValueValidator(0)])
@property
def recurring_price(self):
# FIXME: handle recurring_period somehow.
return self.custom_recurring_price
@property
def description(self):
return self.custom_description
@property
def one_time_price(self):
return self.custom_one_time_price
@staticmethod
def allowed_recurring_periods():
return RecurringPeriod.choices

View file

@ -0,0 +1,60 @@
from rest_framework import serializers
from .models import *
from uncloud_vm.serializers import ManagedVMProductSerializer
from uncloud_vm.models import VMProduct
from uncloud_pay.models import RecurringPeriod, BillingAddress
# XXX: the OrderSomethingSomthingProductSerializer classes add a lot of
# boilerplate: can we reduce it somehow?
class MatrixServiceProductSerializer(serializers.ModelSerializer):
vm = ManagedVMProductSerializer()
class Meta:
model = MatrixServiceProduct
fields = ['uuid', 'order', 'owner', 'status', 'vm', 'domain',
'recurring_period']
read_only_fields = ['uuid', 'order', 'owner', 'status']
class OrderMatrixServiceProductSerializer(MatrixServiceProductSerializer):
recurring_period = serializers.ChoiceField(
choices=MatrixServiceProduct.allowed_recurring_periods())
def __init__(self, *args, **kwargs):
super(OrderMatrixServiceProductSerializer, self).__init__(*args, **kwargs)
self.fields['billing_address'] = serializers.ChoiceField(
choices=BillingAddress.get_addresses_for(
self.context['request'].user)
)
class Meta:
model = MatrixServiceProductSerializer.Meta.model
fields = MatrixServiceProductSerializer.Meta.fields + [
'recurring_period', 'billing_address'
]
read_only_fields = MatrixServiceProductSerializer.Meta.read_only_fields
class GenericServiceProductSerializer(serializers.ModelSerializer):
class Meta:
model = GenericServiceProduct
fields = ['uuid', 'order', 'owner', 'status', 'custom_recurring_price',
'custom_description', 'custom_one_time_price']
read_only_fields = ['uuid', 'order', 'owner', 'status']
class OrderGenericServiceProductSerializer(GenericServiceProductSerializer):
recurring_period = serializers.ChoiceField(
choices=GenericServiceProduct.allowed_recurring_periods())
def __init__(self, *args, **kwargs):
super(OrderGenericServiceProductSerializer, self).__init__(*args, **kwargs)
self.fields['billing_address'] = serializers.ChoiceField(
choices=BillingAddress.get_addresses_for(
self.context['request'].user)
)
class Meta:
model = GenericServiceProductSerializer.Meta.model
fields = GenericServiceProductSerializer.Meta.fields + [
'recurring_period', 'billing_address'
]
read_only_fields = GenericServiceProductSerializer.Meta.read_only_fields

3
uncloud_service/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

128
uncloud_service/views.py Normal file
View file

@ -0,0 +1,128 @@
from rest_framework import viewsets, permissions
from rest_framework.response import Response
from django.db import transaction
from django.utils import timezone
from .models import *
from .serializers import *
from uncloud_pay.helpers import ProductViewSet
from uncloud_pay.models import Order
from uncloud_vm.models import VMProduct, VMDiskProduct
def create_managed_vm(cores, ram, disk_size, image, order):
# Create VM
disk = VMDiskProduct(
owner=order.owner,
order=order,
size_in_gb=disk_size,
image=image)
vm = VMProduct(
name="Managed Service Host",
owner=order.owner,
cores=cores,
ram_in_gb=ram,
primary_disk=disk)
disk.vm = vm
vm.save()
disk.save()
return vm
class MatrixServiceProductViewSet(ProductViewSet):
permission_classes = [permissions.IsAuthenticated]
serializer_class = MatrixServiceProductSerializer
def get_queryset(self):
return MatrixServiceProduct.objects.filter(owner=self.request.user)
def get_serializer_class(self):
if self.action == 'create':
return OrderMatrixServiceProductSerializer
else:
return MatrixServiceProductSerializer
@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.objects.create(
recurring_period=order_recurring_period,
owner=request.user,
billing_address=order_billing_address,
starting_date=timezone.now()
)
order.save()
# Create unerderlying VM.
data = serializer.validated_data.pop('vm')
vm = create_managed_vm(
order=order,
cores=data['cores'],
ram=data['ram_in_gb'],
disk_size=data['primary_disk']['size_in_gb'],
image=MatrixServiceProduct.base_image())
# Create service.
service = serializer.save(
order=order,
owner=request.user,
vm=vm)
return Response(serializer.data)
class GenericServiceProductViewSet(ProductViewSet):
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return GenericServiceProduct.objects.filter(owner=self.request.user)
def get_serializer_class(self):
if self.action == 'create':
return OrderGenericServiceProductSerializer
else:
return GenericServiceProductSerializer
@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.objects.create(
recurring_period=order_recurring_period,
owner=request.user,
billing_address=order_billing_address,
starting_date=timezone.now()
)
order.save()
# Create service.
print(serializer.validated_data)
service = serializer.save(order=order, owner=request.user)
# XXX: Move this to some kind of on_create hook in parent
# Product class?
order.add_record(
service.one_time_price,
service.recurring_price,
service.description)
# XXX: Move this to some kind of on_create hook in parent
# Product class?
order.add_record(
service.one_time_price,
service.recurring_price,
service.description)
return Response(serializer.data)