Merge master into task/3747/multiple_cards_support
This commit is contained in:
commit
6d2b011925
14 changed files with 371 additions and 153 deletions
|
|
@ -2,6 +2,9 @@ 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.conf import settings
|
||||
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,6 +295,36 @@ 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.
|
||||
"""
|
||||
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()
|
||||
)
|
||||
|
||||
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,
|
||||
|
|
@ -303,3 +336,17 @@ 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,
|
||||
),
|
||||
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."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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.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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.4 on 2018-06-24 08:23
|
||||
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, 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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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
|
||||
|
|
@ -10,16 +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.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
|
||||
|
||||
from .models import VMPricing
|
||||
|
||||
logger = get_task_logger(__name__)
|
||||
|
|
@ -52,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')
|
||||
|
|
@ -98,42 +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
|
||||
|
||||
# Create a Hosting Bill
|
||||
HostingBill.create(
|
||||
customer=customer, billing_address=billing_address
|
||||
)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
stripe_utils = StripeUtils()
|
||||
stripe_utils.set_subscription_meta_data(
|
||||
stripe_subscription_id, {'ID': vm_id}
|
||||
result = stripe_utils.set_subscription_metadata(
|
||||
subscription_id=hosting_order.subscription_id,
|
||||
metadata={"VM_ID": str(vm_id)}
|
||||
)
|
||||
|
||||
# If the Stripe payment succeeds, set order status approved
|
||||
order.set_approved()
|
||||
if result.get('error') 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
|
||||
|
||||
|
|
@ -147,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']
|
||||
|
|
@ -176,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')},
|
||||
|
|
@ -193,11 +185,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_ipv4(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:
|
||||
|
|
@ -208,13 +200,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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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', 'card_id',
|
||||
'token', 'customer']:
|
||||
if session_var in request.session:
|
||||
del request.session[session_var]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue