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>
|
||||||
|
@ -42,4 +46,4 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
|
@ -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>
|
|
||||||
<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p>
|
|
||||||
<hr>
|
<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>
|
<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>
|
</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 %}
|
||||||
|
@ -66,4 +67,4 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
203
hosting/views.py
203
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…
Reference in a new issue