merged opnnebula api changes
This commit is contained in:
		
				commit
				
					
						8980f6b2fc
					
				
			
		
					 19 changed files with 458 additions and 888 deletions
				
			
		|  | @ -12,6 +12,8 @@ import debug_toolbar | |||
| 
 | ||||
| urlpatterns = [   url(r'^index.html$', LandingView.as_view()), | ||||
|                   url(r'^hosting/', include('hosting.urls', namespace="hosting")), | ||||
|                   url(r'^open_api/', include('opennebula_api.urls', | ||||
|                       namespace='opennebula_api')), | ||||
|                   url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"), | ||||
|                   url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"), | ||||
|                   url(r'^djangohosting/', DjangoHostingView.as_view(), name="django.hosting"), | ||||
|  |  | |||
|  | @ -4,99 +4,8 @@ from django.core.urlresolvers import reverse | |||
| 
 | ||||
| from utils.mailer import BaseEmail | ||||
| 
 | ||||
| from .forms import HostingOrderAdminForm | ||||
| from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, \ | ||||
|                     ManageVM, HostingBill | ||||
| from .opennebula_functions import HostingManageVMAdmin | ||||
| from .models import HostingOrder, HostingBill | ||||
| 
 | ||||
| 
 | ||||
| class HostingOrderAdmin(admin.ModelAdmin): | ||||
|     # 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(HostingOrder) | ||||
| admin.site.register(HostingBill) | ||||
|  |  | |||
|  | @ -7,39 +7,7 @@ from django.contrib.auth import authenticate | |||
| 
 | ||||
| from utils.stripe_utils import StripeUtils | ||||
| 
 | ||||
| from .models import HostingOrder, VirtualMachinePlan, 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 | ||||
| 
 | ||||
| from .models import HostingOrder, UserHostingKey | ||||
| 
 | ||||
| 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', | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										21
									
								
								hosting/migrations/0039_hostingorder_price.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								hosting/migrations/0039_hostingorder_price.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-12 16:37 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0038_auto_20170512_1006'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='hostingorder', | ||||
|             name='price', | ||||
|             field=models.FloatField(default=0), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
|  | @ -1,35 +1,20 @@ | |||
| from django.shortcuts import redirect | ||||
| from django.core.urlresolvers import reverse | ||||
| from .models import VirtualMachinePlan, VirtualMachineType | ||||
| 
 | ||||
| 
 | ||||
| class ProcessVMSelectionMixin(object): | ||||
| 
 | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         configuration = request.POST.get('configuration') | ||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||
|         vm_template = request.POST.get('vm_template') | ||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) | ||||
|         vm_specs = vm_type.get_specs() | ||||
|         #configuration = request.POST.get('configuration') | ||||
|         #configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||
|         vm_template_id = request.POST.get('vm_template_id') | ||||
|         vm_specs.update({ | ||||
|             'configuration_display': configuration_display, | ||||
|             'configuration': configuration, | ||||
|             'final_price': vm_type.final_price, | ||||
|             'vm_template': vm_template | ||||
|             'vm_template_id': vm_template_id | ||||
|         }) | ||||
|         # 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 | ||||
|         if not request.user.is_authenticated(): | ||||
|             request.session['vm_specs'] = vm_specs | ||||
|             request.session['next'] = reverse('hosting:payment') | ||||
|             return redirect(reverse('hosting:login')) | ||||
|         return redirect(reverse('hosting:payment')) | ||||
|  |  | |||
|  | @ -1,5 +1,7 @@ | |||
| import os | ||||
| import socket | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| import oca | ||||
| from django.db import models | ||||
|  | @ -19,252 +21,15 @@ from .managers import VMPlansManager | |||
| from oca.exceptions import OpenNebulaException | ||||
| from oca.pool import WrongNameError | ||||
| 
 | ||||
| import logging | ||||
| 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 | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_vm_templates(self, user): | ||||
|         opennebula_client = OpenNebulaManager( | ||||
|             email=user.email, | ||||
|             password=user.password, | ||||
|         ) | ||||
| 
 | ||||
|         templates = opennebula_client.get_vm_templates() | ||||
|         for template in templates: | ||||
|             print(OpenNebulaManager.parse_vm(template)) | ||||
|         return templates | ||||
| 
 | ||||
| 
 | ||||
| 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, vm_id): | ||||
|         self.status = self.CANCELED_STATUS | ||||
|         self.save(update_fields=['status']) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def terminate_opennebula_vm(self, user, vm_id): | ||||
| 
 | ||||
|         opennebula_client = OpenNebulaManager( | ||||
|             user.email, | ||||
|             user.password, | ||||
|         ) | ||||
| 
 | ||||
|         return opennebula_client.terminate_vm(vm_id) | ||||
| 
 | ||||
| 
 | ||||
|     @classmethod | ||||
|     def create_opennebula_vm(self, user, specs): | ||||
|         # import pdb | ||||
|         # pdb.set_trace() | ||||
| 
 | ||||
| 
 | ||||
|         # Init opennebula manager using given user | ||||
|         opennebula_client = OpenNebulaManager( | ||||
|             user.email, | ||||
|             user.password, | ||||
|         ) | ||||
| 
 | ||||
|         # 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, | ||||
|         ) | ||||
| 
 | ||||
|         # 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, | ||||
|         ) | ||||
| 
 | ||||
|         # 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): | ||||
| 
 | ||||
|     ORDER_APPROVED_STATUS = 'Approved' | ||||
|     ORDER_DECLINED_STATUS = 'Declined' | ||||
| 
 | ||||
|     vm_plan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') | ||||
|     vm_id = models.IntegerField(default=0) | ||||
|     customer = models.ForeignKey(StripeCustomer) | ||||
|     billing_address = models.ForeignKey(BillingAddress) | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|  | @ -272,6 +37,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | |||
|     last4 = models.CharField(max_length=4) | ||||
|     cc_brand = models.CharField(max_length=10) | ||||
|     stripe_charge_id = models.CharField(max_length=100, null=True) | ||||
|     price = models.FloatField() | ||||
| 
 | ||||
|     permissions = ('view_hostingorder',) | ||||
| 
 | ||||
|  | @ -288,9 +54,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | |||
|         return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS | ||||
| 
 | ||||
|     @classmethod | ||||
|     def create(cls, vm_plan=None, customer=None, billing_address=None): | ||||
|         instance = cls.objects.create(vm_plan=vm_plan, customer=customer, | ||||
|                                       billing_address=billing_address) | ||||
|     def create(cls, price=None, vm_id=None, customer=None, billing_address=None): | ||||
|         instance = cls.objects.create( | ||||
|             price=price, | ||||
|             vm_id=vm_id, | ||||
|             customer=customer, | ||||
|             billing_address=billing_address | ||||
|         ) | ||||
|         instance.assign_permissions(customer.user) | ||||
|         return instance | ||||
| 
 | ||||
|  | @ -336,17 +106,6 @@ class UserHostingKey(models.Model): | |||
|         # self.save(update_fields=['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): | ||||
|     customer = models.ForeignKey(StripeCustomer) | ||||
|     billing_address = models.ForeignKey(BillingAddress) | ||||
|  | @ -367,32 +126,3 @@ class HostingBill(AssignPermissionsMixin, models.Model): | |||
|         instance = cls.objects.create(customer=customer, billing_address=billing_address) | ||||
|         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 class="row"> | ||||
|                 <div class="col-sm-6"> | ||||
|                     {% trans "CH02 ............" %} | ||||
|                     {% trans "CH02 0900 0000 6071 8848 8%} | ||||
|                 </div> | ||||
|                 <div class="col-sm-6"> | ||||
|                     {% trans "POFICHBEXXX" %} | ||||
|  |  | |||
|  | @ -12,15 +12,19 @@ | |||
|                     {% csrf_token %} | ||||
|                     <div class="form-group"> | ||||
|                         Select VM: | ||||
|                         <select name="vm_template"> | ||||
|                             {% for vm in vm_types %} | ||||
|                         <select name="vm_template_id"> | ||||
|                             {% 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}} GiB,  | ||||
|                                                 SSD: {{template.disk_size}} GiB | ||||
|                                   </option> | ||||
|                                  | ||||
|                             {% endfor %} | ||||
|                         </select> | ||||
|                     </div>  | ||||
|                     <div class="form-group"> | ||||
| <!--                     <div class="form-group"> | ||||
|                         Select VM Configuration: | ||||
|                         <select name="configuration"> | ||||
|                             {% for config in configuration_options %} | ||||
|  | @ -29,7 +33,7 @@ | |||
|                                  | ||||
|                             {% endfor %} | ||||
|                         </select> | ||||
|                     </div>                           | ||||
|                     </div>  -->                          | ||||
|                     <div class="form-group"> | ||||
|                         <button class="btn btn-success" >{% trans "Start VM"%} </button>                          | ||||
|                     </div> | ||||
|  | @ -42,4 +46,4 @@ | |||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| {%endblock%} | ||||
| {%endblock%} | ||||
|  |  | |||
|  | @ -49,17 +49,13 @@ | |||
|             <h3><b>{% trans "Order summary"%}</b></h3> | ||||
|             <hr> | ||||
|             <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> | ||||
|                 <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> | ||||
|                 <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> | ||||
|                 <p><b>{% trans "Memory"%}</b> <span class="pull-right">{{order.vm_plan.memory}} GiB</span></p> | ||||
|                 <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> | ||||
|                 <h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b></p></h4> | ||||
|             </div> | ||||
|             <br/> | ||||
|             {% url 'hosting:payment' as payment_url %} | ||||
|  |  | |||
|  | @ -100,15 +100,14 @@ | |||
| 							<div class="content"> | ||||
| 								<!-- <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> --> | ||||
| 								<!-- <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> | ||||
| 								<p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_display}}</span></p> | ||||
| 								<hr>								 | ||||
| 								<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p> | ||||
| 								<p><b>Memory</b> <span class="pull-right">{{request.session.template.memory}} GiB</span></p> | ||||
| 								<hr> | ||||
| 								<p><b>Disk space</b> <span class="pull-right">{{request.session.vm_specs.disk_size}} GiB</span></p> | ||||
| 								<p><b>Disk space</b> <span class="pull-right">{{request.session.template.disk_size}} GiB</span></p> | ||||
| 								<hr> | ||||
| 								<h4>Total<p class="pull-right"><b>{{request.session.vm_specs.final_price}} CHF</b></p></h4> | ||||
| 								<h4>Total<p | ||||
|                                     class="pull-right"><b>{{request.session.template.price }} CHF</b></p></h4> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  |  | |||
|  | @ -25,12 +25,6 @@ | |||
| 				            		{% trans "Billing"%} | ||||
| 				            	</a> | ||||
| 				            </li> | ||||
| 				            <li> | ||||
| 				            	<a href="#orders-v" data-toggle="tab"> | ||||
| 				            		<i class="fa fa-credit-card"></i>  | ||||
| 				            		{% trans "Orders"%} | ||||
| 				            	</a> | ||||
| 				            </li> | ||||
| 				            <li> | ||||
| 				            	<a href="#status-v" data-toggle="tab"> | ||||
| 				            		<i class="fa fa-signal" aria-hidden="true"></i> {% trans "Status"%} | ||||
|  | @ -109,56 +103,22 @@ | |||
| 									</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="row "> | ||||
| 									<div class="col-md-12 inline-headers"> | ||||
| 										<h3>{% trans "Current status"%}</h3> | ||||
| 
 | ||||
| 										<div  class="pull-right space-above"> | ||||
|                                             {% if virtual_machine.state == 'ACTIVE' %} | ||||
|                                                 <span class="h3 label label-success"><strong> {{virtual_machine.state}}</strong></span> | ||||
|                                             {% elif  virtual_machine.state == 'POWEROFF' %} | ||||
|                                                 <span class="h3 label label-danger"><strong>{{virtual_machine.state}}</strong></span> | ||||
|                                             {% else %} | ||||
|                                                 <span class="h3 label label-warning"><strong>{{virtual_machine.state}}</strong></span> | ||||
|                                             {% endif %}   | ||||
| 											{% if virtual_machine.state == 'PENDING' %} | ||||
| 												<span class="label | ||||
|                                                     label-warning"><strong>Pending</strong></span> | ||||
| 											{% elif  virtual_machine.state == 'ACTIVE' %} | ||||
| 												<span class="label | ||||
|                                                     label-success"><strong>Online</strong></span> | ||||
| 											{% elif  virtual_machine.state == 'FAILED'%} | ||||
| 												<span class="label | ||||
|                                                     label-danger"><strong>Failed</strong></span> | ||||
| 											{% endif %} | ||||
| 										</div> | ||||
| 									</div> | ||||
| 				            	</div> | ||||
|  | @ -166,11 +126,12 @@ | |||
| 				            	<div class="row"> | ||||
| 									<div class="col-md-12 space-above-big"> | ||||
| 										<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 %}  | ||||
| 											</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 "Terminate 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 "Terminate Virtual Machine"%}</button> | ||||
| 																						 | ||||
| 										</div> | ||||
|      | ||||
|  | @ -194,7 +155,7 @@ | |||
| 									                {% trans "Terminate your Virtual Machine"%} | ||||
| 									            </div> | ||||
| 									            <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 class="modal-footer"> | ||||
| 									                <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel"%}</button> | ||||
|  | @ -220,12 +181,3 @@ | |||
| </div> | ||||
| 
 | ||||
| {%endblock%} | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,15 +30,15 @@ | |||
| 				</tr> | ||||
| 				</thead> | ||||
| 				<tbody>  | ||||
| 					{% for vm in vms_opennebula %} | ||||
| 					{% for vm in vms %} | ||||
| 					<tr> | ||||
| 						<td scope="row">{{vm.name}}</td>  | ||||
| 						<td>{{vm.price|floatformat:2}} CHF</td>  | ||||
| 						<td scope="row">{{vm.vm_id}}</td>  | ||||
| 						<td>{{vm.price}} CHF</td>  | ||||
| 						<td> | ||||
|                             | ||||
| 							{% if vm.state == 'ACTIVE' %} | ||||
| 								<span class="h3 label label-success"><strong> {{vm.state}}</strong></span> | ||||
| 							{% elif  vm.state == 'POWEROFF' %} | ||||
| 							{% elif  vm.state == 'FAILED' %} | ||||
| 								<span class="h3 label label-danger"><strong>{{vm.state}}</strong></span> | ||||
| 							{% else %} | ||||
| 								<span class="h3 label label-warning"><strong>{{vm.state}}</strong></span> | ||||
|  | @ -46,7 +46,8 @@ | |||
| 
 | ||||
| 						</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> | ||||
| 					</tr> | ||||
| 					{% endfor %} | ||||
|  | @ -76,4 +77,4 @@ | |||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| {%endblock%} | ||||
| {%endblock%} | ||||
|  |  | |||
							
								
								
									
										251
									
								
								hosting/views.py
									
										
									
									
									
								
							
							
						
						
									
										251
									
								
								hosting/views.py
									
										
									
									
									
								
							|  | @ -20,16 +20,18 @@ from stored_messages.models import Message | |||
| from stored_messages.api import mark_read | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| from membership.models import CustomUser, StripeCustomer | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.forms import BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm | ||||
| from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin | ||||
| 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 .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.pool import WrongNameError | ||||
|  | @ -387,57 +389,26 @@ class PaymentVMView(LoginRequiredMixin, FormView): | |||
| 
 | ||||
|             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) | ||||
| 
 | ||||
|             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) | ||||
|             final_price = specifications.get('price', 1) | ||||
| 
 | ||||
|             token = form.cleaned_data.get('token') | ||||
| 
 | ||||
|             owner = self.request.user | ||||
| 
 | ||||
|             # 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) | ||||
|             if not customer: | ||||
|                 form.add_error("__all__", "Invalid credit card") | ||||
|                 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 | ||||
|             billing_address = form.save() | ||||
| 
 | ||||
|             # Create Billing Address for User if he does not have one | ||||
|             if not customer.user.billing_addresses.count(): | ||||
|                 billing_address_data.update({ | ||||
|                     'user': customer.user.id | ||||
|                 }) | ||||
|                 billing_address_user_form = UserBillingAddressForm(billing_address_data) | ||||
|                 billing_address_user_form.is_valid() | ||||
|                 billing_address_user_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 | ||||
|             stripe_utils = StripeUtils() | ||||
|             charge_response = stripe_utils.make_charge(amount=final_price, | ||||
|  | @ -454,11 +425,11 @@ class PaymentVMView(LoginRequiredMixin, FormView): | |||
| 
 | ||||
|             charge = charge_response.get('response_object') | ||||
| 
 | ||||
|             # Associate an order with a stripe payment | ||||
|             order.set_stripe_charge(charge) | ||||
| 
 | ||||
|             # If the Stripe payment was successed, set order status approved | ||||
|             order.set_approved() | ||||
|             # Create OpenNebulaManager | ||||
|             manager = OpenNebulaManager(email=owner.email, | ||||
|                                         password=owner.password, | ||||
|                                         create_user=True) | ||||
|             template = manager.get_template(vm_template_id) | ||||
| 
 | ||||
|             # Get user ssh key | ||||
|             try: | ||||
|  | @ -466,26 +437,46 @@ class PaymentVMView(LoginRequiredMixin, FormView): | |||
|                     user=self.request.user | ||||
|                 ) | ||||
| 
 | ||||
|                 # Add ssh_key to specs | ||||
|                 specs.update({ | ||||
|                     'ssh_key': user_key.public_key | ||||
|                 }) | ||||
| 
 | ||||
|             except UserHostingKey.DoesNotExist: | ||||
|                 pass | ||||
| 
 | ||||
|             # Create a vm using logged user | ||||
|             opennebula_vm_id = VirtualMachinePlan.create_opennebula_vm( | ||||
|                 self.request.user, | ||||
|                 specs | ||||
|             vm_id = manager.create_vm( | ||||
|                 vm_template_id, | ||||
|                 ssh_key=user_key.public_key | ||||
|             ) | ||||
| 
 | ||||
|             plan.opennebula_id = opennebula_vm_id | ||||
|             plan.save() | ||||
|             # Create a Hosting Order | ||||
|             order = HostingOrder.create( | ||||
|                 price=final_price, | ||||
|                 vm_id=vm_id, | ||||
|                 customer=customer, | ||||
|                 billing_address=billing_address | ||||
|             ) | ||||
| 
 | ||||
|             # Create a Hosting Bill | ||||
|             bill = HostingBill.create(customer=customer, billing_address=billing_address) | ||||
| 
 | ||||
|             # Create Billing Address for User if he does not have one | ||||
|             if not customer.user.billing_addresses.count(): | ||||
|                 billing_address_data.update({ | ||||
|                     'user': customer.user.id | ||||
|                 }) | ||||
|                 billing_address_user_form = UserBillingAddressForm(billing_address_data) | ||||
|                 billing_address_user_form.is_valid() | ||||
|                 billing_address_user_form.save() | ||||
| 
 | ||||
|             # Associate an order with a stripe payment | ||||
|             order.set_stripe_charge(charge) | ||||
| 
 | ||||
|             # If the Stripe payment was successed, set order status approved | ||||
|             order.set_approved() | ||||
| 
 | ||||
|             vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data | ||||
| 
 | ||||
|             # Send notification to ungleich as soon as VM has been booked | ||||
|             context = { | ||||
|                 'vm': plan, | ||||
|                 'vm': vm, | ||||
|                 'order': order, | ||||
|                 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) | ||||
| 
 | ||||
|  | @ -512,6 +503,18 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai | |||
|     permission_required = ['view_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, | ||||
|                                     create_user=True) | ||||
|         vm = manager.get_vm(obj.vm_id) | ||||
|         context['vm'] = VirtualMachineSerializer(vm).data | ||||
|         return context | ||||
| 
 | ||||
| 
 | ||||
| class OrdersHostingListView(LoginRequiredMixin, ListView): | ||||
|     template_name = "hosting/orders.html" | ||||
|  | @ -537,24 +540,17 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): | |||
|     template_name = "hosting/virtual_machines.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
|     context_object_name = "vms" | ||||
|     model = VirtualMachinePlan | ||||
|     paginate_by = 10 | ||||
|     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): | ||||
|         # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) | ||||
|         # print(hosting_admin.show_vms_view(self.request)) | ||||
|         # print(VirtualMachinePlan.get_vms(self.request.user.)) | ||||
|         user = self.request.user | ||||
|         self.queryset = VirtualMachinePlan.objects.active(user) | ||||
|         return super(VirtualMachinesPlanListView, self).get_queryset() | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user=True) | ||||
|         queryset = manager.get_vms() | ||||
|         serializer = VirtualMachineSerializer(queryset, many=True) | ||||
|         return serializer.data | ||||
| 
 | ||||
| 
 | ||||
| class CreateVirtualMachinesView(LoginRequiredMixin, View): | ||||
|  | @ -574,107 +570,70 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): | |||
|             ) | ||||
|             return HttpResponseRedirect(reverse('hosting:key_pair')) | ||||
| 
 | ||||
|         #TODO: Replace with OpenNebulaManager.get_apps | ||||
|         templates = OpenNebulaManager().get_templates() | ||||
|         data = VirtualMachineTemplateSerializer(templates, many=True).data | ||||
| 
 | ||||
|         context = { | ||||
|             'vm_types': VirtualMachineType.get_serialized_vm_types(), | ||||
|             'configuration_options': VirtualMachinePlan.VM_CONFIGURATION | ||||
|             'templates': data, | ||||
|         } | ||||
|         # context = {} | ||||
|         return render(request, self.template_name, context) | ||||
| 
 | ||||
|     def post(self, request): | ||||
|         configuration = request.POST.get('configuration') | ||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||
|         vm_template = request.POST.get('vm_template') | ||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) | ||||
|         vm_specs = vm_type.get_specs() | ||||
|         vm_specs.update({ | ||||
|             'configuration_display': configuration_display, | ||||
|             'configuration': configuration, | ||||
|             'final_price': vm_type.final_price, | ||||
|             'vm_template': vm_template | ||||
|         }) | ||||
|         request.session['vm_specs'] = vm_specs | ||||
|         template_id = int(request.POST.get('vm_template_id')) | ||||
|         template = OpenNebulaManager().get_template(template_id) | ||||
|         data = VirtualMachineTemplateSerializer(template).data | ||||
|         request.session['template'] = data | ||||
|         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(PermissionRequiredMixin, LoginRequiredMixin, View): | ||||
| class VirtualMachineView(LoginRequiredMixin, View): | ||||
|     template_name = "hosting/virtual_machine_detail.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
|     # model = VirtualMachinePlan | ||||
|     # context_object_name = "virtual_machine" | ||||
|     permission_required = [] | ||||
|     # 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_object(self): | ||||
|         opennebula_vm_id = self.kwargs.get('pk') | ||||
|         opennebula_vm = None | ||||
|         owner = self.request.user | ||||
|         vm = None | ||||
|         manager = OpenNebulaManager( | ||||
|             email=owner.email, | ||||
|             password=owner.password, | ||||
|             create_user=True | ||||
|         ) | ||||
|         vm_id = self.kwargs.get('pk') | ||||
|         try: | ||||
|             opennebula_vm = VirtualMachinePlan.objects.get(opennebula_id=opennebula_vm_id) | ||||
|             vm = manager.get_vm(vm_id) | ||||
|         except Exception as error: | ||||
|             print(error) | ||||
|             raise Http404() | ||||
|         return opennebula_vm | ||||
|         return vm | ||||
| 
 | ||||
|     def get_success_url(self): | ||||
|         final_url = reverse('hosting:virtual_machines') | ||||
|         return final_url | ||||
| 
 | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         opennebula_vm_id = self.kwargs.get('pk') | ||||
|         try: | ||||
|             opennebula_vm = VirtualMachinePlan.get_vm( | ||||
|                 self.request.user, | ||||
|                 opennebula_vm_id | ||||
|             ) | ||||
|         except Exception as error: | ||||
|             print(error) | ||||
|             raise Http404() | ||||
| 
 | ||||
|         vm = self.get_object() | ||||
|         serializer = VirtualMachineSerializer(vm) | ||||
|         context = { | ||||
|             'virtual_machine': opennebula_vm, | ||||
|             'virtual_machine': serializer.data, | ||||
|         } | ||||
|         # context = {} | ||||
|         return render(request, self.template_name, context) | ||||
| 
 | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         owner = self.request.user | ||||
|         vm = self.get_object() | ||||
| 
 | ||||
|         opennebula_vm_id = self.kwargs.get('pk') | ||||
| 
 | ||||
|         terminated = VirtualMachinePlan.terminate_opennebula_vm( | ||||
|             self.request.user, | ||||
|             opennebula_vm_id | ||||
|         manager = OpenNebulaManager( | ||||
|             email=owner.email, | ||||
|             password=owner.password, | ||||
|             create_user=True | ||||
|         ) | ||||
| 
 | ||||
|         terminated = manager.delete_vm( | ||||
|             vm.id | ||||
|         ) | ||||
| 
 | ||||
|         if not terminated: | ||||
|  | @ -684,8 +643,6 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, View): | |||
|             ) | ||||
|             return HttpResponseRedirect(self.get_success_url()) | ||||
| 
 | ||||
|         #vm.cancel_plan() | ||||
| 
 | ||||
|         context = { | ||||
|             'vm': vm, | ||||
|             'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) | ||||
|  | @ -735,10 +692,14 @@ class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailV | |||
|     def get_context_data(self, **kwargs): | ||||
|         # Get context | ||||
|         context = super(DetailView, self).get_context_data(**kwargs) | ||||
| 
 | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user=True) | ||||
|         # Get vms | ||||
|         try: | ||||
|             context['vms'] = self.get_object().get_vms() | ||||
|         except: | ||||
|             pass | ||||
|         queryset = manager.get_vms() | ||||
|         vms = VirtualMachineSerializer(queryset, many=True).data | ||||
|         context['vms'] = vms | ||||
| 
 | ||||
|         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,110 +1,14 @@ | |||
| import oca | ||||
| import socket | ||||
| import logging | ||||
| 
 | ||||
| 
 | ||||
| from django.db import models | ||||
| from django.conf import settings | ||||
| from django.utils.functional import cached_property | ||||
| 
 | ||||
| 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() | ||||
| 
 | ||||
|     def calculate_price(self): | ||||
|         template = OpenNebulaManager()._get_template(self.opennebula_id).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 = OpenNebulaManager()._get_template(template_id=self.opennebula_id) | ||||
|         return template.name | ||||
| 
 | ||||
|     def get_cores(self): | ||||
| 
 | ||||
|         template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template | ||||
|         return int(template.vcpu) | ||||
|      | ||||
|     def get_disk_size(self): | ||||
| 
 | ||||
|         template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template | ||||
|         disk_size = 0 | ||||
|         for disk in template.disks: | ||||
|             disk_size += int(disk.size) | ||||
|         return disk_size / 1024 | ||||
| 
 | ||||
|     def get_memory(self): | ||||
| 
 | ||||
|         template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template | ||||
|         return int(template.memory) / 1024 | ||||
| 
 | ||||
| class VirtualMachine(models.Model): | ||||
|     """This class represents an opennebula virtual machine.""" | ||||
|     opennebula_id = models.IntegerField() | ||||
|     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', | ||||
|     } | ||||
| 
 | ||||
|     def get_name(self): | ||||
|              | ||||
|         vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) | ||||
|         return 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): | ||||
| 
 | ||||
|         vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) | ||||
|         return vm.id | ||||
| 
 | ||||
|     def get_ip(self): | ||||
| 
 | ||||
|         vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) | ||||
|         try: | ||||
|             return vm.user_template.ungleich_public_ip | ||||
|         except AttributeError: | ||||
|             return '-' | ||||
| 
 | ||||
|     def get_state(self): | ||||
| 
 | ||||
|         vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) | ||||
|         return self.VM_STATE.get(str(vm.state)) | ||||
| 
 | ||||
|     def get_price(self): | ||||
|         return self.vm_template.calculate_price() | ||||
| from oca.exceptions import OpenNebulaException | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| class OpenNebulaManager(): | ||||
|     """This class represents an opennebula manager.""" | ||||
|  | @ -175,8 +79,13 @@ class OpenNebulaManager(): | |||
| 
 | ||||
|     def _get_vm_pool(self): | ||||
|         try: | ||||
|            vm_pool = oca.VirtualMachinePool(self.oneadmin_client) | ||||
|            vm_pool.info() | ||||
|             vm_pool = oca.VirtualMachinePool(self.client) | ||||
|             vm_pool.info() | ||||
|         except AttributeError: | ||||
|             print('Could not connect via client, using oneadmin instead')  | ||||
|             vm_pool = oca.VirtualMachinePool(self.oneadmin_client) | ||||
|             vm_pool.info(filter=-2) | ||||
| 
 | ||||
|         #TODO: Replace with logger | ||||
|         except ConnectionRefusedError: | ||||
|             print('Could not connect to host: {host} via protocol {protocol}'.format( | ||||
|  | @ -186,19 +95,25 @@ class OpenNebulaManager(): | |||
|             raise ConnectionRefusedError | ||||
|         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() | ||||
|         # Get virtual machines from all users  | ||||
|         vm_pool.info(filter=-2) | ||||
|         return vm_pool.get_by_id(vm_id) | ||||
|         return vm_pool.get_by_id(int(vm_id)) | ||||
| 
 | ||||
|     def create_vm(self, template_id): | ||||
|         template_pool = self._get_template_pool() | ||||
| 
 | ||||
|         template = template_pool.get_by_id(template_id) | ||||
| 
 | ||||
|         vm_id = template.instantiate() | ||||
|     #TODO: get app with id  | ||||
|     def create_vm(self, template_id, app_id=None, ssh_key=None): | ||||
|         extra_template = "<CONTEXT><SSH_PUBLIC_KEY>{ssh_key}</SSH_PUBLIC_KEY></CONTEXT>".format( | ||||
|             ssh_key=ssh_key | ||||
|         ) | ||||
|         vm_id = self.oneadmin_client.call( | ||||
|             oca.VmTemplate.METHODS['instantiate'], | ||||
|             template_id, | ||||
|             '', | ||||
|             False, | ||||
|             extra_template | ||||
|         ) | ||||
|         try: | ||||
|             self.oneadmin_client.call( | ||||
|                 oca.VirtualMachine.METHODS['chown'], | ||||
|  | @ -207,12 +122,33 @@ class OpenNebulaManager(): | |||
|                 self.opennebula_user.group_ids[0] | ||||
|             ) | ||||
|         except AttributeError: | ||||
|             pass | ||||
|             print('Could not change owner, opennebula_user is not set.') | ||||
|         return vm_id | ||||
| 
 | ||||
|     def delete_vm(self, vm_id): | ||||
|         vm = self._get_vm(vm_id) | ||||
|         vm.delete() | ||||
|         TERMINATE_ACTION = 'terminate' | ||||
|         vm_terminated = False | ||||
|         try: | ||||
|             self.oneadmin_client.call( | ||||
|                 oca.VirtualMachine.METHODS['action'], | ||||
|                 TERMINATE_ACTION, | ||||
|                 int(vm_id), | ||||
|             ) | ||||
|             vm_terminated = True | ||||
|         except socket.timeout as socket_err: | ||||
|             logger.info("Socket timeout error: {0}".format(socket_err)) | ||||
|             print("Socket timeout error: {0}".format(socket_err)) | ||||
|         except OpenNebulaException as opennebula_err: | ||||
|             logger.info("OpenNebulaException error: {0}".format(opennebula_err)) | ||||
|             print("OpenNebulaException error: {0}".format(opennebula_err)) | ||||
|         except OSError as os_err: | ||||
|             logger.info("OSError : {0}".format(os_err)) | ||||
|             print("OSError : {0}".format(os_err)) | ||||
|         except ValueError as value_err: | ||||
|             logger.info("ValueError : {0}".format(value_err)) | ||||
|             print("ValueError : {0}".format(value_err)) | ||||
| 
 | ||||
|         return vm_terminated | ||||
| 
 | ||||
|     def _get_template_pool(self): | ||||
|         try: | ||||
|  | @ -227,19 +163,31 @@ class OpenNebulaManager(): | |||
|             raise ConnectionRefusedError | ||||
|         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() | ||||
|         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. | ||||
|         :param name:      A string representation describing the template. | ||||
|                           Used as label in view. | ||||
|         :param cores:     Amount of virtual cpu cores for the VM. | ||||
|         :param memory:    Amount of RAM for the VM (MB) | ||||
|         :param disk_size: Amount of disk space for VM (MB) | ||||
|         :param memory:  Amount of RAM for the VM (GB) | ||||
|         :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> | ||||
|                                         <NAME>{name}</NAME> | ||||
|  | @ -251,6 +199,10 @@ class OpenNebulaManager(): | |||
|                                          <SIZE>{size}</SIZE> | ||||
|                                          <DEV_PREFIX>vd</DEV_PREFIX> | ||||
|                                         </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_id = oca.VmTemplate.allocate( | ||||
|  | @ -260,7 +212,12 @@ class OpenNebulaManager(): | |||
|                 vcpu=cores, | ||||
|                 cpu=0.1*cores, | ||||
|                 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 | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|  | @ -269,5 +226,3 @@ class OpenNebulaManager(): | |||
|     def delete_template(self, template_id): | ||||
|         self.oneadmin_client.call(oca.VmTemplate.METHODS['delete'], template_id, False) | ||||
| 
 | ||||
| 
 | ||||
|     | ||||
|  |  | |||
|  | @ -3,91 +3,117 @@ import oca | |||
| from rest_framework import serializers | ||||
| 
 | ||||
| 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.""" | ||||
|     cores       = serializers.IntegerField(source='get_cores')  | ||||
|     name        = serializers.CharField(source='get_name') | ||||
|     disk_size   = serializers.IntegerField(source='get_disk_size') | ||||
|     memory      = serializers.IntegerField(source='get_memory') | ||||
|     id          = serializers.IntegerField(read_only=True) | ||||
|     name        = serializers.CharField() | ||||
|     cores       = serializers.IntegerField(source='template.vcpu')  | ||||
|     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: | ||||
|         model = VirtualMachineTemplate | ||||
|         fields = ('id', 'name', 'cores', 'memory', 'disk_size', 'base_price',  | ||||
|                   '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') | ||||
|     def create(self, validated_data): | ||||
|         data = validated_data | ||||
|         template = data.pop('template') | ||||
| 
 | ||||
|         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) | ||||
|          | ||||
|         try: | ||||
|             opennebula_id = manager.create_template(name=name, cores=cores, | ||||
|                                                     memory=memory, | ||||
|                                                     disk_size=disk_size) | ||||
|             data.update({'opennebula_id':opennebula_id}) | ||||
|                                                     disk_size=disk_size, | ||||
|                                                     core_price=core_price, | ||||
|                                                     disk_size_price=disk_size_price, | ||||
|                                                     memory_price=memory_price) | ||||
|         except OpenNebulaException as err: | ||||
|             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) | ||||
|          | ||||
|         return data | ||||
|         return manager.get_template(template_id=opennebula_id) | ||||
| 
 | ||||
|     def create(self, validated_data): | ||||
|         return VirtualMachineTemplate.objects.create(**validated_data) | ||||
|     def get_disk_size(self, obj): | ||||
|         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 display_value(self, instance): | ||||
|         return 'Template: {}'.format(instance.get_name()) | ||||
|     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 | ||||
| 
 | ||||
| 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.""" | ||||
| 
 | ||||
|     #TODO: Maybe we can change to template.get_cores | ||||
|     cores       = serializers.IntegerField(read_only=True, source='get_cores')  | ||||
|     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') | ||||
|     name        = serializers.CharField(read_only=True) | ||||
|     cores       = serializers.IntegerField(read_only=True, source='template.vcpu')  | ||||
| 
 | ||||
|     vm_template = VirtualMachineTemplateSerializer(read_only=True) | ||||
|     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_id = TemplatePrimaryKeyRelatedField( | ||||
|                 queryset=VirtualMachineTemplate.objects.all(), | ||||
|                 source='vm_template' | ||||
|     template_id = serializers.ChoiceField( | ||||
|                 choices=[(key.id, key.name) for key in | ||||
|                     OpenNebulaManager().get_templates()], | ||||
|                 source='template.template_id', | ||||
|                 write_only=True | ||||
|             ) | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = VirtualMachine | ||||
|         fields = ('id', 'opennebula_id', 'vm_template', 'vm_template_id', 'cores', 'name', | ||||
|                 'disk_size', 'memory', 'ip', 'deploy_id', 'state', 'vm_id', | ||||
|                 'price') | ||||
|         read_only_fields = ('opennebula_id', ) | ||||
|     def create(self, validated_data): | ||||
|         owner = validated_data['owner'] | ||||
|         template_id = validated_data['template']['template_id'] | ||||
| 
 | ||||
|     def validate(self, data): | ||||
|         # Create the opennebula model | ||||
|         manager = OpenNebulaManager(create_user = False) | ||||
|          | ||||
|         try: | ||||
|             template_id = data['vm_template'].opennebula_id | ||||
|             manager = OpenNebulaManager(email=owner.email, | ||||
|                                         password=owner.password, | ||||
|                                         create_user = True) | ||||
|             opennebula_id = manager.create_vm(template_id) | ||||
|             data.update({'opennebula_id':opennebula_id}) | ||||
|         except OpenNebulaException as err: | ||||
|             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) | ||||
|          | ||||
|         return data | ||||
|         return manager.get_vm(opennebula_id) | ||||
| 
 | ||||
|     def create(self, validated_data): | ||||
|         return VirtualMachine.objects.create(**validated_data) | ||||
|     def get_memory(self, obj): | ||||
|         return int(obj.template.memory)/1024 | ||||
| 
 | ||||
|     def get_disk_size(self, obj): | ||||
|         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 | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										18
									
								
								opennebula_api/urls.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								opennebula_api/urls.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| from django.conf.urls import url, include | ||||
| from rest_framework.urlpatterns import format_suffix_patterns | ||||
| from .views import TemplateCreateView, TemplateDetailsView,\ | ||||
|                    VmCreateView, VmDetailsView | ||||
| 
 | ||||
| urlpatterns = { | ||||
|     url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), | ||||
| 
 | ||||
|     url(r'^templates/$', TemplateCreateView.as_view(), name="template_create"), | ||||
|     url(r'^templates/(?P<pk>[0-9]+)/$', TemplateDetailsView.as_view(), | ||||
|         name="templates_details"), | ||||
|      | ||||
|     url(r'^vms/$', VmCreateView.as_view(), name="vm_create"), | ||||
|     url(r'^vms/(?P<pk>[0-9]+)/$', VmDetailsView.as_view(), | ||||
|         name="vm_details"), | ||||
| } | ||||
| 
 | ||||
| urlpatterns = format_suffix_patterns(urlpatterns) | ||||
|  | @ -1,13 +1,29 @@ | |||
| from rest_framework import generics | ||||
| from rest_framework import permissions | ||||
| 
 | ||||
| from django.contrib.auth.mixins import LoginRequiredMixin | ||||
| from django.contrib.auth import authenticate, login | ||||
| 
 | ||||
| 
 | ||||
| from utils.views import LoginViewMixin | ||||
| from membership.models import CustomUser, StripeCustomer | ||||
| from guardian.mixins import PermissionRequiredMixin | ||||
| 
 | ||||
| from .serializers import VirtualMachineTemplateSerializer, \ | ||||
|                          VirtualMachineSerializer | ||||
| from .models import VirtualMachineTemplate, VirtualMachine, OpenNebulaManager | ||||
| from .models import OpenNebulaManager | ||||
| 
 | ||||
| 
 | ||||
| class TemplateCreateView(generics.ListCreateAPIView): | ||||
|     """This class defines the create behavior of our rest api.""" | ||||
|     queryset = VirtualMachineTemplate.objects.all() | ||||
|     """This class handles the GET and POST requests.""" | ||||
| 
 | ||||
|     serializer_class = VirtualMachineTemplateSerializer | ||||
|     permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser) | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         manager = OpenNebulaManager() | ||||
|         return manager.get_templates() | ||||
| 
 | ||||
| 
 | ||||
|     def perform_create(self, serializer): | ||||
|         """Save the post data when creating a new template.""" | ||||
|  | @ -16,20 +32,53 @@ class TemplateCreateView(generics.ListCreateAPIView): | |||
| class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """This class handles the http GET, PUT and DELETE requests.""" | ||||
| 
 | ||||
|     queryset = VirtualMachineTemplate.objects.all() | ||||
|     serializer_class = VirtualMachineTemplateSerializer | ||||
|     permission_classes = (permissions.IsAuthenticated) | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         manager = OpenNebulaManager() | ||||
|         return manager.get_templates() | ||||
| 
 | ||||
| class VmCreateView(generics.ListCreateAPIView): | ||||
|     """This class defines the create behavior of our rest api.""" | ||||
|     queryset = VirtualMachine.objects.all() | ||||
|     """This class handles the GET and POST requests.""" | ||||
|     serializer_class = VirtualMachineSerializer | ||||
|     permission_classes = (permissions.IsAuthenticated, ) | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user=True) | ||||
|         return manager.get_vms() | ||||
| 
 | ||||
|     def perform_create(self, serializer): | ||||
|         """Save the post data when creating a new template.""" | ||||
|         serializer.save() | ||||
|         serializer.save(owner=self.request.user) | ||||
| 
 | ||||
| class VmDetailsView(generics.RetrieveUpdateDestroyAPIView): | ||||
|     """This class handles the http GET, PUT and DELETE requests.""" | ||||
|     permission_classes = (permissions.IsAuthenticated, ) | ||||
| 
 | ||||
|     queryset = VirtualMachine.objects.all() | ||||
|     serializer_class = VirtualMachineSerializer | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user=True) | ||||
|         return manager.get_vms() | ||||
| 
 | ||||
|     def get_object(self): | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user=True) | ||||
|         return manager.get_vm(self.kwargs.get('pk')) | ||||
| 
 | ||||
|     def perform_destroy(self, instance): | ||||
|         owner = self.request.user | ||||
|         manager = OpenNebulaManager(email=owner.email, | ||||
|                                     password=owner.password, | ||||
|                                     create_user = True) | ||||
|         manager.delete_vm(instance.id) | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue