API Integration

Please review carefully.
This commit is contained in:
Modulos 2017-05-12 12:07:05 +02:00
parent b7e8eceb25
commit 130c00c8ee
19 changed files with 310 additions and 890 deletions

View file

@ -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)

View file

@ -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):

View 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',
),
]

View file

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

View file

@ -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

View file

@ -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" %}

View file

@ -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%}

View file

@ -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 %}

View file

@ -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>

View file

@ -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%}

View file

@ -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%}

View file

@ -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

View file

@ -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'),
),
]

View file

@ -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',
),
]

View file

@ -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,
),
]

View file

@ -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
) )
) )

View file

@ -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

View file

@ -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

View file

@ -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()