From b3e505d37cc1f267c6ee7752fb85f8aa440be5be Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 23 Feb 2020 21:34:22 +0500 Subject: [PATCH 01/10] migration fix --- uncloud/opennebula/migrations/0004_auto_20200222_0713.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uncloud/opennebula/migrations/0004_auto_20200222_0713.py b/uncloud/opennebula/migrations/0004_auto_20200222_0713.py index a298c06..89913cb 100644 --- a/uncloud/opennebula/migrations/0004_auto_20200222_0713.py +++ b/uncloud/opennebula/migrations/0004_auto_20200222_0713.py @@ -7,7 +7,7 @@ import uuid class Migration(migrations.Migration): dependencies = [ - ('opennebula', '0003_auto_20200221_1113'), + ('opennebula', '0003_auto_20200223_1058'), ] operations = [ From 739bd7252612ccbb9f3f72183084effd0e743099 Mon Sep 17 00:00:00 2001 From: meow Date: Sun, 23 Feb 2020 23:00:42 +0500 Subject: [PATCH 02/10] Migration fixed + opennebula/views.py fixed --- .../opennebula/management/commands/syncvm.py | 6 ++- uncloud/opennebula/migrations/0001_initial.py | 4 +- uncloud/opennebula/migrations/0002_vm_uuid.py | 19 -------- .../migrations/0003_auto_20200223_1058.py | 19 -------- .../migrations/0004_auto_20200222_0713.py | 23 ---------- uncloud/opennebula/views.py | 16 +++---- .../uncloud_api/migrations/0001_initial.py | 2 +- .../migrations/0002_auto_20200222_0719.py | 46 ------------------- .../uncloud_auth/migrations/0001_initial.py | 2 +- uncloud/uncloud_vm/migrations/__init__.py | 0 10 files changed, 17 insertions(+), 120 deletions(-) delete mode 100644 uncloud/opennebula/migrations/0002_vm_uuid.py delete mode 100644 uncloud/opennebula/migrations/0003_auto_20200223_1058.py delete mode 100644 uncloud/opennebula/migrations/0004_auto_20200222_0713.py delete mode 100644 uncloud/uncloud_api/migrations/0002_auto_20200222_0719.py delete mode 100644 uncloud/uncloud_vm/migrations/__init__.py diff --git a/uncloud/opennebula/management/commands/syncvm.py b/uncloud/opennebula/management/commands/syncvm.py index 795d53a..f5f80b1 100644 --- a/uncloud/opennebula/management/commands/syncvm.py +++ b/uncloud/opennebula/management/commands/syncvm.py @@ -11,6 +11,7 @@ from opennebula.models import VM as VMModel import uncloud.secrets + class Command(BaseCommand): help = 'Syncronize VM information from OpenNebula' @@ -24,6 +25,7 @@ class Command(BaseCommand): ) if success: vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM'] + unknown_user_with_email = set() for i, vm in enumerate(vms): vm_id = vm['ID'] vm_owner_email = vm['UNAME'] @@ -31,7 +33,7 @@ class Command(BaseCommand): try: user = get_user_model().objects.get(email=vm_owner_email) except get_user_model().DoesNotExist: - print("Skipping VM import for unknown user with email: {}".format(vm_owner_email)) + unknown_user_with_email.add(vm_owner_email) continue # user = get_user_model().objects.create_user(username=vm_owner) @@ -40,7 +42,7 @@ class Command(BaseCommand): 'owner': user }, vmid=vm_id ) - + print('User with email but no username:', unknown_user_with_email) else: print(response) print(uncloud.secrets.OPENNEBULA_USER_PASS) diff --git a/uncloud/opennebula/migrations/0001_initial.py b/uncloud/opennebula/migrations/0001_initial.py index f1d3d6b..4c0527a 100644 --- a/uncloud/opennebula/migrations/0001_initial.py +++ b/uncloud/opennebula/migrations/0001_initial.py @@ -1,9 +1,10 @@ -# Generated by Django 3.0.3 on 2020-02-23 10:02 +# Generated by Django 3.0.3 on 2020-02-23 17:12 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): @@ -19,6 +20,7 @@ class Migration(migrations.Migration): name='VM', fields=[ ('vmid', models.IntegerField(primary_key=True, serialize=False)), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)), ('data', django.contrib.postgres.fields.jsonb.JSONField()), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], diff --git a/uncloud/opennebula/migrations/0002_vm_uuid.py b/uncloud/opennebula/migrations/0002_vm_uuid.py deleted file mode 100644 index 595fd05..0000000 --- a/uncloud/opennebula/migrations/0002_vm_uuid.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.3 on 2020-02-23 10:55 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('opennebula', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='vm', - name='uuid', - field=models.UUIDField(default=uuid.uuid4, editable=False), - ), - ] diff --git a/uncloud/opennebula/migrations/0003_auto_20200223_1058.py b/uncloud/opennebula/migrations/0003_auto_20200223_1058.py deleted file mode 100644 index d2173da..0000000 --- a/uncloud/opennebula/migrations/0003_auto_20200223_1058.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.3 on 2020-02-23 10:58 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('opennebula', '0002_vm_uuid'), - ] - - operations = [ - migrations.AlterField( - model_name='vm', - name='uuid', - field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True), - ), - ] diff --git a/uncloud/opennebula/migrations/0004_auto_20200222_0713.py b/uncloud/opennebula/migrations/0004_auto_20200222_0713.py deleted file mode 100644 index 89913cb..0000000 --- a/uncloud/opennebula/migrations/0004_auto_20200222_0713.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.3 on 2020-02-22 07:13 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('opennebula', '0003_auto_20200223_1058'), - ] - - operations = [ - migrations.RemoveField( - model_name='vm', - name='id', - ), - migrations.AddField( - model_name='vm', - name='uuid', - field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False), - ), - ] diff --git a/uncloud/opennebula/views.py b/uncloud/opennebula/views.py index 29fdb64..5498928 100644 --- a/uncloud/opennebula/views.py +++ b/uncloud/opennebula/views.py @@ -1,11 +1,10 @@ -from rest_framework import viewsets, generics, permissions +from rest_framework import viewsets, permissions from rest_framework.response import Response -from django.contrib.auth import get_user_model - from .models import VM from .serializers import VMSerializer, OpenNebulaVMSerializer + class RawVMViewSet(viewsets.ModelViewSet): queryset = VM.objects.all() serializer_class = VMSerializer @@ -14,14 +13,15 @@ class RawVMViewSet(viewsets.ModelViewSet): class VMViewSet(viewsets.ModelViewSet): permission_classes = [permissions.IsAuthenticated] + serializer_class = OpenNebulaVMSerializer + + def get_queryset(self): + return VM.objects.filter(owner=self.request.user) def list(self, request): - queryset = VM.objects.filter(owner=request.user) - serializer = OpenNebulaVMSerializer(queryset, many=True, context={'request': request}) + serializer = OpenNebulaVMSerializer(self.queryset, many=True, context={'request': request}) return Response(serializer.data) def retrieve(self, request, pk=None): - queryset = VM.objects.filter(owner=request.user) - user = get_object_or_404(queryset, pk=pk) - serializer = OpenNebulaVMSerializer(queryset) + serializer = OpenNebulaVMSerializer(self.queryset) return Response(serializer.data) diff --git a/uncloud/uncloud_api/migrations/0001_initial.py b/uncloud/uncloud_api/migrations/0001_initial.py index d8d9630..c549a9d 100644 --- a/uncloud/uncloud_api/migrations/0001_initial.py +++ b/uncloud/uncloud_api/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.3 on 2020-02-23 10:16 +# Generated by Django 3.0.3 on 2020-02-23 17:12 from django.conf import settings from django.db import migrations, models diff --git a/uncloud/uncloud_api/migrations/0002_auto_20200222_0719.py b/uncloud/uncloud_api/migrations/0002_auto_20200222_0719.py deleted file mode 100644 index a52eade..0000000 --- a/uncloud/uncloud_api/migrations/0002_auto_20200222_0719.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 3.0.3 on 2020-02-22 07:19 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('uncloud_api', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='VMSnapshotOrder', - fields=[ - ('order_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='uncloud_api.Order')), - ], - bases=('uncloud_api.order',), - ), - migrations.CreateModel( - name='VMSnapshotProduct', - fields=[ - ('product_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='uncloud_api.Product')), - ('gb_ssd', models.FloatField()), - ('gb_hdd', models.FloatField()), - ], - bases=('uncloud_api.product',), - ), - migrations.DeleteModel( - name='OrderReference', - ), - migrations.RemoveField( - model_name='product', - name='name', - ), - migrations.RemoveField( - model_name='product', - name='recurring_period', - ), - migrations.AddField( - model_name='product', - name='status', - field=models.CharField(choices=[('pending', 'Pending'), ('being_created', 'Being created'), ('created_active', 'Created'), ('deleted', 'Deleted')], default='pending', max_length=256), - ), - ] diff --git a/uncloud/uncloud_auth/migrations/0001_initial.py b/uncloud/uncloud_auth/migrations/0001_initial.py index a3ade55..63885c4 100644 --- a/uncloud/uncloud_auth/migrations/0001_initial.py +++ b/uncloud/uncloud_auth/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0.3 on 2020-02-23 10:02 +# Generated by Django 3.0.3 on 2020-02-23 17:11 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/uncloud/uncloud_vm/migrations/__init__.py b/uncloud/uncloud_vm/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 From a72bc142a68bcbe9f5a339e723fb3b3db0a5dfcc Mon Sep 17 00:00:00 2001 From: meow Date: Tue, 25 Feb 2020 11:50:49 +0500 Subject: [PATCH 03/10] Fixed issues in opennebula/views.py + syncvm now behaves correctly and print users which are not in ldap as per their email address --- .../opennebula/management/commands/syncvm.py | 54 ++++++++++++------- uncloud/opennebula/views.py | 14 ++--- uncloud/requirements.txt | 1 + 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/uncloud/opennebula/management/commands/syncvm.py b/uncloud/opennebula/management/commands/syncvm.py index f5f80b1..00108f0 100644 --- a/uncloud/opennebula/management/commands/syncvm.py +++ b/uncloud/opennebula/management/commands/syncvm.py @@ -1,15 +1,22 @@ -import os import json +import uncloud.secrets as secrets + + +from xmlrpc.client import ServerProxy as RPCClient + from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model -from xmlrpc.client import ServerProxy as RPCClient - from xmltodict import parse +from ungleich_common.ldap.ldap_manager import LdapManager from opennebula.models import VM as VMModel -import uncloud.secrets + +def find_user_based_on_email(users, email): + for user in users: + if email in user.mail.values: + return user class Command(BaseCommand): @@ -19,30 +26,39 @@ class Command(BaseCommand): pass def handle(self, *args, **options): - with RPCClient(uncloud.secrets.OPENNEBULA_URL) as rpc_client: + ldap_server_uri = secrets.LDAP_SERVER_URI.split(',')[0] + ldap_manager = LdapManager( + server=ldap_server_uri, + admin_dn=secrets.LDAP_ADMIN_DN, + admin_password=secrets.LDAP_ADMIN_PASSWORD, + ) + users = ldap_manager.get('') # Get all users + + with RPCClient(secrets.OPENNEBULA_URL) as rpc_client: success, response, *_ = rpc_client.one.vmpool.infoextended( - uncloud.secrets.OPENNEBULA_USER_PASS, -2, -1, -1, -1 + secrets.OPENNEBULA_USER_PASS, -2, -1, -1, -1 ) if success: vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM'] unknown_user_with_email = set() - for i, vm in enumerate(vms): + + for vm in vms: vm_id = vm['ID'] vm_owner_email = vm['UNAME'] - try: - user = get_user_model().objects.get(email=vm_owner_email) - except get_user_model().DoesNotExist: + user = find_user_based_on_email(users, vm_owner_email) + if not user: unknown_user_with_email.add(vm_owner_email) - continue - # user = get_user_model().objects.create_user(username=vm_owner) + else: + try: + user_in_db = get_user_model().objects.get(email=vm_owner_email) + except get_user_model().DoesNotExist: + user_in_db = get_user_model().objects.create_user(username=user.uid, email=vm_owner_email) - VMModel.objects.update_or_create( - defaults= { 'data': vm, - 'owner': user }, - vmid=vm_id - ) - print('User with email but no username:', unknown_user_with_email) + VMModel.objects.update_or_create( + defaults={'data': vm, 'owner': user_in_db}, vmid=vm_id + ) + print('User with email but not found in ldap:', unknown_user_with_email) else: print(response) - print(uncloud.secrets.OPENNEBULA_USER_PASS) + print(secrets.OPENNEBULA_USER_PASS) diff --git a/uncloud/opennebula/views.py b/uncloud/opennebula/views.py index 5498928..66269c7 100644 --- a/uncloud/opennebula/views.py +++ b/uncloud/opennebula/views.py @@ -1,5 +1,6 @@ from rest_framework import viewsets, permissions from rest_framework.response import Response +from django.shortcuts import get_object_or_404 from .models import VM from .serializers import VMSerializer, OpenNebulaVMSerializer @@ -11,17 +12,16 @@ class RawVMViewSet(viewsets.ModelViewSet): permission_classes = [permissions.IsAdminUser] -class VMViewSet(viewsets.ModelViewSet): +class VMViewSet(viewsets.ViewSet): permission_classes = [permissions.IsAuthenticated] - serializer_class = OpenNebulaVMSerializer - - def get_queryset(self): - return VM.objects.filter(owner=self.request.user) def list(self, request): - serializer = OpenNebulaVMSerializer(self.queryset, many=True, context={'request': request}) + queryset = VM.objects.filter(owner=request.user) + serializer = OpenNebulaVMSerializer(queryset, many=True, context={'request': request}) return Response(serializer.data) def retrieve(self, request, pk=None): - serializer = OpenNebulaVMSerializer(self.queryset) + queryset = VM.objects.filter(owner=request.user) + user = get_object_or_404(queryset, pk=pk) + serializer = OpenNebulaVMSerializer(queryset) return Response(serializer.data) diff --git a/uncloud/requirements.txt b/uncloud/requirements.txt index 11ab309..e79f479 100644 --- a/uncloud/requirements.txt +++ b/uncloud/requirements.txt @@ -3,3 +3,4 @@ djangorestframework django-auth-ldap stripe xmltodict +git+https://code.ungleich.ch/ahmedbilal/ungleich-common/#egg=ungleich-common-ldap&subdirectory=ldap From c7252cde5312046492514e6a271aa7eccbbfed24 Mon Sep 17 00:00:00 2001 From: meow Date: Tue, 25 Feb 2020 13:09:54 +0500 Subject: [PATCH 04/10] Introduced local settings in meow-pay/uncloud django app --- uncloud/.gitignore | 3 +- uncloud/uncloud/settings.py | 57 ++++++++++--------------------------- 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/uncloud/.gitignore b/uncloud/.gitignore index 4ade18f..71202e1 100644 --- a/uncloud/.gitignore +++ b/uncloud/.gitignore @@ -1,3 +1,4 @@ db.sqlite3 uncloud/secrets.py -debug.log \ No newline at end of file +debug.log +uncloud/local_settings.py \ No newline at end of file diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py index 5ce8e92..e8530e7 100644 --- a/uncloud/uncloud/settings.py +++ b/uncloud/uncloud/settings.py @@ -12,18 +12,26 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ import os +import stripe +import ldap # Uncommitted file with secrets import uncloud.secrets -import stripe -import ldap - -import uncloud.secrets as secrets - from django_auth_ldap.config import LDAPSearch - +# Uncommitted file with local settings i.e logging +try: + from uncloud.local_settings import LOGGING, DATABASES +except ModuleNotFoundError: + LOGGING = {} + # https://docs.djangoproject.com/en/3.0/ref/settings/#databases + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': uncloud.secrets.POSTGRESQL_DB_NAME, + } + } # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -88,8 +96,6 @@ TEMPLATES = [ WSGI_APPLICATION = 'uncloud.wsgi.application' - - # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators @@ -167,37 +173,4 @@ USE_TZ = True STATIC_URL = '/static/' -stripe.api_key = secrets.STRIPE_KEY - -# FIXME: not sure if we really need this -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'file': { - 'level': 'DEBUG', - 'class': 'logging.FileHandler', - 'filename': 'debug.log', - }, - }, - 'loggers': { - 'django': { - 'handlers': ['file'], - 'level': 'DEBUG', - 'propagate': True, - }, - 'django_auth_ldap': { - 'handlers': ['file'], - 'level': 'DEBUG', - 'propagate': True - } - }, -} - -# https://docs.djangoproject.com/en/3.0/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': uncloud.secrets.POSTGRESQL_DB_NAME, - } -} +stripe.api_key = uncloud.secrets.STRIPE_KEY From d658b9635dff80764c2edbe1580bdcccbc32d438 Mon Sep 17 00:00:00 2001 From: meow Date: Tue, 25 Feb 2020 21:03:20 +0500 Subject: [PATCH 05/10] Replace (vmid,uuid) with id in VM model + Add last_host and graphics in VM model + Fixed retrieve view in uncloud.opennebula --- .../opennebula/management/commands/syncvm.py | 4 +-- .../migrations/0002_auto_20200225_1335.py | 27 +++++++++++++++++++ .../migrations/0003_auto_20200225_1428.py | 19 +++++++++++++ uncloud/opennebula/models.py | 16 ++++++++--- uncloud/opennebula/serializers.py | 4 +-- uncloud/opennebula/views.py | 4 +-- uncloud/uncloud/settings.py | 1 + 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 uncloud/opennebula/migrations/0002_auto_20200225_1335.py create mode 100644 uncloud/opennebula/migrations/0003_auto_20200225_1428.py diff --git a/uncloud/opennebula/management/commands/syncvm.py b/uncloud/opennebula/management/commands/syncvm.py index 00108f0..55844e3 100644 --- a/uncloud/opennebula/management/commands/syncvm.py +++ b/uncloud/opennebula/management/commands/syncvm.py @@ -54,9 +54,9 @@ class Command(BaseCommand): user_in_db = get_user_model().objects.get(email=vm_owner_email) except get_user_model().DoesNotExist: user_in_db = get_user_model().objects.create_user(username=user.uid, email=vm_owner_email) - VMModel.objects.update_or_create( - defaults={'data': vm, 'owner': user_in_db}, vmid=vm_id + id=f'opennebula{vm_id}', + defaults={'data': vm, 'owner': user_in_db} ) print('User with email but not found in ldap:', unknown_user_with_email) else: diff --git a/uncloud/opennebula/migrations/0002_auto_20200225_1335.py b/uncloud/opennebula/migrations/0002_auto_20200225_1335.py new file mode 100644 index 0000000..1554aa6 --- /dev/null +++ b/uncloud/opennebula/migrations/0002_auto_20200225_1335.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.3 on 2020-02-25 13:35 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('opennebula', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='vm', + name='uuid', + ), + migrations.RemoveField( + model_name='vm', + name='vmid', + ), + migrations.AddField( + model_name='vm', + name='id', + field=models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, unique=True), + ), + ] diff --git a/uncloud/opennebula/migrations/0003_auto_20200225_1428.py b/uncloud/opennebula/migrations/0003_auto_20200225_1428.py new file mode 100644 index 0000000..8bb3d8d --- /dev/null +++ b/uncloud/opennebula/migrations/0003_auto_20200225_1428.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-02-25 14:28 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('opennebula', '0002_auto_20200225_1335'), + ] + + operations = [ + migrations.AlterField( + model_name='vm', + name='id', + field=models.CharField(default=uuid.uuid4, max_length=64, primary_key=True, serialize=False, unique=True), + ), + ] diff --git a/uncloud/opennebula/models.py b/uncloud/opennebula/models.py index 0b0f307..904699d 100644 --- a/uncloud/opennebula/models.py +++ b/uncloud/opennebula/models.py @@ -1,15 +1,17 @@ import uuid from django.db import models from django.contrib.auth import get_user_model - from django.contrib.postgres.fields import JSONField + class VM(models.Model): - vmid = models.IntegerField(primary_key=True) - uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) + id = models.CharField(primary_key=True, editable=True, default=uuid.uuid4, unique=True, max_length=64) owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) data = JSONField() + def save(self, *args, **kwargs): + self.id = 'opennebula' + str(self.data.get("ID")) + super().save(*args, **kwargs) @property def cores(self): @@ -48,3 +50,11 @@ class VM(models.Model): ] return disks + + @property + def last_host(self): + return ((self.data.get('HISTORY_RECORDS', {}) or {}).get('HISTORY', {}) or {}).get('HOSTNAME', None) + + @property + def graphics(self): + return self.data.get('TEMPLATE', {}).get('GRAPHICS', {}) diff --git a/uncloud/opennebula/serializers.py b/uncloud/opennebula/serializers.py index 30bd20a..6bfaf56 100644 --- a/uncloud/opennebula/serializers.py +++ b/uncloud/opennebula/serializers.py @@ -5,10 +5,10 @@ from opennebula.models import VM class VMSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = VM - fields = ['vmid', 'owner', 'data'] + fields = ['id', 'owner', 'data'] class OpenNebulaVMSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = VM - fields = ['vmid', 'owner', 'cores', 'ram_in_gb', 'disks' ] + fields = ['id', 'owner', 'cores', 'ram_in_gb', 'disks', 'last_host', 'graphics'] diff --git a/uncloud/opennebula/views.py b/uncloud/opennebula/views.py index 66269c7..61ed5a4 100644 --- a/uncloud/opennebula/views.py +++ b/uncloud/opennebula/views.py @@ -22,6 +22,6 @@ class VMViewSet(viewsets.ViewSet): def retrieve(self, request, pk=None): queryset = VM.objects.filter(owner=request.user) - user = get_object_or_404(queryset, pk=pk) - serializer = OpenNebulaVMSerializer(queryset) + vm = get_object_or_404(queryset, pk=pk) + serializer = OpenNebulaVMSerializer(vm, context={'request': request}) return Response(serializer.data) diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py index e8530e7..91d2f73 100644 --- a/uncloud/uncloud/settings.py +++ b/uncloud/uncloud/settings.py @@ -148,6 +148,7 @@ AUTH_USER_MODEL = 'uncloud_auth.User' # AUTH/REST REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ] } From cc3d2f2d427c8ddafc939d98bee09c9437c59713 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Feb 2020 18:15:22 +0100 Subject: [PATCH 06/10] in-between-commit Signed-off-by: Nico Schottelius --- uncloud/README.md | 3 +++ uncloud/uncloud/settings.py | 1 + 2 files changed, 4 insertions(+) diff --git a/uncloud/README.md b/uncloud/README.md index 9db1c5c..6d5f1c8 100644 --- a/uncloud/README.md +++ b/uncloud/README.md @@ -39,6 +39,9 @@ Then create the database owner by the new role: postgres=# create database uncloud owner nico; ``` +Installing the postgresql service is os dependent, but some hints: + +* Alpine: `apk add postgresql-server && rc-update add postgresql && rc-service postgresql start` ### Secrets diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py index f671dc5..bdef1df 100644 --- a/uncloud/uncloud/settings.py +++ b/uncloud/uncloud/settings.py @@ -142,6 +142,7 @@ AUTH_USER_MODEL = 'uncloud_auth.User' # AUTH/REST REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ] } From 446c13b77c12461f3436b33ec993eb9daadf5979 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Feb 2020 19:23:39 +0100 Subject: [PATCH 07/10] fix/simplify syncvm --- nicohack202002/uncloud/opennebula/models.py | 11 -- nicohack202002/uncloud/opennebula/views.py | 59 --------- nicohack202002/uncloud/uncloud_api/models.py | 125 ------------------ nicohack202002/uncloud/uncloud_api/views.py | 83 ------------ .../opennebula/management/commands/syncvm.py | 37 ++---- .../migrations/0004_auto_20200225_1816.py | 23 ++++ uncloud/opennebula/models.py | 3 +- uncloud/requirements.txt | 1 - uncloud/uncloud/settings.py | 14 +- .../0002_vmsnapshotproduct_vm_uuid.py | 19 +++ uncloud/uncloud_api/models.py | 2 +- 11 files changed, 57 insertions(+), 320 deletions(-) delete mode 100644 nicohack202002/uncloud/opennebula/models.py delete mode 100644 nicohack202002/uncloud/opennebula/views.py delete mode 100644 nicohack202002/uncloud/uncloud_api/models.py delete mode 100644 nicohack202002/uncloud/uncloud_api/views.py create mode 100644 uncloud/opennebula/migrations/0004_auto_20200225_1816.py create mode 100644 uncloud/uncloud_api/migrations/0002_vmsnapshotproduct_vm_uuid.py diff --git a/nicohack202002/uncloud/opennebula/models.py b/nicohack202002/uncloud/opennebula/models.py deleted file mode 100644 index 915862a..0000000 --- a/nicohack202002/uncloud/opennebula/models.py +++ /dev/null @@ -1,11 +0,0 @@ -import uuid - -from django.db import models -from django.contrib.auth import get_user_model - - -class VM(models.Model): - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - vmid = models.IntegerField() - owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) - data = models.CharField(max_length=65536, null=True) diff --git a/nicohack202002/uncloud/opennebula/views.py b/nicohack202002/uncloud/opennebula/views.py deleted file mode 100644 index 1030101..0000000 --- a/nicohack202002/uncloud/opennebula/views.py +++ /dev/null @@ -1,59 +0,0 @@ -import json - -from rest_framework import generics -from rest_framework.authentication import SessionAuthentication, BasicAuthentication -from rest_framework.permissions import IsAuthenticated, IsAdminUser - -from .models import VM -from .serializers import VMSerializer - -class VMList(generics.ListAPIView): - authentication_classes = [SessionAuthentication, BasicAuthentication] - permission_classes = [IsAuthenticated, IsAdminUser] - queryset = VM.objects.all() - serializer_class = VMSerializer - - -class VMDetail(generics.RetrieveAPIView): - authentication_classes = [SessionAuthentication, BasicAuthentication] - permission_classes = [IsAuthenticated, IsAdminUser] - lookup_field = 'uuid' - queryset = VM.objects.all() - serializer_class = VMSerializer - - -class UserVMList(generics.ListAPIView): - authentication_classes = [SessionAuthentication, BasicAuthentication] - permission_classes = [IsAuthenticated] - serializer_class = VMSerializer - - def get_queryset(self): - user_email = self.request.user.ldap_user.attrs.data['mail'] - vms = [] - for mail in user_email: - vms += VM.objects.filter(owner__username=mail) - - for vm in vms: - data = json.loads(vm.data) - vm_template = data['TEMPLATE'] - vm.data = { - 'cpu': vm_template['VCPU'], - 'ram': vm_template['MEMORY'], - 'nic': vm_template['NIC'], - 'disks': vm_template['DISK'] - } - - return vms - -####################################### -# Following for quick experimentation # -####################################### - -# from django.http import HttpResponse -# -# def test(request): -# user_email = request.user.ldap_user.attrs.data['mail'] -# vms = [] -# for mail in user_email: -# vms += VM.objects.filter(owner__username=mail) -# return HttpResponse("Hello World") diff --git a/nicohack202002/uncloud/uncloud_api/models.py b/nicohack202002/uncloud/uncloud_api/models.py deleted file mode 100644 index 7eaec7b..0000000 --- a/nicohack202002/uncloud/uncloud_api/models.py +++ /dev/null @@ -1,125 +0,0 @@ -import uuid - -from django.db import models -from django.contrib.auth import get_user_model - -# Product in DB vs. product in code -# DB: -# - need to define params (+param types) in db -> messy? -# - get /products/ is easy / automatic -# -# code -# - can have serializer/verification of fields easily in DRF -# - can have per product side effects / extra code running -# - might (??) make features easier?? -# - how to setup / query the recurring period (?) -# - could get products list via getattr() + re ...Product() classes -# -> this could include the url for ordering => /order/vm_snapshot (params) -# ---> this would work with urlpatterns - -# Combination: create specific product in DB (?) -# - a table per product (?) with 1 entry? - -# Orders -# define state in DB -# select a price from a product => product might change, order stays -# params: -# - the product uuid or name (?) => productuuid -# - the product parameters => for each feature -# - -# logs -# Should have a log = ... => 1:n field for most models! - - -class Product(models.Model): - # override these fields by default - description = "" - recurring_period = "not_recurring" - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - status = models.CharField( - max_length=256, choices=( - ('pending', 'Pending'), - ('being_created', 'Being created'), - ('created_active', 'Created'), - ('deleted', 'Deleted') - ), - default='pending' - ) - - def __str__(self): - return "{}".format(self.name) - - -class VMSnapshotProduct(Product): - price_per_gb_ssd = 0.35 - price_per_gb_hdd = 1.5/100 - - sample_ssd = 10 - sample_hdd = 100 - - def recurring_price(self): - return 0 - - def one_time_price(self): - return 0 - - @classmethod - def sample_price(cls): - return cls.sample_ssd * cls.price_per_gb_ssd + cls.sample_hdd * cls.price_per_gb_hdd - - description = "Create snapshot of a VM" - recurring_period = "monthly" - - @classmethod - def pricing_model(cls): - return """ -Pricing is on monthly basis and storage prices are equivalent to the storage -price in the VM. - -Price per GB SSD is: {} -Price per GB HDD is: {} - - -Sample price for a VM with {} GB SSD and {} GB HDD VM is: {}. -""".format(cls.price_per_gb_ssd, cls.price_per_gb_hdd, - cls.sample_ssd, cls.sample_hdd, cls.sample_price()) - - gb_ssd = models.FloatField() - gb_hdd = models.FloatField() - - -class Feature(models.Model): - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=256) - - recurring_price = models.FloatField(default=0) - one_time_price = models.FloatField() - - product = models.ForeignKey(Product, on_delete=models.CASCADE) - - # params for "cpu": cpu_count -> int - # each feature can only have one parameters - # could call this "value" and set whether it is user usable - # has_value = True/False - # value = string -> int (?) - # value_int - # value_str - # value_float - - def __str__(self): - return "'{}' - '{}'".format(self.product, self.name) - - -class Order(models.Model): - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - - owner = models.ForeignKey(get_user_model(), - on_delete=models.CASCADE) - - product = models.ForeignKey(Product, - on_delete=models.CASCADE) - - -class VMSnapshotOrder(Order): - pass diff --git a/nicohack202002/uncloud/uncloud_api/views.py b/nicohack202002/uncloud/uncloud_api/views.py deleted file mode 100644 index 68963ff..0000000 --- a/nicohack202002/uncloud/uncloud_api/views.py +++ /dev/null @@ -1,83 +0,0 @@ -from django.shortcuts import render -from django.contrib.auth import get_user_model -from django.contrib.auth.models import Group - -from rest_framework import viewsets, permissions, generics -from .serializers import UserSerializer, GroupSerializer -from rest_framework.views import APIView -from rest_framework.response import Response - - -class CreditCardViewSet(viewsets.ModelViewSet): - - """ - API endpoint that allows credit cards to be listed - """ - queryset = get_user_model().objects.all().order_by('-date_joined') - serializer_class = UserSerializer - - permission_classes = [permissions.IsAuthenticated] - - -class UserViewSet(viewsets.ModelViewSet): - - """ - API endpoint that allows users to be viewed or edited. - """ - queryset = get_user_model().objects.all().order_by('-date_joined') - serializer_class = UserSerializer - - permission_classes = [permissions.IsAuthenticated] - -class GroupViewSet(viewsets.ModelViewSet): - """ - API endpoint that allows groups to be viewed or edited. - """ - queryset = Group.objects.all() - serializer_class = GroupSerializer - - permission_classes = [permissions.IsAuthenticated] - -class GroupViewSet(viewsets.ModelViewSet): - """ - API endpoint that allows groups to be viewed or edited. - """ - queryset = Group.objects.all() - serializer_class = GroupSerializer - - permission_classes = [permissions.IsAuthenticated] - - -# POST /vm/snapshot/ vmuuid=... => create snapshot, returns snapshot uuid -# GET /vm/snapshot => list -# DEL /vm/snapshot/ => delete -# create-list -> get, post => ListCreateAPIView -# del on other! -class VMSnapshotView(generics.ListCreateAPIView): - #lookup_field = 'uuid' - permission_classes = [permissions.IsAuthenticated] - -import inspect -import sys -import re - -# Next: create /order/ urls -# Next: strip off "Product" at the end -class ProductsView(APIView): - def get(self, request, format=None): - clsmembers = inspect.getmembers(sys.modules['uncloud_api.models'], inspect.isclass) - products = [] - for name, c in clsmembers: - # Include everything that ends in Product, but not Product itself - m = re.match(r'(?P.+)Product$', name) - if m: - products.append({ - 'name': m.group('pname'), - 'description': c.description, - 'recurring_period': c.recurring_period, - 'pricing_model': c.pricing_model() - } - ) - - - return Response(products) diff --git a/uncloud/opennebula/management/commands/syncvm.py b/uncloud/opennebula/management/commands/syncvm.py index 55844e3..779db61 100644 --- a/uncloud/opennebula/management/commands/syncvm.py +++ b/uncloud/opennebula/management/commands/syncvm.py @@ -8,15 +8,10 @@ from xmlrpc.client import ServerProxy as RPCClient from django.core.management.base import BaseCommand from django.contrib.auth import get_user_model from xmltodict import parse -from ungleich_common.ldap.ldap_manager import LdapManager from opennebula.models import VM as VMModel - -def find_user_based_on_email(users, email): - for user in users: - if email in user.mail.values: - return user +from django_auth_ldap.backend import LDAPBackend class Command(BaseCommand): @@ -26,39 +21,29 @@ class Command(BaseCommand): pass def handle(self, *args, **options): - ldap_server_uri = secrets.LDAP_SERVER_URI.split(',')[0] - ldap_manager = LdapManager( - server=ldap_server_uri, - admin_dn=secrets.LDAP_ADMIN_DN, - admin_password=secrets.LDAP_ADMIN_PASSWORD, - ) - users = ldap_manager.get('') # Get all users - with RPCClient(secrets.OPENNEBULA_URL) as rpc_client: success, response, *_ = rpc_client.one.vmpool.infoextended( secrets.OPENNEBULA_USER_PASS, -2, -1, -1, -1 ) if success: vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM'] - unknown_user_with_email = set() + unknown_user = set() + + backend = LDAPBackend() for vm in vms: vm_id = vm['ID'] - vm_owner_email = vm['UNAME'] + vm_owner = vm['UNAME'] + + user = backend.populate_user(username=vm_owner) - user = find_user_based_on_email(users, vm_owner_email) if not user: - unknown_user_with_email.add(vm_owner_email) + unknown_user.add(vm_owner) else: - try: - user_in_db = get_user_model().objects.get(email=vm_owner_email) - except get_user_model().DoesNotExist: - user_in_db = get_user_model().objects.create_user(username=user.uid, email=vm_owner_email) VMModel.objects.update_or_create( - id=f'opennebula{vm_id}', - defaults={'data': vm, 'owner': user_in_db} + vmid=vm_id, + defaults={'data': vm, 'owner': user} ) - print('User with email but not found in ldap:', unknown_user_with_email) + print('User not found in ldap:', unknown_user) else: print(response) - print(secrets.OPENNEBULA_USER_PASS) diff --git a/uncloud/opennebula/migrations/0004_auto_20200225_1816.py b/uncloud/opennebula/migrations/0004_auto_20200225_1816.py new file mode 100644 index 0000000..5b39f26 --- /dev/null +++ b/uncloud/opennebula/migrations/0004_auto_20200225_1816.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.3 on 2020-02-25 18:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('opennebula', '0003_auto_20200225_1428'), + ] + + operations = [ + migrations.RemoveField( + model_name='vm', + name='id', + ), + migrations.AddField( + model_name='vm', + name='vmid', + field=models.IntegerField(default=42, primary_key=True, serialize=False), + preserve_default=False, + ), + ] diff --git a/uncloud/opennebula/models.py b/uncloud/opennebula/models.py index 904699d..fff811b 100644 --- a/uncloud/opennebula/models.py +++ b/uncloud/opennebula/models.py @@ -5,7 +5,7 @@ from django.contrib.postgres.fields import JSONField class VM(models.Model): - id = models.CharField(primary_key=True, editable=True, default=uuid.uuid4, unique=True, max_length=64) + vmid = models.IntegerField(primary_key=True) owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) data = JSONField() @@ -34,7 +34,6 @@ class VM(models.Model): disks = [] if 'DISK' in self.data['TEMPLATE']: - if type(self.data['TEMPLATE']['DISK']) is dict: disks = [ self.data['TEMPLATE']['DISK'] ] else: diff --git a/uncloud/requirements.txt b/uncloud/requirements.txt index c7efd69..1b4e05b 100644 --- a/uncloud/requirements.txt +++ b/uncloud/requirements.txt @@ -3,5 +3,4 @@ djangorestframework django-auth-ldap stripe xmltodict -git+https://code.ungleich.ch/ahmedbilal/ungleich-common/#egg=ungleich-common-ldap&subdirectory=ldap psycopg2 diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py index b32b89a..624c9bb 100644 --- a/uncloud/uncloud/settings.py +++ b/uncloud/uncloud/settings.py @@ -18,7 +18,7 @@ import ldap # Uncommitted file with secrets import uncloud.secrets -from django_auth_ldap.config import LDAPSearch +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion # Uncommitted file with local settings i.e logging try: @@ -129,9 +129,7 @@ AUTH_LDAP_USER_ATTR_MAP = { AUTH_LDAP_BIND_DN = uncloud.secrets.LDAP_ADMIN_DN AUTH_LDAP_BIND_PASSWORD = uncloud.secrets.LDAP_ADMIN_PASSWORD -AUTH_LDAP_USER_SEARCH = LDAPSearch( - "dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=%(user)s)" -) +AUTH_LDAP_USER_SEARCH = LDAPSearch("dc=ungleich,dc=ch", ldap.SCOPE_SUBTREE, "(uid=%(user)s)") ################################################################################ @@ -174,12 +172,4 @@ USE_TZ = True STATIC_URL = '/static/' -# https://docs.djangoproject.com/en/3.0/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': uncloud.secrets.POSTGRESQL_DB_NAME, - } -} - stripe.api_key = uncloud.secrets.STRIPE_KEY diff --git a/uncloud/uncloud_api/migrations/0002_vmsnapshotproduct_vm_uuid.py b/uncloud/uncloud_api/migrations/0002_vmsnapshotproduct_vm_uuid.py new file mode 100644 index 0000000..b35317e --- /dev/null +++ b/uncloud/uncloud_api/migrations/0002_vmsnapshotproduct_vm_uuid.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-02-25 18:16 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_api', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='vmsnapshotproduct', + name='vm_uuid', + field=models.UUIDField(default=uuid.uuid4, editable=False), + ), + ] diff --git a/uncloud/uncloud_api/models.py b/uncloud/uncloud_api/models.py index 50857fb..6affaa3 100644 --- a/uncloud/uncloud_api/models.py +++ b/uncloud/uncloud_api/models.py @@ -72,7 +72,7 @@ class VMSnapshotProduct(Product): gb_ssd = models.FloatField() gb_hdd = models.FloatField() - vm_uuid = models.UUIDField() + vm_uuid = models.UUIDField(default=uuid.uuid4, editable=False) # Need to setup recurring_price and one_time_price and recurring period From d4b170f813d997f6c67c3ab0dfaa46a43d4535f3 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Feb 2020 20:53:12 +0100 Subject: [PATCH 08/10] phase in vmhost Signed-off-by: Nico Schottelius --- uncloud/README.md | 15 ++++ uncloud/uncloud/settings.py | 1 + uncloud/uncloud/urls.py | 12 ++- .../migrations/0003_auto_20200225_1950.py | 36 +++++++++ uncloud/uncloud_api/models.py | 24 ++---- uncloud/uncloud_api/serializers.py | 14 +++- uncloud/uncloud_api/views.py | 43 ++++++++++- uncloud/uncloud_vm/migrations/0001_initial.py | 75 +++++++++++++++++++ .../migrations/0002_auto_20200225_1952.py | 38 ++++++++++ uncloud/uncloud_vm/migrations/__init__.py | 0 uncloud/uncloud_vm/models.py | 40 +++++++--- uncloud/uncloud_vm/serializers.py | 9 +++ uncloud/uncloud_vm/views.py | 24 +++--- 13 files changed, 280 insertions(+), 51 deletions(-) create mode 100644 uncloud/uncloud_api/migrations/0003_auto_20200225_1950.py create mode 100644 uncloud/uncloud_vm/migrations/0001_initial.py create mode 100644 uncloud/uncloud_vm/migrations/0002_auto_20200225_1952.py create mode 100644 uncloud/uncloud_vm/migrations/__init__.py create mode 100644 uncloud/uncloud_vm/serializers.py diff --git a/uncloud/README.md b/uncloud/README.md index 6d5f1c8..e0c0d10 100644 --- a/uncloud/README.md +++ b/uncloud/README.md @@ -48,3 +48,18 @@ Installing the postgresql service is os dependent, but some hints: cp `uncloud/secrets_sample.py` to `uncloud/secrets.py` and replace the sample values with real values. + + +## Flows / Orders + +### Creating a VMHost + + + +### Creating a VM + +* Create a VMHost +* Create a VM on a VMHost + + +### Creating a VM Snapshot diff --git a/uncloud/uncloud/settings.py b/uncloud/uncloud/settings.py index 624c9bb..614cd25 100644 --- a/uncloud/uncloud/settings.py +++ b/uncloud/uncloud/settings.py @@ -62,6 +62,7 @@ INSTALLED_APPS = [ 'rest_framework', 'uncloud_api', 'uncloud_auth', + 'uncloud_vm', 'opennebula' ] diff --git a/uncloud/uncloud/urls.py b/uncloud/uncloud/urls.py index 60054c4..1fe8833 100644 --- a/uncloud/uncloud/urls.py +++ b/uncloud/uncloud/urls.py @@ -17,20 +17,26 @@ from django.contrib import admin from django.urls import path, include from rest_framework import routers -from uncloud_api import views +from uncloud_api import views as apiviews +from uncloud_vm import views as vmviews from opennebula import views as oneviews router = routers.DefaultRouter() -router.register(r'users', views.UserViewSet) +router.register(r'users', apiviews.UserViewSet) router.register(r'opennebula', oneviews.VMViewSet, basename='opennebula') router.register(r'opennebula_raw', oneviews.RawVMViewSet) +router.register(r'vmsnapshot', apiviews.VMSnapshotView, basename='vmsnapshot') + +# admin/staff urls +router.register(r'admin/vmhost', vmviews.VMHostViewSet) + # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ path('', include(router.urls)), path('admin/', admin.site.urls), - path('products/', views.ProductsView.as_view(), name='products'), + path('products/', apiviews.ProductsView.as_view(), name='products'), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) ] diff --git a/uncloud/uncloud_api/migrations/0003_auto_20200225_1950.py b/uncloud/uncloud_api/migrations/0003_auto_20200225_1950.py new file mode 100644 index 0000000..be7624c --- /dev/null +++ b/uncloud/uncloud_api/migrations/0003_auto_20200225_1950.py @@ -0,0 +1,36 @@ +# Generated by Django 3.0.3 on 2020-02-25 19:50 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('uncloud_api', '0002_vmsnapshotproduct_vm_uuid'), + ] + + operations = [ + migrations.AlterField( + model_name='vmsnapshotproduct', + name='gb_hdd', + field=models.FloatField(editable=False), + ), + migrations.AlterField( + model_name='vmsnapshotproduct', + name='gb_ssd', + field=models.FloatField(editable=False), + ), + migrations.AlterField( + model_name='vmsnapshotproduct', + name='owner', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='vmsnapshotproduct', + name='vm_uuid', + field=models.UUIDField(), + ), + ] diff --git a/uncloud/uncloud_api/models.py b/uncloud/uncloud_api/models.py index 6affaa3..acc3c63 100644 --- a/uncloud/uncloud_api/models.py +++ b/uncloud/uncloud_api/models.py @@ -34,7 +34,8 @@ from django.contrib.auth import get_user_model class Product(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) owner = models.ForeignKey(get_user_model(), - on_delete=models.CASCADE) + on_delete=models.CASCADE, + editable=False) # override these fields by default @@ -52,8 +53,8 @@ class Product(models.Model): ) # This is calculated by each product and saved in the DB - recurring_price = models.FloatField() - one_time_price = models.FloatField() + recurring_price = models.FloatField(editable=False) + one_time_price = models.FloatField(editable=False) @@ -69,14 +70,13 @@ class VMSnapshotProduct(Product): price_per_gb_hdd = 1.5/100 # This we need to get from the VM - gb_ssd = models.FloatField() - gb_hdd = models.FloatField() + gb_ssd = models.FloatField(editable=False) + gb_hdd = models.FloatField(editable=False) - vm_uuid = models.UUIDField(default=uuid.uuid4, editable=False) + vm_uuid = models.UUIDField() # Need to setup recurring_price and one_time_price and recurring period - sample_ssd = 10 sample_hdd = 100 @@ -137,13 +137,3 @@ class Feature(models.Model): def __str__(self): return "'{}' - '{}'".format(self.product, self.name) - - -# class Order(models.Model): -# uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - -# owner = models.ForeignKey(get_user_model(), -# on_delete=models.CASCADE) - -# product = models.ForeignKey(Product, -# on_delete=models.CASCADE) diff --git a/uncloud/uncloud_api/serializers.py b/uncloud/uncloud_api/serializers.py index 1573bf0..a3a8386 100644 --- a/uncloud/uncloud_api/serializers.py +++ b/uncloud/uncloud_api/serializers.py @@ -3,17 +3,25 @@ from django.contrib.auth import get_user_model from rest_framework import serializers +from .models import VMSnapshotProduct + class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = get_user_model() fields = ['url', 'username', 'email', 'groups'] - class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ['url', 'name'] -class VMSnapshotSerializer(serializers.Serializer): - pass +class VMSnapshotSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = VMSnapshotProduct + fields = ['uuid', 'status', 'recurring_price', 'one_time_price' ] + +class VMSnapshotCreateSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = VMSnapshotProduct + fields = '__all__' diff --git a/uncloud/uncloud_api/views.py b/uncloud/uncloud_api/views.py index c8ffca7..b71b3d2 100644 --- a/uncloud/uncloud_api/views.py +++ b/uncloud/uncloud_api/views.py @@ -3,14 +3,21 @@ from django.contrib.auth import get_user_model from django.contrib.auth.models import Group from rest_framework import viewsets, permissions, generics -from .serializers import UserSerializer, GroupSerializer + from rest_framework.views import APIView from rest_framework.response import Response +from uncloud_vm.models import VMProduct +from .models import VMSnapshotProduct +from .serializers import UserSerializer, GroupSerializer, VMSnapshotSerializer, VMSnapshotCreateSerializer + + import inspect import sys import re + + class UserViewSet(viewsets.ModelViewSet): """ @@ -27,10 +34,40 @@ class UserViewSet(viewsets.ModelViewSet): # DEL /vm/snapshot/ => delete # create-list -> get, post => ListCreateAPIView # del on other! -class VMSnapshotView(generics.ListCreateAPIView): - #lookup_field = 'uuid' +class VMSnapshotView(viewsets.ViewSet): permission_classes = [permissions.IsAuthenticated] + def list(self, request): + queryset = VMSnapshotProduct.objects.filter(owner=request.user) + serializer = VMSnapshotSerializer(queryset, many=True, context={'request': request}) + return Response(serializer.data) + + def retrieve(self, request, pk=None): + queryset = VMSnapshotProduct.objects.filter(owner=request.user) + vm = get_object_or_404(queryset, pk=pk) + serializer = VMSnapshotSerializer(vm, context={'request': request}) + return Response(serializer.data) + + def create(self, request): + print(request.data) + serializer = VMSnapshotCreateSerializer(data=request.data) + + serializer.gb_ssd = 12 + serializer.gb_hdd = 120 + print("F") + serializer.is_valid(raise_exception=True) + + print(serializer) + print("A") + serializer.save() + print("B") + + + # snapshot = VMSnapshotProduct(owner=request.user, + # **serialzer.data) + + return Response(serializer.data) + # Next: create /order/ urls # Next: strip off "Product" at the end diff --git a/uncloud/uncloud_vm/migrations/0001_initial.py b/uncloud/uncloud_vm/migrations/0001_initial.py new file mode 100644 index 0000000..dc4d657 --- /dev/null +++ b/uncloud/uncloud_vm/migrations/0001_initial.py @@ -0,0 +1,75 @@ +# Generated by Django 3.0.3 on 2020-02-25 19:50 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='VMDiskProduct', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('size_in_gb', models.FloatField()), + ('storage_class', models.CharField(choices=[('hdd', 'HDD'), ('ssd', 'SSD')], default='ssd', max_length=32)), + ], + ), + migrations.CreateModel( + name='VMHost', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('hostname', models.CharField(max_length=253)), + ('physical_cores', models.IntegerField()), + ('usable_cores', models.IntegerField()), + ('usable_ram_in_gb', models.FloatField()), + ('status', models.CharField(choices=[('pending', 'Pending'), ('active', 'Active'), ('unusable', 'Unusable')], default='pending', max_length=32)), + ], + ), + migrations.CreateModel( + name='VMProduct', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('cores', models.IntegerField()), + ('ram_in_gb', models.FloatField()), + ('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('vmhost', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMHost')), + ], + ), + migrations.CreateModel( + name='OperatingSystemDisk', + fields=[ + ('vmdiskproduct_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='uncloud_vm.VMDiskProduct')), + ('os_name', models.CharField(max_length=128)), + ], + bases=('uncloud_vm.vmdiskproduct',), + ), + migrations.CreateModel( + name='VMWithOSProduct', + fields=[ + ('vmproduct_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='uncloud_vm.VMProduct')), + ], + bases=('uncloud_vm.vmproduct',), + ), + migrations.CreateModel( + name='VMNetworkCard', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('mac_address', models.IntegerField()), + ('vm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMProduct')), + ], + ), + migrations.AddField( + model_name='vmdiskproduct', + name='vm', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMProduct'), + ), + ] diff --git a/uncloud/uncloud_vm/migrations/0002_auto_20200225_1952.py b/uncloud/uncloud_vm/migrations/0002_auto_20200225_1952.py new file mode 100644 index 0000000..46a207b --- /dev/null +++ b/uncloud/uncloud_vm/migrations/0002_auto_20200225_1952.py @@ -0,0 +1,38 @@ +# Generated by Django 3.0.3 on 2020-02-25 19:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_vm', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='vmhost', + name='hostname', + field=models.CharField(max_length=253, unique=True), + ), + migrations.AlterField( + model_name='vmhost', + name='physical_cores', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='vmhost', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('active', 'Active'), ('unusable', 'Unusable'), ('deleted', 'Deleted')], default='pending', max_length=32), + ), + migrations.AlterField( + model_name='vmhost', + name='usable_cores', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='vmhost', + name='usable_ram_in_gb', + field=models.FloatField(default=0), + ), + ] diff --git a/uncloud/uncloud_vm/migrations/__init__.py b/uncloud/uncloud_vm/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/uncloud/uncloud_vm/models.py b/uncloud/uncloud_vm/models.py index faf61b0..f79caf3 100644 --- a/uncloud/uncloud_vm/models.py +++ b/uncloud/uncloud_vm/models.py @@ -1,20 +1,22 @@ from django.db import models +from django.contrib.auth import get_user_model +import uuid class VMHost(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # 253 is the maximum DNS name length - hostname = models.CharField(max_length=253) + hostname = models.CharField(max_length=253, unique=True) # indirectly gives a maximum number of cores / VM - f.i. 32 - physical_cores = models.IntegerField() + physical_cores = models.IntegerField(default=0) # determines the maximum usable cores - f.i. 320 if you overbook by a factor of 10 - usable_cores = models.IntegerField() + usable_cores = models.IntegerField(default=0) # ram that can be used of the server - usable_ram_in_gb = models.FloatField() + usable_ram_in_gb = models.FloatField(default=0) status = models.CharField(max_length=32, @@ -22,24 +24,33 @@ class VMHost(models.Model): ('pending', 'Pending'), ('active', 'Active'), ('unusable', 'Unusable'), + ('deleted', 'Deleted'), ), default='pending' ) -class VM(models.Model): - uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) +class VMProduct(models.Model): + uuid = models.UUIDField(primary_key=True, + default=uuid.uuid4, + editable=False) + owner = models.ForeignKey(get_user_model(), + on_delete=models.CASCADE, + editable=False) + vmhost = models.ForeignKey(VMHost, + on_delete=models.CASCADE, + editable=False) cores = models.IntegerField() ram_in_gb = models.FloatField() - vmhost = models.ForeignKey(VMHost, on_delete=models.CASCADE) +class VMWithOSProduct(VMProduct): + pass -class VMDisk(models.Model): +class VMDiskProduct(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - vm = models.ForeignKey(VM, on_delete=models.CASCADE) + vm = models.ForeignKey(VMProduct, on_delete=models.CASCADE) size_in_gb = models.FloatField() storage_class = models.CharField(max_length=32, @@ -49,3 +60,12 @@ class VMDisk(models.Model): ), default='ssd' ) + +class OperatingSystemDisk(VMDiskProduct): + """ Defines an Operating System Disk that can be cloned for a VM """ + os_name = models.CharField(max_length=128) + + +class VMNetworkCard(models.Model): + vm = models.ForeignKey(VMProduct, on_delete=models.CASCADE) + mac_address = models.IntegerField() diff --git a/uncloud/uncloud_vm/serializers.py b/uncloud/uncloud_vm/serializers.py new file mode 100644 index 0000000..1279df2 --- /dev/null +++ b/uncloud/uncloud_vm/serializers.py @@ -0,0 +1,9 @@ +from django.contrib.auth import get_user_model + +from rest_framework import serializers +from .models import VMHost + +class VMHostSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = VMHost + fields = '__all__' diff --git a/uncloud/uncloud_vm/views.py b/uncloud/uncloud_vm/views.py index aa5855c..7b4d7a2 100644 --- a/uncloud/uncloud_vm/views.py +++ b/uncloud/uncloud_vm/views.py @@ -1,24 +1,18 @@ from django.shortcuts import render - from django.contrib.auth.models import User from django.shortcuts import get_object_or_404 -from myapps.serializers import UserSerializer -from rest_framework import viewsets + +from rest_framework import viewsets, permissions from rest_framework.response import Response + from opennebula.models import VM as OpenNebulaVM -class VMViewSet(viewsets.ViewSet): - def list(self, request): - queryset = User.objects.all() - serializer = UserSerializer(queryset, many=True) - return Response(serializer.data) +from .models import VMHost +from .serializers import VMHostSerializer - def retrieve(self, request, pk=None): - queryset = User.objects.all() - user = get_object_or_404(queryset, pk=pk) - serializer = UserSerializer(user) - return Response(serializer.data) - - permission_classes = [permissions.IsAuthenticated] +class VMHostViewSet(viewsets.ModelViewSet): + serializer_class = VMHostSerializer + queryset = VMHost.objects.all() + permission_classes = [permissions.IsAdminUser] From c7ded96658ee10085642d4f8e07ee08321ce02a3 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Feb 2020 22:01:55 +0100 Subject: [PATCH 09/10] vmhosts, restructure urls, etc. --- uncloud/uncloud/urls.py | 16 ++--- uncloud/uncloud_api/serializers.py | 5 +- uncloud/uncloud_api/views.py | 63 ++++++++++--------- .../management/commands/schedulevms.py | 21 +++++++ .../management/commands/vmhealth.py | 24 +++++++ .../migrations/0003_auto_20200225_2028.py | 19 ++++++ uncloud/uncloud_vm/models.py | 4 +- uncloud/uncloud_vm/serializers.py | 8 ++- uncloud/uncloud_vm/views.py | 21 +++++-- 9 files changed, 134 insertions(+), 47 deletions(-) create mode 100644 uncloud/uncloud_vm/management/commands/schedulevms.py create mode 100644 uncloud/uncloud_vm/management/commands/vmhealth.py create mode 100644 uncloud/uncloud_vm/migrations/0003_auto_20200225_2028.py diff --git a/uncloud/uncloud/urls.py b/uncloud/uncloud/urls.py index 1fe8833..23392c5 100644 --- a/uncloud/uncloud/urls.py +++ b/uncloud/uncloud/urls.py @@ -23,20 +23,22 @@ from uncloud_vm import views as vmviews from opennebula import views as oneviews router = routers.DefaultRouter() -router.register(r'users', apiviews.UserViewSet) -router.register(r'opennebula', oneviews.VMViewSet, basename='opennebula') -router.register(r'opennebula_raw', oneviews.RawVMViewSet) -router.register(r'vmsnapshot', apiviews.VMSnapshotView, basename='vmsnapshot') + +router.register(r'user', apiviews.UserViewSet, basename='user') + +router.register(r'vm/snapshot', apiviews.VMSnapshotView, basename='VMSnapshot') +router.register(r'vm/vm', vmviews.VMProductViewSet, basename='vmproduct') # admin/staff urls router.register(r'admin/vmhost', vmviews.VMHostViewSet) +router.register(r'admin/opennebula', oneviews.VMViewSet, basename='opennebula') +router.register(r'admin/opennebula_raw', oneviews.RawVMViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ path('', include(router.urls)), - path('admin/', admin.site.urls), - path('products/', apiviews.ProductsView.as_view(), name='products'), - path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) + path('admin/', admin.site.urls), # login to django itself + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) # for login to REST API ] diff --git a/uncloud/uncloud_api/serializers.py b/uncloud/uncloud_api/serializers.py index a3a8386..7dc3686 100644 --- a/uncloud/uncloud_api/serializers.py +++ b/uncloud/uncloud_api/serializers.py @@ -5,11 +5,10 @@ from rest_framework import serializers from .models import VMSnapshotProduct - -class UserSerializer(serializers.HyperlinkedModelSerializer): +class UserSerializer(serializers.ModelSerializer): class Meta: model = get_user_model() - fields = ['url', 'username', 'email', 'groups'] + fields = ['url', 'username', 'email'] class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: diff --git a/uncloud/uncloud_api/views.py b/uncloud/uncloud_api/views.py index b71b3d2..eb4cc77 100644 --- a/uncloud/uncloud_api/views.py +++ b/uncloud/uncloud_api/views.py @@ -17,18 +17,6 @@ import sys import re - -class UserViewSet(viewsets.ModelViewSet): - - """ - API endpoint that allows users to be viewed or edited. - """ - queryset = get_user_model().objects.all().order_by('-date_joined') - serializer_class = UserSerializer - - permission_classes = [permissions.IsAuthenticated] - - # POST /vm/snapshot/ vmuuid=... => create snapshot, returns snapshot uuid # GET /vm/snapshot => list # DEL /vm/snapshot/ => delete @@ -69,23 +57,38 @@ class VMSnapshotView(viewsets.ViewSet): return Response(serializer.data) -# Next: create /order/ urls -# Next: strip off "Product" at the end -class ProductsView(APIView): - def get(self, request, format=None): - clsmembers = inspect.getmembers(sys.modules['uncloud_api.models'], inspect.isclass) - products = [] - for name, c in clsmembers: - # Include everything that ends in Product, but not Product itself - m = re.match(r'(?P.+)Product$', name) - if m: - products.append({ - 'name': m.group('pname'), - 'description': c.description, - 'recurring_period': c.recurring_period, - 'pricing_model': c.pricing_model() - } - ) + +# maybe drop or not --- we need something to guide the user! +# class ProductsViewSet(viewsets.ViewSet): +# permission_classes = [permissions.IsAuthenticated] + +# def list(self, request): + +# clsmembers = [] +# for modules in [ 'uncloud_api.models', 'uncloud_vm.models' ]: +# clsmembers.extend(inspect.getmembers(sys.modules[modules], inspect.isclass)) - return Response(products) +# products = [] +# for name, c in clsmembers: +# # Include everything that ends in Product, but not Product itself +# m = re.match(r'(?P.+)Product$', name) +# if m: +# products.append({ +# 'name': m.group('pname'), +# 'description': c.description, +# 'recurring_period': c.recurring_period, +# 'pricing_model': c.pricing_model() +# } +# ) + + +# return Response(products) + + +class UserViewSet(viewsets.ModelViewSet): + serializer_class = UserSerializer + permission_classes = [permissions.IsAuthenticated] + + def get_queryset(self): + return self.request.user diff --git a/uncloud/uncloud_vm/management/commands/schedulevms.py b/uncloud/uncloud_vm/management/commands/schedulevms.py new file mode 100644 index 0000000..836e100 --- /dev/null +++ b/uncloud/uncloud_vm/management/commands/schedulevms.py @@ -0,0 +1,21 @@ +import json + +import uncloud.secrets as secrets + +from django.core.management.base import BaseCommand +from django.contrib.auth import get_user_model + +from uncloud_vm.models import VMProduct, VMHost + +class Command(BaseCommand): + help = 'Select VM Host for VMs' + + def add_arguments(self, parser): + pass + + def handle(self, *args, **options): + pending_vms = VMProduct.objects.filter(vmhost__isnull=True) + vmhosts = VMHost.objects.filter(status='active') + for vm in pending_vms: + print(vm) + # FIXME: implement smart placement diff --git a/uncloud/uncloud_vm/management/commands/vmhealth.py b/uncloud/uncloud_vm/management/commands/vmhealth.py new file mode 100644 index 0000000..6109af7 --- /dev/null +++ b/uncloud/uncloud_vm/management/commands/vmhealth.py @@ -0,0 +1,24 @@ +import json + +from django.core.management.base import BaseCommand +from django.contrib.auth import get_user_model + +from uncloud_vm.models import VMProduct, VMHost + +class Command(BaseCommand): + help = 'Check health of VMs and VMHosts' + + def add_arguments(self, parser): + pass + + def handle(self, *args, **options): + pending_vms = VMProduct.objects.filter(vmhost__isnull=True) + vmhosts = VMHost.objects.filter(status='active') + + # 1. Check that all active hosts reported back N seconds ago + # 2. Check that no VM is running on a dead host + # 3. Migrate VMs if necessary + # 4. Check that no VMs have been pending for longer than Y seconds + + + print("Nothing is good, you should implement me") diff --git a/uncloud/uncloud_vm/migrations/0003_auto_20200225_2028.py b/uncloud/uncloud_vm/migrations/0003_auto_20200225_2028.py new file mode 100644 index 0000000..a4e5976 --- /dev/null +++ b/uncloud/uncloud_vm/migrations/0003_auto_20200225_2028.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-02-25 20:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('uncloud_vm', '0002_auto_20200225_1952'), + ] + + operations = [ + migrations.AlterField( + model_name='vmproduct', + name='vmhost', + field=models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='uncloud_vm.VMHost'), + ), + ] diff --git a/uncloud/uncloud_vm/models.py b/uncloud/uncloud_vm/models.py index f79caf3..f4b68dd 100644 --- a/uncloud/uncloud_vm/models.py +++ b/uncloud/uncloud_vm/models.py @@ -39,7 +39,9 @@ class VMProduct(models.Model): editable=False) vmhost = models.ForeignKey(VMHost, on_delete=models.CASCADE, - editable=False) + editable=False, + blank=True, + null=True) cores = models.IntegerField() ram_in_gb = models.FloatField() diff --git a/uncloud/uncloud_vm/serializers.py b/uncloud/uncloud_vm/serializers.py index 1279df2..4154aee 100644 --- a/uncloud/uncloud_vm/serializers.py +++ b/uncloud/uncloud_vm/serializers.py @@ -1,9 +1,15 @@ from django.contrib.auth import get_user_model from rest_framework import serializers -from .models import VMHost +from .models import VMHost, VMProduct class VMHostSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = VMHost fields = '__all__' + + +class VMProductSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = VMProduct + fields = '__all__' diff --git a/uncloud/uncloud_vm/views.py b/uncloud/uncloud_vm/views.py index 7b4d7a2..91e81e1 100644 --- a/uncloud/uncloud_vm/views.py +++ b/uncloud/uncloud_vm/views.py @@ -6,13 +6,24 @@ from django.shortcuts import get_object_or_404 from rest_framework import viewsets, permissions from rest_framework.response import Response - -from opennebula.models import VM as OpenNebulaVM - -from .models import VMHost -from .serializers import VMHostSerializer +from .models import VMHost, VMProduct +from .serializers import VMHostSerializer, VMProductSerializer class VMHostViewSet(viewsets.ModelViewSet): serializer_class = VMHostSerializer queryset = VMHost.objects.all() permission_classes = [permissions.IsAdminUser] + +class VMProductViewSet(viewsets.ModelViewSet): + permission_classes = [permissions.IsAuthenticated] + serializer_class = VMProductSerializer + + def get_queryset(self): + return VMProduct.objects.filter(owner=self.request.user) + + def create(self, request): + serializer = VMProductSerializer(data=request.data, context={'request': request}) + serializer.is_valid(raise_exception=True) + serializer.save(owner=request.user) + + return Response(serializer.data) From bd3d21faa9f90426927341820a8886f3cb1294c7 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Tue, 25 Feb 2020 22:04:04 +0100 Subject: [PATCH 10/10] add thoughts for health checking --- uncloud/uncloud_vm/management/commands/vmhealth.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uncloud/uncloud_vm/management/commands/vmhealth.py b/uncloud/uncloud_vm/management/commands/vmhealth.py index 6109af7..9397b16 100644 --- a/uncloud/uncloud_vm/management/commands/vmhealth.py +++ b/uncloud/uncloud_vm/management/commands/vmhealth.py @@ -20,5 +20,7 @@ class Command(BaseCommand): # 3. Migrate VMs if necessary # 4. Check that no VMs have been pending for longer than Y seconds + # If VM snapshots exist without a VM -> notify user (?) + print("Nothing is good, you should implement me")