merge master

This commit is contained in:
Arvind Tiwari 2017-09-28 18:37:33 +05:30
commit ba366eed77
27 changed files with 916 additions and 678 deletions

1
.gitignore vendored
View file

@ -36,3 +36,4 @@ secret-key
.env
*.mo
*.log
*.sql

View file

@ -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
* #3756: [dcl] Update landing calculator and billing info page
1.2.3: 2017-09-25
* #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email
* #3731: [dcl, hosting] Added cdist ssh key handler

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 04:21+0530\n"
"POT-Creation-Date: 2017-09-28 18:36+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -65,24 +65,6 @@ msgstr "Bitte gib einen Wert von 10 bis 200 ein."
msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)"
msgid "Name"
msgstr ""
msgid "Your Name"
msgstr "Dein Name"
msgid "Please enter your name."
msgstr "Bitte gib Deinen Namen ein."
msgid "Email"
msgstr "E-Mail-Adresse"
msgid "Your Email"
msgstr "Deine E-Mail"
msgid "Please enter a valid email address."
msgstr "Bitte gib eine gültige E-Mailadresse ein."
msgid "Continue"
msgstr "Weiter"
@ -95,6 +77,18 @@ msgstr "Vielen Dank für Deine Nachricht."
msgid "Get in touch with us!"
msgstr "Sende uns eine Nachricht."
msgid "Name"
msgstr ""
msgid "Please enter your name."
msgstr "Bitte gib Deinen Namen ein."
msgid "Email"
msgstr "E-Mail-Adresse"
msgid "Please enter a valid email address."
msgstr "Bitte gib eine gültige E-Mailadresse ein."
msgid "Message"
msgstr "Nachricht"
@ -270,6 +264,45 @@ msgstr "Kontaktiere uns"
msgid "Switzerland "
msgstr "Schweiz "
msgid "Welcome back"
msgstr "Willkommen zurück"
msgid ""
"Review your billing address and card details and proceed to make payment."
msgstr ""
"Überprüfe die Rechnungsadresse und Kreditkartendaten und fahre mit der "
"Zahlung fort."
msgid "Log in"
msgstr "Anmelden"
msgid ""
"Already signed up?<br>By logging in you can retrieve saved billing "
"information."
msgstr ""
"Bereits eingeloggt? Nach der Anmeldung kannst Du gespeicherte "
"Rechnungsinformationen abrufen."
msgid "LOGIN"
msgstr "ANMELDEN"
msgid "Don't have an account yet?"
msgstr "Besitzt du kein Benutzerkonto?"
msgid "You can sign up by filling in the information below."
msgstr ""
"Du kannst Dich anmelden, indem Du die die untenstehenden Informationen "
"ausfüllst."
msgid "Forgot password?"
msgstr "Passwort vergessen?"
msgid "Sign up"
msgstr "Registrieren"
msgid "Billing Address"
msgstr "Rechnungsadresse"
msgid "Your Order"
msgstr "Deine Bestellung"
@ -294,23 +327,15 @@ msgstr "inkl. Mehrwertsteuer"
msgid "Month"
msgstr "Monat"
msgid "Billing Address"
msgstr "Rechnungsadresse"
msgid "Credit Card"
msgstr "Kreditkarte"
msgid ""
"\n"
" Please fill in your credit card information "
"below. We are using <a\n"
" href=\"https://stripe.com\" target="
"\"_blank\">Stripe</a> for payment and do not store\n"
" your information in our database.\n"
" "
"Please fill in your credit card information below. We are using <a href="
"\"https://stripe.com\" target=\"_blank\">Stripe</a> for payment and do not "
"store your information in our database."
msgstr ""
"\n"
"Bitte füll Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
"Bitte fülle Deine Kreditkarteninformationen unten aus. Wir nutzen <a href="
"\"https://stripe.com\" target=\"_blank\">Stripe</a> für die Bezahlung und "
"speichern keine Informationen in unserer Datenbank."
@ -336,6 +361,13 @@ msgstr ""
msgid "Card Type"
msgstr "Kartentyp"
msgid ""
"You are not making any payment yet. After placing your order, you will be "
"taken to the Submit Payment Page."
msgstr ""
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
msgid "Processing"
msgstr "Weiter"
@ -371,26 +403,20 @@ msgstr ""
msgid "Place order"
msgstr "Bestellen"
msgid "We are cutting down the costs significantly!"
msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
msgid "Processing..."
msgstr "Abarbeitung..."
msgid "Order Now!"
msgstr "Bestelle jetzt!"
msgid "Hold tight, we are processing your request"
msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade"
msgid ""
"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."
msgid "OK"
msgstr ""
"Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden sich "
"zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren und hast "
"auf unserer Website nicht genügend Informationen gefunden? Möchtest eine "
"detailliertere Bestellung aufgeben? Bist du auf technische Probleme "
"gestossen, die du uns mitteilen möchtest? Dann zögere nicht und kontaktiere "
"uns unter support@datacenterlight.ch. Unser Team wird sich umgehend um dein "
"Anliegen kümmern!"
msgid "Close"
msgstr ""
msgid "Some problem encountered. Please try again later."
msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal."
msgid "Thank you for order! Our team will contact you via email"
msgstr ""
@ -485,11 +511,59 @@ msgstr "Ungültige RAM-Grösse"
msgid "Invalid storage size"
msgstr "Ungültige Speicher-Grösse"
msgid "is not a proper name"
msgstr "ist kein gültiger Name"
msgid "Error."
msgstr ""
msgid "is not a proper email"
msgstr "ist keine gültige E-Mailadresse"
msgid ""
"There was a payment related error. On close of this popup, you will be "
"redirected back to the payment page."
msgstr ""
msgid "Thank you for the order."
msgstr "Danke für Deine Bestellung."
msgid ""
"Your VM will be up and running in a few moments. We will send you a "
"confirmation email as soon as it is ready."
msgstr ""
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
"auf sie zugreifen kannst."
#~ msgid "We are cutting down the costs significantly!"
#~ msgstr "Wir sorgen dafür, dass die Kosten für Dich signifikant abnehmen"
#~ msgid "Order Now!"
#~ msgstr "Bestelle jetzt!"
#~ msgid ""
#~ "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."
#~ msgstr ""
#~ "Unsere VMs werden in der Schweiz im Kanton Glarus gehostet und befinden "
#~ "sich zur Zeit noch in der BETA-Phase. Möchtest du mehr über uns erfahren "
#~ "und hast auf unserer Website nicht genügend Informationen gefunden? "
#~ "Möchtest eine detailliertere Bestellung aufgeben? Bist du auf technische "
#~ "Probleme gestossen, die du uns mitteilen möchtest? Dann zögere nicht und "
#~ "kontaktiere uns unter support@datacenterlight.ch. Unser Team wird sich "
#~ "umgehend um dein Anliegen kümmern!"
#~ msgid "Email Address"
#~ msgstr "E-Mail-Adresse"
#~ msgid "is not a proper name"
#~ msgstr "ist kein gültiger Name"
#~ msgid "is not a proper email"
#~ msgstr "ist keine gültige E-Mailadresse"
#~ msgid "Your Name"
#~ msgstr "Dein Name"
#~ msgid "Your Email"
#~ msgstr "Deine E-Mail"
#~ msgid ""
#~ "\n"

View file

@ -155,9 +155,7 @@
function _calcPricing() {
var total = (cardPricing['cpu'].value * 5) + (2 * cardPricing['ram'].value) + (0.6 * cardPricing['storage'].value);
total = parseFloat(total.toFixed(2));
$("#total").text(total);
$('input[name=total]').val(total);
}
function form_success() {
@ -191,4 +189,4 @@
});
})
}
})(jQuery);
})(jQuery);

View file

@ -14,7 +14,7 @@ from hosting.models import HostingOrder, HostingBill
from membership.models import StripeCustomer, CustomUser
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineSerializer
from utils.hosting_utils import get_all_public_keys
from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail
from utils.forms import UserBillingAddressForm
from utils.mailer import BaseEmail
from utils.models import BillingAddress
@ -53,8 +53,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
stripe_customer_id, billing_address_data,
billing_address_id,
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
try:
final_price = specs.get('price')
@ -146,8 +146,8 @@ def create_vm_task(self, vm_template_id, user, specs, template,
if 'pass' in user:
lang = 'en-us'
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')
translation.activate(lang)
# Send notification to the user as soon as VM has been booked
@ -178,6 +178,7 @@ def create_vm_task(self, vm_template_id, user, specs, template,
logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id))
if new_host is not None:
custom_user = CustomUser.objects.get(email=user.get('email'))
get_or_create_vm_detail(custom_user, manager, vm_id)
if custom_user is not None:
public_keys = get_all_public_keys(custom_user)
keys = [{'value': key, 'state': True} for key in

View file

@ -77,47 +77,9 @@
{% endfor %}
</select>
</div>
<input type="hidden" name="total">
<!--<div class="description check-ip">
<input type="checkbox" name="ipv6"> Ipv6 Only<br>
</div>-->
<div class="form-group">
<div class="description input justify-center">
<label for="name" class="control-label">{% trans "Name"%}</label>
<input type="text" name="name" class="form-control" placeholder="{% trans 'Your Name'%}"
data-minlength="3" data-error="{% trans 'Please enter your name.' %}" required>
</div>
<div class="help-block with-errors">
{% for message in messages %}
{% if 'name' in message.tags %}
<ul class="list-unstyled">
<li>
{{ message|safe }}
</li>
</ul>
{% endif %}
{% endfor %}
</div>
</div>
<div class="form-group">
<div class="description input justify-center">
<label for="email" class="control-label">{% trans "Email"%}</label>
<input name="email" type="email" pattern="^[^@\s]+@([^@\s]+\.)+[^@\s]+$" class="form-control"
placeholder="{% trans 'Your Email' %}"
data-error="{% trans 'Please enter a valid email address.' %}" required>
</div>
<div class="help-block with-errors">
{% for message in messages %}
{% if 'email' in message.tags %}
<ul class="list-unstyled">
<li>
{{ message|safe }}
</li>
</ul>
{% endif %}
{% endfor %}
</div>
</div>
</div>
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
</form>

View file

@ -1,190 +1,182 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 i18n %}
{% block css_extra %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paymentfont/1.1.2/css/paymentfont.min.css"/>
{% endblock css_extra %}
{% block navbar %}
{% include "datacenterlight/includes/_navbar.html" %}
{% endblock navbar %}
{% block content %}
<!-- Credit card form -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/paymentfont/1.1.2/css/paymentfont.min.css"/>
<div class="dcl-order-container">
<div class="payment-container">
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-sec">
<h3><strong>{%trans "Your Order" %}</strong></h3>
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-header">
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-header">
{%trans "Cores" %}
</div>
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-header">
{%trans "Memory" %}
</div>
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-header">
{%trans "Disk space" %}
</div>
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-header">
{%trans "Configuration" %}
</div>
</div>
<div class="col-xs-6 col-sm-12 col-md-12 col-lg-12 dcl-order-table-content">
<div class="col-xs-12 col-sm-2 col-md-1 col-lg-1 tbl-content">
{{request.session.specs.cpu|floatformat}}
</div>
<div class="col-xs-12 col-sm-3 col-md-4 col-lg-4 tbl-content">
{{request.session.specs.memory|floatformat}} GB
</div>
<div class="col-xs-12 col-sm-3 col-md-3 col-lg-3 tbl-content">
{{request.session.specs.disk_size|floatformat}} GB
</div>
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 tbl-content">
{{request.session.template.name}}
</div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-order-table-total">
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-tot tbl-no-padding">
{%trans "Total" %} <span>{%trans "including VAT" %}</span>
</div>
<div class="col-xs-6 col-sm-6 col-md-6 col-lg-6 tbl-no-padding">
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4"></div>
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 tbl-total">{{request.session.specs.price}}
CHF<span class="dcl-price-month">/{% trans "Month" %}</span>
<div class="dcl-payment-grid">
<div class="dcl-payment-box">
<div class="dcl-payment-section">
{% if request.user.is_authenticated %}
<div class="dcl-payment-user">
<h4>{% trans "Welcome back" %} {{request.user.name}}!</h4>
<p>{% trans "Review your billing address and card details and proceed to make payment." %}</p>
</div>
</div>
{% else %}
<h3>{%trans "Log in" %}</h3>
<hr class="top-hr">
<p style="margin-bottom: 20px;">{% blocktrans %}Already signed up?<br>By logging in you can retrieve saved billing information.{% endblocktrans %}</p>
<form role="form" id="login-form" method="post" action="{% url 'hosting:login' %}" novalidate>
{% for field in login_form %}
{% csrf_token %}
{% bootstrap_field field show_label=False type='fields'%}
{% endfor %}
<input type='hidden' name='next' value='{{request.path}}'/>
<div class="form-group text-right">
<button type="submit" class="btn btn-wide btn-vm-contact">{% trans "LOGIN" %}</button>
</div>
</form>
<p>
{% trans "Don't have an account yet?" %}<br>
{% trans "You can sign up by filling in the information below." %}<br>
<a href="{% url 'hosting:reset_password' %}">{% trans "Forgot password?" %}</a>
</p>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 dcl-billing-sec">
<div class="col-xs-12 col-sm-5 col-md-6 billing dcl-billing">
<h3><b>{%trans "Billing Address"%}</b></h3>
<hr>
<div class="dcl-payment-box">
<div class="dcl-payment-section">
{% if not request.user.is_authenticated %}
<h3><b>{%trans "Sign up"%}</b></h3>
{% else %}
<h3><b>{%trans "Billing Address"%}</b></h3>
{% endif %}
<hr class="top-hr">
{% for message in messages %}
{% if 'duplicate_email' in message.tags %}
<p class="text-danger">{{message}}</p>
{% endif %}
{% endfor %}
<form role="form" id="billing-form" method="post" action="" novalidate>
{% for field in form %}
{% csrf_token %}
{% for field in form %}
{% bootstrap_field field show_label=False type='fields'%}
{% endfor %}
</form>
</div>
<div class="col-xs-12 col-sm-7 col-md-6 creditcard-box dcl-creditcard">
</div>
<div class="dcl-payment-box">
<div class="dcl-payment-section">
<h3>{%trans "Your Order" %}</h3>
<hr class="top-hr">
<div class="dcl-payment-order">
<p>{% trans "Cores"%} <strong class="pull-right">{{request.session.specs.cpu|floatformat}}</strong></p>
<hr>
<p>{% trans "Memory"%} <strong class="pull-right">{{request.session.specs.memory|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Disk space"%} <strong class="pull-right">{{request.session.specs.disk_size|floatformat}} GB</strong></p>
<hr>
<p>{% trans "Configuration"%} <strong class="pull-right">{{request.session.template.name}}</strong></p>
<hr>
<p class="last-p"><strong>{%trans "Total" %}</strong>&nbsp;&nbsp;<small>({%trans "including VAT" %})</small> <strong class="pull-right">{{request.session.specs.price}} CHF/{% trans "Month" %}</strong></p>
</div>
</div>
</div>
<div class="dcl-payment-box">
<div class="dcl-payment-section">
<h3><b>{%trans "Credit Card"%}</b></h3>
<hr>
<hr class="top-hr">
<p>
{% blocktrans %}Please fill in your credit card information below. We are using <a href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store your information in our database.{% endblocktrans %}
</p>
<div>
<div>
<p>
{% blocktrans %}
Please fill in your credit card information below. We are using <a
href="https://stripe.com" target="_blank">Stripe</a> for payment and do not store
your information in our database.
{% endblocktrans %}
</p>
</div>
<br>
<div>
{% if credit_card_data.last4 %}
{% if credit_card_data.last4 %}
<form role="form" id="payment-form-with-creditcard" novalidate>
<h5 class="billing-head">Credit Card</h5>
<h5 class="membership-lead">Last 4: *****{{credit_card_data.last4}}</h5>
<h5 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h5>
<input type="hidden" name="credit_card_needed" value="false"/>
</form>
<div class="row">
<div class="col-xs-12">
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content card-warning-addtional-margin">
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled"><li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li></ul>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
</div>
<div class="col-xs-12">
<div class="col-xs-6 pull-right">
<button id="payment_button_with_creditcard" class="btn btn-success stripe-payment-btn"
type="submit">
{%trans "Submit" %}
</button>
</div>
</div>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content card-warning-addtional-margin">
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled"><li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li></ul>
{% endif %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
{% else %}
<div class="text-right">
<button id="payment_button_with_creditcard" class="btn btn-success btn-vm-contact" type="submit">{%trans "Submit" %}</button>
</div>
{% else %}
<form action="" id="payment-form-new" method="POST">
<input type="hidden" name="token"/>
<div class="group">
<div class="col-xs-12 col-sm-12 col-md-10 col-lg-9 credit-card-goup">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 card-element card-number-element">
<div class="credit-card-goup">
<div class="card-element card-number-element">
<label>{%trans "Card Number" %}</label>
<div id="card-number-element" class="field my-input"></div>
</div>
<div class="col-xs-5 col-sm-3 col-md-3 col-lg-3 card-element card-expiry-element">
<label>{%trans "Expiry Date" %}</label>
<div id="card-expiry-element" class="field my-input"></div>
<div class="row">
<div class="col-xs-5 card-element card-expiry-element">
<label>{%trans "Expiry Date" %}</label>
<div id="card-expiry-element" class="field my-input"></div>
</div>
<div class="col-xs-3 col-xs-offset-4 card-element card-cvc-element">
<label>{%trans "CVC" %}</label>
<div id="card-cvc-element" class="field my-input"></div>
</div>
</div>
<div class="col-xs-12 col-sm-2 col-md-6 col-lg-7 hide-mobile"></div>
<div class="col-xs-3 col-sm-3 col-md-3 col-lg-2 card-element card-cvc-element">
<label>{%trans "CVC" %}</label>
<div id="card-cvc-element" class="field my-input"></div>
</div>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 card-element brand">
<div class="card-element brand">
<label>{%trans "Card Type" %}</label>
<i class="pf pf-credit-card" id="brand-icon"></i>
</div>
</div>
</div>
<div id="card-errors" role="alert"></div>
<div class="row">
<div class="col-xs-12">
{% if not messages and not form.non_field_errors %}
<div id="card-errors"></div>
{% if not messages and not form.non_field_errors %}
<p class="card-warning-content">
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' in message.tags or 'make_charge_error' in message.tags %}
<ul class="list-unstyled">
<li><p class="card-warning-content card-warning-error">{{ message|safe }}</p></li>
</ul>
{% elif not form.non_field_errors %}
<p class="card-warning-content">
{% trans "You are not making any payment yet. After submitting your card information, you will be taken to the Confirm Order Page." %}
{% trans "You are not making any payment yet. After placing your order, you will be taken to the Submit Payment Page." %}
</p>
{% endif %}
<div id='payment_error'>
{% for message in messages %}
{% if 'failed_payment' or 'make_charge_error' in message.tags %}
<ul class="list-unstyled"><li>
<p class="card-warning-content card-warning-error">{{ message|safe }}</p>
</li></ul>
{% endif %}
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
</div>
<div class="col-xs-12">
<div class="col-xs-6 pull-right">
<button class="btn btn-success stripe-payment-btn" type="submit">{%trans "Submit" %}
</button>
</div>
</div>
{% for error in form.non_field_errors %}
<p class="card-warning-content card-warning-error">
{{ error|escape }}
</p>
{% endfor %}
</div>
<div class="text-right">
<button class="btn btn-vm-contact btn-wide" type="submit">{%trans "SUBMIT" %}</button>
</div>
<div class="row" style="display:none;">
<div class="col-xs-12">
<p class="payment-errors"></p>
</div>
<div style="display:none;">
<p class="payment-errors"></p>
</div>
</form>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>

View file

@ -9,98 +9,135 @@
{% block content %}
<div class="order-detail-container">
{% if messages %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<br/>
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
<div class="order-detail-container">
{% if messages %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<br/>
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
</div>
{% endif %}
{% if not error %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div class="invoice-title">
<h2>{% trans "Confirm Order"%}</h2>
</div>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-6 pull-right order-confirm-date">
<address>
<strong>{% trans "Date"%}:</strong><br>
<span id="order-created_at">{% now "Y-m-d H:i" %}</span><br><br>
</address>
</div>
<div class="col-xs-12 col-sm-6">
<address>
<h3><b>{% trans "Billed To:"%}</b></h3>
{% 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 %}
</address>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<address>
<strong>{% trans "Payment Method:"%}</strong><br>
{{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}<br>
{{request.session.user.email}}
</address>
</div>
</div>
</div>
{% endif %}
{% if not error %}
<div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div class="invoice-title">
<h2>{% trans "Confirm Order"%}</h2>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h3><b>{% trans "Order summary"%}</b></h3>
<hr>
<div class="content">
{% with request.session.specs as vm %}
<p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cpu}}</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>
<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>
<hr>
<div class="row">
<div class="col-xs-12 col-sm-6 pull-right order-confirm-date">
<address>
<strong>{% trans "Date"%}:</strong><br>
<span id="order-created_at">{% now "Y-m-d H:i" %}</span><br><br>
</address>
</div>
<div class="col-xs-12 col-sm-6">
<address>
<h3><b>{% trans "Billed To:"%}</b></h3>
{% with billing_address_data as billing_address %}
{{billing_address.cardholder_name}}<br> {{billing_address.street_address}}, {{billing_address.postal_code}}<br>
{{billing_address.city}}, {{billing_address.country}}.
{% endwith %}
</address>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<address>
<strong>{% trans "Payment Method:"%}</strong><br>
{{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}<br>
{{request.session.user.email}}
</address>
</div>
<br/>
<form method="post">
{% csrf_token %}
<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 class="col-sm-4 content">
<a href="{{next_url}}" ><button class="btn btn-info pull-right">{% trans "Place order"%}</button></a>
</div>
</div>
</form>
</div>
</div>
{% endif %}
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h3><b>{% trans "Order summary"%}</b></h3>
<hr>
<div class="content">
{% with request.session.specs as vm %}
<p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cpu}}</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>
<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 %}
</div>
<br/>
<form method="post" id="virtual_machine_create_form">
{% csrf_token %}
<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 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>
</div>
</div>
{% endif %}
</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">&times;</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">
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide hide" href="">{% trans "OK" %}</a>
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
</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 () {
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");
document.getElementById('order-created_at').innerHTML = locale_date;
window.onload = function () {
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");
document.getElementById('order-created_at').innerHTML = locale_date;
};
};
</script>
</script>
{%endblock%}

View file

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

View file

@ -12,6 +12,7 @@ from datacenterlight.models import VMTemplate
from datacenterlight.tasks import create_vm_task
from membership.models import StripeCustomer
from opennebula_api.serializers import VMTemplateSerializer
from utils.hosting_utils import get_vm_price
from utils.models import BillingAddress
from utils.stripe_utils import StripeUtils
@ -94,12 +95,11 @@ class CeleryTaskTestCase(TestCase):
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)
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
disk_size=disk_size)
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
ram=memory,
ssd=disk_size,

View file

@ -2,7 +2,7 @@ from django.conf.urls import url
from .views import (
IndexView, BetaProgramView, LandingProgramView, BetaAccessView,
PricingView, SuccessView, PaymentOrderView, OrderConfirmationView,
SuccessView, PaymentOrderView, OrderConfirmationView,
WhyDataCenterLightView, ContactUsView
)
@ -17,7 +17,6 @@ urlpatterns = [
name='whydatacenterlight'),
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
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'^order-confirmation/?$', OrderConfirmationView.as_view(),
name='order_confirmation'),

View file

@ -1,28 +1,35 @@
import logging
import json
from django import forms
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, authenticate
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import get_language, ugettext_lazy as _
from django.views.decorators.cache import cache_control
from django.views.generic import FormView, CreateView, TemplateView, DetailView
from datacenterlight.tasks import create_vm_task
from hosting.models import HostingOrder
from hosting.forms import HostingUserLoginForm
from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineTemplateSerializer, \
VMTemplateSerializer
from utils.forms import BillingAddressForm
from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import (
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm
)
from utils.hosting_utils import get_vm_price
from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
from .forms import BetaAccessForm, ContactForm
from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate
logger = logging.getLogger(__name__)
class ContactUsView(FormView):
template_name = "datacenterlight/contact_form.html"
@ -88,56 +95,6 @@ class SuccessView(TemplateView):
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):
template_name = "datacenterlight/beta_access.html"
form_class = BetaAccessForm
@ -274,17 +231,11 @@ class IndexView(CreateView):
memory_field = forms.IntegerField(validators=[self.validate_memory])
storage = request.POST.get('storage')
storage_field = forms.IntegerField(validators=[self.validate_storage])
price = request.POST.get('total')
template_id = int(request.POST.get('config'))
template = VMTemplate.objects.filter(
opennebula_vm_template_id=template_id).first()
template_data = VMTemplateSerializer(template).data
name = request.POST.get('name')
email = request.POST.get('email')
name_field = forms.CharField()
email_field = forms.EmailField()
try:
cores = cores_field.clean(cores)
except ValidationError as err:
@ -311,40 +262,16 @@ class IndexView(CreateView):
extra_tags='storage')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try:
name = name_field.clean(name)
except ValidationError as err:
msg = '{} {}.'.format(name, _('is not a proper name'))
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='name')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
try:
email = email_field.clean(email)
except ValidationError as err:
msg = '{} {}.'.format(email, _('is not a proper email'))
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='email')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
amount_to_be_charged = get_vm_price(cpu=cores, memory=memory,
disk_size=storage)
specs = {
'cpu': cores,
'memory': memory,
'disk_size': storage,
'price': price
'price': amount_to_be_charged
}
this_user = {
'name': name,
'email': email
}
request.session['specs'] = specs
request.session['template'] = template_data
request.session['user'] = this_user
return HttpResponseRedirect(reverse('datacenterlight:payment'))
def get_success_url(self):
@ -407,20 +334,19 @@ class WhyDataCenterLightView(IndexView):
class PaymentOrderView(FormView):
template_name = 'datacenterlight/landing_payment.html'
form_class = BillingAddressForm
def get_form_class(self):
if self.request.user.is_authenticated():
return BillingAddressForm
else:
return BillingAddressFormSignup
def get_form_kwargs(self):
form_kwargs = super(PaymentOrderView, self).get_form_kwargs()
billing_address_data = self.request.session.get('billing_address_data')
if billing_address_data:
# if user is signed in, get billing address
if self.request.user.is_authenticated():
form_kwargs.update({
'initial': {
'cardholder_name': billing_address_data['cardholder_name'],
'street_address': billing_address_data['street_address'],
'city': billing_address_data['city'],
'postal_code': billing_address_data['postal_code'],
'country': billing_address_data['country'],
}
'instance': self.request.user.billing_addresses.first()
})
return form_kwargs
@ -428,48 +354,49 @@ class PaymentOrderView(FormView):
context = super(PaymentOrderView, self).get_context_data(**kwargs)
context.update({
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
'site_url': reverse('datacenterlight:index')
'site_url': reverse('datacenterlight:index'),
'login_form': HostingUserLoginForm()
})
return context
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
if 'specs' not in request.session or 'user' not in request.session:
# user is no longer added to session on the index page
if 'specs' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:index'))
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# Get billing address data
billing_address_data = form.cleaned_data
token = form.cleaned_data.get('token')
user = request.session.get('user')
try:
CustomUser.objects.get(email=user.get('email'))
except CustomUser.DoesNotExist:
password = CustomUser.get_random_password()
# Register the user, and do not send emails
CustomUser.register(user.get('name'),
password,
user.get('email'),
app='dcl',
base_url=None, send_email=False)
if request.user.is_authenticated():
this_user = {
'email': request.user.email,
'name': request.user.name
}
customer = StripeCustomer.get_or_create(
email=this_user.get('email'),
token=token)
else:
this_user = {
'email': form.cleaned_data.get('email'),
'name': form.cleaned_data.get('name')
}
customer = StripeCustomer.create_stripe_api_customer(
email=this_user.get('email'),
token=token,
customer_name=form.cleaned_data.get('name'))
request.session['billing_address_data'] = form.cleaned_data
request.session['user'] = this_user
# Get or create stripe customer
customer = StripeCustomer.get_or_create(email=user.get('email'),
token=token)
if not customer:
form.add_error("__all__", "Invalid credit card")
return self.render_to_response(
self.get_context_data(form=form))
# Create Billing Address
billing_address = form.save()
request.session['billing_address_data'] = billing_address_data
request.session['billing_address'] = billing_address.id
request.session['token'] = token
request.session['customer'] = customer.id
request.session[
'customer'] = customer.id if request.user.is_authenticated() else customer
return HttpResponseRedirect(
reverse('datacenterlight:order_confirmation'))
else:
@ -489,9 +416,15 @@ class OrderConfirmationView(DetailView):
if 'token' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:payment'))
stripe_customer_id = request.session.get('customer')
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
if request.user.is_authenticated():
customer = StripeCustomer.objects.filter(
id=stripe_customer_id).first()
stripe_api_cus_id = customer.stripe_id
else:
stripe_api_cus_id = stripe_customer_id
stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(customer.stripe_id,
card_details = stripe_utils.get_card_details(stripe_api_cus_id,
request.session.get(
'token'))
if not card_details.get('response_object'):
@ -503,7 +436,8 @@ class OrderConfirmationView(DetailView):
context = {
'site_url': reverse('datacenterlight:index'),
'cc_last4': card_details.get('response_object').get('last4'),
'cc_brand': card_details.get('response_object').get('brand')
'cc_brand': card_details.get('response_object').get('brand'),
'billing_address_data': request.session.get('billing_address_data')
}
return render(request, self.template_name, context)
@ -512,32 +446,43 @@ class OrderConfirmationView(DetailView):
specs = request.session.get('specs')
user = request.session.get('user')
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')
if request.user.is_authenticated():
customer = StripeCustomer.objects.filter(
id=stripe_customer_id).first()
stripe_api_cus_id = customer.stripe_id
else:
stripe_api_cus_id = stripe_customer_id
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,
card_details = stripe_utils.get_card_details(stripe_api_cus_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')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=reverse('datacenterlight:payment'),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
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)
amount_to_be_charged = specs.get('price')
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
ram=memory,
ssd=disk_size,
@ -548,7 +493,7 @@ class OrderConfirmationView(DetailView):
name=plan_name,
stripe_plan_id=stripe_plan_id)
subscription_result = stripe_utils.subscribe_customer_to_plan(
customer.stripe_id,
stripe_api_cus_id,
[{"plan": stripe_plan.get(
'response_object').stripe_plan_id}])
stripe_subscription_obj = subscription_result.get('response_object')
@ -558,11 +503,96 @@ class OrderConfirmationView(DetailView):
msg = subscription_result.get('error')
messages.add_message(self.request, messages.ERROR, msg,
extra_tags='failed_payment')
return HttpResponseRedirect(
reverse('datacenterlight:payment') + '#payment_error')
response = {
'status': False,
'redirect': "{url}#{section}".format(
url=reverse('datacenterlight:payment'),
section='payment_error'),
'msg_title': str(_('Error.')),
'msg_body': str(
_('There was a payment related error.'
' On close of this popup, you will be redirected back to'
' the payment page.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
# Create user if the user is not logged in and if he is not already
# registered
if not request.user.is_authenticated():
try:
custom_user = CustomUser.objects.get(
email=user.get('email'))
customer = StripeCustomer.objects.filter(
user_id=custom_user.id).first()
stripe_customer_id = customer.id
except CustomUser.DoesNotExist:
logger.debug(
"Customer {} does not exist.".format(user.get('email')))
password = CustomUser.get_random_password()
base_url = "{0}://{1}".format(self.request.scheme,
self.request.get_host())
custom_user = CustomUser.register(
user.get('name'), password,
user.get('email'),
app='dcl', base_url=base_url, send_email=True,
account_details=password
)
logger.debug("Created user {}.".format(user.get('email')))
stripe_customer = StripeCustomer.objects. \
create(user=custom_user, stripe_id=stripe_api_cus_id)
stripe_customer_id = stripe_customer.id
new_user = authenticate(username=custom_user.email,
password=password)
login(request, new_user)
else:
customer = StripeCustomer.objects.filter(
id=stripe_customer_id).first()
custom_user = customer.user
stripe_customer_id = customer.id
# Save billing address
billing_address_data = request.session.get('billing_address_data')
logger.debug('billing_address_data is {}'.format(billing_address_data))
billing_address_data.update({
'user': custom_user.id
})
billing_address_user_form = UserBillingAddressForm(
instance=custom_user.billing_addresses.first(),
data=billing_address_data)
billing_address = billing_address_user_form.save()
billing_address_id = billing_address.id
logger.debug("billing address id = {}".format(billing_address_id))
user = {
'name': custom_user.name,
'email': custom_user.email,
'pass': custom_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)
request.session['order_confirmation'] = True
return HttpResponseRedirect(reverse('datacenterlight:order_success'))
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') if request.user.is_authenticated() else reverse(
'datacenterlight:index'),
'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")

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 04:21+0530\n"
"POT-Creation-Date: 2017-09-28 18:36+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -198,6 +198,7 @@ msgstr "Deine Neue VM %(vm_name)s"
msgid "You have ordered a new virtual machine!"
msgstr "Du hast eine neue virtuelle Maschine bestellt!"
#, python-format
msgid "Your order of [ <strong>%(vm_name)s</strong> ] has been charged."
msgstr "Deine Bestellung von [ <strong>%(vm_name)s</strong> ] wurde erhoben."
@ -237,13 +238,13 @@ msgstr "Dankeschön!"
msgid "Virtual Machine Cancellation"
msgstr "VM Kündigung"
#, python-format
msgid ""
"You are receiving this email because your virutal machine [ <strong>"
"%(vm_name)s</strong> ] has been cancelled."
msgstr ""
"Du erhälst diese E-Mail, da deine virtuelle Maschine [ <strong>"
"%(vm_name)s</strong> ] gekündigt "
"wurde."
"Du erhälst diese E-Mail, da deine virtuelle Maschine [ <strong>%(vm_name)s</"
"strong> ] gekündigt wurde."
msgid "You can always order a new VM by clicking the button below."
msgstr ""
@ -313,6 +314,9 @@ msgstr "Rechnung Datum"
msgid "Status"
msgstr ""
msgid "Terminated"
msgstr "Beendet"
msgid "Approved"
msgstr "Akzeptiert"
@ -334,6 +338,9 @@ msgstr "Bestellungsübersicht"
msgid "Product"
msgstr "Produkt"
msgid "Period"
msgstr ""
msgid "Cores"
msgstr "Prozessorkerne"
@ -595,8 +602,13 @@ msgstr "Virtuelle Maschinen"
msgid "To create a new virtual machine, click \"Create VM\""
msgstr "Um eine neue VM zu erzeugen, klicke \"Neue VM erzeugen\""
#, python-format
msgid ""
"To access your VM, add your SSH key <a href=\"%(create_ssh_url)s\">here</a>"
msgstr ""
msgid "login"
msgstr "Einloggen"
msgstr "anmelden"
msgid ""
"Thank you for signing up. We have sent an email to you. Please follow the "
@ -673,12 +685,17 @@ msgid ""
"contact Data Center Light Support."
msgstr "Kontaktiere den Data Center Light Support."
msgid "Terminated"
msgstr "Beendet"
msgid "Error terminating VM"
msgstr "Fehler beenden VM"
#, fuzzy, python-format
#| msgid "Virtual Machine Cancellation"
msgid "Virtual Machine %(vm_name)s Cancelled"
msgstr "VM Kündigung"
#~ msgid "My VM page"
#~ msgstr "Meine VM page"
#~ msgid ""
#~ "\n"
#~ "You're receiving this email because you requested a password reset for "

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

View file

@ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model):
instance = cls.objects.create(customer=customer,
billing_address=billing_address)
return instance
class VMDetail(models.Model):
user = models.ForeignKey(CustomUser)
vm_id = models.IntegerField(default=0)
disk_size = models.FloatField(default=0.0)
cores = models.FloatField(default=0.0)
memory = models.FloatField(default=0.0)
configuration = models.CharField(default='', max_length=25)
ipv4 = models.TextField(default='')
ipv6 = models.TextField(default='')
created_at = models.DateTimeField(auto_now_add=True)
terminated_at = models.DateTimeField(null=True)

View file

@ -857,6 +857,8 @@ a.list-group-item-danger:focus,
background: rgba(235, 204, 209, 0.2);
}
.has-error .form-control,
.has-error .form-control:focus,
.has-error .form-control:active,
.has-error .input-group-addon {
color: #eb4d5c;
border-color: #eb4d5c;
@ -871,6 +873,13 @@ a.list-group-item-danger.active:focus {
background-color: #eb4d5c;
}
/* bootstrap input box-shadom disable */
.has-error .form-control:focus,
.has-error .form-control:active,
.has-success .form-control:focus,
.has-success .form-control:active {
box-shadow: inset 0 0 1px rgba(0,0,0,0.25);
}
.checkmark {
display: inline-block;
}

View file

@ -22,7 +22,100 @@
}
.summary-box .content {
padding-top: 15px;
}
/* landing page payment new style */
.last-p {
margin-bottom: 0;
}
.dcl-payment-section {
max-width: 391px;
margin: 0 auto 30px;
padding: 0 10px 30px;
border-bottom: 1px solid #edebeb;
height: 100%;
}
.dcl-payment-section hr{
margin-top: 15px;
margin-bottom: 15px;
}
.dcl-payment-section .top-hr {
margin-left: -10px;
}
.dcl-payment-section h3 {
font-weight: 600;
}
.dcl-payment-section p {
padding: 0 5px;
font-weight: 400;
}
.dcl-payment-section .card-warning-content {
padding: 8px 10px;
font-weight: 300;
}
.dcl-payment-order strong{
font-size: 17px;
}
.dcl-payment-order p {
font-weight: 300;
}
.dcl-payment-section .form-group {
margin-bottom: 10px;
}
.dcl-payment-section .form-control {
box-shadow: none;
padding: 6px 12px;
height: 32px;
}
.dcl-payment-user {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.dcl-payment-user h4 {
font-weight: 600;
padding-left: 5px;
font-size: 17px;
}
@media (min-width: 768px) {
.dcl-payment-grid {
display: flex;
align-items: stretch;
flex-wrap: wrap;
}
.dcl-payment-box {
width: 50%;
position: relative;
padding: 0 30px;
}
.dcl-payment-box:nth-child(2) {
order: 1;
}
.dcl-payment-box:nth-child(4) {
order: 2;
}
.dcl-payment-section {
padding: 15px 10px;
margin-bottom: 0;
border-bottom-width: 5px;
}
.dcl-payment-box:nth-child(2n) .dcl-payment-section {
border-bottom: none;
}
.dcl-payment-box:nth-child(1):after,
.dcl-payment-box:nth-child(2):after {
content: ' ';
display: block;
background: #eee;
width: 1px;
position: absolute;
right: 0;
z-index: 2;
top: 20px;
bottom: 20px;
}
}

View file

@ -47,20 +47,5 @@
window.location.href = '{{next_url}}';
</script>
{% 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%}

View file

@ -42,7 +42,9 @@
<p>
<strong>{% trans "Status" %}: </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>
{% else %}
<span class="vm-status-failed">{% trans "Declined" %}</span>
@ -96,14 +98,14 @@
</p>
<div class="row">
<div class="col-sm-6">
{% comment %}
{% if vm.created_at %}
<p>
<span>{% trans "Period" %}: </span>
<span>{{ vm.created_at|date:'Y/m/d' }} - {% if vm.terminated_at %}{{ vm.terminated_at|date:'Y/m/d' }}{% else %}{% now 'Y/m/d' %}{% endif %}</span>
</p>
{% endif %}
<p>
<span>{% trans "Period" %}</span>
<span class="pull-right">{{}}</span>
</p>
{% endcomment %}
<p>
<span>{% trans "Cores" %}</span>
<span>{% trans "Cores" %}: </span>
{% if vm.cores %}
<span class="pull-right">{{vm.cores|floatformat}}</span>
{% else %}
@ -111,11 +113,11 @@
{% endif %}
</p>
<p>
<span>{% trans "Memory" %}</span>
<span>{% trans "Memory" %}: </span>
<span class="pull-right">{{vm.memory}} GB</span>
</p>
<p>
<span>{% trans "Disk space" %}</span>
<span>{% trans "Disk space" %}: </span>
<span class="pull-right">{{vm.disk_size}} GB</span>
</p>
<p>
@ -171,7 +173,7 @@
{% trans "Hold tight, we are processing your request" %}
</div>
<div class="modal-footer">
<a id="createvm-modal-done-btn" class="btn btn-danger btn-ok btn-wide hide" href="{% url 'hosting:virtual_machines' %}">{% trans "OK" %}</a>
<a id="createvm-modal-done-btn" class="btn btn-success btn-ok btn-wide hide" href="{% url 'hosting:virtual_machines' %}">{% trans "OK" %}</a>
<button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button>
</div>
</div>
@ -199,4 +201,4 @@
<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/order.js' %}"></script>
{% endblock js_extra %}
{% endblock js_extra %}

View file

@ -101,21 +101,5 @@
window.location.href = '{{next_url}}';
</script>
{% 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%}

View file

@ -13,7 +13,12 @@
{% endif %}
{% if not error %}
<div class="dashboard-subtitle">
<p>{% trans 'To create a new virtual machine, click "Create VM"' %}</p>
<p>{% trans 'To create a new virtual machine, click "Create VM"' %}
{% if show_create_ssh_key_msg %}
{% url 'hosting:create_ssh_key' as create_ssh_url %}
<br/>{% blocktrans %}To access your VM, add your SSH key <a href="{{create_ssh_url}}">here</a>{% endblocktrans %}
{% endif %}
</p>
<div class="text-right">
<a class="btn btn-vm" href="{% url 'hosting:create_virtual_machine' %}"><span class="css-plus"></span> <span>{% trans "CREATE VM" %}</span></a>
</div>

View file

@ -1,6 +1,7 @@
import json
import logging
import uuid
from datetime import datetime
from time import sleep
from django import forms
@ -37,6 +38,7 @@ from utils.forms import (
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
ResendActivationEmailForm
)
from utils.hosting_utils import get_vm_price
from utils.mailer import BaseEmail
from utils.stripe_utils import StripeUtils
from utils.views import (
@ -46,10 +48,11 @@ from utils.views import (
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
UserHostingKeyForm, generate_ssh_key_name
from .mixins import ProcessVMSelectionMixin
from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey
from .models import (
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail
)
from datacenterlight.models import VMTemplate
logger = logging.getLogger(__name__)
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
@ -627,13 +630,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
return context
def get(self, request, *args, **kwargs):
if not UserHostingKey.objects.filter(user=self.request.user).exists():
messages.success(
request,
'In order to create a VM, you create/upload your SSH KEY first.'
)
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
if 'next' in request.session:
del request.session['next']
@ -706,25 +702,30 @@ class OrdersHostingDetailView(LoginRequiredMixin,
if obj is not None:
# invoice for previous order
try:
manager = OpenNebulaManager(
email=owner.email, password=owner.password
)
vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data
except WrongIdError:
messages.error(
self.request,
_('The VM you are looking for is unavailable at the '
'moment. Please contact Data Center Light support.')
)
self.kwargs['error'] = 'WrongIdError'
context['error'] = 'WrongIdError'
except ConnectionRefusedError:
messages.error(
self.request,
_('In order to create a VM, you need to create/upload '
'your SSH KEY first.')
)
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
context['vm'] = vm_detail.__dict__
context['vm']['name'] = '{}-{}'.format(context['vm']['configuration'], context['vm']['vm_id'])
except VMDetail.DoesNotExist:
try:
manager = OpenNebulaManager(
email=owner.email, password=owner.password
)
vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data
except WrongIdError:
messages.error(
self.request,
_('The VM you are looking for is unavailable at the '
'moment. Please contact Data Center Light support.')
)
self.kwargs['error'] = 'WrongIdError'
context['error'] = 'WrongIdError'
except ConnectionRefusedError:
messages.error(
self.request,
_('In order to create a VM, you need to create/upload '
'your SSH KEY first.')
)
elif not card_details.get('response_object'):
# new order, failed to get card details
context['failed_payment'] = True
@ -784,12 +785,11 @@ class OrdersHostingDetailView(LoginRequiredMixin,
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)
amount_to_be_charged = get_vm_price(cpu=cpu, memory=memory,
disk_size=disk_size)
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
memory=memory,
disk_size=disk_size)
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
ram=memory,
ssd=disk_size,
@ -834,9 +834,10 @@ class OrdersHostingDetailView(LoginRequiredMixin,
'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.'))
'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),
@ -894,6 +895,10 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
context = {'error': 'connection'}
else:
context = super(ListView, self).get_context_data(**kwargs)
if UserHostingKey.objects.filter(user=self.request.user).exists():
context['show_create_ssh_key_msg'] = False
else:
context['show_create_ssh_key_msg'] = True
return context
@ -914,15 +919,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
raise ValidationError(_('Invalid storage size'))
def get(self, request, *args, **kwargs):
if not UserHostingKey.objects.filter(user=self.request.user).exists():
messages.success(
request,
_(
'In order to create a VM, you need to '
'create/upload your SSH KEY first.'
)
)
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
context = {'templates': VMTemplate.objects.all()}
return render(request, self.template_name, context)
@ -933,7 +929,6 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
memory_field = forms.IntegerField(validators=[self.validate_memory])
storage = request.POST.get('storage')
storage_field = forms.IntegerField(validators=[self.validate_storage])
price = request.POST.get('total')
template_id = int(request.POST.get('config'))
template = VMTemplate.objects.filter(
opennebula_vm_template_id=template_id).first()
@ -965,7 +960,8 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
extra_tags='storage')
return HttpResponseRedirect(
reverse('datacenterlight:index') + "#order_form")
price = get_vm_price(cpu=cores, memory=memory,
disk_size=storage)
specs = {
'cpu': cores,
'memory': memory,
@ -1072,6 +1068,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
except WrongIdError:
response['status'] = True
response['text'] = ugettext('Terminated')
vm_detail_obj = VMDetail.objects.filter(
vm_id=opennebula_vm_id).first()
vm_detail_obj.terminated_at = datetime.utcnow()
vm_detail_obj.save()
break
except BaseException:
break

View file

@ -82,7 +82,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
@classmethod
def register(cls, name, password, email, app='digital_glarus',
base_url=None, send_email=True):
base_url=None, send_email=True, account_details=None):
user = cls.objects.filter(email=email).first()
if not user:
user = cls.objects.create_user(name=name, email=email,
@ -112,6 +112,9 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
'template_name': 'user_activation',
'template_path': 'datacenterlight/emails/'
}
if account_details:
email_data['context'][
'account_details'] = account_details
email = BaseEmail(**email_data)
email.send()
return user
@ -176,6 +179,25 @@ class StripeCustomer(models.Model):
def __str__(self):
return "%s - %s" % (self.stripe_id, self.user.email)
@classmethod
def create_stripe_api_customer(cls, email=None, token=None,
customer_name=None):
"""
This method creates a Stripe API customer with the given
email, token and customer_name. This is different from
get_or_create method below in that it does not create a
CustomUser and associate the customer created in stripe
with it, while get_or_create does that before creating the
stripe user.
"""
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token, email, customer_name)
if stripe_data.get('response_object'):
stripe_cus_id = stripe_data.get('response_object').get('id')
return stripe_cus_id
else:
return None
@classmethod
def get_or_create(cls, email=None, token=None):
"""
@ -195,7 +217,6 @@ class StripeCustomer(models.Model):
except StripeCustomer.DoesNotExist:
user = CustomUser.objects.get(email=email)
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token, email, user.name)
if stripe_data.get('response_object'):

View file

@ -150,9 +150,10 @@ class OpenNebulaManager():
oca.User.METHODS['allocate'], email,
password, 'core')
logger.debug(
"User {0} does not exist. Created the user. User id = {1}",
email,
opennebula_user
"User {} does not exist. Created the user. User id = {}".format(
email,
opennebula_user
)
)
return opennebula_user
except ConnectionRefusedError:

View file

@ -131,6 +131,12 @@ class BillingAddressForm(forms.ModelForm):
}
class BillingAddressFormSignup(BillingAddressForm):
name = forms.CharField(label=_('Name'))
email = forms.EmailField(label=_('Email Address'))
field_order = ['name', 'email']
class UserBillingAddressForm(forms.ModelForm):
user = forms.ModelChoiceField(queryset=CustomUser.objects.all(),
widget=forms.HiddenInput())

View file

@ -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):
@ -9,3 +15,48 @@ def get_all_public_keys(customer):
"""
return UserHostingKey.objects.filter(user_id=customer.id).values_list(
"public_key", flat=True)
def get_or_create_vm_detail(user, manager, vm_id):
"""
Returns VMDetail object related to given vm_id. Creates the object
if it does not exist
:param vm_id: The ID of the VM which should be greater than 0.
:param user: The CustomUser object that owns this VM
:param manager: The OpenNebulaManager object
:return: The VMDetail object. None if vm_id is less than or equal to 0.
Also, for the cases where the VMDetail does not exist and we can not
fetch data about the VM from OpenNebula, the function returns None
"""
if vm_id <= 0:
return None
try:
vm_detail_obj = VMDetail.objects.get(vm_id=vm_id)
except VMDetail.DoesNotExist:
try:
vm_obj = manager.get_vm(vm_id)
except (WrongIdError, ConnectionRefusedError) as e:
logger.error(str(e))
return None
vm = VirtualMachineSerializer(vm_obj).data
vm_detail_obj = VMDetail.objects.create(
user=user, vm_id=vm_id, disk_size=vm['disk_size'],
cores=vm['cores'], memory=vm['memory'],
configuration=vm['configuration'], ipv4=vm['ipv4'],
ipv6=vm['ipv6']
)
return vm_detail_obj
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)

View file

@ -238,7 +238,7 @@ class StripeUtils(object):
@staticmethod
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
:param cpu: The number of cores
@ -256,7 +256,19 @@ class StripeUtils(object):
if hdd is not None:
dcl_plan_string = '{dcl_plan_string}-hdd-{hdd}gb'.format(
dcl_plan_string=dcl_plan_string, hdd=hdd)
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(app=app,
version=version,
plan=dcl_plan_string)
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(
app=app,
version=version,
plan=dcl_plan_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)