From 2a9b208719e7d2eb3cff3c64c38cf4ea6dbafc0a Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 6 Sep 2017 13:25:18 +0530 Subject: [PATCH 01/32] hosting: Moving the stripe charge to order confirmation page (still incomplete) --- hosting/templates/hosting/order_detail.html | 91 +++++++++++++++---- hosting/views.py | 99 ++------------------- 2 files changed, 80 insertions(+), 110 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 64c0b5d3..989d7419 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -20,30 +20,50 @@
-

{{page_header_text}}

{% trans "Order #"%} {{order.id}}

+

{{page_header_text}}

+

+ {% if order %} + {% trans "Order #"%} {{order.id}} + {% endif %} +


{% trans "Date"%}:
- {{order.created_at|date:'Y-m-d H:i'}}

+ + {% if order %} + {{order.created_at|date:'Y-m-d H:i'}} + {% else %} + {% now "Y-m-d H:i" %} + {% endif %} +

+ {% if order %} {% trans "Status:"%}
- {% if order.status == 'Approved' %} - {% trans "Approved" %} - {% else %} - {% trans "Declined" %} - {% endif %} + {% if order.status == 'Approved' %} + {% trans "Approved" %} + {% else %} + {% trans "Declined" %} + {% endif %}

+ {% endif %}

{% trans "Billed To:"%}

+ {% if order %} {{user.name}}
{{order.billing_address.street_address}},{{order.billing_address.postal_code}}
{{order.billing_address.city}}, {{order.billing_address.country}}. + {% else %} + {% with request.session.billing_address_data as billing_address %} + {{billing_address|get_value_from_dict:'cardholder_name'}}
{{billing_address|get_value_from_dict:'street_address'}}, {{billing_address|get_value_from_dict:'postal_code'}}
+ {{billing_address|get_value_from_dict:'city'}}, {{billing_address|get_value_from_dict:'country'}}. + {% endwith %} + {% endif %}
@@ -52,8 +72,13 @@
{% trans "Payment Method:"%}
+ {% if order %} {{order.cc_brand}} {% trans "ending in" %} **** {{order.last4}}
{{user.email}} + {% else %} + {{cc_brand}} {% trans "ending" %} **** {{cc_last4}}
+ {{request.session.user.email}} + {% endif %}
@@ -65,20 +90,48 @@

{% trans "Order summary"%}


-

{% trans "Cores"%} {{vm.cores}}

-
-

{% trans "Memory"%} {{vm.memory}} GB

-
-

{% trans "Disk space"%} {{vm.disk_size}} GB

-
-

{% trans "Total"%}

{{vm.price}} CHF

+ {% if request.session.specs %} + {% with request.session.specs as vm %} +

{% trans "Cores"%} {{vm.cpu}}

+
+

{% trans "Memory"%} {{vm.memory}} GB

+
+

{% trans "Disk space"%} {{vm.disk_size}} GB

+
+

{% trans "Configuration"%} {{request.session.template.name}}

+
+

{% trans "Total"%}

{{vm.price}} CHF /{% trans "Month" %}

+ {% endwith %} + {% else %} +

{% trans "Cores"%} {{vm.cores}}

+
+

{% trans "Memory"%} {{vm.memory}} GB

+
+

{% trans "Disk space"%} {{vm.disk_size}} GB

+
+

{% trans "Total"%}

{{vm.price}} CHF

+ {% endif %}

- {% url 'hosting:payment' as payment_url %} - {% if payment_url in request.META.HTTP_REFERER %} - + {% if not order %} +
+ {% csrf_token %} +
+
+

{% blocktrans with vm_price=request.session.specs.price %}By clicking "Place order" this plan will charge your credit card account with the fee of {{ vm_price }}CHF/month{% endblocktrans %}.

+
+ +
+
+ {% else %} + {% url 'hosting:payment' as payment_url %} + {% if payment_url in request.META.HTTP_REFERER %} + + {% endif %} {% endif %}
diff --git a/hosting/views.py b/hosting/views.py index 08f0862e..67cf8c28 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -569,100 +569,17 @@ class PaymentVMView(LoginRequiredMixin, FormView): customer = StripeCustomer.get_or_create(email=owner.email, token=token) if not customer: - msg = _("Invalid credit card") - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') + form.add_error("__all__", _("Invalid credit card")) + return self.render_to_response( + self.get_context_data(form=form)) # Create Billing Address billing_address = form.save() - - # Make stripe charge to a customer - stripe_utils = StripeUtils() - charge_response = stripe_utils.make_charge(amount=final_price, - customer=customer.stripe_id) - - # Check if the payment was approved - if not charge_response.get('response_object'): - msg = charge_response.get('error') - messages.add_message( - self.request, messages.ERROR, msg, extra_tags='make_charge_error') - return HttpResponseRedirect(reverse('hosting:payment') + '#payment_error') - - charge = charge_response.get('response_object') - - # Create OpenNebulaManager - manager = OpenNebulaManager(email=owner.email, - password=owner.password) - # Get user ssh key - if not UserHostingKey.objects.filter(user=self.request.user).exists(): - context.update({ - 'sshError': 'error', - 'form': form - }) - return render(request, self.template_name, context) - # For now just get first one - user_key = UserHostingKey.objects.filter( - user=self.request.user).first() - - # Create a vm using logged user - vm_id = manager.create_vm( - template_id=vm_template_id, - # XXX: Confi - specs=specs, - ssh_key=user_key.public_key, - ) - - # 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 payment - order.set_stripe_charge(charge) - - # If the Stripe payment was successed, set order status approved - order.set_approved() - - vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data - - # Send notification to ungleich as soon as VM has been booked - context = { - 'vm': vm, - 'order': order, - 'base_url': "{0}://{1}".format(request.scheme, request.get_host()) - - } - email_data = { - 'subject': 'New VM request', - 'to': request.user.email, - 'context': context, - 'template_name': 'new_booked_vm', - 'template_path': 'hosting/emails/' - } - email = BaseEmail(**email_data) - email.send() - - return HttpResponseRedirect( - "{url}?{query_params}".format(url=reverse('hosting:orders', kwargs={'pk': order.id}), - query_params='page=payment')) + request.session['billing_address_data'] = billing_address_data + request.session['billing_address'] = billing_address.id + request.session['token'] = token + request.session['customer'] = customer.id + return HttpResponseRedirect(reverse('hosting:orders')) else: return self.form_invalid(form) From df23862ea16ceabc71bd4eb7706d39bbe4f2b561 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 6 Sep 2017 23:49:38 +0200 Subject: [PATCH 02/32] Added per month text for price --- hosting/templates/hosting/order_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 989d7419..d29d00d9 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -109,7 +109,7 @@

{% trans "Disk space"%} {{vm.disk_size}} GB


-

{% trans "Total"%}

{{vm.price}} CHF

+

{% trans "Total"%}

{{vm.price}} CHF /{% trans "Month" %}

{% endif %}
From 67ab4ef0a1d32e705c5a83e74f235651686190ca Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 6 Sep 2017 23:50:19 +0200 Subject: [PATCH 03/32] hosting: Added order-confirmation url --- hosting/urls.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hosting/urls.py b/hosting/urls.py index e6b6fee3..039331bd 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -20,9 +20,13 @@ urlpatterns = [ url(r'pricing/?$', HostingPricingView.as_view(), name='pricing'), url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), - url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), + url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(), + name='order-confirmation'), + url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), + name='orders'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'), - url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'), + url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), + name='bills'), url(r'cancel_order/(?P\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(), @@ -39,13 +43,16 @@ urlpatterns = [ name='delete_ssh_key'), url(r'create_ssh_key/?$', SSHKeyCreateView.as_view(), name='create_ssh_key'), - url(r'^notifications/$', NotificationsView.as_view(), name='notifications'), + url(r'^notifications/$', NotificationsView.as_view(), + name='notifications'), url(r'^notifications/(?P\d+)/?$', MarkAsReadNotificationView.as_view(), name='read_notification'), url(r'login/?$', LoginView.as_view(), name='login'), url(r'signup/?$', SignupView.as_view(), name='signup'), - url(r'signup-validate/?$', SignupValidateView.as_view(), name='signup-validate'), - url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'), + url(r'signup-validate/?$', SignupValidateView.as_view(), + name='signup-validate'), + url(r'reset-password/?$', PasswordResetView.as_view(), + name='reset_password'), url(r'reset-password-confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', PasswordResetConfirmView.as_view(), name='reset_password_confirm'), url(r'^logout/?$', auth_views.logout, From 0fc0b0630cc184577a1985d6b9feedf9cfc4985b Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Wed, 6 Sep 2017 23:52:02 +0200 Subject: [PATCH 04/32] hosting: Updated OrdersHostingDetailView for stripe charging in this view --- hosting/views.py | 109 +++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 42 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 67cf8c28..36744724 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -7,7 +7,8 @@ from django.shortcuts import render from django.http import Http404 from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin -from django.views.generic import View, CreateView, FormView, ListView, DetailView, \ +from django.views.generic import View, CreateView, FormView, ListView, \ + DetailView, \ DeleteView, TemplateView, UpdateView from django.http import HttpResponseRedirect from django.contrib import messages @@ -24,11 +25,14 @@ from django.utils.safestring import mark_safe from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils -from utils.forms import BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm -from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin +from utils.forms import BillingAddressForm, PasswordResetRequestForm, \ + UserBillingAddressForm +from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \ + LoginViewMixin from utils.mailer import BaseEmail from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey -from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm, generate_ssh_key_name +from .forms import HostingUserSignupForm, HostingUserLoginForm, \ + UserHostingKeyForm, generate_ssh_key_name from .mixins import ProcessVMSelectionMixin from opennebula_api.models import OpenNebulaManager @@ -296,7 +300,8 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin): form = self.form_class(request.POST) - if user is not None and default_token_generator.check_token(user, token): + if user is not None and default_token_generator.check_token(user, + token): if form.is_valid(): new_password = form.cleaned_data['new_password2'] user.set_password(new_password) @@ -397,7 +402,8 @@ class SSHKeyListView(LoginRequiredMixin, ListView): def render_to_response(self, context, **response_kwargs): if not self.queryset: return HttpResponseRedirect(reverse('hosting:choice_ssh_keys')) - return super(SSHKeyListView, self).render_to_response(context, **response_kwargs) + return super(SSHKeyListView, self).render_to_response(context, + **response_kwargs) class SSHKeyChoiceView(LoginRequiredMixin, View): @@ -548,23 +554,10 @@ class PaymentVMView(LoginRequiredMixin, FormView): def post(self, request, *args, **kwargs): form = self.get_form() if form.is_valid(): - # Get billing address data billing_address_data = form.cleaned_data - - context = self.get_context_data() - - template = request.session.get('template') - specs = request.session.get('specs') - - vm_template_id = template.get('id', 1) - - final_price = specs.get('price') - token = form.cleaned_data.get('token') - owner = self.request.user - # Get or create stripe customer customer = StripeCustomer.get_or_create(email=owner.email, token=token) @@ -579,12 +572,13 @@ class PaymentVMView(LoginRequiredMixin, FormView): request.session['billing_address'] = billing_address.id request.session['token'] = token request.session['customer'] = customer.id - return HttpResponseRedirect(reverse('hosting:orders')) + return HttpResponseRedirect(reverse('hosting:order-confirmation')) else: return self.form_invalid(form) -class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailView): +class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, + DetailView): template_name = "hosting/order_detail.html" context_object_name = "order" login_url = reverse_lazy('hosting:login') @@ -596,26 +590,52 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai context = super(DetailView, self).get_context_data(**kwargs) obj = self.get_object() owner = self.request.user - manager = OpenNebulaManager(email=owner.email, - password=owner.password) + if 'specs' not in self.request.session or 'user' not in self.request.session: + return HttpResponseRedirect( + reverse('hosting:create_virtual_machine')) + if 'token' not in self.request.session: + return HttpResponseRedirect(reverse('hosting:payment')) + stripe_customer_id = self.request.session.get('customer') + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + stripe_utils = StripeUtils() + card_details = stripe_utils.get_card_details(customer.stripe_id, + self.request.session.get( + 'token')) + if not card_details.get('response_object'): + msg = card_details.get('error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + return HttpResponseRedirect( + reverse('hosting:payment') + '#payment_error') + if self.request.GET.get('page', '') == 'payment': context['page_header_text'] = _('Confirm Order') else: context['page_header_text'] = _('Invoice') - try: - vm = manager.get_vm(obj.vm_id) - context['vm'] = VirtualMachineSerializer(vm).data - except WrongIdError: - messages.error(self.request, - 'The VM you are looking for is unavailable at the moment. \ - Please contact Data Center Light support.' - ) - self.kwargs['error'] = 'WrongIdError' - context['error'] = 'WrongIdError' - except ConnectionRefusedError: - messages.error(self.request, - 'In order to create a VM, you need to create/upload your SSH KEY first.' - ) + + if obj.vm_id: + try: + manager = OpenNebulaManager(email=owner.email, + password=owner.password) + vm = manager.get_vm(obj.vm_id) + context['vm'] = VirtualMachineSerializer(vm).data + except WrongIdError: + messages.error(self.request, + 'The VM you are looking for is unavailable at the moment. \ + Please contact Data Center Light support.' + ) + self.kwargs['error'] = 'WrongIdError' + context['error'] = 'WrongIdError' + except ConnectionRefusedError: + messages.error(self.request, + 'In order to create a VM, you need to create/upload your SSH KEY first.' + ) + else: + context['site_url'] = reverse('hosting:create_virtual_machine') + context['cc_last4'] = card_details.get('response_object').get( + 'last4') + context['cc_brand'] = card_details.get('response_object').get( + 'cc_brand') return context @@ -692,7 +712,8 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): configuration_options = HostingPlan.get_serialized_configs() context = { - 'templates': VirtualMachineTemplateSerializer(templates, many=True).data, + 'templates': VirtualMachineTemplateSerializer(templates, + many=True).data, 'configuration_options': configuration_options, } except: @@ -763,7 +784,8 @@ class VirtualMachineView(LoginRequiredMixin, View): serializer = VirtualMachineSerializer(vm) context = { 'virtual_machine': serializer.data, - 'order': HostingOrder.objects.get(vm_id=serializer.data['vm_id']) + 'order': HostingOrder.objects.get( + vm_id=serializer.data['vm_id']) } except: pass @@ -794,7 +816,8 @@ class VirtualMachineView(LoginRequiredMixin, View): context = { 'vm': vm, - 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) + 'base_url': "{0}://{1}".format(self.request.scheme, + self.request.get_host()) } email_data = { 'subject': 'Virtual machine plan canceled', @@ -814,7 +837,8 @@ class VirtualMachineView(LoginRequiredMixin, View): return HttpResponseRedirect(self.get_success_url()) -class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, ListView): +class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, + ListView): template_name = "hosting/bills.html" login_url = reverse_lazy('hosting:login') permission_required = ['view_hostingview'] @@ -824,7 +848,8 @@ class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin, ListView) ordering = '-id' -class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailView): +class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, + DetailView): template_name = "hosting/bill_detail.html" login_url = reverse_lazy('hosting:login') permission_required = ['view_hostingview'] From c8baf3d4c13d91b89c9cc5afe2d37c38bd9a1bcb Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sat, 9 Sep 2017 12:06:29 +0200 Subject: [PATCH 05/32] Implemented post method for hosting OrderDetailView --- hosting/views.py | 116 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 28 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 36744724..cc761d33 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,44 +1,41 @@ import uuid -from django.core.files.base import ContentFile - -from oca.pool import WrongNameError, WrongIdError -from django.shortcuts import render -from django.http import Http404 -from django.core.urlresolvers import reverse_lazy, reverse +from django.conf import settings +from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.tokens import default_token_generator +from django.core.files.base import ContentFile +from django.core.urlresolvers import reverse_lazy, reverse +from django.http import Http404 +from django.http import HttpResponseRedirect +from django.shortcuts import redirect +from django.shortcuts import render +from django.utils.http import urlsafe_base64_decode +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ from django.views.generic import View, CreateView, FormView, ListView, \ DetailView, \ DeleteView, TemplateView, UpdateView -from django.http import HttpResponseRedirect -from django.contrib import messages -from django.conf import settings -from django.shortcuts import redirect -from django.utils.http import urlsafe_base64_decode -from django.contrib.auth.tokens import default_token_generator - from guardian.mixins import PermissionRequiredMixin -from stored_messages.settings import stored_messages_settings -from stored_messages.models import Message +from oca.pool import WrongNameError, WrongIdError from stored_messages.api import mark_read -from django.utils.safestring import mark_safe +from stored_messages.models import Message +from stored_messages.settings import stored_messages_settings +from datacenterlight.tasks import create_vm_task from membership.models import CustomUser, StripeCustomer -from utils.stripe_utils import StripeUtils -from utils.forms import BillingAddressForm, PasswordResetRequestForm, \ - UserBillingAddressForm -from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \ - LoginViewMixin -from utils.mailer import BaseEmail -from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey -from .forms import HostingUserSignupForm, HostingUserLoginForm, \ - UserHostingKeyForm, generate_ssh_key_name -from .mixins import ProcessVMSelectionMixin - from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer, \ VirtualMachineTemplateSerializer -from django.utils.translation import ugettext_lazy as _ +from utils.forms import BillingAddressForm, PasswordResetRequestForm +from utils.mailer import BaseEmail +from utils.stripe_utils import StripeUtils +from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, \ + LoginViewMixin +from .forms import HostingUserSignupForm, HostingUserLoginForm, \ + UserHostingKeyForm, generate_ssh_key_name +from .mixins import ProcessVMSelectionMixin +from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a backend \ connection error. please try again in a few minutes." @@ -638,6 +635,69 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, 'cc_brand') return context + def post(self, request): + template = request.session.get('template') + specs = request.session.get('specs') + stripe_customer_id = request.session.get('customer') + customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() + billing_address_data = request.session.get('billing_address_data') + billing_address_id = request.session.get('billing_address') + vm_template_id = template.get('id', 1) + + # Make stripe charge to a customer + stripe_utils = StripeUtils() + card_details = stripe_utils.get_card_details(customer.stripe_id, + request.session.get( + 'token')) + if not card_details.get('response_object'): + msg = card_details.get('error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + return HttpResponseRedirect( + reverse('datacenterlight:payment') + '#payment_error') + card_details_dict = card_details.get('response_object') + cpu = specs.get('cpu') + memory = specs.get('memory') + disk_size = specs.get('disk_size') + amount_to_be_charged = (cpu * 5) + (memory * 2) + (disk_size * 0.6) + plan_name = "{cpu} Cores, {memory} GB RAM, {disk_size} GB SSD".format( + cpu=cpu, + memory=memory, + disk_size=disk_size) + + stripe_plan_id = StripeUtils.get_stripe_plan_id(cpu=cpu, + ram=memory, + ssd=disk_size, + version=1, + app='dcl') + stripe_plan = stripe_utils.get_or_create_stripe_plan( + amount=amount_to_be_charged, + name=plan_name, + stripe_plan_id=stripe_plan_id) + subscription_result = stripe_utils.subscribe_customer_to_plan( + customer.stripe_id, + [{"plan": stripe_plan.get( + 'response_object').stripe_plan_id}]) + stripe_subscription_obj = subscription_result.get('response_object') + # Check if the subscription was approved and is active + if stripe_subscription_obj is None or \ + stripe_subscription_obj.status != 'active': + msg = subscription_result.get('error') + messages.add_message(self.request, messages.ERROR, msg, + extra_tags='failed_payment') + return HttpResponseRedirect( + reverse('hosting:payment') + '#payment_error') + user = { + 'name': self.request.user.name, + 'email': self.request.user.email + } + create_vm_task.delay(vm_template_id, user, specs, template, + stripe_customer_id, billing_address_data, + billing_address_id, + stripe_subscription_obj, card_details_dict) + request.session['order_confirmation'] = True + return HttpResponseRedirect(reverse('hosting:my-virtual-machines')) + class OrdersHostingListView(LoginRequiredMixin, ListView): template_name = "hosting/orders.html" From e135bc9e8cef3c7ded3f36f99dd75e2c5049bbe6 Mon Sep 17 00:00:00 2001 From: "M.Ravi" Date: Sat, 9 Sep 2017 13:27:17 +0200 Subject: [PATCH 06/32] hosting: Overriden get_object method in OrderHostingDetail and cleared session on completion of creation of VM --- hosting/templates/hosting/order_detail.html | 2 ++ hosting/views.py | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index d29d00d9..581d5b07 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -1,6 +1,8 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} {% load i18n %} +{% load custom_tags %} + {% block content %}
diff --git a/hosting/views.py b/hosting/views.py index cc761d33..ea699d26 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -582,12 +582,16 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, permission_required = ['view_hostingorder'] model = HostingOrder + def get_object(self): + return HostingOrder.objects.filter( + pk=self.kwargs.get('pk')) if self.kwargs.get('pk') else None + def get_context_data(self, **kwargs): # Get context context = super(DetailView, self).get_context_data(**kwargs) obj = self.get_object() owner = self.request.user - if 'specs' not in self.request.session or 'user' not in self.request.session: + if 'specs' not in self.request.session: return HttpResponseRedirect( reverse('hosting:create_virtual_machine')) if 'token' not in self.request.session: @@ -610,7 +614,7 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, else: context['page_header_text'] = _('Invoice') - if obj.vm_id: + if obj is not None: try: manager = OpenNebulaManager(email=owner.email, password=owner.password) @@ -695,8 +699,17 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, stripe_customer_id, billing_address_data, billing_address_id, stripe_subscription_obj, card_details_dict) - request.session['order_confirmation'] = True - return HttpResponseRedirect(reverse('hosting:my-virtual-machines')) + + for session_var in ['specs', 'template', 'billing_address', + 'billing_address_data', + 'token', 'customer']: + if session_var in request.session: + del request.session[session_var] + messages.success(self.request, + "{first_line}".format( + first_line=_( + "Thank you for order! Our team will contact you via email"))) + return HttpResponseRedirect(reverse('hosting:virtual_machines')) class OrdersHostingListView(LoginRequiredMixin, ListView): From 6d826fdfd2d448ea24038d0ecf9ad95f8af79351 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 9 Sep 2017 18:19:09 +0530 Subject: [PATCH 07/32] Added logged in user as a parameter to create VM --- datacenterlight/tasks.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 1e3e1caa..78a8e87d 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,15 +1,17 @@ -from dynamicweb.celery import app +from datetime import datetime + +from celery.exceptions import MaxRetriesExceededError from celery.utils.log import get_task_logger from django.conf import settings +from django.core.mail import EmailMessage + +from dynamicweb.celery import app +from hosting.models import HostingOrder, HostingBill +from membership.models import StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer -from hosting.models import HostingOrder, HostingBill from utils.forms import UserBillingAddressForm -from datetime import datetime -from membership.models import StripeCustomer -from django.core.mail import EmailMessage from utils.models import BillingAddress -from celery.exceptions import MaxRetriesExceededError logger = get_task_logger(__name__) @@ -52,8 +54,15 @@ def create_vm_task(self, vm_template_id, user, specs, template, id=billing_address_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() # Create OpenNebulaManager - manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, - password=settings.OPENNEBULA_PASSWORD) + if self.request.user is None: + manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, + password=settings.OPENNEBULA_PASSWORD) + logger.debug("Using OpenNebula admin user to create VM") + else: + manager = OpenNebulaManager(email=self.request.user.email, + password=self.request.user.password) + logger.debug("Using user {user} to create VM".format( + user=self.request.user.email)) # Create a vm using oneadmin, also specify the name vm_id = manager.create_vm( From 085786907c16208e0ff80ede9d2a8aa31a0f21d2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 9 Sep 2017 18:19:33 +0530 Subject: [PATCH 08/32] Removed a comment --- datacenterlight/tasks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 78a8e87d..2864a3a6 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -64,7 +64,6 @@ def create_vm_task(self, vm_template_id, user, specs, template, logger.debug("Using user {user} to create VM".format( user=self.request.user.email)) - # Create a vm using oneadmin, also specify the name vm_id = manager.create_vm( template_id=vm_template_id, specs=specs, From d96d2907bc85b1d93ea369b559195a282a6c6c84 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 9 Sep 2017 20:46:43 +0530 Subject: [PATCH 09/32] Passing pass as parameter to celery task --- datacenterlight/tasks.py | 12 ++++++------ hosting/views.py | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 2864a3a6..10208735 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -54,15 +54,15 @@ def create_vm_task(self, vm_template_id, user, specs, template, id=billing_address_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() # Create OpenNebulaManager - if self.request.user is None: + if 'pass' in user: + manager = OpenNebulaManager(email=user.get('email'), + password=user.get('pass')) + logger.debug("Using user {user} to create VM".format( + user=user.get('email'))) + else: manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, password=settings.OPENNEBULA_PASSWORD) logger.debug("Using OpenNebula admin user to create VM") - else: - manager = OpenNebulaManager(email=self.request.user.email, - password=self.request.user.password) - logger.debug("Using user {user} to create VM".format( - user=self.request.user.email)) vm_id = manager.create_vm( template_id=vm_template_id, diff --git a/hosting/views.py b/hosting/views.py index ea699d26..ef7b1956 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -574,7 +574,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): return self.form_invalid(form) -class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, +class OrdersHostingDetailView(LoginRequiredMixin, DetailView): template_name = "hosting/order_detail.html" context_object_name = "order" @@ -693,7 +693,8 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, reverse('hosting:payment') + '#payment_error') user = { 'name': self.request.user.name, - 'email': self.request.user.email + 'email': self.request.user.email, + 'pass': self.request.user.password } create_vm_task.delay(vm_template_id, user, specs, template, stripe_customer_id, billing_address_data, From b1589e9c6831d7f769887a5400cbbc6295b0d17f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 9 Sep 2017 21:41:20 +0530 Subject: [PATCH 10/32] Reorganized imports hosting/views.py --- hosting/views.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index d6ce90b2..9e913598 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -8,15 +8,8 @@ from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy, reverse from oca.pool import WrongNameError, WrongIdError -from django.shortcuts import render from django.http import Http404 -from django.core.urlresolvers import reverse_lazy, reverse -from django.contrib.auth.mixins import LoginRequiredMixin -from django.views.generic import View, CreateView, FormView, ListView, DetailView, \ - DeleteView, TemplateView, UpdateView from django.http import HttpResponseRedirect -from django.contrib import messages -from django.conf import settings from django.shortcuts import redirect from django.shortcuts import render from django.utils.http import urlsafe_base64_decode @@ -25,27 +18,14 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic import View, CreateView, FormView, ListView, \ DetailView, \ DeleteView, TemplateView, UpdateView -from django.contrib.auth.tokens import default_token_generator from guardian.mixins import PermissionRequiredMixin -from oca.pool import WrongNameError, WrongIdError from stored_messages.api import mark_read from stored_messages.models import Message from stored_messages.settings import stored_messages_settings -from stored_messages.models import Message -from stored_messages.api import mark_read -from django.utils.safestring import mark_safe from datacenterlight.tasks import create_vm_task from membership.models import CustomUser, StripeCustomer -from utils.stripe_utils import StripeUtils -from utils.forms import BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm -from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin -from utils.mailer import BaseEmail -from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey -from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm, generate_ssh_key_name -from .mixins import ProcessVMSelectionMixin - from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer, \ VirtualMachineTemplateSerializer From 1836d315cddd1f995b49459fdf9326cd32271ce7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Sep 2017 12:22:32 +0530 Subject: [PATCH 11/32] Added query param to distinguish confirm order from invoice --- hosting/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 9e913598..e8759702 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -6,8 +6,6 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.tokens import default_token_generator from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy, reverse - -from oca.pool import WrongNameError, WrongIdError from django.http import Http404 from django.http import HttpResponseRedirect from django.shortcuts import redirect @@ -18,8 +16,8 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic import View, CreateView, FormView, ListView, \ DetailView, \ DeleteView, TemplateView, UpdateView - from guardian.mixins import PermissionRequiredMixin +from oca.pool import WrongNameError, WrongIdError from stored_messages.api import mark_read from stored_messages.models import Message from stored_messages.settings import stored_messages_settings @@ -627,7 +625,9 @@ class PaymentVMView(LoginRequiredMixin, FormView): request.session['billing_address'] = billing_address.id request.session['token'] = token request.session['customer'] = customer.id - return HttpResponseRedirect(reverse('hosting:order-confirmation')) + return HttpResponseRedirect("{url}?{query_params}".format( + url=reverse('hosting:order-confirmation'), + query_params='page=payment')) else: return self.form_invalid(form) From 56a8ad2edf7f3339d94ff09adbdb9e89ab894a2d Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Sep 2017 14:52:06 +0530 Subject: [PATCH 12/32] Changed VM name format for create vm when user is already logged in --- datacenterlight/tasks.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 10208735..1d9af626 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -59,19 +59,21 @@ def create_vm_task(self, vm_template_id, user, specs, template, password=user.get('pass')) logger.debug("Using user {user} to create VM".format( user=user.get('email'))) + vm_name = None else: manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, password=settings.OPENNEBULA_PASSWORD) logger.debug("Using OpenNebula admin user to create VM") + vm_name = "{email}-{template_name}-{date}".format( + email=user.get('email'), + template_name=template.get('name'), + date=int(datetime.now().strftime("%s"))) vm_id = manager.create_vm( template_id=vm_template_id, specs=specs, ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY, - vm_name="{email}-{template_name}-{date}".format( - email=user.get('email'), - template_name=template.get('name'), - date=int(datetime.now().strftime("%s"))) + vm_name=vm_name ) if vm_id is None: From 38a8997e29d7e9789a2fe17264c8d456d24b9f3d Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 10 Sep 2017 15:26:29 +0530 Subject: [PATCH 13/32] Refactored code --- datacenterlight/tasks.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 1d9af626..943e4139 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -53,22 +53,24 @@ def create_vm_task(self, vm_template_id, user, specs, template, billing_address = BillingAddress.objects.filter( id=billing_address_id).first() customer = StripeCustomer.objects.filter(id=stripe_customer_id).first() - # Create OpenNebulaManager + if 'pass' in user: - manager = OpenNebulaManager(email=user.get('email'), - password=user.get('pass')) - logger.debug("Using user {user} to create VM".format( - user=user.get('email'))) + on_user = user.get('email') + on_pass = user.get('pass') + logger.debug("Using user {user} to create VM".format(user=on_user)) vm_name = None else: - manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, - password=settings.OPENNEBULA_PASSWORD) + on_user = settings.OPENNEBULA_USERNAME + on_pass = settings.OPENNEBULA_PASSWORD logger.debug("Using OpenNebula admin user to create VM") vm_name = "{email}-{template_name}-{date}".format( email=user.get('email'), template_name=template.get('name'), date=int(datetime.now().strftime("%s"))) + # Create OpenNebulaManager + manager = OpenNebulaManager(email=on_user, password=on_pass) + vm_id = manager.create_vm( template_id=vm_template_id, specs=specs, From 8c3544c0a4476d33149fee77c5cbe447cbd53b0c Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 13 Sep 2017 00:01:10 +0530 Subject: [PATCH 14/32] Formatted code and added text - ending in --- hosting/templates/hosting/order_detail.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 581d5b07..c5c2665e 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -75,11 +75,11 @@
{% trans "Payment Method:"%}
{% if order %} - {{order.cc_brand}} {% trans "ending in" %} **** {{order.last4}}
- {{user.email}} + {{order.cc_brand}} {% trans "ending in" %} **** {{order.last4}}
+ {{user.email}} {% else %} - {{cc_brand}} {% trans "ending" %} **** {{cc_last4}}
- {{request.session.user.email}} + {{cc_brand}} {% trans "ending in" %} **** {{cc_last4}}
+ {{request.session.user.email}} {% endif %}
From 0561688f846b930668766777ea71c0a412d004e1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 15 Sep 2017 12:43:48 +0530 Subject: [PATCH 15/32] Add ssh keys on creation of new VM --- datacenterlight/tasks.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 943e4139..b9efb328 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -10,6 +10,7 @@ from hosting.models import HostingOrder, HostingBill from membership.models import StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer +from utils.hosting_utils import get_all_public_keys from utils.forms import UserBillingAddressForm from utils.models import BillingAddress @@ -134,6 +135,24 @@ def create_vm_task(self, vm_template_id, user, specs, template, } email = EmailMessage(**email_data) email.send() + + if 'pass' in user: + # 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) + if new_host is not None: + public_keys = get_all_public_keys(owner) + keys = [{'value': key, 'state': True} for key in public_keys] + if len(keys) > 0: + logger.debug( + "Calling configure on {host} for {num_keys} keys".format( + host=new_host, 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) except Exception as e: logger.error(str(e)) try: From 589a4fcaa9b1e722e756b283c9d84113d229a8bb Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 15 Sep 2017 12:45:11 +0530 Subject: [PATCH 16/32] Added create vm modal --- hosting/templates/hosting/order_detail.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index c5c2665e..7b9be300 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -139,6 +139,26 @@ {% endif %} + + +