API Integration
Please review carefully.
This commit is contained in:
		
					parent
					
						
							
								b7e8eceb25
							
						
					
				
			
			
				commit
				
					
						130c00c8ee
					
				
			
		
					 19 changed files with 310 additions and 890 deletions
				
			
		|  | @ -4,99 +4,8 @@ from django.core.urlresolvers import reverse | ||||||
| 
 | 
 | ||||||
| from utils.mailer import BaseEmail | from utils.mailer import BaseEmail | ||||||
| 
 | 
 | ||||||
| from .forms import HostingOrderAdminForm | from .models import HostingOrder, HostingBill | ||||||
| from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, \ |  | ||||||
|                     ManageVM, HostingBill |  | ||||||
| from .opennebula_functions import HostingManageVMAdmin |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class HostingOrderAdmin(admin.ModelAdmin): | admin.site.register(HostingOrder) | ||||||
|     # fields = ('slug', 'imdb_link', 'start', 'finish', 'added_by') |  | ||||||
|     list_display = ('id', 'created_at', 'plan', 'user') |  | ||||||
|     search_fields = ['vm_plan__id', 'customer__user__email'] |  | ||||||
| 
 |  | ||||||
|     def save_model(self, request, obj, form, change): |  | ||||||
|         if not change: |  | ||||||
|             customer = form.cleaned_data.get('customer') |  | ||||||
| 
 |  | ||||||
|             # Get and set billing address from the lastest charged order |  | ||||||
|             last_order = HostingOrder.objects.filter(customer=customer).latest('id') |  | ||||||
|             billing_address = last_order.billing_address |  | ||||||
|             obj.billing_address = billing_address |  | ||||||
| 
 |  | ||||||
|             charge = form.cleaned_data.get('charge') |  | ||||||
|             # Associate an order with a stripe payment |  | ||||||
|             obj.set_stripe_charge(charge) |  | ||||||
| 
 |  | ||||||
|             # If the Stripe payment was successed, set order status approved |  | ||||||
|             obj.set_approved() |  | ||||||
| 
 |  | ||||||
|             # Assigning permissions |  | ||||||
|             obj.assign_permissions(customer.user) |  | ||||||
| 
 |  | ||||||
|             context = { |  | ||||||
|                 'order': obj, |  | ||||||
|                 'vm': obj.vm_plan, |  | ||||||
|                 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) |  | ||||||
|             } |  | ||||||
|             email_data = { |  | ||||||
|                 'subject': 'Your VM plan has been charged', |  | ||||||
|                 'to': obj.customer.user.email, |  | ||||||
|                 'context': context, |  | ||||||
|                 'template_name': 'vm_charged', |  | ||||||
|                 'template_path': 'hosting/emails/' |  | ||||||
|             } |  | ||||||
|             email = BaseEmail(**email_data) |  | ||||||
|             email.send() |  | ||||||
| 
 |  | ||||||
|         obj.save() |  | ||||||
|         return obj |  | ||||||
| 
 |  | ||||||
|     def get_form(self, request, obj=None, **kwargs): |  | ||||||
|         if obj is None: |  | ||||||
|             kwargs['form'] = HostingOrderAdminForm |  | ||||||
|         return super(HostingOrderAdmin, self).get_form(request, obj, **kwargs) |  | ||||||
| 
 |  | ||||||
|     def user(self, obj): |  | ||||||
|         email = obj.customer.user.email |  | ||||||
|         user_url = reverse("admin:membership_customuser_change", args=[obj.customer.user.id]) |  | ||||||
|         return format_html("<a href='{url}'>{email}</a>", url=user_url, email=email) |  | ||||||
| 
 |  | ||||||
|     def plan(self, obj): |  | ||||||
|         vm_name = obj.vm_plan.name |  | ||||||
|         vm_url = reverse("admin:hosting_virtualmachineplan_change", args=[obj.vm_plan.id]) |  | ||||||
|         return format_html("<a href='{url}'>{vm_name}</a>", url=vm_url, vm_name=vm_name) |  | ||||||
| 
 |  | ||||||
|     plan.short_description = "Virtual Machine Plan" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VirtualMachinePlanAdmin(admin.ModelAdmin): |  | ||||||
|     list_display = ('name', 'id', 'email') |  | ||||||
| 
 |  | ||||||
|     def email(self, obj): |  | ||||||
|         return obj.hosting_orders.latest('id').customer.user.email |  | ||||||
| 
 |  | ||||||
|     def save_model(self, request, obj, form, change): |  | ||||||
|         email = self.email(obj) |  | ||||||
|         if 'status' in form.changed_data: |  | ||||||
|             context = { |  | ||||||
|                 'vm': obj, |  | ||||||
|                 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) |  | ||||||
|             } |  | ||||||
|             email_data = { |  | ||||||
|                 'subject': 'Your VM has been activated', |  | ||||||
|                 'to': email, |  | ||||||
|                 'context': context, |  | ||||||
|                 'template_name': 'vm_status_changed', |  | ||||||
|                 'template_path': 'hosting/emails/' |  | ||||||
|             } |  | ||||||
|             email = BaseEmail(**email_data) |  | ||||||
|             email.send() |  | ||||||
|         obj.save() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| admin.site.register(HostingOrder, HostingOrderAdmin) |  | ||||||
| admin.site.register(VirtualMachineType) |  | ||||||
| admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin) |  | ||||||
| admin.site.register(ManageVM, HostingManageVMAdmin) |  | ||||||
| admin.site.register(HostingBill) | admin.site.register(HostingBill) | ||||||
|  |  | ||||||
|  | @ -7,39 +7,7 @@ from django.contrib.auth import authenticate | ||||||
| 
 | 
 | ||||||
| from utils.stripe_utils import StripeUtils | from utils.stripe_utils import StripeUtils | ||||||
| 
 | 
 | ||||||
| from .models import HostingOrder, VirtualMachinePlan, UserHostingKey | from .models import HostingOrder, UserHostingKey | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class HostingOrderAdminForm(forms.ModelForm): |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
|         model = HostingOrder |  | ||||||
|         fields = ['vm_plan', 'customer'] |  | ||||||
| 
 |  | ||||||
|     def clean(self): |  | ||||||
|         customer = self.cleaned_data.get('customer') |  | ||||||
|         vm_plan = self.cleaned_data.get('vm_plan') |  | ||||||
| 
 |  | ||||||
|         if vm_plan.status == VirtualMachinePlan.CANCELED_STATUS: |  | ||||||
|             raise forms.ValidationError("""You can't make a charge over |  | ||||||
|                                          a canceled virtual machine plan""") |  | ||||||
| 
 |  | ||||||
|         if not customer: |  | ||||||
|             raise forms.ValidationError("""You need select a costumer""") |  | ||||||
| 
 |  | ||||||
|         # Make a charge to the customer |  | ||||||
|         stripe_utils = StripeUtils() |  | ||||||
|         charge_response = stripe_utils.make_charge(customer=customer.stripe_id, |  | ||||||
|                                                    amount=vm_plan.price) |  | ||||||
|         charge = charge_response.get('response_object') |  | ||||||
|         if not charge: |  | ||||||
|             raise forms.ValidationError(charge_response.get('error')) |  | ||||||
| 
 |  | ||||||
|         self.cleaned_data.update({ |  | ||||||
|             'charge': charge |  | ||||||
|         }) |  | ||||||
|         return self.cleaned_data |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class HostingUserLoginForm(forms.Form): | class HostingUserLoginForm(forms.Form): | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								hosting/migrations/0038_auto_20170512_1006.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								hosting/migrations/0038_auto_20170512_1006.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2017-05-12 10:06 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | from django.db import migrations, models | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('hosting', '0037_merge'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='virtualmachineplan', | ||||||
|  |             name='vm_type', | ||||||
|  |         ), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='hostingorder', | ||||||
|  |             name='vm_plan', | ||||||
|  |         ), | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='hostingorder', | ||||||
|  |             name='vm_id', | ||||||
|  |             field=models.IntegerField(default=0), | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='VirtualMachinePlan', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='VirtualMachineType', | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -1,35 +1,20 @@ | ||||||
| from django.shortcuts import redirect | from django.shortcuts import redirect | ||||||
| from django.core.urlresolvers import reverse | from django.core.urlresolvers import reverse | ||||||
| from .models import VirtualMachinePlan, VirtualMachineType |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ProcessVMSelectionMixin(object): | class ProcessVMSelectionMixin(object): | ||||||
| 
 | 
 | ||||||
|     def post(self, request, *args, **kwargs): |     def post(self, request, *args, **kwargs): | ||||||
|         configuration = request.POST.get('configuration') |         #configuration = request.POST.get('configuration') | ||||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) |         #configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||||
|         vm_template = request.POST.get('vm_template') |         vm_template_id = request.POST.get('vm_template_id') | ||||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) |  | ||||||
|         vm_specs = vm_type.get_specs() |  | ||||||
|         vm_specs.update({ |         vm_specs.update({ | ||||||
|             'configuration_display': configuration_display, |             'configuration_display': configuration_display, | ||||||
|             'configuration': configuration, |             'configuration': configuration, | ||||||
|             'final_price': vm_type.final_price, |             'vm_template_id': vm_template_id | ||||||
|             'vm_template': vm_template |  | ||||||
|         }) |         }) | ||||||
|         # vm_specs = { |  | ||||||
|         #     # 'cores': request.POST.get('cores'), |  | ||||||
|         #     # 'memory': request.POST.get('memory'), |  | ||||||
|         #     # 'disk_size': request.POST.get('disk_space'), |  | ||||||
|         #     # 'hosting_company': request.POST.get('hosting_company'), |  | ||||||
|         #     # 'location_code': request.POST.get('location_code'), |  | ||||||
|         #     # 'configuration': hosting, |  | ||||||
|         #     # 'configuration_detail': configuration_detail, |  | ||||||
|         #     'final_price': request.POST.get('final_price') |  | ||||||
|         # } |  | ||||||
|         request.session['vm_specs'] = vm_specs |         request.session['vm_specs'] = vm_specs | ||||||
|         if not request.user.is_authenticated(): |         if not request.user.is_authenticated(): | ||||||
|             request.session['vm_specs'] = vm_specs |  | ||||||
|             request.session['next'] = reverse('hosting:payment') |             request.session['next'] = reverse('hosting:payment') | ||||||
|             return redirect(reverse('hosting:login')) |             return redirect(reverse('hosting:login')) | ||||||
|         return redirect(reverse('hosting:payment')) |         return redirect(reverse('hosting:payment')) | ||||||
|  |  | ||||||
|  | @ -22,224 +22,12 @@ from oca.pool import WrongNameError | ||||||
| import logging | import logging | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| class VirtualMachineType(models.Model): |  | ||||||
| 
 |  | ||||||
|     description = models.TextField() |  | ||||||
|     base_price = models.FloatField() |  | ||||||
|     memory_price = models.FloatField() |  | ||||||
|     core_price = models.FloatField() |  | ||||||
|     disk_size_price = models.FloatField() |  | ||||||
|     cores = models.IntegerField() |  | ||||||
|     memory = models.IntegerField() |  | ||||||
|     disk_size = models.IntegerField() |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return "VM Type %s" % (self.id) |  | ||||||
| 
 |  | ||||||
|     @cached_property |  | ||||||
|     def final_price(self): |  | ||||||
|         price = self.cores * self.core_price |  | ||||||
|         price += self.memory * self.memory_price |  | ||||||
|         price += self.disk_size * self.disk_size_price |  | ||||||
|         return price |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def get_serialized_vm_types(cls): |  | ||||||
|         return [vm.get_serialized_data() |  | ||||||
|                 for vm in cls.objects.all()] |  | ||||||
| 
 |  | ||||||
|     def calculate_price(self): |  | ||||||
|         price = self.cores * self.core_price |  | ||||||
|         price += self.memory * self.memory_price |  | ||||||
|         price += self.disk_size * self.disk_size_price |  | ||||||
|         # price += self.base_price |  | ||||||
|         return price |  | ||||||
| 
 |  | ||||||
|     # @classmethod |  | ||||||
|     # def get_price(cls, vm_template): |  | ||||||
|     #     return cls.BASE_PRICE * vm_template |  | ||||||
| 
 |  | ||||||
|     def get_specs(self): |  | ||||||
|         return { |  | ||||||
|             'memory': self.memory, |  | ||||||
|             'cores': self.cores, |  | ||||||
|             'disk_size': self.disk_size |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     # def calculate_price(self, vm_template): |  | ||||||
|     #     price = self.base_price * vm_template |  | ||||||
|     #     return price |  | ||||||
| 
 |  | ||||||
|     # def defeault_price(self): |  | ||||||
|     #     price = self.base_price |  | ||||||
|     #     price += self.core_price |  | ||||||
|     #     price += self.memory_price |  | ||||||
|     #     price += self.disk_size_price * 10 |  | ||||||
|     #     return price |  | ||||||
| 
 |  | ||||||
|     def get_serialized_data(self): |  | ||||||
|         return { |  | ||||||
|             'description': self.description, |  | ||||||
|             'core_price': self.core_price, |  | ||||||
|             'disk_size_price': self.disk_size_price, |  | ||||||
|             'memory_price': self.memory_price, |  | ||||||
|             'id': self.id, |  | ||||||
|             'final_price': self.final_price, |  | ||||||
|             'cores': self.cores, |  | ||||||
|             'memory': self.memory, |  | ||||||
|             'disk_size': self.disk_size |  | ||||||
| 
 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VirtualMachinePlan(AssignPermissionsMixin, models.Model): |  | ||||||
| 
 |  | ||||||
|     PENDING_STATUS = 'pending' |  | ||||||
|     ONLINE_STATUS = 'online' |  | ||||||
|     CANCELED_STATUS = 'canceled' |  | ||||||
| 
 |  | ||||||
|     VM_STATUS_CHOICES = ( |  | ||||||
|         (PENDING_STATUS, 'Pending for activation'), |  | ||||||
|         (ONLINE_STATUS, 'Online'), |  | ||||||
|         (CANCELED_STATUS, 'Canceled') |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     # DJANGO = 'django' |  | ||||||
|     # RAILS = 'rails' |  | ||||||
|     # NODEJS = 'nodejs' |  | ||||||
| 
 |  | ||||||
|     # VM_CONFIGURATION = ( |  | ||||||
|     #     (DJANGO, 'Ubuntu 14.04, Django'), |  | ||||||
|     #     (RAILS, 'Ubuntu 14.04, Rails'), |  | ||||||
|     #     (NODEJS, 'Debian, NodeJS'), |  | ||||||
|     # ) |  | ||||||
| 
 |  | ||||||
|     VM_CONFIGURATION = ( |  | ||||||
|         ('debian', 'Debian 8'), |  | ||||||
|         ('ubuntu', 'Ubuntu 16.06'), |  | ||||||
|         ('devuan', 'Devuan 1'), |  | ||||||
|         ('centos', 'CentOS 7') |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     permissions = ('view_virtualmachineplan', |  | ||||||
|                    'cancel_virtualmachineplan', |  | ||||||
|                    'change_virtualmachineplan') |  | ||||||
| 
 |  | ||||||
|     cores = models.IntegerField() |  | ||||||
|     memory = models.IntegerField() |  | ||||||
|     disk_size = models.IntegerField() |  | ||||||
|     vm_type = models.ForeignKey(VirtualMachineType, null=True) |  | ||||||
|     price = models.FloatField() |  | ||||||
|     public_key = models.TextField(blank=True) |  | ||||||
|     status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS) |  | ||||||
|     ip = models.CharField(max_length=50, blank=True) |  | ||||||
|     configuration = models.CharField(max_length=20, choices=VM_CONFIGURATION) |  | ||||||
|     opennebula_id = models.IntegerField(null=True) |  | ||||||
| 
 |  | ||||||
|     objects = VMPlansManager() |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
|         permissions = ( |  | ||||||
|             ('view_virtualmachineplan', 'View Virtual Machine Plan'), |  | ||||||
|             ('cancel_virtualmachineplan', 'Cancel Virtual Machine Plan'), |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|     def __str__(self): |  | ||||||
|         return self.name |  | ||||||
| 
 |  | ||||||
|     # @cached_property |  | ||||||
|     # def hosting_company_name(self): |  | ||||||
|     #     return self.vm_type.get_hosting_company_display() |  | ||||||
| 
 |  | ||||||
|     # @cached_property |  | ||||||
|     # def location(self): |  | ||||||
|     #     return self.vm_type.get_location_display() |  | ||||||
| 
 |  | ||||||
|     @cached_property |  | ||||||
|     def name(self): |  | ||||||
|         name = 'vm-%s' % self.id |  | ||||||
|         return name |  | ||||||
| 
 |  | ||||||
|     @cached_property |  | ||||||
|     def notifications(self): |  | ||||||
|         stripe_customer = StripeCustomer.objects.get(hostingorder__vm_plan=self) |  | ||||||
|         backend = stored_messages_settings.STORAGE_BACKEND() |  | ||||||
|         messages = backend.inbox_list(stripe_customer.user) |  | ||||||
|         return messages |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def create(cls, data, user): |  | ||||||
|         instance = cls.objects.create(**data) |  | ||||||
|         instance.assign_permissions(user) |  | ||||||
|         return instance |  | ||||||
| 
 |  | ||||||
|     def cancel_plan(self): |  | ||||||
|         self.status = self.CANCELED_STATUS |  | ||||||
|         self.save(update_fields=['status']) |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def create_opennebula_vm(self, user, specs): |  | ||||||
| 
 |  | ||||||
|         # Init opennebula manager using given user |  | ||||||
|         opennebula_client = OpenNebulaManager( |  | ||||||
|             user.email, |  | ||||||
|             user.password[0:20], |  | ||||||
|             create_user=True |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Create a vm in opennebula using given specs |  | ||||||
|         vm = opennebula_client.create_vm(specs) |  | ||||||
|         return vm |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def get_vm(self, user, vm_id): |  | ||||||
|         # Get opennebula client |  | ||||||
|         opennebula_client = OpenNebulaManager( |  | ||||||
|             email=user.email, |  | ||||||
|             password=user.password[:20], |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Get vm given the id |  | ||||||
|         vm = opennebula_client.get_vm( |  | ||||||
|             user.email, |  | ||||||
|             vm_id |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Parse vm data |  | ||||||
|         vm_data = OpenNebulaManager.parse_vm(vm) |  | ||||||
| 
 |  | ||||||
|         return vm_data |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def get_vms(self, user): |  | ||||||
| 
 |  | ||||||
|         # Get opennebula client |  | ||||||
|         opennebula_client = OpenNebulaManager( |  | ||||||
|             email=user.email, |  | ||||||
|             password=user.password[:20], |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Get vm pool |  | ||||||
|         vm_pool = opennebula_client.get_vms(user.email) |  | ||||||
| 
 |  | ||||||
|         # Reset total price |  | ||||||
|         self.total_price = 0 |  | ||||||
|         vms = [] |  | ||||||
|         # Add vm in vm_pool to context |  | ||||||
|         for vm in vm_pool: |  | ||||||
|             vm_data = OpenNebulaManager.parse_vm(vm) |  | ||||||
|             vms.append(vm_data) |  | ||||||
|             # self.total_price += price |  | ||||||
|         # self.save() |  | ||||||
|         return vms |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class HostingOrder(AssignPermissionsMixin, models.Model): | class HostingOrder(AssignPermissionsMixin, models.Model): | ||||||
| 
 | 
 | ||||||
|     ORDER_APPROVED_STATUS = 'Approved' |     ORDER_APPROVED_STATUS = 'Approved' | ||||||
|     ORDER_DECLINED_STATUS = 'Declined' |     ORDER_DECLINED_STATUS = 'Declined' | ||||||
| 
 | 
 | ||||||
|     vm_plan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') |     vm_id = models.IntegerField(default=0) | ||||||
|     customer = models.ForeignKey(StripeCustomer) |     customer = models.ForeignKey(StripeCustomer) | ||||||
|     billing_address = models.ForeignKey(BillingAddress) |     billing_address = models.ForeignKey(BillingAddress) | ||||||
|     created_at = models.DateTimeField(auto_now_add=True) |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
|  | @ -305,17 +93,6 @@ class UserHostingKey(models.Model): | ||||||
|         # self.save(update_fields=['public_key']) |         # self.save(update_fields=['public_key']) | ||||||
|         return private_key, public_key |         return private_key, public_key | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| class ManageVM(models.Model): |  | ||||||
|     def has_add_permission(self, request): |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     def has_delete_permission(self, request, obj=None): |  | ||||||
|         return False |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
|         managed = False |  | ||||||
| 
 |  | ||||||
| class HostingBill(AssignPermissionsMixin, models.Model): | class HostingBill(AssignPermissionsMixin, models.Model): | ||||||
|     customer = models.ForeignKey(StripeCustomer) |     customer = models.ForeignKey(StripeCustomer) | ||||||
|     billing_address = models.ForeignKey(BillingAddress) |     billing_address = models.ForeignKey(BillingAddress) | ||||||
|  | @ -336,32 +113,3 @@ class HostingBill(AssignPermissionsMixin, models.Model): | ||||||
|         instance = cls.objects.create(customer=customer, billing_address=billing_address) |         instance = cls.objects.create(customer=customer, billing_address=billing_address) | ||||||
|         return instance |         return instance | ||||||
| 
 | 
 | ||||||
|     def get_vms(self): |  | ||||||
|         email = self.customer.user.email |  | ||||||
|         # Get opennebula client |  | ||||||
|         opennebula_client = OpenNebulaManager(create_user=False) |  | ||||||
| 
 |  | ||||||
|         # Get vm pool |  | ||||||
|         vm_pool = opennebula_client.get_vms(email) |  | ||||||
| 
 |  | ||||||
|         # Reset total price |  | ||||||
|         self.total_price = 0 |  | ||||||
|         vms = [] |  | ||||||
|         # Add vm in vm_pool to context |  | ||||||
|         for vm in vm_pool: |  | ||||||
|             vm_data = OpenNebulaManager.parse_vm(vm) |  | ||||||
|             self.total_price += vm_data['price'] |  | ||||||
|             vms.append(vm_data) |  | ||||||
|         self.save() |  | ||||||
|         return vms |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|          |  | ||||||
| def get_user_opennebula_password(): |  | ||||||
|     ''' |  | ||||||
|     TODO: Implement the way we obtain the user's opennebula password  |  | ||||||
|     ''' |  | ||||||
|     pw = os.environ.get('OPENNEBULA_USER_PW') |  | ||||||
|     if pw is None: |  | ||||||
|         raise Exception("Define OPENNEBULA_USER_PW env variable") |  | ||||||
|     return pw |  | ||||||
|  |  | ||||||
|  | @ -78,7 +78,7 @@ | ||||||
|             </div> |             </div> | ||||||
|             <div class="row"> |             <div class="row"> | ||||||
|                 <div class="col-sm-6"> |                 <div class="col-sm-6"> | ||||||
|                     {% trans "CH02 ............" %} |                     {% trans "CH02 0900 0000 6071 8848 8%} | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="col-sm-6"> |                 <div class="col-sm-6"> | ||||||
|                     {% trans "POFICHBEXXX" %} |                     {% trans "POFICHBEXXX" %} | ||||||
|  |  | ||||||
|  | @ -12,10 +12,14 @@ | ||||||
|                     {% csrf_token %} |                     {% csrf_token %} | ||||||
|                     <div class="form-group"> |                     <div class="form-group"> | ||||||
|                         Select VM: |                         Select VM: | ||||||
|                         <select name="vm_template"> |                         <select name="vm_template_id"> | ||||||
|                             {% for vm in vm_types %} |                             {% for template in templates %} | ||||||
|                                 |                                 | ||||||
|                                   <option value="{{vm.id}}">CORE: {{vm.cores}}, RAM: {{vm.memory}}, SSD: {{vm.disk_size}}  </option> |                                   <option value="{{template.id}}"> | ||||||
|  |                                                 CORE: {{template.cores}}, | ||||||
|  |                                                 RAM: {{template.memory}},r | ||||||
|  |                                                 SSD: {{template.disk_size}} | ||||||
|  |                                   </option> | ||||||
|                                  |                                  | ||||||
|                             {% endfor %} |                             {% endfor %} | ||||||
|                         </select> |                         </select> | ||||||
|  |  | ||||||
|  | @ -49,17 +49,13 @@ | ||||||
|             <h3><b>{% trans "Order summary"%}</b></h3> |             <h3><b>{% trans "Order summary"%}</b></h3> | ||||||
|             <hr> |             <hr> | ||||||
|             <div class="content"> |             <div class="content"> | ||||||
|                 <p><b>{% trans "Type"%}</b> <span class="pull-right">{{order.vm_plan.hosting_company_name}}</span></p> |                 <p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cores}}</span></p> | ||||||
|                 <hr> |                 <hr> | ||||||
|                 <p><b>{% trans "Configuration"%}</b> <span class="pull-right">{{order.vm_plan.get_configuration_display}}</span></p> |                 <p><b>{% trans "Memory"%}</b> <span class="pull-right">{{vm.memory}} GiB</span></p> | ||||||
|                 <hr> |                 <hr> | ||||||
|                 <p><b>{% trans "Cores"%}</b> <span class="pull-right">{{order.vm_plan.cores}}</span></p> |                 <p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{vm.disk_size}} GiB</span></p> | ||||||
|                 <hr> |                 <hr> | ||||||
|                 <p><b>{% trans "Memory"%}</b> <span class="pull-right">{{order.vm_plan.memory}} GiB</span></p> |                 <h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b></p></h4> | ||||||
|                 <hr> |  | ||||||
|                 <p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{order.vm_plan.disk_size}} GiB</span></p> |  | ||||||
|                 <hr> |  | ||||||
|                 <h4>{% trans "Total"%}<p class="pull-right"><b>{{order.vm_plan.price}} CHF</b></p></h4> |  | ||||||
|             </div> |             </div> | ||||||
|             <br/> |             <br/> | ||||||
|             {% url 'hosting:payment' as payment_url %} |             {% url 'hosting:payment' as payment_url %} | ||||||
|  |  | ||||||
|  | @ -88,15 +88,14 @@ | ||||||
| 							<div class="content"> | 							<div class="content"> | ||||||
| 								<!-- <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> --> | 								<!-- <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> --> | ||||||
| 								<!-- <hr> --> | 								<!-- <hr> --> | ||||||
| 								<p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p> | 								<p><b>Cores</b> <span class="pull-right">{{request.session.template.cores}}</span></p> | ||||||
| 								<hr> | 								<hr> | ||||||
| 								<p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_display}}</span></p> | 								<p><b>Memory</b> <span class="pull-right">{{request.session.template.memory}} GiB</span></p> | ||||||
| 								<hr> | 								<hr> | ||||||
| 								<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p> | 								<p><b>Disk space</b> <span class="pull-right">{{request.session.template.disk_size}} GiB</span></p> | ||||||
| 								<hr> | 								<hr> | ||||||
| 								<p><b>Disk space</b> <span class="pull-right">{{request.session.vm_specs.disk_size}} GiB</span></p> | 								<h4>Total<p | ||||||
| 								<hr> |                                     class="pull-right"><b>{{request.session.template.price }} CHF</b></p></h4> | ||||||
| 								<h4>Total<p class="pull-right"><b>{{request.session.vm_specs.final_price}} CHF</b></p></h4> |  | ||||||
| 							</div> | 							</div> | ||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
|  |  | ||||||
|  | @ -25,12 +25,6 @@ | ||||||
| 				            		{% trans "Billing"%} | 				            		{% trans "Billing"%} | ||||||
| 				            	</a> | 				            	</a> | ||||||
| 				            </li> | 				            </li> | ||||||
| 				            <li> |  | ||||||
| 				            	<a href="#orders-v" data-toggle="tab"> |  | ||||||
| 				            		<i class="fa fa-credit-card"></i>  |  | ||||||
| 				            		{% trans "Orders"%} |  | ||||||
| 				            	</a> |  | ||||||
| 				            </li> |  | ||||||
| 				            <li> | 				            <li> | ||||||
| 				            	<a href="#status-v" data-toggle="tab"> | 				            	<a href="#status-v" data-toggle="tab"> | ||||||
| 				            		<i class="fa fa-signal" aria-hidden="true"></i> {% trans "Status"%} | 				            		<i class="fa fa-signal" aria-hidden="true"></i> {% trans "Status"%} | ||||||
|  | @ -109,54 +103,20 @@ | ||||||
| 									</div> | 									</div> | ||||||
| 				            	</div> | 				            	</div> | ||||||
| 				            </div> | 				            </div> | ||||||
| 				            <div class="tab-pane" id="orders-v"> |  | ||||||
| 								<div class="row"> |  | ||||||
| 								  <div class="col-md-12"> |  | ||||||
| 									<table class="table borderless table-hover">  |  | ||||||
| 										<h3>Orders</h3>  |  | ||||||
| 										<br/> |  | ||||||
| 										<thead>  |  | ||||||
| 										<tr>  |  | ||||||
| 											<th>#</th> |  | ||||||
| 											<th>{% trans "Date"%}</th> |  | ||||||
| 											<th>{% trans "Amount"%}</th> |  | ||||||
| 											<th>{% trans "Status"%}</th> |  | ||||||
| 											<th></th> |  | ||||||
| 										</tr>  |  | ||||||
| 										</thead>  |  | ||||||
| 										<tbody>  |  | ||||||
| 											{% for order in virtual_machine.hosting_orders.all %} |  | ||||||
| 											<tr>  |  | ||||||
| 												<td scope="row">{{order.id}}</td>  |  | ||||||
| 												<td>{{order.created_at}}</td>  |  | ||||||
| 												<td>{{order.vm_plan.price}} CHF</td>  |  | ||||||
| 												<td>{% if order.approved %} |  | ||||||
| 														<span class="text-success strong">{% trans "Approved"%}</span> |  | ||||||
| 													{% else%}  |  | ||||||
| 														<span class="text-danger strong">{% trans "Declined"%}</span> |  | ||||||
| 													{% endif%} |  | ||||||
| 												</td>  |  | ||||||
| 												<td> |  | ||||||
| 													<button type="button" class="btn btn-default"><a href="{% url 'hosting:orders' order.id %}">{% trans "View Detail"%}</a></button> |  | ||||||
| 												</td> |  | ||||||
| 											</tr> |  | ||||||
| 											{% endfor %} |  | ||||||
| 										</tbody>  |  | ||||||
| 									</table> |  | ||||||
| 								  </div><!--/col-12--> |  | ||||||
| 								</div><!--/row--> |  | ||||||
| 				            </div> |  | ||||||
| 				            <div class="tab-pane" id="status-v"> | 				            <div class="tab-pane" id="status-v"> | ||||||
| 				            	<div class="row "> | 				            	<div class="row "> | ||||||
| 									<div class="col-md-12 inline-headers"> | 									<div class="col-md-12 inline-headers"> | ||||||
| 										<h3>{% trans "Current status"%}</h3> | 										<h3>{% trans "Current status"%}</h3> | ||||||
| 										<div  class="pull-right space-above"> | 										<div  class="pull-right space-above"> | ||||||
| 											{% if virtual_machine.status == 'pending' %} | 											{% if virtual_machine.state == 'PENDING' %} | ||||||
| 												<span class="label label-warning"><strong>{{virtual_machine.get_status_display}}</strong></span> | 												<span class="label | ||||||
| 											{% elif  virtual_machine.status == 'online' %} |                                                     label-warning"><strong>Pending</strong></span> | ||||||
| 												<span class="label label-success"><strong>{{virtual_machine.get_status_display}}</strong></span> | 											{% elif  virtual_machine.state == 'ACTIVE' %} | ||||||
| 											{% elif  virtual_machine.status == 'canceled'%} | 												<span class="label | ||||||
| 												<span class="label label-danger"><strong>{{virtual_machine.get_status_display}}</strong></span> |                                                     label-success"><strong>Online</strong></span> | ||||||
|  | 											{% elif  virtual_machine.state == 'FAILED'%} | ||||||
|  | 												<span class="label | ||||||
|  |                                                     label-danger"><strong>Failed</strong></span> | ||||||
| 											{% endif %} | 											{% endif %} | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
|  | @ -165,11 +125,13 @@ | ||||||
| 				            	<div class="row"> | 				            	<div class="row"> | ||||||
| 									<div class="col-md-12 space-above-big"> | 									<div class="col-md-12 space-above-big"> | ||||||
| 										<div class="pull-right"> | 										<div class="pull-right"> | ||||||
| 											<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.id %}"> | 											<form method="POST" | ||||||
|  |                  id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}"> | ||||||
| 											{% csrf_token %}  | 											{% csrf_token %}  | ||||||
| 											</form>	 | 											</form>	 | ||||||
| 												 | 												 | ||||||
| 												<button type="text" data-href="{% url 'hosting:virtual_machines' virtual_machine.id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-danger">{% trans "Cancel Virtual Machine"%}</button> | 												<button type="text" | ||||||
|  |                     data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-danger">{% trans "Cancel Virtual Machine"%}</button> | ||||||
| 																						 | 																						 | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
|  | @ -181,7 +143,7 @@ | ||||||
| 									                {% trans "Cancel your Virtual Machine"%} | 									                {% trans "Cancel your Virtual Machine"%} | ||||||
| 									            </div> | 									            </div> | ||||||
| 									            <div class="modal-body"> | 									            <div class="modal-body"> | ||||||
| 									                {% trans "Are you sure do you want to cancel your Virtual Machine "%} {{vm.virtual_machine}}  {% trans "plan?"%} | 									                {% trans "Are you sure do you want to cancel your Virtual Machine "%} {{virtual_machine.name}}  {% trans "plan?"%} | ||||||
| 									            </div> | 									            </div> | ||||||
| 									            <div class="modal-footer"> | 									            <div class="modal-footer"> | ||||||
| 									                <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel"%}</button> | 									                <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel"%}</button> | ||||||
|  | @ -207,12 +169,3 @@ | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| {%endblock%} | {%endblock%} | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
|  | @ -20,9 +20,9 @@ | ||||||
| 				</tr> | 				</tr> | ||||||
| 				</thead> | 				</thead> | ||||||
| 				<tbody>  | 				<tbody>  | ||||||
| 					{% for vm in vms_opennebula %} | 					{% for vm in vms %} | ||||||
| 					<tr> | 					<tr> | ||||||
| 						<td scope="row">{{vm.deploy_id}}</td>  | 						<td scope="row">{{vm.vm_id}}</td>  | ||||||
| 						<td>{{vm.price}} CHF</td>  | 						<td>{{vm.price}} CHF</td>  | ||||||
| 						<td> | 						<td> | ||||||
|                             |                             | ||||||
|  | @ -36,7 +36,8 @@ | ||||||
| 
 | 
 | ||||||
| 						</td>  | 						</td>  | ||||||
| 						<td> | 						<td> | ||||||
|                             <button type="button" class="btn btn-default"><a href="{% url 'hosting:virtual_machines' vm.id %}">{% trans "View Detail"%}</a></button> |                             <button type="button" class="btn btn-default"><a | ||||||
|  |                                     href="{% url 'hosting:virtual_machines' vm.vm_id %}">{% trans "View Detail"%}</a></button> | ||||||
|                         </td> |                         </td> | ||||||
| 					</tr> | 					</tr> | ||||||
| 					{% endfor %} | 					{% endfor %} | ||||||
|  |  | ||||||
							
								
								
									
										201
									
								
								hosting/views.py
									
										
									
									
									
								
							
							
						
						
									
										201
									
								
								hosting/views.py
									
										
									
									
									
								
							|  | @ -19,16 +19,18 @@ from stored_messages.models import Message | ||||||
| from stored_messages.api import mark_read | from stored_messages.api import mark_read | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| from membership.models import CustomUser, StripeCustomer | from membership.models import CustomUser, StripeCustomer | ||||||
| from utils.stripe_utils import StripeUtils | from utils.stripe_utils import StripeUtils | ||||||
| from utils.forms import BillingAddressForm, PasswordResetRequestForm | from utils.forms import BillingAddressForm, PasswordResetRequestForm | ||||||
| from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin | from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin | ||||||
| from utils.mailer import BaseEmail | from utils.mailer import BaseEmail | ||||||
| from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill, UserHostingKey | from .models import HostingOrder, HostingBill, UserHostingKey | ||||||
| from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm | from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm | ||||||
| from .mixins import ProcessVMSelectionMixin | from .mixins import ProcessVMSelectionMixin | ||||||
| from .opennebula_functions import HostingManageVMAdmin, OpenNebulaManager | 
 | ||||||
|  | from opennebula_api.models import OpenNebulaManager | ||||||
|  | from opennebula_api.serializers import VirtualMachineSerializer,\ | ||||||
|  |                                        VirtualMachineTemplateSerializer | ||||||
| 
 | 
 | ||||||
| from oca.exceptions import OpenNebulaException | from oca.exceptions import OpenNebulaException | ||||||
| from oca.pool import WrongNameError | from oca.pool import WrongNameError | ||||||
|  | @ -282,48 +284,26 @@ class PaymentVMView(LoginRequiredMixin, FormView): | ||||||
| 
 | 
 | ||||||
|         if form.is_valid(): |         if form.is_valid(): | ||||||
|             context = self.get_context_data() |             context = self.get_context_data() | ||||||
|             specifications = request.session.get('vm_specs') |             specifications = request.session.get('template') | ||||||
| 
 | 
 | ||||||
|             vm_template = specifications.get('vm_template', 1) |             vm_template_id = specifications.get('id', 1) | ||||||
|              |              | ||||||
|             vm_type = VirtualMachineType.objects.get(id=vm_template) |             final_price = specifications.get('price', 1) | ||||||
| 
 |  | ||||||
|             specs = vm_type.get_specs() |  | ||||||
| 
 |  | ||||||
|             final_price = vm_type.calculate_price() |  | ||||||
| 
 |  | ||||||
|             plan_data = { |  | ||||||
|                 'vm_type': vm_type, |  | ||||||
|                 'configuration': specifications.get( |  | ||||||
|                     'configuration', |  | ||||||
|                     'django' |  | ||||||
|                 ), |  | ||||||
|                 'price': final_price |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             plan_data.update(specs) |  | ||||||
| 
 | 
 | ||||||
|             token = form.cleaned_data.get('token') |             token = form.cleaned_data.get('token') | ||||||
| 
 | 
 | ||||||
|  |             owner = self.request.user | ||||||
|  | 
 | ||||||
|             # Get or create stripe customer |             # Get or create stripe customer | ||||||
|             customer = StripeCustomer.get_or_create(email=self.request.user.email, |             customer = StripeCustomer.get_or_create(email=owner.email, | ||||||
|                                                     token=token) |                                                     token=token) | ||||||
|             if not customer: |             if not customer: | ||||||
|                 form.add_error("__all__", "Invalid credit card") |                 form.add_error("__all__", "Invalid credit card") | ||||||
|                 return self.render_to_response(self.get_context_data(form=form)) |                 return self.render_to_response(self.get_context_data(form=form)) | ||||||
| 
 | 
 | ||||||
|             # Create Virtual Machine Plan |  | ||||||
|             plan = VirtualMachinePlan.create(plan_data, request.user) |  | ||||||
| 
 |  | ||||||
|             # Create Billing Address |             # Create Billing Address | ||||||
|             billing_address = form.save() |             billing_address = form.save() | ||||||
| 
 | 
 | ||||||
|             # Create a Hosting Order |  | ||||||
|             order = HostingOrder.create(vm_plan=plan, customer=customer, |  | ||||||
|                                         billing_address=billing_address) |  | ||||||
|             # Create a Hosting Bill |  | ||||||
|             bill = HostingBill.create(customer=customer, billing_address=billing_address) |  | ||||||
| 
 |  | ||||||
|             # Make stripe charge to a customer |             # Make stripe charge to a customer | ||||||
|             stripe_utils = StripeUtils() |             stripe_utils = StripeUtils() | ||||||
|             charge_response = stripe_utils.make_charge(amount=final_price, |             charge_response = stripe_utils.make_charge(amount=final_price, | ||||||
|  | @ -340,24 +320,34 @@ class PaymentVMView(LoginRequiredMixin, FormView): | ||||||
| 
 | 
 | ||||||
|             charge = charge_response.get('response_object') |             charge = charge_response.get('response_object') | ||||||
| 
 | 
 | ||||||
|  |             # Create OpenNebulaManager  | ||||||
|  |              | ||||||
|  |             manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                         password=owner.password[0:20], | ||||||
|  |                                         create_user=True) | ||||||
|  |             template = manager.get_template(vm_template_id) | ||||||
|  | 
 | ||||||
|  |             # Create a vm using logged user | ||||||
|  |             vm_id = manager.create_vm(vm_template_id) | ||||||
|  |             # Create a Hosting Order | ||||||
|  |             order = HostingOrder.create(vm_id=vm_id, customer=customer, | ||||||
|  |                                         billing_address=billing_address) | ||||||
|  |             # Create a Hosting Bill | ||||||
|  |             bill = HostingBill.create(customer=customer, billing_address=billing_address) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|             # Associate an order with a stripe payment |             # Associate an order with a stripe payment | ||||||
|             order.set_stripe_charge(charge) |             order.set_stripe_charge(charge) | ||||||
| 
 | 
 | ||||||
|             # If the Stripe payment was successed, set order status approved |             # If the Stripe payment was successed, set order status approved | ||||||
|             order.set_approved() |             order.set_approved() | ||||||
| 
 | 
 | ||||||
|             # Create a vm using logged user |             vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data | ||||||
|             oppennebula_vm_id = VirtualMachinePlan.create_opennebula_vm( |  | ||||||
|                 self.request.user, |  | ||||||
|                 specs |  | ||||||
|             ) |  | ||||||
| 
 | 
 | ||||||
|             plan.oppenebula_id = oppennebula_vm_id |  | ||||||
|             plan.save() |  | ||||||
| 
 | 
 | ||||||
|             # Send notification to ungleich as soon as VM has been booked |             # Send notification to ungleich as soon as VM has been booked | ||||||
|             context = { |             context = { | ||||||
|                 'vm': plan, |                 'vm': vm, | ||||||
|                 'order': order, |                 'order': order, | ||||||
|                 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) |                 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) | ||||||
| 
 | 
 | ||||||
|  | @ -384,6 +374,18 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai | ||||||
|     permission_required = ['view_hostingorder'] |     permission_required = ['view_hostingorder'] | ||||||
|     model = HostingOrder |     model = HostingOrder | ||||||
| 
 | 
 | ||||||
|  |     def get_context_data(self, **kwargs): | ||||||
|  |         # Get context | ||||||
|  |         context = super(DetailView, self).get_context_data(**kwargs) | ||||||
|  |         obj = self.get_object() | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|  |         vm = manager.get_vm(obj.vm_id) | ||||||
|  |         context['vm'] = VirtualMachineSerializer(vm).data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class OrdersHostingListView(LoginRequiredMixin, ListView): | class OrdersHostingListView(LoginRequiredMixin, ListView): | ||||||
|     template_name = "hosting/orders.html" |     template_name = "hosting/orders.html" | ||||||
|     login_url = reverse_lazy('hosting:login') |     login_url = reverse_lazy('hosting:login') | ||||||
|  | @ -408,24 +410,17 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): | ||||||
|     template_name = "hosting/virtual_machines.html" |     template_name = "hosting/virtual_machines.html" | ||||||
|     login_url = reverse_lazy('hosting:login') |     login_url = reverse_lazy('hosting:login') | ||||||
|     context_object_name = "vms" |     context_object_name = "vms" | ||||||
|     model = VirtualMachinePlan |  | ||||||
|     paginate_by = 10 |     paginate_by = 10 | ||||||
|     ordering = '-id' |     ordering = '-id' | ||||||
| 
 | 
 | ||||||
|     def get_context_data(self, **kwargs): |  | ||||||
|         context = super(VirtualMachinesPlanListView, self).get_context_data(**kwargs) |  | ||||||
|         context.update({ |  | ||||||
|             'vms_opennebula': VirtualMachinePlan.get_vms(self.request.user) |  | ||||||
|         }) |  | ||||||
|         return context |  | ||||||
| 
 |  | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|         # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) |         owner = self.request.user | ||||||
|         # print(hosting_admin.show_vms_view(self.request)) |         manager = OpenNebulaManager(email=owner.email, | ||||||
|         # print(VirtualMachinePlan.get_vms(self.request.user.)) |                                     password=owner.password[0:20], | ||||||
|         user = self.request.user |                                     create_user=True) | ||||||
|         self.queryset = VirtualMachinePlan.objects.active(user) |         queryset = manager.get_vms() | ||||||
|         return super(VirtualMachinesPlanListView, self).get_queryset() |         serializer = VirtualMachineSerializer(queryset, many=True) | ||||||
|  |         return serializer.data | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class CreateVirtualMachinesView(LoginRequiredMixin, View): | class CreateVirtualMachinesView(LoginRequiredMixin, View): | ||||||
|  | @ -433,92 +428,56 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): | ||||||
|     login_url = reverse_lazy('hosting:login') |     login_url = reverse_lazy('hosting:login') | ||||||
| 
 | 
 | ||||||
|     def get(self, request, *args, **kwargs): |     def get(self, request, *args, **kwargs): | ||||||
|  |         #TODO: Replace with OpenNebulaManager.get_apps | ||||||
|  |         templates = OpenNebulaManager().get_templates() | ||||||
|  |         data = VirtualMachineTemplateSerializer(templates, many=True).data | ||||||
|         context = { |         context = { | ||||||
|             'vm_types': VirtualMachineType.get_serialized_vm_types(), |             'templates': data, | ||||||
|             'configuration_options': VirtualMachinePlan.VM_CONFIGURATION |             #'configuration_options': VirtualMachinePlan.VM_CONFIGURATION | ||||||
|         } |         } | ||||||
|         # context = {} |         # context = {} | ||||||
|         return render(request, self.template_name, context) |         return render(request, self.template_name, context) | ||||||
| 
 | 
 | ||||||
|     def post(self, request): |     def post(self, request): | ||||||
|         configuration = request.POST.get('configuration') |         #XXX: Fix this! | ||||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) |         #configuration = request.POST.get('configuration') | ||||||
|         vm_template = request.POST.get('vm_template') |         #configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) |         template_id = int(request.POST.get('vm_template_id')) | ||||||
|         vm_specs = vm_type.get_specs() |         template = OpenNebulaManager().get_template(template_id) | ||||||
|         vm_specs.update({ |         data = VirtualMachineTemplateSerializer(template).data | ||||||
|             'configuration_display': configuration_display, |         vm_specs = { | ||||||
|             'configuration': configuration, |             #'configuration_display': configuration_display, | ||||||
|             'final_price': vm_type.final_price, |             #'configuration': configuration, | ||||||
|             'vm_template': vm_template |             'template': data,  | ||||||
|         }) |         } | ||||||
|         request.session['vm_specs'] = vm_specs |         request.session['template'] = data  | ||||||
|         return redirect(reverse('hosting:payment')) |         return redirect(reverse('hosting:payment')) | ||||||
| 
 | 
 | ||||||
|     # def get_queryset(self): |  | ||||||
|     #     # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) |  | ||||||
|     #     # print(hosting_admin.show_vms(self.request)) |  | ||||||
|     #     user = self.request.user |  | ||||||
|     #     self.queryset = VirtualMachinePlan.objects.active(user) |  | ||||||
|     #     return super(VirtualMachinesPlanListView, self).get_queryset() |  | ||||||
| 
 | 
 | ||||||
| 
 | class VirtualMachineView(LoginRequiredMixin, View): | ||||||
| class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, View): |  | ||||||
|     template_name = "hosting/virtual_machine_detail.html" |     template_name = "hosting/virtual_machine_detail.html" | ||||||
|     login_url = reverse_lazy('hosting:login') |     login_url = reverse_lazy('hosting:login') | ||||||
|     # model = VirtualMachinePlan |  | ||||||
|     # context_object_name = "virtual_machine" |  | ||||||
|     permission_required = ['view_virtualmachineplan', 'cancel_virtualmachineplan'] |  | ||||||
|     # fields = '__all__' |  | ||||||
| 
 |  | ||||||
|     # def get_context_data(self, **kwargs): |  | ||||||
|     #     vm_plan = get_object() |  | ||||||
|     #     context = super(VirtualMachineView, self).get_context_data(**kwargs) |  | ||||||
|     #     context.update({ |  | ||||||
|     #         'opennebula_vm': VirtualMachinePlan.get_vm( |  | ||||||
|     #             self.request.user.email, |  | ||||||
|     #             opennebula_id |  | ||||||
|     #         ) |  | ||||||
|     #     }) |  | ||||||
|     #     return context |  | ||||||
| 
 |  | ||||||
|     # def get_object(self, queryset=None): |  | ||||||
|     #     # if queryset is None: |  | ||||||
|     #     #     queryset = self.get_queryset() |  | ||||||
|     #     # Next, try looking up by primary key. |  | ||||||
|     #     vm_id = self.kwargs.get(self.pk_url_kwarg) |  | ||||||
|     #     try: |  | ||||||
|     #         return VirtualMachinePlan.get_vm( |  | ||||||
|     #             self.request.user.email, |  | ||||||
|     #             vm_id |  | ||||||
|     #         ) |  | ||||||
|     #     except Exception as error: |  | ||||||
|     #         raise Http404() |  | ||||||
| 
 |  | ||||||
|     # def get_success_url(self): |  | ||||||
|     #     vm = self.get_object() |  | ||||||
|     #     final_url = "%s%s" % (reverse('hosting:virtual_machines', kwargs={'pk': vm.id}), |  | ||||||
|     #                           '#status-v') |  | ||||||
|     #     return final_url |  | ||||||
| 
 | 
 | ||||||
|     def get(self, request, *args, **kwargs): |     def get(self, request, *args, **kwargs): | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|         vm_id = self.kwargs.get('pk') |         vm_id = self.kwargs.get('pk') | ||||||
|         try: |         try: | ||||||
|             opennebula_vm = VirtualMachinePlan.get_vm( |             vm = manager.get_vm(vm_id) | ||||||
|                 self.request.user, |             serializer = VirtualMachineSerializer(vm) | ||||||
|                 vm_id |  | ||||||
|             ) |  | ||||||
|         except Exception as error: |         except Exception as error: | ||||||
|             print(error) |             print(error) | ||||||
|             raise Http404() |             raise Http404() | ||||||
| 
 | 
 | ||||||
