From a45679d89a3c97da8e046f4b7464645a550de6b1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:00:45 +0530 Subject: [PATCH 1/9] Added VMDetail model --- hosting/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index 478ed745..73c082bb 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model): instance = cls.objects.create(customer=customer, billing_address=billing_address) return instance + + +class VMDetail(models.Model): + user = models.ForeignKey(CustomUser) + vm_id = models.IntegerField(default=0) + disk_size = models.FloatField(default=0.0) + cores = models.FloatField(default=0.0) + memory = models.FloatField(default=0.0) + configuration = models.CharField(default='', max_length=25) + ipv4 = models.TextField(default='') + ipv6 = models.TextField(default='') + created_at = models.DateTimeField(auto_now_add=True) + terminated_at = models.DateTimeField(null=True) From f4899ffc184a849f4a56b4796667ad19ae0b5c9e Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:01:48 +0530 Subject: [PATCH 2/9] Added missing get_language import --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index d7c4c168..7ce66e57 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -16,7 +16,7 @@ from django.http import Http404, HttpResponseRedirect, HttpResponse from django.shortcuts import redirect, render from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import get_language, ugettext_lazy as _ from django.utils.translation import ugettext from django.views.generic import ( View, CreateView, FormView, ListView, DetailView, DeleteView, From 7278a201358dbc4c00c5cbefb860d35c0d63d571 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:02:36 +0530 Subject: [PATCH 3/9] Added get_or_create_vm_detail function --- datacenterlight/tasks.py | 5 +++-- utils/hosting_utils.py | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 1335869b..fd83a320 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -9,11 +9,11 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder, HostingBill, VMDetail from membership.models import StripeCustomer, CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from utils.hosting_utils import get_all_public_keys +from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.forms import UserBillingAddressForm from utils.mailer import BaseEmail from utils.models import BillingAddress @@ -174,6 +174,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) if new_host is not None: custom_user = CustomUser.objects.get(email=user.get('email')) + get_or_create_vm_detail(custom_user, manager, vm_id) if custom_user is not None: public_keys = get_all_public_keys(custom_user) keys = [{'value': key, 'state': True} for key in diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 7c1a83ad..7faf2a29 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -1,5 +1,10 @@ -from hosting.models import UserHostingKey +import logging +from oca.pool import WrongIdError +from hosting.models import UserHostingKey, VMDetail +from opennebula_api.serializers import VirtualMachineSerializer + +logger = logging.getLogger(__name__) def get_all_public_keys(customer): """ @@ -9,3 +14,35 @@ def get_all_public_keys(customer): """ return UserHostingKey.objects.filter(user_id=customer.id).values_list( "public_key", flat=True) + + +def get_or_create_vm_detail(user, manager, vm_id): + """ + Returns VMDetail object related to given vm_id. Creates the object + if it does not exist + + :param vm_id: The ID of the VM which should be greater than 0. + :param user: The CustomUser object that owns this VM + :param manager: The OpenNebulaManager object + :return: The VMDetail object. None if vm_id is less than or equal to 0. + Also, for the cases where the VMDetail does not exist and we can not + fetch data about the VM from OpenNebula, the function returns None + """ + if vm_id <= 0: + return None + try: + vm_detail_obj = VMDetail.objects.get(vm_id=vm_id) + except VMDetail.DoesNotExist: + try: + vm_obj = manager.get_vm(vm_id) + except (WrongIdError, ConnectionRefusedError) as e: + logger.error(str(e)) + return None + vm = VirtualMachineSerializer(vm_obj).data + vm_detail_obj = VMDetail.objects.create( + user=user, vm_id=vm_id, disk_size=vm['disk_size'], + cores=vm['cores'], memory=vm['memory'], + configuration=vm['configuration'], ipv4=vm['ipv4'], + ipv6=vm['ipv6'] + ) + return vm_detail_obj From f2f2fc22df5aae4a0f787ecc20ed4f38bdcb4a1e Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:30:28 +0530 Subject: [PATCH 4/9] Update VMDetail's terminated_at on delete of a vm --- hosting/views.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 7ce66e57..19c02979 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,6 +1,7 @@ import json import logging import uuid +from datetime import datetime from time import sleep from django import forms @@ -43,7 +44,9 @@ from utils.views import ( from .forms import HostingUserSignupForm, HostingUserLoginForm, \ UserHostingKeyForm, generate_ssh_key_name from .mixins import ProcessVMSelectionMixin -from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey +from .models import ( + HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail +) from datacenterlight.models import VMTemplate @@ -1043,6 +1046,9 @@ class VirtualMachineView(LoginRequiredMixin, View): except WrongIdError: response['status'] = True response['text'] = ugettext('Terminated') + vm_detail_obj = VMDetail.objects.filter(vm_id=opennebula_vm_id).first() + vm_detail_obj.terminated_at = datetime.utcnow() + vm_detail_obj.save() break except BaseException: break From 114511e9240762c283d5452783d494e0fc3bb21f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:34:18 +0530 Subject: [PATCH 5/9] Fixed some flake8 warnings --- datacenterlight/tasks.py | 4 ++-- utils/hosting_utils.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index fd83a320..a5496d83 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -9,7 +9,7 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill, VMDetail +from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer, CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer @@ -142,7 +142,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, email.send() if 'pass' in user: - lang = 'en-us' + lang = 'en-us' if user.get('language') is not None: logger.debug("Language is set to {}".format(user.get('language'))) lang = user.get('language') diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index 7faf2a29..09f81536 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -6,6 +6,7 @@ from opennebula_api.serializers import VirtualMachineSerializer logger = logging.getLogger(__name__) + def get_all_public_keys(customer): """ Returns all the public keys of the user From 83ccb9cffae5475e3b8626a0e90dbe07b2c5a29f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:36:33 +0530 Subject: [PATCH 6/9] Added vmdetail migration file --- hosting/migrations/0043_vmdetail.py | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 hosting/migrations/0043_vmdetail.py diff --git a/hosting/migrations/0043_vmdetail.py b/hosting/migrations/0043_vmdetail.py new file mode 100644 index 00000000..66966233 --- /dev/null +++ b/hosting/migrations/0043_vmdetail.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2017-09-24 18:12 +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), + ('hosting', '0042_hostingorder_subscription_id'), + ] + + operations = [ + migrations.CreateModel( + name='VMDetail', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('vm_id', models.IntegerField(default=0)), + ('disk_size', models.FloatField(default=0.0)), + ('cores', models.FloatField(default=0.0)), + ('memory', models.FloatField(default=0.0)), + ('configuration', models.CharField(default='', max_length=25)), + ('ipv4', models.TextField(default='')), + ('ipv6', models.TextField(default='')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('terminated_at', models.DateTimeField(null=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] From f2213305f61fff09509d4f82dd0b5db875ec9edd Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 00:46:39 +0530 Subject: [PATCH 7/9] Reformatted hosting views.py --- hosting/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 19c02979..77bf05b7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -49,7 +49,6 @@ from .models import ( ) from datacenterlight.models import VMTemplate - logger = logging.getLogger(__name__) CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \ @@ -1046,7 +1045,8 @@ class VirtualMachineView(LoginRequiredMixin, View): except WrongIdError: response['status'] = True response['text'] = ugettext('Terminated') - vm_detail_obj = VMDetail.objects.filter(vm_id=opennebula_vm_id).first() + vm_detail_obj = VMDetail.objects.filter( + vm_id=opennebula_vm_id).first() vm_detail_obj.terminated_at = datetime.utcnow() vm_detail_obj.save() break From 7fcece40c1d986c66380509d55f250aca06fd125 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 01:49:00 +0530 Subject: [PATCH 8/9] Using VMDetail model to show order details --- hosting/views.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 77bf05b7..577715ca 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -680,25 +680,29 @@ class OrdersHostingDetailView(LoginRequiredMixin, if obj is not None: # invoice for previous order try: - manager = OpenNebulaManager( - email=owner.email, password=owner.password - ) - vm = manager.get_vm(obj.vm_id) - context['vm'] = VirtualMachineSerializer(vm).data - except WrongIdError: - messages.error( - self.request, - _('The VM you are looking for is unavailable at the ' - 'moment. Please contact Data Center Light support.') - ) - self.kwargs['error'] = 'WrongIdError' - context['error'] = 'WrongIdError' - except ConnectionRefusedError: - messages.error( - self.request, - _('In order to create a VM, you need to create/upload ' - 'your SSH KEY first.') - ) + vm_detail = VMDetail.objects.get(vm_id=obj.vm_id) + context['vm'] = vm_detail.__dict__ + except VMDetail.DoesNotExist: + try: + manager = OpenNebulaManager( + email=owner.email, password=owner.password + ) + vm = manager.get_vm(obj.vm_id) + context['vm'] = VirtualMachineSerializer(vm).data + except WrongIdError: + messages.error( + self.request, + _('The VM you are looking for is unavailable at the ' + 'moment. Please contact Data Center Light support.') + ) + self.kwargs['error'] = 'WrongIdError' + context['error'] = 'WrongIdError' + except ConnectionRefusedError: + messages.error( + self.request, + _('In order to create a VM, you need to create/upload ' + 'your SSH KEY first.') + ) elif not card_details.get('response_object'): # new order, failed to get card details context['failed_payment'] = True From 46d82268f41c002a6b6d74fad89ae62db8f7efbe Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 25 Sep 2017 02:04:40 +0530 Subject: [PATCH 9/9] Show terminated in invoice for VM that has been terminated --- hosting/templates/hosting/order_detail.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 345632d2..ece84a9e 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -42,7 +42,9 @@

{% trans "Status" %}: - {% if order.status == 'Approved' %} + {% if vm.terminated_at %} + {% trans "Terminated" %} + {% elif order.status == 'Approved' %} {% trans "Approved" %} {% else %} {% trans "Declined" %} @@ -202,4 +204,4 @@ -{% endblock js_extra %} \ No newline at end of file +{% endblock js_extra %}