diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index 5e6b99ee..adbe0242 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -12,6 +12,8 @@ import debug_toolbar urlpatterns = [ url(r'^index.html$', LandingView.as_view()), url(r'^hosting/', include('hosting.urls', namespace="hosting")), + url(r'^open_api/', include('opennebula_api.urls', + namespace='opennebula_api')), url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"), url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"), url(r'^djangohosting/', DjangoHostingView.as_view(), name="django.hosting"), diff --git a/hosting/admin.py b/hosting/admin.py index ee4e7415..4f2f15e1 100644 --- a/hosting/admin.py +++ b/hosting/admin.py @@ -4,99 +4,8 @@ from django.core.urlresolvers import reverse from utils.mailer import BaseEmail -from .forms import HostingOrderAdminForm -from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, \ - ManageVM, HostingBill -from .opennebula_functions import HostingManageVMAdmin +from .models import HostingOrder, HostingBill -class HostingOrderAdmin(admin.ModelAdmin): - # fields = ('slug', 'imdb_link', 'start', 'finish', 'added_by') - list_display = ('id', 'created_at', 'plan', 'user') - search_fields = ['vm_plan__id', 'customer__user__email'] - - def save_model(self, request, obj, form, change): - if not change: - customer = form.cleaned_data.get('customer') - - # Get and set billing address from the lastest charged order - last_order = HostingOrder.objects.filter(customer=customer).latest('id') - billing_address = last_order.billing_address - obj.billing_address = billing_address - - charge = form.cleaned_data.get('charge') - # Associate an order with a stripe payment - obj.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - obj.set_approved() - - # Assigning permissions - obj.assign_permissions(customer.user) - - context = { - 'order': obj, - 'vm': obj.vm_plan, - 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) - } - email_data = { - 'subject': 'Your VM plan has been charged', - 'to': obj.customer.user.email, - 'context': context, - 'template_name': 'vm_charged', - 'template_path': 'hosting/emails/' - } - email = BaseEmail(**email_data) - email.send() - - obj.save() - return obj - - def get_form(self, request, obj=None, **kwargs): - if obj is None: - kwargs['form'] = HostingOrderAdminForm - return super(HostingOrderAdmin, self).get_form(request, obj, **kwargs) - - def user(self, obj): - email = obj.customer.user.email - user_url = reverse("admin:membership_customuser_change", args=[obj.customer.user.id]) - return format_html("{email}", 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("{vm_name}", url=vm_url, vm_name=vm_name) - - plan.short_description = "Virtual Machine Plan" - - -class VirtualMachinePlanAdmin(admin.ModelAdmin): - list_display = ('name', 'id', 'email') - - def email(self, obj): - return obj.hosting_orders.latest('id').customer.user.email - - def save_model(self, request, obj, form, change): - email = self.email(obj) - if 'status' in form.changed_data: - context = { - 'vm': obj, - 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) - } - email_data = { - 'subject': 'Your VM has been activated', - 'to': email, - 'context': context, - 'template_name': 'vm_status_changed', - 'template_path': 'hosting/emails/' - } - email = BaseEmail(**email_data) - email.send() - obj.save() - - -admin.site.register(HostingOrder, HostingOrderAdmin) -admin.site.register(VirtualMachineType) -admin.site.register(VirtualMachinePlan, VirtualMachinePlanAdmin) -admin.site.register(ManageVM, HostingManageVMAdmin) +admin.site.register(HostingOrder) admin.site.register(HostingBill) diff --git a/hosting/forms.py b/hosting/forms.py index b143140d..c94c4822 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -7,39 +7,7 @@ from django.contrib.auth import authenticate from utils.stripe_utils import StripeUtils -from .models import HostingOrder, VirtualMachinePlan, UserHostingKey - - -class HostingOrderAdminForm(forms.ModelForm): - - class Meta: - model = HostingOrder - fields = ['vm_plan', 'customer'] - - def clean(self): - customer = self.cleaned_data.get('customer') - vm_plan = self.cleaned_data.get('vm_plan') - - if vm_plan.status == VirtualMachinePlan.CANCELED_STATUS: - raise forms.ValidationError("""You can't make a charge over - a canceled virtual machine plan""") - - if not customer: - raise forms.ValidationError("""You need select a costumer""") - - # Make a charge to the customer - stripe_utils = StripeUtils() - charge_response = stripe_utils.make_charge(customer=customer.stripe_id, - amount=vm_plan.price) - charge = charge_response.get('response_object') - if not charge: - raise forms.ValidationError(charge_response.get('error')) - - self.cleaned_data.update({ - 'charge': charge - }) - return self.cleaned_data - +from .models import HostingOrder, UserHostingKey class HostingUserLoginForm(forms.Form): diff --git a/hosting/migrations/0038_auto_20170512_1006.py b/hosting/migrations/0038_auto_20170512_1006.py new file mode 100644 index 00000000..716faa38 --- /dev/null +++ b/hosting/migrations/0038_auto_20170512_1006.py @@ -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', + ), + ] diff --git a/hosting/migrations/0039_hostingorder_price.py b/hosting/migrations/0039_hostingorder_price.py new file mode 100644 index 00000000..0d4945fa --- /dev/null +++ b/hosting/migrations/0039_hostingorder_price.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2017-05-12 16:37 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0038_auto_20170512_1006'), + ] + + operations = [ + migrations.AddField( + model_name='hostingorder', + name='price', + field=models.FloatField(default=0), + preserve_default=False, + ), + ] diff --git a/hosting/mixins.py b/hosting/mixins.py index 404c4cb9..359b1434 100644 --- a/hosting/mixins.py +++ b/hosting/mixins.py @@ -1,35 +1,20 @@ from django.shortcuts import redirect from django.core.urlresolvers import reverse -from .models import VirtualMachinePlan, VirtualMachineType class ProcessVMSelectionMixin(object): def post(self, request, *args, **kwargs): - configuration = request.POST.get('configuration') - configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) - vm_template = request.POST.get('vm_template') - vm_type = VirtualMachineType.objects.get(id=vm_template) - vm_specs = vm_type.get_specs() + #configuration = request.POST.get('configuration') + #configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) + vm_template_id = request.POST.get('vm_template_id') vm_specs.update({ 'configuration_display': configuration_display, 'configuration': configuration, - 'final_price': vm_type.final_price, - 'vm_template': vm_template + 'vm_template_id': vm_template_id }) - # vm_specs = { - # # 'cores': request.POST.get('cores'), - # # 'memory': request.POST.get('memory'), - # # 'disk_size': request.POST.get('disk_space'), - # # 'hosting_company': request.POST.get('hosting_company'), - # # 'location_code': request.POST.get('location_code'), - # # 'configuration': hosting, - # # 'configuration_detail': configuration_detail, - # 'final_price': request.POST.get('final_price') - # } request.session['vm_specs'] = vm_specs if not request.user.is_authenticated(): - request.session['vm_specs'] = vm_specs request.session['next'] = reverse('hosting:payment') return redirect(reverse('hosting:login')) return redirect(reverse('hosting:payment')) diff --git a/hosting/models.py b/hosting/models.py index 892aa45d..188f1733 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,5 +1,7 @@ import os import socket +import logging + import oca from django.db import models @@ -19,252 +21,15 @@ from .managers import VMPlansManager from oca.exceptions import OpenNebulaException from oca.pool import WrongNameError -import logging logger = logging.getLogger(__name__) -class VirtualMachineType(models.Model): - - description = models.TextField() - base_price = models.FloatField() - memory_price = models.FloatField() - core_price = models.FloatField() - disk_size_price = models.FloatField() - cores = models.IntegerField() - memory = models.IntegerField() - disk_size = models.IntegerField() - - def __str__(self): - return "VM Type %s" % (self.id) - - @cached_property - def final_price(self): - price = self.cores * self.core_price - price += self.memory * self.memory_price - price += self.disk_size * self.disk_size_price - return price - - @classmethod - def get_serialized_vm_types(cls): - return [vm.get_serialized_data() - for vm in cls.objects.all()] - - def calculate_price(self): - price = self.cores * self.core_price - price += self.memory * self.memory_price - price += self.disk_size * self.disk_size_price - # price += self.base_price - return price - - # @classmethod - # def get_price(cls, vm_template): - # return cls.BASE_PRICE * vm_template - - def get_specs(self): - return { - 'memory': self.memory, - 'cores': self.cores, - 'disk_size': self.disk_size - } - - # def calculate_price(self, vm_template): - # price = self.base_price * vm_template - # return price - - # def defeault_price(self): - # price = self.base_price - # price += self.core_price - # price += self.memory_price - # price += self.disk_size_price * 10 - # return price - - def get_serialized_data(self): - return { - 'description': self.description, - 'core_price': self.core_price, - 'disk_size_price': self.disk_size_price, - 'memory_price': self.memory_price, - 'id': self.id, - 'final_price': self.final_price, - 'cores': self.cores, - 'memory': self.memory, - 'disk_size': self.disk_size - - } - - @classmethod - def get_vm_templates(self, user): - opennebula_client = OpenNebulaManager( - email=user.email, - password=user.password, - ) - - templates = opennebula_client.get_vm_templates() - for template in templates: - print(OpenNebulaManager.parse_vm(template)) - return templates - - -class VirtualMachinePlan(AssignPermissionsMixin, models.Model): - - PENDING_STATUS = 'pending' - ONLINE_STATUS = 'online' - CANCELED_STATUS = 'canceled' - - VM_STATUS_CHOICES = ( - (PENDING_STATUS, 'Pending for activation'), - (ONLINE_STATUS, 'Online'), - (CANCELED_STATUS, 'Canceled') - ) - - # DJANGO = 'django' - # RAILS = 'rails' - # NODEJS = 'nodejs' - - # VM_CONFIGURATION = ( - # (DJANGO, 'Ubuntu 14.04, Django'), - # (RAILS, 'Ubuntu 14.04, Rails'), - # (NODEJS, 'Debian, NodeJS'), - # ) - - VM_CONFIGURATION = ( - ('debian', 'Debian 8'), - ('ubuntu', 'Ubuntu 16.06'), - ('devuan', 'Devuan 1'), - ('centos', 'CentOS 7') - ) - - permissions = ('view_virtualmachineplan', - 'cancel_virtualmachineplan', - 'change_virtualmachineplan') - - cores = models.IntegerField() - memory = models.IntegerField() - disk_size = models.IntegerField() - vm_type = models.ForeignKey(VirtualMachineType, null=True) - price = models.FloatField() - public_key = models.TextField(blank=True) - status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS) - ip = models.CharField(max_length=50, blank=True) - configuration = models.CharField(max_length=20, choices=VM_CONFIGURATION) - opennebula_id = models.IntegerField(null=True) - - objects = VMPlansManager() - - class Meta: - permissions = ( - ('view_virtualmachineplan', 'View Virtual Machine Plan'), - ('cancel_virtualmachineplan', 'Cancel Virtual Machine Plan'), - ) - - def __str__(self): - return self.name - - # @cached_property - # def hosting_company_name(self): - # return self.vm_type.get_hosting_company_display() - - # @cached_property - # def location(self): - # return self.vm_type.get_location_display() - - @cached_property - def name(self): - name = 'vm-%s' % self.id - return name - - @cached_property - def notifications(self): - stripe_customer = StripeCustomer.objects.get(hostingorder__vm_plan=self) - backend = stored_messages_settings.STORAGE_BACKEND() - messages = backend.inbox_list(stripe_customer.user) - return messages - - @classmethod - def create(cls, data, user): - instance = cls.objects.create(**data) - instance.assign_permissions(user) - return instance - - def cancel_plan(self, vm_id): - self.status = self.CANCELED_STATUS - self.save(update_fields=['status']) - - @classmethod - def terminate_opennebula_vm(self, user, vm_id): - - opennebula_client = OpenNebulaManager( - user.email, - user.password, - ) - - return opennebula_client.terminate_vm(vm_id) - - - @classmethod - def create_opennebula_vm(self, user, specs): - # import pdb - # pdb.set_trace() - - - # Init opennebula manager using given user - opennebula_client = OpenNebulaManager( - user.email, - user.password, - ) - - # Create a vm in opennebula using given specs - vm = opennebula_client.create_vm(specs) - return vm - - @classmethod - def get_vm(self, user, vm_id): - # Get opennebula client - opennebula_client = OpenNebulaManager( - email=user.email, - password=user.password, - ) - - # Get vm given the id - vm = opennebula_client.get_vm( - user.email, - vm_id - ) - - # Parse vm data - vm_data = OpenNebulaManager.parse_vm(vm) - - return vm_data - - @classmethod - def get_vms(self, user): - - # Get opennebula client - opennebula_client = OpenNebulaManager( - email=user.email, - password=user.password, - ) - - # Get vm pool - vm_pool = opennebula_client.get_vms(user.email) - - # Reset total price - self.total_price = 0 - vms = [] - # Add vm in vm_pool to context - for vm in vm_pool: - vm_data = OpenNebulaManager.parse_vm(vm) - vms.append(vm_data) - # self.total_price += price - # self.save() - return vms - class HostingOrder(AssignPermissionsMixin, models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' - vm_plan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') + vm_id = models.IntegerField(default=0) customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) @@ -272,6 +37,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): last4 = models.CharField(max_length=4) cc_brand = models.CharField(max_length=10) stripe_charge_id = models.CharField(max_length=100, null=True) + price = models.FloatField() permissions = ('view_hostingorder',) @@ -288,9 +54,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model): return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS @classmethod - def create(cls, vm_plan=None, customer=None, billing_address=None): - instance = cls.objects.create(vm_plan=vm_plan, customer=customer, - billing_address=billing_address) + def create(cls, price=None, vm_id=None, customer=None, billing_address=None): + instance = cls.objects.create( + price=price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address + ) instance.assign_permissions(customer.user) return instance @@ -336,17 +106,6 @@ class UserHostingKey(models.Model): # self.save(update_fields=['public_key']) return private_key, public_key - -class ManageVM(models.Model): - def has_add_permission(self, request): - return False - - def has_delete_permission(self, request, obj=None): - return False - - class Meta: - managed = False - class HostingBill(AssignPermissionsMixin, models.Model): customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) @@ -367,32 +126,3 @@ class HostingBill(AssignPermissionsMixin, models.Model): instance = cls.objects.create(customer=customer, billing_address=billing_address) return instance - def get_vms(self): - email = self.customer.user.email - # Get opennebula client - opennebula_client = OpenNebulaManager(create_user=False) - - # Get vm pool - vm_pool = opennebula_client.get_vms(email) - - # Reset total price - self.total_price = 0 - vms = [] - # Add vm in vm_pool to context - for vm in vm_pool: - vm_data = OpenNebulaManager.parse_vm(vm) - self.total_price += vm_data['price'] - vms.append(vm_data) - self.save() - return vms - - - -def get_user_opennebula_password(): - ''' - TODO: Implement the way we obtain the user's opennebula password - ''' - pw = os.environ.get('OPENNEBULA_USER_PW') - if pw is None: - raise Exception("Define OPENNEBULA_USER_PW env variable") - return pw diff --git a/hosting/templates/hosting/bill_detail.html b/hosting/templates/hosting/bill_detail.html index 2d7f04b4..9e92d0e9 100644 --- a/hosting/templates/hosting/bill_detail.html +++ b/hosting/templates/hosting/bill_detail.html @@ -78,7 +78,7 @@
- {% trans "CH02 ............" %} + {% trans "CH02 0900 0000 6071 8848 8%}
{% trans "POFICHBEXXX" %} diff --git a/hosting/templates/hosting/create_virtual_machine.html b/hosting/templates/hosting/create_virtual_machine.html index 5d67b305..3a68421e 100644 --- a/hosting/templates/hosting/create_virtual_machine.html +++ b/hosting/templates/hosting/create_virtual_machine.html @@ -12,15 +12,19 @@ {% csrf_token %}
Select VM: - + {% for template in templates %} - + {% endfor %}
-
+
@@ -42,4 +46,4 @@
-{%endblock%} \ No newline at end of file +{%endblock%} diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 5eb9bac1..868680aa 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -49,17 +49,13 @@

{% trans "Order summary"%}


-

{% trans "Type"%} {{order.vm_plan.hosting_company_name}}

+

{% trans "Cores"%} {{vm.cores}}


-

{% trans "Configuration"%} {{order.vm_plan.get_configuration_display}}

+

{% trans "Memory"%} {{vm.memory}} GiB


-

{% trans "Cores"%} {{order.vm_plan.cores}}

+

{% trans "Disk space"%} {{vm.disk_size}} GiB


-

{% trans "Memory"%} {{order.vm_plan.memory}} GiB

-
-

{% trans "Disk space"%} {{order.vm_plan.disk_size}} GiB

-
-

{% trans "Total"%}

{{order.vm_plan.price}} CHF

+

{% trans "Total"%}

{{vm.price}} CHF


{% url 'hosting:payment' as payment_url %} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 90da3870..bb80d566 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -100,15 +100,14 @@
-

Cores {{request.session.vm_specs.cores}}

+

Cores {{request.session.template.cores}}


-

Configuration {{request.session.vm_specs.configuration_display}}

-
-

Memory {{request.session.vm_specs.memory}} GiB

+

Memory {{request.session.template.memory}} GiB


-

Disk space {{request.session.vm_specs.disk_size}} GiB

+

Disk space {{request.session.template.disk_size}} GiB


-

Total

{{request.session.vm_specs.final_price}} CHF

+

Total

{{request.session.template.price }} CHF

diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 04c50279..e2f38d60 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -25,12 +25,6 @@ {% trans "Billing"%} -
  • - - - {% trans "Orders"%} - -
  • {% trans "Status"%} @@ -109,56 +103,22 @@ -
    -
    -
    - -

    Orders

    -
    - - - - - - - - - - - {% for order in virtual_machine.hosting_orders.all %} - - - - - - - - {% endfor %} - -
    #{% trans "Date"%}{% trans "Amount"%}{% trans "Status"%}
    {{order.id}}{{order.created_at}}{{order.vm_plan.price}} CHF{% if order.approved %} - {% trans "Approved"%} - {% else%} - {% trans "Declined"%} - {% endif%} - - -
    -
    -
    -

    {% trans "Current status"%}

    - {% if virtual_machine.state == 'ACTIVE' %} - {{virtual_machine.state}} - {% elif virtual_machine.state == 'POWEROFF' %} - {{virtual_machine.state}} - {% else %} - {{virtual_machine.state}} - {% endif %} + {% if virtual_machine.state == 'PENDING' %} + Pending + {% elif virtual_machine.state == 'ACTIVE' %} + Online + {% elif virtual_machine.state == 'FAILED'%} + Failed + {% endif %}
    @@ -166,11 +126,12 @@
    -{%endblock%} \ No newline at end of file +{%endblock%} diff --git a/hosting/views.py b/hosting/views.py index 8f76c167..b292ce45 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -20,16 +20,18 @@ from stored_messages.models import Message from stored_messages.api import mark_read - from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils from utils.forms import BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin from utils.mailer import BaseEmail -from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill, UserHostingKey +from .models import HostingOrder, HostingBill, UserHostingKey from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm from .mixins import ProcessVMSelectionMixin -from .opennebula_functions import HostingManageVMAdmin, OpenNebulaManager + +from opennebula_api.models import OpenNebulaManager +from opennebula_api.serializers import VirtualMachineSerializer,\ + VirtualMachineTemplateSerializer from oca.exceptions import OpenNebulaException from oca.pool import WrongNameError @@ -387,57 +389,26 @@ class PaymentVMView(LoginRequiredMixin, FormView): context = self.get_context_data() - specifications = request.session.get('vm_specs') + specifications = request.session.get('template') - vm_template = specifications.get('vm_template', 1) + vm_template_id = specifications.get('id', 1) - vm_type = VirtualMachineType.objects.get(id=vm_template) - - specs = vm_type.get_specs() - - final_price = vm_type.calculate_price() - - plan_data = { - 'vm_type': vm_type, - 'configuration': specifications.get( - 'configuration', - 'django' - ), - 'price': final_price - } - - plan_data.update(specs) + final_price = specifications.get('price', 1) token = form.cleaned_data.get('token') + owner = self.request.user + # Get or create stripe customer - customer = StripeCustomer.get_or_create(email=self.request.user.email, + customer = StripeCustomer.get_or_create(email=owner.email, token=token) if not customer: form.add_error("__all__", "Invalid credit card") return self.render_to_response(self.get_context_data(form=form)) - # Create Virtual Machine Plan - plan = VirtualMachinePlan.create(plan_data, request.user) - # Create Billing Address billing_address = form.save() - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm(billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Create a Hosting Order - order = HostingOrder.create(vm_plan=plan, customer=customer, - billing_address=billing_address) - # Create a Hosting Bill - bill = HostingBill.create(customer=customer, billing_address=billing_address) - # Make stripe charge to a customer stripe_utils = StripeUtils() charge_response = stripe_utils.make_charge(amount=final_price, @@ -454,11 +425,11 @@ class PaymentVMView(LoginRequiredMixin, FormView): charge = charge_response.get('response_object') - # Associate an order with a stripe payment - order.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - order.set_approved() + # Create OpenNebulaManager + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + template = manager.get_template(vm_template_id) # Get user ssh key try: @@ -466,26 +437,46 @@ class PaymentVMView(LoginRequiredMixin, FormView): user=self.request.user ) - # Add ssh_key to specs - specs.update({ - 'ssh_key': user_key.public_key - }) - except UserHostingKey.DoesNotExist: pass # Create a vm using logged user - opennebula_vm_id = VirtualMachinePlan.create_opennebula_vm( - self.request.user, - specs + vm_id = manager.create_vm( + vm_template_id, + ssh_key=user_key.public_key ) - plan.opennebula_id = opennebula_vm_id - plan.save() + # Create a Hosting Order + order = HostingOrder.create( + price=final_price, + vm_id=vm_id, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + bill = HostingBill.create(customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm(billing_address_data) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe payment + order.set_stripe_charge(charge) + + # If the Stripe payment was successed, set order status approved + order.set_approved() + + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data # Send notification to ungleich as soon as VM has been booked context = { - 'vm': plan, + 'vm': vm, 'order': order, 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) @@ -512,6 +503,18 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai permission_required = ['view_hostingorder'] model = HostingOrder + def get_context_data(self, **kwargs): + # Get context + context = super(DetailView, self).get_context_data(**kwargs) + obj = self.get_object() + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + vm = manager.get_vm(obj.vm_id) + context['vm'] = VirtualMachineSerializer(vm).data + return context + class OrdersHostingListView(LoginRequiredMixin, ListView): template_name = "hosting/orders.html" @@ -537,24 +540,17 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): template_name = "hosting/virtual_machines.html" login_url = reverse_lazy('hosting:login') context_object_name = "vms" - model = VirtualMachinePlan paginate_by = 10 ordering = '-id' - def get_context_data(self, **kwargs): - context = super(VirtualMachinesPlanListView, self).get_context_data(**kwargs) - context.update({ - 'vms_opennebula': VirtualMachinePlan.get_vms(self.request.user) - }) - return context - def get_queryset(self): - # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) - # print(hosting_admin.show_vms_view(self.request)) - # print(VirtualMachinePlan.get_vms(self.request.user.)) - user = self.request.user - self.queryset = VirtualMachinePlan.objects.active(user) - return super(VirtualMachinesPlanListView, self).get_queryset() + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + queryset = manager.get_vms() + serializer = VirtualMachineSerializer(queryset, many=True) + return serializer.data class CreateVirtualMachinesView(LoginRequiredMixin, View): @@ -574,107 +570,70 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): ) return HttpResponseRedirect(reverse('hosting:key_pair')) + #TODO: Replace with OpenNebulaManager.get_apps + templates = OpenNebulaManager().get_templates() + data = VirtualMachineTemplateSerializer(templates, many=True).data + context = { - 'vm_types': VirtualMachineType.get_serialized_vm_types(), - 'configuration_options': VirtualMachinePlan.VM_CONFIGURATION + 'templates': data, } # context = {} return render(request, self.template_name, context) def post(self, request): - configuration = request.POST.get('configuration') - configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) - vm_template = request.POST.get('vm_template') - vm_type = VirtualMachineType.objects.get(id=vm_template) - vm_specs = vm_type.get_specs() - vm_specs.update({ - 'configuration_display': configuration_display, - 'configuration': configuration, - 'final_price': vm_type.final_price, - 'vm_template': vm_template - }) - request.session['vm_specs'] = vm_specs + template_id = int(request.POST.get('vm_template_id')) + template = OpenNebulaManager().get_template(template_id) + data = VirtualMachineTemplateSerializer(template).data + request.session['template'] = data return redirect(reverse('hosting:payment')) - # def get_queryset(self): - # # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) - # # print(hosting_admin.show_vms(self.request)) - # user = self.request.user - # self.queryset = VirtualMachinePlan.objects.active(user) - # return super(VirtualMachinesPlanListView, self).get_queryset() - -class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, View): +class VirtualMachineView(LoginRequiredMixin, View): template_name = "hosting/virtual_machine_detail.html" login_url = reverse_lazy('hosting:login') - # model = VirtualMachinePlan - # context_object_name = "virtual_machine" - permission_required = [] - # fields = '__all__' - - # def get_context_data(self, **kwargs): - # vm_plan = get_object() - # context = super(VirtualMachineView, self).get_context_data(**kwargs) - # context.update({ - # 'opennebula_vm': VirtualMachinePlan.get_vm( - # self.request.user.email, - # opennebula_id - # ) - # }) - # return context - - # def get_object(self, queryset=None): - # # if queryset is None: - # # queryset = self.get_queryset() - # # Next, try looking up by primary key. - # vm_id = self.kwargs.get(self.pk_url_kwarg) - # try: - # return VirtualMachinePlan.get_vm( - # self.request.user.email, - # vm_id - # ) - # except Exception as error: - # raise Http404() def get_object(self): - opennebula_vm_id = self.kwargs.get('pk') - opennebula_vm = None + owner = self.request.user + vm = None + manager = OpenNebulaManager( + email=owner.email, + password=owner.password, + create_user=True + ) + vm_id = self.kwargs.get('pk') try: - opennebula_vm = VirtualMachinePlan.objects.get(opennebula_id=opennebula_vm_id) + vm = manager.get_vm(vm_id) except Exception as error: print(error) raise Http404() - return opennebula_vm + return vm def get_success_url(self): final_url = reverse('hosting:virtual_machines') return final_url def get(self, request, *args, **kwargs): - opennebula_vm_id = self.kwargs.get('pk') - try: - opennebula_vm = VirtualMachinePlan.get_vm( - self.request.user, - opennebula_vm_id - ) - except Exception as error: - print(error) - raise Http404() - + vm = self.get_object() + serializer = VirtualMachineSerializer(vm) context = { - 'virtual_machine': opennebula_vm, + 'virtual_machine': serializer.data, } - # context = {} return render(request, self.template_name, context) def post(self, request, *args, **kwargs): + owner = self.request.user vm = self.get_object() opennebula_vm_id = self.kwargs.get('pk') - terminated = VirtualMachinePlan.terminate_opennebula_vm( - self.request.user, - opennebula_vm_id + manager = OpenNebulaManager( + email=owner.email, + password=owner.password, + create_user=True + ) + + terminated = manager.delete_vm( + vm.id ) if not terminated: @@ -684,8 +643,6 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, View): ) return HttpResponseRedirect(self.get_success_url()) - #vm.cancel_plan() - context = { 'vm': vm, 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) @@ -735,10 +692,14 @@ class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailV def get_context_data(self, **kwargs): # Get context context = super(DetailView, self).get_context_data(**kwargs) + + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) # Get vms - try: - context['vms'] = self.get_object().get_vms() - except: - pass + queryset = manager.get_vms() + vms = VirtualMachineSerializer(queryset, many=True).data + context['vms'] = vms return context diff --git a/opennebula_api/migrations/0001_initial.py b/opennebula_api/migrations/0001_initial.py deleted file mode 100644 index 78747cd2..00000000 --- a/opennebula_api/migrations/0001_initial.py +++ /dev/null @@ -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'), - ), - ] diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 6a7ba2fc..8c7743b4 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -1,110 +1,14 @@ import oca import socket +import logging + -from django.db import models from django.conf import settings +from django.utils.functional import cached_property from oca.pool import WrongNameError - -class VirtualMachineTemplate(models.Model): - """This class represents an opennebula template.""" - opennebula_id = models.IntegerField() - base_price = models.FloatField() - memory_price = models.FloatField() - core_price = models.FloatField() - disk_size_price = models.FloatField() - - def calculate_price(self): - template = OpenNebulaManager()._get_template(self.opennebula_id).template - - price = int(template.vcpu) * self.core_price - price += int(template.memory) / 1024 * self.memory_price - try: - price += int(template.disk.size) / 1024 * self.disk_size_price - except AttributeError: - for disk in template.disks: - price += int(disk.size) / 1024 * self.disk_size_price - return price - - def get_name(self): - - template = OpenNebulaManager()._get_template(template_id=self.opennebula_id) - return template.name - - def get_cores(self): - - template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template - return int(template.vcpu) - - def get_disk_size(self): - - template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template - disk_size = 0 - for disk in template.disks: - disk_size += int(disk.size) - return disk_size / 1024 - - def get_memory(self): - - template = OpenNebulaManager()._get_template(template_id=self.opennebula_id).template - return int(template.memory) / 1024 - -class VirtualMachine(models.Model): - """This class represents an opennebula virtual machine.""" - opennebula_id = models.IntegerField() - vm_template = models.ForeignKey(VirtualMachineTemplate) - - VM_STATE = { - '0': 'INIT', - '1': 'PENDING', - '2': 'HOLD', - '3': 'ACTIVE', - '4': 'STOPPED', - '5': 'SUSPENDED', - '6': 'DONE', - '8': 'POWEROFF', - '9': 'UNDEPLOYED', - '10': 'CLONING', - '11': 'CLONING_FAILURE', - } - - def get_name(self): - - vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) - return vm.name - - def get_cores(self): - - return self.vm_template.get_cores() - - def get_disk_size(self): - - return self.vm_template.get_disk_size() - - def get_memory(self): - - return self.vm_template.get_memory() - - def get_id(self): - - vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) - return vm.id - - def get_ip(self): - - vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) - try: - return vm.user_template.ungleich_public_ip - except AttributeError: - return '-' - - def get_state(self): - - vm = OpenNebulaManager()._get_vm(vm_id=self.opennebula_id) - return self.VM_STATE.get(str(vm.state)) - - def get_price(self): - return self.vm_template.calculate_price() +from oca.exceptions import OpenNebulaException +logger = logging.getLogger(__name__) class OpenNebulaManager(): """This class represents an opennebula manager.""" @@ -175,8 +79,13 @@ class OpenNebulaManager(): def _get_vm_pool(self): try: - vm_pool = oca.VirtualMachinePool(self.oneadmin_client) - vm_pool.info() + vm_pool = oca.VirtualMachinePool(self.client) + vm_pool.info() + except AttributeError: + print('Could not connect via client, using oneadmin instead') + vm_pool = oca.VirtualMachinePool(self.oneadmin_client) + vm_pool.info(filter=-2) + #TODO: Replace with logger except ConnectionRefusedError: print('Could not connect to host: {host} via protocol {protocol}'.format( @@ -186,19 +95,25 @@ class OpenNebulaManager(): raise ConnectionRefusedError return vm_pool + def get_vms(self): + return self._get_vm_pool() - def _get_vm(self, vm_id): + def get_vm(self, vm_id): vm_pool = self._get_vm_pool() - # Get virtual machines from all users - vm_pool.info(filter=-2) - return vm_pool.get_by_id(vm_id) + return vm_pool.get_by_id(int(vm_id)) - def create_vm(self, template_id): - template_pool = self._get_template_pool() - - template = template_pool.get_by_id(template_id) - - vm_id = template.instantiate() + #TODO: get app with id + def create_vm(self, template_id, app_id=None, ssh_key=None): + extra_template = "{ssh_key}".format( + ssh_key=ssh_key + ) + vm_id = self.oneadmin_client.call( + oca.VmTemplate.METHODS['instantiate'], + template_id, + '', + False, + extra_template + ) try: self.oneadmin_client.call( oca.VirtualMachine.METHODS['chown'], @@ -207,12 +122,33 @@ class OpenNebulaManager(): self.opennebula_user.group_ids[0] ) except AttributeError: - pass + print('Could not change owner, opennebula_user is not set.') return vm_id def delete_vm(self, vm_id): - vm = self._get_vm(vm_id) - vm.delete() + TERMINATE_ACTION = 'terminate' + vm_terminated = False + try: + self.oneadmin_client.call( + oca.VirtualMachine.METHODS['action'], + TERMINATE_ACTION, + int(vm_id), + ) + vm_terminated = True + except socket.timeout as socket_err: + logger.info("Socket timeout error: {0}".format(socket_err)) + print("Socket timeout error: {0}".format(socket_err)) + except OpenNebulaException as opennebula_err: + logger.info("OpenNebulaException error: {0}".format(opennebula_err)) + print("OpenNebulaException error: {0}".format(opennebula_err)) + except OSError as os_err: + logger.info("OSError : {0}".format(os_err)) + print("OSError : {0}".format(os_err)) + except ValueError as value_err: + logger.info("ValueError : {0}".format(value_err)) + print("ValueError : {0}".format(value_err)) + + return vm_terminated def _get_template_pool(self): try: @@ -227,19 +163,31 @@ class OpenNebulaManager(): raise ConnectionRefusedError return template_pool - def _get_template(self, template_id): + def get_templates(self): + public_templates = [ + template + for template in self._get_template_pool() + if 'public-' in template.name + ] + return public_templates + def get_template(self, template_id): template_pool = self._get_template_pool() return template_pool.get_by_id(template_id) - def create_template(self, name, cores, memory, disk_size): + def create_template(self, name, cores, memory, disk_size, core_price, memory_price, + disk_size_price, ssh='' ): """Create and add a new template to opennebula. :param name: A string representation describing the template. Used as label in view. :param cores: Amount of virtual cpu cores for the VM. - :param memory: Amount of RAM for the VM (MB) - :param disk_size: Amount of disk space for VM (MB) + :param memory: Amount of RAM for the VM (GB) + :param disk_size: Amount of disk space for VM (GB) + :param core_price: Price of virtual cpu for the VM per core. + :param memory_price: Price of RAM for the VM per GB + :param disk_size_price: Price of disk space for VM per GB + :param ssh: User public ssh key """ template_string_formatter = """ """ template_id = oca.VmTemplate.allocate( @@ -260,7 +212,12 @@ class OpenNebulaManager(): vcpu=cores, cpu=0.1*cores, size=1024 * disk_size, - memory=1024 * memory + memory=1024 * memory, + # * 10 because we set cpu to *0.1 + cpu_cost=10*core_price, + memory_cost=memory_price, + disk_cost=disk_size_price, + ssh=ssh ) ) @@ -269,5 +226,3 @@ class OpenNebulaManager(): def delete_template(self, template_id): self.oneadmin_client.call(oca.VmTemplate.METHODS['delete'], template_id, False) - - diff --git a/opennebula_api/serializers.py b/opennebula_api/serializers.py index e8ff33bd..b73682e6 100644 --- a/opennebula_api/serializers.py +++ b/opennebula_api/serializers.py @@ -3,91 +3,117 @@ import oca from rest_framework import serializers from oca import OpenNebulaException +from oca.template import VmTemplate -from .models import VirtualMachine, VirtualMachineTemplate, OpenNebulaManager +from .models import OpenNebulaManager -class VirtualMachineTemplateSerializer(serializers.ModelSerializer): +class VirtualMachineTemplateSerializer(serializers.Serializer): """Serializer to map the virtual machine template instance into JSON format.""" - cores = serializers.IntegerField(source='get_cores') - name = serializers.CharField(source='get_name') - disk_size = serializers.IntegerField(source='get_disk_size') - memory = serializers.IntegerField(source='get_memory') + id = serializers.IntegerField(read_only=True) + name = serializers.CharField() + cores = serializers.IntegerField(source='template.vcpu') + disk = serializers.IntegerField(write_only=True) + disk_size = serializers.SerializerMethodField() + memory = serializers.SerializerMethodField() + core_price = serializers.FloatField(source='template.cpu_cost') + disk_size_price = serializers.FloatField(source='template.disk_cost') + memory_price = serializers.FloatField(source='template.memory_cost') + price = serializers.SerializerMethodField() - class Meta: - model = VirtualMachineTemplate - fields = ('id', 'name', 'cores', 'memory', 'disk_size', 'base_price', - 'core_price', 'memory_price', 'disk_size_price', 'opennebula_id') - read_only_fields = ('opennebula_id', ) - - def validate(self, data): - # Create the opennebula model - cores = data.pop('get_cores') - name = data.pop('get_name') - disk_size = data.pop('get_disk_size') - memory = data.pop('get_memory') + def create(self, validated_data): + data = validated_data + template = data.pop('template') + cores = template.pop('vcpu') + name = data.pop('name') + disk_size = data.pop('disk') + memory = template.pop('memory') + core_price = template.pop('cpu_cost') + memory_price = template.pop('memory_cost') + disk_size_price = template.pop('disk_cost') manager = OpenNebulaManager(create_user = False) try: opennebula_id = manager.create_template(name=name, cores=cores, memory=memory, - disk_size=disk_size) - data.update({'opennebula_id':opennebula_id}) + disk_size=disk_size, + core_price=core_price, + disk_size_price=disk_size_price, + memory_price=memory_price) except OpenNebulaException as err: raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) - return data + return manager.get_template(template_id=opennebula_id) - def create(self, validated_data): - return VirtualMachineTemplate.objects.create(**validated_data) + def get_disk_size(self, obj): + template = obj.template + disk_size = 0 + for disk in template.disks: + disk_size += int(disk.size) + return disk_size / 1024 -class TemplatePrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): - def display_value(self, instance): - return 'Template: {}'.format(instance.get_name()) + def get_price(self, obj): + template = obj.template + price = float(template.cpu) * float(template.cpu_cost) + price += (int(template.memory)/1024 * float(template.memory_cost)) + for disk in template.disks: + price += int(disk.size)/1024 * float(template.disk_cost) + return price -class VirtualMachineSerializer(serializers.ModelSerializer): + def get_memory(self, obj): + return int(obj.template.memory)/1024 + +class VirtualMachineSerializer(serializers.Serializer): """Serializer to map the virtual machine instance into JSON format.""" - #TODO: Maybe we can change to template.get_cores - cores = serializers.IntegerField(read_only=True, source='get_cores') - name = serializers.CharField(read_only=True, source='get_name') - disk_size = serializers.IntegerField(read_only=True, source='get_disk_size') - memory = serializers.IntegerField(read_only=True, source='get_memory') - #TODO: See if we can change to IPAddressField - ip = serializers.CharField(read_only=True, source='get_ip') - deploy_id = serializers.IntegerField(read_only=True, source='get_deploy_id') - vm_id = serializers.IntegerField(read_only=True, source='get_vm_id') - state = serializers.CharField(read_only=True, source='get_state') - price = serializers.FloatField(read_only=True, source='get_price') + name = serializers.CharField(read_only=True) + cores = serializers.IntegerField(read_only=True, source='template.vcpu') - vm_template = VirtualMachineTemplateSerializer(read_only=True) + disk_size = serializers.SerializerMethodField() + memory = serializers.SerializerMethodField() + ip = serializers.CharField(read_only=True, + source='user_template.ungleich_public_ip', + default='-') + vm_id = serializers.IntegerField(read_only=True, source='id') + state = serializers.CharField(read_only=True, source='str_state') + price = serializers.SerializerMethodField() - vm_template_id = TemplatePrimaryKeyRelatedField( - queryset=VirtualMachineTemplate.objects.all(), - source='vm_template' + template_id = serializers.ChoiceField( + choices=[(key.id, key.name) for key in + OpenNebulaManager().get_templates()], + source='template.template_id', + write_only=True ) - class Meta: - model = VirtualMachine - fields = ('id', 'opennebula_id', 'vm_template', 'vm_template_id', 'cores', 'name', - 'disk_size', 'memory', 'ip', 'deploy_id', 'state', 'vm_id', - 'price') - read_only_fields = ('opennebula_id', ) + def create(self, validated_data): + owner = validated_data['owner'] + template_id = validated_data['template']['template_id'] - def validate(self, data): - # Create the opennebula model - manager = OpenNebulaManager(create_user = False) - try: - template_id = data['vm_template'].opennebula_id + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user = True) opennebula_id = manager.create_vm(template_id) - data.update({'opennebula_id':opennebula_id}) except OpenNebulaException as err: raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) - return data + return manager.get_vm(opennebula_id) - def create(self, validated_data): - return VirtualMachine.objects.create(**validated_data) + def get_memory(self, obj): + return int(obj.template.memory)/1024 + def get_disk_size(self, obj): + template = obj.template + disk_size = 0 + for disk in template.disks: + disk_size += int(disk.size) + return disk_size / 1024 + + def get_price(self, obj): + template = obj.template + price = float(template.cpu) * float(template.cpu_cost) + price += (int(template.memory)/1024 * float(template.memory_cost)) + for disk in template.disks: + price += int(disk.size)/1024 * float(template.disk_cost) + return price diff --git a/opennebula_api/urls.py b/opennebula_api/urls.py new file mode 100644 index 00000000..be5bdbf2 --- /dev/null +++ b/opennebula_api/urls.py @@ -0,0 +1,18 @@ +from django.conf.urls import url, include +from rest_framework.urlpatterns import format_suffix_patterns +from .views import TemplateCreateView, TemplateDetailsView,\ + VmCreateView, VmDetailsView + +urlpatterns = { + url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), + + url(r'^templates/$', TemplateCreateView.as_view(), name="template_create"), + url(r'^templates/(?P[0-9]+)/$', TemplateDetailsView.as_view(), + name="templates_details"), + + url(r'^vms/$', VmCreateView.as_view(), name="vm_create"), + url(r'^vms/(?P[0-9]+)/$', VmDetailsView.as_view(), + name="vm_details"), +} + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/opennebula_api/views.py b/opennebula_api/views.py index 4cd237ae..20a28968 100644 --- a/opennebula_api/views.py +++ b/opennebula_api/views.py @@ -1,13 +1,29 @@ from rest_framework import generics +from rest_framework import permissions + +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth import authenticate, login + + +from utils.views import LoginViewMixin +from membership.models import CustomUser, StripeCustomer +from guardian.mixins import PermissionRequiredMixin from .serializers import VirtualMachineTemplateSerializer, \ VirtualMachineSerializer -from .models import VirtualMachineTemplate, VirtualMachine, OpenNebulaManager +from .models import OpenNebulaManager + class TemplateCreateView(generics.ListCreateAPIView): - """This class defines the create behavior of our rest api.""" - queryset = VirtualMachineTemplate.objects.all() + """This class handles the GET and POST requests.""" + serializer_class = VirtualMachineTemplateSerializer + permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser) + + def get_queryset(self): + manager = OpenNebulaManager() + return manager.get_templates() + def perform_create(self, serializer): """Save the post data when creating a new template.""" @@ -16,20 +32,53 @@ class TemplateCreateView(generics.ListCreateAPIView): class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView): """This class handles the http GET, PUT and DELETE requests.""" - queryset = VirtualMachineTemplate.objects.all() serializer_class = VirtualMachineTemplateSerializer + permission_classes = (permissions.IsAuthenticated) + + def get_queryset(self): + manager = OpenNebulaManager() + return manager.get_templates() class VmCreateView(generics.ListCreateAPIView): - """This class defines the create behavior of our rest api.""" - queryset = VirtualMachine.objects.all() + """This class handles the GET and POST requests.""" serializer_class = VirtualMachineSerializer + permission_classes = (permissions.IsAuthenticated, ) + + def get_queryset(self): + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + return manager.get_vms() def perform_create(self, serializer): """Save the post data when creating a new template.""" - serializer.save() + serializer.save(owner=self.request.user) class VmDetailsView(generics.RetrieveUpdateDestroyAPIView): """This class handles the http GET, PUT and DELETE requests.""" + permission_classes = (permissions.IsAuthenticated, ) - queryset = VirtualMachine.objects.all() serializer_class = VirtualMachineSerializer + + def get_queryset(self): + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + return manager.get_vms() + + def get_object(self): + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user=True) + return manager.get_vm(self.kwargs.get('pk')) + + def perform_destroy(self, instance): + owner = self.request.user + manager = OpenNebulaManager(email=owner.email, + password=owner.password, + create_user = True) + manager.delete_vm(instance.id) +