diff --git a/Changelog b/Changelog index e2bb96cd..2538c802 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,20 @@ +Next: + * 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 + * 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) +2.0.2: 2018-07-14 + * bugfix: [blog] Add missing content block in the blog_ungleich.html template file +2.0.1: 2018-07-14 + * bugfix: [blog] Enable content/structure mode in blog page 2.0: 2018-07-07 * #3747: [dcl,hosting] Add multiple cards support (PR #530) * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder (PR #624) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 281d5f45..2779f79b 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -8,13 +8,16 @@ from django.core.mail import EmailMessage from django.core.urlresolvers import reverse from django.utils import translation from django.utils.translation import ugettext_lazy as _ +from time import sleep from dynamicweb.celery import app from hosting.models import HostingOrder from membership.models import CustomUser from opennebula_api.models import OpenNebulaManager 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.stripe_utils import StripeUtils 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) ) ) - # Let's delay the task by 75 seconds to be sure - # that we run the cdist configure after the host - # is up - manager.manage_public_key( - keys, hosts=[vm_ipv6], countdown=75 - ) + # Let's wait until the IP responds to ping before we + # run the cdist configure on the host + did_manage_public_key = False + for i in range(0, 15): + 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: logger.error(str(e)) try: diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 1c54148e..8da408a0 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,3 +1,4 @@ +import logging from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task @@ -8,6 +9,8 @@ from utils.models import BillingAddress from .cms_models import CMSIntegration from .models import VMPricing, VMTemplate +logger = logging.getLogger(__name__) + def get_cms_integration(name): current_site = Site.objects.get_current() diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 75dfaa73..7d62ceb5 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -2,16 +2,15 @@ Copyright 2015 ungleich. """ +import json +import logging # -*- coding: utf-8 -*- # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os -import json - -from django.utils.translation import ugettext_lazy as _ # dotenv import dotenv -import logging +from django.utils.translation import ugettext_lazy as _ logger = logging.getLogger(__name__) @@ -56,6 +55,7 @@ PROJECT_DIR = os.path.abspath( dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) from multisite import SiteID + SITE_ID = SiteID(default=1) APP_ROOT_ENDPOINT = "/" @@ -179,9 +179,7 @@ ROOT_URLCONF = 'dynamicweb.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(PROJECT_DIR, 'cms_templates/'), - os.path.join(PROJECT_DIR, 'cms_templates/djangocms_blog/'), - os.path.join(PROJECT_DIR, 'membership'), + 'DIRS': [os.path.join(PROJECT_DIR, 'membership'), os.path.join(PROJECT_DIR, 'hosting/templates/'), os.path.join(PROJECT_DIR, 'nosystemd/templates/'), os.path.join(PROJECT_DIR, @@ -192,6 +190,8 @@ TEMPLATES = [ os.path.join(PROJECT_DIR, 'ungleich_page/templates/ungleich_page'), 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, 'OPTIONS': { @@ -580,7 +580,6 @@ MULTISITE_FALLBACK_KWARGS = { FILER_ENABLE_PERMISSIONS = True - ############################################# # configurations for opennebula-integration # ############################################# @@ -702,6 +701,12 @@ if ENABLE_LOGGING: TEST_MANAGE_SSH_KEY_PUBKEY = env('TEST_MANAGE_SSH_KEY_PUBKEY') TEST_MANAGE_SSH_KEY_HOST = env('TEST_MANAGE_SSH_KEY_HOST') +X_FRAME_OPTIONS_ALLOW_FROM_URI = env('X_FRAME_OPTIONS_ALLOW_FROM_URI') +X_FRAME_OPTIONS = ('SAMEORIGIN' if X_FRAME_OPTIONS_ALLOW_FROM_URI is None else + 'ALLOW-FROM {}'.format( + X_FRAME_OPTIONS_ALLOW_FROM_URI.strip() + )) + DEBUG = bool_env('DEBUG') if DEBUG: diff --git a/dynamicweb/urls.py b/dynamicweb/urls.py index c8961971..7e2d58a1 100644 --- a/dynamicweb/urls.py +++ b/dynamicweb/urls.py @@ -11,7 +11,6 @@ from hosting.views import ( RailsHostingView, DjangoHostingView, NodeJSHostingView ) from membership import urls as membership_urls -from ungleich import views as ungleich_views from ungleich_page.views import LandingView from django.views.generic import RedirectView from django.core.urlresolvers import reverse_lazy @@ -57,9 +56,6 @@ urlpatterns += i18n_patterns( url(r'^blog/$', RedirectView.as_view(url=reverse_lazy('ungleich:post-list')), name='blog_list_view'), - url(r'^comic/$', - ungleich_views.PostListViewUngleich.as_view(category='comic'), - name='comic_post_list_view'), url(r'^cms/', include('cms.urls')), url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), url(r'^$', RedirectView.as_view(url='/cms') if REDIRECT_TO_CMS diff --git a/hosting/migrations/0047_auto_20180821_1240.py b/hosting/migrations/0047_auto_20180821_1240.py new file mode 100644 index 00000000..7976c58d --- /dev/null +++ b/hosting/migrations/0047_auto_20180821_1240.py @@ -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), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index 3ae3b0a5..e9fcdc7e 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -69,7 +69,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): created_at = models.DateTimeField(auto_now_add=True) approved = models.BooleanField(default=False) 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) price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) @@ -212,7 +212,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): permissions = ('view_usercarddetail',) stripe_customer = models.ForeignKey(StripeCustomer) 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='') fingerprint = models.CharField(max_length=100) exp_month = models.IntegerField(null=False) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 29632009..3682c5da 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -315,7 +315,7 @@ class OpenNebulaManager(): return vm_id def delete_vm(self, vm_id): - TERMINATE_ACTION = 'terminate' + TERMINATE_ACTION = 'terminate-hard' vm_terminated = False try: self.oneadmin_client.call( diff --git a/opennebula_api/serializers.py b/opennebula_api/serializers.py index 79f37ecd..c7418aa5 100644 --- a/opennebula_api/serializers.py +++ b/opennebula_api/serializers.py @@ -36,7 +36,10 @@ class VirtualMachineTemplateSerializer(serializers.Serializer): return int(obj.template.memory) / 1024 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): @@ -133,7 +136,10 @@ class VirtualMachineSerializer(serializers.Serializer): def get_configuration(self, obj): template_id = obj.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): """ @@ -162,7 +168,10 @@ class VirtualMachineSerializer(serializers.Serializer): return '-' 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): diff --git a/ungleich/templates/cms/ungleichch/_footer.html b/ungleich/templates/cms/ungleichch/_footer.html index 81194f69..94832ed4 100644 --- a/ungleich/templates/cms/ungleichch/_footer.html +++ b/ungleich/templates/cms/ungleichch/_footer.html @@ -30,6 +30,14 @@ +
  • + + + + + + +
  • {% block base_content %} - {% placeholder "default" %} + {% placeholder "base_ungleich_content" %} {% endblock %}
    diff --git a/ungleich/templates/cms/ungleichch/blog_ungleich.html b/ungleich/templates/cms/ungleichch/blog_ungleich.html index d5475867..e7ed3f25 100644 --- a/ungleich/templates/cms/ungleichch/blog_ungleich.html +++ b/ungleich/templates/cms/ungleichch/blog_ungleich.html @@ -1,5 +1,7 @@ {% extends "base_ungleich.html" %} +{% load cms_tags %} {% block base_content %} +{% placeholder "default" %} {% block content %} {% endblock %} {% endblock %} diff --git a/utils/hosting_utils.py b/utils/hosting_utils.py index c0494b90..1859a82c 100644 --- a/utils/hosting_utils.py +++ b/utils/hosting_utils.py @@ -1,5 +1,6 @@ import decimal import logging +import subprocess from oca.pool import WrongIdError from datacenterlight.models import VMPricing @@ -130,6 +131,23 @@ def get_vm_price_with_vat(cpu, memory, ssd_size, hdd_size=0, return float(price), float(vat), 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: @staticmethod def clear_items_from_list(from_list, items_list):