From bd875ffe7deb21c8d99eaa0501a43ef75285141d Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 18 Apr 2018 23:50:52 +0200 Subject: [PATCH 01/43] Create hostingorder outside celery task --- datacenterlight/tasks.py | 69 +++++++++++++++------------------------- datacenterlight/tests.py | 11 +++++-- datacenterlight/views.py | 52 +++++++++++++++++++++++++++--- hosting/views.py | 48 ++++++++++++++++++++++++++-- 4 files changed, 126 insertions(+), 54 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 3db6eb54..67b00c5d 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -49,24 +49,11 @@ def retry_task(task, exception=None): @app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) -def create_vm_task(self, vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_id, cc_details): +def create_vm_task(self, vm_template_id, user, specs, template, order_id): logger.debug( "Running create_vm_task on {}".format(current_task.request.hostname)) vm_id = None try: - final_price = specs.get('price') - billing_address = BillingAddress( - cardholder_name=billing_address_data['cardholder_name'], - street_address=billing_address_data['street_address'], - city=billing_address_data['city'], - postal_code=billing_address_data['postal_code'], - country=billing_address_data['country'] - ) - billing_address.save() - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - if 'pass' in user: on_user = user.get('email') on_pass = user.get('pass') @@ -94,33 +81,26 @@ def create_vm_task(self, vm_template_id, user, specs, template, if vm_id is None: raise Exception("Could not create VM") - # Create a Hosting Order - order = HostingOrder.create( - price=final_price, - vm_id=vm_id, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - HostingBill.create( - customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe subscription - order.set_subscription_id(stripe_subscription_id, cc_details) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() + # Update HostingOrder with the created vm_id + hosting_order = HostingOrder.objects.filter(id=order_id).first() + error_msg = None + if hosting_order: + logger.debug( + "Updating hosting_order {} with vm_id={}".format( + hosting_order.id, vm_id + ) + ) + hosting_order.vm_id = vm_id + hosting_order.save() + else: + error_msg = ( + "HostingOrder with id {order_id} not found. This means that " + "the hosting order was not created and/or it is/was not " + "associated with VM with id {vm_id}".format( + order_id=order_id, vm_id=vm_id + ) + ) + logger.error(error_msg) vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data @@ -134,8 +114,11 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'template': template.get('name'), 'vm_name': vm.get('name'), 'vm_id': vm['vm_id'], - 'order_id': order.id + 'order_id': order_id } + + if error_msg: + context['errors'] = error_msg email_data = { 'subject': settings.DCL_TEXT + " Order from %s" % context['email'], 'from_email': settings.DCL_SUPPORT_FROM_ADDRESS, @@ -159,7 +142,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'base_url': "{0}://{1}".format(user.get('request_scheme'), user.get('request_host')), 'order_url': reverse('hosting:orders', - kwargs={'pk': order.id}), + kwargs={'pk': order_id}), 'page_header': _( 'Your New VM %(vm_name)s at Data Center Light') % { 'vm_name': vm.get('name')}, diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index d1ce9785..25c0bf4c 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -122,10 +122,15 @@ class CeleryTaskTestCase(TestCase): msg = subscription_result.get('error') raise Exception("Creating subscription failed: {}".format(msg)) + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + async_task = create_vm_task.delay( - vm_template_id, self.user, specs, template_data, - stripe_customer.id, billing_address_data, - stripe_subscription_obj.id, card_details_dict + vm_template_id, self.user, specs, template_data, order.id ) new_vm_id = 0 res = None diff --git a/datacenterlight/views.py b/datacenterlight/views.py index af3b774c..a855b3cc 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -15,11 +15,14 @@ from django.views.generic import FormView, CreateView, DetailView from datacenterlight.tasks import create_vm_task from hosting.forms import HostingUserLoginForm -from hosting.models import HostingOrder +from hosting.models import HostingOrder, HostingBill from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer -from utils.forms import BillingAddressForm, BillingAddressFormSignup +from utils.forms import ( + BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm +) from utils.hosting_utils import get_vm_price +from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm @@ -484,9 +487,48 @@ class OrderConfirmationView(DetailView): 'language': get_language(), } - create_vm_task.delay(vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_obj.id, card_details_dict) + billing_address = BillingAddress( + cardholder_name=billing_address_data['cardholder_name'], + street_address=billing_address_data['street_address'], + city=billing_address_data['city'], + postal_code=billing_address_data['postal_code'], + country=billing_address_data['country'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + HostingBill.create(customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'token', 'customer']: diff --git a/hosting/views.py b/hosting/views.py index 6e143760..c7c7655a 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -44,6 +44,7 @@ from utils.forms import ( ) from utils.hosting_utils import get_vm_price from utils.mailer import BaseEmail +from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from utils.views import ( @@ -882,9 +883,50 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): 'request_host': request.get_host(), 'language': get_language(), } - create_vm_task.delay(vm_template_id, user, specs, template, - stripe_customer_id, billing_address_data, - stripe_subscription_obj.id, card_details_dict) + + billing_address = BillingAddress( + cardholder_name=billing_address_data['cardholder_name'], + street_address=billing_address_data['street_address'], + city=billing_address_data['city'], + postal_code=billing_address_data['postal_code'], + country=billing_address_data['country'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + HostingBill.create(customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data + ) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', From 1a7412f8ff3bda668adaa0bc74d78e50b6c40c9a Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 00:51:55 +0200 Subject: [PATCH 02/43] stripe_utils: Add set_subscription_metadata method --- utils/stripe_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index 79bca243..3809e138 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -233,6 +233,12 @@ class StripeUtils(object): ) return subscription_result + @handleStripeError + def set_subscription_metadata(self, subscription_id, metadata): + subscription = stripe.Subscription.retrieve(subscription_id) + subscription.metadata = metadata + subscription.save() + @handleStripeError def unsubscribe_customer(self, subscription_id): """ From 8a659c153e85e9a1a4f43cc761617e7c79d2aa93 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 00:52:38 +0200 Subject: [PATCH 03/43] Set VM_ID metadata to the created subscription --- datacenterlight/tasks.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 67b00c5d..8c63c84c 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -18,6 +18,7 @@ from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.forms import UserBillingAddressForm from utils.mailer import BaseEmail from utils.models import BillingAddress +from utils.stripe_utils import StripeUtils logger = get_task_logger(__name__) @@ -102,6 +103,22 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): ) logger.error(error_msg) + stripe_utils = StripeUtils() + result = stripe_utils.set_subscription_metadata( + subscription_id=hosting_order.subscription_id, + metadata={"VM_ID": str(vm_id)} + ) + stripe_subscription_obj = result.get('response_object') + if stripe_subscription_obj is not None: + emsg = "Could not update subscription metadata for {sub}".format( + sub=hosting_order.subscription_id + ) + logger.error(emsg) + if error_msg: + error_msg += emsg + else: + error_msg = emsg + vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data context = { From 791f48513af985a617250c10788e571325ef1d4a Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:07:59 +0200 Subject: [PATCH 04/43] Organize imports --- datacenterlight/tasks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 8c63c84c..758778b6 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,14 +10,12 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill -from membership.models import StripeCustomer, CustomUser +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.forms import UserBillingAddressForm from utils.mailer import BaseEmail -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils logger = get_task_logger(__name__) From a3a8227007ac517a08fe856bc4a038d9e016e4f7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:08:52 +0200 Subject: [PATCH 05/43] Check if subscription metadata update response does not have errors --- datacenterlight/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 758778b6..12fabc1e 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -106,8 +106,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): subscription_id=hosting_order.subscription_id, metadata={"VM_ID": str(vm_id)} ) - stripe_subscription_obj = result.get('response_object') - if stripe_subscription_obj is not None: + + if result.get('error') is not None: emsg = "Could not update subscription metadata for {sub}".format( sub=hosting_order.subscription_id ) From 80c3ac5346a449bc4d7f2863a818c5c8542250c0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Thu, 19 Apr 2018 01:09:25 +0200 Subject: [PATCH 06/43] Improve create_vm_task test --- datacenterlight/tests.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tests.py b/datacenterlight/tests.py index 25c0bf4c..d6cd6adf 100644 --- a/datacenterlight/tests.py +++ b/datacenterlight/tests.py @@ -12,9 +12,11 @@ from unittest import skipIf from datacenterlight.models import VMTemplate from datacenterlight.tasks import create_vm_task +from hosting.models import HostingOrder from membership.models import StripeCustomer from opennebula_api.serializers import VMTemplateSerializer from utils.hosting_utils import get_vm_price +from utils.models import BillingAddress from utils.stripe_utils import StripeUtils @@ -81,11 +83,14 @@ class CeleryTaskTestCase(TestCase): stripe_customer = StripeCustomer.get_or_create( email=self.customer_email, - token=self.token) + token=self.token + ) card_details = self.stripe_utils.get_card_details( stripe_customer.stripe_id, - self.token) - card_details_dict = card_details.get('response_object') + self.token + ) + card_details_dict = card_details.get('error') + self.assertEquals(card_details_dict, None) billing_address_data = {'cardholder_name': self.customer_name, 'postal_code': '1231', 'country': 'CH', @@ -122,10 +127,19 @@ class CeleryTaskTestCase(TestCase): msg = subscription_result.get('error') raise Exception("Creating subscription failed: {}".format(msg)) + billing_address = BillingAddress( + cardholder_name=billing_address_data['cardholder_name'], + street_address=billing_address_data['street_address'], + city=billing_address_data['city'], + postal_code=billing_address_data['postal_code'], + country=billing_address_data['country'] + ) + billing_address.save() + order = HostingOrder.create( price=specs['price'], vm_id=0, - customer=customer, + customer=stripe_customer, billing_address=billing_address ) From fae1c7fbeb74a634fbc0618228b190f4535277b4 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Thu, 19 Apr 2018 07:26:34 +0200 Subject: [PATCH 07/43] Update hosting_order in a pythonic way --- datacenterlight/tasks.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 12fabc1e..815d627d 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -83,20 +83,21 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): # Update HostingOrder with the created vm_id hosting_order = HostingOrder.objects.filter(id=order_id).first() error_msg = None - if hosting_order: + + try: + hosting_order.vm_id = vm_id + hosting_order.save() logger.debug( - "Updating hosting_order {} with vm_id={}".format( + "Updated hosting_order {} with vm_id={}".format( hosting_order.id, vm_id ) ) - hosting_order.vm_id = vm_id - hosting_order.save() - else: + except Exception as ex: error_msg = ( "HostingOrder with id {order_id} not found. This means that " "the hosting order was not created and/or it is/was not " - "associated with VM with id {vm_id}".format( - order_id=order_id, vm_id=vm_id + "associated with VM with id {vm_id}. Details {details}".format( + order_id=order_id, vm_id=vm_id, details=str(ex) ) ) logger.error(error_msg) From 3debf3411858df959c96362ecf9a073649464583 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:25:24 +0530 Subject: [PATCH 08/43] remove vm creation to util function --- datacenterlight/utils.py | 59 ++++++++++++++++++++++++++++++ datacenterlight/views.py | 64 ++++++--------------------------- hosting/views.py | 77 +++++++--------------------------------- 3 files changed, 82 insertions(+), 118 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 2efade8e..6c7a25bd 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,5 +1,10 @@ from django.contrib.sites.models import Site +from datacenterlight.tasks import create_vm_task +from hosting.models import HostingOrder, HostingBill +from membership.models import StripeCustomer +from utils.forms import UserBillingAddressForm +from utils.models import BillingAddress from .cms_models import CMSIntegration @@ -12,3 +17,57 @@ def get_cms_integration(name): except CMSIntegration.DoesNotExist: cms_integration = CMSIntegration.objects.get(name=name, domain=None) return cms_integration + + +def create_vm(billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user): + billing_address = BillingAddress( + cardholder_name=billing_address_data['cardholder_name'], + street_address=billing_address_data['street_address'], + city=billing_address_data['city'], + postal_code=billing_address_data['postal_code'], + country=billing_address_data['country'] + ) + billing_address.save() + + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + + # Create a Hosting Order with vm_id = 0, we shall set it later in + # celery task once the VM instance is up and running + order = HostingOrder.create( + price=specs['price'], + vm_id=0, + customer=customer, + billing_address=billing_address + ) + + # Create a Hosting Bill + HostingBill.create(customer=customer, billing_address=billing_address) + + # Create Billing Address for User if he does not have one + if not customer.user.billing_addresses.count(): + billing_address_data.update({ + 'user': customer.user.id + }) + billing_address_user_form = UserBillingAddressForm( + billing_address_data + ) + billing_address_user_form.is_valid() + billing_address_user_form.save() + + # Associate an order with a stripe subscription + order.set_subscription_id( + stripe_subscription_obj.id, card_details_dict + ) + + # If the Stripe payment succeeds, set order status approved + order.set_approved() + + create_vm_task.delay(vm_template_id, user, specs, template, order.id) + + for session_var in ['specs', 'template', 'billing_address', + 'billing_address_data', + 'token', 'customer']: + if session_var in request.session: + del request.session[session_var] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index a855b3cc..203846fb 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -7,7 +7,7 @@ from django.contrib import messages from django.contrib.auth import login, authenticate from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse from django.shortcuts import render from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control @@ -27,7 +27,7 @@ from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm from .models import VMTemplate -from .utils import get_cms_integration +from .utils import get_cms_integration, create_vm logger = logging.getLogger(__name__) @@ -390,8 +390,8 @@ class OrderConfirmationView(DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) + card_details_dict = card_details.get('response_object') cpu = specs.get('cpu') memory = specs.get('memory') @@ -431,8 +431,7 @@ class OrderConfirmationView(DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) # Create user if the user is not logged in and if he is not already # registered @@ -487,53 +486,11 @@ class OrderConfirmationView(DetailView): 'language': get_language(), } - billing_address = BillingAddress( - cardholder_name=billing_address_data['cardholder_name'], - street_address=billing_address_data['street_address'], - city=billing_address_data['city'], - postal_code=billing_address_data['postal_code'], - country=billing_address_data['country'] + create_vm( + billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user ) - billing_address.save() - - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - - # Create a Hosting Order with vm_id = 0, we shall set it later in - # celery task once the VM instance is up and running - order = HostingOrder.create( - price=specs['price'], - vm_id=0, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - HostingBill.create(customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe subscription - order.set_subscription_id( - stripe_subscription_obj.id, card_details_dict - ) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() - - create_vm_task.delay(vm_template_id, user, specs, template, order.id) - for session_var in ['specs', 'template', 'billing_address', - 'billing_address_data', - 'token', 'customer']: - if session_var in request.session: - del request.session[session_var] response = { 'status': True, @@ -549,5 +506,4 @@ class OrderConfirmationView(DetailView): ' it is ready.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) diff --git a/hosting/views.py b/hosting/views.py index c7c7655a..5d5051e4 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,4 +1,3 @@ -import json import logging import uuid from datetime import datetime @@ -12,7 +11,9 @@ 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.urlresolvers import reverse_lazy, reverse -from django.http import Http404, HttpResponseRedirect, HttpResponse +from django.http import ( + Http404, HttpResponseRedirect, HttpResponse, JsonResponse +) from django.shortcuts import redirect, render from django.utils.http import urlsafe_base64_decode from django.utils.safestring import mark_safe @@ -31,7 +32,7 @@ from stored_messages.models import Message from stored_messages.settings import stored_messages_settings from datacenterlight.models import VMTemplate -from datacenterlight.tasks import create_vm_task +from datacenterlight.utils import create_vm from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -44,7 +45,6 @@ from utils.forms import ( ) from utils.hosting_utils import get_vm_price from utils.mailer import BaseEmail -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from utils.views import ( @@ -873,8 +873,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ' On close of this popup, you will be redirected back to' ' the payment page.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) + user = { 'name': self.request.user.name, 'email': self.request.user.email, @@ -884,55 +884,11 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): 'language': get_language(), } - billing_address = BillingAddress( - cardholder_name=billing_address_data['cardholder_name'], - street_address=billing_address_data['street_address'], - city=billing_address_data['city'], - postal_code=billing_address_data['postal_code'], - country=billing_address_data['country'] + create_vm( + billing_address_data, stripe_customer_id, specs, + stripe_subscription_obj, card_details_dict, request, + vm_template_id, template, user ) - billing_address.save() - - customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - - # Create a Hosting Order with vm_id = 0, we shall set it later in - # celery task once the VM instance is up and running - order = HostingOrder.create( - price=specs['price'], - vm_id=0, - customer=customer, - billing_address=billing_address - ) - - # Create a Hosting Bill - HostingBill.create(customer=customer, billing_address=billing_address) - - # Create Billing Address for User if he does not have one - if not customer.user.billing_addresses.count(): - billing_address_data.update({ - 'user': customer.user.id - }) - billing_address_user_form = UserBillingAddressForm( - billing_address_data - ) - billing_address_user_form.is_valid() - billing_address_user_form.save() - - # Associate an order with a stripe subscription - order.set_subscription_id( - stripe_subscription_obj.id, card_details_dict - ) - - # If the Stripe payment succeeds, set order status approved - order.set_approved() - - create_vm_task.delay(vm_template_id, user, specs, template, order.id) - - for session_var in ['specs', 'template', 'billing_address', - 'billing_address_data', - 'token', 'customer']: - if session_var in request.session: - del request.session[session_var] response = { 'status': True, @@ -944,8 +900,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): ' it is ready.')) } - return HttpResponse(json.dumps(response), - content_type="application/json") + return JsonResponse(response) class OrdersHostingListView(LoginRequiredMixin, ListView): @@ -1128,10 +1083,7 @@ class VirtualMachineView(LoginRequiredMixin, View): for m in storage: pass storage.used = True - return HttpResponse( - json.dumps({'text': ugettext('Terminated')}), - content_type="application/json" - ) + return JsonResponse({'text': ugettext('Terminated')}) else: return redirect(reverse('hosting:virtual_machines')) elif self.request.is_ajax(): @@ -1262,10 +1214,7 @@ class VirtualMachineView(LoginRequiredMixin, View): ["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]), } send_plain_email_task.delay(email_to_admin_data) - return HttpResponse( - json.dumps(response), - content_type="application/json" - ) + return JsonResponse(response) class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, From 736253fedacd424eb709fc0c4e0d78a8435d2749 Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Fri, 20 Apr 2018 20:27:26 +0530 Subject: [PATCH 09/43] refactor imports --- datacenterlight/views.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 203846fb..49e4d15a 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -1,4 +1,3 @@ -import json import logging from django import forms @@ -7,22 +6,18 @@ from django.contrib import messages from django.contrib.auth import login, authenticate from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse -from django.http import HttpResponseRedirect, HttpResponse, JsonResponse +from django.http import HttpResponseRedirect, JsonResponse from django.shortcuts import render from django.utils.translation import get_language, ugettext_lazy as _ from django.views.decorators.cache import cache_control from django.views.generic import FormView, CreateView, DetailView -from datacenterlight.tasks import create_vm_task from hosting.forms import HostingUserLoginForm -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder from membership.models import CustomUser, StripeCustomer from opennebula_api.serializers import VMTemplateSerializer -from utils.forms import ( - BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm -) +from utils.forms import BillingAddressForm, BillingAddressFormSignup from utils.hosting_utils import get_vm_price -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .forms import ContactForm From 3b6c2b9d4e578b60c8e9264ce591034d3737569c Mon Sep 17 00:00:00 2001 From: Arvind Tiwari Date: Sat, 21 Apr 2018 22:27:43 +0530 Subject: [PATCH 10/43] fix vm_id default --- hosting/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/models.py b/hosting/models.py index 09c6eb2a..de4d3aec 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -72,7 +72,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS @classmethod - def create(cls, price=None, vm_id=None, customer=None, + def create(cls, price=None, vm_id=0, customer=None, billing_address=None, vm_pricing=None): instance = cls.objects.create( price=price, From 5748eecedb665675ae62b4bf5eb33dc55209c52f Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:16:49 +0200 Subject: [PATCH 11/43] Create field for storing os_templates_to_show --- datacenterlight/models.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 729bbdf9..3801465a 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -1,5 +1,7 @@ import logging +from django import forms +from django.contrib.postgres.fields import ArrayField from django.db import models logger = logging.getLogger(__name__) @@ -32,7 +34,31 @@ class VMTemplate(models.Model): return vm_template +class MultipleChoiceArrayField(ArrayField): + """ + A field that allows us to store an array of choices. + Uses Django's Postgres ArrayField + and a MultipleChoiceField for its formfield. + """ + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.base_field.choices, + 'initial': [c[0] for c in self.base_field.choices], + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + class VMPricing(models.Model): + VMTemplateChoices = list( + (str(obj.opennebula_vm_template_id), obj.name) + for obj in VMTemplate.objects.all() + ) name = models.CharField(max_length=255, unique=True) vat_inclusive = models.BooleanField(default=True) vat_percentage = models.DecimalField( @@ -55,6 +81,18 @@ class VMPricing(models.Model): max_digits=6, decimal_places=2, default=0 ) + vm_templates_to_show = MultipleChoiceArrayField( + base_field=models.CharField( + blank=True, + max_length=256, + choices=VMTemplateChoices + ), + default=list, + blank=True, + help_text="Not selecting any items above will result in showing all " + "templates" + ) + def __str__(self): display_str = self.name + ' => ' + ' - '.join([ '{}/Core'.format(self.cores_unit_price.normalize()), From 94f520be355754a9164014f2569cf248441f4337 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:17:18 +0200 Subject: [PATCH 12/43] Add migration --- .../0024_vmpricing_vm_templates_to_show.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py diff --git a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py new file mode 100644 index 00000000..5705df5b --- /dev/null +++ b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-06-13 09:09 +from __future__ import unicode_literals + +import datacenterlight.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0023_auto_20180524_0349'), + ] + + operations = [ + migrations.AddField( + model_name='vmpricing', + name='vm_templates_to_show', + field=datacenterlight.models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Not selecting any items above will result in showing all templates', size=None), + ), + ] From b872777bda964c36dd65073f03650db25e9a977c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Jun 2018 11:53:43 +0200 Subject: [PATCH 13/43] Filter context templates also by the ids that have been set for the calculator --- datacenterlight/cms_plugins.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 769824e0..9c6279bc 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -88,9 +88,15 @@ class DCLCalculatorPlugin(CMSPluginBase): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) - context['templates'] = VMTemplate.objects.filter( - vm_type=instance.vm_type - ) + ids = instance.pricing.vm_templates_to_show + if ids: + context['templates'] = VMTemplate.objects.filter( + vm_type=instance.vm_type + ).filter(opennebula_vm_template_id__in=ids) + else: + context['templates'] = VMTemplate.objects.filter( + vm_type=instance.vm_type + ) return context From dcbb0c2d64bb217e96a2921c115e8048d8f2b3c7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 19 Jun 2018 08:49:21 +0200 Subject: [PATCH 14/43] Add google analytics code for comic.ungleich.ch --- dynamicweb/settings/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index f540e998..75dfaa73 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -630,6 +630,7 @@ GOOGLE_ANALYTICS_PROPERTY_IDS = { 'ipv6onlyhosting.ch': 'UA-62285904-10', 'ipv6onlyhosting.net': 'UA-62285904-10', 'ipv6onlyhosting.com': 'UA-62285904-10', + 'comic.ungleich.ch': 'UA-62285904-13', '127.0.0.1:8000': 'localhost', 'dynamicweb-development.ungleich.ch': 'development', 'dynamicweb-staging.ungleich.ch': 'staging' From 79e83b4480e731b1ac8730227a2c691f74fe4b27 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 09:08:22 +0200 Subject: [PATCH 15/43] Refactor show_vm_templates to DCLCalculatorPluginModel from VMPricing --- datacenterlight/cms_models.py | 45 +++++++++++++++++++ datacenterlight/cms_plugins.py | 2 +- ...culatorpluginmodel_vm_templates_to_show.py | 21 +++++++++ .../0024_vmpricing_vm_templates_to_show.py | 21 --------- datacenterlight/models.py | 38 ---------------- 5 files changed, 67 insertions(+), 60 deletions(-) create mode 100644 datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py delete mode 100644 datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index e1703aaa..deb84dc7 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -2,6 +2,8 @@ from cms.extensions import PageExtension from cms.extensions.extension_pool import extension_pool from cms.models.fields import PlaceholderField from cms.models.pluginmodel import CMSPlugin +from django import forms +from django.contrib.postgres.fields import ArrayField from django.contrib.sites.models import Site from django.db import models from django.utils.safestring import mark_safe @@ -292,7 +294,35 @@ class DCLSectionPromoPluginModel(CMSPlugin): return extra_classes +class MultipleChoiceArrayField(ArrayField): + """ + A field that allows us to store an array of choices. + Uses Django's Postgres ArrayField + and a MultipleChoiceField for its formfield. + """ + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.base_field.choices, + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + class DCLCalculatorPluginModel(CMSPlugin): + VMTemplateChoices = list( + ( + str(obj.opennebula_vm_template_id), + (obj.name + ' - ' + VMTemplate.IPV6.title() + if obj.vm_type == VMTemplate.IPV6 else obj.name + ) + ) + for obj in VMTemplate.objects.all() + ) pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", @@ -303,3 +333,18 @@ class DCLCalculatorPluginModel(CMSPlugin): max_length=50, choices=VMTemplate.VM_TYPE_CHOICES, default=VMTemplate.PUBLIC ) + vm_templates_to_show = MultipleChoiceArrayField( + base_field=models.CharField( + blank=True, + max_length=256, + choices=VMTemplateChoices + ), + default=list, + blank=True, + help_text="Recommended: If you wish to show all templates of the " + "corresponding VM Type (public/ipv6only), please do not " + "select any of the items in the above field. " + "This will allow any new template(s) added " + "in the backend to be automatically listed in this " + "calculator instance." + ) diff --git a/datacenterlight/cms_plugins.py b/datacenterlight/cms_plugins.py index 9c6279bc..95a496d8 100644 --- a/datacenterlight/cms_plugins.py +++ b/datacenterlight/cms_plugins.py @@ -88,7 +88,7 @@ class DCLCalculatorPlugin(CMSPluginBase): context = super(DCLCalculatorPlugin, self).render( context, instance, placeholder ) - ids = instance.pricing.vm_templates_to_show + ids = instance.vm_templates_to_show if ids: context['templates'] = VMTemplate.objects.filter( vm_type=instance.vm_type diff --git a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py new file mode 100644 index 00000000..179dcff9 --- /dev/null +++ b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-06-24 06:54 +from __future__ import unicode_literals + +import datacenterlight.cms_models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0023_auto_20180524_0349'), + ] + + operations = [ + migrations.AddField( + model_name='dclcalculatorpluginmodel', + name='vm_templates_to_show', + field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), + ), + ] diff --git a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py b/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py deleted file mode 100644 index 5705df5b..00000000 --- a/datacenterlight/migrations/0024_vmpricing_vm_templates_to_show.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-06-13 09:09 -from __future__ import unicode_literals - -import datacenterlight.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('datacenterlight', '0023_auto_20180524_0349'), - ] - - operations = [ - migrations.AddField( - model_name='vmpricing', - name='vm_templates_to_show', - field=datacenterlight.models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Not selecting any items above will result in showing all templates', size=None), - ), - ] diff --git a/datacenterlight/models.py b/datacenterlight/models.py index 3801465a..729bbdf9 100644 --- a/datacenterlight/models.py +++ b/datacenterlight/models.py @@ -1,7 +1,5 @@ import logging -from django import forms -from django.contrib.postgres.fields import ArrayField from django.db import models logger = logging.getLogger(__name__) @@ -34,31 +32,7 @@ class VMTemplate(models.Model): return vm_template -class MultipleChoiceArrayField(ArrayField): - """ - A field that allows us to store an array of choices. - Uses Django's Postgres ArrayField - and a MultipleChoiceField for its formfield. - """ - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.MultipleChoiceField, - 'choices': self.base_field.choices, - 'initial': [c[0] for c in self.base_field.choices], - } - defaults.update(kwargs) - # Skip our parent's formfield implementation completely as we don't - # care for it. - # pylint:disable=bad-super-call - return super(ArrayField, self).formfield(**defaults) - - class VMPricing(models.Model): - VMTemplateChoices = list( - (str(obj.opennebula_vm_template_id), obj.name) - for obj in VMTemplate.objects.all() - ) name = models.CharField(max_length=255, unique=True) vat_inclusive = models.BooleanField(default=True) vat_percentage = models.DecimalField( @@ -81,18 +55,6 @@ class VMPricing(models.Model): max_digits=6, decimal_places=2, default=0 ) - vm_templates_to_show = MultipleChoiceArrayField( - base_field=models.CharField( - blank=True, - max_length=256, - choices=VMTemplateChoices - ), - default=list, - blank=True, - help_text="Not selecting any items above will result in showing all " - "templates" - ) - def __str__(self): display_str = self.name + ' => ' + ' - '.join([ '{}/Core'.format(self.cores_unit_price.normalize()), From 70cac38f819bb67b50290e91a03afcb5bacdb4b6 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 10:28:17 +0200 Subject: [PATCH 16/43] Move initialization of VMTemplates out of the plugin --- datacenterlight/cms_models.py | 29 +++++++++---------- ...culatorpluginmodel_vm_templates_to_show.py | 4 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index deb84dc7..8c31696f 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -300,20 +300,6 @@ class MultipleChoiceArrayField(ArrayField): Uses Django's Postgres ArrayField and a MultipleChoiceField for its formfield. """ - - def formfield(self, **kwargs): - defaults = { - 'form_class': forms.MultipleChoiceField, - 'choices': self.base_field.choices, - } - defaults.update(kwargs) - # Skip our parent's formfield implementation completely as we don't - # care for it. - # pylint:disable=bad-super-call - return super(ArrayField, self).formfield(**defaults) - - -class DCLCalculatorPluginModel(CMSPlugin): VMTemplateChoices = list( ( str(obj.opennebula_vm_template_id), @@ -323,6 +309,20 @@ class DCLCalculatorPluginModel(CMSPlugin): ) for obj in VMTemplate.objects.all() ) + + def formfield(self, **kwargs): + defaults = { + 'form_class': forms.MultipleChoiceField, + 'choices': self.VMTemplateChoices, + } + defaults.update(kwargs) + # Skip our parent's formfield implementation completely as we don't + # care for it. + # pylint:disable=bad-super-call + return super(ArrayField, self).formfield(**defaults) + + +class DCLCalculatorPluginModel(CMSPlugin): pricing = models.ForeignKey( VMPricing, related_name="dcl_custom_pricing_vm_pricing", @@ -337,7 +337,6 @@ class DCLCalculatorPluginModel(CMSPlugin): base_field=models.CharField( blank=True, max_length=256, - choices=VMTemplateChoices ), default=list, blank=True, diff --git a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py index 179dcff9..65bfce21 100644 --- a/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py +++ b/datacenterlight/migrations/0024_dclcalculatorpluginmodel_vm_templates_to_show.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-06-24 06:54 +# Generated by Django 1.9.4 on 2018-06-24 08:23 from __future__ import unicode_literals import datacenterlight.cms_models @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='dclcalculatorpluginmodel', name='vm_templates_to_show', - field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, choices=[('4', 'CentOS 7'), ('14', 'Debian 8'), ('25', 'Ubuntu 14.04'), ('26', 'Ubuntu 16.04'), ('36', 'Devuan Jessie'), ('65', 'Devuan Ascii'), ('69', 'FreeBSD 11.1')], max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), + field=datacenterlight.cms_models.MultipleChoiceArrayField(base_field=models.CharField(blank=True, max_length=256), blank=True, default=list, help_text='Recommended: If you wish to show all templates of the corresponding VM Type (public/ipv6only), please do not select any of the items in the above field. This will allow any new template(s) added in the backend to be automatically listed in this calculator instance.', size=None), ), ] From fb9000de901737eb92ec8b1114e576628501ee1b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 14:47:05 +0200 Subject: [PATCH 17/43] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index 8146462d..045ab004 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator 1.8: 2018-05-01 From 549e882ebe114880e31dbba720e3eff6d38d3ba3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 14:54:50 +0200 Subject: [PATCH 18/43] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 045ab004..2cf17fbe 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #4847: [comic] Add google analytics code for comic.ungleich.ch * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator From e816f65114b0f7463eb7c11f33c0c968b52d4df2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 16:00:54 +0200 Subject: [PATCH 19/43] Update Changelog --- Changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 2cf17fbe..df8b892f 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,6 @@ Next: - * #4847: [comic] Add google analytics code for comic.ungleich.ch + * #4799: [dcl] Show selected vm templates only in calculator (PR #638) + * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) 1.9: 2018-05-16 * #4559: [cms] enable discount on cms calculator From a5c42b9c44f93406631945c5409be5449931652f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 24 Jun 2018 17:05:33 +0200 Subject: [PATCH 20/43] Update Changelog for 1.9.1 --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index df8b892f..d175a734 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,4 @@ -Next: +1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) * feature: add vm_type option to vm_template and dcl calculator to distinguish between public and ipv6only templates (PR #635) From 6c2eabbe6af3880c6ea4e09aa9baf33111e0d72a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 00:01:50 +0200 Subject: [PATCH 21/43] Use the public ipv4 if it exists; otherwise use the ipv6 to do a cdist cdist ssh configure --- datacenterlight/tasks.py | 2 +- opennebula_api/models.py | 59 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index db479b43..07d132e2 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -190,7 +190,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, # try to see if we have the IP and that if the ssh keys can # be configured - new_host = manager.get_primary_ipv4(vm_id) + new_host = manager.get_primary_ip(vm_id) logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) if new_host is not None: custom_user = CustomUser.objects.get(email=user.get('email')) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 35f3d8e8..d573d6d5 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -218,6 +218,59 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError + def is_public_ipv4(self, ipv4): + """ + Checks whether the passed ipv4 is a public IP. + + :param ipv4: + :return: Returns true if it is a public IP else returns false + """ + if ipv4.startswith("10."): + return False + else: + return True + + def get_primary_ip(self, vm_id): + """ + Returns primary ipv4 if it exists and is a public ip. Otherwise returns + the IPv6 of the machine + + :param vm_id: + :return: + """ + ipv4 = self.get_primary_ipv4(vm_id) + if self.is_public_ipv4(ipv4): + return ipv4 + else: + return self.get_primary_ipv6(vm_id) + + def get_primary_ipv6(self, vm_id): + """ + Returns the primary IPv6 of the given vm. + For now, we return the first available IPv6 (to be changed later) + + :return: An IP address string, if it exists else returns None + """ + all_ipv4s = self.get_vm_ipv4_addresses(vm_id) + if len(all_ipv4s) > 0: + return all_ipv4s[0] + else: + return None + + def get_vm_ipv6_addresses(self, vm_id): + """ + Returns a list of IPv6 addresses of the given vm + + :param vm_id: The ID of the vm + :return: + """ + ipv6s = [] + vm = self.get_vm(vm_id) + for nic in vm.template.nics: + if hasattr(nic, 'ip6_global'): + ipv6s.append(nic.ip6_global) + return ipv6s + def get_primary_ipv4(self, vm_id): """ Returns the primary IPv4 of the given vm. @@ -579,7 +632,11 @@ class OpenNebulaManager(): vm = self.get_vm(order.vm_id) for nic in vm.template.nics: if hasattr(nic, 'ip'): - hosts.append(nic.ip) + if str(nic.ip).startswith("10."): + if hasattr(nic, 'ip6_global'): + hosts.append(nic.ip6_global) + else: + hosts.append(nic.ip) except WrongIdError: logger.debug( "VM with ID {} does not exist".format(order.vm_id)) From 1ec7cb876168a06a89597c3d9160b264271a4092 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 00:44:17 +0200 Subject: [PATCH 22/43] Fix silly copy/paste bug --- opennebula_api/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index d573d6d5..0cc870c3 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -251,9 +251,9 @@ class OpenNebulaManager(): :return: An IP address string, if it exists else returns None """ - all_ipv4s = self.get_vm_ipv4_addresses(vm_id) - if len(all_ipv4s) > 0: - return all_ipv4s[0] + all_ipv6s = self.get_vm_ipv6_addresses(vm_id) + if len(all_ipv6s) > 0: + return all_ipv6s[0] else: return None From 4a5c5f79425341ede31eac3b7878a6f5212183b3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 01:06:10 +0200 Subject: [PATCH 23/43] Refactor getting primary ip of a vm --- opennebula_api/models.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 0cc870c3..5416b6f2 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -629,14 +629,8 @@ class OpenNebulaManager(): "the ssh keys.".format(self.email)) for order in all_orders: try: - vm = self.get_vm(order.vm_id) - for nic in vm.template.nics: - if hasattr(nic, 'ip'): - if str(nic.ip).startswith("10."): - if hasattr(nic, 'ip6_global'): - hosts.append(nic.ip6_global) - else: - hosts.append(nic.ip) + ip = self.get_primary_ip(order.vm_id) + hosts.append(ip) except WrongIdError: logger.debug( "VM with ID {} does not exist".format(order.vm_id)) From 60260ccb0811299616c08e37c50ab922767cabdb Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 09:06:28 +0200 Subject: [PATCH 24/43] Attempt to fix flake8 error --- datacenterlight/cms_models.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/datacenterlight/cms_models.py b/datacenterlight/cms_models.py index 8c31696f..62a7b312 100644 --- a/datacenterlight/cms_models.py +++ b/datacenterlight/cms_models.py @@ -3,6 +3,7 @@ from cms.extensions.extension_pool import extension_pool from cms.models.fields import PlaceholderField from cms.models.pluginmodel import CMSPlugin from django import forms +from django.conf import settings from django.contrib.postgres.fields import ArrayField from django.contrib.sites.models import Site from django.db import models @@ -300,15 +301,17 @@ class MultipleChoiceArrayField(ArrayField): Uses Django's Postgres ArrayField and a MultipleChoiceField for its formfield. """ - VMTemplateChoices = list( - ( - str(obj.opennebula_vm_template_id), - (obj.name + ' - ' + VMTemplate.IPV6.title() - if obj.vm_type == VMTemplate.IPV6 else obj.name + VMTemplateChoices = [] + if settings.OPENNEBULA_DOMAIN != 'test_domain': + VMTemplateChoices = list( + ( + str(obj.opennebula_vm_template_id), + (obj.name + ' - ' + VMTemplate.IPV6.title() + if obj.vm_type == VMTemplate.IPV6 else obj.name + ) ) - ) - for obj in VMTemplate.objects.all() - ) + for obj in VMTemplate.objects.all() + ) def formfield(self, **kwargs): defaults = { From 88f0d733365f68db9ba8ec1274125251548bef73 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 27 Jun 2018 09:21:41 +0200 Subject: [PATCH 25/43] Update Changelog --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index d175a734..d616964f 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +Next: + * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) * #4847: [comic] Add google analytics code for comic.ungleich.ch (PR #639) From 88e6d9d21619e132eaa29587263fabfbad2e448a Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:24:16 +0200 Subject: [PATCH 26/43] Separate two error messages with a period --- datacenterlight/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 66a91917..3554580f 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -121,7 +121,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): ) logger.error(emsg) if error_msg: - error_msg += emsg + error_msg += ". " + emsg else: error_msg = emsg From f1e021e1e9d9ded16fd32fffed5ad0c983b14ac8 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:24:53 +0200 Subject: [PATCH 27/43] Improve comments --- datacenterlight/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 042eec73..8fed5dcd 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -67,12 +67,12 @@ def create_vm(billing_address_data, stripe_customer_id, specs, billing_address_user_form.is_valid() billing_address_user_form.save() - # Associate an order with a stripe subscription + # Associate the given stripe subscription with the order order.set_subscription_id( stripe_subscription_obj.id, card_details_dict ) - # If the Stripe payment succeeds, set order status approved + # Set order status approved order.set_approved() create_vm_task.delay(vm_template_id, user, specs, template, order.id) From 5eff54cffecd5616be658d4719829ac7b3aac4d5 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 27 Jun 2018 12:34:41 +0200 Subject: [PATCH 28/43] Fix a bug -- json not imported; use JsonResponse instead of HttpResponse --- hosting/views.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 07bafd97..e5383535 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1266,10 +1266,7 @@ class VirtualMachineView(LoginRequiredMixin, View): ["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]), } send_plain_email_task.delay(email_to_admin_data) - return HttpResponse( - json.dumps(response), - content_type="application/json" - ) + return JsonResponse(response) class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, From 68a65b7bc73056364673dac63c90dfd8b377f5b5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 30 Jun 2018 00:30:51 +0200 Subject: [PATCH 29/43] Detect private IP correctly --- opennebula_api/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 5416b6f2..c06d790e 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -1,3 +1,4 @@ +import ipaddress import logging import socket @@ -225,7 +226,7 @@ class OpenNebulaManager(): :param ipv4: :return: Returns true if it is a public IP else returns false """ - if ipv4.startswith("10."): + if ipaddress.ip_address(ipv4).is_private: return False else: return True From 52e53d479ecfeb729d6c68ab45e0e3d94a333169 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 07:57:15 +0200 Subject: [PATCH 30/43] Rename a variable: ipv6s -> ipv6_list --- opennebula_api/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index c06d790e..3b69d91d 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -252,9 +252,9 @@ class OpenNebulaManager(): :return: An IP address string, if it exists else returns None """ - all_ipv6s = self.get_vm_ipv6_addresses(vm_id) - if len(all_ipv6s) > 0: - return all_ipv6s[0] + ipv6_list = self.get_vm_ipv6_addresses(vm_id) + if len(ipv6_list) > 0: + return ipv6_list[0] else: return None @@ -265,12 +265,12 @@ class OpenNebulaManager(): :param vm_id: The ID of the vm :return: """ - ipv6s = [] + ipv6_list = [] vm = self.get_vm(vm_id) for nic in vm.template.nics: if hasattr(nic, 'ip6_global'): - ipv6s.append(nic.ip6_global) - return ipv6s + ipv6_list.append(nic.ip6_global) + return ipv6_list def get_primary_ipv4(self, vm_id): """ From 8993a7bde1a13f37648787c2488e5218331b6307 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:05:23 +0200 Subject: [PATCH 31/43] Reorganize imports + format code --- datacenterlight/tasks.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 07d132e2..4d0fa070 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,8 +1,8 @@ from datetime import datetime +from celery import current_task from celery.exceptions import MaxRetriesExceededError from celery.utils.log import get_task_logger -from celery import current_task from django.conf import settings from django.core.mail import EmailMessage from django.core.urlresolvers import reverse @@ -14,11 +14,10 @@ from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer, CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.forms import UserBillingAddressForm +from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.mailer import BaseEmail from utils.models import BillingAddress - from .models import VMPricing logger = get_task_logger(__name__) @@ -173,8 +172,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'order_url': reverse('hosting:orders', kwargs={'pk': order.id}), 'page_header': _( - 'Your New VM %(vm_name)s at Data Center Light') % { - 'vm_name': vm.get('name')}, + 'Your New VM %(vm_name)s at Data Center Light') % + {'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { From c13af950179bef9c8fb408e9c22d1df0dcefc699 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:07:29 +0200 Subject: [PATCH 32/43] Refactor code: get_primary_ip -> get_ipv6 and remove unwanted code --- datacenterlight/tasks.py | 18 +++++----- opennebula_api/models.py | 74 ++++++---------------------------------- 2 files changed, 21 insertions(+), 71 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 4d0fa070..b220cc38 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -187,11 +187,11 @@ def create_vm_task(self, vm_template_id, user, specs, template, email = BaseEmail(**email_data) email.send() - # try to see if we have the IP and that if the ssh keys can - # be configured - new_host = manager.get_primary_ip(vm_id) + # try to see if we have the IPv6 of the new vm and that if the ssh + # keys can be configured + vm_ipv6 = manager.get_ipv6(vm_id) logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) - if new_host is not None: + if vm_ipv6 is not None: custom_user = CustomUser.objects.get(email=user.get('email')) get_or_create_vm_detail(custom_user, manager, vm_id) if custom_user is not None: @@ -202,13 +202,15 @@ def create_vm_task(self, vm_template_id, user, specs, template, logger.debug( "Calling configure on {host} for " "{num_keys} keys".format( - host=new_host, num_keys=len(keys))) + 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=[new_host], - countdown=75) + manager.manage_public_key( + keys, hosts=[vm_ipv6], countdown=75 + ) except Exception as e: logger.error(str(e)) try: diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 3b69d91d..d6811349 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -219,46 +219,19 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - def is_public_ipv4(self, ipv4): + def get_ipv6(self, vm_id): """ - Checks whether the passed ipv4 is a public IP. + Returns the first IPv6 of the given vm. - :param ipv4: - :return: Returns true if it is a public IP else returns false + :return: An IPv6 address string, if it exists else returns None """ - if ipaddress.ip_address(ipv4).is_private: - return False - else: - return True - - def get_primary_ip(self, vm_id): - """ - Returns primary ipv4 if it exists and is a public ip. Otherwise returns - the IPv6 of the machine - - :param vm_id: - :return: - """ - ipv4 = self.get_primary_ipv4(vm_id) - if self.is_public_ipv4(ipv4): - return ipv4 - else: - return self.get_primary_ipv6(vm_id) - - def get_primary_ipv6(self, vm_id): - """ - Returns the primary IPv6 of the given vm. - For now, we return the first available IPv6 (to be changed later) - - :return: An IP address string, if it exists else returns None - """ - ipv6_list = self.get_vm_ipv6_addresses(vm_id) + ipv6_list = self.get_all_ipv6_addresses(vm_id) if len(ipv6_list) > 0: return ipv6_list[0] else: return None - def get_vm_ipv6_addresses(self, vm_id): + def get_all_ipv6_addresses(self, vm_id): """ Returns a list of IPv6 addresses of the given vm @@ -272,33 +245,6 @@ class OpenNebulaManager(): ipv6_list.append(nic.ip6_global) return ipv6_list - def get_primary_ipv4(self, vm_id): - """ - Returns the primary IPv4 of the given vm. - To be changed later. - - :return: An IP address string, if it exists else returns None - """ - all_ipv4s = self.get_vm_ipv4_addresses(vm_id) - if len(all_ipv4s) > 0: - return all_ipv4s[0] - else: - return None - - def get_vm_ipv4_addresses(self, vm_id): - """ - Returns a list of IPv4 addresses of the given vm - - :param vm_id: The ID of the vm - :return: - """ - ipv4s = [] - vm = self.get_vm(vm_id) - for nic in vm.template.nics: - if hasattr(nic, 'ip'): - ipv4s.append(nic.ip) - return ipv4s - def create_vm(self, template_id, specs, ssh_key=None, vm_name=None): template = self.get_template(template_id) @@ -601,7 +547,7 @@ class OpenNebulaManager(): 'value': 'sha-.....', # public key as string 'state': True # whether key is to be added or } # removed - :param hosts: A list of hosts IP addresses + :param hosts: A list of hosts IPv6 addresses :param countdown: Parameter to be passed to celery apply_async Allows to delay a task by `countdown` number of seconds :return: @@ -614,12 +560,14 @@ class OpenNebulaManager(): link_error=save_ssh_key_error_handler.s()) else: logger.debug( - "Keys and/or hosts are empty, so not managing any keys") + "Keys and/or hosts are empty, so not managing any keys" + ) def get_all_hosts(self): """ A utility function to obtain all hosts of this owner - :return: A list of hosts IP addresses, empty if none exist + :return: A list of IPv6 addresses of all the hosts of this customer or + an empty list if none exist """ owner = CustomUser.objects.filter( email=self.email).first() @@ -630,7 +578,7 @@ class OpenNebulaManager(): "the ssh keys.".format(self.email)) for order in all_orders: try: - ip = self.get_primary_ip(order.vm_id) + ip = self.get_ipv6(order.vm_id) hosts.append(ip) except WrongIdError: logger.debug( From ae0d4c0841d10a0d459bcdfd993f0727368cdbd0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:17:45 +0200 Subject: [PATCH 33/43] Refactor code --- opennebula_api/models.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index d6811349..29632009 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -1,4 +1,3 @@ -import ipaddress import logging import socket @@ -54,27 +53,18 @@ class OpenNebulaManager(): ConnectionError: If the connection to the opennebula server can't be established """ - return oca.Client("{0}:{1}".format( - user.email, - user.password), - "{protocol}://{domain}:{port}{endpoint}".format( - protocol=settings.OPENNEBULA_PROTOCOL, - domain=settings.OPENNEBULA_DOMAIN, - port=settings.OPENNEBULA_PORT, - endpoint=settings.OPENNEBULA_ENDPOINT - )) + return self._get_opennebula_client(user.email, user.password) def _get_opennebula_client(self, username, password): - return oca.Client("{0}:{1}".format( - username, - - password), + return oca.Client( + "{0}:{1}".format(username, password), "{protocol}://{domain}:{port}{endpoint}".format( protocol=settings.OPENNEBULA_PROTOCOL, domain=settings.OPENNEBULA_DOMAIN, port=settings.OPENNEBULA_PORT, endpoint=settings.OPENNEBULA_ENDPOINT - )) + ) + ) def _get_user(self, user): """Get the corresponding opennebula user for a CustomUser object @@ -438,8 +428,9 @@ class OpenNebulaManager(): return template_id def delete_template(self, template_id): - self.oneadmin_client.call(oca.VmTemplate.METHODS[ - 'delete'], template_id, False) + self.oneadmin_client.call( + oca.VmTemplate.METHODS['delete'], template_id, False + ) def change_user_password(self, passwd_hash): self.oneadmin_client.call( From ef8e380ab7a0884be9645076e134910ebd1fa5f9 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 11:22:42 +0200 Subject: [PATCH 34/43] Fix flake8 error --- datacenterlight/tasks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index b220cc38..22ee29ad 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -172,8 +172,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, 'order_url': reverse('hosting:orders', kwargs={'pk': order.id}), 'page_header': _( - 'Your New VM %(vm_name)s at Data Center Light') % - {'vm_name': vm.get('name')}, + 'Your New VM %(vm_name)s at Data Center Light') % { + 'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { From 6593983f0488b704848c244f51c0a87599d2eb78 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:02:35 +0200 Subject: [PATCH 35/43] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index d616964f..499fd781 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24 * #4799: [dcl] Show selected vm templates only in calculator (PR #638) From 43b3a63958fcdec044cb3f2d736168bce6aeb0ae Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:25:02 +0200 Subject: [PATCH 36/43] Reorganize imports --- datacenterlight/tasks.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 259a2e36..281d5f45 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,14 +10,12 @@ from django.utils import translation from django.utils.translation import ugettext_lazy as _ from dynamicweb.celery import app -from hosting.models import HostingOrder, HostingBill -from membership.models import StripeCustomer, CustomUser +from hosting.models import HostingOrder +from membership.models import CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from utils.forms import UserBillingAddressForm from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail from utils.mailer import BaseEmail -from utils.models import BillingAddress from utils.stripe_utils import StripeUtils from .models import VMPricing From 5851277d9aa0e9b6a234be89a53c075f4e066709 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 16:36:36 +0200 Subject: [PATCH 37/43] Reorganize imports --- datacenterlight/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 8fed5dcd..5388b9d3 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -5,8 +5,8 @@ from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress -from .models import VMPricing from .cms_models import CMSIntegration +from .models import VMPricing def get_cms_integration(name): From 900f014d922fd73fb97da08a35e3074d1d2d74f4 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 18:33:10 +0200 Subject: [PATCH 38/43] Save order specifications in HostingOrder also --- datacenterlight/utils.py | 11 ++++-- hosting/migrations/0045_auto_20180701_1631.py | 35 +++++++++++++++++++ hosting/models.py | 16 ++++++++- 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 hosting/migrations/0045_auto_20180701_1631.py diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 5388b9d3..7b3ef73d 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,12 +1,12 @@ from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task -from hosting.models import HostingOrder, HostingBill +from hosting.models import HostingOrder, HostingBill, OrderSpecifications from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress from .cms_models import CMSIntegration -from .models import VMPricing +from .models import VMPricing, VMTemplate def get_cms_integration(name): @@ -53,6 +53,13 @@ def create_vm(billing_address_data, stripe_customer_id, specs, vm_pricing=vm_pricing ) + order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( + vm_template=VMTemplate.objects.get(vm_template_id), + cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] + ) + order.order_specs = order_specs_obj + order.save() + # Create a Hosting Bill HostingBill.create(customer=customer, billing_address=billing_address) diff --git a/hosting/migrations/0045_auto_20180701_1631.py b/hosting/migrations/0045_auto_20180701_1631.py new file mode 100644 index 00000000..8af4d821 --- /dev/null +++ b/hosting/migrations/0045_auto_20180701_1631.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2018-07-01 16:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import utils.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('datacenterlight', '0024_dclcalculatorpluginmodel_vm_templates_to_show'), + ('hosting', '0044_hostingorder_vm_pricing'), + ] + + operations = [ + migrations.CreateModel( + name='OrderSpecifications', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('cores', models.IntegerField(default=0)), + ('memory', models.IntegerField(default=0)), + ('hdd_size', models.IntegerField(default=0)), + ('ssd_size', models.IntegerField(default=0)), + ('vm_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMTemplate')), + ], + bases=(utils.mixins.AssignPermissionsMixin, models.Model), + ), + migrations.AddField( + model_name='hostingorder', + name='order_specs', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.OrderSpecifications'), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index de4d3aec..c30a25a8 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -7,7 +7,7 @@ from django.utils import timezone from django.utils.functional import cached_property from Crypto.PublicKey import RSA -from datacenterlight.models import VMPricing +from datacenterlight.models import VMPricing, VMTemplate from membership.models import StripeCustomer, CustomUser from utils.models import BillingAddress from utils.mixins import AssignPermissionsMixin @@ -41,6 +41,19 @@ class HostingPlan(models.Model): return price +class OrderSpecifications(AssignPermissionsMixin, models.Model): + vm_template = models.ForeignKey(VMTemplate, blank=True, null=True) + cores = models.IntegerField(default=0) + memory = models.IntegerField(default=0) + hdd_size = models.IntegerField(default=0) + ssd_size = models.IntegerField(default=0) + + def __str__(self): + return "%s - %s cores, %s GB RAM, %s GB SSD" % ( + self.vm_template.name, self.cores, self.memory, self.ssd_size + ) + + class HostingOrder(AssignPermissionsMixin, models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' @@ -56,6 +69,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) + order_specs = models.ForeignKey(OrderSpecifications, null=True, blank=True) permissions = ('view_hostingorder',) From f48005166e9d78b4b25dd02bb4225f8893fc31ac Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 18:42:11 +0200 Subject: [PATCH 39/43] Fix bug getting VMTemplate object from vm_template_id --- datacenterlight/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 7b3ef73d..3d0e4370 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -54,7 +54,9 @@ def create_vm(billing_address_data, stripe_customer_id, specs, ) order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( - vm_template=VMTemplate.objects.get(vm_template_id), + vm_template=VMTemplate.objects.get( + opennebula_vm_template_id=vm_template_id + ), cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] ) order.order_specs = order_specs_obj From 7f57ace92d7f46bd0ae84029cbfcdade2726c784 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 19:23:05 +0200 Subject: [PATCH 40/43] Set default and on_delete attributes --- ...uto_20180701_1631.py => 0045_auto_20180701_1718.py} | 6 +++--- hosting/models.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) rename hosting/migrations/{0045_auto_20180701_1631.py => 0045_auto_20180701_1718.py} (73%) diff --git a/hosting/migrations/0045_auto_20180701_1631.py b/hosting/migrations/0045_auto_20180701_1718.py similarity index 73% rename from hosting/migrations/0045_auto_20180701_1631.py rename to hosting/migrations/0045_auto_20180701_1718.py index 8af4d821..d9705a24 100644 --- a/hosting/migrations/0045_auto_20180701_1631.py +++ b/hosting/migrations/0045_auto_20180701_1718.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-07-01 16:31 +# Generated by Django 1.9.4 on 2018-07-01 17:18 from __future__ import unicode_literals from django.db import migrations, models @@ -23,13 +23,13 @@ class Migration(migrations.Migration): ('memory', models.IntegerField(default=0)), ('hdd_size', models.IntegerField(default=0)), ('ssd_size', models.IntegerField(default=0)), - ('vm_template', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='datacenterlight.VMTemplate')), + ('vm_template', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='datacenterlight.VMTemplate')), ], bases=(utils.mixins.AssignPermissionsMixin, models.Model), ), migrations.AddField( model_name='hostingorder', name='order_specs', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.OrderSpecifications'), + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderSpecifications'), ), ] diff --git a/hosting/models.py b/hosting/models.py index c30a25a8..b182bfc5 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -42,7 +42,10 @@ class HostingPlan(models.Model): class OrderSpecifications(AssignPermissionsMixin, models.Model): - vm_template = models.ForeignKey(VMTemplate, blank=True, null=True) + vm_template = models.ForeignKey( + VMTemplate, blank=True, null=True, default=None, + on_delete=models.SET_NULL + ) cores = models.IntegerField(default=0) memory = models.IntegerField(default=0) hdd_size = models.IntegerField(default=0) @@ -69,7 +72,10 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) - order_specs = models.ForeignKey(OrderSpecifications, null=True, blank=True) + order_specs = models.ForeignKey( + OrderSpecifications, null=True, blank=True, default=None, + on_delete=models.SET_NULL + ) permissions = ('view_hostingorder',) From 00cb1de75d8998675d1e925df70806dad44ae939 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 19:46:21 +0200 Subject: [PATCH 41/43] Add type to OrderSpecification string --- hosting/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index b182bfc5..2ce604d0 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -52,8 +52,9 @@ class OrderSpecifications(AssignPermissionsMixin, models.Model): ssd_size = models.IntegerField(default=0) def __str__(self): - return "%s - %s cores, %s GB RAM, %s GB SSD" % ( - self.vm_template.name, self.cores, self.memory, self.ssd_size + return "%s - %s, %s cores, %s GB RAM, %s GB SSD" % ( + self.vm_template.name, self.vm_template.vm_type, self.cores, + self.memory, self.ssd_size ) From 44900f6a4886f4ded6b744bcebe3f4991018dad7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 22:30:23 +0200 Subject: [PATCH 42/43] Rename OrderSpecifications to OrderDetail --- datacenterlight/utils.py | 6 +++--- ...5_auto_20180701_1718.py => 0045_auto_20180701_2028.py} | 8 ++++---- hosting/models.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) rename hosting/migrations/{0045_auto_20180701_1718.py => 0045_auto_20180701_2028.py} (89%) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 3d0e4370..a6f760af 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,7 +1,7 @@ from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task -from hosting.models import HostingOrder, HostingBill, OrderSpecifications +from hosting.models import HostingOrder, HostingBill, OrderDetail from membership.models import StripeCustomer from utils.forms import UserBillingAddressForm from utils.models import BillingAddress @@ -53,13 +53,13 @@ def create_vm(billing_address_data, stripe_customer_id, specs, vm_pricing=vm_pricing ) - order_specs_obj, obj_created = OrderSpecifications.objects.get_or_create( + order_detail_obj, obj_created = OrderDetail.objects.get_or_create( vm_template=VMTemplate.objects.get( opennebula_vm_template_id=vm_template_id ), cores=specs['cpu'], memory=specs['memory'], ssd_size=specs['disk_size'] ) - order.order_specs = order_specs_obj + order.order_detail = order_detail_obj order.save() # Create a Hosting Bill diff --git a/hosting/migrations/0045_auto_20180701_1718.py b/hosting/migrations/0045_auto_20180701_2028.py similarity index 89% rename from hosting/migrations/0045_auto_20180701_1718.py rename to hosting/migrations/0045_auto_20180701_2028.py index d9705a24..39b58aa8 100644 --- a/hosting/migrations/0045_auto_20180701_1718.py +++ b/hosting/migrations/0045_auto_20180701_2028.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.9.4 on 2018-07-01 17:18 +# Generated by Django 1.9.4 on 2018-07-01 20:28 from __future__ import unicode_literals from django.db import migrations, models @@ -16,7 +16,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='OrderSpecifications', + name='OrderDetail', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('cores', models.IntegerField(default=0)), @@ -29,7 +29,7 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='hostingorder', - name='order_specs', - field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderSpecifications'), + name='order_detail', + field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderDetail'), ), ] diff --git a/hosting/models.py b/hosting/models.py index 2ce604d0..411bd267 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -41,7 +41,7 @@ class HostingPlan(models.Model): return price -class OrderSpecifications(AssignPermissionsMixin, models.Model): +class OrderDetail(AssignPermissionsMixin, models.Model): vm_template = models.ForeignKey( VMTemplate, blank=True, null=True, default=None, on_delete=models.SET_NULL @@ -73,8 +73,8 @@ class HostingOrder(AssignPermissionsMixin, models.Model): price = models.FloatField() subscription_id = models.CharField(max_length=100, null=True) vm_pricing = models.ForeignKey(VMPricing) - order_specs = models.ForeignKey( - OrderSpecifications, null=True, blank=True, default=None, + order_detail = models.ForeignKey( + OrderDetail, null=True, blank=True, default=None, on_delete=models.SET_NULL ) From cb911e05c5cac36eb52dc536ac24c2bb7f492b79 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 1 Jul 2018 22:45:18 +0200 Subject: [PATCH 43/43] Update Changelog --- Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog b/Changelog index 499fd781..712bce85 100644 --- a/Changelog +++ b/Changelog @@ -1,4 +1,5 @@ Next: + * #3934: [dcl,hosting] Create HostingOrder outside celery task and add and associate OrderDetail with HostingOrder * #4890: [hosting] Manage SSH keys using IPv6 of the VM (PR #640) * bugfix: Fix flake8 error that was ignored in release 1.9.1 1.9.1: 2018-06-24