Merged upstream master into task/3730/refactor_price_parameter
This commit is contained in:
commit
39a2455817
18 changed files with 883 additions and 285 deletions
|
@ -4,6 +4,8 @@ Pre-changelog: 1.2.3 2017-09-20
|
||||||
* #3628: [dcl] on hosting, VM is created at credit card info submit
|
* #3628: [dcl] on hosting, VM is created at credit card info submit
|
||||||
* #3772: [dcl] Updated hosting app billing into monthly subscription and added new text and translations
|
* #3772: [dcl] Updated hosting app billing into monthly subscription and added new text and translations
|
||||||
* #3786: [hosting] Redesigned the hosting invoice and order-confirmation page
|
* #3786: [hosting] Redesigned the hosting invoice and order-confirmation page
|
||||||
|
* #3728: [hosting] VM Termination animation added
|
||||||
|
* #3777: [hosting] Create new VM calculator added like dcl landing
|
||||||
* Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls
|
* Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls
|
||||||
* Bugfix: [dcl, hosting] added host to celery error mails
|
* Bugfix: [dcl, hosting] added host to celery error mails
|
||||||
* Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch
|
* Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch
|
||||||
|
|
|
@ -188,13 +188,13 @@ msgid "All Rights Reserved"
|
||||||
msgstr "Alle Rechte vorbehalten"
|
msgstr "Alle Rechte vorbehalten"
|
||||||
|
|
||||||
msgid "Toggle navigation"
|
msgid "Toggle navigation"
|
||||||
msgstr "Konfiguration"
|
msgstr "Umschalten"
|
||||||
|
|
||||||
msgid "Why Data Center Light?"
|
msgid "Why Data Center Light?"
|
||||||
msgstr "Warum Data Center Light?"
|
msgstr "Warum Data Center Light?"
|
||||||
|
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr ""
|
msgstr "Anmelden"
|
||||||
|
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -147,7 +147,6 @@
|
||||||
|
|
||||||
function _fetchPricing() {
|
function _fetchPricing() {
|
||||||
Object.keys(cardPricing).map(function(element) {
|
Object.keys(cardPricing).map(function(element) {
|
||||||
//$('#'+cardPricing[element].id).val(cardPricing[element].value);
|
|
||||||
$('input[name=' + element + ']').val(cardPricing[element].value);
|
$('input[name=' + element + ']').val(cardPricing[element].value);
|
||||||
});
|
});
|
||||||
_calcPricing();
|
_calcPricing();
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
<div class="help-block with-errors">
|
<div class="help-block with-errors">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
{% if 'cores' in message.tags %}
|
{% if 'cores' in message.tags %}
|
||||||
<ul class="list-unstyled"><li>
|
<ul class="list-unstyled">
|
||||||
{{ message|safe }}
|
<li>{{ message|safe }}</li>
|
||||||
</li></ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -110,6 +110,27 @@ msgstr "vorherige"
|
||||||
msgid "next"
|
msgid "next"
|
||||||
msgstr "nächste"
|
msgstr "nächste"
|
||||||
|
|
||||||
|
msgid "Month"
|
||||||
|
msgstr "Monat"
|
||||||
|
|
||||||
|
msgid "VAT included"
|
||||||
|
msgstr "MwSt. inklusive"
|
||||||
|
|
||||||
|
msgid "Please enter a value in range 1 - 48."
|
||||||
|
msgstr "Bitte gib einen Wert von 1 bis 48 ein."
|
||||||
|
|
||||||
|
msgid "Please enter a value in range 2 - 200."
|
||||||
|
msgstr "Bitte gib einen Wert von 2 bis 200 ein."
|
||||||
|
|
||||||
|
msgid "Please enter a value in range 10 - 2000."
|
||||||
|
msgstr "Bitte gib einen Wert von 10 bis 200 ein."
|
||||||
|
|
||||||
|
msgid "GB Storage (SSD)"
|
||||||
|
msgstr "GB Storage (SSD)"
|
||||||
|
|
||||||
|
msgid "Continue"
|
||||||
|
msgstr "Weiter"
|
||||||
|
|
||||||
msgid "SSH Key"
|
msgid "SSH Key"
|
||||||
msgstr "SSH Key"
|
msgstr "SSH Key"
|
||||||
|
|
||||||
|
@ -149,30 +170,12 @@ msgstr "Hast Du bereits ein Benutzerkonto?"
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Anmelden"
|
msgstr "Anmelden"
|
||||||
|
|
||||||
msgid "New Virtual Machine"
|
msgid "Create VM"
|
||||||
msgstr "Neue virtuelle Maschine"
|
msgstr "VM erstellen"
|
||||||
|
|
||||||
msgid "Step 1. Select VM Template:"
|
|
||||||
msgstr "Wähle eine Vorlage"
|
|
||||||
|
|
||||||
msgid "Step2. Select VM Configuration"
|
|
||||||
msgstr "Wähle eine Konfiguration"
|
|
||||||
|
|
||||||
msgid "Price "
|
|
||||||
msgstr "Preis"
|
|
||||||
|
|
||||||
msgid "CHF/Month"
|
|
||||||
msgstr "CHF/Monat"
|
|
||||||
|
|
||||||
msgid "Start VM"
|
|
||||||
msgstr "VM jetzt starten"
|
|
||||||
|
|
||||||
msgid "My Dashboard"
|
msgid "My Dashboard"
|
||||||
msgstr "Mein Dashboard"
|
msgstr "Mein Dashboard"
|
||||||
|
|
||||||
msgid "Create VM"
|
|
||||||
msgstr "VM erstellen"
|
|
||||||
|
|
||||||
msgid "My VMs"
|
msgid "My VMs"
|
||||||
msgstr "Meine VMs"
|
msgstr "Meine VMs"
|
||||||
|
|
||||||
|
@ -286,10 +289,10 @@ msgstr ""
|
||||||
"%(base_url)s%(my_virtual_machines_url)s\n"
|
"%(base_url)s%(my_virtual_machines_url)s\n"
|
||||||
|
|
||||||
msgid "Toggle navigation"
|
msgid "Toggle navigation"
|
||||||
msgstr "Konfiguration"
|
msgstr "Umschalten"
|
||||||
|
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
msgstr "Mein Dashboard"
|
msgstr "Dashboard"
|
||||||
|
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Abmelden"
|
msgstr "Abmelden"
|
||||||
|
@ -414,9 +417,6 @@ msgstr "Konfiguration"
|
||||||
msgid "including VAT"
|
msgid "including VAT"
|
||||||
msgstr "inkl. Mehrwertsteuer"
|
msgstr "inkl. Mehrwertsteuer"
|
||||||
|
|
||||||
msgid "Month"
|
|
||||||
msgstr "Monat"
|
|
||||||
|
|
||||||
msgid "Billing Address"
|
msgid "Billing Address"
|
||||||
msgstr "Rechnungsadresse"
|
msgstr "Rechnungsadresse"
|
||||||
|
|
||||||
|
@ -561,6 +561,9 @@ msgstr "Aktueller Preis"
|
||||||
msgid "Your VM is"
|
msgid "Your VM is"
|
||||||
msgstr "Deine VM ist"
|
msgstr "Deine VM ist"
|
||||||
|
|
||||||
|
msgid "Terminating"
|
||||||
|
msgstr "Beenden"
|
||||||
|
|
||||||
msgid "Pending"
|
msgid "Pending"
|
||||||
msgstr "In Vorbereitung"
|
msgstr "In Vorbereitung"
|
||||||
|
|
||||||
|
@ -573,6 +576,11 @@ msgstr "Fehlgeschlagen"
|
||||||
msgid "Terminate VM"
|
msgid "Terminate VM"
|
||||||
msgstr "VM Beenden"
|
msgstr "VM Beenden"
|
||||||
|
|
||||||
|
msgid "Sorry, there was an unexpected error. Kindly retry."
|
||||||
|
msgstr ""
|
||||||
|
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
|
||||||
|
"Versuche es doch bitte noch einmal."
|
||||||
|
|
||||||
msgid "Something doesn't work?"
|
msgid "Something doesn't work?"
|
||||||
msgstr "Etwas funktioniert nicht?"
|
msgstr "Etwas funktioniert nicht?"
|
||||||
|
|
||||||
|
@ -591,6 +599,11 @@ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
|
||||||
msgid "OK"
|
msgid "OK"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Your Virtual Machine %(virtual_machine.name)s is successfully terminated!"
|
||||||
|
msgstr "Deine Virtuelle Machine (VM) %(virtual_machine.name)s wurde erfolgreich beendet!"
|
||||||
|
|
||||||
msgid "Virtual Machines"
|
msgid "Virtual Machines"
|
||||||
msgstr "Virtuelle Maschinen"
|
msgstr "Virtuelle Maschinen"
|
||||||
|
|
||||||
|
@ -664,17 +677,55 @@ msgstr ""
|
||||||
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
|
"Deine VM ist gleich bereit. Wir senden Dir eine Bestätigungsemail, sobald Du "
|
||||||
"auf sie zugreifen kannst."
|
"auf sie zugreifen kannst."
|
||||||
|
|
||||||
|
msgid "Invalid number of cores"
|
||||||
|
msgstr "Ungültige Anzahle CPU-Kerne"
|
||||||
|
|
||||||
|
msgid "Invalid RAM size"
|
||||||
|
msgstr "Ungültige RAM-Grösse"
|
||||||
|
|
||||||
|
msgid "Invalid storage size"
|
||||||
|
msgstr "Ungültige Speicher-Grösse"
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"We could not find the requested VM. Please "
|
"We could not find the requested VM. Please "
|
||||||
"contact Data Center Light Support."
|
"contact Data Center Light Support."
|
||||||
msgstr "Kontaktiere den Data Center Light Support."
|
msgstr "Kontaktiere den Data Center Light Support."
|
||||||
|
|
||||||
|
msgid "Terminated"
|
||||||
|
msgstr "Beendet"
|
||||||
|
|
||||||
|
msgid "Error terminating VM"
|
||||||
|
msgstr "Fehler beenden VM"
|
||||||
|
|
||||||
msgid "Virtual Machine Cancellation"
|
msgid "Virtual Machine Cancellation"
|
||||||
msgstr "VM Kündigung"
|
msgstr "VM Kündigung"
|
||||||
|
|
||||||
#, python-format
|
#~ msgid "Close"
|
||||||
msgid "VM %(VM_ID)s terminated successfully"
|
#~ msgstr "Schliessen"
|
||||||
msgstr "VM %(VM_ID)s erfolgreich beendet"
|
|
||||||
|
#~ msgid "VM %(VM_ID)s terminated successfully"
|
||||||
|
#~ msgstr "VM %(VM_ID)s erfolgreich beendet"
|
||||||
|
|
||||||
|
#~ msgid "days"
|
||||||
|
#~ msgstr "tage"
|
||||||
|
|
||||||
|
#~ msgid "New Virtual Machine"
|
||||||
|
#~ msgstr "Neue virtuelle Maschine"
|
||||||
|
|
||||||
|
#~ msgid "Step 1. Select VM Template:"
|
||||||
|
#~ msgstr "Wähle eine Vorlage"
|
||||||
|
|
||||||
|
#~ msgid "Step2. Select VM Configuration"
|
||||||
|
#~ msgstr "Wähle eine Konfiguration"
|
||||||
|
|
||||||
|
#~ msgid "Price "
|
||||||
|
#~ msgstr "Preis"
|
||||||
|
|
||||||
|
#~ msgid "CHF/Month"
|
||||||
|
#~ msgstr "CHF/Monat"
|
||||||
|
|
||||||
|
#~ msgid "Start VM"
|
||||||
|
#~ msgstr "VM jetzt starten"
|
||||||
|
|
||||||
#~ msgid "Finish Configuration"
|
#~ msgid "Finish Configuration"
|
||||||
#~ msgstr "Konfiguration beenden"
|
#~ msgstr "Konfiguration beenden"
|
||||||
|
@ -730,9 +781,6 @@ msgstr "VM %(VM_ID)s erfolgreich beendet"
|
||||||
#~ msgid "Ipv6"
|
#~ msgid "Ipv6"
|
||||||
#~ msgstr "IPv6"
|
#~ msgstr "IPv6"
|
||||||
|
|
||||||
#~ msgid "Close"
|
|
||||||
#~ msgstr "Schliessen"
|
|
||||||
|
|
||||||
#~ msgid "Cancel"
|
#~ msgid "Cancel"
|
||||||
#~ msgstr "Beenden"
|
#~ msgstr "Beenden"
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,10 @@
|
||||||
.modal-text p:not(:last-of-type){
|
.modal-text p:not(:last-of-type){
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-title + .modal-footer {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
border-top: 0px solid #e5e5e5;
|
border-top: 0px solid #e5e5e5;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -853,6 +853,9 @@ a.list-group-item-danger:focus,
|
||||||
.panel-danger > .panel-heading {
|
.panel-danger > .panel-heading {
|
||||||
color: #eb4d5c;
|
color: #eb4d5c;
|
||||||
}
|
}
|
||||||
|
.alert-danger{
|
||||||
|
background: rgba(235, 204, 209, 0.2);
|
||||||
|
}
|
||||||
.has-error .form-control,
|
.has-error .form-control,
|
||||||
.has-error .input-group-addon {
|
.has-error .input-group-addon {
|
||||||
color: #eb4d5c;
|
color: #eb4d5c;
|
||||||
|
|
237
hosting/static/hosting/css/price_calculator.css
Normal file
237
hosting/static/hosting/css/price_calculator.css
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/* Create VM calculator */
|
||||||
|
|
||||||
|
.price-calc-section {
|
||||||
|
padding: 80px 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.price-calc-section {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .text {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .text .section-heading {
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 48px;
|
||||||
|
padding-bottom: 27px;
|
||||||
|
color: #3a3a3a;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
position: relative;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .text .description {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .text .section-heading::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
background: #29427A;
|
||||||
|
height: 7px;
|
||||||
|
width: 70px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card {
|
||||||
|
width: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 1px 3px 6px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
padding-bottom: 30px;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 320px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.price-calc-section .card {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .landing {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-padding {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .img-beta {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
width: 60px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .title {
|
||||||
|
padding: 15px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .title h3 {
|
||||||
|
/*font-family: 'Lato', sans-serif;*/
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .price {
|
||||||
|
background: #5A74AF;
|
||||||
|
padding: 22px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .price .price-text {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description {
|
||||||
|
padding: 7px 8px 2px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description span {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 5px;
|
||||||
|
/* margin-left: 0px; */
|
||||||
|
/* justify-self: start; */
|
||||||
|
width: 29%;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 16px;
|
||||||
|
/* font-weight: normal; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description .select-number{
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
width: 85px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description i {
|
||||||
|
color: #29427a;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 5px 6px 3px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description .left {
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description .right {
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .descriptions {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .btn {
|
||||||
|
margin-top: 15px;
|
||||||
|
font-size: 20px;
|
||||||
|
width: 150px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .select-configuration select {
|
||||||
|
outline: none;
|
||||||
|
background: #fff;
|
||||||
|
border-color: #d0d0d0;
|
||||||
|
height: 32px;
|
||||||
|
width: 150px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .check-ip {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .justify-center {
|
||||||
|
justify-content: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .description.input label {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
/*font-weight: 800;*/
|
||||||
|
/*font-family: 'Lato';*/
|
||||||
|
margin-bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*Changed class****.price-calc-section .card .description.input input*/
|
||||||
|
|
||||||
|
.price-calc-section .card .description input {
|
||||||
|
width: 200px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #d0d0d0;
|
||||||
|
background: #fff;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .card .check-ip input[type=checkbox] {
|
||||||
|
font-size: 17px;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .help-block.with-errors {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0;
|
||||||
|
padding: 0 0 5px;
|
||||||
|
}
|
||||||
|
.price-calc-section .help-block.with-errors ul {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .form-group {
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .form-group:after {
|
||||||
|
content: ' ';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 18%;
|
||||||
|
z-index: 20;
|
||||||
|
height: 1px;
|
||||||
|
width: 65%;
|
||||||
|
background: rgba(128, 128, 128, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-calc-section .btn-primary {
|
||||||
|
background: #29427A;
|
||||||
|
border-color: #29427A;
|
||||||
|
color: #fff;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(min-width: 768px) {
|
||||||
|
.create-vm-container {
|
||||||
|
padding-top: 120px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -290,6 +290,11 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vm-vmid .alert {
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
.vm-item-lg {
|
.vm-item-lg {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
@ -305,6 +310,10 @@
|
||||||
color: #e47f2f;
|
color: #e47f2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vm-color-failed {
|
||||||
|
color: #eb4d5c;
|
||||||
|
}
|
||||||
|
|
||||||
.vm-detail-item .value{
|
.vm-detail-item .value{
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
@ -512,7 +521,7 @@
|
||||||
color: #87B6EA;
|
color: #87B6EA;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vm-status, .vm-status-active, .vm-status-failed {
|
.vm-status, .vm-status-active, .vm-status-failed, .vm-status-pending {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.vm-status-active {
|
.vm-status-active {
|
||||||
|
@ -521,6 +530,9 @@
|
||||||
.vm-status-failed {
|
.vm-status-failed {
|
||||||
color: #eb4d5c;
|
color: #eb4d5c;
|
||||||
}
|
}
|
||||||
|
.vm-status-pending {
|
||||||
|
color: #e47f2f;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width:768px) {
|
@media (min-width:768px) {
|
||||||
.dashboard-subtitle {
|
.dashboard-subtitle {
|
||||||
|
@ -627,4 +639,28 @@
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.processing > .btn {
|
||||||
|
position: relative;
|
||||||
|
border-color: #eee;
|
||||||
|
}
|
||||||
|
.processing > .btn:hover,
|
||||||
|
.processing > .btn:focus,
|
||||||
|
.processing > .btn:active {
|
||||||
|
border-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.processing > .btn:after {
|
||||||
|
content: ' ';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
background-image: url('/static/hosting/img/ajax-loader.gif');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-color: #eee;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
}
|
}
|
BIN
hosting/static/hosting/img/ajax-loader.gif
Normal file
BIN
hosting/static/hosting/img/ajax-loader.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 404 B |
|
@ -1,73 +1,75 @@
|
||||||
(function($){
|
(function($){
|
||||||
"use strict"; // Start of use strict
|
"use strict"; // Start of use strict
|
||||||
|
|
||||||
|
|
||||||
$(window).load(function(){
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function(){
|
|
||||||
_initOs();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).resize(function(){
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
var cardPricing = {
|
||||||
function _initOs(){
|
'cpu': {
|
||||||
|
'id': 'coreValue',
|
||||||
|
'value': 1,
|
||||||
$('.os-circle').click(function(event){
|
'min': 1,
|
||||||
$('.os-circle').removeClass('active');
|
'max': 48,
|
||||||
$(this).addClass('active');
|
'interval': 1
|
||||||
|
},
|
||||||
var idTemplate = $(this).data('id');
|
'ram': {
|
||||||
$('input[name=vm_template_id]').val(idTemplate);
|
'id': 'ramValue',
|
||||||
});
|
'value': 2,
|
||||||
$('.config-box').click(function(event){
|
'min': 2,
|
||||||
$('.config-box').removeClass('active');
|
'max': 200,
|
||||||
$(this).addClass('active');
|
'interval': 1
|
||||||
var idConfig = $(this).data('id');
|
},
|
||||||
var price = $(this).data('price');
|
'storage': {
|
||||||
$('input[name=configuration]').val(idConfig);
|
'id': 'storageValue',
|
||||||
$('.container-button').fadeIn();
|
'value': 10,
|
||||||
$('#priceValue').text(price);
|
'min': 10,
|
||||||
});
|
'max': 2000,
|
||||||
|
'interval': 10
|
||||||
$('.owl-carousel').owlCarousel({
|
|
||||||
items:4,
|
|
||||||
nav: true,
|
|
||||||
margin:30,
|
|
||||||
responsiveClass:true,
|
|
||||||
navText: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'],
|
|
||||||
responsive:{
|
|
||||||
0:{
|
|
||||||
items:1,
|
|
||||||
nav:true
|
|
||||||
},
|
|
||||||
600:{
|
|
||||||
items:2,
|
|
||||||
nav:true
|
|
||||||
},
|
|
||||||
768:{
|
|
||||||
items:3,
|
|
||||||
nav:true
|
|
||||||
},
|
|
||||||
990:{
|
|
||||||
items:4,
|
|
||||||
nav:true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
function _initPricing() {
|
||||||
|
_fetchPricing();
|
||||||
|
|
||||||
})(jQuery);
|
$('.fa-minus.left').click(function(event) {
|
||||||
|
var data = $(this).data('minus');
|
||||||
|
|
||||||
|
if (cardPricing[data].value > cardPricing[data].min) {
|
||||||
|
cardPricing[data].value = Number(cardPricing[data].value) - cardPricing[data].interval;
|
||||||
|
}
|
||||||
|
_fetchPricing();
|
||||||
|
});
|
||||||
|
$('.fa-plus.right').click(function(event) {
|
||||||
|
var data = $(this).data('plus');
|
||||||
|
if (cardPricing[data].value < cardPricing[data].max) {
|
||||||
|
cardPricing[data].value = Number(cardPricing[data].value) + cardPricing[data].interval;
|
||||||
|
}
|
||||||
|
_fetchPricing();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.input-price').change(function() {
|
||||||
|
var data = $(this).attr("name");
|
||||||
|
cardPricing[data].value = $('input[name=' + data + ']').val();
|
||||||
|
_fetchPricing();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _fetchPricing() {
|
||||||
|
Object.keys(cardPricing).map(function(element) {
|
||||||
|
$('input[name=' + element + ']').val(cardPricing[element].value);
|
||||||
|
});
|
||||||
|
_calcPricing();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
_initPricing();
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
|
|
||||||
|
|
73
hosting/static/hosting/js/createvm_old.js
Normal file
73
hosting/static/hosting/js/createvm_old.js
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
(function($){
|
||||||
|
"use strict"; // Start of use strict
|
||||||
|
|
||||||
|
|
||||||
|
$(window).load(function(){
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
_initOs();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).resize(function(){
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function _initOs(){
|
||||||
|
|
||||||
|
|
||||||
|
$('.os-circle').click(function(event){
|
||||||
|
$('.os-circle').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
|
||||||
|
var idTemplate = $(this).data('id');
|
||||||
|
$('input[name=vm_template_id]').val(idTemplate);
|
||||||
|
});
|
||||||
|
$('.config-box').click(function(event){
|
||||||
|
$('.config-box').removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
var idConfig = $(this).data('id');
|
||||||
|
var price = $(this).data('price');
|
||||||
|
$('input[name=configuration]').val(idConfig);
|
||||||
|
$('.container-button').fadeIn();
|
||||||
|
$('#priceValue').text(price);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.owl-carousel').owlCarousel({
|
||||||
|
items:4,
|
||||||
|
nav: true,
|
||||||
|
margin:30,
|
||||||
|
responsiveClass:true,
|
||||||
|
navText: ['<i class="fa fa-angle-left"></i>', '<i class="fa fa-angle-right"></i>'],
|
||||||
|
responsive:{
|
||||||
|
0:{
|
||||||
|
items:1,
|
||||||
|
nav:true
|
||||||
|
},
|
||||||
|
600:{
|
||||||
|
items:2,
|
||||||
|
nav:true
|
||||||
|
},
|
||||||
|
768:{
|
||||||
|
items:3,
|
||||||
|
nav:true
|
||||||
|
},
|
||||||
|
990:{
|
||||||
|
items:4,
|
||||||
|
nav:true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,84 @@
|
||||||
$(document).ready(function () {
|
function VMTerminateStatus($container, url) {
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(data) {
|
||||||
|
VMTerminateSuccess($container, data);
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
setTimeout(function(){
|
||||||
|
VMTerminateStatus($container, url);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function VMTerminateActive($container, altText) {
|
||||||
|
$container.find('.alert-danger').addClass('hide');
|
||||||
|
$container.addClass('processing')
|
||||||
|
.find('.vm-item-lg').attr('class', '')
|
||||||
|
.addClass('vm-item-lg vm-color-failed')
|
||||||
|
.text(altText);
|
||||||
|
$container.find('.btn').prop('disabled', true);
|
||||||
|
$('#confirm-cancel').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
function VMTerminateSuccess($container, data) {
|
||||||
|
$container.addClass('terminate-success')
|
||||||
|
.find('.vm-item-lg').text(data.text);
|
||||||
|
$container.find('.btn').remove();
|
||||||
|
$('#terminate-success').modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function VMTerminateFail($container, data, text) {
|
||||||
|
$container.addClass('terminate-fail')
|
||||||
|
.find('.vm-item-lg').text(text);
|
||||||
|
$container.find('.btn').prop('disabled', false);
|
||||||
|
$container.find('.alert-danger').text(data.text).removeClass('hide');
|
||||||
|
$container.removeClass('processing');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#confirm-cancel').on('click', '.btn-ok', function(e) {
|
||||||
|
var url = $('#virtual_machine_cancel_form').attr('action');
|
||||||
|
var $container = $('#terminate-VM');
|
||||||
|
var text = $container.find('.vm-item-lg').text();
|
||||||
|
var altText = $container.attr('data-alt');
|
||||||
|
VMTerminateActive($container, altText);
|
||||||
|
|
||||||
|
$.post(url)
|
||||||
|
.done(function(data) {
|
||||||
|
if (data.status == true) {
|
||||||
|
VMTerminateSuccess($container, data);
|
||||||
|
} else {
|
||||||
|
if ('text' in data) {
|
||||||
|
VMTerminateFail($container, data, text);
|
||||||
|
} else {
|
||||||
|
VMTerminateStatus($container, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function(data) {
|
||||||
|
if (data.status==504) {
|
||||||
|
VMTerminateStatus($container, url);
|
||||||
|
} else {
|
||||||
|
VMTerminateFail($container, data, text);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var hash = window.location.hash;
|
||||||
|
hash && $('ul.nav a[href="' + hash + '"]').tab('show');
|
||||||
|
|
||||||
|
$('.nav-tabs a').click(function(e) {
|
||||||
|
$(this).tab('show');
|
||||||
|
var scrollmem = $('body').scrollTop() || $('html').scrollTop();
|
||||||
|
window.location.hash = this.hash;
|
||||||
|
$('html,body').scrollTop(scrollmem);
|
||||||
|
});
|
||||||
|
|
||||||
$('.modal-text').removeClass('hide');
|
$('.modal-text').removeClass('hide');
|
||||||
var create_vm_form = $('#virtual_machine_create_form');
|
var create_vm_form = $('#virtual_machine_create_form');
|
||||||
create_vm_form.submit(function () {
|
create_vm_form.submit(function () {
|
||||||
|
@ -32,19 +112,4 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#confirm-cancel').on('click', '.btn-ok', function (e) {
|
|
||||||
$('#virtual_machine_cancel_form').trigger('submit');
|
|
||||||
});
|
|
||||||
|
|
||||||
var hash = window.location.hash;
|
|
||||||
hash && $('ul.nav a[href="' + hash + '"]').tab('show');
|
|
||||||
|
|
||||||
$('.nav-tabs a').click(function (e) {
|
|
||||||
$(this).tab('show');
|
|
||||||
var scrollmem = $('body').scrollTop() || $('html').scrollTop();
|
|
||||||
window.location.hash = this.hash;
|
|
||||||
$('html,body').scrollTop(scrollmem);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<link href="{% static 'hosting/css/commons.css' %}" rel="stylesheet">
|
<link href="{% static 'hosting/css/commons.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'hosting/css/virtual-machine.css' %}" rel="stylesheet">
|
<link href="{% static 'hosting/css/virtual-machine.css' %}" rel="stylesheet">
|
||||||
<link href="{% static 'hosting/css/dashboard.css' %}" rel="stylesheet">
|
<link href="{% static 'hosting/css/dashboard.css' %}" rel="stylesheet">
|
||||||
|
<link href="{% static 'hosting/css/price_calculator.css' %}" rel="stylesheet">
|
||||||
{% block css_extra %}
|
{% block css_extra %}
|
||||||
{% endblock css_extra %}
|
{% endblock css_extra %}
|
||||||
|
|
||||||
|
@ -33,9 +34,6 @@
|
||||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
||||||
<link href="//fonts.googleapis.com/css?family=Lato:300,400,500,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
|
<link href="//fonts.googleapis.com/css?family=Lato:300,400,500,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
|
||||||
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
|
<link rel="shortcut icon" href="img/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="stylesheet" href="{% static 'hosting/css/owl.carousel.min.css' %}">
|
|
||||||
<link rel="stylesheet" href="{% static 'hosting/css/owl.theme.default.min.css' %}">
|
|
||||||
|
|
||||||
|
|
||||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||||
|
@ -76,8 +74,8 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="{% static 'hosting/js/jquery.js' %}"></script>
|
<script src="{% static 'hosting/js/jquery.js' %}"></script>
|
||||||
<script type="text/javascript" src="//cdn.jsdelivr.net/jquery.validation/1.13.1/jquery.validate.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script>
|
||||||
<script src="{% static 'hosting/js/vendor/owl.carousel.min.js'%}"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
|
||||||
<!-- Copy Clipboard -->
|
<!-- Copy Clipboard -->
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.10/clipboard.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.10/clipboard.min.js"></script>
|
||||||
|
|
||||||
|
|
87
hosting/templates/hosting/calculator_form.html
Normal file
87
hosting/templates/hosting/calculator_form.html
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{% load staticfiles i18n%}
|
||||||
|
<form id="order_form" method="POST" action="" data-toggle="validator" role="form">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="price">
|
||||||
|
<span id="total">15</span>
|
||||||
|
<span>CHF/{% trans "Month" %}</span>
|
||||||
|
<div class="price-text">
|
||||||
|
<span>{% trans "VAT included" %}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="descriptions">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="description input">
|
||||||
|
<i class="fa fa-minus left" data-minus="cpu" aria-hidden="true"></i>
|
||||||
|
<input class="input-price select-number" type="number" min="1" max="48" id="coreValue" name="cpu" data-error="{% trans 'Please enter a value in range 1 - 48.' %}" required>
|
||||||
|
<span> Core</span>
|
||||||
|
<i class="fa fa-plus right" data-plus="cpu" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<div class="help-block with-errors">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if 'cores' in message.tags %}
|
||||||
|
<ul class="list-unstyled"><li>
|
||||||
|
{{ message|safe }}
|
||||||
|
</li></ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="description input">
|
||||||
|
<i class="fa fa-minus left" data-minus="ram" aria-hidden="true"></i>
|
||||||
|
<input id="ramValue" class="input-price select-number" type="number" min="2" max="200" name="ram"
|
||||||
|
data-error="{% trans 'Please enter a value in range 2 - 200.' %}" required>
|
||||||
|
<span> GB RAM</span>
|
||||||
|
<i class="fa fa-plus right" data-plus="ram" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<div class="help-block with-errors">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if 'memory' in message.tags %}
|
||||||
|
<ul class="list-unstyled"><li>
|
||||||
|
{{ message|safe }}
|
||||||
|
</li></ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="description input">
|
||||||
|
<i class="fa fa-minus left" data-minus="storage" aria-hidden="true"></i>
|
||||||
|
<input id="storageValue" class="input-price select-number" type="number" min="10" max="2000" step="10"
|
||||||
|
name="storage" data-error="{% trans 'Please enter a value in range 10 - 2000.' %}" required>
|
||||||
|
<span>{% trans "GB Storage (SSD)" %}</span>
|
||||||
|
<i class="fa fa-plus right" data-plus="storage" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<div class="help-block with-errors">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if 'storage' in message.tags %}
|
||||||
|
<ul class="list-unstyled"><li>
|
||||||
|
{{ message|safe }}
|
||||||
|
</li></ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="description select-configuration input justify-center">
|
||||||
|
<label for="config">OS</label>
|
||||||
|
<select name="config" id="">
|
||||||
|
{% for template in templates %}
|
||||||
|
<option value="{{template.opennebula_vm_template_id}}">{{template.name}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="help-block with-errors">
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if 'cores' in message.tags %}
|
||||||
|
<ul class="list-unstyled"><li>
|
||||||
|
{{ message|safe }}
|
||||||
|
</li></ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="total">
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-primary disabled" value="{% trans 'Continue' %}"></input>
|
||||||
|
</form>
|
|
@ -1,70 +1,30 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 i18n %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% block content %}
|
|
||||||
<div>
|
|
||||||
<div class="dashboard-container" >
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-md-12">
|
{% block content %}
|
||||||
<br/>
|
<div class="dashboard-container create-vm-container">
|
||||||
{% if messages %}
|
<div class="row">
|
||||||
<div class="alert alert-warning">
|
<div class="col-sm-5">
|
||||||
{% for message in messages %}
|
<div class="dashboard-container-head">
|
||||||
|
<h3 class="dashboard-title-thin"><img src="{% static 'hosting/img/plusVM.svg' %}" class="un-icon" style="margin-top: -18px;width: 42px;height: 42px;"> {% trans "Create VM" %}</h3>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% for message in messages %}
|
||||||
<span>{{ message }}</span>
|
<span>{{ message }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if not error %}
|
|
||||||
<div class="dashboard-title">
|
|
||||||
<h3>{% trans "New Virtual Machine"%} </h3>
|
|
||||||
<hr/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form method="POST" action="">
|
|
||||||
{% csrf_token %}
|
|
||||||
|
|
||||||
<div class="step-title">
|
|
||||||
<h4>{% trans "Step 1. Select VM Template:" %} </h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="parent-container">
|
|
||||||
<div class="container-os owl-carousel owl-theme" id="containerOs">
|
|
||||||
|
|
||||||
{% for template in templates %}
|
|
||||||
<div class="os-circle" data-id="{{template.id}}">
|
|
||||||
<span class="text" >{{template.name}}</span>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="vm_template_id">
|
|
||||||
</div>
|
|
||||||
<div class="step-title">
|
|
||||||
<h4>{% trans "Step2. Select VM Configuration" %}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="parent-container">
|
|
||||||
<div class="container-os config owl-carousel owl-theme">
|
|
||||||
|
|
||||||
{% for config in configuration_options %}
|
|
||||||
<div class="config-box" data-id="{{config.id}}" data-price="{{config.price|floatformat}}">
|
|
||||||
<span>CORE: {{config.cpu|floatformat}}</span>
|
|
||||||
<span>RAM: {{config.memory|floatformat}} GB</span>
|
|
||||||
<span>SSD: {{config.disk_size|floatformat}} GB</span>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<input type="hidden" name="configuration">
|
|
||||||
</div>
|
|
||||||
<div class="container-button">
|
|
||||||
<div class="price">
|
|
||||||
<span class="label-price">{% trans "Price " %}<span id="priceValue">0</span>{% trans "CHF/Month" %}</span>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-success" >{% trans "Start VM"%} </button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<div class="price-calc-section no-padding">
|
||||||
|
<div class="landing card">
|
||||||
|
<div class="caption">
|
||||||
|
{% include "hosting/calculator_form.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{%endblock%}
|
||||||
{%endblock%}
|
|
|
@ -53,19 +53,26 @@
|
||||||
<h2 class="vm-detail-title">{% trans "Status" %} <img src="{% static 'hosting/img/connected.svg' %}" class="un-icon"></h2>
|
<h2 class="vm-detail-title">{% trans "Status" %} <img src="{% static 'hosting/img/connected.svg' %}" class="un-icon"></h2>
|
||||||
<div class="vm-vmid">
|
<div class="vm-vmid">
|
||||||
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
|
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
|
||||||
{% if virtual_machine.state == 'PENDING' %}
|
<div id="terminate-VM" data-alt="{% trans 'Terminating' %}">
|
||||||
<div class="vm-item-lg vm-color-pending">{% trans "Pending" %}</div>
|
{% if virtual_machine.state == 'PENDING' %}
|
||||||
{% elif virtual_machine.state == 'ACTIVE' %}
|
<div class="vm-item-lg vm-color-pending">{% trans "Pending" %}</div>
|
||||||
<div class="vm-item-lg vm-color-online">{% trans "Online" %}</div>
|
{% elif virtual_machine.state == 'ACTIVE' %}
|
||||||
{% elif virtual_machine.state == 'FAILED'%}
|
<div class="vm-item-lg vm-color-online">{% trans "Online" %}</div>
|
||||||
<div class="vm-item-lg vm-color-failed">{% trans "Failed" %}</div>
|
{% elif virtual_machine.state == 'FAILED'%}
|
||||||
{% endif %}
|
<div class="vm-item-lg vm-color-failed">{% trans "Failed" %}</div>
|
||||||
{% if not virtual_machine.status == 'canceled' %}
|
{% else %}
|
||||||
<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
|
<div class="vm-item-lg"></div>
|
||||||
{% csrf_token %}
|
{% endif %}
|
||||||
</form>
|
{% if not virtual_machine.status == 'canceled' %}
|
||||||
<button data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button>
|
<form method="POST" id="virtual_machine_cancel_form" class="cancel-form" action="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}">
|
||||||
{% endif %}
|
{% csrf_token %}
|
||||||
|
</form>
|
||||||
|
<button data-toggle="modal" data-target="#confirm-cancel" class="btn btn-vm-term">{% trans "Terminate VM" %}</button>
|
||||||
|
<div class="alert alert-danger hide">
|
||||||
|
{% trans "Sorry, there was an unexpected error. Kindly retry." %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -89,24 +96,41 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Cancel Modal -->
|
<!-- Cancel Modal -->
|
||||||
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Confirm"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Confirm"><span aria-hidden="true">×</span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div>
|
<div class="modal-icon"><i class="fa fa-ban" aria-hidden="true"></i></div>
|
||||||
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine"%}</h4>
|
<h4 class="modal-title" id="ModalLabel">{% trans "Terminate your Virtual Machine" %}</h4>
|
||||||
<div class="modal-text">
|
<div class="modal-text">
|
||||||
<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p>
|
<p>{% trans "Do you want to cancel your Virtual Machine" %} ?</p>
|
||||||
<p><strong>{{virtual_machine.name}}</strong></p>
|
<p><strong>{{virtual_machine.name}}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a>
|
<a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- / Cancel Modal -->
|
||||||
|
<!-- Success Modal -->
|
||||||
|
<div class="modal fade" id="terminate-success" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div>
|
||||||
|
<h4 class="modal-title" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</h4>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a href="{% url 'hosting:virtual_machines' %}" class="btn btn-success btn-wide">{% trans "OK" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- / Cancel Modal -->
|
<!-- / Cancel Modal -->
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
210
hosting/views.py
210
hosting/views.py
|
@ -1,23 +1,27 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.auth.tokens import default_token_generator
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
from django.http import Http404
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import Http404, HttpResponseRedirect, HttpResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect, render
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import get_language, ugettext_lazy as _
|
from django.utils.translation import get_language, ugettext_lazy as _
|
||||||
from django.views.generic import View, CreateView, FormView, ListView, \
|
from django.utils.translation import ugettext
|
||||||
DetailView, \
|
from django.views.generic import (
|
||||||
DeleteView, TemplateView, UpdateView
|
View, CreateView, FormView, ListView, DetailView, DeleteView,
|
||||||
|
TemplateView, UpdateView
|
||||||
|
)
|
||||||
from guardian.mixins import PermissionRequiredMixin
|
from guardian.mixins import PermissionRequiredMixin
|
||||||
from oca.pool import WrongIdError
|
from oca.pool import WrongIdError
|
||||||
from stored_messages.api import mark_read
|
from stored_messages.api import mark_read
|
||||||
|
@ -28,18 +32,21 @@ from datacenterlight.tasks import create_vm_task
|
||||||
from membership.models import CustomUser, StripeCustomer
|
from membership.models import CustomUser, StripeCustomer
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.models import OpenNebulaManager
|
||||||
from opennebula_api.serializers import VirtualMachineSerializer, \
|
from opennebula_api.serializers import VirtualMachineSerializer, \
|
||||||
VirtualMachineTemplateSerializer
|
VirtualMachineTemplateSerializer, VMTemplateSerializer
|
||||||
from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
|
from utils.forms import BillingAddressForm, PasswordResetRequestForm, \
|
||||||
UserBillingAddressForm
|
UserBillingAddressForm
|
||||||
|
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 PasswordResetViewMixin, PasswordResetConfirmViewMixin, \
|
from utils.views import (
|
||||||
LoginViewMixin
|
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
|
||||||
from utils.hosting_utils import get_vm_price
|
)
|
||||||
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
|
||||||
|
from datacenterlight.models import VMTemplate
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -866,48 +873,80 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
template_name = "hosting/create_virtual_machine.html"
|
template_name = "hosting/create_virtual_machine.html"
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def validate_cores(self, value):
|
||||||
|
if (value > 48) or (value < 1):
|
||||||
|
raise ValidationError(_('Invalid number of cores'))
|
||||||
|
|
||||||
|
def validate_memory(self, value):
|
||||||
|
if (value > 200) or (value < 2):
|
||||||
|
raise ValidationError(_('Invalid RAM size'))
|
||||||
|
|
||||||
|
def validate_storage(self, value):
|
||||||
|
if (value > 2000) or (value < 10):
|
||||||
|
raise ValidationError(_('Invalid storage size'))
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
if not UserHostingKey.objects.filter(user=self.request.user).exists():
|
if not UserHostingKey.objects.filter(user=self.request.user).exists():
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_(
|
_(
|
||||||
'In order to create a VM, you need to create/upload your SSH KEY first.')
|
'In order to create a VM, you need to '
|
||||||
|
'create/upload your SSH KEY first.'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
|
return HttpResponseRedirect(reverse('hosting:ssh_keys'))
|
||||||
|
context = {'templates': VMTemplate.objects.all()}
|
||||||
try:
|
|
||||||
manager = OpenNebulaManager()
|
|
||||||
templates = manager.get_templates()
|
|
||||||
configuration_options = HostingPlan.get_serialized_configs()
|
|
||||||
|
|
||||||
context = {
|
|
||||||
'templates': VirtualMachineTemplateSerializer(templates,
|
|
||||||
many=True).data,
|
|
||||||
'configuration_options': configuration_options,
|
|
||||||
}
|
|
||||||
except:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
'We could not load the VM templates due to a backend connection \
|
|
||||||
error. Please try again in a few minutes'
|
|
||||||
)
|
|
||||||
context = {
|
|
||||||
'error': 'connection'
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
manager = OpenNebulaManager()
|
cores = request.POST.get('cpu')
|
||||||
template_id = request.POST.get('vm_template_id')
|
cores_field = forms.IntegerField(validators=[self.validate_cores])
|
||||||
template = manager.get_template(template_id)
|
memory = request.POST.get('ram')
|
||||||
configuration_id = int(request.POST.get('configuration'))
|
memory_field = forms.IntegerField(validators=[self.validate_memory])
|
||||||
configuration = HostingPlan.objects.get(id=configuration_id)
|
storage = request.POST.get('storage')
|
||||||
request.session['template'] = VirtualMachineTemplateSerializer(
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||||
template).data
|
template_id = int(request.POST.get('config'))
|
||||||
|
template = VMTemplate.objects.filter(
|
||||||
|
opennebula_vm_template_id=template_id).first()
|
||||||
|
template_data = VMTemplateSerializer(template).data
|
||||||
|
|
||||||
request.session['specs'] = configuration.serialize()
|
try:
|
||||||
|
cores = cores_field.clean(cores)
|
||||||
|
except ValidationError as err:
|
||||||
|
msg = '{} : {}.'.format(cores, str(err))
|
||||||
|
messages.add_message(self.request, messages.ERROR, msg,
|
||||||
|
extra_tags='cores')
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
reverse('datacenterlight:index') + "#order_form")
|
||||||
|
|
||||||
|
try:
|
||||||
|
memory = memory_field.clean(memory)
|
||||||
|
except ValidationError as err:
|
||||||
|
msg = '{} : {}.'.format(memory, str(err))
|
||||||
|
messages.add_message(self.request, messages.ERROR, msg,
|
||||||
|
extra_tags='memory')
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
reverse('datacenterlight:index') + "#order_form")
|
||||||
|
|
||||||
|
try:
|
||||||
|
storage = storage_field.clean(storage)
|
||||||
|
except ValidationError as err:
|
||||||
|
msg = '{} : {}.'.format(storage, str(err))
|
||||||
|
messages.add_message(self.request, messages.ERROR, msg,
|
||||||
|
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,
|
||||||
|
'disk_size': storage,
|
||||||
|
'price': price
|
||||||
|
}
|
||||||
|
|
||||||
|
request.session['specs'] = specs
|
||||||
|
request.session['template'] = template_data
|
||||||
return redirect(reverse('hosting:payment'))
|
return redirect(reverse('hosting:payment'))
|
||||||
|
|
||||||
|
|
||||||
|
@ -949,7 +988,19 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
vm = self.get_object()
|
vm = self.get_object()
|
||||||
if vm is None:
|
if vm is None:
|
||||||
return redirect(reverse('hosting:virtual_machines'))
|
if self.request.is_ajax():
|
||||||
|
storage = messages.get_messages(request)
|
||||||
|
for m in storage:
|
||||||
|
pass
|
||||||
|
storage.used = True
|
||||||
|
return HttpResponse(
|
||||||
|
json.dumps({'text': ugettext('Terminated')}),
|
||||||
|
content_type="application/json"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return redirect(reverse('hosting:virtual_machines'))
|
||||||
|
elif self.request.is_ajax():
|
||||||
|
return HttpResponse()
|
||||||
try:
|
try:
|
||||||
serializer = VirtualMachineSerializer(vm)
|
serializer = VirtualMachineSerializer(vm)
|
||||||
context = {
|
context = {
|
||||||
|
@ -964,6 +1015,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
response = {'status': False}
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
vm = self.get_object()
|
vm = self.get_object()
|
||||||
|
|
||||||
|
@ -973,42 +1025,50 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
email=owner.email,
|
email=owner.email,
|
||||||
password=owner.password
|
password=owner.password
|
||||||
)
|
)
|
||||||
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
|
|
||||||
terminated = manager.delete_vm(
|
try:
|
||||||
vm.id
|
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
|
||||||
)
|
except WrongIdError:
|
||||||
|
return redirect(reverse('hosting:virtual_machines'))
|
||||||
|
|
||||||
|
terminated = manager.delete_vm(vm.id)
|
||||||
|
|
||||||
if not terminated:
|
if not terminated:
|
||||||
messages.error(
|
response['text'] = ugettext(
|
||||||
request,
|
'Error terminating VM') + opennebula_vm_id
|
||||||
'Error terminating VM %s' % (opennebula_vm_id)
|
else:
|
||||||
)
|
for t in range(15):
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
try:
|
||||||
context = {
|
manager.get_vm(opennebula_vm_id)
|
||||||
'vm': vm_data,
|
except WrongIdError:
|
||||||
'base_url': "{0}://{1}".format(self.request.scheme,
|
response['status'] = True
|
||||||
self.request.get_host()),
|
response['text'] = ugettext('Terminated')
|
||||||
'page_header': _('Virtual Machine Cancellation')
|
break
|
||||||
}
|
except BaseException:
|
||||||
email_data = {
|
break
|
||||||
'subject': context['page_header'],
|
else:
|
||||||
'to': self.request.user.email,
|
sleep(2)
|
||||||
'context': context,
|
context = {
|
||||||
'template_name': 'vm_canceled',
|
'vm': vm_data,
|
||||||
'template_path': 'hosting/emails/',
|
'base_url': "{0}://{1}".format(self.request.scheme,
|
||||||
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
|
self.request.get_host()),
|
||||||
}
|
'page_header': _('Virtual Machine Cancellation')
|
||||||
email = BaseEmail(**email_data)
|
}
|
||||||
email.send()
|
email_data = {
|
||||||
|
'subject': context['page_header'],
|
||||||
messages.error(
|
'to': self.request.user.email,
|
||||||
request,
|
'context': context,
|
||||||
_('VM %(VM_ID)s terminated successfully') % {
|
'template_name': 'vm_canceled',
|
||||||
'VM_ID': opennebula_vm_id}
|
'template_path': 'hosting/emails/',
|
||||||
|
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
|
||||||
|
}
|
||||||
|
email = BaseEmail(**email_data)
|
||||||
|
email.send()
|
||||||
|
return HttpResponse(
|
||||||
|
json.dumps(response),
|
||||||
|
content_type="application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin,
|
class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin,
|
||||||
ListView):
|
ListView):
|
||||||
|
|
Loading…
Reference in a new issue