Merge nico/meow-pay into ahmedbilal/meow-pay

This commit is contained in:
Ahmed Bilal 2020-02-22 07:32:52 +01:00
parent d61a7e670f
commit dc34c0ecd4
13 changed files with 232 additions and 26 deletions

View file

@ -9,8 +9,7 @@ from xmltodict import parse
from opennebula.models import VM as VMModel from opennebula.models import VM as VMModel
OCA_SESSION_STRING = os.environ.get('OCASECRETS', '') import uncloud.secrets
class Command(BaseCommand): class Command(BaseCommand):
help = 'Syncronize VM information from OpenNebula' help = 'Syncronize VM information from OpenNebula'
@ -19,9 +18,9 @@ class Command(BaseCommand):
pass pass
def handle(self, *args, **options): 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( 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: if success:
vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM'] vms = json.loads(json.dumps(parse(response)))['VM_POOL']['VM']
@ -37,4 +36,3 @@ class Command(BaseCommand):
vm_object.save() vm_object.save()
else: else:
print(response) print(response)

View file

@ -0,0 +1,5 @@
django
djangorestframework
django-auth-ldap
stripe
xmltodict

View file

@ -0,0 +1,17 @@
# 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'

View file

@ -30,7 +30,9 @@ router.register(r'groups', views.GroupViewSet)
urlpatterns = [ urlpatterns = [
path('', include(router.urls)), path('', include(router.urls)),
path('admin/', admin.site.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('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('vm/list/', oneviews.VMList.as_view(), name='vm_list'), path('vm/list/', oneviews.VMList.as_view(), name='vm_list'),
path('vm/detail/<int:vmid>/', oneviews.VMDetail.as_view(), name='vm_detail'), path('vm/detail/<int:vmid>/', oneviews.VMDetail.as_view(), name='vm_detail'),
] ]

View file

@ -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 = '<None>'
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))

View file

@ -0,0 +1,29 @@
import time
from django.conf import settings
from django.core.management.base import BaseCommand
from uncloud_api import models
class Command(BaseCommand):
args = '<None>'
help = 'VM Snapshot support'
def add_arguments(self, parser):
parser.add_argument('command', type=str, help='Command')
def handle(self, *args, **options):
print("Snapshotting")
#getattr(self, options['command'])(**options)
@classmethod
def monitor(cls, **_):
while True:
try:
tweets = models.Reply.get_target_tweets()
responses = models.Reply.objects.values_list('tweet_id', flat=True)
new_tweets = [x for x in tweets if x.id not in responses]
models.Reply.send(new_tweets)
except TweepError as e:
print(e)
time.sleep(60)

View file

@ -3,28 +3,89 @@ import uuid
from django.db import models from django.db import models
from django.contrib.auth import get_user_model 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): class Product(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # override these fields by default
name = models.CharField(max_length=256) description = ""
recurring_period = "not_recurring"
recurring_period = models.CharField(max_length=256, status = models.CharField(max_length=256,
choices = ( choices = (
("per_year", "Per Year"), ('pending', 'Pending'),
("per_month", "Per Month"), ('being_created', 'Being created'),
("per_week", "Per Week"), ('created_active', 'Created'),
("per_day", "Per Day"), ('deleted', 'Deleted')
("per_hour", "Per Hour"),
("not_recurring", "Not recurring")
),
default="not_recurring"
) )
def __str__(self): def __str__(self):
return "{}".format(self.name) 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): class Feature(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
@ -35,6 +96,15 @@ class Feature(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE) 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): def __str__(self):
return "'{}' - '{}'".format(self.product, self.name) return "'{}' - '{}'".format(self.product, self.name)
@ -49,10 +119,5 @@ class Order(models.Model):
on_delete=models.CASCADE) on_delete=models.CASCADE)
class OrderReference(models.Model): class VMSnapshotOrder(Order):
"""
An order can references another product / relate to it.
This model is used for the relation
"""
pass pass

View file

@ -14,3 +14,6 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = Group model = Group
fields = ['url', 'name'] fields = ['url', 'name']
class VMSnapshotSerializer(serializers.Serializer):
pass

View file

@ -2,9 +2,11 @@ from django.shortcuts import render
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group 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 .serializers import UserSerializer, GroupSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
class CreditCardViewSet(viewsets.ModelViewSet): class CreditCardViewSet(viewsets.ModelViewSet):
@ -35,3 +37,47 @@ class GroupViewSet(viewsets.ModelViewSet):
serializer_class = GroupSerializer serializer_class = GroupSerializer
permission_classes = [permissions.IsAuthenticated] 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/<uuid:uuid> => 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/<productname> 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<pname>.+)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)

View file

@ -1,5 +1,14 @@
* snapshot feature * snapshot feature
** product: vm-snapshot ** 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 * steps
** DONE authenticate via ldap ** DONE authenticate via ldap
CLOSED: [2020-02-20 Thu 19:05] CLOSED: [2020-02-20 Thu 19:05]

6
plan.org Normal file
View file

@ -0,0 +1,6 @@
* TODO register CC
* TODO list products
* ahmed
** schemas
*** field: is_valid? - used by schemas
*** definition of a "schema"