From b1bb6bc314c44ac028ae27ce664caec22c7ff3b2 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Sat, 22 Feb 2020 00:22:42 +0100 Subject: [PATCH] Make products available via getattr --- .../opennebula/management/commands/syncvm.py | 8 ++- nicohack202002/uncloud/requirements.txt | 1 + .../uncloud/uncloud/secrets_sample.py | 8 ++- nicohack202002/uncloud/uncloud/urls.py | 2 + .../uncloud_api/management/commands/hack.py | 26 +++++++++ nicohack202002/uncloud/uncloud_api/models.py | 54 ++++++++++++++++--- .../uncloud/uncloud_api/serializers.py | 3 ++ nicohack202002/uncloud/uncloud_api/views.py | 45 +++++++++++++++- notes-nico.org | 9 ++++ plan.org | 6 +++ 10 files changed, 147 insertions(+), 15 deletions(-) create mode 100644 nicohack202002/uncloud/uncloud_api/management/commands/hack.py create mode 100644 plan.org diff --git a/nicohack202002/uncloud/opennebula/management/commands/syncvm.py b/nicohack202002/uncloud/opennebula/management/commands/syncvm.py index 5ea451d..205b066 100644 --- a/nicohack202002/uncloud/opennebula/management/commands/syncvm.py +++ b/nicohack202002/uncloud/opennebula/management/commands/syncvm.py @@ -9,8 +9,7 @@ from xmltodict import parse from opennebula.models import VM as VMModel -OCA_SESSION_STRING = os.environ.get('OCASECRETS', '') - +import uncloud.secrets class Command(BaseCommand): help = 'Syncronize VM information from OpenNebula' @@ -19,9 +18,9 @@ class Command(BaseCommand): pass def handle(self, *args, **options): - with RPCClient('https://opennebula.ungleich.ch:2634/RPC2') as rpc_client: + with RPCClient(uncloud.secrets.OPENNEBULA_URL) as rpc_client: success, response, *_ = rpc_client.one.vmpool.infoextended( - OCA_SESSION_STRING, -2, -1, -1, -1 + uncloud.secrets.OPENNEBULA_USER_PASS, -2, -1, -1, -1 ) if success: vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM'] @@ -37,4 +36,3 @@ class Command(BaseCommand): vm_object.save() else: print(response) - diff --git a/nicohack202002/uncloud/requirements.txt b/nicohack202002/uncloud/requirements.txt index d81b59b..11ab309 100644 --- a/nicohack202002/uncloud/requirements.txt +++ b/nicohack202002/uncloud/requirements.txt @@ -2,3 +2,4 @@ django djangorestframework django-auth-ldap stripe +xmltodict diff --git a/nicohack202002/uncloud/uncloud/secrets_sample.py b/nicohack202002/uncloud/uncloud/secrets_sample.py index a895bc9..d145124 100644 --- a/nicohack202002/uncloud/uncloud/secrets_sample.py +++ b/nicohack202002/uncloud/uncloud/secrets_sample.py @@ -7,5 +7,11 @@ - +# Live/test key from stripe STRIPE_KEY="" + +# XML-RPC interface of opennebula +OPENNEBULA_URL='https://opennebula.ungleich.ch:2634/RPC2' + +# user:pass for accessing opennebula +OPENNEBULA_USER_PASS='user:password' diff --git a/nicohack202002/uncloud/uncloud/urls.py b/nicohack202002/uncloud/uncloud/urls.py index f5804c9..c7ce9b6 100644 --- a/nicohack202002/uncloud/uncloud/urls.py +++ b/nicohack202002/uncloud/uncloud/urls.py @@ -30,7 +30,9 @@ router.register(r'groups', views.GroupViewSet) urlpatterns = [ path('', include(router.urls)), path('admin/', admin.site.urls), + path('products/', views.ProductsView.as_view(), name='products'), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), path('vm/list/', oneviews.VMList.as_view(), name='vm_list'), path('vm/detail//', oneviews.VMDetail.as_view(), name='vm_detail'), + ] diff --git a/nicohack202002/uncloud/uncloud_api/management/commands/hack.py b/nicohack202002/uncloud/uncloud_api/management/commands/hack.py new file mode 100644 index 0000000..e129952 --- /dev/null +++ b/nicohack202002/uncloud/uncloud_api/management/commands/hack.py @@ -0,0 +1,26 @@ +import time +from django.conf import settings +from django.core.management.base import BaseCommand + +import uncloud_api.models + +import inspect +import sys +import re + +class Command(BaseCommand): + args = '' + help = 'hacking - only use if you are Nico' + + def add_arguments(self, parser): + parser.add_argument('command', type=str, help='Command') + + def handle(self, *args, **options): + getattr(self, options['command'])(**options) + + @classmethod + def classtest(cls, **_): + clsmembers = inspect.getmembers(sys.modules['uncloud_api.models'], inspect.isclass) + for name, c in clsmembers: + if re.match(r'.+Product$', name): + print("{} -> {}".format(name, c)) diff --git a/nicohack202002/uncloud/uncloud_api/models.py b/nicohack202002/uncloud/uncloud_api/models.py index 06e77ed..6df17c4 100644 --- a/nicohack202002/uncloud/uncloud_api/models.py +++ b/nicohack202002/uncloud/uncloud_api/models.py @@ -3,9 +3,38 @@ 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): + + description = "" + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=256) @@ -21,10 +50,18 @@ class Product(models.Model): default="not_recurring" ) + # params = [ vmuuid, ... ] + # features -> required as defined + def __str__(self): return "{}".format(self.name) +class VMSnapshotProduct(Product): + # need to setup recurring_periodd + + description = "Create snapshot of a VM" + class Feature(models.Model): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -35,6 +72,15 @@ class Feature(models.Model): 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) @@ -49,11 +95,5 @@ class Order(models.Model): on_delete=models.CASCADE) - -class OrderReference(models.Model): - """ - An order can references another product / relate to it. - This model is used for the relation - """ - +class VMSnapshotOrder(Order): pass diff --git a/nicohack202002/uncloud/uncloud_api/serializers.py b/nicohack202002/uncloud/uncloud_api/serializers.py index 57532f2..1573bf0 100644 --- a/nicohack202002/uncloud/uncloud_api/serializers.py +++ b/nicohack202002/uncloud/uncloud_api/serializers.py @@ -14,3 +14,6 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ['url', 'name'] + +class VMSnapshotSerializer(serializers.Serializer): + pass diff --git a/nicohack202002/uncloud/uncloud_api/views.py b/nicohack202002/uncloud/uncloud_api/views.py index 88e0543..8cf76f2 100644 --- a/nicohack202002/uncloud/uncloud_api/views.py +++ b/nicohack202002/uncloud/uncloud_api/views.py @@ -2,9 +2,11 @@ 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 - +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): @@ -35,3 +37,42 @@ class GroupViewSet(viewsets.ModelViewSet): 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 + +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 + if re.search(r'.+Product$', name): + products.append({ + 'name': name, + 'description': c.description + } + ) + + + return Response(products) diff --git a/notes-nico.org b/notes-nico.org index 21102f9..93e0c00 100644 --- a/notes-nico.org +++ b/notes-nico.org @@ -1,5 +1,14 @@ * snapshot feature ** product: vm-snapshot +** flow +*** list all my VMs +**** get the uuid of the VM I want to take a snapshot of +*** request a snapshot +``` +vmuuid=$(http nicocustomer +http -a nicocustomer:xxx http://uncloud.ch/vm/create_snapshot uuid= +password=... +``` * steps ** DONE authenticate via ldap CLOSED: [2020-02-20 Thu 19:05] diff --git a/plan.org b/plan.org new file mode 100644 index 0000000..9f172c2 --- /dev/null +++ b/plan.org @@ -0,0 +1,6 @@ +* TODO register CC +* TODO list products +* ahmed +** schemas +*** field: is_valid? - used by schemas +*** definition of a "schema"