|         context = { |         context = { | ||||||
|             'virtual_machine': opennebula_vm, |             'virtual_machine': serializer.data, | ||||||
|         } |         } | ||||||
|         # context = {} |  | ||||||
|         return render(request, self.template_name, context) |         return render(request, self.template_name, context) | ||||||
| 
 | 
 | ||||||
|     def post(self, *args, **kwargs): |     def post(self, *args, **kwargs): | ||||||
|  |         #TODO: add api to OpenNebulaManager | ||||||
|         vm = self.get_object() |         vm = self.get_object() | ||||||
|         vm.cancel_plan() |         vm.cancel_plan() | ||||||
| 
 | 
 | ||||||
|  | @ -564,10 +523,14 @@ class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailV | ||||||
|     def get_context_data(self, **kwargs): |     def get_context_data(self, **kwargs): | ||||||
|         # Get context |         # Get context | ||||||
|         context = super(DetailView, self).get_context_data(**kwargs) |         context = super(DetailView, self).get_context_data(**kwargs) | ||||||
|  | 
 | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|         # Get vms |         # Get vms | ||||||
|         try: |         queryset = manager.get_vms() | ||||||
|             context['vms'] = self.get_object().get_vms() |         vms = VirtualMachineSerializer(queryset, many=True).data | ||||||
|         except: |         context['vms'] = vms | ||||||
|             pass |  | ||||||
| 
 | 
 | ||||||
|         return context |         return context | ||||||
|  |  | ||||||
|  | @ -1,40 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # Generated by Django 1.9.4 on 2017-05-09 14:36 |  | ||||||
| from __future__ import unicode_literals |  | ||||||
| 
 |  | ||||||
| from django.db import migrations, models |  | ||||||
| import django.db.models.deletion |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
| 
 |  | ||||||
|     initial = True |  | ||||||
| 
 |  | ||||||
|     dependencies = [ |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     operations = [ |  | ||||||
|         migrations.CreateModel( |  | ||||||
|             name='VirtualMachine', |  | ||||||
|             fields=[ |  | ||||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |  | ||||||
|                 ('opennebula_id', models.IntegerField()), |  | ||||||
|             ], |  | ||||||
|         ), |  | ||||||
|         migrations.CreateModel( |  | ||||||
|             name='VirtualMachineTemplate', |  | ||||||
|             fields=[ |  | ||||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |  | ||||||
|                 ('opennebula_id', models.IntegerField()), |  | ||||||
|                 ('base_price', models.FloatField()), |  | ||||||
|                 ('memory_price', models.FloatField()), |  | ||||||
|                 ('core_price', models.FloatField()), |  | ||||||
|                 ('disk_size_price', models.FloatField()), |  | ||||||
|             ], |  | ||||||
|         ), |  | ||||||
|         migrations.AddField( |  | ||||||
|             model_name='virtualmachine', |  | ||||||
|             name='template', |  | ||||||
|             field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='opennebula_api.VirtualMachineTemplate'), |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # Generated by Django 1.9.4 on 2017-05-11 02:46 |  | ||||||
| from __future__ import unicode_literals |  | ||||||
| 
 |  | ||||||
| from django.db import migrations |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Migration(migrations.Migration): |  | ||||||
| 
 |  | ||||||
|     dependencies = [ |  | ||||||
|         ('opennebula_api', '0001_initial'), |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     operations = [ |  | ||||||
|         migrations.RenameField( |  | ||||||
|             model_name='virtualmachine', |  | ||||||
|             old_name='template', |  | ||||||
|             new_name='vm_template', |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,24 +0,0 @@ | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # Generated by Django 1.9.4 on 2017-05-11 09:06 |  | ||||||
| from __future__ import unicode_literals |  | ||||||
| 
 |  | ||||||
| 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), |  | ||||||
|         ('opennebula_api', '0002_auto_20170511_0246'), |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
|     operations = [ |  | ||||||
|         migrations.AddField( |  | ||||||
|             model_name='virtualmachine', |  | ||||||
|             name='owner', |  | ||||||
|             field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='virtual_machines', to=settings.AUTH_USER_MODEL), |  | ||||||
|             preserve_default=False, |  | ||||||
|         ), |  | ||||||
|     ] |  | ||||||
|  | @ -1,124 +1,11 @@ | ||||||
| import oca | import oca | ||||||
| import socket | import socket | ||||||
| 
 | 
 | ||||||
| from django.db import models |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.utils.functional import cached_property | from django.utils.functional import cached_property | ||||||
| 
 | 
 | ||||||
| from membership.models import CustomUser |  | ||||||
| 
 |  | ||||||
| from oca.pool import WrongNameError | from oca.pool import WrongNameError | ||||||
| 
 | 
 | ||||||
| class VirtualMachineTemplate(models.Model): |  | ||||||
|     """This class represents an opennebula template.""" |  | ||||||
|     opennebula_id = models.IntegerField() |  | ||||||
|     base_price = models.FloatField() |  | ||||||
|     memory_price = models.FloatField() |  | ||||||
|     core_price = models.FloatField() |  | ||||||
|     disk_size_price = models.FloatField() |  | ||||||
| 
 |  | ||||||
|     @cached_property |  | ||||||
|     def template(self): |  | ||||||
|         template = OpenNebulaManager()._get_template(self.opennebula_id) |  | ||||||
|         return template |  | ||||||
| 
 |  | ||||||
|     def calculate_price(self): |  | ||||||
|         template = self.template.template |  | ||||||
| 
 |  | ||||||
|         price = int(template.vcpu) * self.core_price |  | ||||||
|         price += int(template.memory) / 1024 * self.memory_price |  | ||||||
|         try: |  | ||||||
|             price += int(template.disk.size) / 1024 * self.disk_size_price |  | ||||||
|         except AttributeError: |  | ||||||
|             for disk in template.disks: |  | ||||||
|                 price += int(disk.size) / 1024 * self.disk_size_price |  | ||||||
|         return price |  | ||||||
| 
 |  | ||||||
|     def get_name(self): |  | ||||||
| 
 |  | ||||||
|         template = self.template |  | ||||||
|         return template.name |  | ||||||
| 
 |  | ||||||
|     def get_cores(self): |  | ||||||
| 
 |  | ||||||
|         template = self.template.template |  | ||||||
|         return int(template.vcpu) |  | ||||||
|      |  | ||||||
|     def get_disk_size(self): |  | ||||||
| 
 |  | ||||||
|         template = self.template.template |  | ||||||
|         disk_size = 0 |  | ||||||
|         for disk in template.disks: |  | ||||||
|             disk_size += int(disk.size) |  | ||||||
|         return disk_size / 1024 |  | ||||||
| 
 |  | ||||||
|     def get_memory(self): |  | ||||||
| 
 |  | ||||||
|         template = self.template.template |  | ||||||
|         return int(template.memory) / 1024 |  | ||||||
| 
 |  | ||||||
| class VirtualMachine(models.Model): |  | ||||||
|     """This class represents an opennebula virtual machine.""" |  | ||||||
|     opennebula_id = models.IntegerField() |  | ||||||
|     owner = models.ForeignKey(CustomUser, related_name='virtual_machines') |  | ||||||
|     vm_template = models.ForeignKey(VirtualMachineTemplate) |  | ||||||
| 
 |  | ||||||
|     VM_STATE = { |  | ||||||
|         '0': 'INIT', |  | ||||||
|         '1': 'PENDING', |  | ||||||
|         '2': 'HOLD', |  | ||||||
|         '3': 'ACTIVE', |  | ||||||
|         '4': 'STOPPED', |  | ||||||
|         '5': 'SUSPENDED', |  | ||||||
|         '6': 'DONE', |  | ||||||
|         '8': 'POWEROFF', |  | ||||||
|         '9': 'UNDEPLOYED', |  | ||||||
|         '10': 'CLONING', |  | ||||||
|         '11': 'CLONING_FAILURE', |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @cached_property |  | ||||||
|     def vm(self): |  | ||||||
|         manager = OpenNebulaManager(email=self.owner.email, |  | ||||||
|                 password=self.owner.password[0:20],  |  | ||||||
|                                     create_user=True) |  | ||||||
|         vm = manager._get_vm(self.opennebula_id) |  | ||||||
|         return vm |  | ||||||
| 
 |  | ||||||
|     def get_name(self): |  | ||||||
|              |  | ||||||
|         return self.vm.name |  | ||||||
| 
 |  | ||||||
|     def get_cores(self): |  | ||||||
| 
 |  | ||||||
|         return self.vm_template.get_cores() |  | ||||||
|      |  | ||||||
|     def get_disk_size(self): |  | ||||||
|          |  | ||||||
|         return self.vm_template.get_disk_size() |  | ||||||
| 
 |  | ||||||
|     def get_memory(self): |  | ||||||
| 
 |  | ||||||
|         return self.vm_template.get_memory() |  | ||||||
| 
 |  | ||||||
|     def get_id(self): |  | ||||||
| 
 |  | ||||||
|         return self.vm.id |  | ||||||
| 
 |  | ||||||
|     def get_ip(self): |  | ||||||
| 
 |  | ||||||
|         try: |  | ||||||
|             return self.vm.user_template.ungleich_public_ip |  | ||||||
|         except AttributeError: |  | ||||||
|             return '-' |  | ||||||
| 
 |  | ||||||
|     def get_state(self): |  | ||||||
| 
 |  | ||||||
|         return self.VM_STATE.get(str(self.vm.state)) |  | ||||||
| 
 |  | ||||||
|     def get_price(self): |  | ||||||
|         return self.vm_template.calculate_price() |  | ||||||
| 
 |  | ||||||
| class OpenNebulaManager(): | class OpenNebulaManager(): | ||||||
|     """This class represents an opennebula manager.""" |     """This class represents an opennebula manager.""" | ||||||
| 
 | 
 | ||||||
|  | @ -204,12 +91,15 @@ class OpenNebulaManager(): | ||||||
|             raise ConnectionRefusedError |             raise ConnectionRefusedError | ||||||
|         return vm_pool |         return vm_pool | ||||||
| 
 | 
 | ||||||
|  |     def get_vms(self): | ||||||
|  |         return self._get_vm_pool() | ||||||
|     |     | ||||||
|     def _get_vm(self, vm_id): |     def get_vm(self, vm_id): | ||||||
|         vm_pool = self._get_vm_pool() |         vm_pool = self._get_vm_pool() | ||||||
|         return vm_pool.get_by_id(vm_id) |         return vm_pool.get_by_id(int(vm_id)) | ||||||
| 
 | 
 | ||||||
|     def create_vm(self, template_id): |     #TODO: get app with id  | ||||||
|  |     def create_vm(self, template_id, app_id=None): | ||||||
|         vm_id = self.oneadmin_client.call( |         vm_id = self.oneadmin_client.call( | ||||||
|                     oca.VmTemplate.METHODS['instantiate'], |                     oca.VmTemplate.METHODS['instantiate'], | ||||||
|                     template_id, |                     template_id, | ||||||
|  | @ -232,7 +122,7 @@ class OpenNebulaManager(): | ||||||
|         self.oneadmin_client.call( |         self.oneadmin_client.call( | ||||||
|                 oca.VirtualMachine.METHODS['action'],  |                 oca.VirtualMachine.METHODS['action'],  | ||||||
|                 'terminate', |                 'terminate', | ||||||
|                 vm_id |                 int(vm_id) | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
|     def _get_template_pool(self): |     def _get_template_pool(self): | ||||||
|  | @ -248,19 +138,31 @@ class OpenNebulaManager(): | ||||||
|             raise ConnectionRefusedError |             raise ConnectionRefusedError | ||||||
|         return template_pool |         return template_pool | ||||||
| 
 | 
 | ||||||
|     def _get_template(self, template_id): |     def get_templates(self): | ||||||
|  |         public_templates = [ | ||||||
|  |                 template  | ||||||
|  |                 for template in self._get_template_pool() | ||||||
|  |                 if 'public-' in template.name  | ||||||
|  |                 ] | ||||||
|  |         return public_templates  | ||||||
|  |     def get_template(self, template_id): | ||||||
|         template_pool = self._get_template_pool() |         template_pool = self._get_template_pool() | ||||||
|         return template_pool.get_by_id(template_id) |         return template_pool.get_by_id(template_id) | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|      |      | ||||||
|     def create_template(self, name, cores, memory, disk_size): |     def create_template(self, name, cores, memory, disk_size, core_price, memory_price, | ||||||
|  |                         disk_size_price, ssh='' ): | ||||||
|         """Create and add a new template to opennebula. |         """Create and add a new template to opennebula. | ||||||
|         :param name:      A string representation describing the template. |         :param name:      A string representation describing the template. | ||||||
|                           Used as label in view. |                           Used as label in view. | ||||||
|         :param cores:     Amount of virtual cpu cores for the VM. |         :param cores:     Amount of virtual cpu cores for the VM. | ||||||
|         :param memory:    Amount of RAM for the VM (MB) |         :param memory:  Amount of RAM for the VM (GB) | ||||||
|         :param disk_size: Amount of disk space for VM (MB) |         :param disk_size:    Amount of disk space for VM (GB) | ||||||
|  |         :param core_price:     Price of virtual cpu for the VM per core. | ||||||
|  |         :param memory_price:  Price of RAM for the VM per GB | ||||||
|  |         :param disk_size_price:    Price of disk space for VM per GB | ||||||
|  |         :param ssh: User public ssh key | ||||||
|         """ |         """ | ||||||
|         template_string_formatter = """<TEMPLATE> |         template_string_formatter = """<TEMPLATE> | ||||||
|                                         <NAME>{name}</NAME> |                                         <NAME>{name}</NAME> | ||||||
|  | @ -272,6 +174,10 @@ class OpenNebulaManager(): | ||||||
|                                          <SIZE>{size}</SIZE> |                                          <SIZE>{size}</SIZE> | ||||||
|                                          <DEV_PREFIX>vd</DEV_PREFIX> |                                          <DEV_PREFIX>vd</DEV_PREFIX> | ||||||
|                                         </DISK> |                                         </DISK> | ||||||
|  |                                         <CPU_COST>{cpu_cost}</CPU_COST> | ||||||
|  |                                         <MEMORY_COST>{memory_cost}</MEMORY_COST> | ||||||
|  |                                         <DISK_COST>{disk_cost}</DISK_COST> | ||||||
|  |                                         <SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY> | ||||||
|                                        </TEMPLATE> |                                        </TEMPLATE> | ||||||
|                                        """ |                                        """ | ||||||
|         template_id = oca.VmTemplate.allocate( |         template_id = oca.VmTemplate.allocate( | ||||||
|  | @ -281,7 +187,12 @@ class OpenNebulaManager(): | ||||||
|                 vcpu=cores, |                 vcpu=cores, | ||||||
|                 cpu=0.1*cores, |                 cpu=0.1*cores, | ||||||
|                 size=1024 * disk_size, |                 size=1024 * disk_size, | ||||||
|                 memory=1024 * memory |                 memory=1024 * memory, | ||||||
|  |                 # * 10 because we set cpu to *0.1 | ||||||
|  |                 cpu_cost=10*core_price, | ||||||
|  |                 memory_cost=memory_price, | ||||||
|  |                 disk_cost=disk_size_price, | ||||||
|  |                 ssh=ssh | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| from rest_framework.permissions import BasePermission |  | ||||||
| 
 |  | ||||||
| from .models import VirtualMachine |  | ||||||
| 
 |  | ||||||
| class IsOwner(BasePermission): |  | ||||||
| 
 |  | ||||||
|     def has_object_permission(self, request, view, obj): |  | ||||||
|         if isinstance(obj, VirtualMachine): |  | ||||||
|             return obj.owner == request.user |  | ||||||
|         return obj.owner == request.user |  | ||||||
|  | @ -3,98 +3,117 @@ import oca | ||||||
| from rest_framework import serializers | from rest_framework import serializers | ||||||
| 
 | 
 | ||||||
| from oca import OpenNebulaException | from oca import OpenNebulaException | ||||||
|  | from oca.template import VmTemplate | ||||||
| 
 | 
 | ||||||
| from .models import VirtualMachine, VirtualMachineTemplate, OpenNebulaManager | from .models import OpenNebulaManager | ||||||
| 
 | 
 | ||||||
| class VirtualMachineTemplateSerializer(serializers.ModelSerializer): | class VirtualMachineTemplateSerializer(serializers.Serializer): | ||||||
|     """Serializer to map the virtual machine template instance into JSON format.""" |     """Serializer to map the virtual machine template instance into JSON format.""" | ||||||
|     cores       = serializers.IntegerField(source='get_cores')  |     id          = serializers.IntegerField(read_only=True) | ||||||
|     name        = serializers.CharField(source='get_name') |     name        = serializers.CharField() | ||||||
|     disk_size   = serializers.IntegerField(source='get_disk_size') |     cores       = serializers.IntegerField(source='template.vcpu')  | ||||||
|     memory      = serializers.IntegerField(source='get_memory') |     disk        = serializers.IntegerField(write_only=True) | ||||||
|  |     disk_size   = serializers.SerializerMethodField() | ||||||
|  |     memory      = serializers.SerializerMethodField() | ||||||
|  |     core_price  = serializers.FloatField(source='template.cpu_cost') | ||||||
|  |     disk_size_price  = serializers.FloatField(source='template.disk_cost') | ||||||
|  |     memory_price  = serializers.FloatField(source='template.memory_cost') | ||||||
|  |     price       = serializers.SerializerMethodField() | ||||||
| 
 | 
 | ||||||
|     class Meta: |     def create(self, validated_data): | ||||||
|         model = VirtualMachineTemplate |         data = validated_data | ||||||
|         fields = ('id', 'name', 'cores', 'memory', 'disk_size', 'base_price',  |         template = data.pop('template') | ||||||
|                   'core_price', 'memory_price', 'disk_size_price', 'opennebula_id') |  | ||||||
|         read_only_fields = ('opennebula_id', ) |  | ||||||
| 
 |  | ||||||
|     def validate(self, data): |  | ||||||
|         # Create the opennebula model |  | ||||||
|         cores   = data.pop('get_cores') |  | ||||||
|         name    = data.pop('get_name') |  | ||||||
|         disk_size = data.pop('get_disk_size') |  | ||||||
|         memory  = data.pop('get_memory') |  | ||||||
| 
 | 
 | ||||||
|  |         cores = template.pop('vcpu') | ||||||
|  |         name    = data.pop('name') | ||||||
|  |         disk_size = data.pop('disk')  | ||||||
|  |         memory  = template.pop('memory') | ||||||
|  |         core_price = template.pop('cpu_cost')  | ||||||
|  |         memory_price = template.pop('memory_cost')  | ||||||
|  |         disk_size_price = template.pop('disk_cost') | ||||||
|         manager = OpenNebulaManager(create_user = False) |         manager = OpenNebulaManager(create_user = False) | ||||||
|          |          | ||||||
|         try: |         try: | ||||||
|             opennebula_id = manager.create_template(name=name, cores=cores, |             opennebula_id = manager.create_template(name=name, cores=cores, | ||||||
|                                                     memory=memory, |                                                     memory=memory, | ||||||
|                                                     disk_size=disk_size) |                                                     disk_size=disk_size, | ||||||
|             data.update({'opennebula_id':opennebula_id}) |                                                     core_price=core_price, | ||||||
|  |                                                     disk_size_price=disk_size_price, | ||||||
|  |                                                     memory_price=memory_price) | ||||||
|         except OpenNebulaException as err: |         except OpenNebulaException as err: | ||||||
|             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) |             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) | ||||||
|          |          | ||||||
|         return data |         return manager.get_template(template_id=opennebula_id) | ||||||
| 
 | 
 | ||||||
|     def create(self, validated_data): |     def get_disk_size(self, obj): | ||||||
|         return VirtualMachineTemplate.objects.create(**validated_data) |         template = obj.template | ||||||
|  |         disk_size = 0 | ||||||
|  |         for disk in template.disks: | ||||||
|  |             disk_size += int(disk.size) | ||||||
|  |         return disk_size / 1024  | ||||||
| 
 | 
 | ||||||
| class TemplatePrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): |     def get_price(self, obj): | ||||||
|     def display_value(self, instance): |         template = obj.template | ||||||
|         return 'Template: {}'.format(instance.get_name()) |         price = float(template.cpu) * float(template.cpu_cost) | ||||||
|  |         price += (int(template.memory)/1024 * float(template.memory_cost)) | ||||||
|  |         for disk in template.disks: | ||||||
|  |             price += int(disk.size)/1024 * float(template.disk_cost) | ||||||
|  |         return price | ||||||
| 
 | 
 | ||||||
| class VirtualMachineSerializer(serializers.ModelSerializer): |     def get_memory(self, obj): | ||||||
|  |         return int(obj.template.memory)/1024 | ||||||
|  | 
 | ||||||
|  | class VirtualMachineSerializer(serializers.Serializer): | ||||||
|     """Serializer to map the virtual machine instance into JSON format.""" |     """Serializer to map the virtual machine instance into JSON format.""" | ||||||
| 
 | 
 | ||||||
|     #TODO: Maybe we can change to template.get_cores |     name        = serializers.CharField(read_only=True) | ||||||
|     cores       = serializers.IntegerField(read_only=True, source='get_cores')  |     cores       = serializers.IntegerField(read_only=True, source='template.vcpu')  | ||||||
|     name        = serializers.CharField(read_only=True, source='get_name') |  | ||||||
|     disk_size   = serializers.IntegerField(read_only=True, source='get_disk_size') |  | ||||||
|     memory      = serializers.IntegerField(read_only=True, source='get_memory') |  | ||||||
|     #TODO: See if we can change to IPAddressField |  | ||||||
|     ip          = serializers.CharField(read_only=True, source='get_ip') |  | ||||||
|     deploy_id   = serializers.IntegerField(read_only=True, source='get_deploy_id') |  | ||||||
|     vm_id          = serializers.IntegerField(read_only=True, source='get_vm_id') |  | ||||||
|     state       = serializers.CharField(read_only=True, source='get_state') |  | ||||||
|     price       = serializers.FloatField(read_only=True, source='get_price') |  | ||||||
| 
 | 
 | ||||||
|     owner = serializers.ReadOnlyField(source='owner.name') |     disk_size   = serializers.SerializerMethodField() | ||||||
|  |     memory      = serializers.SerializerMethodField() | ||||||
|  |     ip          = serializers.CharField(read_only=True, | ||||||
|  |                                         source='user_template.ungleich_public_ip', | ||||||
|  |                                         default='-') | ||||||
|  |     vm_id       = serializers.IntegerField(read_only=True, source='id') | ||||||
|  |     state       = serializers.CharField(read_only=True, source='str_state') | ||||||
|  |     price       = serializers.SerializerMethodField() | ||||||
| 
 | 
 | ||||||
|     vm_template = VirtualMachineTemplateSerializer(read_only=True) |     template_id = serializers.ChoiceField( | ||||||
| 
 |                 choices=[(key.id, key.name) for key in | ||||||
|     vm_template_id = TemplatePrimaryKeyRelatedField( |                     OpenNebulaManager().get_templates()], | ||||||
|                 queryset=VirtualMachineTemplate.objects.all(), |                 source='template.template_id', | ||||||
|                 source='vm_template' |                 write_only=True | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     class Meta: |     def create(self, validated_data): | ||||||
|         model = VirtualMachine |         owner = validated_data['owner'] | ||||||
|         fields = ('id', 'opennebula_id', 'vm_template', 'vm_template_id', 'cores', 'name', |         template_id = validated_data['template']['template_id'] | ||||||
|                 'disk_size', 'memory', 'ip', 'deploy_id', 'state', 'vm_id', |  | ||||||
|                 'price', 'owner') |  | ||||||
|         read_only_fields = ('opennebula_id', ) |  | ||||||
| 
 |  | ||||||
|     def validate(self, data): |  | ||||||
|         # Create the opennebula model |  | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             template_id = data['vm_template'].opennebula_id |  | ||||||
|             owner = self.context.get('request').user |  | ||||||
|             manager = OpenNebulaManager(email=owner.email, |             manager = OpenNebulaManager(email=owner.email, | ||||||
|                                         password=owner.password[0:20], |                                         password=owner.password[0:20], | ||||||
|                                         create_user = True) |                                         create_user = True) | ||||||
|             opennebula_id = manager.create_vm(template_id) |             opennebula_id = manager.create_vm(template_id) | ||||||
|             data.update({'opennebula_id':opennebula_id}) |  | ||||||
|         except OpenNebulaException as err: |         except OpenNebulaException as err: | ||||||
|             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) |             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) | ||||||
|          |          | ||||||
|         return data |         return manager.get_vm(opennebula_id) | ||||||
| 
 | 
 | ||||||
|     def create(self, validated_data): |     def get_memory(self, obj): | ||||||
|         return VirtualMachine.objects.create(**validated_data) |         return int(obj.template.memory)/1024 | ||||||
| 
 | 
 | ||||||
|     def update(self, instance, validated_data): |     def get_disk_size(self, obj): | ||||||
|         pass |         template = obj.template | ||||||
|  |         disk_size = 0 | ||||||
|  |         for disk in template.disks: | ||||||
|  |             disk_size += int(disk.size) | ||||||
|  |         return disk_size / 1024 | ||||||
|  | 
 | ||||||
|  |     def get_price(self, obj): | ||||||
|  |         template = obj.template | ||||||
|  |         price = float(template.cpu) * float(template.cpu_cost) | ||||||
|  |         price += (int(template.memory)/1024 * float(template.memory_cost)) | ||||||
|  |         for disk in template.disks: | ||||||
|  |             price += int(disk.size)/1024 * float(template.disk_cost) | ||||||
|  |         return price | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,17 +11,20 @@ from guardian.mixins import PermissionRequiredMixin | ||||||
| 
 | 
 | ||||||
| from .serializers import VirtualMachineTemplateSerializer, \ | from .serializers import VirtualMachineTemplateSerializer, \ | ||||||
|                          VirtualMachineSerializer |                          VirtualMachineSerializer | ||||||
| from .models import VirtualMachineTemplate, VirtualMachine, OpenNebulaManager | from .models import OpenNebulaManager | ||||||
| from .permissions import IsOwner |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TemplateCreateView(generics.ListCreateAPIView): | class TemplateCreateView(generics.ListCreateAPIView): | ||||||
|     """This class handles the GET and POST requests.""" |     """This class handles the GET and POST requests.""" | ||||||
| 
 | 
 | ||||||
|     queryset = VirtualMachineTemplate.objects.all() |  | ||||||
|     serializer_class = VirtualMachineTemplateSerializer |     serializer_class = VirtualMachineTemplateSerializer | ||||||
|     permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser) |     permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser) | ||||||
| 
 | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         manager = OpenNebulaManager() | ||||||
|  |         return manager.get_templates() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     def perform_create(self, serializer): |     def perform_create(self, serializer): | ||||||
|         """Save the post data when creating a new template.""" |         """Save the post data when creating a new template.""" | ||||||
|         serializer.save() |         serializer.save() | ||||||
|  | @ -29,15 +32,24 @@ class TemplateCreateView(generics.ListCreateAPIView): | ||||||
| class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView): | class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView): | ||||||
|     """This class handles the http GET, PUT and DELETE requests.""" |     """This class handles the http GET, PUT and DELETE requests.""" | ||||||
| 
 | 
 | ||||||
|     queryset = VirtualMachineTemplate.objects.all() |  | ||||||
|     serializer_class = VirtualMachineTemplateSerializer |     serializer_class = VirtualMachineTemplateSerializer | ||||||
|     permission_classes = (permissions.IsAuthenticated) |     permission_classes = (permissions.IsAuthenticated) | ||||||
| 
 | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         manager = OpenNebulaManager() | ||||||
|  |         return manager.get_templates() | ||||||
|  | 
 | ||||||
| class VmCreateView(generics.ListCreateAPIView): | class VmCreateView(generics.ListCreateAPIView): | ||||||
|     """This class handles the GET and POST requests.""" |     """This class handles the GET and POST requests.""" | ||||||
|     queryset = VirtualMachine.objects.all() |  | ||||||
|     serializer_class = VirtualMachineSerializer |     serializer_class = VirtualMachineSerializer | ||||||
|     permission_classes = (permissions.IsAuthenticated, IsOwner) |     permission_classes = (permissions.IsAuthenticated, ) | ||||||
|  | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|  |         return manager.get_vms() | ||||||
| 
 | 
 | ||||||
|     def perform_create(self, serializer): |     def perform_create(self, serializer): | ||||||
|         """Save the post data when creating a new template.""" |         """Save the post data when creating a new template.""" | ||||||
|  | @ -45,16 +57,28 @@ class VmCreateView(generics.ListCreateAPIView): | ||||||
| 
 | 
 | ||||||
| class VmDetailsView(generics.RetrieveUpdateDestroyAPIView): | class VmDetailsView(generics.RetrieveUpdateDestroyAPIView): | ||||||
|     """This class handles the http GET, PUT and DELETE requests.""" |     """This class handles the http GET, PUT and DELETE requests.""" | ||||||
|     permission_classes = (permissions.IsAuthenticated, IsOwner) |     permission_classes = (permissions.IsAuthenticated, ) | ||||||
| 
 | 
 | ||||||
|     queryset = VirtualMachine.objects.all() |  | ||||||
|     serializer_class = VirtualMachineSerializer |     serializer_class = VirtualMachineSerializer | ||||||
| 
 | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|  |         return manager.get_vms() | ||||||
|  | 
 | ||||||
|  |     def get_object(self): | ||||||
|  |         owner = self.request.user | ||||||
|  |         manager = OpenNebulaManager(email=owner.email, | ||||||
|  |                                     password=owner.password[0:20], | ||||||
|  |                                     create_user=True) | ||||||
|  |         return manager.get_vm(self.kwargs.get('pk')) | ||||||
|  | 
 | ||||||
|     def perform_destroy(self, instance): |     def perform_destroy(self, instance): | ||||||
|         owner = instance.owner |         owner = self.request.user | ||||||
|         manager = OpenNebulaManager(email=owner.email, |         manager = OpenNebulaManager(email=owner.email, | ||||||
|                                     password=owner.password[0:20], |                                     password=owner.password[0:20], | ||||||
|                                     create_user = True) |                                     create_user = True) | ||||||
|         manager.delete_vm(instance.opennebula_id) |         manager.delete_vm(instance.id) | ||||||
|         instance.delete() |  | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue