Attempt to merge master into task/3747/multiple_cards_support
This commit is contained in:
commit
cf00ff6bd8
269 changed files with 8500 additions and 29554 deletions
330
hosting/views.py
330
hosting/views.py
|
|
@ -19,6 +19,8 @@ from django.utils.http import urlsafe_base64_decode
|
|||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import get_language, ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.generic import (
|
||||
View, CreateView, FormView, ListView, DetailView, DeleteView,
|
||||
TemplateView, UpdateView
|
||||
|
|
@ -29,29 +31,34 @@ from stored_messages.api import mark_read
|
|||
from stored_messages.models import Message
|
||||
from stored_messages.settings import stored_messages_settings
|
||||
|
||||
from datacenterlight.models import VMTemplate
|
||||
from datacenterlight.models import VMTemplate, VMPricing
|
||||
from datacenterlight.tasks import create_vm_task
|
||||
from datacenterlight.utils import get_cms_integration
|
||||
from membership.models import CustomUser, StripeCustomer
|
||||
from opennebula_api.models import OpenNebulaManager
|
||||
from opennebula_api.serializers import VirtualMachineSerializer, \
|
||||
VirtualMachineTemplateSerializer, VMTemplateSerializer
|
||||
from opennebula_api.serializers import (
|
||||
VirtualMachineSerializer, VirtualMachineTemplateSerializer,
|
||||
VMTemplateSerializer
|
||||
)
|
||||
from utils.forms import (
|
||||
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
||||
ResendActivationEmailForm
|
||||
)
|
||||
from utils.hosting_utils import get_vm_price, HostingUtils
|
||||
from utils.hosting_utils import get_vm_price_with_vat, HostingUtils
|
||||
from utils.mailer import BaseEmail
|
||||
from utils.stripe_utils import StripeUtils
|
||||
from utils.tasks import send_plain_email_task
|
||||
from utils.views import (
|
||||
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
|
||||
ResendActivationLinkViewMixin
|
||||
)
|
||||
from .forms import HostingUserSignupForm, HostingUserLoginForm, \
|
||||
UserHostingKeyForm, generate_ssh_key_name
|
||||
from .mixins import ProcessVMSelectionMixin
|
||||
from .forms import (
|
||||
HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm,
|
||||
generate_ssh_key_name
|
||||
)
|
||||
from .mixins import ProcessVMSelectionMixin, HostingContextMixin
|
||||
from .models import (
|
||||
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail,
|
||||
UserCardDetail
|
||||
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -59,6 +66,7 @@ logger = logging.getLogger(__name__)
|
|||
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
||||
backend connection error. please try again in a few \
|
||||
minutes."
|
||||
decorators = [never_cache]
|
||||
|
||||
|
||||
class DashboardView(LoginRequiredMixin, View):
|
||||
|
|
@ -69,6 +77,7 @@ class DashboardView(LoginRequiredMixin, View):
|
|||
context = {}
|
||||
return context
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data()
|
||||
return render(request, self.template_name, context)
|
||||
|
|
@ -200,23 +209,23 @@ class IndexView(View):
|
|||
}
|
||||
return context
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = self.get_context_data()
|
||||
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
|
||||
class LoginView(LoginViewMixin):
|
||||
class LoginView(HostingContextMixin, LoginViewMixin):
|
||||
template_name = "hosting/login.html"
|
||||
form_class = HostingUserLoginForm
|
||||
success_url = reverse_lazy('hosting:dashboard')
|
||||
|
||||
|
||||
class SignupView(CreateView):
|
||||
class SignupView(HostingContextMixin, CreateView):
|
||||
template_name = 'hosting/signup.html'
|
||||
form_class = HostingUserSignupForm
|
||||
model = CustomUser
|
||||
success_url = reverse_lazy('hosting:ssh_keys')
|
||||
success_url = reverse_lazy('hosting:dashboard')
|
||||
|
||||
def get_success_url(self):
|
||||
next_url = self.request.session.get(
|
||||
|
|
@ -234,8 +243,14 @@ class SignupView(CreateView):
|
|||
|
||||
return HttpResponseRedirect(reverse_lazy('hosting:signup-validate'))
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
if self.request.user.is_authenticated():
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
return super(SignupView, self).get(request, *args, **kwargs)
|
||||
|
||||
class SignupValidateView(TemplateView):
|
||||
|
||||
class SignupValidateView(HostingContextMixin, TemplateView):
|
||||
template_name = "hosting/signup_validate.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
|
@ -259,7 +274,7 @@ class SignupValidateView(TemplateView):
|
|||
return context
|
||||
|
||||
|
||||
class SignupValidatedView(SignupValidateView):
|
||||
class SignupValidatedView(SignupValidateView, HostingContextMixin):
|
||||
template_name = "hosting/signup_validate.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
|
@ -294,7 +309,7 @@ class SignupValidatedView(SignupValidateView):
|
|||
email.send()
|
||||
else:
|
||||
home_url = '<a href="' + \
|
||||
reverse('datacenterlight:index') + \
|
||||
reverse('datacenterlight:cms_index') + \
|
||||
'">Data Center Light</a>'
|
||||
message = '{sorry_message} <br />{go_back_to} {hurl}'.format(
|
||||
sorry_message=_("Sorry. Your request is invalid."),
|
||||
|
|
@ -305,8 +320,15 @@ class SignupValidatedView(SignupValidateView):
|
|||
context['section_title'] = section_title
|
||||
return context
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
if self.request.user.is_authenticated():
|
||||
return HttpResponseRedirect(reverse_lazy('hosting:dashboard'))
|
||||
return super(SignupValidatedView, self).get(request, *args, **kwargs)
|
||||
|
||||
class ResendActivationEmailView(ResendActivationLinkViewMixin):
|
||||
|
||||
class ResendActivationEmailView(HostingContextMixin,
|
||||
ResendActivationLinkViewMixin):
|
||||
template_name = 'hosting/resend_activation_link.html'
|
||||
form_class = ResendActivationEmailForm
|
||||
success_url = reverse_lazy('hosting:login')
|
||||
|
|
@ -314,7 +336,7 @@ class ResendActivationEmailView(ResendActivationLinkViewMixin):
|
|||
email_template_name = 'user_activation'
|
||||
|
||||
|
||||
class PasswordResetView(PasswordResetViewMixin):
|
||||
class PasswordResetView(HostingContextMixin, PasswordResetViewMixin):
|
||||
site = 'dcl'
|
||||
template_name = 'hosting/reset_password.html'
|
||||
form_class = PasswordResetRequestForm
|
||||
|
|
@ -322,7 +344,8 @@ class PasswordResetView(PasswordResetViewMixin):
|
|||
template_email_path = 'hosting/emails/'
|
||||
|
||||
|
||||
class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
|
||||
class PasswordResetConfirmView(HostingContextMixin,
|
||||
PasswordResetConfirmViewMixin):
|
||||
template_name = 'hosting/confirm_reset_password.html'
|
||||
success_url = reverse_lazy('hosting:login')
|
||||
|
||||
|
|
@ -439,6 +462,7 @@ class SSHKeyListView(LoginRequiredMixin, ListView):
|
|||
self.queryset = UserHostingKey.objects.filter(user=user)
|
||||
return super(SSHKeyListView, self).get_queryset()
|
||||
|
||||
@method_decorator(decorators)
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
if not self.queryset:
|
||||
return HttpResponseRedirect(reverse('hosting:choice_ssh_keys'))
|
||||
|
|
@ -450,10 +474,12 @@ class SSHKeyChoiceView(LoginRequiredMixin, View):
|
|||
template_name = "hosting/choice_ssh_keys.html"
|
||||
login_url = reverse_lazy('hosting:login')
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = {}
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
@method_decorator(decorators)
|
||||
def post(self, request, *args, **kwargs):
|
||||
name = generate_ssh_key_name()
|
||||
private_key, public_key = UserHostingKey.generate_keys()
|
||||
|
|
@ -472,6 +498,7 @@ class SSHKeyChoiceView(LoginRequiredMixin, View):
|
|||
return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar')
|
||||
|
||||
|
||||
@method_decorator(decorators, name='dispatch')
|
||||
class SSHKeyCreateView(LoginRequiredMixin, FormView):
|
||||
form_class = UserHostingKeyForm
|
||||
model = UserHostingKey
|
||||
|
|
@ -487,7 +514,7 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView):
|
|||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
if 'dcl-generated-key-' in form.instance.name:
|
||||
if settings.DCL_SSH_KEY_NAME_PREFIX in form.instance.name:
|
||||
content = ContentFile(form.cleaned_data.get('private_key'))
|
||||
filename = form.cleaned_data.get(
|
||||
'name') + '_' + str(uuid.uuid4())[:8] + '_private.pem'
|
||||
|
|
@ -534,6 +561,7 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView):
|
|||
return self.form_invalid(form)
|
||||
|
||||
|
||||
@method_decorator(decorators, name='dispatch')
|
||||
class SettingsView(LoginRequiredMixin, FormView):
|
||||
template_name = "hosting/settings.html"
|
||||
login_url = reverse_lazy('hosting:login')
|
||||
|
|
@ -562,6 +590,7 @@ class SettingsView(LoginRequiredMixin, FormView):
|
|||
'cards_list': cards_list,
|
||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
|
@ -666,6 +695,7 @@ class SettingsView(LoginRequiredMixin, FormView):
|
|||
messages.add_message(request, messages.SUCCESS, msg)
|
||||
return self.render_to_response(self.get_context_data())
|
||||
else:
|
||||
billing_address_data = form.cleaned_data
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
|
|
@ -705,8 +735,10 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
|||
'cards_list': cards_list,
|
||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||
})
|
||||
|
||||
return context
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'next' in request.session:
|
||||
del request.session['next']
|
||||
|
|
@ -716,6 +748,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
|||
)
|
||||
return self.render_to_response(self.get_context_data())
|
||||
|
||||
@method_decorator(decorators)
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
|
|
@ -772,27 +805,56 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
|||
return self.form_invalid(form)
|
||||
|
||||
|
||||
class OrdersHostingDetailView(LoginRequiredMixin,
|
||||
DetailView):
|
||||
class OrdersHostingDetailView(LoginRequiredMixin, DetailView):
|
||||
template_name = "hosting/order_detail.html"
|
||||
context_object_name = "order"
|
||||
login_url = reverse_lazy('hosting:login')
|
||||
permission_required = ['view_hostingorder']
|
||||
model = HostingOrder
|
||||
|
||||
def get_object(self):
|
||||
return HostingOrder.objects.get(
|
||||
pk=self.kwargs.get('pk')) if self.kwargs.get('pk') else None
|
||||
def get_object(self, queryset=None):
|
||||
order_id = self.kwargs.get('pk')
|
||||
try:
|
||||
hosting_order_obj = HostingOrder.objects.get(pk=order_id)
|
||||
logger.debug("Found HostingOrder for id {order_id}".format(
|
||||
order_id=order_id
|
||||
))
|
||||
except HostingOrder.DoesNotExist:
|
||||
logger.debug("HostingOrder not found for id {order_id}".format(
|
||||
order_id=order_id
|
||||
))
|
||||
hosting_order_obj = None
|
||||
return hosting_order_obj
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Get context
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
context = super(
|
||||
OrdersHostingDetailView, self
|
||||
).get_context_data(**kwargs)
|
||||
obj = self.get_object()
|
||||
owner = self.request.user
|
||||
stripe_api_cus_id = self.request.session.get('customer')
|
||||
stripe_utils = StripeUtils()
|
||||
card_details = stripe_utils.get_card_details(
|
||||
stripe_api_cus_id,
|
||||
self.request.session.get('token')
|
||||
)
|
||||
|
||||
if self.request.GET.get('page') == 'payment':
|
||||
context['page_header_text'] = _('Confirm Order')
|
||||
else:
|
||||
context['page_header_text'] = _('Invoice')
|
||||
if not self.request.user.has_perm(
|
||||
self.permission_required[0], obj
|
||||
):
|
||||
logger.debug(
|
||||
"User {user} does not have permission on HostingOrder "
|
||||
"{order_id}. Raising 404 error now.".format(
|
||||
user=self.request.user.email,
|
||||
order_id=obj.id if obj else 'None'
|
||||
)
|
||||
)
|
||||
raise Http404
|
||||
|
||||
if obj is not None:
|
||||
# invoice for previous order
|
||||
|
|
@ -801,11 +863,18 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
context['vm'] = vm_detail.__dict__
|
||||
context['vm']['name'] = '{}-{}'.format(
|
||||
context['vm']['configuration'], context['vm']['vm_id'])
|
||||
context['vm']['price'] = get_vm_price(
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=context['vm']['cores'],
|
||||
disk_size=context['vm']['disk_size'],
|
||||
memory=context['vm']['memory']
|
||||
ssd_size=context['vm']['disk_size'],
|
||||
memory=context['vm']['memory'],
|
||||
pricing_name=(obj.vm_pricing.name
|
||||
if obj.vm_pricing else 'default')
|
||||
)
|
||||
context['vm']['vat'] = vat
|
||||
context['vm']['price'] = price
|
||||
context['vm']['discount'] = discount
|
||||
context['vm']['vat_percent'] = vat_percent
|
||||
context['vm']['total_price'] = price + vat - discount['amount']
|
||||
context['subscription_end_date'] = vm_detail.end_date()
|
||||
except VMDetail.DoesNotExist:
|
||||
try:
|
||||
|
|
@ -814,6 +883,19 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
)
|
||||
vm = manager.get_vm(obj.vm_id)
|
||||
context['vm'] = VirtualMachineSerializer(vm).data
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=context['vm']['cores'],
|
||||
ssd_size=context['vm']['disk_size'],
|
||||
memory=context['vm']['memory'],
|
||||
pricing_name=(obj.vm_pricing.name
|
||||
if obj.vm_pricing else 'default')
|
||||
)
|
||||
context['vm']['vat'] = vat
|
||||
context['vm']['price'] = price
|
||||
context['vm']['discount'] = discount
|
||||
context['vm']['vat_percent'] = vat_percent
|
||||
context['vm']['total_price'] = price + \
|
||||
vat - discount['amount']
|
||||
except WrongIdError:
|
||||
messages.error(
|
||||
self.request,
|
||||
|
|
@ -828,6 +910,10 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
_('In order to create a VM, you need to create/upload '
|
||||
'your SSH KEY first.')
|
||||
)
|
||||
elif not card_details.get('response_object'):
|
||||
# new order, failed to get card details
|
||||
context['failed_payment'] = True
|
||||
context['card_details'] = card_details
|
||||
else:
|
||||
# new order, confirm payment
|
||||
if 'token' in self.request.session:
|
||||
|
|
@ -850,19 +936,29 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
context['vm'] = self.request.session.get('specs')
|
||||
return context
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not self.kwargs.get('pk'):
|
||||
if 'specs' not in self.request.session:
|
||||
return HttpResponseRedirect(
|
||||
reverse('hosting:create_virtual_machine')
|
||||
)
|
||||
if ('card_id' not in self.request.session and
|
||||
'token' not in self.request.session):
|
||||
if 'token' not in self.request.session:
|
||||
return HttpResponseRedirect(reverse('hosting:payment'))
|
||||
self.object = self.get_object()
|
||||
context = self.get_context_data(object=self.object)
|
||||
if 'failed_payment' in context:
|
||||
msg = context['card_details'].get('error')
|
||||
messages.add_message(
|
||||
self.request, messages.ERROR, msg,
|
||||
extra_tags='failed_payment'
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
reverse('hosting:payment') + '#payment_error'
|
||||
)
|
||||
return self.render_to_response(context)
|
||||
|
||||
@method_decorator(decorators)
|
||||
def post(self, request):
|
||||
template = request.session.get('template')
|
||||
specs = request.session.get('specs')
|
||||
|
|
@ -933,27 +1029,28 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
cpu = specs.get('cpu')
|
||||
memory = specs.get('memory')
|
||||
disk_size = specs.get('disk_size')
|
||||
amount_to_be_charged = specs.get('price')
|
||||
plan_name = StripeUtils.get_stripe_plan_name(
|
||||
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'
|
||||
)
|
||||
amount_to_be_charged = specs.get('total_price')
|
||||
plan_name = StripeUtils.get_stripe_plan_name(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
|
||||
)
|
||||
|
||||
amount=amount_to_be_charged,
|
||||
name=plan_name,
|
||||
stripe_plan_id=stripe_plan_id)
|
||||
subscription_result = stripe_utils.subscribe_customer_to_plan(
|
||||
stripe_api_cus_id,
|
||||
[{"plan": stripe_plan.get('response_object').stripe_plan_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'):
|
||||
# At this point, we have created a Stripe API card, but and
|
||||
# At this point, we have created a Stripe API card and
|
||||
# associated it with the customer; but the transaction failed
|
||||
# due to some reason. So, we dissociate this card.
|
||||
stripe_utils.dissociate_customer_card(
|
||||
|
|
@ -1000,7 +1097,8 @@ class OrdersHostingDetailView(LoginRequiredMixin,
|
|||
stripe_subscription_obj.id, card_details_dict)
|
||||
|
||||
for session_var in ['specs', 'template', 'billing_address',
|
||||
'billing_address_data', 'card_id', 'token']:
|
||||
'billing_address_data', 'card_id',
|
||||
'token', 'customer']:
|
||||
if session_var in request.session:
|
||||
del request.session[session_var]
|
||||
|
||||
|
|
@ -1031,6 +1129,10 @@ class OrdersHostingListView(LoginRequiredMixin, ListView):
|
|||
self.queryset = HostingOrder.objects.filter(customer__user=user)
|
||||
return super(OrdersHostingListView, self).get_queryset()
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
return super(OrdersHostingListView, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView):
|
||||
login_url = reverse_lazy('hosting:login')
|
||||
|
|
@ -1085,17 +1187,22 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
raise ValidationError(_('Invalid number of cores'))
|
||||
|
||||
def validate_memory(self, value):
|
||||
if (value > 200) or (value < 2):
|
||||
if (value > 200) or (value < 1):
|
||||
raise ValidationError(_('Invalid RAM size'))
|
||||
|
||||
def validate_storage(self, value):
|
||||
if (value > 2000) or (value < 10):
|
||||
raise ValidationError(_('Invalid storage size'))
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
context = {'templates': VMTemplate.objects.all()}
|
||||
context = {
|
||||
'templates': VMTemplate.objects.all(),
|
||||
'cms_integration': get_cms_integration('default'),
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
@method_decorator(decorators)
|
||||
def post(self, request):
|
||||
cores = request.POST.get('cpu')
|
||||
cores_field = forms.IntegerField(validators=[self.validate_cores])
|
||||
|
|
@ -1104,18 +1211,34 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
storage = request.POST.get('storage')
|
||||
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
||||
template_id = int(request.POST.get('config'))
|
||||
pricing_name = request.POST.get('pricing_name')
|
||||
vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name)
|
||||
template = VMTemplate.objects.filter(
|
||||
opennebula_vm_template_id=template_id).first()
|
||||
template_data = VMTemplateSerializer(template).data
|
||||
|
||||
if vm_pricing is None:
|
||||
vm_pricing_name_msg = _(
|
||||
"Incorrect pricing name. Please contact support"
|
||||
"{support_email}".format(
|
||||
support_email=settings.DCL_SUPPORT_FROM_ADDRESS
|
||||
)
|
||||
)
|
||||
messages.add_message(
|
||||
self.request, messages.ERROR, vm_pricing_name_msg,
|
||||
extra_tags='pricing'
|
||||
)
|
||||
return redirect(CreateVirtualMachinesView.as_view())
|
||||
else:
|
||||
vm_pricing_name = vm_pricing.name
|
||||
|
||||
try:
|
||||
cores = cores_field.clean(cores)
|
||||
except ValidationError as err:
|
||||
msg = '{} : {}.'.format(cores, str(err))
|
||||
messages.add_message(self.request, messages.ERROR, msg,
|
||||
extra_tags='cores')
|
||||
return HttpResponseRedirect(
|
||||
reverse('datacenterlight:index') + "#order_form")
|
||||
return redirect(CreateVirtualMachinesView.as_view())
|
||||
|
||||
try:
|
||||
memory = memory_field.clean(memory)
|
||||
|
|
@ -1123,8 +1246,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
msg = '{} : {}.'.format(memory, str(err))
|
||||
messages.add_message(self.request, messages.ERROR, msg,
|
||||
extra_tags='memory')
|
||||
return HttpResponseRedirect(
|
||||
reverse('datacenterlight:index') + "#order_form")
|
||||
return redirect(CreateVirtualMachinesView.as_view())
|
||||
|
||||
try:
|
||||
storage = storage_field.clean(storage)
|
||||
|
|
@ -1132,15 +1254,25 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|||
msg = '{} : {}.'.format(storage, str(err))
|
||||
messages.add_message(self.request, messages.ERROR, msg,
|
||||
extra_tags='storage')
|
||||
return HttpResponseRedirect(
|
||||
reverse('datacenterlight:index') + "#order_form")
|
||||
price = get_vm_price(cpu=cores, memory=memory,
|
||||
disk_size=storage)
|
||||
return redirect(CreateVirtualMachinesView.as_view())
|
||||
|
||||
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
||||
cpu=cores,
|
||||
memory=memory,
|
||||
ssd_size=storage,
|
||||
pricing_name=vm_pricing_name
|
||||
)
|
||||
|
||||
specs = {
|
||||
'cpu': cores,
|
||||
'memory': memory,
|
||||
'disk_size': storage,
|
||||
'price': price
|
||||
'discount': discount,
|
||||
'price': price,
|
||||
'vat': vat,
|
||||
'vat_percent': vat_percent,
|
||||
'total_price': price + vat - discount['amount'],
|
||||
'pricing_name': vm_pricing_name
|
||||
}
|
||||
|
||||
request.session['specs'] = specs
|
||||
|
|
@ -1176,13 +1308,14 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
)
|
||||
return None
|
||||
except Exception as error:
|
||||
print(error)
|
||||
logger.error(str(error))
|
||||
raise Http404()
|
||||
|
||||
def get_success_url(self):
|
||||
final_url = reverse('hosting:virtual_machines')
|
||||
return final_url
|
||||
|
||||
@method_decorator(decorators)
|
||||
def get(self, request, *args, **kwargs):
|
||||
vm = self.get_object()
|
||||
if vm is None:
|
||||
|
|
@ -1205,7 +1338,8 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
context = {
|
||||
'virtual_machine': serializer.data,
|
||||
'order': HostingOrder.objects.get(
|
||||
vm_id=serializer.data['vm_id'])
|
||||
vm_id=serializer.data['vm_id']
|
||||
)
|
||||
}
|
||||
except Exception as ex:
|
||||
logger.debug("Exception generated {}".format(str(ex)))
|
||||
|
|
@ -1217,51 +1351,91 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
@method_decorator(decorators)
|
||||
def post(self, request, *args, **kwargs):
|
||||
response = {'status': False}
|
||||
admin_email_body = {}
|
||||
owner = self.request.user
|
||||
vm = self.get_object()
|
||||
|
||||
opennebula_vm_id = self.kwargs.get('pk')
|
||||
|
||||
manager = OpenNebulaManager(
|
||||
email=owner.email,
|
||||
password=owner.password
|
||||
)
|
||||
|
||||
try:
|
||||
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
|
||||
vm_name = vm_data.get('name')
|
||||
except WrongIdError:
|
||||
except WrongIdError as wrong_id_err:
|
||||
logger.error(str(wrong_id_err))
|
||||
return redirect(reverse('hosting:virtual_machines'))
|
||||
|
||||
# Cancel Stripe subscription
|
||||
stripe_utils = StripeUtils()
|
||||
try:
|
||||
hosting_order = HostingOrder.objects.get(
|
||||
vm_id=vm.id
|
||||
)
|
||||
result = stripe_utils.unsubscribe_customer(
|
||||
subscription_id=hosting_order.subscription_id
|
||||
)
|
||||
stripe_subscription_obj = result.get('response_object')
|
||||
# Check if the subscription was canceled
|
||||
if (stripe_subscription_obj is None or
|
||||
stripe_subscription_obj.status != 'canceled'):
|
||||
error_msg = result.get('error')
|
||||
logger.error(
|
||||
'Error canceling subscription for {user} and vm id '
|
||||
'{vm_id}'.format(user=owner.email, vm_id=vm.id)
|
||||
)
|
||||
logger.error(error_msg)
|
||||
admin_email_body['stripe_error_msg'] = error_msg
|
||||
except HostingOrder.DoesNotExist:
|
||||
error_msg = (
|
||||
"HostingOrder corresponding to vm_id={vm_id} does"
|
||||
"not exist. Hence, can not find subscription to "
|
||||
"cancel ".format(vm_id=vm.id)
|
||||
)
|
||||
logger.error(error_msg)
|
||||
admin_email_body['stripe_error_msg'] = error_msg
|
||||
|
||||
terminated = manager.delete_vm(vm.id)
|
||||
|
||||
if not terminated:
|
||||
response['text'] = ugettext(
|
||||
'Error terminating VM') + opennebula_vm_id
|
||||
logger.debug(
|
||||
"manager.delete_vm returned False. Hence, error making "
|
||||
"xml-rpc call to delete vm failed."
|
||||
)
|
||||
response['text'] = ugettext('Error terminating VM') + vm.id
|
||||
else:
|
||||
for t in range(15):
|
||||
try:
|
||||
manager.get_vm(opennebula_vm_id)
|
||||
manager.get_vm(vm.id)
|
||||
except WrongIdError:
|
||||
response['status'] = True
|
||||
response['text'] = ugettext('Terminated')
|
||||
vm_detail_obj = VMDetail.objects.filter(
|
||||
vm_id=opennebula_vm_id).first()
|
||||
vm_id=vm.id
|
||||
).first()
|
||||
vm_detail_obj.terminated_at = datetime.utcnow()
|
||||
vm_detail_obj.save()
|
||||
break
|
||||
except BaseException:
|
||||
except BaseException as base_exception:
|
||||
logger.error(
|
||||
"manager.get_vm({vm_id}) returned exception: "
|
||||
"{details}.".format(
|
||||
details=str(base_exception), vm_id=vm.id
|
||||
)
|
||||
)
|
||||
break
|
||||
else:
|
||||
sleep(2)
|
||||
context = {
|
||||
'vm_name': vm_name,
|
||||
'base_url': "{0}://{1}".format(self.request.scheme,
|
||||
self.request.get_host()),
|
||||
'base_url': "{0}://{1}".format(
|
||||
self.request.scheme, self.request.get_host()
|
||||
),
|
||||
'page_header': _('Virtual Machine %(vm_name)s Cancelled') % {
|
||||
'vm_name': vm_name}
|
||||
'vm_name': vm_name
|
||||
}
|
||||
}
|
||||
email_data = {
|
||||
'subject': context['page_header'],
|
||||
|
|
@ -1273,6 +1447,18 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
|||
}
|
||||
email = BaseEmail(**email_data)
|
||||
email.send()
|
||||
admin_email_body.update(response)
|
||||
email_to_admin_data = {
|
||||
'subject': "Deleted VM and Subscription for VM {vm_id} and "
|
||||
"user: {user}".format(
|
||||
vm_id=vm.id, user=owner.email
|
||||
),
|
||||
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
||||
'to': ['info@ungleich.ch'],
|
||||
'body': "\n".join(
|
||||
["%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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue