2017-08-30 10:02:53 +00:00
|
|
|
import logging
|
2017-07-06 11:18:22 +00:00
|
|
|
import uuid
|
2017-09-24 19:00:28 +00:00
|
|
|
from datetime import datetime
|
2020-12-01 11:43:09 +00:00
|
|
|
from urllib.parse import quote
|
2017-09-13 20:54:10 +00:00
|
|
|
from time import sleep
|
2017-07-06 11:18:22 +00:00
|
|
|
|
2020-01-20 06:37:32 +00:00
|
|
|
import stripe
|
2017-09-15 14:33:52 +00:00
|
|
|
from django import forms
|
2017-09-09 10:06:29 +00:00
|
|
|
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
|
2017-09-15 14:33:52 +00:00
|
|
|
from django.core.exceptions import ValidationError
|
2017-07-06 11:18:22 +00:00
|
|
|
from django.core.files.base import ContentFile
|
2020-01-20 06:37:32 +00:00
|
|
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
2016-04-20 06:03:32 +00:00
|
|
|
from django.core.urlresolvers import reverse_lazy, reverse
|
2020-04-01 11:39:02 +00:00
|
|
|
from django.db.models import Q
|
2018-04-20 14:55:24 +00:00
|
|
|
from django.http import (
|
|
|
|
Http404, HttpResponseRedirect, HttpResponse, JsonResponse
|
|
|
|
)
|
2017-09-13 20:54:10 +00:00
|
|
|
from django.shortcuts import redirect, render
|
2018-07-06 23:50:46 +00:00
|
|
|
from django.utils.decorators import method_decorator
|
2018-07-03 19:52:44 +00:00
|
|
|
from django.utils.html import escape
|
2017-05-12 05:56:35 +00:00
|
|
|
from django.utils.http import urlsafe_base64_decode
|
2017-09-09 10:06:29 +00:00
|
|
|
from django.utils.safestring import mark_safe
|
2017-09-16 15:25:37 +00:00
|
|
|
from django.utils.translation import get_language, ugettext_lazy as _
|
2017-09-13 20:54:10 +00:00
|
|
|
from django.utils.translation import ugettext
|
2018-03-09 18:49:40 +00:00
|
|
|
from django.views.decorators.cache import never_cache
|
2017-09-20 17:49:32 +00:00
|
|
|
from django.views.generic import (
|
|
|
|
View, CreateView, FormView, ListView, DetailView, DeleteView,
|
|
|
|
TemplateView, UpdateView
|
|
|
|
)
|
2016-07-11 03:08:51 +00:00
|
|
|
from guardian.mixins import PermissionRequiredMixin
|
2017-09-13 00:00:05 +00:00
|
|
|
from oca.pool import WrongIdError
|
2019-06-08 02:40:16 +00:00
|
|
|
from rest_framework.renderers import JSONRenderer
|
|
|
|
from rest_framework.response import Response
|
|
|
|
from rest_framework.views import APIView
|
2016-05-29 18:37:43 +00:00
|
|
|
from stored_messages.api import mark_read
|
2017-09-09 10:06:29 +00:00
|
|
|
from stored_messages.models import Message
|
|
|
|
from stored_messages.settings import stored_messages_settings
|
2016-04-19 06:04:15 +00:00
|
|
|
|
2018-10-17 07:29:06 +00:00
|
|
|
from datacenterlight.cms_models import DCLCalculatorPluginModel
|
2018-04-25 10:25:58 +00:00
|
|
|
from datacenterlight.models import VMTemplate, VMPricing
|
2019-12-26 08:01:15 +00:00
|
|
|
from datacenterlight.utils import (
|
|
|
|
create_vm, get_cms_integration, check_otp, validate_vat_number
|
|
|
|
)
|
2020-01-23 11:07:35 +00:00
|
|
|
from hosting.models import UserCardDetail, StripeTaxRate
|
2016-04-26 06:16:03 +00:00
|
|
|
from membership.models import CustomUser, StripeCustomer
|
2017-09-09 10:06:29 +00:00
|
|
|
from opennebula_api.models import OpenNebulaManager
|
2017-12-25 14:07:12 +00:00
|
|
|
from opennebula_api.serializers import (
|
|
|
|
VirtualMachineSerializer, VirtualMachineTemplateSerializer,
|
|
|
|
VMTemplateSerializer
|
|
|
|
)
|
2017-09-24 10:19:40 +00:00
|
|
|
from utils.forms import (
|
|
|
|
BillingAddressForm, PasswordResetRequestForm, UserBillingAddressForm,
|
|
|
|
ResendActivationEmailForm
|
|
|
|
)
|
2019-05-12 17:21:19 +00:00
|
|
|
from utils.hosting_utils import get_all_public_keys
|
2019-12-09 07:00:49 +00:00
|
|
|
from utils.hosting_utils import (
|
|
|
|
get_vm_price_with_vat, get_vm_price_for_given_vat, HostingUtils,
|
|
|
|
get_vat_rate_for_country
|
|
|
|
)
|
2020-01-20 06:37:32 +00:00
|
|
|
from utils.ldap_manager import LdapManager
|
2017-09-09 10:06:29 +00:00
|
|
|
from utils.mailer import BaseEmail
|
2016-04-26 06:16:03 +00:00
|
|
|
from utils.stripe_utils import StripeUtils
|
2017-12-20 19:59:46 +00:00
|
|
|
from utils.tasks import send_plain_email_task
|
2017-09-20 17:49:32 +00:00
|
|
|
from utils.views import (
|
2017-09-24 10:19:40 +00:00
|
|
|
PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin,
|
|
|
|
ResendActivationLinkViewMixin
|
2017-09-20 17:49:32 +00:00
|
|
|
)
|
2017-12-25 14:07:12 +00:00
|
|
|
from .forms import (
|
|
|
|
HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm,
|
|
|
|
generate_ssh_key_name
|
|
|
|
)
|
2018-04-07 10:53:53 +00:00
|
|
|
from .mixins import ProcessVMSelectionMixin, HostingContextMixin
|
2017-09-24 19:00:28 +00:00
|
|
|
from .models import (
|
2018-09-26 07:10:15 +00:00
|
|
|
HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail,
|
2020-01-20 06:37:32 +00:00
|
|
|
GenericProduct, MonthlyHostingBill
|
2017-09-24 19:00:28 +00:00
|
|
|
)
|
2017-09-15 14:33:52 +00:00
|
|
|
|
2017-08-29 08:01:05 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2017-05-13 04:59:57 +00:00
|
|
|
|
2019-05-13 01:34:10 +00:00
|
|
|
|
2017-08-29 15:51:58 +00:00
|
|
|
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \
|
|
|
|
backend connection error. please try again in a few \
|
|
|
|
minutes."
|
2019-05-13 01:34:10 +00:00
|
|
|
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
decorators = [never_cache]
|
2016-04-19 06:04:15 +00:00
|
|
|
|
2017-05-25 09:27:49 +00:00
|
|
|
|
2017-10-01 21:06:51 +00:00
|
|
|
class DashboardView(LoginRequiredMixin, View):
|
2017-08-22 15:15:18 +00:00
|
|
|
template_name = "hosting/dashboard.html"
|
2017-10-01 21:06:51 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2017-08-22 15:15:18 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = {}
|
|
|
|
return context
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-08-22 15:15:18 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
context = self.get_context_data()
|
2019-04-04 06:10:08 +00:00
|
|
|
context['has_invoices'] = False
|
2019-04-04 05:55:53 +00:00
|
|
|
try:
|
2019-04-06 12:07:26 +00:00
|
|
|
bills = []
|
|
|
|
if hasattr(self.request.user, 'stripecustomer'):
|
|
|
|
bills = MonthlyHostingBill.objects.filter(
|
|
|
|
customer=self.request.user.stripecustomer
|
|
|
|
)
|
2019-04-04 06:10:08 +00:00
|
|
|
if len(bills) > 0:
|
|
|
|
context['has_invoices'] = True
|
2019-04-04 05:55:53 +00:00
|
|
|
except MonthlyHostingBill.DoesNotExist as dne:
|
|
|
|
logger.error("{}'s monthly hosting bill not imported ?".format(
|
|
|
|
self.request.user.email
|
|
|
|
))
|
2017-08-22 15:15:18 +00:00
|
|
|
return render(request, self.template_name, context)
|
|
|
|
|
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
class DjangoHostingView(ProcessVMSelectionMixin, View):
|
2016-04-19 06:04:15 +00:00
|
|
|
template_name = "hosting/django.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2016-06-07 05:29:22 +00:00
|
|
|
HOSTING = 'django'
|
2017-05-13 05:31:29 +00:00
|
|
|
templates = OpenNebulaManager().get_templates()
|
|
|
|
data = VirtualMachineTemplateSerializer(templates, many=True).data
|
2017-05-22 03:01:26 +00:00
|
|
|
configuration_options = HostingPlan.get_serialized_configs()
|
2016-04-22 13:36:38 +00:00
|
|
|
context = {
|
2016-06-07 05:29:22 +00:00
|
|
|
'hosting': HOSTING,
|
2016-04-22 13:36:38 +00:00
|
|
|
'hosting_long': "Django",
|
2017-05-13 05:31:29 +00:00
|
|
|
# 'configuration_detail': configuration_detail,
|
2016-04-22 13:36:38 +00:00
|
|
|
'domain': "django-hosting.ch",
|
|
|
|
'google_analytics': "UA-62285904-6",
|
2017-05-13 05:31:29 +00:00
|
|
|
'vm_types': data,
|
2016-04-22 13:36:38 +00:00
|
|
|
'email': "info@django-hosting.ch",
|
2017-05-22 03:01:26 +00:00
|
|
|
'configuration_options': configuration_options,
|
|
|
|
'templates': templates,
|
2016-04-22 13:36:38 +00:00
|
|
|
}
|
2016-05-19 06:17:16 +00:00
|
|
|
|
2016-04-19 06:04:15 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
2016-05-19 06:17:16 +00:00
|
|
|
request.session['hosting_url'] = reverse('hosting:djangohosting')
|
2016-04-19 06:04:15 +00:00
|
|
|
context = self.get_context_data()
|
2016-04-23 07:22:44 +00:00
|
|
|
|
2016-04-19 06:04:15 +00:00
|
|
|
return render(request, self.template_name, context)
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2015-05-27 10:21:30 +00:00
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
class RailsHostingView(ProcessVMSelectionMixin, View):
|
2016-04-20 06:03:32 +00:00
|
|
|
template_name = "hosting/rails.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2016-06-07 05:29:22 +00:00
|
|
|
HOSTING = 'rails'
|
2017-05-13 05:31:29 +00:00
|
|
|
|
|
|
|
templates = OpenNebulaManager().get_templates()
|
2017-05-22 03:01:26 +00:00
|
|
|
configuration_options = HostingPlan.get_serialized_configs()
|
2017-05-13 05:31:29 +00:00
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
context = {
|
2016-06-07 05:29:22 +00:00
|
|
|
'hosting': HOSTING,
|
2016-04-22 13:36:38 +00:00
|
|
|
'hosting_long': "Ruby On Rails",
|
|
|
|
'domain': "rails-hosting.ch",
|
|
|
|
'google_analytics': "UA-62285904-5",
|
|
|
|
'email': "info@rails-hosting.ch",
|
2017-05-22 03:01:26 +00:00
|
|
|
'configuration_options': configuration_options,
|
|
|
|
'templates': templates,
|
2016-04-22 13:36:38 +00:00
|
|
|
}
|
2016-04-20 06:03:32 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
2016-05-19 06:17:16 +00:00
|
|
|
request.session['hosting_url'] = reverse('hosting:railshosting')
|
2016-04-20 06:03:32 +00:00
|
|
|
context = self.get_context_data()
|
|
|
|
return render(request, self.template_name, context)
|
|
|
|
|
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
class NodeJSHostingView(ProcessVMSelectionMixin, View):
|
2016-04-20 06:03:32 +00:00
|
|
|
template_name = "hosting/nodejs.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2016-06-07 05:29:22 +00:00
|
|
|
HOSTING = 'nodejs'
|
2017-05-13 05:31:29 +00:00
|
|
|
templates = OpenNebulaManager().get_templates()
|
2017-05-22 03:01:26 +00:00
|
|
|
configuration_options = HostingPlan.get_serialized_configs()
|
2017-05-13 05:31:29 +00:00
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
context = {
|
2017-05-13 05:31:29 +00:00
|
|
|
'hosting': HOSTING,
|
2016-04-22 13:36:38 +00:00
|
|
|
'hosting_long': "NodeJS",
|
2017-05-13 05:31:29 +00:00
|
|
|
# 'configuration_detail': configuration_detail,
|
2016-04-22 13:36:38 +00:00
|
|
|
'domain': "node-hosting.ch",
|
|
|
|
'google_analytics': "UA-62285904-7",
|
|
|
|
'email': "info@node-hosting.ch",
|
2017-05-22 03:01:26 +00:00
|
|
|
'templates': templates,
|
|
|
|
'configuration_options': configuration_options,
|
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
}
|
2016-04-20 06:03:32 +00:00
|
|
|
return context
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
2016-05-19 06:17:16 +00:00
|
|
|
request.session['hosting_url'] = reverse('hosting:nodejshosting')
|
2016-04-20 06:03:32 +00:00
|
|
|
context = self.get_context_data()
|
2016-04-23 07:22:44 +00:00
|
|
|
|
2016-04-20 06:03:32 +00:00
|
|
|
return render(request, self.template_name, context)
|
2016-06-30 06:23:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HostingPricingView(ProcessVMSelectionMixin, View):
|
|
|
|
template_name = "hosting/hosting_pricing.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2017-05-13 05:31:29 +00:00
|
|
|
# configuration_options = dict(VirtualMachinePlan.VM_CONFIGURATION)
|
|
|
|
templates = OpenNebulaManager().get_templates()
|
2017-05-22 03:01:26 +00:00
|
|
|
configuration_options = HostingPlan.get_serialized_configs()
|
2017-05-13 05:31:29 +00:00
|
|
|
|
2016-06-30 06:23:14 +00:00
|
|
|
context = {
|
2017-05-13 05:31:29 +00:00
|
|
|
# 'configuration_options': configuration_options,
|
2016-06-30 06:23:14 +00:00
|
|
|
'email': "info@django-hosting.ch",
|
2017-05-22 03:01:26 +00:00
|
|
|
'templates': templates,
|
|
|
|
'configuration_options': configuration_options,
|
|
|
|
|
2016-06-30 06:23:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return context
|
|
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
request.session['hosting_url'] = reverse('hosting:djangohosting')
|
|
|
|
context = self.get_context_data()
|
|
|
|
|
|
|
|
return render(request, self.template_name, context)
|
2016-04-20 06:03:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class IndexView(View):
|
|
|
|
template_name = "hosting/index.html"
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
2017-05-13 05:31:29 +00:00
|
|
|
templates = OpenNebulaManager().get_templates()
|
|
|
|
data = VirtualMachineTemplateSerializer(templates, many=True).data
|
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
context = {
|
|
|
|
'hosting': "nodejs",
|
|
|
|
'hosting_long': "NodeJS",
|
|
|
|
'domain': "node-hosting.ch",
|
|
|
|
'google_analytics': "UA-62285904-7",
|
|
|
|
'email': "info@node-hosting.ch",
|
2017-05-13 05:31:29 +00:00
|
|
|
'vm_types': data
|
|
|
|
# 'vm_types': VirtualMachineType.get_serialized_vm_types(),
|
2016-04-22 13:36:38 +00:00
|
|
|
}
|
2016-04-20 06:03:32 +00:00
|
|
|
return context
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2016-04-20 06:03:32 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
context = self.get_context_data()
|
|
|
|
return render(request, self.template_name, context)
|
|
|
|
|
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class LoginView(HostingContextMixin, LoginViewMixin):
|
2016-08-20 05:57:35 +00:00
|
|
|
template_name = "hosting/login.html"
|
2016-04-20 06:03:32 +00:00
|
|
|
form_class = HostingUserLoginForm
|
2017-09-07 16:54:03 +00:00
|
|
|
success_url = reverse_lazy('hosting:dashboard')
|
2016-06-04 07:59:37 +00:00
|
|
|
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class SignupView(HostingContextMixin, CreateView):
|
2016-04-20 06:03:32 +00:00
|
|
|
template_name = 'hosting/signup.html'
|
|
|
|
form_class = HostingUserSignupForm
|
2016-05-18 22:58:28 +00:00
|
|
|
model = CustomUser
|
2017-12-27 07:32:55 +00:00
|
|
|
success_url = reverse_lazy('hosting:dashboard')
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2016-04-20 06:03:32 +00:00
|
|
|
def get_success_url(self):
|
2017-05-25 09:27:49 +00:00
|
|
|
next_url = self.request.session.get(
|
2017-05-25 14:23:31 +00:00
|
|
|
'next', self.success_url)
|
2016-04-30 18:55:55 +00:00
|
|
|
return next_url
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2016-04-20 06:03:32 +00:00
|
|
|
def form_valid(self, form):
|
|
|
|
name = form.cleaned_data.get('name')
|
|
|
|
email = form.cleaned_data.get('email')
|
|
|
|
password = form.cleaned_data.get('password')
|
2017-07-26 11:15:49 +00:00
|
|
|
this_base_url = "{0}://{1}".format(self.request.scheme,
|
|
|
|
self.request.get_host())
|
|
|
|
CustomUser.register(name, password, email,
|
|
|
|
app='dcl', base_url=this_base_url)
|
2017-06-10 23:48:06 +00:00
|
|
|
|
|
|
|
return HttpResponseRedirect(reverse_lazy('hosting:signup-validate'))
|
2015-07-28 19:22:06 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-12-27 07:33:53 +00:00
|
|
|
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)
|
|
|
|
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class SignupValidateView(HostingContextMixin, TemplateView):
|
2017-06-10 23:48:06 +00:00
|
|
|
template_name = "hosting/signup_validate.html"
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2017-06-10 23:48:06 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(SignupValidateView, self).get_context_data(**kwargs)
|
2017-07-26 11:15:49 +00:00
|
|
|
login_url = '<a href="' + \
|
2017-08-04 15:49:35 +00:00
|
|
|
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
|
2017-07-26 11:15:49 +00:00
|
|
|
home_url = '<a href="' + \
|
2017-08-04 15:49:35 +00:00
|
|
|
reverse('datacenterlight:index') + '">Data Center Light</a>'
|
2017-06-29 16:23:25 +00:00
|
|
|
message = '{signup_success_message} {lurl}</a> \
|
2017-06-17 14:08:22 +00:00
|
|
|
<br />{go_back} {hurl}.'.format(
|
2017-06-29 16:23:25 +00:00
|
|
|
signup_success_message=_(
|
|
|
|
'Thank you for signing up. We have sent an email to you. '
|
2017-09-29 06:50:03 +00:00
|
|
|
'Please follow the instructions in it to activate your '
|
|
|
|
'account. Once activated, you can login using'),
|
2017-06-29 16:23:25 +00:00
|
|
|
go_back=_('Go back to'),
|
|
|
|
lurl=login_url,
|
|
|
|
hurl=home_url
|
|
|
|
)
|
2017-06-10 23:48:06 +00:00
|
|
|
context['message'] = mark_safe(message)
|
2017-06-17 14:08:22 +00:00
|
|
|
context['section_title'] = _('Sign up')
|
2017-06-10 23:48:06 +00:00
|
|
|
return context
|
2015-08-09 20:13:38 +00:00
|
|
|
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2018-04-07 11:15:44 +00:00
|
|
|
class SignupValidatedView(SignupValidateView, HostingContextMixin):
|
2017-06-10 23:48:06 +00:00
|
|
|
template_name = "hosting/signup_validate.html"
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2017-06-10 23:48:06 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(SignupValidateView, self).get_context_data(**kwargs)
|
|
|
|
validated = CustomUser.validate_url(self.kwargs['validate_slug'])
|
2017-07-26 11:15:49 +00:00
|
|
|
login_url = '<a href="' + \
|
2017-08-04 15:49:35 +00:00
|
|
|
reverse('hosting:login') + '">' + str(_('login')) + '</a>'
|
2017-06-29 16:23:25 +00:00
|
|
|
section_title = _('Account activation')
|
2017-09-22 19:33:43 +00:00
|
|
|
user = CustomUser.objects.filter(
|
|
|
|
validation_slug=self.kwargs['validate_slug']).first()
|
2017-06-10 23:48:06 +00:00
|
|
|
if validated:
|
2017-09-29 06:50:03 +00:00
|
|
|
message = ('{account_activation_string} <br />'
|
|
|
|
' {login_string} {lurl}.').format(
|
2017-07-26 11:15:49 +00:00
|
|
|
account_activation_string=_(
|
|
|
|
"Your account has been activated."),
|
2017-06-29 16:23:25 +00:00
|
|
|
login_string=_("You can now"),
|
|
|
|
lurl=login_url)
|
2017-09-26 21:15:24 +00:00
|
|
|
email_data = {
|
|
|
|
'subject': _('Welcome to Data Center Light!'),
|
2017-09-26 21:39:43 +00:00
|
|
|
'to': user.email,
|
2017-09-26 21:15:24 +00:00
|
|
|
'context': {
|
|
|
|
'base_url': "{0}://{1}".format(
|
|
|
|
self.request.scheme,
|
|
|
|
self.request.get_host()
|
|
|
|
)
|
|
|
|
},
|
|
|
|
'template_name': 'welcome_user',
|
|
|
|
'template_path': 'datacenterlight/emails/',
|
|
|
|
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
|
|
|
|
}
|
|
|
|
email = BaseEmail(**email_data)
|
|
|
|
email.send()
|
2017-06-10 23:48:06 +00:00
|
|
|
else:
|
2017-07-26 11:15:49 +00:00
|
|
|
home_url = '<a href="' + \
|
2018-04-07 11:16:36 +00:00
|
|
|
reverse('datacenterlight:cms_index') + \
|
2017-08-18 20:51:34 +00:00
|
|
|
'">Data Center Light</a>'
|
2017-06-17 14:08:22 +00:00
|
|
|
message = '{sorry_message} <br />{go_back_to} {hurl}'.format(
|
2017-06-29 16:23:25 +00:00
|
|
|
sorry_message=_("Sorry. Your request is invalid."),
|
|
|
|
go_back_to=_('Go back to'),
|
|
|
|
hurl=home_url
|
|
|
|
)
|
2017-06-10 23:48:06 +00:00
|
|
|
context['message'] = mark_safe(message)
|
|
|
|
context['section_title'] = section_title
|
|
|
|
return context
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-12-27 08:03:17 +00:00
|
|
|
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)
|
|
|
|
|
2015-07-30 17:07:29 +00:00
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class ResendActivationEmailView(HostingContextMixin,
|
|
|
|
ResendActivationLinkViewMixin):
|
2017-09-24 10:19:40 +00:00
|
|
|
template_name = 'hosting/resend_activation_link.html'
|
|
|
|
form_class = ResendActivationEmailForm
|
|
|
|
success_url = reverse_lazy('hosting:login')
|
|
|
|
email_template_path = 'datacenterlight/emails/'
|
|
|
|
email_template_name = 'user_activation'
|
|
|
|
|
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class PasswordResetView(HostingContextMixin, PasswordResetViewMixin):
|
2017-06-12 15:21:48 +00:00
|
|
|
site = 'dcl'
|
2016-06-21 05:10:38 +00:00
|
|
|
template_name = 'hosting/reset_password.html'
|
|
|
|
form_class = PasswordResetRequestForm
|
|
|
|
success_url = reverse_lazy('hosting:login')
|
Created signup view. Added login after signup.Added signup url to nosystem app urls.py. Added logout view, Added logout button on nabber, Added password reset form, Added password view , Added password reset html, Added password reset email for nosystemd app. Added confirm_reset_password.html, Added confirm_ reset password view, Added confirm reset password form, Fixed reset password token generation, Started donation view, Added donation view, Added donation.html, Added donation form, Adding donation.js lib in order to capture stripe payments for nosystem app.
2016-07-22 06:24:32 +00:00
|
|
|
template_email_path = 'hosting/emails/'
|
2016-06-21 05:10:38 +00:00
|
|
|
|
|
|
|
|
2018-04-07 10:53:53 +00:00
|
|
|
class PasswordResetConfirmView(HostingContextMixin,
|
|
|
|
PasswordResetConfirmViewMixin):
|
2016-06-21 05:10:38 +00:00
|
|
|
template_name = 'hosting/confirm_reset_password.html'
|
|
|
|
success_url = reverse_lazy('hosting:login')
|
|
|
|
|
2017-05-12 05:56:35 +00:00
|
|
|
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
|
|
|
try:
|
|
|
|
uid = urlsafe_base64_decode(uidb64)
|
|
|
|
user = CustomUser.objects.get(pk=uid)
|
|
|
|
|
|
|
|
opennebula_client = OpenNebulaManager(
|
2020-03-05 12:47:14 +00:00
|
|
|
email=user.username,
|
2017-05-12 05:56:35 +00:00
|
|
|
password=user.password,
|
|
|
|
)
|
|
|
|
|
|
|
|
except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
|
|
|
|
user = None
|
|
|
|
opennebula_client = None
|
|
|
|
|
|
|
|
form = self.form_class(request.POST)
|
|
|
|
|
2017-09-06 21:52:02 +00:00
|
|
|
if user is not None and default_token_generator.check_token(user,
|
|
|
|
token):
|
2017-05-12 05:56:35 +00:00
|
|
|
if form.is_valid():
|
2019-12-10 17:53:50 +00:00
|
|
|
ldap_manager = LdapManager()
|
2017-05-12 05:56:35 +00:00
|
|
|
new_password = form.cleaned_data['new_password2']
|
2019-12-14 09:29:45 +00:00
|
|
|
|
2019-12-18 07:52:46 +00:00
|
|
|
# Make sure the user have an ldap account already
|
2019-12-14 09:29:45 +00:00
|
|
|
user.create_ldap_account(new_password)
|
2019-12-18 07:52:46 +00:00
|
|
|
|
|
|
|
# We are changing password in ldap before changing in database because
|
|
|
|
# ldap have more chances of failure than local database
|
|
|
|
if ldap_manager.change_password(user.username, new_password):
|
|
|
|
user.set_password(new_password)
|
|
|
|
user.save()
|
|
|
|
|
|
|
|
messages.success(request, _('Password has been reset.'))
|
|
|
|
|
|
|
|
# Change opennebula password
|
|
|
|
opennebula_client.change_user_password(user.password)
|
|
|
|
|
|
|
|
return self.form_valid(form)
|
|
|
|
|
|
|
|
messages.error(
|
|
|
|
request, _('Password reset has not been successful.'))
|
|
|
|
form.add_error(None,
|
|
|
|
_('Password reset has not been successful.'))
|
|
|
|
return self.form_invalid(form)
|
2017-05-12 05:56:35 +00:00
|
|
|
|
|
|
|
else:
|
2017-09-02 11:20:09 +00:00
|
|
|
error_msg = _('The reset password link is no longer valid.')
|
|
|
|
messages.error(request, _(error_msg))
|
|
|
|
form.add_error(None, error_msg)
|
2017-05-12 05:56:35 +00:00
|
|
|
return self.form_invalid(form)
|
|
|
|
|
2016-06-21 05:10:38 +00:00
|
|
|
|
2016-07-05 04:44:15 +00:00
|
|
|
class NotificationsView(LoginRequiredMixin, TemplateView):
|
2016-05-29 18:37:43 +00:00
|
|
|
template_name = 'hosting/notifications.html'
|
2016-07-05 04:44:15 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-05-29 18:37:43 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(NotificationsView, self).get_context_data(**kwargs)
|
|
|
|
backend = stored_messages_settings.STORAGE_BACKEND()
|
|
|
|
unread_notifications = backend.inbox_list(self.request.user)
|
|
|
|
read_notifications = backend.archive_list(self.request.user)
|
|
|
|
context.update({
|
|
|
|
'unread_notifications': unread_notifications,
|
|
|
|
'all_notifications': read_notifications + unread_notifications
|
|
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
|
|
class MarkAsReadNotificationView(LoginRequiredMixin, UpdateView):
|
|
|
|
model = Message
|
|
|
|
success_url = reverse_lazy('hosting:notifications')
|
2016-07-05 04:44:15 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-05-29 18:37:43 +00:00
|
|
|
fields = '__all__'
|
|
|
|
|
|
|
|
def post(self, *args, **kwargs):
|
|
|
|
message = self.get_object()
|
|
|
|
backend = stored_messages_settings.STORAGE_BACKEND()
|
|
|
|
backend.archive_store([self.request.user], message)
|
|
|
|
mark_read(self.request.user, message)
|
|
|
|
return HttpResponseRedirect(reverse('hosting:notifications'))
|
|
|
|
|
|
|
|
|
2017-06-03 11:33:05 +00:00
|
|
|
class SSHKeyDeleteView(LoginRequiredMixin, DeleteView):
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
2017-06-03 13:03:55 +00:00
|
|
|
success_url = reverse_lazy('hosting:ssh_keys')
|
2017-06-03 11:33:05 +00:00
|
|
|
model = UserHostingKey
|
|
|
|
|
2017-08-04 15:24:28 +00:00
|
|
|
def get_object(self, queryset=None):
|
2017-08-04 15:49:35 +00:00
|
|
|
""" Hook to ensure UserHostingKey object is owned by request.user.
|
|
|
|
We reply with a Http404 if the user is not the owner of the key.
|
|
|
|
"""
|
2017-08-04 15:24:28 +00:00
|
|
|
obj = super(SSHKeyDeleteView, self).get_object()
|
2017-08-04 15:49:35 +00:00
|
|
|
if not obj.user == self.request.user:
|
2017-08-04 15:24:28 +00:00
|
|
|
raise Http404
|
|
|
|
return obj
|
|
|
|
|
2017-06-03 13:03:55 +00:00
|
|
|
def delete(self, request, *args, **kwargs):
|
|
|
|
owner = self.request.user
|
2017-08-30 07:42:56 +00:00
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 09:57:18 +00:00
|
|
|
email=owner.username,
|
2017-08-30 07:42:56 +00:00
|
|
|
password=owner.password
|
|
|
|
)
|
2017-06-03 13:03:55 +00:00
|
|
|
pk = self.kwargs.get('pk')
|
|
|
|
# Get user ssh key
|
2017-06-09 20:36:14 +00:00
|
|
|
public_key = UserHostingKey.objects.get(pk=pk).public_key
|
2019-05-10 07:19:31 +00:00
|
|
|
keys = UserHostingKey.objects.filter(user=self.request.user)
|
2019-05-12 17:56:14 +00:00
|
|
|
keys_to_save = [k.public_key for k in keys if k.public_key != public_key]
|
|
|
|
manager.save_key_in_opennebula_user('\n'.join(keys_to_save), update_type=0)
|
2017-06-03 13:03:55 +00:00
|
|
|
|
|
|
|
return super(SSHKeyDeleteView, self).delete(request, *args, **kwargs)
|
|
|
|
|
2017-06-29 14:34:40 +00:00
|
|
|
|
2017-06-03 13:03:55 +00:00
|
|
|
class SSHKeyListView(LoginRequiredMixin, ListView):
|
|
|
|
template_name = "hosting/user_keys.html"
|
2016-07-05 04:44:15 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2017-06-03 13:03:55 +00:00
|
|
|
context_object_name = "keys"
|
|
|
|
model = UserHostingKey
|
|
|
|
paginate_by = 10
|
|
|
|
ordering = '-id'
|
2016-05-20 21:11:42 +00:00
|
|
|
|
2017-06-03 13:03:55 +00:00
|
|
|
def get_queryset(self):
|
|
|
|
user = self.request.user
|
|
|
|
self.queryset = UserHostingKey.objects.filter(user=user)
|
|
|
|
return super(SSHKeyListView, self).get_queryset()
|
2017-05-04 04:19:32 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-06-03 13:03:55 +00:00
|
|
|
def render_to_response(self, context, **response_kwargs):
|
|
|
|
if not self.queryset:
|
2017-07-12 05:21:43 +00:00
|
|
|
return HttpResponseRedirect(reverse('hosting:choice_ssh_keys'))
|
2017-09-06 21:52:02 +00:00
|
|
|
return super(SSHKeyListView, self).render_to_response(context,
|
|
|
|
**response_kwargs)
|
2017-06-03 13:03:55 +00:00
|
|
|
|
|
|
|
|
2017-07-05 13:57:49 +00:00
|
|
|
class SSHKeyChoiceView(LoginRequiredMixin, View):
|
|
|
|
template_name = "hosting/choice_ssh_keys.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-07-05 13:57:49 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
context = {}
|
|
|
|
return render(request, self.template_name, context)
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-07-06 08:47:12 +00:00
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
name = generate_ssh_key_name()
|
|
|
|
private_key, public_key = UserHostingKey.generate_keys()
|
2017-07-06 11:18:22 +00:00
|
|
|
content = ContentFile(private_key)
|
2017-07-26 11:15:49 +00:00
|
|
|
ssh_key = UserHostingKey.objects.create(
|
|
|
|
user=request.user, public_key=public_key, name=name)
|
2017-07-06 11:18:22 +00:00
|
|
|
filename = name + '_' + str(uuid.uuid4())[:8] + '_private.pem'
|
|
|
|
ssh_key.private_key.save(filename, content)
|
2017-08-30 10:02:53 +00:00
|
|
|
owner = self.request.user
|
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 09:57:18 +00:00
|
|
|
email=owner.username,
|
2017-08-30 10:02:53 +00:00
|
|
|
password=owner.password
|
|
|
|
)
|
2019-05-10 07:19:31 +00:00
|
|
|
keys = get_all_public_keys(request.user)
|
2019-05-10 23:54:35 +00:00
|
|
|
manager.save_key_in_opennebula_user('\n'.join(keys))
|
2017-07-06 08:47:12 +00:00
|
|
|
return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar')
|
|
|
|
|
2017-07-05 13:57:49 +00:00
|
|
|
|
2018-03-26 19:24:44 +00:00
|
|
|
@method_decorator(decorators, name='dispatch')
|
2017-08-24 16:41:15 +00:00
|
|
|
class SettingsView(LoginRequiredMixin, FormView):
|
|
|
|
template_name = "hosting/settings.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
form_class = BillingAddressForm
|
2017-10-20 22:02:35 +00:00
|
|
|
permission_required = ['view_usercarddetail']
|
2017-08-24 16:41:15 +00:00
|
|
|
|
2017-08-29 12:12:35 +00:00
|
|
|
def get_form(self, form_class):
|
|
|
|
"""
|
|
|
|
Check if the user already saved contact details. If so, then show
|
|
|
|
the form populated with those details, to let user change them.
|
|
|
|
"""
|
2024-02-09 15:37:34 +00:00
|
|
|
username = self.request.GET.get('username')
|
|
|
|
if self.request.user.is_admin and username:
|
|
|
|
user = CustomUser.objects.get(username=username)
|
|
|
|
else:
|
|
|
|
user = self.request.user
|
2019-12-21 03:13:34 +00:00
|
|
|
return form_class(
|
2024-02-09 15:37:34 +00:00
|
|
|
instance=user.billing_addresses.first(),
|
2017-08-29 12:12:35 +00:00
|
|
|
**self.get_form_kwargs())
|
2017-08-24 16:41:15 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(SettingsView, self).get_context_data(**kwargs)
|
|
|
|
# Get user
|
2024-02-09 15:37:34 +00:00
|
|
|
username = self.request.GET.get('username')
|
|
|
|
if self.request.user.is_admin and username:
|
|
|
|
user = CustomUser.objects.get(username=username)
|
|
|
|
else:
|
|
|
|
user = self.request.user
|
2017-10-30 07:26:35 +00:00
|
|
|
stripe_customer = None
|
|
|
|
if hasattr(user, 'stripecustomer'):
|
|
|
|
stripe_customer = user.stripecustomer
|
2020-12-31 20:29:41 +00:00
|
|
|
stripe_utils = StripeUtils()
|
|
|
|
cards_list_request = stripe_utils.get_available_payment_methods(
|
|
|
|
stripe_customer
|
2017-10-15 21:57:15 +00:00
|
|
|
)
|
2020-12-31 20:29:41 +00:00
|
|
|
cards_list = cards_list_request.get('response_object')
|
2017-08-24 16:41:15 +00:00
|
|
|
context.update({
|
2017-10-05 19:47:35 +00:00
|
|
|
'cards_list': cards_list,
|
2017-08-24 16:41:15 +00:00
|
|
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
|
|
|
})
|
|
|
|
|
|
|
|
return context
|
|
|
|
|
2017-08-29 12:12:35 +00:00
|
|
|
def post(self, request, *args, **kwargs):
|
2017-10-28 20:19:53 +00:00
|
|
|
if 'card' in request.POST and request.POST['card'] is not '':
|
|
|
|
card_id = escape(request.POST['card'])
|
|
|
|
UserCardDetail.set_default_card(
|
|
|
|
stripe_api_cus_id=request.user.stripecustomer.stripe_id,
|
2021-01-12 08:10:03 +00:00
|
|
|
stripe_source_id=card_id
|
2017-10-28 20:19:53 +00:00
|
|
|
)
|
2021-01-12 08:10:03 +00:00
|
|
|
stripe_utils = StripeUtils()
|
|
|
|
card_details = stripe_utils.get_cards_details_from_payment_method(
|
|
|
|
card_id
|
|
|
|
)
|
|
|
|
if not card_details.get('response_object'):
|
|
|
|
logger.debug("Could not find card %s in stripe" % card_id)
|
|
|
|
messages.add_message(request, messages.ERROR,
|
|
|
|
_("Could not set a default card."))
|
|
|
|
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
|
|
|
|
card_details_response = card_details['response_object']
|
2017-10-28 20:19:53 +00:00
|
|
|
msg = _(
|
|
|
|
("Your {brand} card ending in {last4} set as "
|
|
|
|
"default card").format(
|
2021-01-12 08:10:03 +00:00
|
|
|
brand=card_details_response['brand'],
|
|
|
|
last4=card_details_response['last4']
|
2017-10-28 20:19:53 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
|
|
|
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
|
2017-10-16 14:29:09 +00:00
|
|
|
if 'delete_card' in request.POST:
|
2020-12-31 21:07:52 +00:00
|
|
|
card = self.kwargs.get('pk')
|
|
|
|
stripe_utils = StripeUtils()
|
|
|
|
stripe_utils.dissociate_customer_card(
|
|
|
|
request.user.stripecustomer.stripe_id,
|
|
|
|
card
|
|
|
|
)
|
|
|
|
msg = _("Card deassociation successful")
|
|
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
2017-10-16 14:29:09 +00:00
|
|
|
return HttpResponseRedirect(reverse_lazy('hosting:settings'))
|
2017-08-29 12:12:35 +00:00
|
|
|
form = self.get_form()
|
|
|
|
if form.is_valid():
|
2017-10-15 17:55:37 +00:00
|
|
|
if 'billing-form' in request.POST:
|
2019-12-31 17:22:24 +00:00
|
|
|
current_billing_address = self.request.user.billing_addresses.last()
|
2017-10-15 17:55:37 +00:00
|
|
|
billing_address_data = form.cleaned_data
|
|
|
|
billing_address_data.update({
|
|
|
|
'user': self.request.user.id
|
|
|
|
})
|
|
|
|
billing_address_user_form = UserBillingAddressForm(
|
2019-12-26 13:24:17 +00:00
|
|
|
instance=self.request.user.billing_addresses.order_by('-id').first(),
|
2017-10-15 17:55:37 +00:00
|
|
|
data=billing_address_data)
|
2019-12-26 08:01:15 +00:00
|
|
|
billing_address = billing_address_user_form.save()
|
2019-12-26 08:19:18 +00:00
|
|
|
billing_address.stripe_tax_id = ''
|
2019-12-26 08:22:14 +00:00
|
|
|
billing_address.vat_number_validated_on = None
|
2019-12-26 14:57:15 +00:00
|
|
|
billing_address.vat_validation_status = ''
|
2019-12-26 08:19:18 +00:00
|
|
|
billing_address.save()
|
2019-12-26 08:01:15 +00:00
|
|
|
vat_number = billing_address_user_form.cleaned_data.get(
|
|
|
|
'vat_number').strip()
|
2019-12-26 13:17:40 +00:00
|
|
|
logger.debug("Vat number = %s" % vat_number)
|
2019-12-26 08:01:15 +00:00
|
|
|
if vat_number:
|
2019-12-31 17:22:49 +00:00
|
|
|
try:
|
|
|
|
stripe_customer = request.user.stripecustomer
|
|
|
|
except StripeCustomer.DoesNotExist as dne:
|
|
|
|
logger.debug(
|
|
|
|
"User %s does not have a stripecustomer. "
|
|
|
|
"Creating one." % request.user.email)
|
|
|
|
stripe_customer = StripeCustomer.get_or_create(
|
|
|
|
email=request.user.email,
|
|
|
|
token=None)
|
|
|
|
request.user.stripecustomer = stripe_customer
|
|
|
|
request.user.save()
|
2019-12-26 08:01:15 +00:00
|
|
|
validate_result = validate_vat_number(
|
|
|
|
stripe_customer_id=request.user.stripecustomer.stripe_id,
|
|
|
|
billing_address_id=billing_address.id,
|
|
|
|
is_user_ba=True
|
|
|
|
)
|
2019-12-26 13:17:40 +00:00
|
|
|
logger.debug("validate_result = %s" % str(validate_result))
|
2019-12-26 08:01:15 +00:00
|
|
|
if 'error' in validate_result and validate_result['error']:
|
|
|
|
messages.add_message(
|
2019-12-26 08:26:31 +00:00
|
|
|
request, messages.ERROR,
|
|
|
|
"VAT Number validation error: %s" % validate_result["error"],
|
2019-12-26 08:38:53 +00:00
|
|
|
extra_tags='error'
|
2019-12-26 08:01:15 +00:00
|
|
|
)
|
2019-12-31 17:23:32 +00:00
|
|
|
billing_address = current_billing_address
|
2019-12-31 19:58:35 +00:00
|
|
|
if billing_address:
|
|
|
|
billing_address.save()
|
2019-12-31 18:31:44 +00:00
|
|
|
email_data = {
|
|
|
|
'subject': "%s updated VAT number to %s but failed" %
|
|
|
|
(request.user.email, vat_number),
|
|
|
|
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
|
|
|
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
|
|
|
|
'body': "\n".join(
|
|
|
|
["%s=%s" % (k, v) for (k, v) in
|
|
|
|
validate_result.items()]),
|
|
|
|
}
|
2019-12-26 08:01:15 +00:00
|
|
|
else:
|
2019-12-31 18:31:44 +00:00
|
|
|
email_data = {
|
|
|
|
'subject': "%s updated VAT number to %s" % (
|
|
|
|
request.user.email, vat_number
|
|
|
|
),
|
|
|
|
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
|
|
|
'to': settings.DCL_ERROR_EMAILS_TO_LIST,
|
|
|
|
'body': "\n".join(
|
|
|
|
["%s=%s" % (k, v) for (k, v) in
|
|
|
|
validate_result.items()]),
|
|
|
|
}
|
2019-12-26 08:01:15 +00:00
|
|
|
msg = _("Billing address updated successfully")
|
|
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
2019-12-31 18:31:44 +00:00
|
|
|
send_plain_email_task.delay(email_data)
|
2019-12-26 08:01:15 +00:00
|
|
|
else:
|
|
|
|
msg = _("Billing address updated successfully")
|
|
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
2017-10-15 17:55:37 +00:00
|
|
|
else:
|
2020-12-31 21:07:52 +00:00
|
|
|
id_payment_method = request.POST.get('id_payment_method', None)
|
2017-10-15 21:37:01 +00:00
|
|
|
stripe_utils = StripeUtils()
|
2020-12-31 04:34:21 +00:00
|
|
|
card_details = stripe_utils.get_cards_details_from_payment_method(
|
|
|
|
id_payment_method
|
2017-10-15 17:55:37 +00:00
|
|
|
)
|
2017-10-15 21:37:01 +00:00
|
|
|
if not card_details.get('response_object'):
|
|
|
|
form.add_error("__all__", card_details.get('error'))
|
|
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
stripe_customer = StripeCustomer.get_or_create(
|
2020-12-31 04:34:21 +00:00
|
|
|
email=request.user.email, id_payment_method=id_payment_method
|
2017-10-15 21:37:01 +00:00
|
|
|
)
|
2017-10-28 16:29:19 +00:00
|
|
|
card = card_details['response_object']
|
2021-02-07 10:28:03 +00:00
|
|
|
acc_result = stripe_utils.associate_customer_card(
|
|
|
|
request.user.stripecustomer.stripe_id,
|
|
|
|
id_payment_method,
|
|
|
|
set_as_default=True
|
|
|
|
)
|
|
|
|
if acc_result['response_object'] is None:
|
2017-10-21 15:02:24 +00:00
|
|
|
msg = _(
|
2021-02-07 10:28:03 +00:00
|
|
|
'An error occurred while associating the card.'
|
|
|
|
' Details: {details}'.format(
|
|
|
|
details=acc_result['error']
|
|
|
|
)
|
2017-10-21 15:02:24 +00:00
|
|
|
)
|
2021-02-07 10:28:03 +00:00
|
|
|
messages.add_message(request, messages.ERROR, msg)
|
|
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
preferred = False
|
|
|
|
if stripe_customer.usercarddetail_set.count() == 0:
|
|
|
|
preferred = True
|
|
|
|
UserCardDetail.create(
|
|
|
|
stripe_customer=stripe_customer,
|
|
|
|
last4=card['last4'],
|
|
|
|
brand=card['brand'],
|
|
|
|
fingerprint=card['fingerprint'],
|
|
|
|
exp_month=card['exp_month'],
|
|
|
|
exp_year=card['exp_year'],
|
|
|
|
card_id=card['card_id'],
|
|
|
|
preferred=preferred
|
|
|
|
)
|
|
|
|
msg = _(
|
|
|
|
"Successfully associated the card with your account"
|
|
|
|
)
|
|
|
|
messages.add_message(request, messages.SUCCESS, msg)
|
2017-08-29 12:12:35 +00:00
|
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
else:
|
|
|
|
billing_address_data = form.cleaned_data
|
|
|
|
return self.form_invalid(form)
|
|
|
|
|
2017-08-24 16:41:15 +00:00
|
|
|
|
2016-05-12 06:57:34 +00:00
|
|
|
class PaymentVMView(LoginRequiredMixin, FormView):
|
2016-04-22 13:36:38 +00:00
|
|
|
template_name = 'hosting/payment.html'
|
2016-05-12 06:57:34 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-04-23 07:22:44 +00:00
|
|
|
form_class = BillingAddressForm
|
2016-04-22 13:36:38 +00:00
|
|
|
|
2017-05-11 05:11:33 +00:00
|
|
|
def get_form_kwargs(self):
|
2019-12-31 17:23:32 +00:00
|
|
|
current_billing_address = self.request.user.billing_addresses.last()
|
2017-05-11 05:11:33 +00:00
|
|
|
form_kwargs = super(PaymentVMView, self).get_form_kwargs()
|
|
|
|
if not current_billing_address:
|
|
|
|
return form_kwargs
|
|
|
|
|
|
|
|
form_kwargs.update({
|
|
|
|
'initial': {
|
2017-07-16 17:03:52 +00:00
|
|
|
'cardholder_name': current_billing_address.cardholder_name,
|
2017-05-11 05:11:33 +00:00
|
|
|
'street_address': current_billing_address.street_address,
|
|
|
|
'city': current_billing_address.city,
|
|
|
|
'postal_code': current_billing_address.postal_code,
|
|
|
|
'country': current_billing_address.country,
|
2019-12-21 03:13:34 +00:00
|
|
|
'vat_number': current_billing_address.vat_number
|
2017-05-11 05:11:33 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
return form_kwargs
|
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super(PaymentVMView, self).get_context_data(**kwargs)
|
2017-05-11 05:11:33 +00:00
|
|
|
# Get user
|
|
|
|
user = self.request.user
|
2017-10-26 13:00:54 +00:00
|
|
|
if hasattr(user, 'stripecustomer'):
|
|
|
|
stripe_customer = user.stripecustomer
|
|
|
|
else:
|
|
|
|
stripe_customer = None
|
2017-10-21 21:14:55 +00:00
|
|
|
cards_list = UserCardDetail.get_all_cards_list(
|
2017-10-26 13:00:54 +00:00
|
|
|
stripe_customer=stripe_customer
|
2017-10-21 21:14:55 +00:00
|
|
|
)
|
2016-04-22 13:36:38 +00:00
|
|
|
context.update({
|
2018-05-06 23:37:58 +00:00
|
|
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY,
|
|
|
|
'vm_pricing': VMPricing.get_vm_pricing_by_name(
|
2018-05-07 03:44:31 +00:00
|
|
|
self.request.session.get('specs', {}).get('pricing_name')
|
2018-05-07 00:41:44 +00:00
|
|
|
),
|
2017-10-21 21:14:55 +00:00
|
|
|
'cards_list': cards_list,
|
2016-04-22 13:36:38 +00:00
|
|
|
})
|
2016-05-25 06:23:32 +00:00
|
|
|
|
2016-04-22 13:36:38 +00:00
|
|
|
return context
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-05-12 05:56:35 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
2017-05-24 15:58:16 +00:00
|
|
|
if 'next' in request.session:
|
|
|
|
del request.session['next']
|
2017-10-29 22:48:33 +00:00
|
|
|
HostingUtils.clear_items_from_list(
|
|
|
|
request.session,
|
|
|
|
['token', 'card_id', 'customer', 'user']
|
|
|
|
)
|
2017-05-12 05:56:35 +00:00
|
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2016-04-23 07:22:44 +00:00
|
|
|
def post(self, request, *args, **kwargs):
|
|
|
|
form = self.get_form()
|
|
|
|
if form.is_valid():
|
2017-05-11 05:11:33 +00:00
|
|
|
# Get billing address data
|
|
|
|
billing_address_data = form.cleaned_data
|
2016-04-26 06:16:03 +00:00
|
|
|
token = form.cleaned_data.get('token')
|
2017-05-12 10:07:05 +00:00
|
|
|
owner = self.request.user
|
2017-10-26 22:45:26 +00:00
|
|
|
if token is '':
|
|
|
|
card_id = form.cleaned_data.get('card')
|
|
|
|
customer = owner.stripecustomer
|
|
|
|
try:
|
|
|
|
user_card_detail = UserCardDetail.objects.get(id=card_id)
|
2017-10-28 13:30:42 +00:00
|
|
|
if not request.user.has_perm(
|
|
|
|
'view_usercarddetail', user_card_detail
|
|
|
|
):
|
2017-10-26 22:45:26 +00:00
|
|
|
raise UserCardDetail.DoesNotExist(
|
|
|
|
_("{user} does not have permission to access the "
|
|
|
|
"card").format(user=request.user.email)
|
|
|
|
)
|
|
|
|
except UserCardDetail.DoesNotExist as e:
|
|
|
|
ex = str(e)
|
|
|
|
logger.error("Card Id: {card_id}, Exception: {ex}".format(
|
|
|
|
card_id=card_id, ex=ex
|
|
|
|
)
|
|
|
|
)
|
|
|
|
msg = _("An error occurred. Details: {}".format(ex))
|
|
|
|
messages.add_message(
|
|
|
|
self.request, messages.ERROR, msg,
|
|
|
|
extra_tags='make_charge_error'
|
|
|
|
)
|
|
|
|
return HttpResponseRedirect(
|
|
|
|
reverse('hosting:payment') + '#payment_error'
|
|
|
|
)
|
2017-10-28 13:30:42 +00:00
|
|
|
request.session['card_id'] = user_card_detail.id
|
2017-10-26 22:45:26 +00:00
|
|
|
else:
|
|
|
|
# Get or create stripe customer
|
|
|
|
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')
|
2017-10-28 13:30:42 +00:00
|
|
|
request.session['token'] = token
|
2017-09-06 07:55:18 +00:00
|
|
|
request.session['billing_address_data'] = billing_address_data
|
2019-07-01 15:00:06 +00:00
|
|
|
self.request.session['order_confirm_url'] = "{url}?{query_params}".format(
|
|
|
|
url=reverse('hosting:order-confirmation'),
|
|
|
|
query_params='page=payment')
|
|
|
|
return HttpResponseRedirect(reverse('hosting:add_ssh_key'))
|
2016-04-23 07:22:44 +00:00
|
|
|
else:
|
|
|
|
return self.form_invalid(form)
|
2016-04-27 06:54:15 +00:00
|
|
|
|
2016-04-30 18:55:55 +00:00
|
|
|
|
2019-06-25 01:10:50 +00:00
|
|
|
class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
|
|
|
|
form_class = UserHostingKeyForm
|
2016-05-03 05:59:40 +00:00
|
|
|
template_name = "hosting/order_detail.html"
|
2016-05-14 06:42:42 +00:00
|
|
|
context_object_name = "order"
|
2016-04-29 06:53:24 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-07-11 03:08:51 +00:00
|
|
|
permission_required = ['view_hostingorder']
|
2016-05-03 05:59:40 +00:00
|
|
|
model = HostingOrder
|
2016-04-27 06:54:15 +00:00
|
|
|
|
2019-06-25 01:10:50 +00:00
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super(OrdersHostingDetailView, self).get_form_kwargs()
|
|
|
|
kwargs.update({'request': self.request})
|
|
|
|
return kwargs
|
|
|
|
|
2017-12-12 08:38:08 +00:00
|
|
|
def get_object(self, queryset=None):
|
2017-12-12 11:14:39 +00:00
|
|
|
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
|
|
|
|
))
|
2017-12-12 08:38:08 +00:00
|
|
|
except HostingOrder.DoesNotExist:
|
2017-12-12 11:14:39 +00:00
|
|
|
logger.debug("HostingOrder not found for id {order_id}".format(
|
|
|
|
order_id=order_id
|
|
|
|
))
|
2017-12-12 08:38:08 +00:00
|
|
|
hosting_order_obj = None
|
|
|
|
return hosting_order_obj
|
2017-09-09 11:27:17 +00:00
|
|
|
|
2017-05-12 10:07:05 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
# Get context
|
2017-12-12 08:53:14 +00:00
|
|
|
context = super(
|
|
|
|
OrdersHostingDetailView, self
|
|
|
|
).get_context_data(**kwargs)
|
2017-05-12 10:07:05 +00:00
|
|
|
obj = self.get_object()
|
|
|
|
owner = self.request.user
|
2017-09-06 21:52:02 +00:00
|
|
|
|
2017-09-21 11:08:54 +00:00
|
|
|
if self.request.GET.get('page') == 'payment':
|
2017-07-30 15:11:11 +00:00
|
|
|
context['page_header_text'] = _('Confirm Order')
|
2019-06-25 01:10:50 +00:00
|
|
|
context['form'] = UserHostingKeyForm(request=self.request)
|
|
|
|
context['keys'] = get_all_public_keys(self.request.user)
|
2017-07-30 15:11:11 +00:00
|
|
|
else:
|
|
|
|
context['page_header_text'] = _('Invoice')
|
2017-12-12 14:43:25 +00:00
|
|
|
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
|
2017-09-06 21:52:02 +00:00
|
|
|
|
2017-09-09 11:27:17 +00:00
|
|
|
if obj is not None:
|
2018-09-25 00:20:57 +00:00
|
|
|
if obj.generic_product_id is not None:
|
2018-09-24 06:22:46 +00:00
|
|
|
# generic payment case
|
|
|
|
logger.debug("Generic payment case")
|
2018-09-26 07:10:15 +00:00
|
|
|
context['product_name'] = GenericProduct.objects.get(
|
|
|
|
id=obj.generic_product_id
|
|
|
|
).product_name
|
2018-09-24 06:22:46 +00:00
|
|
|
else:
|
|
|
|
# invoice for previous order
|
|
|
|
logger.debug("Invoice of VM order")
|
2017-09-24 20:19:00 +00:00
|
|
|
try:
|
2018-09-24 06:22:46 +00:00
|
|
|
vm_detail = VMDetail.objects.get(vm_id=obj.vm_id)
|
|
|
|
context['vm'] = vm_detail.__dict__
|
|
|
|
context['vm']['name'] = '{}-{}'.format(
|
|
|
|
context['vm']['configuration'], context['vm']['vm_id'])
|
2019-12-09 07:00:49 +00:00
|
|
|
user_vat_country = obj.billing_address.country
|
|
|
|
user_country_vat_rate = get_vat_rate_for_country(
|
|
|
|
user_vat_country)
|
|
|
|
price, vat, vat_percent, discount = get_vm_price_for_given_vat(
|
2018-04-16 02:46:43 +00:00
|
|
|
cpu=context['vm']['cores'],
|
|
|
|
ssd_size=context['vm']['disk_size'],
|
|
|
|
memory=context['vm']['memory'],
|
|
|
|
pricing_name=(obj.vm_pricing.name
|
2019-12-09 07:00:49 +00:00
|
|
|
if obj.vm_pricing else 'default'),
|
|
|
|
vat_rate= (
|
|
|
|
user_country_vat_rate * 100
|
2019-12-09 12:35:57 +00:00
|
|
|
if obj.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
|
|
|
else settings.PRE_EU_VAT_RATE
|
2019-12-09 07:00:49 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
context['vm']["after_eu_vat_intro"] = (
|
2019-12-09 12:37:19 +00:00
|
|
|
True if obj.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
2019-12-09 07:00:49 +00:00
|
|
|
else False
|
2018-04-16 02:46:43 +00:00
|
|
|
)
|
2019-12-09 07:00:49 +00:00
|
|
|
context['vm']["price"] = price
|
|
|
|
context['vm']["vat"] = vat
|
|
|
|
context['vm']["vat_percent"] = vat_percent
|
|
|
|
context['vm']["vat_country"] = user_vat_country
|
|
|
|
context['vm']["discount"] = discount
|
|
|
|
context['vm']["total_price"] = round(
|
|
|
|
price + vat - discount['amount'], 2)
|
2018-09-24 06:22:46 +00:00
|
|
|
context['subscription_end_date'] = vm_detail.end_date()
|
|
|
|
except VMDetail.DoesNotExist:
|
|
|
|
try:
|
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 09:57:18 +00:00
|
|
|
email=owner.username, password=owner.password
|
2018-09-24 06:22:46 +00:00
|
|
|
)
|
|
|
|
vm = manager.get_vm(obj.vm_id)
|
|
|
|
context['vm'] = VirtualMachineSerializer(vm).data
|
2019-12-09 09:39:05 +00:00
|
|
|
user_vat_country = obj.billing_address.country
|
|
|
|
user_country_vat_rate = get_vat_rate_for_country(
|
|
|
|
user_vat_country)
|
|
|
|
price, vat, vat_percent, discount = get_vm_price_for_given_vat(
|
2018-09-24 06:22:46 +00:00
|
|
|
cpu=context['vm']['cores'],
|
|
|
|
ssd_size=context['vm']['disk_size'],
|
|
|
|
memory=context['vm']['memory'],
|
|
|
|
pricing_name=(obj.vm_pricing.name
|
2019-12-09 09:39:05 +00:00
|
|
|
if obj.vm_pricing else 'default'),
|
|
|
|
vat_rate=(
|
|
|
|
user_country_vat_rate * 100
|
2019-12-09 12:35:57 +00:00
|
|
|
if obj.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
|
|
|
else settings.PRE_EU_VAT_RATE
|
2019-12-09 09:39:05 +00:00
|
|
|
)
|
2018-09-24 06:22:46 +00:00
|
|
|
)
|
2019-12-09 09:39:05 +00:00
|
|
|
context['vm']["after_eu_vat_intro"] = (
|
2019-12-09 12:37:19 +00:00
|
|
|
True if obj.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
2019-12-09 09:39:05 +00:00
|
|
|
else False
|
2018-09-24 06:22:46 +00:00
|
|
|
)
|
2019-12-09 09:39:05 +00:00
|
|
|
context['vm']["price"] = price
|
|
|
|
context['vm']["vat"] = vat
|
|
|
|
context['vm']["vat_percent"] = vat_percent
|
|
|
|
context['vm']["vat_country"] = user_vat_country
|
|
|
|
context['vm']["discount"] = discount
|
|
|
|
context['vm']["total_price"] = round(
|
|
|
|
price + vat - discount['amount'], 2)
|
2018-09-24 06:22:46 +00:00
|
|
|
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.')
|
|
|
|
)
|
2017-09-06 21:52:02 +00:00
|
|
|
else:
|
2017-09-21 11:52:02 +00:00
|
|
|
# new order, confirm payment
|
2017-10-28 13:30:42 +00:00
|
|
|
if 'token' in self.request.session:
|
|
|
|
token = self.request.session['token']
|
|
|
|
stripe_utils = StripeUtils()
|
|
|
|
card_details = stripe_utils.get_cards_details_from_token(
|
|
|
|
token
|
|
|
|
)
|
|
|
|
if not card_details.get('response_object'):
|
|
|
|
return HttpResponseRedirect(reverse('hosting:payment'))
|
|
|
|
card_details_response = card_details['response_object']
|
|
|
|
context['cc_last4'] = card_details_response['last4']
|
|
|
|
context['cc_brand'] = card_details_response['brand']
|
2019-07-09 13:10:41 +00:00
|
|
|
context['cc_exp_year'] = card_details_response['exp_year']
|
|
|
|
context['cc_exp_month'] = card_details_response['exp_month']
|
2017-10-28 13:30:42 +00:00
|
|
|
else:
|
|
|
|
card_id = self.request.session.get('card_id')
|
|
|
|
card_detail = UserCardDetail.objects.get(id=card_id)
|
|
|
|
context['cc_last4'] = card_detail.last4
|
|
|
|
context['cc_brand'] = card_detail.brand
|
2019-07-09 13:10:41 +00:00
|
|
|
context['cc_exp_year'] = card_detail.exp_year
|
2019-07-09 13:33:09 +00:00
|
|
|
context['cc_exp_month'] = '{:02d}'.format(card_detail.exp_month)
|
2017-09-06 21:52:02 +00:00
|
|
|
context['site_url'] = reverse('hosting:create_virtual_machine')
|
2019-12-09 09:39:05 +00:00
|
|
|
vm_specs = self.request.session.get('specs')
|
|
|
|
user_vat_country = (
|
|
|
|
self.request.session.get('billing_address_data').get("country")
|
|
|
|
)
|
|
|
|
user_country_vat_rate = get_vat_rate_for_country(user_vat_country)
|
|
|
|
price, vat, vat_percent, discount = get_vm_price_for_given_vat(
|
|
|
|
cpu=vm_specs['cpu'],
|
|
|
|
memory=vm_specs['memory'],
|
|
|
|
ssd_size=vm_specs['disk_size'],
|
|
|
|
pricing_name=vm_specs['pricing_name'],
|
|
|
|
vat_rate=user_country_vat_rate * 100
|
|
|
|
)
|
|
|
|
vm_specs["price"] = price
|
|
|
|
vm_specs["vat"] = vat
|
|
|
|
vm_specs["vat_percent"] = vat_percent
|
|
|
|
vm_specs["vat_country"] = user_vat_country
|
|
|
|
vm_specs["discount"] = discount
|
|
|
|
vm_specs["total_price"] = round(price + vat - discount['amount'],
|
|
|
|
2)
|
2019-12-09 09:49:59 +00:00
|
|
|
vm_specs["after_eu_vat_intro"] = True
|
2019-12-09 09:39:05 +00:00
|
|
|
context['vm'] = vm_specs
|
2017-05-12 17:13:18 +00:00
|
|
|
return context
|
2017-05-12 10:07:05 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-09-21 11:08:54 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
2017-09-21 14:02:22 +00:00
|
|
|
if not self.kwargs.get('pk'):
|
|
|
|
if 'specs' not in self.request.session:
|
|
|
|
return HttpResponseRedirect(
|
|
|
|
reverse('hosting:create_virtual_machine')
|
|
|
|
)
|
2018-06-12 09:14:36 +00:00
|
|
|
|
|
|
|
if ('token' not in self.request.session and
|
|
|
|
'card_id' not in self.request.session):
|
2017-09-21 14:02:22 +00:00
|
|
|
return HttpResponseRedirect(reverse('hosting:payment'))
|
2017-09-21 11:08:54 +00:00
|
|
|
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)
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-09-09 10:06:29 +00:00
|
|
|
def post(self, request):
|
|
|
|
template = request.session.get('template')
|
|
|
|
specs = request.session.get('specs')
|
2017-10-28 13:30:42 +00:00
|
|
|
stripe_utils = StripeUtils()
|
2017-10-03 16:05:08 +00:00
|
|
|
# We assume that if the user is here, his/her StripeCustomer
|
|
|
|
# object already exists
|
|
|
|
stripe_customer_id = request.user.stripecustomer.id
|
2017-09-09 10:06:29 +00:00
|
|
|
billing_address_data = request.session.get('billing_address_data')
|
|
|
|
vm_template_id = template.get('id', 1)
|
2017-10-26 15:57:11 +00:00
|
|
|
stripe_api_cus_id = request.user.stripecustomer.stripe_id
|
2020-10-11 11:15:23 +00:00
|
|
|
logger.debug("template=%s specs=%s stripe_customer_id=%s "
|
|
|
|
"billing_address_data=%s vm_template_id=%s "
|
|
|
|
"stripe_api_cus_id=%s" % (
|
|
|
|
template, specs, stripe_customer_id, billing_address_data,
|
|
|
|
vm_template_id, stripe_api_cus_id)
|
|
|
|
)
|
2020-12-31 04:34:21 +00:00
|
|
|
if 'id_payment_method' in self.request.session:
|
2017-10-28 13:30:42 +00:00
|
|
|
card_details = stripe_utils.get_cards_details_from_token(
|
|
|
|
request.session['token']
|
|
|
|
)
|
|
|
|
if not card_details.get('response_object'):
|
|
|
|
return HttpResponseRedirect(reverse('hosting:payment'))
|
|
|
|
card_details_response = card_details['response_object']
|
|
|
|
card_details_dict = {
|
|
|
|
'last4': card_details_response['last4'],
|
2017-10-29 13:45:14 +00:00
|
|
|
'brand': card_details_response['brand'],
|
|
|
|
'card_id': card_details_response['card_id']
|
2017-10-28 13:30:42 +00:00
|
|
|
}
|
2017-10-29 20:31:11 +00:00
|
|
|
ucd = UserCardDetail.get_user_card_details(
|
2017-10-28 16:29:19 +00:00
|
|
|
request.user.stripecustomer, card_details_response
|
2017-10-28 13:30:42 +00:00
|
|
|
)
|
2017-10-28 16:29:19 +00:00
|
|
|
if not ucd:
|
2017-10-29 13:45:14 +00:00
|
|
|
acc_result = stripe_utils.associate_customer_card(
|
2020-12-31 04:34:21 +00:00
|
|
|
stripe_api_cus_id, request.session['id_payment_method'],
|
2017-10-28 16:29:19 +00:00
|
|
|
set_as_default=True
|
|
|
|
)
|
2017-10-29 13:45:14 +00:00
|
|
|
if acc_result['response_object'] is None:
|
|
|
|
msg = _(
|
|
|
|
'An error occurred while associating the card.'
|
|
|
|
' Details: {details}'.format(
|
|
|
|
details=acc_result['error']
|
|
|
|
)
|
|
|
|
)
|
|
|
|
messages.add_message(self.request, messages.ERROR, msg,
|
|
|
|
extra_tags='failed_payment')
|
|
|
|
response = {
|
|
|
|
'status': False,
|
|
|
|
'redirect': "{url}#{section}".format(
|
|
|
|
url=reverse('hosting:payment'),
|
|
|
|
section='payment_error'),
|
|
|
|
'msg_title': str(_('Error.')),
|
|
|
|
'msg_body': str(
|
|
|
|
_('There was a payment related error.'
|
|
|
|
' On close of this popup, you will be redirected'
|
|
|
|
' back to the payment page.')
|
|
|
|
)
|
|
|
|
}
|
2018-07-03 19:52:44 +00:00
|
|
|
return JsonResponse(response)
|
2017-10-28 13:30:42 +00:00
|
|
|
else:
|
|
|
|
card_id = request.session.get('card_id')
|
|
|
|
user_card_detail = UserCardDetail.objects.get(id=card_id)
|
|
|
|
card_details_dict = {
|
|
|
|
'last4': user_card_detail.last4,
|
2017-10-29 13:45:14 +00:00
|
|
|
'brand': user_card_detail.brand,
|
|
|
|
'card_id': user_card_detail.card_id
|
2017-10-28 13:30:42 +00:00
|
|
|
}
|
|
|
|
if not user_card_detail.preferred:
|
2017-10-28 20:19:53 +00:00
|
|
|
UserCardDetail.set_default_card(
|
2017-10-28 13:30:42 +00:00
|
|
|
stripe_api_cus_id=stripe_api_cus_id,
|
|
|
|
stripe_source_id=user_card_detail.card_id
|
|
|
|
)
|
2017-09-09 10:06:29 +00:00
|
|
|
cpu = specs.get('cpu')
|
|
|
|
memory = specs.get('memory')
|
|
|
|
disk_size = specs.get('disk_size')
|
2020-01-23 11:07:35 +00:00
|
|
|
amount_to_be_charged = specs.get('price')
|
|
|
|
discount = specs.get('discount')
|
2018-09-05 21:26:51 +00:00
|
|
|
plan_name = StripeUtils.get_stripe_plan_name(
|
|
|
|
cpu=cpu,
|
|
|
|
memory=memory,
|
|
|
|
disk_size=disk_size,
|
|
|
|
price=amount_to_be_charged
|
|
|
|
)
|
|
|
|
stripe_plan_id = StripeUtils.get_stripe_plan_id(
|
|
|
|
cpu=cpu,
|
|
|
|
ram=memory,
|
|
|
|
ssd=disk_size,
|
|
|
|
version=1,
|
|
|
|
app='dcl',
|
|
|
|
price=amount_to_be_charged
|
|
|
|
)
|
2017-09-09 10:06:29 +00:00
|
|
|
stripe_plan = stripe_utils.get_or_create_stripe_plan(
|
|
|
|
amount=amount_to_be_charged,
|
|
|
|
name=plan_name,
|
|
|
|
stripe_plan_id=stripe_plan_id)
|
2020-01-23 11:07:35 +00:00
|
|
|
# Create StripeTaxRate if applicable to the user
|
|
|
|
stripe_tax_rate = None
|
|
|
|
if specs["vat_percent"] > 0:
|
|
|
|
try:
|
|
|
|
stripe_tax_rate = StripeTaxRate.objects.get(
|
|
|
|
description="VAT for %s" % specs["vat_country"]
|
|
|
|
)
|
|
|
|
print("Stripe Tax Rate exists")
|
|
|
|
except StripeTaxRate.DoesNotExist as dne:
|
|
|
|
print("StripeTaxRate does not exist")
|
|
|
|
tax_rate_obj = stripe.TaxRate.create(
|
|
|
|
display_name="VAT",
|
|
|
|
description="VAT for %s" % specs["vat_country"],
|
|
|
|
jurisdiction=specs["vat_country"],
|
|
|
|
percentage=specs["vat_percent"] * 100,
|
|
|
|
inclusive=False,
|
|
|
|
)
|
|
|
|
stripe_tax_rate = StripeTaxRate.objects.create(
|
|
|
|
display_name=tax_rate_obj.display_name,
|
|
|
|
description=tax_rate_obj.description,
|
|
|
|
jurisdiction=tax_rate_obj.jurisdiction,
|
|
|
|
percentage=tax_rate_obj.percentage,
|
|
|
|
inclusive=False,
|
|
|
|
tax_rate_id=tax_rate_obj.id
|
|
|
|
)
|
|
|
|
logger.debug("Created StripeTaxRate %s" %
|
|
|
|
stripe_tax_rate.tax_rate_id)
|
2017-09-09 10:06:29 +00:00
|
|
|
subscription_result = stripe_utils.subscribe_customer_to_plan(
|
2017-10-03 15:54:35 +00:00
|
|
|
stripe_api_cus_id,
|
2020-01-23 11:07:35 +00:00
|
|
|
[{"plan": stripe_plan.get('response_object').stripe_plan_id}],
|
2020-02-04 03:36:10 +00:00
|
|
|
coupon=(discount['stripe_coupon_id']
|
|
|
|
if 'name' in discount and
|
2020-03-25 13:17:00 +00:00
|
|
|
discount['name'] is not None and
|
2020-02-04 03:36:10 +00:00
|
|
|
'ipv6' in discount['name'].lower() and
|
|
|
|
discount['stripe_coupon_id']
|
|
|
|
else ""),
|
2020-01-24 08:41:55 +00:00
|
|
|
tax_rates=[stripe_tax_rate.tax_rate_id] if stripe_tax_rate else [],
|
2020-12-31 20:05:59 +00:00
|
|
|
default_payment_method=request.session['id_payment_method']
|
2020-01-23 11:07:35 +00:00
|
|
|
)
|
2017-09-09 10:06:29 +00:00
|
|
|
stripe_subscription_obj = subscription_result.get('response_object')
|
2020-10-11 10:31:55 +00:00
|
|
|
latest_invoice = stripe.Invoice.retrieve(stripe_subscription_obj.latest_invoice)
|
|
|
|
ret = stripe.PaymentIntent.confirm(
|
|
|
|
latest_invoice.payment_intent
|
|
|
|
)
|
2020-10-11 11:51:15 +00:00
|
|
|
if ret.status == 'requires_source_action' or ret.status == 'requires_action':
|
2020-10-11 10:31:55 +00:00
|
|
|
pi = stripe.PaymentIntent.retrieve(
|
|
|
|
latest_invoice.payment_intent
|
|
|
|
)
|
|
|
|
context = {
|
|
|
|
'sid': stripe_subscription_obj.id,
|
|
|
|
'payment_intent_secret': pi.client_secret,
|
|
|
|
'STRIPE_PUBLISHABLE_KEY': settings.STRIPE_API_PUBLIC_KEY,
|
|
|
|
'showSCA': True
|
|
|
|
}
|
|
|
|
return JsonResponse(context)
|
2017-09-09 10:06:29 +00:00
|
|
|
# Check if the subscription was approved and is active
|
2017-09-29 06:50:03 +00:00
|
|
|
if (stripe_subscription_obj is None or
|
2017-10-02 22:26:50 +00:00
|
|
|
stripe_subscription_obj.status != 'active'):
|
2018-06-12 06:13:48 +00:00
|
|
|
# At this point, we have created a Stripe API card and
|
2017-10-29 13:45:14 +00:00
|
|
|
# associated it with the customer; but the transaction failed
|
2018-07-07 00:03:42 +00:00
|
|
|
# due to some reason. So, we would want to dissociate this card
|
|
|
|
# here.
|
|
|
|
# ...
|
|
|
|
|
2017-09-09 10:06:29 +00:00
|
|
|
msg = subscription_result.get('error')
|
|
|
|
messages.add_message(self.request, messages.ERROR, msg,
|
|
|
|
extra_tags='failed_payment')
|
2017-09-29 23:02:59 +00:00
|
|
|
response = {
|
|
|
|
'status': False,
|
|
|
|
'redirect': "{url}#{section}".format(
|
|
|
|
url=reverse('hosting:payment'),
|
|
|
|
section='payment_error'),
|
|
|
|
'msg_title': str(_('Error.')),
|
|
|
|
'msg_body': str(
|
|
|
|
_('There was a payment related error.'
|
|
|
|
' On close of this popup, you will be redirected back to'
|
2017-10-26 15:57:11 +00:00
|
|
|
' the payment page.')
|
|
|
|
)
|
2017-09-29 23:02:59 +00:00
|
|
|
}
|
2018-04-20 14:55:24 +00:00
|
|
|
return JsonResponse(response)
|
|
|
|
|
2017-10-28 13:30:42 +00:00
|
|
|
if 'token' in request.session:
|
2017-10-28 14:48:03 +00:00
|
|
|
ucd = UserCardDetail.get_or_create_user_card_detail(
|
2017-10-28 13:30:42 +00:00
|
|
|
stripe_customer=self.request.user.stripecustomer,
|
|
|
|
card_details=card_details_response
|
|
|
|
)
|
2017-10-28 20:19:53 +00:00
|
|
|
UserCardDetail.save_default_card_local(
|
2017-10-28 14:48:03 +00:00
|
|
|
self.request.user.stripecustomer.stripe_id,
|
2017-10-28 16:29:19 +00:00
|
|
|
ucd.card_id
|
2017-10-28 14:48:03 +00:00
|
|
|
)
|
2017-09-09 10:06:29 +00:00
|
|
|
user = {
|
|
|
|
'name': self.request.user.name,
|
2017-09-09 15:16:43 +00:00
|
|
|
'email': self.request.user.email,
|
2020-03-05 10:25:44 +00:00
|
|
|
'username': self.request.user.username,
|
2017-09-16 14:00:31 +00:00
|
|
|
'pass': self.request.user.password,
|
|
|
|
'request_scheme': request.scheme,
|
|
|
|
'request_host': request.get_host(),
|
2017-09-16 15:25:37 +00:00
|
|
|
'language': get_language(),
|
2017-09-09 10:06:29 +00:00
|
|
|
}
|
2017-09-09 11:27:17 +00:00
|
|
|
|
2018-04-20 14:55:24 +00:00
|
|
|
create_vm(
|
|
|
|
billing_address_data, stripe_customer_id, specs,
|
|
|
|
stripe_subscription_obj, card_details_dict, request,
|
|
|
|
vm_template_id, template, user
|
2018-04-18 21:50:52 +00:00
|
|
|
)
|
2017-09-15 15:34:23 +00:00
|
|
|
|
|
|
|
response = {
|
|
|
|
'status': True,
|
|
|
|
'redirect': reverse('hosting:virtual_machines'),
|
2017-09-16 12:24:05 +00:00
|
|
|
'msg_title': str(_('Thank you for the order.')),
|
2017-09-24 07:34:30 +00:00
|
|
|
'msg_body': str(
|
|
|
|
_('Your VM will be up and running in a few moments.'
|
|
|
|
' We will send you a confirmation email as soon as'
|
|
|
|
' it is ready.'))
|
2017-09-15 15:34:23 +00:00
|
|
|
}
|
|
|
|
|
2018-04-20 14:55:24 +00:00
|
|
|
return JsonResponse(response)
|
2017-09-09 10:06:29 +00:00
|
|
|
|
2017-05-11 05:11:33 +00:00
|
|
|
|
2016-05-03 05:59:40 +00:00
|
|
|
class OrdersHostingListView(LoginRequiredMixin, ListView):
|
2016-04-29 06:53:24 +00:00
|
|
|
template_name = "hosting/orders.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-05-03 05:59:40 +00:00
|
|
|
context_object_name = "orders"
|
|
|
|
model = HostingOrder
|
|
|
|
paginate_by = 10
|
2016-05-14 06:42:42 +00:00
|
|
|
ordering = '-id'
|
2016-04-29 06:53:24 +00:00
|
|
|
|
2016-05-03 05:59:40 +00:00
|
|
|
def get_queryset(self):
|
2016-04-29 06:53:24 +00:00
|
|
|
user = self.request.user
|
2016-05-03 05:59:40 +00:00
|
|
|
self.queryset = HostingOrder.objects.filter(customer__user=user)
|
|
|
|
return super(OrdersHostingListView, self).get_queryset()
|
2016-04-29 06:53:24 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-12-27 08:09:54 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
return super(OrdersHostingListView, self).get(request, *args, **kwargs)
|
|
|
|
|
2016-05-24 06:19:49 +00:00
|
|
|
|
2020-01-20 06:37:32 +00:00
|
|
|
class InvoiceListView(LoginRequiredMixin, TemplateView):
|
2019-04-03 18:31:54 +00:00
|
|
|
template_name = "hosting/invoices.html"
|
2019-04-03 19:16:19 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2019-04-03 18:31:54 +00:00
|
|
|
context_object_name = "invoices"
|
2019-04-04 22:09:52 +00:00
|
|
|
paginate_by = 10
|
2019-04-03 18:31:54 +00:00
|
|
|
ordering = '-created'
|
|
|
|
|
2019-04-04 21:14:24 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
2020-01-20 06:37:32 +00:00
|
|
|
page = self.request.GET.get('page', 1)
|
2019-04-04 21:14:24 +00:00
|
|
|
context = super(InvoiceListView, self).get_context_data(**kwargs)
|
2020-01-20 06:37:32 +00:00
|
|
|
invs_page = None
|
2020-04-01 11:39:02 +00:00
|
|
|
invs_page_charges = None
|
2019-04-12 06:14:52 +00:00
|
|
|
if ('user_email' in self.request.GET
|
|
|
|
and self.request.user.email == settings.ADMIN_EMAIL):
|
2019-04-12 06:41:05 +00:00
|
|
|
user_email = self.request.GET['user_email']
|
2020-12-01 11:43:09 +00:00
|
|
|
context['user_email'] = '%s' % quote(user_email)
|
2019-04-12 06:41:05 +00:00
|
|
|
logger.debug(
|
|
|
|
"user_email = {}".format(user_email)
|
2019-04-12 06:14:52 +00:00
|
|
|
)
|
2019-04-12 06:41:05 +00:00
|
|
|
try:
|
2019-04-12 07:10:19 +00:00
|
|
|
cu = CustomUser.objects.get(email=user_email)
|
2019-04-12 06:41:05 +00:00
|
|
|
except CustomUser.DoesNotExist as dne:
|
|
|
|
logger.debug("User does not exist")
|
|
|
|
cu = self.request.user
|
2020-01-20 06:37:32 +00:00
|
|
|
invs = stripe.Invoice.list(customer=cu.stripecustomer.stripe_id,
|
2021-03-29 01:53:58 +00:00
|
|
|
count=100,
|
|
|
|
status='paid')
|
2020-01-20 06:37:32 +00:00
|
|
|
paginator = Paginator(invs.data, 10)
|
|
|
|
try:
|
|
|
|
invs_page = paginator.page(page)
|
|
|
|
except PageNotAnInteger:
|
|
|
|
invs_page = paginator.page(1)
|
|
|
|
except EmptyPage:
|
|
|
|
invs_page = paginator.page(paginator.num_pages)
|
2020-04-01 11:39:02 +00:00
|
|
|
hosting_orders = HostingOrder.objects.filter(
|
|
|
|
customer=cu.stripecustomer).filter(
|
|
|
|
Q(subscription_id=None) | Q(subscription_id='')
|
2020-06-22 14:34:01 +00:00
|
|
|
).order_by('-created_at')
|
2020-04-01 11:39:02 +00:00
|
|
|
stripe_chgs = []
|
|
|
|
for ho in hosting_orders:
|
|
|
|
stripe_chgs.append({ho.id: stripe.Charge.retrieve(ho.stripe_charge_id)})
|
|
|
|
|
|
|
|
paginator_charges = Paginator(stripe_chgs, 10)
|
|
|
|
try:
|
|
|
|
invs_page_charges = paginator_charges.page(page)
|
|
|
|
except PageNotAnInteger:
|
|
|
|
invs_page_charges = paginator_charges.page(1)
|
|
|
|
except EmptyPage:
|
|
|
|
invs_page_charges = paginator_charges.page(paginator_charges.num_pages)
|
2019-04-12 06:14:52 +00:00
|
|
|
else:
|
2019-04-04 21:14:24 +00:00
|
|
|
try:
|
2020-01-20 06:37:32 +00:00
|
|
|
invs = stripe.Invoice.list(
|
|
|
|
customer=self.request.user.stripecustomer.stripe_id,
|
|
|
|
count=100
|
|
|
|
)
|
|
|
|
paginator = Paginator(invs.data, 10)
|
|
|
|
try:
|
|
|
|
invs_page = paginator.page(page)
|
|
|
|
except PageNotAnInteger:
|
|
|
|
invs_page = paginator.page(1)
|
|
|
|
except EmptyPage:
|
|
|
|
invs_page = paginator.page(paginator.num_pages)
|
2020-04-01 11:39:02 +00:00
|
|
|
hosting_orders = HostingOrder.objects.filter(
|
|
|
|
customer=self.request.user.stripecustomer).filter(
|
|
|
|
Q(subscription_id=None) | Q(subscription_id='')
|
2020-06-22 14:34:01 +00:00
|
|
|
).order_by('-created_at')
|
2020-04-01 11:39:02 +00:00
|
|
|
stripe_chgs = []
|
|
|
|
for ho in hosting_orders:
|
|
|
|
stripe_chgs.append(
|
2020-04-07 13:51:47 +00:00
|
|
|
{ho: stripe.Charge.retrieve(ho.stripe_charge_id)})
|
2020-04-01 11:39:02 +00:00
|
|
|
paginator_charges = Paginator(stripe_chgs, 10)
|
|
|
|
try:
|
|
|
|
invs_page_charges = paginator_charges.page(page)
|
|
|
|
except PageNotAnInteger:
|
|
|
|
invs_page_charges = paginator_charges.page(1)
|
|
|
|
except EmptyPage:
|
|
|
|
invs_page_charges = paginator_charges.page(
|
|
|
|
paginator_charges.num_pages)
|
2020-01-20 06:37:32 +00:00
|
|
|
except Exception as ex:
|
|
|
|
logger.error(str(ex))
|
|
|
|
invs_page = None
|
|
|
|
context["invs"] = invs_page
|
2020-04-01 11:39:02 +00:00
|
|
|
context["invs_charge"] = invs_page_charges
|
2019-04-04 21:14:24 +00:00
|
|
|
return context
|
|
|
|
|
2019-04-03 18:31:54 +00:00
|
|
|
@method_decorator(decorators)
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
return super(InvoiceListView, self).get(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
2019-04-03 19:52:07 +00:00
|
|
|
class InvoiceDetailView(LoginRequiredMixin, DetailView):
|
2019-04-03 20:49:25 +00:00
|
|
|
template_name = "hosting/invoice_detail.html"
|
2019-04-03 19:52:07 +00:00
|
|
|
context_object_name = "invoice"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
permission_required = ['view_monthlyhostingbill']
|
2019-04-13 13:43:27 +00:00
|
|
|
# model = MonthlyHostingBill
|
2019-04-03 19:52:07 +00:00
|
|
|
|
|
|
|
def get_object(self, queryset=None):
|
|
|
|
invoice_id = self.kwargs.get('invoice_id')
|
|
|
|
try:
|
2019-04-03 21:54:52 +00:00
|
|
|
invoice_obj = MonthlyHostingBill.objects.get(
|
|
|
|
invoice_number=invoice_id
|
|
|
|
)
|
2019-04-03 19:52:07 +00:00
|
|
|
logger.debug("Found MHB for id {invoice_id}".format(
|
|
|
|
invoice_id=invoice_id
|
|
|
|
))
|
|
|
|
if self.request.user.has_perm(
|
|
|
|
self.permission_required[0], invoice_obj
|
|
|
|
) or self.request.user.email == settings.ADMIN_EMAIL:
|
|
|
|
logger.debug("User has permission to invoice_obj")
|
|
|
|
else:
|
|
|
|
logger.error("User does not have permission to access")
|
|
|
|
invoice_obj = None
|
2019-04-03 21:54:52 +00:00
|
|
|
except MonthlyHostingBill.DoesNotExist as dne:
|
2019-04-03 19:52:07 +00:00
|
|
|
logger.debug("MHB not found for id {invoice_id}".format(
|
|
|
|
invoice_id=invoice_id
|
|
|
|
))
|
|
|
|
invoice_obj = None
|
|
|
|
return invoice_obj
|
|
|
|
|
2019-04-03 20:49:45 +00:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
# Get context
|
|
|
|
context = super(InvoiceDetailView, self).get_context_data(**kwargs)
|
|
|
|
obj = self.get_object()
|
|
|
|
|
|
|
|
if obj is not None:
|
|
|
|
vm_id = obj.get_vm_id()
|
|
|
|
try:
|
|
|
|
# Try to get vm details from database
|
|
|
|
vm_detail = VMDetail.objects.get(vm_id=vm_id)
|
|
|
|
context['vm'] = vm_detail.__dict__
|
|
|
|
context['vm']['name'] = '{}-{}'.format(
|
|
|
|
context['vm']['configuration'], context['vm']['vm_id'])
|
2019-12-09 09:12:05 +00:00
|
|
|
user_vat_country = obj.order.billing_address.country
|
|
|
|
user_country_vat_rate = get_vat_rate_for_country(
|
|
|
|
user_vat_country)
|
|
|
|
price, vat, vat_percent, discount = get_vm_price_for_given_vat(
|
2019-04-03 20:49:45 +00:00
|
|
|
cpu=context['vm']['cores'],
|
|
|
|
ssd_size=context['vm']['disk_size'],
|
|
|
|
memory=context['vm']['memory'],
|
2019-12-09 09:14:31 +00:00
|
|
|
pricing_name=(obj.order.vm_pricing.name
|
|
|
|
if obj.order.vm_pricing else 'default'),
|
2019-12-09 09:12:05 +00:00
|
|
|
vat_rate=(
|
|
|
|
user_country_vat_rate * 100
|
2019-12-09 12:35:57 +00:00
|
|
|
if obj.order.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
|
|
|
else settings.PRE_EU_VAT_RATE
|
2019-12-09 09:12:05 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
context['vm']["after_eu_vat_intro"] = (
|
2019-12-09 12:37:19 +00:00
|
|
|
True if obj.order.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
2019-12-09 09:12:05 +00:00
|
|
|
else False
|
2019-04-03 20:49:45 +00:00
|
|
|
)
|
2019-12-09 09:12:05 +00:00
|
|
|
context['vm']["price"] = price
|
|
|
|
context['vm']["vat"] = vat
|
|
|
|
context['vm']["vat_percent"] = vat_percent
|
|
|
|
context['vm']["vat_country"] = user_vat_country
|
|
|
|
context['vm']["discount"] = discount
|
|
|
|
context['vm']["total_price"] = round(
|
|
|
|
price + vat - discount['amount'], 2)
|
2019-04-03 20:49:45 +00:00
|
|
|
except VMDetail.DoesNotExist:
|
|
|
|
# fallback to get it from the infrastructure
|
|
|
|
try:
|
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 12:47:14 +00:00
|
|
|
email=self.request.user.username,
|
2019-04-20 16:52:34 +00:00
|
|
|
password=self.request.user.password
|
2019-04-03 20:49:45 +00:00
|
|
|
)
|
|
|
|
vm = manager.get_vm(vm_id)
|
|
|
|
context['vm'] = VirtualMachineSerializer(vm).data
|
2019-12-09 12:37:46 +00:00
|
|
|
user_vat_country = obj.order.billing_address.country
|
|
|
|
user_country_vat_rate = get_vat_rate_for_country(
|
|
|
|
user_vat_country)
|
|
|
|
price, vat, vat_percent, discount = get_vm_price_for_given_vat(
|
2019-04-03 20:49:45 +00:00
|
|
|
cpu=context['vm']['cores'],
|
|
|
|
ssd_size=context['vm']['disk_size'],
|
|
|
|
memory=context['vm']['memory'],
|
2019-04-03 21:07:37 +00:00
|
|
|
pricing_name=(obj.order.vm_pricing.name
|
2019-12-09 12:35:57 +00:00
|
|
|
if obj.order.vm_pricing else 'default'),
|
|
|
|
vat_rate=(
|
|
|
|
user_country_vat_rate * 100
|
|
|
|
if obj.order.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
|
|
|
else settings.PRE_EU_VAT_RATE
|
|
|
|
)
|
2019-04-03 20:49:45 +00:00
|
|
|
)
|
2019-12-09 12:37:19 +00:00
|
|
|
context['vm']["after_eu_vat_intro"] = (
|
|
|
|
True if obj.order.vm_id >= settings.FIRST_VM_ID_AFTER_EU_VAT
|
|
|
|
else False
|
2019-04-03 20:49:45 +00:00
|
|
|
)
|
2019-12-09 12:37:46 +00:00
|
|
|
context['vm']["price"] = price
|
|
|
|
context['vm']["vat"] = vat
|
|
|
|
context['vm']["vat_percent"] = vat_percent
|
|
|
|
context['vm']["vat_country"] = user_vat_country
|
|
|
|
context['vm']["discount"] = discount
|
|
|
|
context['vm']["total_price"] = round(
|
|
|
|
price + vat - discount['amount'], 2)
|
2019-11-15 15:40:12 +00:00
|
|
|
except TypeError:
|
|
|
|
logger.error("Type error. Probably we "
|
|
|
|
"came from a generic product. "
|
|
|
|
"Invoice ID %s" % obj.invoice_id)
|
2019-04-03 20:49:45 +00:00
|
|
|
except WrongIdError:
|
|
|
|
logger.error("WrongIdError while accessing "
|
|
|
|
"invoice {}".format(obj.invoice_id))
|
|
|
|
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'
|
|
|
|
return context
|
|
|
|
|
2019-04-03 21:24:56 +00:00
|
|
|
# add context params from monthly hosting bill
|
2019-04-13 13:43:27 +00:00
|
|
|
context['period_start'] = obj.get_period_start()
|
|
|
|
context['period_end'] = obj.get_period_end()
|
2019-04-03 21:24:56 +00:00
|
|
|
context['paid_at'] = obj.paid_at
|
|
|
|
context['total_in_chf'] = obj.total_in_chf()
|
|
|
|
context['invoice_number'] = obj.invoice_number
|
|
|
|
context['discount_on_stripe'] = obj.discount_in_chf()
|
2019-04-20 13:20:55 +00:00
|
|
|
if obj.lines_data_count > 1:
|
|
|
|
# special case, we pass the details of each of the line items
|
|
|
|
context['line_items'] = obj.hostingbilllineitem_set.all()
|
2019-04-03 21:24:56 +00:00
|
|
|
return context
|
2019-04-03 20:49:45 +00:00
|
|
|
else:
|
|
|
|
raise Http404
|
|
|
|
|
|
|
|
@method_decorator(decorators)
|
|
|
|
def get(self, request, *args, **kwargs):
|
2019-04-03 21:04:35 +00:00
|
|
|
self.object = self.get_object()
|
2019-04-03 20:59:01 +00:00
|
|
|
context = self.get_context_data(object=self.get_object())
|
2019-04-03 20:49:45 +00:00
|
|
|
return self.render_to_response(context)
|
|
|
|
|
2019-04-03 19:52:07 +00:00
|
|
|
|
2016-05-24 06:19:49 +00:00
|
|
|
class OrdersHostingDeleteView(LoginRequiredMixin, DeleteView):
|
2016-05-29 18:37:43 +00:00
|
|
|
login_url = reverse_lazy('hosting:login')
|
2016-05-18 22:58:28 +00:00
|
|
|
success_url = reverse_lazy('hosting:orders')
|
|
|
|
model = HostingOrder
|
2016-04-29 06:53:24 +00:00
|
|
|
|
2016-05-29 18:37:43 +00:00
|
|
|
|
2016-05-03 05:59:40 +00:00
|
|
|
class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
|
|
|
|
template_name = "hosting/virtual_machines.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
context_object_name = "vms"
|
|
|
|
paginate_by = 10
|
2016-05-14 06:42:42 +00:00
|
|
|
ordering = '-id'
|
2016-04-29 06:53:24 +00:00
|
|
|
|
2016-05-03 05:59:40 +00:00
|
|
|
def get_queryset(self):
|
2024-02-17 10:20:12 +00:00
|
|
|
username = self.request.GET.get('username')
|
|
|
|
if self.request.user.is_admin and username:
|
|
|
|
user = CustomUser.objects.get(username=username)
|
|
|
|
else:
|
|
|
|
user = self.request.user
|
|
|
|
owner = user
|
2020-03-05 09:57:18 +00:00
|
|
|
manager = OpenNebulaManager(email=owner.username,
|
2017-05-13 03:50:56 +00:00
|
|
|
password=owner.password)
|
2017-05-14 10:22:10 +00:00
|
|
|
try:
|
|
|
|
queryset = manager.get_vms()
|
|
|
|
serializer = VirtualMachineSerializer(queryset, many=True)
|
|
|
|
return serializer.data
|
|
|
|
except ConnectionRefusedError:
|
2017-05-25 09:27:49 +00:00
|
|
|
messages.error(self.request,
|
|
|
|
'We could not load your VMs due to a backend connection \
|
2017-05-14 10:22:10 +00:00
|
|
|
error. Please try again in a few minutes'
|
2017-05-25 09:27:49 +00:00
|
|
|
)
|
2017-05-14 10:22:10 +00:00
|
|
|
|
|
|
|
self.kwargs['error'] = 'connection'
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
error = self.kwargs.get('error')
|
|
|
|
if error is not None:
|
|
|
|
print(error)
|
2017-05-25 09:27:49 +00:00
|
|
|
context = {'error': 'connection'}
|
2017-05-14 10:22:10 +00:00
|
|
|
else:
|
|
|
|
context = super(ListView, self).get_context_data(**kwargs)
|
2017-09-23 19:38:15 +00:00
|
|
|
if UserHostingKey.objects.filter(user=self.request.user).exists():
|
|
|
|
context['show_create_ssh_key_msg'] = False
|
|
|
|
else:
|
|
|
|
context['show_create_ssh_key_msg'] = True
|
2017-05-14 10:22:10 +00:00
|
|
|
return context
|
2016-05-04 05:16:41 +00:00
|
|
|
|
|
|
|
|
2017-05-04 04:19:32 +00:00
|
|
|
class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
|
|
|
template_name = "hosting/create_virtual_machine.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
|
2017-09-15 14:33:52 +00:00
|
|
|
def validate_cores(self, value):
|
|
|
|
if (value > 48) or (value < 1):
|
|
|
|
raise ValidationError(_('Invalid number of cores'))
|
|
|
|
|
|
|
|
def validate_memory(self, value):
|
2018-10-17 07:29:06 +00:00
|
|
|
if 'pid' in self.request.POST:
|
|
|
|
try:
|
|
|
|
plugin = DCLCalculatorPluginModel.objects.get(
|
|
|
|
id=self.request.POST['pid']
|
|
|
|
)
|
|
|
|
except DCLCalculatorPluginModel.DoesNotExist as dne:
|
|
|
|
logger.error(
|
|
|
|
str(dne) + " plugin_id: " + self.request.POST['pid']
|
|
|
|
)
|
|
|
|
raise ValidationError(_('Invalid calculator properties'))
|
|
|
|
if plugin.enable_512mb_ram:
|
|
|
|
if value % 1 == 0 or value == 0.5:
|
|
|
|
logger.debug(
|
|
|
|
"Given ram {value} is either 0.5 or a"
|
|
|
|
" whole number".format(value=value)
|
|
|
|
)
|
|
|
|
if (value > 200) or (value < 0.5):
|
|
|
|
raise ValidationError(_('Invalid RAM size'))
|
|
|
|
else:
|
|
|
|
raise ValidationError(_('Invalid RAM size'))
|
|
|
|
elif (value > 200) or (value < 1) or (value % 1 != 0):
|
|
|
|
raise ValidationError(_('Invalid RAM size'))
|
|
|
|
else:
|
2017-09-15 14:33:52 +00:00
|
|
|
raise ValidationError(_('Invalid RAM size'))
|
|
|
|
|
|
|
|
def validate_storage(self, value):
|
|
|
|
if (value > 2000) or (value < 10):
|
|
|
|
raise ValidationError(_('Invalid storage size'))
|
2017-05-12 05:56:35 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-09-15 14:33:52 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
2018-04-25 09:22:25 +00:00
|
|
|
context = {
|
|
|
|
'templates': VMTemplate.objects.all(),
|
|
|
|
'cms_integration': get_cms_integration('default'),
|
|
|
|
}
|
2017-09-15 14:33:52 +00:00
|
|
|
return render(request, self.template_name, context)
|
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-09-15 14:33:52 +00:00
|
|
|
def post(self, request):
|
|
|
|
cores = request.POST.get('cpu')
|
|
|
|
cores_field = forms.IntegerField(validators=[self.validate_cores])
|
|
|
|
memory = request.POST.get('ram')
|
2018-10-17 07:29:06 +00:00
|
|
|
memory_field = forms.FloatField(validators=[self.validate_memory])
|
2017-09-15 14:33:52 +00:00
|
|
|
storage = request.POST.get('storage')
|
|
|
|
storage_field = forms.IntegerField(validators=[self.validate_storage])
|
|
|
|
template_id = int(request.POST.get('config'))
|
2018-04-25 10:25:58 +00:00
|
|
|
pricing_name = request.POST.get('pricing_name')
|
|
|
|
vm_pricing = VMPricing.get_vm_pricing_by_name(pricing_name)
|
2017-09-15 14:33:52 +00:00
|
|
|
template = VMTemplate.objects.filter(
|
|
|
|
opennebula_vm_template_id=template_id).first()
|
|
|
|
template_data = VMTemplateSerializer(template).data
|
2017-05-12 05:56:35 +00:00
|
|
|
|
2018-04-25 10:25:58 +00:00
|
|
|
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
|
|
|
|
|
2017-05-14 10:22:10 +00:00
|
|
|
try:
|
2017-09-15 14:33:52 +00:00
|
|
|
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')
|
2018-04-25 10:25:58 +00:00
|
|
|
return redirect(CreateVirtualMachinesView.as_view())
|
2017-05-14 10:22:10 +00:00
|
|
|
|
2017-09-15 14:33:52 +00:00
|
|
|
try:
|
|
|
|
memory = memory_field.clean(memory)
|
|
|
|
except ValidationError as err:
|
|
|
|
msg = '{} : {}.'.format(memory, str(err))
|
|
|
|
messages.add_message(self.request, messages.ERROR, msg,
|
|
|
|
extra_tags='memory')
|
2018-04-25 10:25:58 +00:00
|
|
|
return redirect(CreateVirtualMachinesView.as_view())
|
2017-05-12 17:13:18 +00:00
|
|
|
|
2017-09-15 14:33:52 +00:00
|
|
|
try:
|
|
|
|
storage = storage_field.clean(storage)
|
|
|
|
except ValidationError as err:
|
|
|
|
msg = '{} : {}.'.format(storage, str(err))
|
|
|
|
messages.add_message(self.request, messages.ERROR, msg,
|
|
|
|
extra_tags='storage')
|
2018-04-25 10:25:58 +00:00
|
|
|
return redirect(CreateVirtualMachinesView.as_view())
|
|
|
|
|
2018-05-07 00:41:44 +00:00
|
|
|
price, vat, vat_percent, discount = get_vm_price_with_vat(
|
2018-04-25 10:25:58 +00:00
|
|
|
cpu=cores,
|
|
|
|
memory=memory,
|
|
|
|
ssd_size=storage,
|
|
|
|
pricing_name=vm_pricing_name
|
|
|
|
)
|
|
|
|
|
2017-09-15 14:33:52 +00:00
|
|
|
specs = {
|
|
|
|
'cpu': cores,
|
|
|
|
'memory': memory,
|
|
|
|
'disk_size': storage,
|
2018-05-11 12:17:27 +00:00
|
|
|
'discount': discount,
|
2018-04-25 10:25:58 +00:00
|
|
|
'price': price,
|
|
|
|
'vat': vat,
|
|
|
|
'vat_percent': vat_percent,
|
2018-10-18 04:58:18 +00:00
|
|
|
'total_price': round(price + vat - discount['amount'], 2),
|
2018-04-25 10:25:58 +00:00
|
|
|
'pricing_name': vm_pricing_name
|
2017-09-15 14:33:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
request.session['specs'] = specs
|
|
|
|
request.session['template'] = template_data
|
2017-05-04 04:19:32 +00:00
|
|
|
return redirect(reverse('hosting:payment'))
|
|
|
|
|
|
|
|
|
2017-05-12 10:07:05 +00:00
|
|
|
class VirtualMachineView(LoginRequiredMixin, View):
|
2016-05-04 05:16:41 +00:00
|
|
|
template_name = "hosting/virtual_machine_detail.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
2017-05-09 00:02:29 +00:00
|
|
|
|
2017-05-12 05:56:35 +00:00
|
|
|
def get_object(self):
|
2024-01-26 03:41:57 +00:00
|
|
|
username = self.request.GET.get('username')
|
|
|
|
if self.request.user.is_admin and username:
|
|
|
|
owner = CustomUser.objects.get(username=username)
|
|
|
|
else:
|
|
|
|
owner = self.request.user
|
2017-05-12 17:13:18 +00:00
|
|
|
vm = None
|
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 09:57:18 +00:00
|
|
|
email=owner.username,
|
2017-05-13 03:50:56 +00:00
|
|
|
password=owner.password
|
2017-05-12 17:13:18 +00:00
|
|
|
)
|
2017-05-09 02:49:40 +00:00
|
|
|
vm_id = self.kwargs.get('pk')
|
2017-05-12 05:56:35 +00:00
|
|
|
try:
|
2017-05-12 10:07:05 +00:00
|
|
|
vm = manager.get_vm(vm_id)
|
2017-05-14 10:22:10 +00:00
|
|
|
return vm
|
2017-06-17 09:16:11 +00:00
|
|
|
except WrongIdError:
|
|
|
|
messages.error(self.request,
|
|
|
|
_('We could not find the requested VM. Please \
|
|
|
|
contact Data Center Light Support.')
|
|
|
|
)
|
|
|
|
return None
|
2017-05-14 10:22:10 +00:00
|
|
|
except ConnectionRefusedError:
|
2017-05-25 09:27:49 +00:00
|
|
|
messages.error(self.request,
|
|
|
|
'We could not load your VM due to a backend connection \
|
2017-05-14 10:22:10 +00:00
|
|
|
error. Please try again in a few minutes'
|
2017-05-25 09:27:49 +00:00
|
|
|
)
|
2017-05-14 10:22:10 +00:00
|
|
|
return None
|
2017-05-12 05:56:35 +00:00
|
|
|
except Exception as error:
|
2017-12-12 21:55:30 +00:00
|
|
|
logger.error(str(error))
|
2017-05-12 05:56:35 +00:00
|
|
|
raise Http404()
|
|
|
|
|
|
|
|
def get_success_url(self):
|
|
|
|
final_url = reverse('hosting:virtual_machines')
|
|
|
|
return final_url
|
2016-06-10 04:50:49 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-05-09 00:02:29 +00:00
|
|
|
def get(self, request, *args, **kwargs):
|
2017-05-12 17:13:18 +00:00
|
|
|
vm = self.get_object()
|
2017-06-17 09:16:11 +00:00
|
|
|
if vm is None:
|
2017-09-15 11:16:56 +00:00
|
|
|
if self.request.is_ajax():
|
2017-09-15 11:27:17 +00:00
|
|
|
storage = messages.get_messages(request)
|
2017-09-15 12:08:52 +00:00
|
|
|
for m in storage:
|
|
|
|
pass
|
2017-09-15 11:27:17 +00:00
|
|
|
storage.used = True
|
2018-04-20 14:55:24 +00:00
|
|
|
return JsonResponse({'text': ugettext('Terminated')})
|
2017-09-15 11:16:56 +00:00
|
|
|
else:
|
|
|
|
return redirect(reverse('hosting:virtual_machines'))
|
2017-09-15 11:50:42 +00:00
|
|
|
elif self.request.is_ajax():
|
|
|
|
return HttpResponse()
|
2017-09-29 19:53:02 +00:00
|
|
|
context = None
|
2017-05-25 09:27:49 +00:00
|
|
|
try:
|
2017-05-14 10:22:10 +00:00
|
|
|
serializer = VirtualMachineSerializer(vm)
|
2020-02-01 06:43:56 +00:00
|
|
|
hosting_order = HostingOrder.objects.get(
|
|
|
|
vm_id=serializer.data['vm_id']
|
|
|
|
)
|
|
|
|
inv_url = None
|
|
|
|
if hosting_order.subscription_id:
|
|
|
|
stripe_obj = stripe.Invoice.list(
|
2020-02-01 06:53:47 +00:00
|
|
|
subscription=hosting_order.subscription_id,
|
2020-02-01 07:45:03 +00:00
|
|
|
count=1
|
2020-02-01 06:43:56 +00:00
|
|
|
)
|
2024-01-26 03:41:57 +00:00
|
|
|
if stripe_obj.data:
|
|
|
|
inv_url = stripe_obj.data[0].hosted_invoice_url
|
|
|
|
else:
|
|
|
|
inv_url = ''
|
2020-02-01 06:43:56 +00:00
|
|
|
elif hosting_order.stripe_charge_id:
|
|
|
|
stripe_obj = stripe.Charge.retrieve(
|
|
|
|
hosting_order.stripe_charge_id
|
|
|
|
)
|
|
|
|
inv_url = stripe_obj.receipt_url
|
2017-05-14 10:22:10 +00:00
|
|
|
context = {
|
|
|
|
'virtual_machine': serializer.data,
|
2020-02-01 06:43:56 +00:00
|
|
|
'order': hosting_order,
|
|
|
|
'keys': UserHostingKey.objects.filter(user=request.user),
|
|
|
|
'inv_url': inv_url
|
2017-05-14 10:22:10 +00:00
|
|
|
}
|
2020-02-01 06:43:56 +00:00
|
|
|
|
2017-08-31 16:20:02 +00:00
|
|
|
except Exception as ex:
|
|
|
|
logger.debug("Exception generated {}".format(str(ex)))
|
2017-09-29 19:53:02 +00:00
|
|
|
messages.error(self.request,
|
|
|
|
_('We could not find the requested VM. Please '
|
|
|
|
'contact Data Center Light Support.')
|
|
|
|
)
|
|
|
|
return redirect(reverse('hosting:virtual_machines'))
|
2017-05-14 10:22:10 +00:00
|
|
|
|
2017-05-09 00:02:29 +00:00
|
|
|
return render(request, self.template_name, context)
|
2016-06-10 04:50:49 +00:00
|
|
|
|
2018-03-09 18:49:40 +00:00
|
|
|
@method_decorator(decorators)
|
2017-05-12 05:56:35 +00:00
|
|
|
def post(self, request, *args, **kwargs):
|
2017-09-15 11:16:56 +00:00
|
|
|
response = {'status': False}
|
2017-12-20 19:59:46 +00:00
|
|
|
admin_email_body = {}
|
2017-05-12 17:13:18 +00:00
|
|
|
owner = self.request.user
|
2016-06-10 04:50:49 +00:00
|
|
|
vm = self.get_object()
|
2017-05-12 05:56:35 +00:00
|
|
|
|
2017-05-12 17:13:18 +00:00
|
|
|
manager = OpenNebulaManager(
|
2020-03-05 09:57:18 +00:00
|
|
|
email=owner.username,
|
2017-05-13 03:50:56 +00:00
|
|
|
password=owner.password
|
2017-05-12 17:13:18 +00:00
|
|
|
)
|
2017-09-18 16:22:59 +00:00
|
|
|
try:
|
|
|
|
vm_data = VirtualMachineSerializer(manager.get_vm(vm.id)).data
|
2017-09-26 22:11:18 +00:00
|
|
|
vm_name = vm_data.get('name')
|
2017-12-20 19:59:46 +00:00
|
|
|
except WrongIdError as wrong_id_err:
|
|
|
|
logger.error(str(wrong_id_err))
|
2017-09-20 20:56:42 +00:00
|
|
|
return redirect(reverse('hosting:virtual_machines'))
|
2016-06-10 04:50:49 +00:00
|
|
|
|
2017-12-20 19:59:46 +00:00
|
|
|
# Cancel Stripe subscription
|
2017-12-17 19:20:42 +00:00
|
|
|
stripe_utils = StripeUtils()
|
2019-09-24 05:04:04 +00:00
|
|
|
hosting_order = None
|
2020-01-02 06:37:52 +00:00
|
|
|
stripe_subscription_obj = None
|
2017-12-17 19:20:42 +00:00
|
|
|
try:
|
|
|
|
hosting_order = HostingOrder.objects.get(
|
2017-12-20 19:59:46 +00:00
|
|
|
vm_id=vm.id
|
2017-12-17 19:20:42 +00:00
|
|
|
)
|
|
|
|
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')
|
2017-12-20 19:59:46 +00:00
|
|
|
logger.error(
|
|
|
|
'Error canceling subscription for {user} and vm id '
|
2020-03-05 09:57:18 +00:00
|
|
|
'{vm_id}'.format(user=owner.username, vm_id=vm.id)
|
2017-12-20 19:59:46 +00:00
|
|
|
)
|
2017-12-17 19:20:42 +00:00
|
|
|
logger.error(error_msg)
|
2017-12-20 19:59:46 +00:00
|
|
|
admin_email_body['stripe_error_msg'] = error_msg
|
2017-12-17 19:20:42 +00:00
|
|
|
except HostingOrder.DoesNotExist:
|
|
|
|
error_msg = (
|
|
|
|
"HostingOrder corresponding to vm_id={vm_id} does"
|
|
|
|
"not exist. Hence, can not find subscription to "
|
2017-12-20 19:59:46 +00:00
|
|
|
"cancel ".format(vm_id=vm.id)
|
2017-12-17 19:20:42 +00:00
|
|
|
)
|
|
|
|
logger.error(error_msg)
|
2017-12-20 19:59:46 +00:00
|
|
|
admin_email_body['stripe_error_msg'] = error_msg
|
2016-06-10 04:50:49 +00:00
|
|
|
|
2017-09-18 19:29:51 +00:00
|
|
|
terminated = manager.delete_vm(vm.id)
|
2017-05-12 05:56:35 +00:00
|
|
|
|
|
|
|
if not terminated:
|
2018-07-25 19:41:05 +00:00
|
|
|
logger.error(
|
2017-12-12 21:55:30 +00:00
|
|
|
"manager.delete_vm returned False. Hence, error making "
|
|
|
|
"xml-rpc call to delete vm failed."
|
|
|
|
)
|
2019-06-25 01:10:50 +00:00
|
|
|
response['text'] = str(_('Error terminating VM')) + str(vm.id)
|
2017-09-13 20:54:10 +00:00
|
|
|
else:
|
2020-06-10 06:57:59 +00:00
|
|
|
for t in range(settings.MAX_TIME_TO_WAIT_FOR_VM_TERMINATE):
|
2017-09-14 19:27:59 +00:00
|
|
|
try:
|
2017-12-20 19:59:46 +00:00
|
|
|
manager.get_vm(vm.id)
|
2017-09-15 12:45:06 +00:00
|
|
|
except WrongIdError:
|
2018-07-25 19:53:33 +00:00
|
|
|
logger.error(
|
|
|
|
"VM {} not found. So, its terminated.".format(vm.id)
|
|
|
|
)
|
2017-09-15 11:16:56 +00:00
|
|
|
response['status'] = True
|
|
|
|
response['text'] = ugettext('Terminated')
|
2017-09-24 19:16:39 +00:00
|
|
|
vm_detail_obj = VMDetail.objects.filter(
|
2017-12-20 19:59:46 +00:00
|
|
|
vm_id=vm.id
|
2017-12-12 21:55:30 +00:00
|
|
|
).first()
|
2017-09-24 19:00:28 +00:00
|
|
|
vm_detail_obj.terminated_at = datetime.utcnow()
|
|
|
|
vm_detail_obj.save()
|
2017-12-17 19:20:42 +00:00
|
|
|
except BaseException as base_exception:
|
|
|
|
logger.error(
|
2017-12-20 19:59:46 +00:00
|
|
|
"manager.get_vm({vm_id}) returned exception: "
|
|
|
|
"{details}.".format(
|
|
|
|
details=str(base_exception), vm_id=vm.id
|
2017-10-03 11:07:41 +00:00
|
|
|
)
|
|
|
|
)
|
2017-09-14 19:27:59 +00:00
|
|
|
break
|
|
|
|
else:
|
2020-06-10 06:57:59 +00:00
|
|
|
logger.debug(
|
|
|
|
'Sleeping 2 seconds for terminate action on VM %s' %
|
|
|
|
vm.id
|
|
|
|
)
|
2017-09-14 19:27:59 +00:00
|
|
|
sleep(2)
|
2018-09-07 21:11:46 +00:00
|
|
|
if not response['status']:
|
2019-09-24 04:14:45 +00:00
|
|
|
response['text'] = str(_("VM terminate action timed out. "
|
|
|
|
"Please contact "
|
|
|
|
"support@datacenterlight.ch for "
|
|
|
|
"further information."))
|
2017-09-13 20:54:10 +00:00
|
|
|
context = {
|
2017-09-26 22:08:39 +00:00
|
|
|
'vm_name': vm_name,
|
2017-12-20 19:59:46 +00:00
|
|
|
'base_url': "{0}://{1}".format(
|
|
|
|
self.request.scheme, self.request.get_host()
|
|
|
|
),
|
2017-09-27 19:41:39 +00:00
|
|
|
'page_header': _('Virtual Machine %(vm_name)s Cancelled') % {
|
2017-12-20 19:59:46 +00:00
|
|
|
'vm_name': vm_name
|
|
|
|
}
|
2017-09-13 20:54:10 +00:00
|
|
|
}
|
|
|
|
email_data = {
|
|
|
|
'subject': context['page_header'],
|
|
|
|
'to': self.request.user.email,
|
|
|
|
'context': context,
|
|
|
|
'template_name': 'vm_canceled',
|
|
|
|
'template_path': 'hosting/emails/',
|
|
|
|
'from_address': settings.DCL_SUPPORT_FROM_ADDRESS,
|
|
|
|
}
|
|
|
|
email = BaseEmail(**email_data)
|
|
|
|
email.send()
|
2017-12-20 19:59:46 +00:00
|
|
|
admin_email_body.update(response)
|
2019-09-24 05:04:04 +00:00
|
|
|
admin_email_body["customer_email"] = owner.email
|
2020-03-05 09:57:18 +00:00
|
|
|
admin_email_body["customer_username"] = owner.username
|
2019-09-24 05:04:04 +00:00
|
|
|
admin_email_body["VM_ID"] = vm.id
|
|
|
|
admin_email_body["VM_created_at"] = (str(hosting_order.created_at) if
|
|
|
|
hosting_order is not None
|
|
|
|
else "unknown")
|
2020-01-02 06:37:52 +00:00
|
|
|
content = ""
|
|
|
|
total_amount = 0
|
|
|
|
if stripe_subscription_obj:
|
|
|
|
for line_item in stripe_subscription_obj["items"]["data"]:
|
|
|
|
total_amount += (line_item["quantity"] *
|
|
|
|
line_item.plan["amount"])
|
|
|
|
content += " %s => %s x %s => %s\n" % (
|
|
|
|
line_item.plan["name"], line_item["quantity"],
|
|
|
|
line_item.plan["amount"]/100,
|
|
|
|
(line_item["quantity"] * line_item.plan["amount"])/100
|
|
|
|
)
|
|
|
|
admin_email_body["subscription_amount"] = total_amount/100
|
|
|
|
admin_email_body["subscription_detail"] = content
|
2020-03-05 09:57:18 +00:00
|
|
|
admin_msg_sub = "VM and Subscription for VM {} and user: {}, {}".format(
|
2018-09-07 21:11:46 +00:00
|
|
|
vm.id,
|
2020-03-05 09:57:18 +00:00
|
|
|
owner.email, owner.username
|
2018-09-07 21:11:46 +00:00
|
|
|
)
|
2017-12-20 19:59:46 +00:00
|
|
|
email_to_admin_data = {
|
2018-09-07 21:11:46 +00:00
|
|
|
'subject': ("Deleted " if response['status']
|
2018-09-07 21:14:57 +00:00
|
|
|
else "ERROR deleting ") + admin_msg_sub,
|
2017-12-20 19:59:46 +00:00
|
|
|
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
|
2022-02-05 03:00:58 +00:00
|
|
|
'to': ['dcl-orders@ungleich.ch'],
|
2017-12-20 19:59:46 +00:00
|
|
|
'body': "\n".join(
|
2017-12-20 21:26:36 +00:00
|
|
|
["%s=%s" % (k, v) for (k, v) in admin_email_body.items()]),
|
2017-12-20 19:59:46 +00:00
|
|
|
}
|
|
|
|
send_plain_email_task.delay(email_to_admin_data)
|
2018-06-27 10:34:41 +00:00
|
|
|
return JsonResponse(response)
|
2017-05-05 12:59:11 +00:00
|
|
|
|
2017-05-12 05:56:35 +00:00
|
|
|
|
2017-09-06 21:52:02 +00:00
|
|
|
class HostingBillListView(PermissionRequiredMixin, LoginRequiredMixin,
|
|
|
|
ListView):
|
2017-05-05 12:59:11 +00:00
|
|
|
template_name = "hosting/bills.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
2017-05-14 01:19:09 +00:00
|
|
|
permission_required = ['view_hostingview']
|
2017-05-05 12:59:11 +00:00
|
|
|
context_object_name = "users"
|
|
|
|
model = StripeCustomer
|
|
|
|
paginate_by = 10
|
|
|
|
ordering = '-id'
|
|
|
|
|
2017-05-12 05:56:35 +00:00
|
|
|
|
2017-09-06 21:52:02 +00:00
|
|
|
class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin,
|
|
|
|
DetailView):
|
2017-05-05 12:59:11 +00:00
|
|
|
template_name = "hosting/bill_detail.html"
|
|
|
|
login_url = reverse_lazy('hosting:login')
|
|
|
|
permission_required = ['view_hostingview']
|
|
|
|
context_object_name = "bill"
|
|
|
|
model = HostingBill
|
|
|
|
|
|
|
|
def get_object(self, queryset=None):
|
2017-05-12 05:56:35 +00:00
|
|
|
# Get HostingBill for primary key (Select from customer users)
|
2017-05-05 12:59:11 +00:00
|
|
|
pk = self.kwargs['pk']
|
2017-05-07 23:56:02 +00:00
|
|
|
object = HostingBill.objects.filter(customer__id=pk).first()
|
|
|
|
if object is None:
|
|
|
|
self.template_name = 'hosting/bill_error.html'
|
|
|
|
return object
|
2017-05-05 12:59:11 +00:00
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
# Get context
|
|
|
|
context = super(DetailView, self).get_context_data(**kwargs)
|
2017-05-12 10:07:05 +00:00
|
|
|
|
|
|
|
owner = self.request.user
|
2020-03-05 09:57:18 +00:00
|
|
|
manager = OpenNebulaManager(email=owner.username,
|
2017-05-13 03:50:56 +00:00
|
|
|
password=owner.password)
|
2017-05-07 04:43:28 +00:00
|
|
|
# Get vms
|
2017-05-12 10:07:05 +00:00
|
|
|
queryset = manager.get_vms()
|
|
|
|
vms = VirtualMachineSerializer(queryset, many=True).data
|
2017-05-13 11:47:53 +00:00
|
|
|
# Set total price
|
|
|
|
bill = context['bill']
|
|
|
|
bill.total_price = 0.0
|
|
|
|
for vm in vms:
|
|
|
|
bill.total_price += vm['price']
|
2017-05-12 10:07:05 +00:00
|
|
|
context['vms'] = vms
|
2017-05-07 23:56:02 +00:00
|
|
|
return context
|
2017-10-01 18:17:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
def forbidden_view(request, exception=None, reason=''):
|
|
|
|
"""
|
|
|
|
Handle 403 error
|
|
|
|
"""
|
|
|
|
logger.error(str(exception) if exception else None)
|
2017-10-01 21:06:51 +00:00
|
|
|
logger.error('Reason = {reason}'.format(reason=reason))
|
2017-10-01 18:32:51 +00:00
|
|
|
err_msg = _('There was an error processing your request. Please try '
|
2017-10-01 21:06:51 +00:00
|
|
|
'again.')
|
2017-10-01 18:32:51 +00:00
|
|
|
messages.add_message(request, messages.ERROR, err_msg)
|
2017-10-01 21:06:51 +00:00
|
|
|
return HttpResponseRedirect(request.get_full_path())
|
2019-05-09 05:34:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
class CheckUserVM(APIView):
|
2019-05-13 01:34:10 +00:00
|
|
|
renderer_classes = (JSONRenderer, )
|
2019-05-09 05:34:18 +00:00
|
|
|
|
|
|
|
def get(self, request):
|
|
|
|
try:
|
|
|
|
email = request.data['email']
|
|
|
|
ip = request.data['ip']
|
2019-05-13 01:34:10 +00:00
|
|
|
user = request.data['user']
|
|
|
|
realm = request.data['realm']
|
|
|
|
token = request.data['token']
|
2019-05-16 20:35:44 +00:00
|
|
|
if realm != settings.READ_VM_REALM:
|
2019-05-13 01:34:10 +00:00
|
|
|
return Response("User not allowed", 403)
|
|
|
|
response = check_otp(user, realm, token)
|
|
|
|
if response != 200:
|
|
|
|
return Response('Invalid token', 403)
|
2019-08-27 05:43:31 +00:00
|
|
|
manager = OpenNebulaManager(settings.OPENNEBULA_USERNAME,
|
|
|
|
settings.OPENNEBULA_PASSWORD)
|
2019-06-08 02:40:16 +00:00
|
|
|
# not the best way to lookup vms by ip
|
|
|
|
# TODO: make this optimal
|
|
|
|
vms = manager.get_vms()
|
2019-06-10 12:51:40 +00:00
|
|
|
users_vms = [vm for vm in vms if vm.uname == email]
|
2019-06-08 02:40:16 +00:00
|
|
|
if len(users_vms) == 0:
|
|
|
|
return Response('No VM found with the given email address',
|
|
|
|
404)
|
|
|
|
for vm in users_vms:
|
|
|
|
for nic in vm.template.nics:
|
|
|
|
if hasattr(nic, 'ip6_global'):
|
|
|
|
if nic.ip6_global == ip:
|
|
|
|
return Response('success', 200)
|
|
|
|
elif hasattr(nic, 'ip'):
|
|
|
|
if nic.ip == ip:
|
|
|
|
return Response('success', 200)
|
|
|
|
return Response('No VM found matching the ip address provided', 404)
|
2019-05-09 05:34:18 +00:00
|
|
|
except KeyError:
|
|
|
|
return Response('Not enough data provided', 400)
|