Merge branch 'master' into bugfix/log_vm_terminate_errors
This commit is contained in:
commit
8e7789462e
24 changed files with 284 additions and 68 deletions
20
Changelog
20
Changelog
|
@ -1,6 +1,24 @@
|
||||||
|
2.2: 2018-09-06
|
||||||
|
* bugfix: Include price in the Stripe plan name to make it distinct and to correct pricing since version 1.9
|
||||||
|
2.1.2: 2018-08-30
|
||||||
|
* bugfix: [blog, comic] Set blog rss feed for all blog templates
|
||||||
|
2.1.1: 2018-08-24
|
||||||
|
* #5487: [hosting] Add explicit warning message for teminating VM (PR #656)
|
||||||
|
* bugfix: [dg] Send email to admin on dg subscription and increase cc_brand field to 128 characters (PR #652)
|
||||||
|
* #5458: [admin] Make hostingorder more readable (PR #657)
|
||||||
|
* bugfix: [CMS templates] Set description meta field of ungleich template (was missing before) and set ungleich glarus ag uniformly as author of various CMS pages (PR #653)
|
||||||
|
* #5473: Ping a VM before saving ssh key of the user (PR #655)
|
||||||
|
2.1: 2018-08-21
|
||||||
|
* Bugfix: Increase CC brand name fields from 10 to 128 characters (PR #654)
|
||||||
|
2.0.5: 2018-08-08
|
||||||
|
* Fix IPv6 VM name in the billing invoice
|
||||||
|
2.0.4: 2018-08-07
|
||||||
|
* Add RSS feed link to the footer of the blog template (PR #651)
|
||||||
|
* #5308: [ipv6only] Fix - when creating a VM, the name begins with v6only (PR #649)
|
||||||
|
* #5293: Use `terminate-hard` action instead of `terminate` in the opennebula call to terminate a vm (PR #650)
|
||||||
2.0.3: 2018-07-18
|
2.0.3: 2018-07-18
|
||||||
* Remove unused /comic url (PR #644)
|
* Remove unused /comic url (PR #644)
|
||||||
* 5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` (PR #645)
|
* #5126: Allow dynamicweb sites to be iframed on other by setting `X_FRAME_OPTIONS_ALLOW_FROM_URI` (PR #645)
|
||||||
2.0.2: 2018-07-14
|
2.0.2: 2018-07-14
|
||||||
* bugfix: [blog] Add missing content block in the blog_ungleich.html template file
|
* bugfix: [blog] Add missing content block in the blog_ungleich.html template file
|
||||||
2.0.1: 2018-07-14
|
2.0.1: 2018-07-14
|
||||||
|
|
|
@ -8,13 +8,16 @@ from django.core.mail import EmailMessage
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
from dynamicweb.celery import app
|
from dynamicweb.celery import app
|
||||||
from hosting.models import HostingOrder
|
from hosting.models import HostingOrder
|
||||||
from membership.models import CustomUser
|
from membership.models import CustomUser
|
||||||
from opennebula_api.models import OpenNebulaManager
|
from opennebula_api.models import OpenNebulaManager
|
||||||
from opennebula_api.serializers import VirtualMachineSerializer
|
from opennebula_api.serializers import VirtualMachineSerializer
|
||||||
from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail
|
from utils.hosting_utils import (
|
||||||
|
get_all_public_keys, get_or_create_vm_detail, ping_ok
|
||||||
|
)
|
||||||
from utils.mailer import BaseEmail
|
from utils.mailer import BaseEmail
|
||||||
from utils.stripe_utils import StripeUtils
|
from utils.stripe_utils import StripeUtils
|
||||||
from .models import VMPricing
|
from .models import VMPricing
|
||||||
|
@ -203,12 +206,45 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id):
|
||||||
host=vm_ipv6, num_keys=len(keys)
|
host=vm_ipv6, num_keys=len(keys)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Let's delay the task by 75 seconds to be sure
|
# Let's wait until the IP responds to ping before we
|
||||||
# that we run the cdist configure after the host
|
# run the cdist configure on the host
|
||||||
# is up
|
did_manage_public_key = False
|
||||||
manager.manage_public_key(
|
for i in range(0, 15):
|
||||||
keys, hosts=[vm_ipv6], countdown=75
|
if ping_ok(vm_ipv6):
|
||||||
)
|
logger.debug(
|
||||||
|
"{} is pingable. Doing a "
|
||||||
|
"manage_public_key".format(vm_ipv6)
|
||||||
|
)
|
||||||
|
sleep(10)
|
||||||
|
manager.manage_public_key(
|
||||||
|
keys, hosts=[vm_ipv6]
|
||||||
|
)
|
||||||
|
did_manage_public_key = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.debug(
|
||||||
|
"Can't ping {}. Wait 5 secs".format(
|
||||||
|
vm_ipv6
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sleep(5)
|
||||||
|
if not did_manage_public_key:
|
||||||
|
emsg = ("Waited for over 75 seconds for {} to be "
|
||||||
|
"pingable. But the VM was not reachable. "
|
||||||
|
"So, gave up manage_public_key. Please do "
|
||||||
|
"this manually".format(vm_ipv6))
|
||||||
|
logger.error(emsg)
|
||||||
|
email_data = {
|
||||||
|
'subject': '{} CELERY TASK INCOMPLETE: {} not '
|
||||||
|
'pingable for 75 seconds'.format(
|
||||||
|
settings.DCL_TEXT, vm_ipv6
|
||||||
|
),
|
||||||
|
'from_email': current_task.request.hostname,
|
||||||
|
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
|
||||||
|
'body': emsg
|
||||||
|
}
|
||||||
|
email = EmailMessage(**email_data)
|
||||||
|
email.send()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="{% page_attribute 'meta_description' %}">
|
|
||||||
<meta name="author" content="ungleich glarus ag">
|
<meta name="author" content="ungleich glarus ag">
|
||||||
|
<meta name="description" content="{% page_attribute 'meta_description' %}">
|
||||||
<title>{% page_attribute "page_title" %}</title>
|
<title>{% page_attribute "page_title" %}</title>
|
||||||
|
|
||||||
<!-- Vendor CSS -->
|
<!-- Vendor CSS -->
|
||||||
|
|
|
@ -105,7 +105,8 @@ class CeleryTaskTestCase(TestCase):
|
||||||
disk_size=disk_size)
|
disk_size=disk_size)
|
||||||
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
disk_size=disk_size)
|
disk_size=disk_size,
|
||||||
|
price=amount_to_be_charged)
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
||||||
ram=memory,
|
ram=memory,
|
||||||
ssd=disk_size,
|
ssd=disk_size,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
from datacenterlight.tasks import create_vm_task
|
from datacenterlight.tasks import create_vm_task
|
||||||
|
@ -8,6 +9,8 @@ from utils.models import BillingAddress
|
||||||
from .cms_models import CMSIntegration
|
from .cms_models import CMSIntegration
|
||||||
from .models import VMPricing, VMTemplate
|
from .models import VMPricing, VMTemplate
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_cms_integration(name):
|
def get_cms_integration(name):
|
||||||
current_site = Site.objects.get_current()
|
current_site = Site.objects.get_current()
|
||||||
|
|
|
@ -508,14 +508,20 @@ class OrderConfirmationView(DetailView):
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = specs.get('total_price')
|
amount_to_be_charged = specs.get('total_price')
|
||||||
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(
|
||||||
memory=memory,
|
cpu=cpu,
|
||||||
disk_size=disk_size)
|
memory=memory,
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
disk_size=disk_size,
|
||||||
ram=memory,
|
price=amount_to_be_charged
|
||||||
ssd=disk_size,
|
)
|
||||||
version=1,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(
|
||||||
app='dcl')
|
cpu=cpu,
|
||||||
|
ram=memory,
|
||||||
|
ssd=disk_size,
|
||||||
|
version=1,
|
||||||
|
app='dcl',
|
||||||
|
price=amount_to_be_charged
|
||||||
|
)
|
||||||
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
||||||
amount=amount_to_be_charged,
|
amount=amount_to_be_charged,
|
||||||
name=plan_name,
|
name=plan_name,
|
||||||
|
|
25
digitalglarus/migrations/0026_auto_20180824_0739.py
Normal file
25
digitalglarus/migrations/0026_auto_20180824_0739.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-08-24 07:39
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('digitalglarus', '0025_membershiporder_stripe_subscription_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bookingorder',
|
||||||
|
name='cc_brand',
|
||||||
|
field=models.CharField(blank=True, max_length=128),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='membershiporder',
|
||||||
|
name='cc_brand',
|
||||||
|
field=models.CharField(blank=True, max_length=128),
|
||||||
|
),
|
||||||
|
]
|
|
@ -39,7 +39,7 @@ class Ordereable(models.Model):
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
approved = models.BooleanField(default=False)
|
approved = models.BooleanField(default=False)
|
||||||
last4 = models.CharField(max_length=4, blank=True)
|
last4 = models.CharField(max_length=4, blank=True)
|
||||||
cc_brand = models.CharField(max_length=10, blank=True)
|
cc_brand = models.CharField(max_length=128, blank=True)
|
||||||
stripe_charge_id = models.CharField(max_length=100, null=True)
|
stripe_charge_id = models.CharField(max_length=100, null=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -492,6 +492,18 @@ class MembershipPaymentView(LoginRequiredMixin, IsNotMemberMixin, FormView):
|
||||||
'membership_dates': membership.type.first_month_formated_range
|
'membership_dates': membership.type.first_month_formated_range
|
||||||
})
|
})
|
||||||
|
|
||||||
|
email_to_admin_data = {
|
||||||
|
'subject': "New Digital Glarus subscription: {user}".format(
|
||||||
|
user=self.request.user.email
|
||||||
|
),
|
||||||
|
'from_email': 'info@digitalglarus.ch',
|
||||||
|
'to': ['info@ungleich.ch'],
|
||||||
|
'body': "\n".join(
|
||||||
|
["%s=%s" % (k, v) for (k, v) in
|
||||||
|
order_data.items()]),
|
||||||
|
}
|
||||||
|
send_plain_email_task.delay(email_to_admin_data)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'membership': membership,
|
'membership': membership,
|
||||||
'order': membership_order,
|
'order': membership_order,
|
||||||
|
|
|
@ -179,9 +179,7 @@ ROOT_URLCONF = 'dynamicweb.urls'
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'),
|
'DIRS': [os.path.join(PROJECT_DIR, 'membership'),
|
||||||
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
|
|
||||||
os.path.join(PROJECT_DIR, 'membership'),
|
|
||||||
os.path.join(PROJECT_DIR, 'hosting/templates/'),
|
os.path.join(PROJECT_DIR, 'hosting/templates/'),
|
||||||
os.path.join(PROJECT_DIR, 'nosystemd/templates/'),
|
os.path.join(PROJECT_DIR, 'nosystemd/templates/'),
|
||||||
os.path.join(PROJECT_DIR,
|
os.path.join(PROJECT_DIR,
|
||||||
|
@ -192,6 +190,8 @@ TEMPLATES = [
|
||||||
os.path.join(PROJECT_DIR,
|
os.path.join(PROJECT_DIR,
|
||||||
'ungleich_page/templates/ungleich_page'),
|
'ungleich_page/templates/ungleich_page'),
|
||||||
os.path.join(PROJECT_DIR, 'templates/analytics'),
|
os.path.join(PROJECT_DIR, 'templates/analytics'),
|
||||||
|
os.path.join(PROJECT_DIR, 'cms_templates/'),
|
||||||
|
os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'),
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2018-07-05 23:15+0000\n"
|
"POT-Creation-Date: 2018-08-24 09:56+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -290,9 +290,8 @@ msgid ""
|
||||||
"You are not making any payment yet. After placing your order, you will be "
|
"You are not making any payment yet. After placing your order, you will be "
|
||||||
"taken to the Submit Payment Page."
|
"taken to the Submit Payment Page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst "
|
"Es wird noch keine Bezahlung vorgenommen. Die Bezahlung wird erst ausgelöst, "
|
||||||
"ausgelöst, nachdem Du die Bestellung auf der nächsten Seite bestätigt "
|
"nachdem Du die Bestellung auf der nächsten Seite bestätigt hast."
|
||||||
"hast."
|
|
||||||
|
|
||||||
msgid "SUBMIT"
|
msgid "SUBMIT"
|
||||||
msgstr "ABSENDEN"
|
msgstr "ABSENDEN"
|
||||||
|
@ -469,9 +468,9 @@ msgid ""
|
||||||
"database."
|
"database."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine "
|
"Bitte wähle eine der zuvor genutzten Kreditkarten oder gib Deine "
|
||||||
"Kreditkartendetails unten an. Die Bezahlung wird über "
|
"Kreditkartendetails unten an. Die Bezahlung wird über <a href=\"https://"
|
||||||
"<a href=\"https://stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. "
|
"stripe.com\" target=\"_blank\">Stripe</a> abgewickelt. Wir speichern Deine "
|
||||||
"Wir speichern Deine Kreditkartendetails nicht in unserer Datenbank."
|
"Kreditkartendetails nicht in unserer Datenbank."
|
||||||
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Please fill in your credit card information below. We are using <a href="
|
"Please fill in your credit card information below. We are using <a href="
|
||||||
|
@ -631,6 +630,12 @@ msgstr ""
|
||||||
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
|
"Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. "
|
||||||
"Versuche es doch bitte noch einmal."
|
"Versuche es doch bitte noch einmal."
|
||||||
|
|
||||||
|
msgid "Attention:"
|
||||||
|
msgstr "Achtung:"
|
||||||
|
|
||||||
|
msgid "terminating VM can not be reverted."
|
||||||
|
msgstr "Das Beenden kann nicht rückgängig gemacht werden."
|
||||||
|
|
||||||
msgid "Something doesn't work?"
|
msgid "Something doesn't work?"
|
||||||
msgstr "Etwas funktioniert nicht?"
|
msgstr "Etwas funktioniert nicht?"
|
||||||
|
|
||||||
|
@ -643,8 +648,12 @@ msgstr "KONTAKT"
|
||||||
msgid "Terminate your Virtual Machine"
|
msgid "Terminate your Virtual Machine"
|
||||||
msgstr "Deine Virtuelle Maschine beenden"
|
msgstr "Deine Virtuelle Maschine beenden"
|
||||||
|
|
||||||
msgid "Do you want to cancel your Virtual Machine"
|
msgid ""
|
||||||
msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
|
"Terminated VMs can not be revived and will not be refunded. Do you want to "
|
||||||
|
"terminate your VM?"
|
||||||
|
msgstr ""
|
||||||
|
"Beendete VMs können nicht wiederhergestellt oder erstattet werden. Möchtest "
|
||||||
|
"du die VM beenden?"
|
||||||
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -723,8 +732,8 @@ msgstr "Es scheint, als hättest du diese Karte bereits hinzugefügt"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "An error occurred while associating the card. Details: {details}"
|
msgid "An error occurred while associating the card. Details: {details}"
|
||||||
msgstr "Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: "
|
msgstr ""
|
||||||
"{details}"
|
"Beim Verbinden der Karte ist ein Fehler aufgetreten. Details: {details}"
|
||||||
|
|
||||||
msgid "Successfully associated the card with your account"
|
msgid "Successfully associated the card with your account"
|
||||||
msgstr "Die Karte wurde erfolgreich mit deinem Konto verbunden"
|
msgstr "Die Karte wurde erfolgreich mit deinem Konto verbunden"
|
||||||
|
@ -807,6 +816,9 @@ msgstr ""
|
||||||
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
|
"Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es "
|
||||||
"noch einmal."
|
"noch einmal."
|
||||||
|
|
||||||
|
#~ msgid "Do you want to cancel your Virtual Machine"
|
||||||
|
#~ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst"
|
||||||
|
|
||||||
#~ msgid "Reset your password"
|
#~ msgid "Reset your password"
|
||||||
#~ msgstr "Passwort zurücksetzen"
|
#~ msgstr "Passwort zurücksetzen"
|
||||||
|
|
||||||
|
|
25
hosting/migrations/0047_auto_20180821_1240.py
Normal file
25
hosting/migrations/0047_auto_20180821_1240.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-08-21 12:40
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0046_usercarddetail'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='hostingorder',
|
||||||
|
name='cc_brand',
|
||||||
|
field=models.CharField(max_length=128),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='usercarddetail',
|
||||||
|
name='brand',
|
||||||
|
field=models.CharField(max_length=128),
|
||||||
|
),
|
||||||
|
]
|
|
@ -53,9 +53,11 @@ class OrderDetail(AssignPermissionsMixin, models.Model):
|
||||||
ssd_size = models.IntegerField(default=0)
|
ssd_size = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s - %s, %s cores, %s GB RAM, %s GB SSD" % (
|
return "Not available" if self.vm_template is None else (
|
||||||
self.vm_template.name, self.vm_template.vm_type, self.cores,
|
"%s - %s, %s cores, %s GB RAM, %s GB SSD" % (
|
||||||
self.memory, self.ssd_size
|
self.vm_template.name, self.vm_template.vm_type, self.cores,
|
||||||
|
self.memory, self.ssd_size
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
approved = models.BooleanField(default=False)
|
approved = models.BooleanField(default=False)
|
||||||
last4 = models.CharField(max_length=4)
|
last4 = models.CharField(max_length=4)
|
||||||
cc_brand = models.CharField(max_length=10)
|
cc_brand = models.CharField(max_length=128)
|
||||||
stripe_charge_id = models.CharField(max_length=100, null=True)
|
stripe_charge_id = models.CharField(max_length=100, null=True)
|
||||||
price = models.FloatField()
|
price = models.FloatField()
|
||||||
subscription_id = models.CharField(max_length=100, null=True)
|
subscription_id = models.CharField(max_length=100, null=True)
|
||||||
|
@ -87,7 +89,11 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s" % (self.id)
|
return ("Order Nr: #{} - VM_ID: {} - {} - {} - "
|
||||||
|
"Specs: {} - Price: {}").format(
|
||||||
|
self.id, self.vm_id, self.customer.user.email, self.created_at,
|
||||||
|
self.order_detail, self.price
|
||||||
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
@ -212,7 +218,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model):
|
||||||
permissions = ('view_usercarddetail',)
|
permissions = ('view_usercarddetail',)
|
||||||
stripe_customer = models.ForeignKey(StripeCustomer)
|
stripe_customer = models.ForeignKey(StripeCustomer)
|
||||||
last4 = models.CharField(max_length=4)
|
last4 = models.CharField(max_length=4)
|
||||||
brand = models.CharField(max_length=10)
|
brand = models.CharField(max_length=128)
|
||||||
card_id = models.CharField(max_length=100, blank=True, default='')
|
card_id = models.CharField(max_length=100, blank=True, default='')
|
||||||
fingerprint = models.CharField(max_length=100)
|
fingerprint = models.CharField(max_length=100)
|
||||||
exp_month = models.IntegerField(null=False)
|
exp_month = models.IntegerField(null=False)
|
||||||
|
|
|
@ -146,6 +146,10 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vm-vmid-with-warning {
|
||||||
|
padding: 50px 0 33px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.vm-vmid .alert {
|
.vm-vmid .alert {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
margin-bottom: -60px;
|
margin-bottom: -60px;
|
||||||
|
@ -183,6 +187,13 @@
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vm-terminate-warning {
|
||||||
|
letter-spacing: 0.6px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #373636;
|
||||||
|
}
|
||||||
|
|
||||||
.vm-contact-us {
|
.vm-contact-us {
|
||||||
margin: 25px 0 30px;
|
margin: 25px 0 30px;
|
||||||
/* text-align: center; */
|
/* text-align: center; */
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="author" content="">
|
<meta name="author" content="ungleich glarus ag">
|
||||||
|
|
||||||
<title>{{ domain }} - {{ hosting }} hosting as easy as possible</title>
|
<title>{{ domain }} - {{ hosting }} hosting as easy as possible</title>
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="vm-detail-item">
|
<div class="vm-detail-item">
|
||||||
<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 vm-vmid-with-warning">
|
||||||
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
|
<div class="vm-item-subtitle">{% trans "Your VM is" %}</div>
|
||||||
<div id="terminate-VM" data-alt="{% trans 'Terminating' %}">
|
<div id="terminate-VM" data-alt="{% trans 'Terminating' %}">
|
||||||
{% if virtual_machine.state == 'PENDING' %}
|
{% if virtual_machine.state == 'PENDING' %}
|
||||||
|
@ -74,6 +74,10 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="vm-terminate-warning text-center">
|
||||||
|
<p>{% trans "Attention:" %}</p>
|
||||||
|
<p>{% trans "terminating VM can not be reverted." %}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="vm-contact-us">
|
<div class="vm-contact-us">
|
||||||
|
@ -105,7 +109,7 @@
|
||||||
<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 "Terminated VMs can not be revived and will not be refunded. Do you want to terminate your VM?" %}</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">
|
||||||
|
|
|
@ -1032,14 +1032,20 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
||||||
memory = specs.get('memory')
|
memory = specs.get('memory')
|
||||||
disk_size = specs.get('disk_size')
|
disk_size = specs.get('disk_size')
|
||||||
amount_to_be_charged = specs.get('total_price')
|
amount_to_be_charged = specs.get('total_price')
|
||||||
plan_name = StripeUtils.get_stripe_plan_name(cpu=cpu,
|
plan_name = StripeUtils.get_stripe_plan_name(
|
||||||
memory=memory,
|
cpu=cpu,
|
||||||
disk_size=disk_size)
|
memory=memory,
|
||||||
stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu,
|
disk_size=disk_size,
|
||||||
ram=memory,
|
price=amount_to_be_charged
|
||||||
ssd=disk_size,
|
)
|
||||||
version=1,
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(
|
||||||
app='dcl')
|
cpu=cpu,
|
||||||
|
ram=memory,
|
||||||
|
ssd=disk_size,
|
||||||
|
version=1,
|
||||||
|
app='dcl',
|
||||||
|
price=amount_to_be_charged
|
||||||
|
)
|
||||||
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
||||||
amount=amount_to_be_charged,
|
amount=amount_to_be_charged,
|
||||||
name=plan_name,
|
name=plan_name,
|
||||||
|
|
|
@ -315,7 +315,7 @@ class OpenNebulaManager():
|
||||||
return vm_id
|
return vm_id
|
||||||
|
|
||||||
def delete_vm(self, vm_id):
|
def delete_vm(self, vm_id):
|
||||||
TERMINATE_ACTION = 'terminate'
|
TERMINATE_ACTION = 'terminate-hard'
|
||||||
vm_terminated = False
|
vm_terminated = False
|
||||||
try:
|
try:
|
||||||
self.oneadmin_client.call(
|
self.oneadmin_client.call(
|
||||||
|
|
|
@ -36,7 +36,10 @@ class VirtualMachineTemplateSerializer(serializers.Serializer):
|
||||||
return int(obj.template.memory) / 1024
|
return int(obj.template.memory) / 1024
|
||||||
|
|
||||||
def get_name(self, obj):
|
def get_name(self, obj):
|
||||||
return obj.name.lstrip('public-')
|
if obj.name.startswith('public-'):
|
||||||
|
return obj.name.lstrip('public-')
|
||||||
|
else:
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineSerializer(serializers.Serializer):
|
class VirtualMachineSerializer(serializers.Serializer):
|
||||||
|
@ -133,7 +136,10 @@ class VirtualMachineSerializer(serializers.Serializer):
|
||||||
def get_configuration(self, obj):
|
def get_configuration(self, obj):
|
||||||
template_id = obj.template.template_id
|
template_id = obj.template.template_id
|
||||||
template = OpenNebulaManager().get_template(template_id)
|
template = OpenNebulaManager().get_template(template_id)
|
||||||
return template.name.lstrip('public-')
|
if template.name.startswith('public-'):
|
||||||
|
return template.name.lstrip('public-')
|
||||||
|
else:
|
||||||
|
return template.name
|
||||||
|
|
||||||
def get_ipv4(self, obj):
|
def get_ipv4(self, obj):
|
||||||
"""
|
"""
|
||||||
|
@ -162,7 +168,10 @@ class VirtualMachineSerializer(serializers.Serializer):
|
||||||
return '-'
|
return '-'
|
||||||
|
|
||||||
def get_name(self, obj):
|
def get_name(self, obj):
|
||||||
return obj.name.lstrip('public-')
|
if obj.name.startswith('public-'):
|
||||||
|
return obj.name.lstrip('public-')
|
||||||
|
else:
|
||||||
|
return obj.name
|
||||||
|
|
||||||
|
|
||||||
class VMTemplateSerializer(serializers.Serializer):
|
class VMTemplateSerializer(serializers.Serializer):
|
||||||
|
|
|
@ -30,6 +30,14 @@
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://blog.ungleich.ch/en-us/cms/blog/feed/">
|
||||||
|
<span class="fa-stack fa-lg">
|
||||||
|
<i class="fa fa-circle fa-stack-2x"></i>
|
||||||
|
<i class="fa fa-rss fa-stack-1x fa-inverse"></i>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="copyright">
|
<p class="copyright">
|
||||||
Copyright © ungleich GmbH {% now "Y" %}
|
Copyright © ungleich GmbH {% now "Y" %}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="author" content="ungleich GmbH">
|
<meta name="author" content="ungleich glarus ag">
|
||||||
<meta name="description" content="{% page_attribute 'meta_description' %}">
|
<meta name="description" content="{% page_attribute 'meta_description' %}">
|
||||||
<title>{% page_attribute "page_title" %}</title>
|
<title>{% page_attribute "page_title" %}</title>
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="description" content="">
|
<meta name="author" content="ungleich glarus ag">
|
||||||
<meta name="author" content="">
|
<meta name="description" content="{% page_attribute 'meta_description' %}">
|
||||||
|
|
||||||
|
|
||||||
<title>{% page_attribute "page_title" %}</title>
|
<title>{% page_attribute "page_title" %}</title>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import decimal
|
import decimal
|
||||||
import logging
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from oca.pool import WrongIdError
|
from oca.pool import WrongIdError
|
||||||
|
|
||||||
from datacenterlight.models import VMPricing
|
from datacenterlight.models import VMPricing
|
||||||
|
@ -79,7 +81,7 @@ def get_vm_price(cpu, memory, disk_size, hdd_size=0, pricing_name='default'):
|
||||||
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
|
(decimal.Decimal(hdd_size) * pricing.hdd_unit_price))
|
||||||
cents = decimal.Decimal('.01')
|
cents = decimal.Decimal('.01')
|
||||||
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
price = price.quantize(cents, decimal.ROUND_HALF_UP)
|
||||||
return float(price)
|
return round(float(price), 2)
|
||||||
|
|
||||||
|
|
||||||
def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
|
def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
|
||||||
|
@ -125,9 +127,27 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0,
|
||||||
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
|
vat = vat.quantize(cents, decimal.ROUND_HALF_UP)
|
||||||
discount = {
|
discount = {
|
||||||
'name': pricing.discount_name,
|
'name': pricing.discount_name,
|
||||||
'amount': float(pricing.discount_amount),
|
'amount': round(float(pricing.discount_amount),2)
|
||||||
}
|
}
|
||||||
return float(price), float(vat), float(vat_percent), discount
|
return (round(float(price), 2), round(float(vat), 2),
|
||||||
|
round(float(vat_percent)), discount)
|
||||||
|
|
||||||
|
|
||||||
|
def ping_ok(host_ipv6):
|
||||||
|
"""
|
||||||
|
A utility method to check if a host responds to ping requests. Note: the
|
||||||
|
function relies on `ping6` utility of debian to check.
|
||||||
|
|
||||||
|
:param host_ipv6 str type parameter that represets the ipv6 of the host to
|
||||||
|
checked
|
||||||
|
:return True if the host responds to ping else returns False
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
subprocess.check_output("ping6 -c 1 " + host_ipv6, shell=True)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.debug(host_ipv6 + " not reachable via ping. Error = " + str(ex))
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class HostingUtils:
|
class HostingUtils:
|
||||||
|
|
|
@ -291,7 +291,8 @@ class StripeUtils(object):
|
||||||
return charge
|
return charge
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None):
|
def get_stripe_plan_id(cpu, ram, ssd, version, app='dcl', hdd=None,
|
||||||
|
price=None):
|
||||||
"""
|
"""
|
||||||
Returns the Stripe plan id string of the form
|
Returns the Stripe plan id string of the form
|
||||||
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
`dcl-v1-cpu-2-ram-5gb-ssd-10gb` based on the input parameters
|
||||||
|
@ -303,6 +304,7 @@ class StripeUtils(object):
|
||||||
:param version: The version of the Stripe plans
|
:param version: The version of the Stripe plans
|
||||||
:param app: The application to which the stripe plan belongs
|
:param app: The application to which the stripe plan belongs
|
||||||
to. By default it is 'dcl'
|
to. By default it is 'dcl'
|
||||||
|
:param price: The price for this plan
|
||||||
:return: A string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb`
|
:return: A string of the form `dcl-v1-cpu-2-ram-5gb-ssd-10gb`
|
||||||
"""
|
"""
|
||||||
dcl_plan_string = 'cpu-{cpu}-ram-{ram}gb-ssd-{ssd}gb'.format(cpu=cpu,
|
dcl_plan_string = 'cpu-{cpu}-ram-{ram}gb-ssd-{ssd}gb'.format(cpu=cpu,
|
||||||
|
@ -314,19 +316,30 @@ class StripeUtils(object):
|
||||||
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(
|
stripe_plan_id_string = '{app}-v{version}-{plan}'.format(
|
||||||
app=app,
|
app=app,
|
||||||
version=version,
|
version=version,
|
||||||
plan=dcl_plan_string)
|
plan=dcl_plan_string
|
||||||
return stripe_plan_id_string
|
)
|
||||||
|
if price is not None:
|
||||||
|
stripe_plan_id_string_with_price = '{}-{}chf'.format(
|
||||||
|
stripe_plan_id_string,
|
||||||
|
round(price, 2)
|
||||||
|
)
|
||||||
|
return stripe_plan_id_string_with_price
|
||||||
|
else:
|
||||||
|
return stripe_plan_id_string
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_stripe_plan_name(cpu, memory, disk_size):
|
def get_stripe_plan_name(cpu, memory, disk_size, price):
|
||||||
"""
|
"""
|
||||||
Returns the Stripe plan name
|
Returns the Stripe plan name
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format(
|
return "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD, " \
|
||||||
cpu=cpu,
|
"{price} CHF".format(
|
||||||
memory=memory,
|
cpu=cpu,
|
||||||
disk_size=disk_size)
|
memory=memory,
|
||||||
|
disk_size=disk_size,
|
||||||
|
price=round(price, 2)
|
||||||
|
)
|
||||||
|
|
||||||
@handleStripeError
|
@handleStripeError
|
||||||
def set_subscription_meta_data(self, subscription_id, meta_data):
|
def set_subscription_meta_data(self, subscription_id, meta_data):
|
||||||
|
|
Loading…
Reference in a new issue