Merge nico/meow-pay into ahmedbilal/meow-pay
This commit is contained in:
		
					parent
					
						
							
								d61a7e670f
							
						
					
				
			
			
				commit
				
					
						dc34c0ecd4
					
				
			
		
					 13 changed files with 232 additions and 26 deletions
				
			
		|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										5
									
								
								nicohack202002/uncloud/requirements.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								nicohack202002/uncloud/requirements.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| django | ||||
| djangorestframework | ||||
| django-auth-ldap | ||||
| stripe | ||||
| xmltodict | ||||
							
								
								
									
										17
									
								
								nicohack202002/uncloud/uncloud/secrets_sample.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								nicohack202002/uncloud/uncloud/secrets_sample.py
									
										
									
									
									
										Normal 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' | ||||
|  | @ -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/<int:vmid>/', oneviews.VMDetail.as_view(), name='vm_detail'), | ||||
| 
 | ||||
| ] | ||||
|  |  | |||
|  | @ -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)) | ||||
|  | @ -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) | ||||
|  | @ -3,28 +3,89 @@ 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): | ||||
|     uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) | ||||
|     name = models.CharField(max_length=256) | ||||
|     # override these fields by default | ||||
|     description = "" | ||||
|     recurring_period = "not_recurring" | ||||
| 
 | ||||
|     recurring_period = models.CharField(max_length=256, | ||||
|                                         choices = ( | ||||
|                                             ("per_year", "Per Year"), | ||||
|                                             ("per_month", "Per Month"), | ||||
|                                             ("per_week", "Per Week"), | ||||
|                                             ("per_day", "Per Day"), | ||||
|                                             ("per_hour", "Per Hour"), | ||||
|                                             ("not_recurring", "Not recurring") | ||||
|                                         ), | ||||
|                                         default="not_recurring" | ||||
|     ) | ||||
|     status = models.CharField(max_length=256, | ||||
|                               choices = ( | ||||
|                                   ('pending', 'Pending'), | ||||
|                                   ('being_created', 'Being created'), | ||||
|                                   ('created_active', 'Created'), | ||||
|                                   ('deleted', 'Deleted') | ||||
|                               ) | ||||
| 
 | ||||
|     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) | ||||
|  | @ -35,6 +96,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,10 +119,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 | ||||
|  |  | |||
|  | @ -14,3 +14,6 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer): | |||
|     class Meta: | ||||
|         model = Group | ||||
|         fields = ['url', 'name'] | ||||
| 
 | ||||
| class VMSnapshotSerializer(serializers.Serializer): | ||||
|     pass | ||||
|  |  | |||
|  | @ -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,47 @@ 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/<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) | ||||
|  |  | |||
|  | @ -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] | ||||
|  |  | |||
							
								
								
									
										6
									
								
								plan.org
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								plan.org
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| * TODO register CC | ||||
| * TODO list products | ||||
| * ahmed | ||||
| ** schemas | ||||
| *** field: is_valid? - used by schemas | ||||
| *** definition of a "schema" | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue