Merge pull request #489 from pcoder/task/3772/hosting_billing_monthly_subscription
Task/3772/hosting billing monthly subscription
This commit is contained in:
		
				commit
				
					
						2223bffa04
					
				
			
		
					 6 changed files with 450 additions and 217 deletions
				
			
		| 
						 | 
					@ -8,7 +8,7 @@ msgid ""
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
"Project-Id-Version: PACKAGE VERSION\n"
 | 
					"Project-Id-Version: PACKAGE VERSION\n"
 | 
				
			||||||
"Report-Msgid-Bugs-To: \n"
 | 
					"Report-Msgid-Bugs-To: \n"
 | 
				
			||||||
"POT-Creation-Date: 2017-09-03 16:44+0000\n"
 | 
					"POT-Creation-Date: 2017-09-16 14:09+0000\n"
 | 
				
			||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
					"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 | 
				
			||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
					"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 | 
				
			||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
					"Language-Team: LANGUAGE <LL@li.org>\n"
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,10 @@ msgstr ""
 | 
				
			||||||
"Content-Transfer-Encoding: 8bit\n"
 | 
					"Content-Transfer-Encoding: 8bit\n"
 | 
				
			||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 | 
					"Plural-Forms: nplurals=2; plural=(n != 1);\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#, python-format
 | 
				
			||||||
 | 
					msgid "Your New VM %(vm_name)s at Data Center Light"
 | 
				
			||||||
 | 
					msgstr "Deine neue VM %(vm_name)s bei Data Center Light"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "Enter name"
 | 
					msgid "Enter name"
 | 
				
			||||||
msgstr "Name"
 | 
					msgstr "Name"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,9 +187,18 @@ msgstr "Kontakt"
 | 
				
			||||||
msgid "All Rights Reserved"
 | 
					msgid "All Rights Reserved"
 | 
				
			||||||
msgstr "Alle Rechte vorbehalten"
 | 
					msgstr "Alle Rechte vorbehalten"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "Toggle navigation"
 | 
				
			||||||
 | 
					msgstr "Konfiguration"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "Why Data Center Light?"
 | 
					msgid "Why Data Center Light?"
 | 
				
			||||||
msgstr "Warum Data Center Light?"
 | 
					msgstr "Warum Data Center Light?"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "Login"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "Dashboard"
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "Finally, an affordable VM hosting in Switzerland!"
 | 
					msgid "Finally, an affordable VM hosting in Switzerland!"
 | 
				
			||||||
msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
 | 
					msgstr "Endlich: bezahlbares VM Hosting in der Schweiz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,22 @@
 | 
				
			||||||
from dynamicweb.celery import app
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from celery.exceptions import MaxRetriesExceededError
 | 
				
			||||||
from celery.utils.log import get_task_logger
 | 
					from celery.utils.log import get_task_logger
 | 
				
			||||||
from celery import current_task
 | 
					from celery import current_task
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.core.mail import EmailMessage
 | 
				
			||||||
 | 
					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 membership.models import StripeCustomer, CustomUser
 | 
				
			||||||
from opennebula_api.models import OpenNebulaManager
 | 
					from opennebula_api.models import OpenNebulaManager
 | 
				
			||||||
from opennebula_api.serializers import VirtualMachineSerializer
 | 
					from opennebula_api.serializers import VirtualMachineSerializer
 | 
				
			||||||
from hosting.models import HostingOrder, HostingBill
 | 
					from utils.hosting_utils import get_all_public_keys
 | 
				
			||||||
from utils.forms import UserBillingAddressForm
 | 
					from utils.forms import UserBillingAddressForm
 | 
				
			||||||
from datetime import datetime
 | 
					from utils.mailer import BaseEmail
 | 
				
			||||||
from membership.models import StripeCustomer
 | 
					 | 
				
			||||||
from django.core.mail import EmailMessage
 | 
					 | 
				
			||||||
from utils.models import BillingAddress
 | 
					from utils.models import BillingAddress
 | 
				
			||||||
from celery.exceptions import MaxRetriesExceededError
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = get_task_logger(__name__)
 | 
					logger = get_task_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,19 +59,29 @@ def create_vm_task(self, vm_template_id, user, specs, template,
 | 
				
			||||||
        billing_address = BillingAddress.objects.filter(
 | 
					        billing_address = BillingAddress.objects.filter(
 | 
				
			||||||
            id=billing_address_id).first()
 | 
					            id=billing_address_id).first()
 | 
				
			||||||
        customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
 | 
					        customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
 | 
				
			||||||
        # Create OpenNebulaManager
 | 
					 | 
				
			||||||
        manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME,
 | 
					 | 
				
			||||||
                                    password=settings.OPENNEBULA_PASSWORD)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Create a vm using oneadmin, also specify the name
 | 
					        if 'pass' in user:
 | 
				
			||||||
 | 
					            on_user = user.get('email')
 | 
				
			||||||
 | 
					            on_pass = user.get('pass')
 | 
				
			||||||
 | 
					            logger.debug("Using user {user} to create VM".format(user=on_user))
 | 
				
			||||||
 | 
					            vm_name = None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            on_user = settings.OPENNEBULA_USERNAME
 | 
				
			||||||
 | 
					            on_pass = settings.OPENNEBULA_PASSWORD
 | 
				
			||||||
 | 
					            logger.debug("Using OpenNebula admin user to create VM")
 | 
				
			||||||
 | 
					            vm_name = "{email}-{template_name}-{date}".format(
 | 
				
			||||||
 | 
					                email=user.get('email'),
 | 
				
			||||||
 | 
					                template_name=template.get('name'),
 | 
				
			||||||
 | 
					                date=int(datetime.now().strftime("%s")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Create OpenNebulaManager
 | 
				
			||||||
 | 
					        manager = OpenNebulaManager(email=on_user, password=on_pass)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        vm_id = manager.create_vm(
 | 
					        vm_id = manager.create_vm(
 | 
				
			||||||
            template_id=vm_template_id,
 | 
					            template_id=vm_template_id,
 | 
				
			||||||
            specs=specs,
 | 
					            specs=specs,
 | 
				
			||||||
            ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY,
 | 
					            ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY,
 | 
				
			||||||
            vm_name="{email}-{template_name}-{date}".format(
 | 
					            vm_name=vm_name
 | 
				
			||||||
                email=user.get('email'),
 | 
					 | 
				
			||||||
                template_name=template.get('name'),
 | 
					 | 
				
			||||||
                date=int(datetime.now().strftime("%s")))
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if vm_id is None:
 | 
					        if vm_id is None:
 | 
				
			||||||
| 
						 | 
					@ -124,6 +140,54 @@ def create_vm_task(self, vm_template_id, user, specs, template,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        email = EmailMessage(**email_data)
 | 
					        email = EmailMessage(**email_data)
 | 
				
			||||||
        email.send()
 | 
					        email.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'pass' in user:
 | 
				
			||||||
 | 
					            lang = 'en-us' 
 | 
				
			||||||
 | 
					            if user.get('language') is not None:
 | 
				
			||||||
 | 
					                logger.debug("Language is set to {}".format(user.get('language')))
 | 
				
			||||||
 | 
					                lang = user.get('language')
 | 
				
			||||||
 | 
					            translation.activate(lang)
 | 
				
			||||||
 | 
					            # Send notification to the user as soon as VM has been booked
 | 
				
			||||||
 | 
					            context = {
 | 
				
			||||||
 | 
					                'vm': vm,
 | 
				
			||||||
 | 
					                'order': order,
 | 
				
			||||||
 | 
					                'base_url': "{0}://{1}".format(user.get('request_scheme'),
 | 
				
			||||||
 | 
					                                               user.get('request_host')),
 | 
				
			||||||
 | 
					                'page_header': _(
 | 
				
			||||||
 | 
					                    'Your New VM %(vm_name)s at Data Center Light') % {
 | 
				
			||||||
 | 
					                                   'vm_name': vm.get('name')}
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            email_data = {
 | 
				
			||||||
 | 
					                'subject': context.get('page_header'),
 | 
				
			||||||
 | 
					                'to': user.get('email'),
 | 
				
			||||||
 | 
					                'context': context,
 | 
				
			||||||
 | 
					                'template_name': 'new_booked_vm',
 | 
				
			||||||
 | 
					                'template_path': 'hosting/emails/',
 | 
				
			||||||
 | 
					                'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            email = BaseEmail(**email_data)
 | 
				
			||||||
 | 
					            email.send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # try to see if we have the IP and that if the ssh keys can
 | 
				
			||||||
 | 
					            # be configured
 | 
				
			||||||
 | 
					            new_host = manager.get_primary_ipv4(vm_id)
 | 
				
			||||||
 | 
					            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'))
 | 
				
			||||||
 | 
					                if custom_user is not None:
 | 
				
			||||||
 | 
					                    public_keys = get_all_public_keys(custom_user)
 | 
				
			||||||
 | 
					                    keys = [{'value': key, 'state': True} for key in
 | 
				
			||||||
 | 
					                            public_keys]
 | 
				
			||||||
 | 
					                    if len(keys) > 0:
 | 
				
			||||||
 | 
					                        logger.debug(
 | 
				
			||||||
 | 
					                            "Calling configure on {host} for {num_keys} keys".format(
 | 
				
			||||||
 | 
					                                host=new_host, num_keys=len(keys)))
 | 
				
			||||||
 | 
					                        # Let's delay the task by 75 seconds to be sure
 | 
				
			||||||
 | 
					                        # that we run the cdist configure after the host
 | 
				
			||||||
 | 
					                        # is up
 | 
				
			||||||
 | 
					                        manager.manage_public_key(keys,
 | 
				
			||||||
 | 
					                                                  hosts=[new_host],
 | 
				
			||||||
 | 
					                                                  countdown=75)
 | 
				
			||||||
    except Exception as e:
 | 
					    except Exception as e:
 | 
				
			||||||
        logger.error(str(e))
 | 
					        logger.error(str(e))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,37 @@
 | 
				
			||||||
 | 
					$(document).ready(function () {
 | 
				
			||||||
 | 
					    var create_vm_form = $('#virtual_machine_create_form');
 | 
				
			||||||
 | 
					    create_vm_form.submit(function () {
 | 
				
			||||||
 | 
					        $('#btn-create-vm').prop('disabled', true);
 | 
				
			||||||
 | 
					        $.ajax({
 | 
				
			||||||
 | 
					            url: create_vm_form.attr('action'),
 | 
				
			||||||
 | 
					            type: 'POST',
 | 
				
			||||||
 | 
					            data: create_vm_form.serialize(),
 | 
				
			||||||
 | 
					            success: function (data) {
 | 
				
			||||||
 | 
					                if (data.status === true) {
 | 
				
			||||||
 | 
					                    fa_icon = $('.modal-icon > .fa');
 | 
				
			||||||
 | 
					                    fa_icon.attr('class', 'fa fa-check');
 | 
				
			||||||
 | 
					                    $('.modal-header > .close').attr('class', 'close');
 | 
				
			||||||
 | 
					                    $('#createvm-modal-title').text(data.msg_title);
 | 
				
			||||||
 | 
					                    $('#createvm-modal-body').text(data.msg_body);
 | 
				
			||||||
 | 
					                    $('#createvm-modal').on('hidden.bs.modal', function () {
 | 
				
			||||||
 | 
					                        window.location = data.redirect;
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            error: function (xmlhttprequest, textstatus, message) {
 | 
				
			||||||
 | 
					                    fa_icon = $('.modal-icon > .fa');
 | 
				
			||||||
 | 
					                    fa_icon.attr('class', 'fa fa-times');
 | 
				
			||||||
 | 
					                    $('.modal-header > .close').attr('class', 'close');
 | 
				
			||||||
 | 
					                    if (typeof(create_vm_error_message) !== 'undefined') {
 | 
				
			||||||
 | 
					                        $('#createvm-modal-title').text(create_vm_error_message);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    $('#btn-create-vm').prop('disabled', false);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$( document ).ready(function() {
 | 
					    $('#confirm-cancel').on('click', '.btn-ok', function (e) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
	$('#confirm-cancel').on('click', '.btn-ok', function(e) {
 | 
					 | 
				
			||||||
        $('#virtual_machine_cancel_form').trigger('submit');
 | 
					        $('#virtual_machine_cancel_form').trigger('submit');
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
{% extends "hosting/base_short.html" %}
 | 
					{% extends "hosting/base_short.html" %}
 | 
				
			||||||
{% load staticfiles bootstrap3 %}
 | 
					{% load staticfiles bootstrap3 %}
 | 
				
			||||||
{% load i18n %}
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					{% load custom_tags %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="order-detail-container">
 | 
					<div class="order-detail-container">
 | 
				
			||||||
| 
						 | 
					@ -20,30 +22,58 @@
 | 
				
			||||||
    <div class="row">
 | 
					    <div class="row">
 | 
				
			||||||
        <div class="col-xs-12 col-md-8 col-md-offset-2">
 | 
					        <div class="col-xs-12 col-md-8 col-md-offset-2">
 | 
				
			||||||
            <div class="invoice-title">
 | 
					            <div class="invoice-title">
 | 
				
			||||||
    			<h2>{{page_header_text}}</h2><h3 class="pull-right">{% trans "Order #"%} {{order.id}}</h3>
 | 
					                <h2>{{page_header_text}}</h2>
 | 
				
			||||||
 | 
					                <h3 class="pull-right">
 | 
				
			||||||
 | 
					                    {% if order %}
 | 
				
			||||||
 | 
					                    {% trans "Order #"%} {{order.id}}
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </h3>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <hr>
 | 
					            <hr>
 | 
				
			||||||
            <div class="row">
 | 
					            <div class="row">
 | 
				
			||||||
                <div class="col-xs-12 col-md-6 pull-right order-confirm-date">
 | 
					                <div class="col-xs-12 col-md-6 pull-right order-confirm-date">
 | 
				
			||||||
                    <address>
 | 
					                    <address>
 | 
				
			||||||
                        <strong>{% trans "Date"%}:</strong><br>
 | 
					                        <strong>{% trans "Date"%}:</strong><br>
 | 
				
			||||||
                        <span id="order-created_at">{{order.created_at|date:'Y-m-d H:i'}}</span><br><br>
 | 
					                        <span id="order-created_at">
 | 
				
			||||||
 | 
					                                {% if order %}
 | 
				
			||||||
 | 
					                                    {{order.created_at|date:'Y-m-d H:i'}}
 | 
				
			||||||
 | 
					                                {% else %}
 | 
				
			||||||
 | 
					                                    {% now "Y-m-d H:i" %}
 | 
				
			||||||
 | 
					                                {% endif %}
 | 
				
			||||||
 | 
					                            </span><br><br>
 | 
				
			||||||
 | 
					                        {% if order %}
 | 
				
			||||||
                        <strong>{% trans "Status:"%}</strong><br>
 | 
					                        <strong>{% trans "Status:"%}</strong><br>
 | 
				
			||||||
                        {% if order.status == 'Approved' %}
 | 
					                        {% if order.status == 'Approved' %}
 | 
				
			||||||
                            <strong class="text-success">{% trans "Approved" %}</strong>
 | 
					                        <strong class="text-success">
 | 
				
			||||||
 | 
					                            {% trans "Approved" %}
 | 
				
			||||||
 | 
					                        </strong>
 | 
				
			||||||
                        {% else %}
 | 
					                        {% else %}
 | 
				
			||||||
                            <strong class="text-danger">{% trans "Declined" %}</strong>
 | 
					                        <strong class="text-danger">
 | 
				
			||||||
 | 
					                            {% trans "Declined" %}
 | 
				
			||||||
 | 
					                        </strong>
 | 
				
			||||||
                        {% endif %}
 | 
					                        {% endif %}
 | 
				
			||||||
                        <br><br>
 | 
					                        <br><br>
 | 
				
			||||||
 | 
					                        {% endif %}
 | 
				
			||||||
                    </address>
 | 
					                    </address>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="col-xs-12 col-md-6">
 | 
					                <div class="col-xs-12 col-md-6">
 | 
				
			||||||
                    <address>
 | 
					                    <address>
 | 
				
			||||||
                        <h3><b>{% trans "Billed To:"%}</b></h3>
 | 
					                        <h3><b>{% trans "Billed To:"%}</b></h3>
 | 
				
			||||||
 | 
					                        {% if order %}
 | 
				
			||||||
                        {{user.name}}<br>
 | 
					                        {{user.name}}<br>
 | 
				
			||||||
                        {{order.billing_address.street_address}},{{order.billing_address.postal_code}}<br>
 | 
					                        {{order.billing_address.street_address}},{{order.billing_address.postal_code}}<br>
 | 
				
			||||||
                        {{order.billing_address.city}}, {{order.billing_address.country}}.
 | 
					                        {{order.billing_address.city}},
 | 
				
			||||||
 | 
					                        {{order.billing_address.country}}.
 | 
				
			||||||
 | 
					                        {% else %}
 | 
				
			||||||
 | 
					                        {% with request.session.billing_address_data as billing_address %}
 | 
				
			||||||
 | 
					                        {{billing_address|get_value_from_dict:'cardholder_name'}}<br>
 | 
				
			||||||
 | 
					                        {{billing_address|get_value_from_dict:'street_address'}},
 | 
				
			||||||
 | 
					                        {{billing_address|get_value_from_dict:'postal_code'}}<br>
 | 
				
			||||||
 | 
					                        {{billing_address|get_value_from_dict:'city'}},
 | 
				
			||||||
 | 
					                        {{billing_address|get_value_from_dict:'country'}}.
 | 
				
			||||||
 | 
					                        {% endwith %}
 | 
				
			||||||
 | 
					                        {% endif %}
 | 
				
			||||||
                    </address>
 | 
					                    </address>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,8 +82,15 @@
 | 
				
			||||||
                <div class="col-xs-6">
 | 
					                <div class="col-xs-6">
 | 
				
			||||||
                    <address>
 | 
					                    <address>
 | 
				
			||||||
                        <strong>{% trans "Payment Method:"%}</strong><br>
 | 
					                        <strong>{% trans "Payment Method:"%}</strong><br>
 | 
				
			||||||
    					{{order.cc_brand}} {% trans "ending in" %} **** {{order.last4}}<br>
 | 
					                        {% if order %}
 | 
				
			||||||
 | 
					                        {{order.cc_brand}} {% trans "ending in" %} ****
 | 
				
			||||||
 | 
					                        {{order.last4}}<br>
 | 
				
			||||||
                        {{user.email}}
 | 
					                        {{user.email}}
 | 
				
			||||||
 | 
					                        {% else %}
 | 
				
			||||||
 | 
					                        {{cc_brand}} {% trans "ending in" %} ****
 | 
				
			||||||
 | 
					                        {{cc_last4}}<br>
 | 
				
			||||||
 | 
					                        {{request.session.user.email}}
 | 
				
			||||||
 | 
					                        {% endif %}
 | 
				
			||||||
                    </address>
 | 
					                    </address>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
| 
						 | 
					@ -65,31 +102,111 @@
 | 
				
			||||||
            <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 "Cores"%}</b> <span class="pull-right">{{vm.cores}}</span></p>
 | 
					                {% if request.session.specs %}
 | 
				
			||||||
 | 
					                {% with request.session.specs as vm %}
 | 
				
			||||||
 | 
					                <p><b>{% trans "Cores"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.cpu}}</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
                <hr>
 | 
					                <hr>
 | 
				
			||||||
                <p><b>{% trans "Memory"%}</b> <span class="pull-right">{{vm.memory}} GB</span></p>
 | 
					                <p><b>{% trans "Memory"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.memory}} GB</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
                <hr>
 | 
					                <hr>
 | 
				
			||||||
                <p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{vm.disk_size}} GB</span></p>
 | 
					                <p><b>{% trans "Disk space"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.disk_size}} GB</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
                <hr>
 | 
					                <hr>
 | 
				
			||||||
                                <h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b></p></h4>
 | 
					                <p><b>{% trans "Configuration"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{request.session.template.name}}</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                <hr>
 | 
				
			||||||
 | 
					                <h4>{% trans "Total"%}
 | 
				
			||||||
 | 
					                    <p class="pull-right">
 | 
				
			||||||
 | 
					                        <b>{{vm.price}} CHF</b>
 | 
				
			||||||
 | 
					                        <span class="dcl-price-month"> /{% trans "Month" %}
 | 
				
			||||||
 | 
					                                </span>
 | 
				
			||||||
 | 
					                    </p>
 | 
				
			||||||
 | 
					                </h4>
 | 
				
			||||||
 | 
					                {% endwith %}
 | 
				
			||||||
 | 
					                {% else %}
 | 
				
			||||||
 | 
					                <p><b>{% trans "Cores"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.cores}}</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                <hr>
 | 
				
			||||||
 | 
					                <p><b>{% trans "Memory"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.memory}} GB</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                <hr>
 | 
				
			||||||
 | 
					                <p><b>{% trans "Disk space"%}</b>
 | 
				
			||||||
 | 
					                    <span class="pull-right">{{vm.disk_size}} GB</span>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                <hr>
 | 
				
			||||||
 | 
					                <h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}}
 | 
				
			||||||
 | 
					                    CHF</b><span
 | 
				
			||||||
 | 
					                        class="dcl-price-month"> /{% trans "Month" %}</span>
 | 
				
			||||||
 | 
					                </p></h4>
 | 
				
			||||||
 | 
					                {% endif %}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <br/>
 | 
					            <br/>
 | 
				
			||||||
            {% url 'hosting:payment' as payment_url %}
 | 
					            {% if not order %}
 | 
				
			||||||
            {% if payment_url in request.META.HTTP_REFERER  %}
 | 
					            <form method="post" id="virtual_machine_create_form">
 | 
				
			||||||
            <div class=" content pull-right">
 | 
					                {% csrf_token %}
 | 
				
			||||||
                <a href="{% url 'hosting:virtual_machines'%}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a>
 | 
					                <div class="row">
 | 
				
			||||||
 | 
					                    <div class="col-sm-8">
 | 
				
			||||||
 | 
					                        <p class="dcl-place-order-text">{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.</p>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="col-sm-4 content">
 | 
				
			||||||
 | 
					                        <button class="btn btn-info pull-right"
 | 
				
			||||||
 | 
					                                id="btn-create-vm"
 | 
				
			||||||
 | 
					                                data-href="{% url 'hosting:order-confirmation' %}"
 | 
				
			||||||
 | 
					                                data-toggle="modal"
 | 
				
			||||||
 | 
					                                data-target="#createvm-modal">
 | 
				
			||||||
 | 
					                            {% trans "Place order"%}
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					<!-- Create VM Modal -->
 | 
				
			||||||
 | 
					<div class="modal fade" id="createvm-modal" tabindex="-1" role="dialog"
 | 
				
			||||||
 | 
					     aria-hidden="true" data-backdrop="static" data-keyboard="false">
 | 
				
			||||||
 | 
					    <div class="modal-dialog">
 | 
				
			||||||
 | 
					        <div class="modal-content">
 | 
				
			||||||
 | 
					            <div class="modal-header">
 | 
				
			||||||
 | 
					                <button type="button" class="close hidden" data-dismiss="modal"
 | 
				
			||||||
 | 
					                        aria-label="create-vm-close">
 | 
				
			||||||
 | 
					                    <span aria-hidden="true">×</span>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="modal-body">
 | 
				
			||||||
 | 
					                <div class="modal-icon">
 | 
				
			||||||
 | 
					                    <i class="fa fa-cog fa-spin fa-3x fa-fw"></i>
 | 
				
			||||||
 | 
					                    <span class="sr-only">{% trans "Processing..." %}</span>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <h4 class="modal-title" id="createvm-modal-title">
 | 
				
			||||||
 | 
					                </h4>
 | 
				
			||||||
 | 
					                <div class="modal-text" id="createvm-modal-body">
 | 
				
			||||||
 | 
					                    {% trans "Hold tight, we are processing your request" %}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="modal-footer">
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<!-- / Create VM Modal -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script type="text/javascript">
 | 
					<script type="text/javascript">
 | 
				
			||||||
 | 
					    {% trans "Some problem encountered. Please try again later." as err_msg %}
 | 
				
			||||||
 | 
					    var create_vm_error_message = '{{err_msg|safe}}.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    window.onload = function () {
 | 
					    window.onload = function () {
 | 
				
			||||||
            var locale_date = moment.utc(document.getElementById("order-created_at").textContent,'YYYY-MM-DD HH:mm').toDate();
 | 
					        var locale_date = moment.utc(document.getElementById("order-created_at").textContent, 'YYYY-MM-DD HH:mm').toDate();
 | 
				
			||||||
        locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
 | 
					        locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
 | 
				
			||||||
        document.getElementById('order-created_at').innerHTML = locale_date;
 | 
					        document.getElementById('order-created_at').innerHTML = locale_date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,9 +21,13 @@ urlpatterns = [
 | 
				
			||||||
    url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
 | 
					    url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
 | 
				
			||||||
    url(r'settings/?$', SettingsView.as_view(), name='settings'),
 | 
					    url(r'settings/?$', SettingsView.as_view(), name='settings'),
 | 
				
			||||||
    url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
 | 
					    url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'),
 | 
				
			||||||
    url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'),
 | 
					    url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(),
 | 
				
			||||||
 | 
					        name='order-confirmation'),
 | 
				
			||||||
 | 
					    url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(),
 | 
				
			||||||
 | 
					        name='orders'),
 | 
				
			||||||
    url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
 | 
					    url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
 | 
				
			||||||
    url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'),
 | 
					    url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(),
 | 
				
			||||||
 | 
					        name='bills'),
 | 
				
			||||||
    url(r'cancel_order/(?P<pk>\d+)/?$',
 | 
					    url(r'cancel_order/(?P<pk>\d+)/?$',
 | 
				
			||||||
        OrdersHostingDeleteView.as_view(), name='delete_order'),
 | 
					        OrdersHostingDeleteView.as_view(), name='delete_order'),
 | 
				
			||||||
    url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(),
 | 
					    url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(),
 | 
				
			||||||
| 
						 | 
					@ -40,13 +44,16 @@ urlpatterns = [
 | 
				
			||||||
        name='delete_ssh_key'),
 | 
					        name='delete_ssh_key'),
 | 
				
			||||||
    url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(),
 | 
					    url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(),
 | 
				
			||||||
        name='create_ssh_key'),
 | 
					        name='create_ssh_key'),
 | 
				
			||||||
    url(r'^notifications/$', NotificationsView.as_view(), name='notifications'),
 | 
					    url(r'^notifications/$', NotificationsView.as_view(),
 | 
				
			||||||
 | 
					        name='notifications'),
 | 
				
			||||||
    url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(),
 | 
					    url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(),
 | 
				
			||||||
        name='read_notification'),
 | 
					        name='read_notification'),
 | 
				
			||||||
    url(r'login/?$', LoginView.as_view(), name='login'),
 | 
					    url(r'login/?$', LoginView.as_view(), name='login'),
 | 
				
			||||||
    url(r'signup/?$', SignupView.as_view(), name='signup'),
 | 
					    url(r'signup/?$', SignupView.as_view(), name='signup'),
 | 
				
			||||||
    url(r'signup-validate/?$', SignupValidateView.as_view(), name='signup-validate'),
 | 
					    url(r'signup-validate/?$', SignupValidateView.as_view(),
 | 
				
			||||||
    url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'),
 | 
					        name='signup-validate'),
 | 
				
			||||||
 | 
					    url(r'reset-password/?$', PasswordResetView.as_view(),
 | 
				
			||||||
 | 
					        name='reset_password'),
 | 
				
			||||||
    url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
 | 
					    url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
 | 
				
			||||||
        PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
 | 
					        PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
 | 
				
			||||||
    url(r'^logout/?$', auth_views.logout,
 | 
					    url(r'^logout/?$', auth_views.logout,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										250
									
								
								hosting/views.py
									
										
									
									
									
								
							
							
						
						
									
										250
									
								
								hosting/views.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,12 +9,12 @@ from django.contrib.auth.tokens import default_token_generator
 | 
				
			||||||
from django.core.files.base import ContentFile
 | 
					from django.core.files.base import ContentFile
 | 
				
			||||||
from django.core.urlresolvers import reverse_lazy, reverse
 | 
					from django.core.urlresolvers import reverse_lazy, reverse
 | 
				
			||||||
from django.http import Http404
 | 
					from django.http import Http404
 | 
				
			||||||
from django.http import HttpResponseRedirect
 | 
					from django.http import HttpResponseRedirect, HttpResponse
 | 
				
			||||||
from django.shortcuts import redirect
 | 
					from django.shortcuts import redirect
 | 
				
			||||||
from django.shortcuts import render
 | 
					from django.shortcuts import render
 | 
				
			||||||
from django.utils.http import urlsafe_base64_decode
 | 
					from django.utils.http import urlsafe_base64_decode
 | 
				
			||||||
from django.utils.safestring import mark_safe
 | 
					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.views.generic import View, CreateView, FormView, ListView, \
 | 
					from django.views.generic import View, CreateView, FormView, ListView, \
 | 
				
			||||||
    DetailView, \
 | 
					    DetailView, \
 | 
				
			||||||
    DeleteView, TemplateView, UpdateView
 | 
					    DeleteView, TemplateView, UpdateView
 | 
				
			||||||
| 
						 | 
					@ -23,13 +24,13 @@ from stored_messages.api import mark_read
 | 
				
			||||||
from stored_messages.models import Message
 | 
					from stored_messages.models import Message
 | 
				
			||||||
from stored_messages.settings import stored_messages_settings
 | 
					from stored_messages.settings import stored_messages_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from datacenterlight.tasks import create_vm_task
 | 
				
			||||||
from membership.models import CustomUser, StripeCustomer
 | 
					from membership.models import CustomUser, StripeCustomer
 | 
				
			||||||
from opennebula_api.models import OpenNebulaManager
 | 
					from opennebula_api.models import OpenNebulaManager
 | 
				
			||||||
from opennebula_api.serializers import VirtualMachineSerializer, \
 | 
					from opennebula_api.serializers import VirtualMachineSerializer, \
 | 
				
			||||||
    VirtualMachineTemplateSerializer
 | 
					    VirtualMachineTemplateSerializer
 | 
				
			||||||
from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
 | 
					from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
 | 
				
			||||||
    UserBillingAddressForm
 | 
					    UserBillingAddressForm
 | 
				
			||||||
from utils.hosting_utils import get_all_public_keys
 | 
					 | 
				
			||||||
from utils.mailer import BaseEmail
 | 
					from utils.mailer import BaseEmail
 | 
				
			||||||
from utils.stripe_utils import StripeUtils
 | 
					from utils.stripe_utils import StripeUtils
 | 
				
			||||||
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \
 | 
					from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \
 | 
				
			||||||
| 
						 | 
					@ -606,23 +607,10 @@ class PaymentVMView(LoginRequiredMixin, FormView):
 | 
				
			||||||
    def post(self, request, *args, **kwargs):
 | 
					    def post(self, request, *args, **kwargs):
 | 
				
			||||||
        form = self.get_form()
 | 
					        form = self.get_form()
 | 
				
			||||||
        if form.is_valid():
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Get billing address data
 | 
					            # Get billing address data
 | 
				
			||||||
            billing_address_data = form.cleaned_data
 | 
					            billing_address_data = form.cleaned_data
 | 
				
			||||||
 | 
					 | 
				
			||||||
            context = self.get_context_data()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            template = request.session.get('template')
 | 
					 | 
				
			||||||
            specs = request.session.get('specs')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            vm_template_id = template.get('id', 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            final_price = specs.get('price')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            token = form.cleaned_data.get('token')
 | 
					            token = form.cleaned_data.get('token')
 | 
				
			||||||
 | 
					 | 
				
			||||||
            owner = self.request.user
 | 
					            owner = self.request.user
 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Get or create stripe customer
 | 
					            # Get or create stripe customer
 | 
				
			||||||
            customer = StripeCustomer.get_or_create(email=owner.email,
 | 
					            customer = StripeCustomer.get_or_create(email=owner.email,
 | 
				
			||||||
                                                    token=token)
 | 
					                                                    token=token)
 | 
				
			||||||
| 
						 | 
					@ -636,115 +624,18 @@ class PaymentVMView(LoginRequiredMixin, FormView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Create Billing Address
 | 
					            # Create Billing Address
 | 
				
			||||||
            billing_address = form.save()
 | 
					            billing_address = form.save()
 | 
				
			||||||
 | 
					            request.session['billing_address_data'] = billing_address_data
 | 
				
			||||||
            # Make stripe charge to a customer
 | 
					            request.session['billing_address'] = billing_address.id
 | 
				
			||||||
            stripe_utils = StripeUtils()
 | 
					            request.session['token'] = token
 | 
				
			||||||
            charge_response = stripe_utils.make_charge(amount=final_price,
 | 
					            request.session['customer'] = customer.id
 | 
				
			||||||
                                                       customer=customer.stripe_id)
 | 
					            return HttpResponseRedirect("{url}?{query_params}".format(
 | 
				
			||||||
 | 
					                url=reverse('hosting:order-confirmation'),
 | 
				
			||||||
            # Check if the payment was approved
 | 
					 | 
				
			||||||
            if not charge_response.get('response_object'):
 | 
					 | 
				
			||||||
                msg = charge_response.get('error')
 | 
					 | 
				
			||||||
                messages.add_message(
 | 
					 | 
				
			||||||
                    self.request, messages.ERROR, msg,
 | 
					 | 
				
			||||||
                    extra_tags='make_charge_error')
 | 
					 | 
				
			||||||
                return HttpResponseRedirect(
 | 
					 | 
				
			||||||
                    reverse('hosting:payment') + '#payment_error')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            charge = charge_response.get('response_object')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Create OpenNebulaManager
 | 
					 | 
				
			||||||
            manager = OpenNebulaManager(email=owner.email,
 | 
					 | 
				
			||||||
                                        password=owner.password)
 | 
					 | 
				
			||||||
            # Get user ssh key
 | 
					 | 
				
			||||||
            if not UserHostingKey.objects.filter(
 | 
					 | 
				
			||||||
                    user=self.request.user).exists():
 | 
					 | 
				
			||||||
                context.update({
 | 
					 | 
				
			||||||
                    'sshError': 'error',
 | 
					 | 
				
			||||||
                    'form': form
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                return render(request, self.template_name, context)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Create a vm using logged user
 | 
					 | 
				
			||||||
            vm_id = manager.create_vm(
 | 
					 | 
				
			||||||
                template_id=vm_template_id,
 | 
					 | 
				
			||||||
                specs=specs,
 | 
					 | 
				
			||||||
                ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Create a Hosting Order
 | 
					 | 
				
			||||||
            order = HostingOrder.create(
 | 
					 | 
				
			||||||
                price=final_price,
 | 
					 | 
				
			||||||
                vm_id=vm_id,
 | 
					 | 
				
			||||||
                customer=customer,
 | 
					 | 
				
			||||||
                billing_address=billing_address
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Create a Hosting 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 the user as soon as VM has been booked
 | 
					 | 
				
			||||||
            context = {
 | 
					 | 
				
			||||||
                'vm': vm,
 | 
					 | 
				
			||||||
                'order': order,
 | 
					 | 
				
			||||||
                'base_url': "{0}://{1}".format(request.scheme,
 | 
					 | 
				
			||||||
                                               request.get_host()),
 | 
					 | 
				
			||||||
                'page_header': _(
 | 
					 | 
				
			||||||
                    'Your New VM %(vm_name)s at Data Center Light') % {
 | 
					 | 
				
			||||||
                                   'vm_name': vm.get('name')}
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            email_data = {
 | 
					 | 
				
			||||||
                'subject': context.get('page_header'),
 | 
					 | 
				
			||||||
                'to': request.user.email,
 | 
					 | 
				
			||||||
                'context': context,
 | 
					 | 
				
			||||||
                'template_name': 'new_booked_vm',
 | 
					 | 
				
			||||||
                'template_path': 'hosting/emails/',
 | 
					 | 
				
			||||||
                'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            email = BaseEmail(**email_data)
 | 
					 | 
				
			||||||
            email.send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # try to see if we have the IP and that if the ssh keys can
 | 
					 | 
				
			||||||
            # be configured
 | 
					 | 
				
			||||||
            new_host = manager.get_primary_ipv4(vm_id)
 | 
					 | 
				
			||||||
            if new_host is not None:
 | 
					 | 
				
			||||||
                public_keys = get_all_public_keys(owner)
 | 
					 | 
				
			||||||
                keys = [{'value': key, 'state': True} for key in public_keys]
 | 
					 | 
				
			||||||
                logger.debug(
 | 
					 | 
				
			||||||
                    "Calling configure on {host} for {num_keys} keys".format(
 | 
					 | 
				
			||||||
                        host=new_host, num_keys=len(keys)))
 | 
					 | 
				
			||||||
                # Let's delay the task by 75 seconds to be sure that we run
 | 
					 | 
				
			||||||
                # the cdist configure after the host is up
 | 
					 | 
				
			||||||
                manager.manage_public_key(keys, hosts=[new_host], countdown=75)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            return HttpResponseRedirect(
 | 
					 | 
				
			||||||
                "{url}?{query_params}".format(
 | 
					 | 
				
			||||||
                    url=reverse('hosting:orders', kwargs={'pk': order.id}),
 | 
					 | 
				
			||||||
                query_params='page=payment'))
 | 
					                query_params='page=payment'))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return self.form_invalid(form)
 | 
					            return self.form_invalid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin,
 | 
					class OrdersHostingDetailView(LoginRequiredMixin,
 | 
				
			||||||
                              DetailView):
 | 
					                              DetailView):
 | 
				
			||||||
    template_name = "hosting/order_detail.html"
 | 
					    template_name = "hosting/order_detail.html"
 | 
				
			||||||
    context_object_name = "order"
 | 
					    context_object_name = "order"
 | 
				
			||||||
| 
						 | 
					@ -752,18 +643,42 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin,
 | 
				
			||||||
    permission_required = ['view_hostingorder']
 | 
					    permission_required = ['view_hostingorder']
 | 
				
			||||||
    model = HostingOrder
 | 
					    model = HostingOrder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_object(self):
 | 
				
			||||||
 | 
					        return HostingOrder.objects.filter(
 | 
				
			||||||
 | 
					            pk=self.kwargs.get('pk')) if self.kwargs.get('pk') else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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)
 | 
				
			||||||
        obj = self.get_object()
 | 
					        obj = self.get_object()
 | 
				
			||||||
        owner = self.request.user
 | 
					        owner = self.request.user
 | 
				
			||||||
        manager = OpenNebulaManager(email=owner.email,
 | 
					        if 'specs' not in self.request.session:
 | 
				
			||||||
                                    password=owner.password)
 | 
					            return HttpResponseRedirect(
 | 
				
			||||||
 | 
					                reverse('hosting:create_virtual_machine'))
 | 
				
			||||||
 | 
					        if 'token' not in self.request.session:
 | 
				
			||||||
 | 
					            return HttpResponseRedirect(reverse('hosting:payment'))
 | 
				
			||||||
 | 
					        stripe_customer_id = self.request.session.get('customer')
 | 
				
			||||||
 | 
					        customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
 | 
				
			||||||
 | 
					        stripe_utils = StripeUtils()
 | 
				
			||||||
 | 
					        card_details = stripe_utils.get_card_details(customer.stripe_id,
 | 
				
			||||||
 | 
					                                                     self.request.session.get(
 | 
				
			||||||
 | 
					                                                         'token'))
 | 
				
			||||||
 | 
					        if not card_details.get('response_object'):
 | 
				
			||||||
 | 
					            msg = card_details.get('error')
 | 
				
			||||||
 | 
					            messages.add_message(self.request, messages.ERROR, msg,
 | 
				
			||||||
 | 
					                                 extra_tags='failed_payment')
 | 
				
			||||||
 | 
					            return HttpResponseRedirect(
 | 
				
			||||||
 | 
					                reverse('hosting:payment') + '#payment_error')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.request.GET.get('page', '') == 'payment':
 | 
					        if self.request.GET.get('page', '') == 'payment':
 | 
				
			||||||
            context['page_header_text'] = _('Confirm Order')
 | 
					            context['page_header_text'] = _('Confirm Order')
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            context['page_header_text'] = _('Invoice')
 | 
					            context['page_header_text'] = _('Invoice')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if obj is not None:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
 | 
					                manager = OpenNebulaManager(email=owner.email,
 | 
				
			||||||
 | 
					                                            password=owner.password)
 | 
				
			||||||
                vm = manager.get_vm(obj.vm_id)
 | 
					                vm = manager.get_vm(obj.vm_id)
 | 
				
			||||||
                context['vm'] = VirtualMachineSerializer(vm).data
 | 
					                context['vm'] = VirtualMachineSerializer(vm).data
 | 
				
			||||||
            except WrongIdError:
 | 
					            except WrongIdError:
 | 
				
			||||||
| 
						 | 
					@ -775,11 +690,98 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin,
 | 
				
			||||||
                context['error'] = 'WrongIdError'
 | 
					                context['error'] = 'WrongIdError'
 | 
				
			||||||
            except ConnectionRefusedError:
 | 
					            except ConnectionRefusedError:
 | 
				
			||||||
                messages.error(self.request,
 | 
					                messages.error(self.request,
 | 
				
			||||||
                           _(
 | 
					                               'In order to create a VM, you need to create/upload your SSH KEY first.'
 | 
				
			||||||
                               'In order to create a VM, you need to create/upload your SSH KEY first.')
 | 
					 | 
				
			||||||
                               )
 | 
					                               )
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            context['site_url'] = reverse('hosting:create_virtual_machine')
 | 
				
			||||||
 | 
					            context['cc_last4'] = card_details.get('response_object').get(
 | 
				
			||||||
 | 
					                'last4')
 | 
				
			||||||
 | 
					            context['cc_brand'] = card_details.get('response_object').get(
 | 
				
			||||||
 | 
					                'cc_brand')
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request):
 | 
				
			||||||
 | 
					        template = request.session.get('template')
 | 
				
			||||||
 | 
					        specs = request.session.get('specs')
 | 
				
			||||||
 | 
					        stripe_customer_id = request.session.get('customer')
 | 
				
			||||||
 | 
					        customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
 | 
				
			||||||
 | 
					        billing_address_data = request.session.get('billing_address_data')
 | 
				
			||||||
 | 
					        billing_address_id = request.session.get('billing_address')
 | 
				
			||||||
 | 
					        vm_template_id = template.get('id', 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Make stripe charge to a customer
 | 
				
			||||||
 | 
					        stripe_utils = StripeUtils()
 | 
				
			||||||
 | 
					        card_details = stripe_utils.get_card_details(customer.stripe_id,
 | 
				
			||||||
 | 
					                                                     request.session.get(
 | 
				
			||||||
 | 
					                                                         'token'))
 | 
				
			||||||
 | 
					        if not card_details.get('response_object'):
 | 
				
			||||||
 | 
					            msg = card_details.get('error')
 | 
				
			||||||
 | 
					            messages.add_message(self.request, messages.ERROR, msg,
 | 
				
			||||||
 | 
					                                 extra_tags='failed_payment')
 | 
				
			||||||
 | 
					            return HttpResponseRedirect(
 | 
				
			||||||
 | 
					                reverse('datacenterlight:payment') + '#payment_error')
 | 
				
			||||||
 | 
					        card_details_dict = card_details.get('response_object')
 | 
				
			||||||
 | 
					        cpu = specs.get('cpu')
 | 
				
			||||||
 | 
					        memory = specs.get('memory')
 | 
				
			||||||
 | 
					        disk_size = specs.get('disk_size')
 | 
				
			||||||
 | 
					        amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
 | 
				
			||||||
 | 
					        plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
 | 
				
			||||||
 | 
					            cpu=cpu,
 | 
				
			||||||
 | 
					            memory=memory,
 | 
				
			||||||
 | 
					            disk_size=disk_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
 | 
				
			||||||
 | 
					                                                        ram=memory,
 | 
				
			||||||
 | 
					                                                        ssd=disk_size,
 | 
				
			||||||
 | 
					                                                        version=1,
 | 
				
			||||||
 | 
					                                                        app='dcl')
 | 
				
			||||||
 | 
					        stripe_plan = stripe_utils.get_or_create_stripe_plan(
 | 
				
			||||||
 | 
					            amount=amount_to_be_charged,
 | 
				
			||||||
 | 
					            name=plan_name,
 | 
				
			||||||
 | 
					            stripe_plan_id=stripe_plan_id)
 | 
				
			||||||
 | 
					        subscription_result = stripe_utils.subscribe_customer_to_plan(
 | 
				
			||||||
 | 
					            customer.stripe_id,
 | 
				
			||||||
 | 
					            [{"plan": stripe_plan.get(
 | 
				
			||||||
 | 
					                'response_object').stripe_plan_id}])
 | 
				
			||||||
 | 
					        stripe_subscription_obj = subscription_result.get('response_object')
 | 
				
			||||||
 | 
					        # Check if the subscription was approved and is active
 | 
				
			||||||
 | 
					        if stripe_subscription_obj is None or stripe_subscription_obj.status != 'active':
 | 
				
			||||||
 | 
					            msg = subscription_result.get('error')
 | 
				
			||||||
 | 
					            messages.add_message(self.request, messages.ERROR, msg,
 | 
				
			||||||
 | 
					                                 extra_tags='failed_payment')
 | 
				
			||||||
 | 
					            return HttpResponseRedirect(
 | 
				
			||||||
 | 
					                reverse('hosting:payment') + '#payment_error')
 | 
				
			||||||
 | 
					        user = {
 | 
				
			||||||
 | 
					            'name': self.request.user.name,
 | 
				
			||||||
 | 
					            'email': self.request.user.email,
 | 
				
			||||||
 | 
					            'pass': self.request.user.password,
 | 
				
			||||||
 | 
					            'request_scheme': request.scheme,
 | 
				
			||||||
 | 
					            'request_host': request.get_host(),
 | 
				
			||||||
 | 
					            'language': get_language(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        create_vm_task.delay(vm_template_id, user, specs, template,
 | 
				
			||||||
 | 
					                             stripe_customer_id, billing_address_data,
 | 
				
			||||||
 | 
					                             billing_address_id,
 | 
				
			||||||
 | 
					                             stripe_subscription_obj, card_details_dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for session_var in ['specs', 'template', 'billing_address',
 | 
				
			||||||
 | 
					                            'billing_address_data',
 | 
				
			||||||
 | 
					                            'token', 'customer']:
 | 
				
			||||||
 | 
					            if session_var in request.session:
 | 
				
			||||||
 | 
					                del request.session[session_var]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = {
 | 
				
			||||||
 | 
					            'status': True,
 | 
				
			||||||
 | 
					            'redirect': reverse('hosting:virtual_machines'),
 | 
				
			||||||
 | 
					            'msg_title': str(_('Thank you for the order.')),
 | 
				
			||||||
 | 
					            'msg_body': str(_('Your VM will be up and running in a few moments.'
 | 
				
			||||||
 | 
					                          ' We will send you a confirmation email as soon as'
 | 
				
			||||||
 | 
					                          ' it is ready.'))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return HttpResponse(json.dumps(response),
 | 
				
			||||||
 | 
					                            content_type="application/json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OrdersHostingListView(LoginRequiredMixin, ListView):
 | 
					class OrdersHostingListView(LoginRequiredMixin, ListView):
 | 
				
			||||||
    template_name = "hosting/orders.html"
 | 
					    template_name = "hosting/orders.html"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue