Merge pull request #624 from pcoder/task/3934/move_hosting_order_out_of_celery_task

Task/3934/Create hostingorder outside celery task
This commit is contained in:
Pcoder 2018-07-01 22:39:08 +02:00 committed by GitHub
commit 1fa260aaf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 241 additions and 100 deletions

View file

@ -10,14 +10,13 @@ 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
logger = get_task_logger(__name__)
@ -50,24 +49,15 @@ 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('total_price') if 'total_price' in specs
else 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']
final_price = (
specs.get('total_price') if 'total_price' in specs
else specs.get('price')
)
billing_address.save()
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
if 'pass' in user:
on_user = user.get('email')
@ -96,38 +86,43 @@ def create_vm_task(self, vm_template_id, user, specs, template,
if vm_id is None:
raise Exception("Could not create VM")
vm_pricing = VMPricing.get_vm_pricing_by_name(
name=specs['pricing_name']
) if 'pricing_name' in specs else VMPricing.get_default_pricing()
# Create a Hosting Order
order = HostingOrder.create(
price=final_price,
vm_id=vm_id,
customer=customer,
billing_address=billing_address,
vm_pricing=vm_pricing
# Update HostingOrder with the created vm_id
hosting_order = HostingOrder.objects.filter(id=order_id).first()
error_msg = None
try:
hosting_order.vm_id = vm_id
hosting_order.save()
logger.debug(
"Updated hosting_order {} with vm_id={}".format(
hosting_order.id, vm_id
)
)
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}. Details {details}".format(
order_id=order_id, vm_id=vm_id, details=str(ex)
)
)
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)}
)
# Create a Hosting Bill
HostingBill.create(
customer=customer, billing_address=billing_address
if result.get('error') is not None:
emsg = "Could not update subscription metadata for {sub}".format(
sub=hosting_order.subscription_id
)
# 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()
logger.error(emsg)
if error_msg:
error_msg += ". " + emsg
else:
error_msg = emsg
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
@ -141,8 +136,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
if 'pricing_name' in specs:
context['pricing'] = str(VMPricing.get_vm_pricing_by_name(
name=specs['pricing_name']
@ -170,7 +168,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')},

View file

@ -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,24 @@ 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=stripe_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

View file

@ -1,6 +1,12 @@
from django.contrib.sites.models import Site
from datacenterlight.tasks import create_vm_task
from hosting.models import HostingOrder, HostingBill, OrderDetail
from membership.models import StripeCustomer
from utils.forms import UserBillingAddressForm
from utils.models import BillingAddress
from .cms_models import CMSIntegration
from .models import VMPricing, VMTemplate
def get_cms_integration(name):
@ -12,3 +18,76 @@ 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()
vm_pricing = (
VMPricing.get_vm_pricing_by_name(name=specs['pricing_name'])
if 'pricing_name' in specs else
VMPricing.get_default_pricing()
)
final_price = (
specs.get('total_price')
if 'total_price' in specs
else specs.get('price')
)
# 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=final_price,
customer=customer,
billing_address=billing_address,
vm_pricing=vm_pricing
)
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_detail = order_detail_obj
order.save()
# 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 the given stripe subscription with the order
order.set_subscription_id(
stripe_subscription_obj.id, card_details_dict
)
# 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]

View file

@ -1,4 +1,3 @@
import json
import logging
from django import forms
@ -7,13 +6,12 @@ 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, 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
from membership.models import CustomUser, StripeCustomer
@ -24,7 +22,7 @@ from utils.stripe_utils import StripeUtils
from utils.tasks import send_plain_email_task
from .forms import ContactForm
from .models import VMTemplate, VMPricing
from .utils import get_cms_integration
from .utils import get_cms_integration, create_vm
logger = logging.getLogger(__name__)
@ -417,8 +415,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')
@ -458,8 +456,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
@ -514,14 +511,11 @@ 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)
for session_var in ['specs', 'template', 'billing_address',
'billing_address_data',
'token', 'customer', 'pricing_name']:
if session_var in request.session:
del request.session[session_var]
create_vm(
billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user
)
response = {
'status': True,
@ -537,5 +531,4 @@ class OrderConfirmationView(DetailView):
' it is ready.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
return JsonResponse(response)

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2018-07-01 20:28
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='OrderDetail',
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, 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_detail',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='hosting.OrderDetail'),
),
]

View file

@ -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,23 @@ class HostingPlan(models.Model):
return price
class OrderDetail(AssignPermissionsMixin, models.Model):
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)
ssd_size = models.IntegerField(default=0)
def __str__(self):
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
)
class HostingOrder(AssignPermissionsMixin, models.Model):
ORDER_APPROVED_STATUS = 'Approved'
ORDER_DECLINED_STATUS = 'Declined'
@ -56,6 +73,10 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
price = models.FloatField()
subscription_id = models.CharField(max_length=100, null=True)
vm_pricing = models.ForeignKey(VMPricing)
order_detail = models.ForeignKey(
OrderDetail, null=True, blank=True, default=None,
on_delete=models.SET_NULL
)
permissions = ('view_hostingorder',)
@ -72,7 +93,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,

View file

@ -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,8 +32,7 @@ from stored_messages.models import Message
from stored_messages.settings import stored_messages_settings
from datacenterlight.models import VMTemplate, VMPricing
from datacenterlight.tasks import create_vm_task
from datacenterlight.utils import get_cms_integration
from datacenterlight.utils import create_vm, get_cms_integration
from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import (
@ -896,8 +896,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,
@ -906,15 +906,12 @@ 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)
for session_var in ['specs', 'template', 'billing_address',
'billing_address_data',
'token', 'customer']:
if session_var in request.session:
del request.session[session_var]
create_vm(
billing_address_data, stripe_customer_id, specs,
stripe_subscription_obj, card_details_dict, request,
vm_template_id, template, user
)
response = {
'status': True,
@ -926,8 +923,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
' it is ready.'))
}
return HttpResponse(json.dumps(response),
content_type="application/json")
return JsonResponse(response)
class OrdersHostingListView(LoginRequiredMixin, ListView):
@ -1138,10 +1134,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():
@ -1273,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,

View file

@ -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):
"""