Merge remote-tracking branch 'ungleich/master'
This commit is contained in:
commit
c103c916ef
17 changed files with 193 additions and 244 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -36,3 +36,4 @@ secret-key
|
||||||
.env
|
.env
|
||||||
*.mo
|
*.mo
|
||||||
*.log
|
*.log
|
||||||
|
*.sql
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
Next:
|
||||||
|
* #3764: [hosting] Show cancelled VMs' invoices
|
||||||
|
* #3736: [dcl] Refactor the place where we compute the VM price
|
||||||
|
* #3730: [dcl] Refactor price parameter passed in the DCL flow
|
||||||
|
* #3807: [dcl] Remove PricingView as it is no more used
|
||||||
|
* #3813: [hosting] JS error in create ssh key page
|
||||||
|
|
||||||
1.2.3: 2017-09-25
|
1.2.3: 2017-09-25
|
||||||
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
|
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
|
||||||
* #3731: [dcl, hosting] Added cdist ssh key handler
|
* #3731: [dcl, hosting] Added cdist ssh key handler
|
||||||
|
|
|
@ -155,9 +155,7 @@
|
||||||
function _calcPricing() {
|
function _calcPricing() {
|
||||||
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
|
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
|
||||||
total = parseFloat(total.toFixed(2));
|
total = parseFloat(total.toFixed(2));
|
||||||
|
|
||||||
$("#total").text(total);
|
$("#total").text(total);
|
||||||
$('input[name=total]').val(total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function form_success() {
|
function form_success() {
|
||||||
|
@ -191,4 +189,4 @@
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -13,7 +13,7 @@ from hosting.models import HostingOrder, HostingBill
|
||||||
from membership.models import StripeCustomer, CustomUser
|
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 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.forms import UserBillingAddressForm
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
|
@ -52,7 +52,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
stripe_customer_id, billing_address_data,
|
stripe_customer_id, billing_address_data,
|
||||||
billing_address_id,
|
billing_address_id,
|
||||||
charge, cc_details):
|
charge, cc_details):
|
||||||
logger.debug("Running create_vm_task on {}".format(current_task.request.hostname))
|
logger.debug(
|
||||||
|
"Running create_vm_task on {}".format(current_task.request.hostname))
|
||||||
vm_id = None
|
vm_id = None
|
||||||
try:
|
try:
|
||||||
final_price = specs.get('price')
|
final_price = specs.get('price')
|
||||||
|
@ -142,9 +143,10 @@ def create_vm_task(self, vm_template_id, user, specs, template,
|
||||||
email.send()
|
email.send()
|
||||||
|
|
||||||
if 'pass' in user:
|
if 'pass' in user:
|
||||||
lang = 'en-us'
|
lang = 'en-us'
|
||||||
if user.get('language') is not None:
|
if user.get('language') is not None:
|
||||||
logger.debug("Language is set to {}".format(user.get('language')))
|
logger.debug(
|
||||||
|
"Language is set to {}".format(user.get('language')))
|
||||||
lang = user.get('language')
|
lang = user.get('language')
|
||||||
translation.activate(lang)
|
translation.activate(lang)
|
||||||
# Send notification to the user as soon as VM has been booked
|
# Send notification to the user as soon as VM has been booked
|
||||||
|
@ -174,6 +176,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))
|
logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
|
||||||
if new_host is not None:
|
if new_host is not None:
|
||||||
custom_user = CustomUser.objects.get(email=user.get('email'))
|
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:
|
if custom_user is not None:
|
||||||
public_keys = get_all_public_keys(custom_user)
|
public_keys = get_all_public_keys(custom_user)
|
||||||
keys = [{'value': key, 'state': True} for key in
|
keys = [{'value': key, 'state': True} for key in
|
||||||
|
|
|
@ -77,7 +77,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="total">
|
|
||||||
<!--<div class="description check-ip">
|
<!--<div class="description check-ip">
|
||||||
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
||||||
</div>-->
|
</div>-->
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
{% extends "datacenterlight/base.html" %}
|
|
||||||
{% load staticfiles i18n%}
|
|
||||||
{% get_current_language as LANGUAGE_CODE %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="intro-pricing">
|
|
||||||
|
|
||||||
<div class="intro-message">
|
|
||||||
<h2 class="section-heading">{% trans "We are cutting down the costs significantly!" %}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="price-calc-section">
|
|
||||||
<div class="card">
|
|
||||||
<img class="img-beta" src="{% static 'datacenterlight/img/beta-img.png' %}" alt="">
|
|
||||||
<div class="caption">
|
|
||||||
<form method="POST" action="">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<div class="title">
|
|
||||||
<h3>{% trans "VM hosting" %} </h3>
|
|
||||||
</div>
|
|
||||||
<div class="price">
|
|
||||||
<span id="total">15</span>
|
|
||||||
<span>CHF</span>
|
|
||||||
<div class="price-text">
|
|
||||||
<p>{% trans "VAT included" %}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="descriptions">
|
|
||||||
<div class="description">
|
|
||||||
<p>{% trans "Hosted in Switzerland" %}</p>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="cpu" aria-hidden="true"></i>
|
|
||||||
<input class="input-price" type="number" min="1" max="42" id="coreValue" name="cpu">
|
|
||||||
<span> Core</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="cpu" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="ram" aria-hidden="true"></i>
|
|
||||||
<input id="ramValue" class="input-price" type="number" min="2" max="200" name="ram">
|
|
||||||
<span> GB RAM</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="ram" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
<div class="description">
|
|
||||||
<i class="fa fa-minus-circle left" data-minus="storage" aria-hidden="true"></i>
|
|
||||||
<input id="storageValue" class="input-price" type="number" min="10" max="500" step="10" name="storage">
|
|
||||||
<span>{% trans "GB Storage (SSD)" %}</span>
|
|
||||||
<i class="fa fa-plus-circle right" data-plus="storage" aria-hidden="true"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="description select-configuration input">
|
|
||||||
<label for="name">OS</label>
|
|
||||||
<select name="config" id="">
|
|
||||||
{% for template in templates %}
|
|
||||||
<option value="{{template.id}}">{{template.name}} </option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="total">
|
|
||||||
|
|
||||||
<!-- <div class="description input">
|
|
||||||
<label for="name">Name</label>
|
|
||||||
<input type="text" name="name" placeholder="Your Name">
|
|
||||||
</div>
|
|
||||||
<div class="description input">
|
|
||||||
<label for="email">Email</label>
|
|
||||||
<input type="email" name="email" placeholder="Your Email">
|
|
||||||
</div> -->
|
|
||||||
|
|
||||||
<!--<div class="description check-ip">
|
|
||||||
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
|
|
||||||
</div>-->
|
|
||||||
</div>
|
|
||||||
<input type="submit" class="btn btn-primary" value="{% trans 'Order Now!' %}"></input>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text">
|
|
||||||
<h2 class="section-heading">{% trans "Simple and affordable: Try our virtual machine with featherlight price." %}</h2>
|
|
||||||
|
|
||||||
<div class="description">
|
|
||||||
<p>{% trans "Our VMs are hosted in Glarus, Switzerland, and our website is currently running in BETA mode. If you want more information that you did not find on our website, or if your order is more detailed, or if you encounter any technical hiccups, please contact us at support@datacenterlight.ch, our team will get in touch with you asap." %}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ from datacenterlight.models import VMTemplate
|
||||||
from datacenterlight.tasks import create_vm_task
|
from datacenterlight.tasks import create_vm_task
|
||||||
from membership.models import StripeCustomer
|
from membership.models import StripeCustomer
|
||||||
from opennebula_api.serializers import VMTemplateSerializer
|
from opennebula_api.serializers import VMTemplateSerializer
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
from utils.models import BillingAddress
|
from utils.models import BillingAddress
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
|
|
||||||
|
@ -94,12 +95,11 @@ class CeleryTaskTestCase(TestCase):
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
disk_size=disk_size)
|
||||||
cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import IndexView, BetaProgramView, LandingProgramView, \
|
from .views import IndexView, BetaProgramView, LandingProgramView, \
|
||||||
BetaAccessView, PricingView, SuccessView, \
|
BetaAccessView, SuccessView, \
|
||||||
PaymentOrderView, OrderConfirmationView, \
|
PaymentOrderView, OrderConfirmationView, \
|
||||||
WhyDataCenterLightView, ContactUsView
|
WhyDataCenterLightView, ContactUsView
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ urlpatterns = [
|
||||||
name='whydatacenterlight'),
|
name='whydatacenterlight'),
|
||||||
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
|
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
|
||||||
url(r'^landing/?$', LandingProgramView.as_view(), name='landing'),
|
url(r'^landing/?$', LandingProgramView.as_view(), name='landing'),
|
||||||
url(r'^pricing/?$', PricingView.as_view(), name='pricing'),
|
|
||||||
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
|
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
|
||||||
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
|
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
|
||||||
name='order_confirmation'),
|
name='order_confirmation'),
|
||||||
|
|
|
@ -4,7 +4,6 @@ from django.contrib import messages
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import redirect
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.decorators.cache import cache_control
|
from django.views.decorators.cache import cache_control
|
||||||
|
@ -13,10 +12,9 @@ from django.views.generic import FormView, CreateView, TemplateView, DetailView
|
||||||
from datacenterlight.tasks import create_vm_task
|
from datacenterlight.tasks import create_vm_task
|
||||||
from hosting.models import HostingOrder
|
from hosting.models import HostingOrder
|
||||||
from membership.models import CustomUser, StripeCustomer
|
from membership.models import CustomUser, StripeCustomer
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.serializers import VMTemplateSerializer
|
||||||
from opennebula_api.serializers import VirtualMachineTemplateSerializer, \
|
|
||||||
VMTemplateSerializer
|
|
||||||
from utils.forms import BillingAddressForm
|
from utils.forms import BillingAddressForm
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from utils.tasks import send_plain_email_task
|
from utils.tasks import send_plain_email_task
|
||||||
|
@ -88,56 +86,6 @@ class SuccessView(TemplateView):
|
||||||
return render(request, self.template_name)
|
return render(request, self.template_name)
|
||||||
|
|
||||||
|
|
||||||
class PricingView(TemplateView):
|
|
||||||
template_name = "datacenterlight/pricing.html"
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
manager = OpenNebulaManager()
|
|
||||||
templates = manager.get_templates()
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'templates': VirtualMachineTemplateSerializer(templates,
|
|
||||||
many=True).data,
|
|
||||||
}
|
|
||||||
except:
|
|
||||||
messages.error(request,
|
|
||||||
'We have a temporary problem to connect to our backend. \
|
|
||||||
Please try again in a few minutes'
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
'error': 'connection'
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, self.template_name, context)
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
|
|
||||||
cores = request.POST.get('cpu')
|
|
||||||
memory = request.POST.get('ram')
|
|
||||||
storage = request.POST.get('storage')
|
|
||||||
price = request.POST.get('total')
|
|
||||||
|
|
||||||
template_id = int(request.POST.get('config'))
|
|
||||||
manager = OpenNebulaManager()
|
|
||||||
template = manager.get_template(template_id)
|
|
||||||
|
|
||||||
request.session['template'] = VirtualMachineTemplateSerializer(
|
|
||||||
template).data
|
|
||||||
|
|
||||||
if not request.user.is_authenticated():
|
|
||||||
request.session['next'] = reverse('hosting:payment')
|
|
||||||
|
|
||||||
request.session['specs'] = {
|
|
||||||
'cpu': cores,
|
|
||||||
'memory': memory,
|
|
||||||
'disk_size': storage,
|
|
||||||
'price': price,
|
|
||||||
}
|
|
||||||
|
|
||||||
return redirect(reverse('hosting:payment'))
|
|
||||||
|
|
||||||
|
|
||||||
class BetaAccessView(FormView):
|
class BetaAccessView(FormView):
|
||||||
template_name = "datacenterlight/beta_access.html"
|
template_name = "datacenterlight/beta_access.html"
|
||||||
form_class = BetaAccessForm
|
form_class = BetaAccessForm
|
||||||
|
@ -274,7 +222,6 @@ class IndexView(CreateView):
|
||||||
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||||
storage = request.POST.get('storage')
|
storage = request.POST.get('storage')
|
||||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
price = request.POST.get('total')
|
|
||||||
template_id = int(request.POST.get('config'))
|
template_id = int(request.POST.get('config'))
|
||||||
template = VMTemplate.objects.filter(
|
template = VMTemplate.objects.filter(
|
||||||
opennebula_vm_template_id=template_id).first()
|
opennebula_vm_template_id=template_id).first()
|
||||||
|
@ -334,7 +281,6 @@ class IndexView(CreateView):
|
||||||
'cpu': cores,
|
'cpu': cores,
|
||||||
'memory': memory,
|
'memory': memory,
|
||||||
'disk_size': storage,
|
'disk_size': storage,
|
||||||
'price': price
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this_user = {
|
this_user = {
|
||||||
|
@ -532,12 +478,12 @@ class OrderConfirmationView(DetailView):
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
disk_size=disk_size)
|
||||||
cpu=cpu,
|
specs['price'] = amount_to_be_charged
|
||||||
memory=memory,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
disk_size=disk_size)
|
memory=memory,
|
||||||
|
disk_size=disk_size)
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
|
34
hosting/migrations/0043_vmdetail.py
Normal file
34
hosting/migrations/0043_vmdetail.py
Normal file
|
@ -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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model):
|
||||||
instance = cls.objects.create(customer=customer,
|
instance = cls.objects.create(customer=customer,
|
||||||
billing_address=billing_address)
|
billing_address=billing_address)
|
||||||
return instance
|
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)
|
||||||
|
|
|
@ -47,20 +47,5 @@
|
||||||
window.location.href = '{{next_url}}';
|
window.location.href = '{{next_url}}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
{% for user_key in keys %}
|
|
||||||
var locale_date = moment.utc(document.getElementById("ssh-created_at-{{user_key.id}}").textContent,'YYYY-MM-DD HH:mm').toDate();
|
|
||||||
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
|
||||||
document.getElementById('ssh-created_at-{{user_key.id}}').innerHTML = locale_date;
|
|
||||||
{% endfor %}
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@
|
||||||
<p>
|
<p>
|
||||||
<strong>{% trans "Status" %}: </strong>
|
<strong>{% trans "Status" %}: </strong>
|
||||||
<strong>
|
<strong>
|
||||||
{% if order.status == 'Approved' %}
|
{% if vm.terminated_at %}
|
||||||
|
<span class="vm-color-failed">{% trans "Terminated" %}</span>
|
||||||
|
{% elif order.status == 'Approved' %}
|
||||||
<span class="vm-color-online">{% trans "Approved" %}</span>
|
<span class="vm-color-online">{% trans "Approved" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="vm-status-failed">{% trans "Declined" %}</span>
|
<span class="vm-status-failed">{% trans "Declined" %}</span>
|
||||||
|
@ -199,4 +201,4 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
|
||||||
<script src="{% static 'hosting/js/html2pdf.js' %}"></script>
|
<script src="{% static 'hosting/js/html2pdf.js' %}"></script>
|
||||||
<script src="{% static 'hosting/js/order.js' %}"></script>
|
<script src="{% static 'hosting/js/order.js' %}"></script>
|
||||||
{% endblock js_extra %}
|
{% endblock js_extra %}
|
||||||
|
|
|
@ -101,21 +101,5 @@
|
||||||
window.location.href = '{{next_url}}';
|
window.location.href = '{{next_url}}';
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
{% for user_key in keys %}
|
|
||||||
var locale_date = moment.utc(document.getElementById("ssh-created_at-{{user_key.id}}").textContent,'YYYY-MM-DD HH:mm').toDate();
|
|
||||||
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
|
|
||||||
document.getElementById('ssh-created_at-{{user_key.id}}').innerHTML = locale_date;
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
from datetime import datetime
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
@ -37,6 +38,7 @@ from utils.forms import (
|
||||||
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
||||||
ResendActivationEmailForm
|
ResendActivationEmailForm
|
||||||
)
|
)
|
||||||
|
from utils.hosting_utils import get_vm_price
|
||||||
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 (
|
from utils.views import (
|
||||||
|
@ -46,10 +48,11 @@ from utils.views import (
|
||||||
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
|
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
|
||||||
UserHostingKeyForm, generate_ssh_key_name
|
UserHostingKeyForm, generate_ssh_key_name
|
||||||
from .mixins import ProcessVMSelectionMixin
|
from .mixins import ProcessVMSelectionMixin
|
||||||
from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey
|
from .models import (
|
||||||
|
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail
|
||||||
|
)
|
||||||
from datacenterlight.models import VMTemplate
|
from datacenterlight.models import VMTemplate
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
||||||
|
@ -689,25 +692,29 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
# invoice for previous order
|
# invoice for previous order
|
||||||
try:
|
try:
|
||||||
manager = OpenNebulaManager(
|
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
|
||||||
email=owner.email, password=owner.password
|
context['vm'] = vm_detail.__dict__
|
||||||
)
|
except VMDetail.DoesNotExist:
|
||||||
vm = manager.get_vm(obj.vm_id)
|
try:
|
||||||
context['vm'] = VirtualMachineSerializer(vm).data
|
manager = OpenNebulaManager(
|
||||||
except WrongIdError:
|
email=owner.email, password=owner.password
|
||||||
messages.error(
|
)
|
||||||
self.request,
|
vm = manager.get_vm(obj.vm_id)
|
||||||
_('The VM you are looking for is unavailable at the '
|
context['vm'] = VirtualMachineSerializer(vm).data
|
||||||
'moment. Please contact Data Center Light support.')
|
except WrongIdError:
|
||||||
)
|
messages.error(
|
||||||
self.kwargs['error'] = 'WrongIdError'
|
self.request,
|
||||||
context['error'] = 'WrongIdError'
|
_('The VM you are looking for is unavailable at the '
|
||||||
except ConnectionRefusedError:
|
'moment. Please contact Data Center Light support.')
|
||||||
messages.error(
|
)
|
||||||
self.request,
|
self.kwargs['error'] = 'WrongIdError'
|
||||||
_('In order to create a VM, you need to create/upload '
|
context['error'] = 'WrongIdError'
|
||||||
'your SSH KEY first.')
|
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'):
|
elif not card_details.get('response_object'):
|
||||||
# new order, failed to get card details
|
# new order, failed to get card details
|
||||||
context['failed_payment'] = True
|
context['failed_payment'] = True
|
||||||
|
@ -767,12 +774,11 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
cpu = specs.get('cpu')
|
cpu = specs.get('cpu')
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
|
||||||
plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
disk_size=disk_size)
|
||||||
cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
|
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
@ -817,9 +823,10 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
||||||
'status': True,
|
'status': True,
|
||||||
'redirect': reverse('hosting:virtual_machines'),
|
'redirect': reverse('hosting:virtual_machines'),
|
||||||
'msg_title': str(_('Thank you for the order.')),
|
'msg_title': str(_('Thank you for the order.')),
|
||||||
'msg_body': str(_('Your VM will be up and running in a few moments.'
|
'msg_body': str(
|
||||||
' We will send you a confirmation email as soon as'
|
_('Your VM will be up and running in a few moments.'
|
||||||
' it is ready.'))
|
' We will send you a confirmation email as soon as'
|
||||||
|
' it is ready.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
return HttpResponse(json.dumps(response),
|
return HttpResponse(json.dumps(response),
|
||||||
|
@ -916,7 +923,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||||
storage = request.POST.get('storage')
|
storage = request.POST.get('storage')
|
||||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
price = request.POST.get('total')
|
|
||||||
template_id = int(request.POST.get('config'))
|
template_id = int(request.POST.get('config'))
|
||||||
template = VMTemplate.objects.filter(
|
template = VMTemplate.objects.filter(
|
||||||
opennebula_vm_template_id=template_id).first()
|
opennebula_vm_template_id=template_id).first()
|
||||||
|
@ -948,7 +954,8 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
extra_tags='storage')
|
extra_tags='storage')
|
||||||
return HttpResponseRedirect(
|
return HttpResponseRedirect(
|
||||||
reverse('datacenterlight:index') + "#order_form")
|
reverse('datacenterlight:index') + "#order_form")
|
||||||
|
price = get_vm_price(cpu=cores, memory=memory,
|
||||||
|
disk_size=storage)
|
||||||
specs = {
|
specs = {
|
||||||
'cpu': cores,
|
'cpu': cores,
|
||||||
'memory': memory,
|
'memory': memory,
|
||||||
|
@ -1054,6 +1061,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
except WrongIdError:
|
except WrongIdError:
|
||||||
response['status'] = True
|
response['status'] = True
|
||||||
response['text'] = ugettext('Terminated')
|
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
|
break
|
||||||
except BaseException:
|
except BaseException:
|
||||||
break
|
break
|
||||||
|
|
|
@ -1,4 +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):
|
def get_all_public_keys(customer):
|
||||||
|
@ -9,3 +15,48 @@ def get_all_public_keys(customer):
|
||||||
"""
|
"""
|
||||||
return UserHostingKey.objects.filter(user_id=customer.id).values_list(
|
return UserHostingKey.objects.filter(user_id=customer.id).values_list(
|
||||||
"public_key", flat=True)
|
"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
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_price(cpu, memory, disk_size):
|
||||||
|
"""
|
||||||
|
A helper function that computes price of a VM from given cpu, ram and
|
||||||
|
ssd parameters
|
||||||
|
|
||||||
|
:param cpu: Number of cores of the VM
|
||||||
|
:param memory: RAM of the VM
|
||||||
|
:param disk_size: Disk space of the VM
|
||||||
|
:return: The price of the VM
|
||||||
|
"""
|
||||||
|
return (cpu * 5) + (memory * 2) + (disk_size * 0.6)
|
||||||
|
|
|
@ -238,7 +238,7 @@ class StripeUtils(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None):
|
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None):
|
||||||
"""
|
"""
|
||||||
Returns the stripe plan id string of the form
|
Returns the Stripe plan id string of the form
|
||||||
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
||||||
|
|
||||||
:param cpu: The number of cores
|
:param cpu: The number of cores
|
||||||
|
@ -256,7 +256,19 @@ class StripeUtils(object):
|
||||||
if hdd is not None:
|
if hdd is not None:
|
||||||
dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(
|
dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(
|
||||||
dcl_plan_string=dcl_plan_string, hdd=hdd)
|
dcl_plan_string=dcl_plan_string, hdd=hdd)
|
||||||
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(app=app,
|
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(
|
||||||
version=version,
|
app=app,
|
||||||
plan=dcl_plan_string)
|
version=version,
|
||||||
|
plan=dcl_plan_string)
|
||||||
return stripe_plan_id_string
|
return stripe_plan_id_string
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_stripe_plan_name(cpu, memory, disk_size):
|
||||||
|
"""
|
||||||
|
Returns the Stripe plan name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
||||||
|
cpu=cpu,
|
||||||
|
memory=memory,
|
||||||
|
disk_size=disk_size)
|
||||||
|
|
Loading…
Reference in a new issue