From 1d70563ea209501f90022809e937ebc6e5b96fb7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 10 May 2019 09:19:31 +0200 Subject: [PATCH 01/79] Save user's key in opennebula --- datacenterlight/tasks.py | 49 +--------------------------------------- hosting/views.py | 13 +++++++---- opennebula_api/models.py | 25 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 52 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 5f12b7df..12c1cf38 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -197,54 +197,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): get_or_create_vm_detail(custom_user, manager, vm_id) if custom_user is not None: public_keys = get_all_public_keys(custom_user) - keys = [{'value': key, 'state': True} for key in - public_keys] - if len(keys) > 0: - logger.debug( - "Calling configure on {host} for " - "{num_keys} keys".format( - host=vm_ipv6, num_keys=len(keys) - ) - ) - # Let's wait until the IP responds to ping before we - # run the cdist configure on the host - did_manage_public_key = False - for i in range(0, 15): - if ping_ok(vm_ipv6): - logger.debug( - "{} is pingable. Doing a " - "manage_public_key".format(vm_ipv6) - ) - sleep(10) - manager.manage_public_key( - keys, hosts=[vm_ipv6] - ) - did_manage_public_key = True - break - else: - logger.debug( - "Can't ping {}. Wait 5 secs".format( - vm_ipv6 - ) - ) - sleep(5) - if not did_manage_public_key: - emsg = ("Waited for over 75 seconds for {} to be " - "pingable. But the VM was not reachable. " - "So, gave up manage_public_key. Please do " - "this manually".format(vm_ipv6)) - logger.error(emsg) - email_data = { - 'subject': '{} CELERY TASK INCOMPLETE: {} not ' - 'pingable for 75 seconds'.format( - settings.DCL_TEXT, vm_ipv6 - ), - 'from_email': current_task.request.hostname, - 'to': settings.DCL_ERROR_EMAILS_TO_LIST, - 'body': emsg - } - email = EmailMessage(**email_data) - email.send() + manager.save_key_in_opennebula('\n'.join(public_keys)) else: logger.debug("VM's ipv6 is None. Hence not created VMDetail") except Exception as e: diff --git a/hosting/views.py b/hosting/views.py index 92dd5aa8..209df30b 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -36,6 +36,7 @@ from datacenterlight.cms_models import DCLCalculatorPluginModel from datacenterlight.models import VMTemplate, VMPricing from datacenterlight.utils import create_vm, get_cms_integration from hosting.models import UserCardDetail +from utils.hosting_utils import get_all_public_keys from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import ( @@ -460,7 +461,9 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): pk = self.kwargs.get('pk') # Get user ssh key public_key = UserHostingKey.objects.get(pk=pk).public_key - manager.manage_public_key([{'value': public_key, 'state': False}]) + keys = UserHostingKey.objects.filter(user=self.request.user) + keys_to_save = [k.public_key for k in keys if k != public_key] + manager.save_key_in_opennebula('\n'.join(keys_to_save)) return super(SSHKeyDeleteView, self).delete(request, *args, **kwargs) @@ -509,8 +512,8 @@ class SSHKeyChoiceView(LoginRequiredMixin, View): email=owner.email, password=owner.password ) - public_key_str = public_key.decode() - manager.manage_public_key([{'value': public_key_str, 'state': True}]) + keys = get_all_public_keys(request.user) + manager.save_key_in_opennebula('\n'.join(keys)) return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar') @@ -563,7 +566,9 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView): public_key = form.cleaned_data['public_key'] if type(public_key) is bytes: public_key = public_key.decode() - manager.manage_public_key([{'value': public_key, 'state': True}]) + keys = UserHostingKey.objects.filter(user=self.request.user) + keys_to_save = [k.public_key for k in keys] + manager.save_key_in_opennebula('\n'.join(keys_to_save)) return HttpResponseRedirect(self.success_url) def post(self, request, *args, **kwargs): diff --git a/opennebula_api/models.py b/opennebula_api/models.py index a333aa23..5a838c02 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -371,6 +371,31 @@ class OpenNebulaManager(): return vm_terminated + def save_key_in_opennebula(self, ssh_key): + """ + Save the given ssh key in OpenNebula user + + # Update type: 0: Replace the whole template. + 1: Merge new template with the existing one. + :param ssh_key: The ssh key to be saved + :return: + """ + UPDATE_TYPE = 1 + return_value = self.client.call( + 'one.user.update', + self.email, + self.password, + '%s' % ssh_key, + UPDATE_TYPE + ) + if type(return_value) == int: + logger.debug( + "Saved the key in opennebula successfully : %s" % return_value) + else: + logger.error( + "Could not save the key in opennebula. %s" % return_value) + return + def _get_template_pool(self): try: template_pool = oca.VmTemplatePool(self.oneadmin_client) From c92b8c6facdff3e8f82cb91475ae14afbe2ac107 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 10 May 2019 23:51:05 +0200 Subject: [PATCH 02/79] one. is appended by oca --- opennebula_api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 5a838c02..8564332c 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -382,7 +382,7 @@ class OpenNebulaManager(): """ UPDATE_TYPE = 1 return_value = self.client.call( - 'one.user.update', + 'user.update', self.email, self.password, '%s' % ssh_key, From 85136d80ccea0ee2e13b55677a2bd7e0febc04a8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 10 May 2019 23:57:52 +0200 Subject: [PATCH 03/79] Pass the opennebula user id as the object id --- opennebula_api/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 8564332c..8b4517e5 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -383,8 +383,7 @@ class OpenNebulaManager(): UPDATE_TYPE = 1 return_value = self.client.call( 'user.update', - self.email, - self.password, + self.opennebula_user.id, '%s' % ssh_key, UPDATE_TYPE ) From 6a1faa52e470a3923a331ce45ad99df84a9b9d72 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 00:25:49 +0200 Subject: [PATCH 04/79] Set user's own ssh keys when creating VM --- datacenterlight/tasks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 12c1cf38..a4d2ebe7 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -79,10 +79,12 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): # Create OpenNebulaManager manager = OpenNebulaManager(email=on_user, password=on_pass) + custom_user = CustomUser.objects.get(email=user.get('email')) + pub_keys = get_all_public_keys(custom_user) vm_id = manager.create_vm( template_id=vm_template_id, specs=specs, - ssh_key=settings.ONEADMIN_USER_SSH_PUBLIC_KEY, + ssh_key='\n'.join(pub_keys), vm_name=vm_name ) @@ -193,7 +195,6 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): vm_ipv6 = manager.get_ipv6(vm_id) logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) if vm_ipv6 is not None: - custom_user = CustomUser.objects.get(email=user.get('email')) get_or_create_vm_detail(custom_user, manager, vm_id) if custom_user is not None: public_keys = get_all_public_keys(custom_user) From 5146daa6806152d5d100f8da6d095b856f562d2b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 00:31:25 +0200 Subject: [PATCH 05/79] Use SSH_PUBLIC_KEY within CONTEXT --- opennebula_api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 8b4517e5..7873413b 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -384,7 +384,7 @@ class OpenNebulaManager(): return_value = self.client.call( 'user.update', self.opennebula_user.id, - '%s' % ssh_key, + '%s' % ssh_key, UPDATE_TYPE ) if type(return_value) == int: From 3602bb0eb7e98a02bc1d32e65b55cfeb4240d0d5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 01:46:28 +0200 Subject: [PATCH 06/79] Add save_key_in_vm_template and get_all_vmids methods --- opennebula_api/models.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 7873413b..61d08d1a 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -371,7 +371,31 @@ class OpenNebulaManager(): return vm_terminated - def save_key_in_opennebula(self, ssh_key): + def save_key_in_vm_template(self, vm_id, ssh_key): + """ + Update the template of a given VM and set the ssh key of the user + :param vm_id: the identifier of the VM object + :param ssh_key: a newline(\n) separated ssh key string that needs to be + set in the VM template + :return: + """ + UPDATE_TYPE = 1 + return_value = self.client.call( + 'vm.updateconf', + vm_id, + '%s' % ssh_key, + UPDATE_TYPE + ) + if type(return_value) == int: + logger.debug( + "Saved the key in VM Template success : %s" % return_value) + else: + logger.error( + "Could not save the key in VM Template. %s" % return_value) + + return + + def save_key_in_opennebula_user(self, ssh_key): """ Save the given ssh key in OpenNebula user @@ -613,6 +637,19 @@ class OpenNebulaManager(): "Keys and/or hosts are empty, so not managing any keys" ) + def get_all_vmids(self): + owner = CustomUser.objects.filter(email=self.email).first() + all_orders = HostingOrder.objects.filter(customer__user=owner) + vm_ids = [] + if len(all_orders) > 0: + logger.debug("The user {} has 1 or more VMs. We need to configure " + "the ssh keys.".format(self.email)) + vm_ids = [order.vm_id for order in all_orders] + else: + logger.debug("The user {} has no VMs. We don't need to configure " + "the ssh keys.".format(self.email)) + return vm_ids + def get_all_hosts(self): """ A utility function to obtain all hosts of this owner From 65c9ccb671fa8e3d690b4cf01b47a99c8c72234c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 01:54:35 +0200 Subject: [PATCH 07/79] Use save_key_in_opennebula_user and save_key_in_vm_template --- hosting/views.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 209df30b..450875e5 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -463,7 +463,11 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): public_key = UserHostingKey.objects.get(pk=pk).public_key keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys if k != public_key] - manager.save_key_in_opennebula('\n'.join(keys_to_save)) + manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) + vm_ids = manager.get_all_vmids() + if len(vm_ids) > 0 and len(keys_to_save) > 0: + for vm_id in vm_ids: + manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) return super(SSHKeyDeleteView, self).delete(request, *args, **kwargs) @@ -513,7 +517,11 @@ class SSHKeyChoiceView(LoginRequiredMixin, View): password=owner.password ) keys = get_all_public_keys(request.user) - manager.save_key_in_opennebula('\n'.join(keys)) + vm_ids = manager.get_all_vmids() + manager.save_key_in_opennebula_user('\n'.join(keys)) + if len(vm_ids) > 0 and len(keys) > 0: + for vm_id in vm_ids: + manager.save_key_in_vm_template(vm_id, '\n'.join(keys)) return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar') @@ -563,12 +571,13 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView): email=owner.email, password=owner.password ) - public_key = form.cleaned_data['public_key'] - if type(public_key) is bytes: - public_key = public_key.decode() keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys] - manager.save_key_in_opennebula('\n'.join(keys_to_save)) + manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) + vm_ids = manager.get_all_vmids() + if len(vm_ids) > 0 and len(keys) > 0: + for vm_id in vm_ids: + manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) return HttpResponseRedirect(self.success_url) def post(self, request, *args, **kwargs): From 0b85784fd3fa5726fadd63c56fdd9d718d2d6058 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 01:56:00 +0200 Subject: [PATCH 08/79] No need to manage ssh keys after VM is created The ssh keys are added at the time the VM is created or later --- datacenterlight/tasks.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index a4d2ebe7..808f6c61 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -190,17 +190,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): email = BaseEmail(**email_data) email.send() - # try to see if we have the IPv6 of the new vm and that if the ssh - # keys can be configured - vm_ipv6 = manager.get_ipv6(vm_id) logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) - if vm_ipv6 is not None: - get_or_create_vm_detail(custom_user, manager, vm_id) - if custom_user is not None: - public_keys = get_all_public_keys(custom_user) - manager.save_key_in_opennebula('\n'.join(public_keys)) - else: - logger.debug("VM's ipv6 is None. Hence not created VMDetail") + get_or_create_vm_detail(custom_user, manager, vm_id) except Exception as e: logger.error(str(e)) try: From 7f6d4c1c53304aa80d032d1df1ed20001e7aca21 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 02:23:51 +0200 Subject: [PATCH 09/79] Refactor get_all_vmids -> get_all_active_vmids We now get this info from opennebula --- hosting/views.py | 6 +++--- opennebula_api/models.py | 31 ++++++++----------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 450875e5..f1a6dd9c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -464,7 +464,7 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys if k != public_key] manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - vm_ids = manager.get_all_vmids() + vm_ids = manager.get_vms() if len(vm_ids) > 0 and len(keys_to_save) > 0: for vm_id in vm_ids: manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) @@ -517,7 +517,7 @@ class SSHKeyChoiceView(LoginRequiredMixin, View): password=owner.password ) keys = get_all_public_keys(request.user) - vm_ids = manager.get_all_vmids() + vm_ids = manager.get_all_active_vmids() manager.save_key_in_opennebula_user('\n'.join(keys)) if len(vm_ids) > 0 and len(keys) > 0: for vm_id in vm_ids: @@ -574,7 +574,7 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView): keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys] manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - vm_ids = manager.get_all_vmids() + vm_ids = manager.get_all_active_vmids() if len(vm_ids) > 0 and len(keys) > 0: for vm_id in vm_ids: manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 61d08d1a..819bd803 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -168,7 +168,7 @@ class OpenNebulaManager(): raise return user_pool - def _get_vm_pool(self, infoextended=True): + def _get_vm_pool(self, infoextended=True, vm_state=-1): """ # filter: # -4: Resources belonging to the user’s primary group @@ -201,8 +201,8 @@ class OpenNebulaManager(): vm_pool = oca.VirtualMachinePool(self.client) if infoextended: vm_pool.infoextended( - filter=-1, # User's resources and any of his groups - vm_state=-1 # Look for VMs in any state, except DONE + filter=-1, # User's resources and any of his groups + vm_state=vm_state ) else: vm_pool.info() @@ -210,13 +210,6 @@ class OpenNebulaManager(): except AttributeError: logger.error( 'Could not connect via client, using oneadmin instead') - try: - vm_pool = oca.VirtualMachinePool(self.oneadmin_client) - vm_pool.info(filter=-2) - return vm_pool - except: - raise ConnectionRefusedError - except ConnectionRefusedError: logger.error( 'Could not connect to host: {host} via protocol {protocol}'.format( @@ -228,9 +221,9 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - def get_vms(self): + def get_vms(self, vm_state=-1): try: - return self._get_vm_pool() + return self._get_vm_pool(vm_state=vm_state) except ConnectionRefusedError: raise ConnectionRefusedError @@ -637,17 +630,9 @@ class OpenNebulaManager(): "Keys and/or hosts are empty, so not managing any keys" ) - def get_all_vmids(self): - owner = CustomUser.objects.filter(email=self.email).first() - all_orders = HostingOrder.objects.filter(customer__user=owner) - vm_ids = [] - if len(all_orders) > 0: - logger.debug("The user {} has 1 or more VMs. We need to configure " - "the ssh keys.".format(self.email)) - vm_ids = [order.vm_id for order in all_orders] - else: - logger.debug("The user {} has no VMs. We don't need to configure " - "the ssh keys.".format(self.email)) + def get_all_active_vmids(self): + vm_pool = self.get_vms(vm_state=3) # get active vms of the user + vm_ids = [vm.id for vm in vm_pool] return vm_ids def get_all_hosts(self): From b189371a7b6050fb9854b4f6bbfb507d5073ee8f Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 02:38:16 +0200 Subject: [PATCH 10/79] Call get_all_active_vmids to get the active vmids --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index f1a6dd9c..5a3e06b2 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -464,7 +464,7 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys if k != public_key] manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - vm_ids = manager.get_vms() + vm_ids = manager.get_all_active_vmids() if len(vm_ids) > 0 and len(keys_to_save) > 0: for vm_id in vm_ids: manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) From 3133bde0e9879909aa68fcd4fe3fd4b18f15f150 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 11 May 2019 09:15:08 +0200 Subject: [PATCH 11/79] Don't set the key in the live template --- hosting/views.py | 15 +-------------- opennebula_api/models.py | 20 +++++++------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 5a3e06b2..dc09644e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -464,10 +464,6 @@ class SSHKeyDeleteView(LoginRequiredMixin, DeleteView): keys = UserHostingKey.objects.filter(user=self.request.user) keys_to_save = [k.public_key for k in keys if k != public_key] manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - vm_ids = manager.get_all_active_vmids() - if len(vm_ids) > 0 and len(keys_to_save) > 0: - for vm_id in vm_ids: - manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) return super(SSHKeyDeleteView, self).delete(request, *args, **kwargs) @@ -517,11 +513,7 @@ class SSHKeyChoiceView(LoginRequiredMixin, View): password=owner.password ) keys = get_all_public_keys(request.user) - vm_ids = manager.get_all_active_vmids() manager.save_key_in_opennebula_user('\n'.join(keys)) - if len(vm_ids) > 0 and len(keys) > 0: - for vm_id in vm_ids: - manager.save_key_in_vm_template(vm_id, '\n'.join(keys)) return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar') @@ -571,13 +563,8 @@ class SSHKeyCreateView(LoginRequiredMixin, FormView): email=owner.email, password=owner.password ) - keys = UserHostingKey.objects.filter(user=self.request.user) - keys_to_save = [k.public_key for k in keys] + keys_to_save = get_all_public_keys(self.request.user) manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - vm_ids = manager.get_all_active_vmids() - if len(vm_ids) > 0 and len(keys) > 0: - for vm_id in vm_ids: - manager.save_key_in_vm_template(vm_id, '\n'.join(keys_to_save)) return HttpResponseRedirect(self.success_url) def post(self, request, *args, **kwargs): diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 819bd803..01bd065a 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -168,7 +168,7 @@ class OpenNebulaManager(): raise return user_pool - def _get_vm_pool(self, infoextended=True, vm_state=-1): + def _get_vm_pool(self, infoextended=True): """ # filter: # -4: Resources belonging to the user’s primary group @@ -201,15 +201,14 @@ class OpenNebulaManager(): vm_pool = oca.VirtualMachinePool(self.client) if infoextended: vm_pool.infoextended( - filter=-1, # User's resources and any of his groups - vm_state=vm_state + filter=-1, # User's resources and any of his groups + vm_state=-1 # Look for VMs in any state, except DONE ) else: vm_pool.info() return vm_pool - except AttributeError: - logger.error( - 'Could not connect via client, using oneadmin instead') + except AttributeError as ae: + logger.error("AttributeError : %s" % str(ae)) except ConnectionRefusedError: logger.error( 'Could not connect to host: {host} via protocol {protocol}'.format( @@ -221,9 +220,9 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - def get_vms(self, vm_state=-1): + def get_vms(self): try: - return self._get_vm_pool(vm_state=vm_state) + return self._get_vm_pool() except ConnectionRefusedError: raise ConnectionRefusedError @@ -630,11 +629,6 @@ class OpenNebulaManager(): "Keys and/or hosts are empty, so not managing any keys" ) - def get_all_active_vmids(self): - vm_pool = self.get_vms(vm_state=3) # get active vms of the user - vm_ids = [vm.id for vm in vm_pool] - return vm_ids - def get_all_hosts(self): """ A utility function to obtain all hosts of this owner From 7e538bf37b86ddd8ffdafff495e9bad04ca4be7b Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 12 May 2019 19:12:34 +0200 Subject: [PATCH 12/79] Add ssh_key_added_to_vm.{html,txt} email templates --- .../hosting/emails/ssh_key_added_to_vm.html | 51 +++++++++++++++++++ .../hosting/emails/ssh_key_added_to_vm.txt | 11 ++++ 2 files changed, 62 insertions(+) create mode 100644 hosting/templates/hosting/emails/ssh_key_added_to_vm.html create mode 100644 hosting/templates/hosting/emails/ssh_key_added_to_vm.txt diff --git a/hosting/templates/hosting/emails/ssh_key_added_to_vm.html b/hosting/templates/hosting/emails/ssh_key_added_to_vm.html new file mode 100644 index 00000000..086c3479 --- /dev/null +++ b/hosting/templates/hosting/emails/ssh_key_added_to_vm.html @@ -0,0 +1,51 @@ +{% load static i18n %} + + + + + + + {% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} + + + + + + + + + + + + + + + + + + + + + +
+ +
+

{% blocktrans %}SSH keys on your VM {{ vm_name }}{% endblocktrans %}

+
+

+ {% blocktrans %}You initiated adding the keys {{keys}} to your VM {{ vm_name }}!{% endblocktrans %} +

+

+ {% blocktrans %}This email is to notify you that it was accomplished successfully.{% endblocktrans %} +

+

+ {% blocktrans %}You can view your VM detail by clicking the button below.{% endblocktrans %} +

+
+ {% trans "View Detail" %} +
+

{% trans "Your Data Center Light Team" %}

+
+ + + \ No newline at end of file diff --git a/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt b/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt new file mode 100644 index 00000000..ede56e3a --- /dev/null +++ b/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt @@ -0,0 +1,11 @@ +{% load i18n %} + +{% blocktrans %}{% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} + +{% blocktrans %}You initiated adding the keys {{keys}} to your VM {{ vm_name }}!{% endblocktrans %} +{% blocktrans %}This email is to notify you that it was accomplished successfully.{% endblocktrans %} +{% blocktrans %}You can view your VM detail by clicking the button below.{% endblocktrans %} + +{{ base_url }}{{ vm_detail_url }} + +{% trans "Your Data Center Light Team" %} \ No newline at end of file From 67d789ebdb5f23058a34807c0193ad235d6559a7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 12 May 2019 19:13:22 +0200 Subject: [PATCH 13/79] Implement save_ssh_key_in_vm_template_task A celery task which first sends a power off signal to the VM with the given ID and polls until it is powered off. Then, it updates the VM template with the given ssh keys of the user and resumes it. User is notified once the VM template has been updated. --- datacenterlight/tasks.py | 87 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 808f6c61..6bf14825 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,4 +1,5 @@ from datetime import datetime +from time import sleep from celery import current_task from celery.exceptions import MaxRetriesExceededError @@ -8,7 +9,6 @@ from django.core.mail import EmailMessage from django.core.urlresolvers import reverse from django.utils import translation from django.utils.translation import ugettext_lazy as _ -from time import sleep from dynamicweb.celery import app from hosting.models import HostingOrder @@ -16,7 +16,7 @@ from membership.models import CustomUser from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer from utils.hosting_utils import ( - get_all_public_keys, get_or_create_vm_detail, ping_ok + get_all_public_keys, get_or_create_vm_detail ) from utils.mailer import BaseEmail from utils.stripe_utils import StripeUtils @@ -176,7 +176,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): kwargs={'pk': order_id}), 'page_header': _( 'Your New VM %(vm_name)s at Data Center Light') % { - 'vm_name': vm.get('name')}, + 'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { @@ -213,3 +213,84 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): return return vm_id + + +@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) +def save_ssh_key_in_vm_template_task(self, user, vm_id, ssh_key_str): + logger.debug("Inside save_ssh_key_in_vm_template_task %s" % vm_id) + + on_user = user.get('email') + on_pass = user.get('pass') + + if on_user is None or on_pass is None: + logger.error( + "Either email or password not supplied. Can't save ssh key" + ) + return + + manager = OpenNebulaManager(email=on_user, password=on_pass) + + # poweroff the vm + vm = manager.power_off_vm(vm_id) + + powered_off = False + for t in range(15): + vm = manager.get_vm(vm_id) + if vm.str_state == 'POWEROFF': + logger.debug( + "VM %s has been powered off. Now adding ssh keys" % vm.id + ) + powered_off = True + break + else: + logger.debug( + "VM {} has state {}. Waiting 2 more seconds to see if it " + "powers off".format(vm.id, vm.str_state) + ) + sleep(2) + + if powered_off: + logger.debug( + "VM %s was powered off by api call" % vm.id + ) + if manager.save_key_in_vm_template(vm_id=vm_id, ssh_key=ssh_key_str) > 0: + logger.debug( + "Added ssh_keys of user %s to VM %s successfully" % + (on_user, vm_id) + ) + manager.resume(vm_id) + lang = 'en-us' + if user.get('language') is not None: + logger.debug( + "Language is set to {}".format(user.get('language'))) + lang = user.get('language') + translation.activate(lang) + # Send notification to the user as soon as VM has been booked + context = { + 'page_header': str(_("Adding of SSH key completed")), + 'base_url': "{0}://{1}".format(user.get('request_scheme'), + user.get('request_host')), + 'vm_detail_url': reverse('hosting:virtual_machines', + kwargs={'pk': vm_id}), + 'vm_name': vm.name + } + email_data = { + 'subject': context.get('page_header'), + 'to': user.get('email'), + 'context': context, + 'template_name': 'new_booked_vm', + 'template_path': 'hosting/emails/', + 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS, + } + email = BaseEmail(**email_data) + email.send() + else: + logger.error( + "There was an error updating ssh keys of the VM %s" % vm_id + ) + else: + logger.error( + "VM {} did not poweroff within 30 seconds after the poweroff api " + "call. Please, ask the admin to poweroff and add the key " + "manually.".format(vm_id) + ) From 61127e56ca84c530d79909a06375164e67ef4c6c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 12 May 2019 19:16:53 +0200 Subject: [PATCH 14/79] Update virtual_machine_detail.html template To show the Add SSH key button and the modal that pops up after clicking it. --- .../hosting/virtual_machine_detail.html | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index ce02036f..8b6537fe 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -80,6 +80,21 @@ + {% if keys|length > 0 %} + {% if not virtual_machine.status == 'canceled' %} +
+ +
+ {% trans "Sorry, there was an unexpected error. Kindly retry." %} +
+
+ {% endif %} + {% else %} + + {% endif %} +

{% trans "Support / Contact" %}

@@ -98,6 +113,41 @@ {% trans "BACK TO LIST" %}
+ + + From 641c556bb65602fc24dbb1fffdba68086aed0591 Mon Sep 17 00:00:00 2001 From: PCoder Date: Sun, 12 May 2019 21:16:46 +0200 Subject: [PATCH 25/79] Simplify code --- .../hosting/js/virtual_machine_detail.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index 32ae3f53..5fb9f6e5 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -81,16 +81,7 @@ $(document).ready(function() { }) }); - $('#modal-add-ssh-key').on('hidden.bs.modal', function () { - location.reload(); - }); - - haveResponse = false; - $('#modal-add-ssh-key').on('click', '.btn-ok', function(e) { - if(haveResponse) { - console.log("We had response, so reload"); - location.reload(); - } + $('#modal-add-ssh-key-button').click(function(e) { var url = $('#add_ssh_key_to_vm_form').attr('action'); console.log("Url to POST " + url); @@ -106,7 +97,7 @@ $(document).ready(function() { console.log("Encoded data = " + encoded_data); fa_icon = $('#ssh-key-modal-icon'); - modal_btn = $('#ssh-key-modal-button'); + modal_btn = $('#modal-add-ssh-key-button'); modal_btn.prop("disabled", true); modal_btn.html(''); $.post(url, {selected_key: encoded_data}) @@ -115,7 +106,7 @@ $(document).ready(function() { modal_btn.prop("disabled", false); modal_btn.html("OK"); if (data.status === true) { - fa_icon.html(''); + fa_icon.html(''); } else { fa_icon.html(''); modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide'); @@ -136,7 +127,9 @@ $(document).ready(function() { }) .always(function () { console.log("changing href to location: " + location); - haveResponse = true; + $('#modal-add-ssh-key-button').unbind('click').click(function () { + location.reload(); + }); }) }); From 9fd396363f79aa594e74b487e9a709e56b4f8db8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 13 May 2019 07:13:49 +0200 Subject: [PATCH 26/79] Center the add ssh key nicely --- hosting/static/hosting/css/virtual-machine.css | 5 +++++ hosting/templates/hosting/virtual_machine_detail.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css index 726b0f35..abd459a0 100644 --- a/hosting/static/hosting/css/virtual-machine.css +++ b/hosting/static/hosting/css/virtual-machine.css @@ -199,6 +199,11 @@ /* text-align: center; */ } +.vm-add-ssh-key { + margin: 25px 0 30px; + /* text-align: center; */ +} + @media(min-width: 768px) { .vm-detail-contain { display: flex; diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 1aad409a..edbc71f9 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -82,7 +82,7 @@ {% if keys|length > 0 %} {% if not virtual_machine.status == 'canceled' %} -
+
{% trans "Sorry, there was an unexpected error. Kindly retry." %} From caa01f344ff69f11a5b18e6356944a29386416dd Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 13 May 2019 07:56:46 +0200 Subject: [PATCH 27/79] Change poweroff to poweroff_hard Issue with poweroff: Executing poweroff not always seems to work. Sometimes, the VM is tries to SHUTDOWN but times out. poweroff-hard seems to poweroff all the time. --- opennebula_api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 7348d538..ef07d4ce 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -367,7 +367,7 @@ class OpenNebulaManager(): vm = None try: vm = self.get_vm(vm_id) - vm.poweroff() + vm.poweroff_hard() except socket.timeout as socket_err: logger.error("Socket timeout error: {0}".format(socket_err)) except OpenNebulaException as opennebula_err: From 72ea362d0143f28f1e2ebea0f575d5608f74c1a7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 13 May 2019 08:01:11 +0200 Subject: [PATCH 28/79] Remove duplicated blocktrans in txt email template --- hosting/templates/hosting/emails/ssh_key_added_to_vm.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt b/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt index ede56e3a..665936cb 100644 --- a/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt +++ b/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt @@ -1,6 +1,6 @@ {% load i18n %} -{% blocktrans %}{% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} +{% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} {% blocktrans %}You initiated adding the keys {{keys}} to your VM {{ vm_name }}!{% endblocktrans %} {% blocktrans %}This email is to notify you that it was accomplished successfully.{% endblocktrans %} From c99e943ebcec10140b89453f225167beb16c334f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 10 Jun 2019 09:23:48 +0200 Subject: [PATCH 29/79] Use oneadmin_client to update a user's ssh key --- opennebula_api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index ef07d4ce..761c84b7 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -432,7 +432,7 @@ class OpenNebulaManager(): :return: """ - return_value = self.client.call( + return_value = self.oneadmin_client.call( 'user.update', self.opennebula_user.id, '%s' % ssh_key, From 108fbb09b00bf15a77201aa162506d9f326bfa16 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 04:29:34 +0200 Subject: [PATCH 30/79] Add ssh key form to order_detail page To ask for the SSH key at the time of confirming and placing the order. The order does not proceed until the user provides a valid ssh key. --- .../datacenterlight/order_detail.html | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 31933e12..fa2ba7ca 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -134,6 +134,27 @@
{% csrf_token %} + {% if generic_payment_details %} + {% else %} + {% comment %} + We are in VM buy flow and we want user to click the "Place order" button. + At this point, we also want the user to input the SSH key for the VM. + {% endcomment %} + + {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} +
+

 {% trans "Add your public SSH key" %}

+
+ {% for field in form %} + {% bootstrap_field field %} + {% endfor %} + {% endif %}
{% if generic_payment_details %} From 1e68ecb047076bbcaf461e8646120c565d7c979b Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 04:31:29 +0200 Subject: [PATCH 31/79] Confirm order button close: Redirect only to url specified --- hosting/static/hosting/js/virtual_machine_detail.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index 5fb9f6e5..e90e73f6 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -161,8 +161,11 @@ $(document).ready(function() { modal_btn = $('#createvm-modal-done-btn'); $('#createvm-modal-title').text(data.msg_title); $('#createvm-modal-body').html(data.msg_body); - modal_btn.attr('href', data.redirect) - .removeClass('hide'); + if (data.redirect) { + modal_btn.attr('href', data.redirect).removeClass('hide'); + } else { + modal_btn.attr('href', ""); + } if (data.status === true) { fa_icon.attr('class', 'checkmark'); } else { From 87f5bf3dcc61abcc0ae00e0a7aff1aca5ee55007 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 04:32:27 +0200 Subject: [PATCH 32/79] Pass UserHostingKeyForm to the context of OrderConfirmationView --- datacenterlight/views.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 5dc3a3d3..f01766d1 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -13,7 +13,8 @@ from django.views.decorators.cache import cache_control from django.views.generic import FormView, CreateView, DetailView from hosting.forms import ( - HostingUserLoginForm, GenericPaymentForm, ProductPaymentForm + HostingUserLoginForm, GenericPaymentForm, ProductPaymentForm, + UserHostingKeyForm ) from hosting.models import ( HostingBill, HostingOrder, UserCardDetail, GenericProduct @@ -529,12 +530,18 @@ class PaymentOrderView(FormView): return self.render_to_response(context) -class OrderConfirmationView(DetailView): +class OrderConfirmationView(DetailView, FormView): + form_class = UserHostingKeyForm template_name = "datacenterlight/order_detail.html" payment_template_name = 'datacenterlight/landing_payment.html' context_object_name = "order" model = HostingOrder + def get_form_kwargs(self): + kwargs = super(OrderConfirmationView, self).get_form_kwargs() + kwargs.update({'request': self.request}) + return kwargs + @cache_control(no_cache=True, must_revalidate=True, no_store=True) def get(self, request, *args, **kwargs): context = {} @@ -567,6 +574,7 @@ class OrderConfirmationView(DetailView): else: context.update({ 'vm': request.session.get('specs'), + 'form': UserHostingKeyForm(request=self.request), }) context.update({ 'site_url': reverse('datacenterlight:index'), @@ -583,6 +591,24 @@ class OrderConfirmationView(DetailView): stripe_api_cus_id = request.session.get('customer') stripe_utils = StripeUtils() + # Check ssh public key and then proceed + form = self.get_form() + required = 'add_ssh' in self.request.POST + form.fields['name'].required = required + form.fields['public_key'].required = required + if not form.is_valid(): + response = { + 'status': False, + 'msg_title': str(_('SSH key related error occurred')), + 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), + } + return JsonResponse(response) + + # We have a valid SSH key from the user, save it in opennebula and db + # and proceed further + form.save() + user_public_key = form.data["public_key"] + if 'token' in request.session: card_details = stripe_utils.get_cards_details_from_token( request.session.get('token') From 110f29171da1d1aa8b270c75fdae4cc64f94d77c Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 04:33:48 +0200 Subject: [PATCH 33/79] Update user ssh key in opennebula --- datacenterlight/tasks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 0ce71577..641bcf6b 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -81,6 +81,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): custom_user = CustomUser.objects.get(email=user.get('email')) pub_keys = get_all_public_keys(custom_user) + if manager.email != settings.OPENNEBULA_USERNAME: + manager.save_key_in_opennebula_user('\n'.join(pub_keys)) vm_id = manager.create_vm( template_id=vm_template_id, specs=specs, From ecc26d14e5d6d988a613ca70f98bfa9ce63302d1 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 17:24:35 +0200 Subject: [PATCH 34/79] Show previous keys if exist in order confirmation --- .../templates/datacenterlight/order_detail.html | 9 +++++++++ datacenterlight/views.py | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index fa2ba7ca..1f01c201 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -151,6 +151,15 @@

 {% trans "Add your public SSH key" %}

+
+ {% if keys|length > 0 %} +
Previous keys
+ {% endif %} + {% for key in keys %} + +
+ {% endfor %} + {% for field in form %} {% bootstrap_field field %} {% endfor %} diff --git a/datacenterlight/views.py b/datacenterlight/views.py index f01766d1..7aa6d8a4 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -25,7 +25,7 @@ from utils.forms import ( BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm, BillingAddress ) -from utils.hosting_utils import get_vm_price_with_vat +from utils.hosting_utils import get_vm_price_with_vat, get_all_public_keys from utils.stripe_utils import StripeUtils from utils.tasks import send_plain_email_task from .cms_models import DCLCalculatorPluginModel @@ -575,6 +575,7 @@ class OrderConfirmationView(DetailView, FormView): context.update({ 'vm': request.session.get('specs'), 'form': UserHostingKeyForm(request=self.request), + 'keys': get_all_public_keys(self.request.user) }) context.update({ 'site_url': reverse('datacenterlight:index'), @@ -607,7 +608,6 @@ class OrderConfirmationView(DetailView, FormView): # We have a valid SSH key from the user, save it in opennebula and db # and proceed further form.save() - user_public_key = form.data["public_key"] if 'token' in request.session: card_details = stripe_utils.get_cards_details_from_token( From 04f1112b098caadbdbcaeee72b7deddad2fe8d8b Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 18:26:45 +0200 Subject: [PATCH 35/79] Add key in the text area --- datacenterlight/templates/datacenterlight/order_detail.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 1f01c201..85f87168 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -156,7 +156,9 @@
Previous keys
{% endif %} {% for key in keys %} - +
{% endfor %} From ba7ff9e409e484881f0cf3b909c601dff4d74f78 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 18:39:25 +0200 Subject: [PATCH 36/79] Adjust textarea styles --- datacenterlight/templates/datacenterlight/order_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 85f87168..410ac2ff 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -156,7 +156,7 @@
Previous keys
{% endif %} {% for key in keys %} -
From a330dee9a1e61e09e8cbdba42f13c74318938537 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 24 Jun 2019 18:58:44 +0200 Subject: [PATCH 37/79] Modify style --- datacenterlight/static/datacenterlight/css/common.css | 5 +++++ datacenterlight/templates/datacenterlight/order_detail.html | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/datacenterlight/static/datacenterlight/css/common.css b/datacenterlight/static/datacenterlight/css/common.css index 00ee52cc..b19b5852 100644 --- a/datacenterlight/static/datacenterlight/css/common.css +++ b/datacenterlight/static/datacenterlight/css/common.css @@ -186,3 +186,8 @@ footer .dcl-link-separator::before { background: transparent !important; resize: none; } + +.existing-keys-title { + font-weight: bold; + font-size: 14px; +} diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 410ac2ff..bebb4d45 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -151,9 +151,9 @@

 {% trans "Add your public SSH key" %}

-
+
{% if keys|length > 0 %} -
Previous keys
+
Existing keys
{% endif %} {% for key in keys %}
{% endfor %} - +
{% for field in form %} {% bootstrap_field field %} {% endfor %} From 08608c726fb20b84b76aa93a0f7793676da2884f Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 25 Jun 2019 02:11:57 +0200 Subject: [PATCH 39/79] Code cleanup: remove updating ssh keys on live VMs --- datacenterlight/tasks.py | 87 +------------------ .../hosting/js/virtual_machine_detail.js | 52 ----------- .../hosting/virtual_machine_detail.html | 35 -------- hosting/urls.py | 4 +- hosting/views.py | 55 ------------ 5 files changed, 4 insertions(+), 229 deletions(-) diff --git a/datacenterlight/tasks.py b/datacenterlight/tasks.py index 641bcf6b..8b4626e8 100644 --- a/datacenterlight/tasks.py +++ b/datacenterlight/tasks.py @@ -1,5 +1,4 @@ from datetime import datetime -from time import sleep from celery import current_task from celery.exceptions import MaxRetriesExceededError @@ -178,7 +177,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): kwargs={'pk': order_id}), 'page_header': _( 'Your New VM %(vm_name)s at Data Center Light') % { - 'vm_name': vm.get('name')}, + 'vm_name': vm.get('name')}, 'vm_name': vm.get('name') } email_data = { @@ -193,7 +192,8 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): email.send() logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) - get_or_create_vm_detail(custom_user, manager, vm_id) + if vm_id > 0: + get_or_create_vm_detail(custom_user, manager, vm_id) except Exception as e: logger.error(str(e)) try: @@ -215,84 +215,3 @@ def create_vm_task(self, vm_template_id, user, specs, template, order_id): return return vm_id - - -@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES) -def save_ssh_key_in_vm_template_task(self, user, vm_id, ssh_key_str): - logger.debug("Inside save_ssh_key_in_vm_template_task %s" % vm_id) - - on_user = user.get('email') - on_pass = user.get('pass') - - if on_user is None or on_pass is None: - logger.error( - "Either email or password not supplied. Can't save ssh key" - ) - return - - manager = OpenNebulaManager(email=on_user, password=on_pass) - - # poweroff the vm - vm = manager.power_off_vm(vm_id) - - powered_off = False - for t in range(15): - vm = manager.get_vm(vm_id) - if vm.str_state == 'POWEROFF': - logger.debug( - "VM %s has been powered off. Now adding ssh keys" % vm.id - ) - powered_off = True - break - else: - logger.debug( - "VM {} has state {}. Waiting 2 more seconds to see if it " - "powers off".format(vm.id, vm.str_state) - ) - sleep(2) - - if powered_off: - logger.debug( - "VM %s was powered off by api call" % vm.id - ) - if manager.save_key_in_vm_template(vm_id=vm_id, ssh_key=ssh_key_str) > 0: - logger.debug( - "Added ssh_keys of user %s to VM %s successfully" % - (on_user, vm_id) - ) - manager.resume(vm_id) - lang = 'en-us' - if user.get('language') is not None: - logger.debug( - "Language is set to {}".format(user.get('language'))) - lang = user.get('language') - translation.activate(lang) - # Send notification to the user as soon as VM has been booked - context = { - 'page_header': str(_("Adding of SSH key completed")), - 'base_url': "{0}://{1}".format(user.get('request_scheme'), - user.get('request_host')), - 'vm_detail_url': reverse('hosting:virtual_machines', - kwargs={'pk': vm_id}), - 'vm_name': vm.name - } - email_data = { - 'subject': context.get('page_header'), - 'to': user.get('email'), - 'context': context, - 'template_name': 'ssh_key_added_to_vm', - 'template_path': 'hosting/emails/', - 'from_address': settings.DCL_SUPPORT_FROM_ADDRESS, - } - email = BaseEmail(**email_data) - email.send() - else: - logger.error( - "There was an error updating ssh keys of the VM %s" % vm_id - ) - else: - logger.error( - "VM {} did not poweroff within 30 seconds after the poweroff api " - "call. Please, ask the admin to poweroff and add the key " - "manually.".format(vm_id) - ) diff --git a/hosting/static/hosting/js/virtual_machine_detail.js b/hosting/static/hosting/js/virtual_machine_detail.js index e90e73f6..28592883 100644 --- a/hosting/static/hosting/js/virtual_machine_detail.js +++ b/hosting/static/hosting/js/virtual_machine_detail.js @@ -81,58 +81,6 @@ $(document).ready(function() { }) }); - $('#modal-add-ssh-key-button').click(function(e) { - var url = $('#add_ssh_key_to_vm_form').attr('action'); - console.log("Url to POST " + url); - - // Declare a checkbox array - var chkArray = []; - var encoded_data =""; - - // Look for all checkboxes that have a specific class and was checked - $(".chk-ssh-key:checked").each(function() { - chkArray.push($(this).val()); - }); - encoded_data = encodeURIComponent(chkArray.join(",")); - console.log("Encoded data = " + encoded_data); - - fa_icon = $('#ssh-key-modal-icon'); - modal_btn = $('#modal-add-ssh-key-button'); - modal_btn.prop("disabled", true); - modal_btn.html(''); - $.post(url, {selected_key: encoded_data}) - .done(function(data) { - console.log("Request Done"); - modal_btn.prop("disabled", false); - modal_btn.html("OK"); - if (data.status === true) { - fa_icon.html(''); - } else { - fa_icon.html(''); - modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide'); - } - console.log("title = " + data.msg_title); - console.log("desc = " + data.msg_body); - $('#ssh-key-modal-title').text(data.msg_title); - $('#ssh-key-modal-description').html(data.msg_body); - console.log("Request Done end"); - }) - .fail(function(data) { - console.log("Request Failed"); - console.log("title " + data.msg_title); - console.log("body " + data.msg_body); - modal_btn.attr('class', '').addClass('btn btn-danger btn-ok btn-wide'); - $('#ssh-key-modal-title').text(data.msg_title); - $('#ssh-key-modal-description').html(data.msg_body); - }) - .always(function () { - console.log("changing href to location: " + location); - $('#modal-add-ssh-key-button').unbind('click').click(function () { - location.reload(); - }); - }) - }); - var hash = window.location.hash; hash && $('ul.nav a[href="' + hash + '"]').tab('show'); diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index edbc71f9..bf869f91 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -113,41 +113,6 @@ {% trans "BACK TO LIST" %}
- - -
- {% if keys|length > 0 %} - {% if not virtual_machine.status == 'canceled' %} -
- -
- {% trans "Sorry, there was an unexpected error. Kindly retry." %} -
-
- {% endif %} - {% else %} - - {% endif %} -

{% trans "Support / Contact" %}

diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 761c84b7..478758fc 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -363,64 +363,6 @@ class OpenNebulaManager(): return vm_terminated - def power_off_vm(self, vm_id): - vm = None - try: - vm = self.get_vm(vm_id) - vm.poweroff_hard() - except socket.timeout as socket_err: - logger.error("Socket timeout error: {0}".format(socket_err)) - except OpenNebulaException as opennebula_err: - logger.error( - "OpenNebulaException error: {0}".format(opennebula_err)) - except OSError as os_err: - logger.error("OSError : {0}".format(os_err)) - except ValueError as value_err: - logger.error("ValueError : {0}".format(value_err)) - - return vm - - def resume(self, vm_id): - vm = None - try: - vm = self.get_vm(vm_id) - vm.resume() - except socket.timeout as socket_err: - logger.error("Socket timeout error: {0}".format(socket_err)) - except OpenNebulaException as opennebula_err: - logger.error( - "OpenNebulaException error: {0}".format(opennebula_err)) - except OSError as os_err: - logger.error("OSError : {0}".format(os_err)) - except ValueError as value_err: - logger.error("ValueError : {0}".format(value_err)) - - return vm - - def save_key_in_vm_template(self, vm_id, ssh_key): - """ - Update the template of a given VM and set the ssh key of the user - :param vm_id: the identifier of the VM object - :param ssh_key: a newline(\n) separated ssh key string that needs to be - set in the VM template - :return: - """ - UPDATE_TYPE = 1 - return_value = self.client.call( - 'vm.updateconf', - vm_id, - '%s' % ssh_key, - UPDATE_TYPE - ) - if type(return_value) == int: - logger.debug( - "Saved the key in VM Template success : %s" % return_value) - else: - logger.error( - "Could not save the key in VM Template. %s" % return_value) - - return return_value - def save_key_in_opennebula_user(self, ssh_key, update_type=1): """ Save the given ssh key in OpenNebula user From 9ee1b7c124dd3cd4367198b07ae0939caed91691 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 25 Jun 2019 02:25:17 +0200 Subject: [PATCH 41/79] Make public_key form params mandatory only if existing keys do not exist --- datacenterlight/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 7aa6d8a4..914c66eb 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -595,8 +595,12 @@ class OrderConfirmationView(DetailView, FormView): # Check ssh public key and then proceed form = self.get_form() required = 'add_ssh' in self.request.POST - form.fields['name'].required = required - form.fields['public_key'].required = required + + # SSH key is required only if the user doesn't have an existing + # key + if len(get_all_public_keys(self.request.user)) == 0: + form.fields['name'].required = required + form.fields['public_key'].required = required if not form.is_valid(): response = { 'status': False, From 85f7d734424a24dfcd272c36d59f2d6432bdb9dd Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 25 Jun 2019 02:32:19 +0200 Subject: [PATCH 42/79] Code cleanup: Remove ssh_key_added_to_vm email templates --- .../hosting/emails/ssh_key_added_to_vm.html | 51 ------------------- .../hosting/emails/ssh_key_added_to_vm.txt | 11 ---- 2 files changed, 62 deletions(-) delete mode 100644 hosting/templates/hosting/emails/ssh_key_added_to_vm.html delete mode 100644 hosting/templates/hosting/emails/ssh_key_added_to_vm.txt diff --git a/hosting/templates/hosting/emails/ssh_key_added_to_vm.html b/hosting/templates/hosting/emails/ssh_key_added_to_vm.html deleted file mode 100644 index 086c3479..00000000 --- a/hosting/templates/hosting/emails/ssh_key_added_to_vm.html +++ /dev/null @@ -1,51 +0,0 @@ -{% load static i18n %} - - - - - - - {% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} - - - - - - - - - - - - - - - - - - - - - -
- -
-

{% blocktrans %}SSH keys on your VM {{ vm_name }}{% endblocktrans %}

-
-

- {% blocktrans %}You initiated adding the keys {{keys}} to your VM {{ vm_name }}!{% endblocktrans %} -

-

- {% blocktrans %}This email is to notify you that it was accomplished successfully.{% endblocktrans %} -

-

- {% blocktrans %}You can view your VM detail by clicking the button below.{% endblocktrans %} -

-
- {% trans "View Detail" %} -
-

{% trans "Your Data Center Light Team" %}

-
- - - \ No newline at end of file diff --git a/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt b/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt deleted file mode 100644 index 665936cb..00000000 --- a/hosting/templates/hosting/emails/ssh_key_added_to_vm.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% load i18n %} - -{% blocktrans %}SSH key(s) {{keys}} added to your VM {{vm_name}}{% endblocktrans %} - -{% blocktrans %}You initiated adding the keys {{keys}} to your VM {{ vm_name }}!{% endblocktrans %} -{% blocktrans %}This email is to notify you that it was accomplished successfully.{% endblocktrans %} -{% blocktrans %}You can view your VM detail by clicking the button below.{% endblocktrans %} - -{{ base_url }}{{ vm_detail_url }} - -{% trans "Your Data Center Light Team" %} \ No newline at end of file From 34c917acc266edcf8037c238383ad9f75d31e306 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 25 Jun 2019 03:10:50 +0200 Subject: [PATCH 43/79] Add SSH form to hosting VM buy flow also --- hosting/templates/hosting/order_detail.html | 29 ++++++++++++++++++ hosting/views.py | 33 +++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 4a62e9fa..7ab79378 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -198,6 +198,35 @@ {% block submit_btn %}
{% csrf_token %} + {% comment %} + We are in VM buy flow and we want user to click the "Place order" button. + At this point, we also want the user to input the SSH key for the VM. + {% endcomment %} + + {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} +
+

 {% trans "Add your public SSH key" %}

+
+
+ {% if keys|length > 0 %} +
Existing keys
+ {% endif %} + {% for key in keys %} + +
+ {% endfor %} +
+ {% for field in form %} + {% bootstrap_field field %} + {% endfor %}
{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.
diff --git a/hosting/views.py b/hosting/views.py index 2ef5383d..33a8748e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -838,13 +838,19 @@ class PaymentVMView(LoginRequiredMixin, FormView): return self.form_invalid(form) -class OrdersHostingDetailView(LoginRequiredMixin, DetailView): +class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): + form_class = UserHostingKeyForm template_name = "hosting/order_detail.html" context_object_name = "order" login_url = reverse_lazy('hosting:login') permission_required = ['view_hostingorder'] model = HostingOrder + def get_form_kwargs(self): + kwargs = super(OrdersHostingDetailView, self).get_form_kwargs() + kwargs.update({'request': self.request}) + return kwargs + def get_object(self, queryset=None): order_id = self.kwargs.get('pk') try: @@ -869,6 +875,8 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): if self.request.GET.get('page') == 'payment': context['page_header_text'] = _('Confirm Order') + context['form'] = UserHostingKeyForm(request=self.request) + context['keys'] = get_all_public_keys(self.request.user) else: context['page_header_text'] = _('Invoice') if not self.request.user.has_perm( @@ -994,6 +1002,27 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView): @method_decorator(decorators) def post(self, request): + # Check ssh public key and then proceed + form = self.get_form() + required = 'add_ssh' in self.request.POST + + # SSH key is required only if the user doesn't have an existing + # key + if len(get_all_public_keys(self.request.user)) == 0: + form.fields['name'].required = required + form.fields['public_key'].required = required + if not form.is_valid(): + response = { + 'status': False, + 'msg_title': str(_('SSH key related error occurred')), + 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), + } + return JsonResponse(response) + + # We have a valid SSH key from the user, save it in opennebula and db + # and proceed further + form.save() + template = request.session.get('template') specs = request.session.get('specs') stripe_utils = StripeUtils() @@ -1641,7 +1670,7 @@ class VirtualMachineView(LoginRequiredMixin, View): "manager.delete_vm returned False. Hence, error making " "xml-rpc call to delete vm failed." ) - response['text'] = str(_('Error terminating VM ')) + str(vm.id) + response['text'] = str(_('Error terminating VM')) + str(vm.id) else: for t in range(15): try: From feeb102f9265afd722960d27efdfe93eaf81186d Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 25 Jun 2019 03:48:29 +0200 Subject: [PATCH 44/79] Do SSH key validation only if the user doesn't have an existing key and the user has input some value in the add ssh key field --- datacenterlight/views.py | 30 +++++++++++++++++------------- hosting/forms.py | 3 ++- hosting/views.py | 22 +++++++++++++--------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 914c66eb..76f50aec 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -588,19 +588,18 @@ class OrderConfirmationView(DetailView, FormView): return render(request, self.template_name, context) def post(self, request, *args, **kwargs): - user = request.session.get('user') - stripe_api_cus_id = request.session.get('customer') - stripe_utils = StripeUtils() - # Check ssh public key and then proceed form = self.get_form() - required = 'add_ssh' in self.request.POST + required = True - # SSH key is required only if the user doesn't have an existing - # key - if len(get_all_public_keys(self.request.user)) == 0: - form.fields['name'].required = required - form.fields['public_key'].required = required + # SSH key validation is required only if the user doesn't have an + # existing key and user has input some value in the add ssh key fields + if (len(get_all_public_keys(self.request.user)) > 0 and + (len(form.data.get('public_key')) == 0 and + len(form.data.get('name')) == 0)): + required = False + form.fields['name'].required = required + form.fields['public_key'].required = required if not form.is_valid(): response = { 'status': False, @@ -609,9 +608,14 @@ class OrderConfirmationView(DetailView, FormView): } return JsonResponse(response) - # We have a valid SSH key from the user, save it in opennebula and db - # and proceed further - form.save() + if required: + # We have a valid SSH key from the user, save it in opennebula and + # db and proceed further + form.save() + + user = request.session.get('user') + stripe_api_cus_id = request.session.get('customer') + stripe_utils = StripeUtils() if 'token' in request.session: card_details = stripe_utils.get_cards_details_from_token( diff --git a/hosting/forms.py b/hosting/forms.py index 576a1996..797bc700 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -187,7 +187,8 @@ class UserHostingKeyForm(forms.ModelForm): alerts the user of it. :return: """ - if 'generate' in self.request.POST: + if ('generate' in self.request.POST + or not self.fields['public_key'].required): return self.data.get('public_key') KEY_ERROR_MESSAGE = _("Please input a proper SSH key") openssh_pubkey_str = self.data.get('public_key').strip() diff --git a/hosting/views.py b/hosting/views.py index 33a8748e..f5146fbf 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1004,13 +1004,16 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): def post(self, request): # Check ssh public key and then proceed form = self.get_form() - required = 'add_ssh' in self.request.POST + required = True - # SSH key is required only if the user doesn't have an existing - # key - if len(get_all_public_keys(self.request.user)) == 0: - form.fields['name'].required = required - form.fields['public_key'].required = required + # SSH key validation is required only if the user doesn't have an + # existing key and user has input some value in the add ssh key fields + if (len(get_all_public_keys(self.request.user)) > 0 and + (len(form.data.get('public_key')) == 0 and + len(form.data.get('name')) == 0)): + required = False + form.fields['name'].required = required + form.fields['public_key'].required = required if not form.is_valid(): response = { 'status': False, @@ -1019,9 +1022,10 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): } return JsonResponse(response) - # We have a valid SSH key from the user, save it in opennebula and db - # and proceed further - form.save() + if required: + # We have a valid SSH key from the user, save it in opennebula and + # db and proceed further + form.save() template = request.session.get('template') specs = request.session.get('specs') From d5d90e0790c70ca11c31a4402c61c9046ca93776 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 06:43:49 +0530 Subject: [PATCH 45/79] Add datacenterlight url: /add-ssh-key --- datacenterlight/urls.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index 006e7fc3..d20366bf 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -3,10 +3,9 @@ from django.views.generic import TemplateView, RedirectView from .views import ( IndexView, PaymentOrderView, OrderConfirmationView, - WhyDataCenterLightView, ContactUsView + WhyDataCenterLightView, ContactUsView, AskSSHKeyView ) - urlpatterns = [ url(r'^$', IndexView.as_view(), name='index'), url(r'^t/$', IndexView.as_view(), name='index_t'), @@ -20,6 +19,8 @@ urlpatterns = [ url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'), url(r'^order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'), + url(r'^add-ssh-key/?$', AskSSHKeyView.as_view(), + name='add_ssh_key'), url(r'^contact/?$', ContactUsView.as_view(), name='contact_us'), url(r'glasfaser/?$', TemplateView.as_view(template_name='ungleich_page/glasfaser.html'), From f502e53845b32f7807ed3d71a52c45b826074272 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 06:45:48 +0530 Subject: [PATCH 46/79] Add basic implementation of AskSSHKeyView --- datacenterlight/views.py | 22 +++++++++++++++++++++- hosting/templates/hosting/user_key.html | 3 ++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 76f50aec..d76303f7 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -523,13 +523,33 @@ class PaymentOrderView(FormView): else: request.session['customer'] = customer return HttpResponseRedirect( - reverse('datacenterlight:order_confirmation')) + reverse('datacenterlight:add_ssh_key')) else: context = self.get_context_data() context['billing_address_form'] = address_form return self.render_to_response(context) +class AskSSHKeyView(FormView): + form_class = UserHostingKeyForm + template_name = "datacenterlight/add_ssh_key.html" + + def get_form_kwargs(self): + kwargs = super(AskSSHKeyView, self).get_form_kwargs() + kwargs.update({'request': self.request}) + return kwargs + + @cache_control(no_cache=True, must_revalidate=True, no_store=True) + def get(self, request, *args, **kwargs): + context = { + 'site_url': reverse('datacenterlight:index'), + 'cms_integration': get_cms_integration('default'), + 'form': UserHostingKeyForm(request=self.request), + 'keys': get_all_public_keys(self.request.user) + } + return render(request, self.template_name, context) + + class OrderConfirmationView(DetailView, FormView): form_class = UserHostingKeyForm template_name = "datacenterlight/order_detail.html" diff --git a/hosting/templates/hosting/user_key.html b/hosting/templates/hosting/user_key.html index 804d661a..247551b5 100644 --- a/hosting/templates/hosting/user_key.html +++ b/hosting/templates/hosting/user_key.html @@ -8,7 +8,8 @@ {% csrf_token %} {% if messages %}
From b6eb72af7d389da94a5c853b3746258a503fdd3d Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:08:43 +0530 Subject: [PATCH 47/79] Refactor SSHKeyCreateView to utils Common between hosting/datacenterlight apps --- utils/views.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/utils/views.py b/utils/views.py index 394a9fc2..93d778b4 100644 --- a/utils/views.py +++ b/utils/views.py @@ -1,16 +1,26 @@ +import uuid + from django.conf import settings from django.contrib import messages from django.contrib.auth import authenticate, login +from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.tokens import default_token_generator +from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy from django.http import HttpResponseRedirect +from django.shortcuts import render from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.utils.translation import ugettext_lazy as _ -from django.views.generic import FormView, CreateView from django.views.decorators.cache import cache_control +from django.views.generic import FormView, CreateView +from datacenterlight.utils import get_cms_integration +from hosting.forms import UserHostingKeyForm +from hosting.models import UserHostingKey from membership.models import CustomUser +from opennebula_api.models import OpenNebulaManager +from utils.hosting_utils import get_all_public_keys from .forms import SetPasswordForm from .mailer import BaseEmail @@ -174,3 +184,107 @@ class PasswordResetConfirmViewMixin(FormView): form.add_error(None, _('The reset password link is no longer valid.')) return self.form_invalid(form) + + +class SSHKeyCreateView(LoginRequiredMixin, FormView): + form_class = UserHostingKeyForm + model = UserHostingKey + template_name = 'hosting/user_key.html' + login_url = reverse_lazy('hosting:login') + context_object_name = "virtual_machine" + success_url = reverse_lazy('hosting:ssh_keys') + + def get_form_kwargs(self): + kwargs = super(SSHKeyCreateView, self).get_form_kwargs() + kwargs.update({'request': self.request}) + return kwargs + + def form_valid(self, form): + form.save() + if settings.DCL_SSH_KEY_NAME_PREFIX in form.instance.name: + content = ContentFile(form.cleaned_data.get('private_key')) + filename = form.cleaned_data.get( + 'name') + '_' + str(uuid.uuid4())[:8] + '_private.pem' + form.instance.private_key.save(filename, content) + context = self.get_context_data() + + next_url = self.request.session.get( + 'next', + reverse_lazy('hosting:create_virtual_machine') + ) + + if 'next' in self.request.session: + context.update({ + 'next_url': next_url + }) + del (self.request.session['next']) + + if form.cleaned_data.get('private_key'): + context.update({ + 'private_key': form.cleaned_data.get('private_key'), + 'key_name': form.cleaned_data.get('name'), + 'form': UserHostingKeyForm(request=self.request), + }) + + owner = self.request.user + manager = OpenNebulaManager( + email=owner.email, + password=owner.password + ) + keys_to_save = get_all_public_keys(self.request.user) + manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) + return HttpResponseRedirect(self.success_url) + + def post(self, request, *args, **kwargs): + form = self.get_form() + required = 'add_ssh' in self.request.POST + form.fields['name'].required = required + form.fields['public_key'].required = required + if form.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + + +class AskSSHKeyView(SSHKeyCreateView): + form_class = UserHostingKeyForm + template_name = "datacenterlight/add_ssh_key.html" + success_url = reverse_lazy('datacenterlight:order_confirmation') + context_object_name = "dcl_vm_buy_add_ssh_key" + + @cache_control(no_cache=True, must_revalidate=True, no_store=True) + def get(self, request, *args, **kwargs): + context = { + 'site_url': reverse_lazy('datacenterlight:index'), + 'cms_integration': get_cms_integration('default'), + 'form': UserHostingKeyForm(request=self.request), + 'keys': get_all_public_keys(self.request.user) + } + return render(request, self.template_name, context) + # + # def post(self, request, *args, **kwargs): + # # Check ssh public key and then proceed + # form = self.get_form() + # required = True + # + # # SSH key validation is required only if the user doesn't have an + # # existing key and user has input some value in the add ssh key fields + # if (len(get_all_public_keys(self.request.user)) > 0 and + # (len(form.data.get('public_key')) == 0 and + # len(form.data.get('name')) == 0)): + # required = False + # form.fields['name'].required = required + # form.fields['public_key'].required = required + # if not form.is_valid(): + # response = { + # 'status': False, + # 'msg_title': str(_('SSH key related error occurred')), + # 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), + # } + # return JsonResponse(response) + # + # if required: + # # We have a valid SSH key from the user, save it in opennebula and + # # db and proceed further + # form.save() + From 47fd9a8f28d85f580bad8947a0aea15435f70ce3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:09:37 +0530 Subject: [PATCH 48/79] Adjust urls in datacenterlight/hosting apps urls/views after refactor --- datacenterlight/urls.py | 3 +- datacenterlight/views.py | 20 ------------- hosting/urls.py | 5 +++- hosting/views.py | 61 ---------------------------------------- 4 files changed, 6 insertions(+), 83 deletions(-) diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index d20366bf..13296de7 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -1,9 +1,10 @@ from django.conf.urls import url from django.views.generic import TemplateView, RedirectView +from utils.views import AskSSHKeyView from .views import ( IndexView, PaymentOrderView, OrderConfirmationView, - WhyDataCenterLightView, ContactUsView, AskSSHKeyView + WhyDataCenterLightView, ContactUsView ) urlpatterns = [ diff --git a/datacenterlight/views.py b/datacenterlight/views.py index d76303f7..99ceae8f 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -530,26 +530,6 @@ class PaymentOrderView(FormView): return self.render_to_response(context) -class AskSSHKeyView(FormView): - form_class = UserHostingKeyForm - template_name = "datacenterlight/add_ssh_key.html" - - def get_form_kwargs(self): - kwargs = super(AskSSHKeyView, self).get_form_kwargs() - kwargs.update({'request': self.request}) - return kwargs - - @cache_control(no_cache=True, must_revalidate=True, no_store=True) - def get(self, request, *args, **kwargs): - context = { - 'site_url': reverse('datacenterlight:index'), - 'cms_integration': get_cms_integration('default'), - 'form': UserHostingKeyForm(request=self.request), - 'keys': get_all_public_keys(self.request.user) - } - return render(request, self.template_name, context) - - class OrderConfirmationView(DetailView, FormView): form_class = UserHostingKeyForm template_name = "datacenterlight/order_detail.html" diff --git a/hosting/urls.py b/hosting/urls.py index 4779f67c..5b2b87b0 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -1,6 +1,7 @@ from django.conf.urls import url from django.contrib.auth import views as auth_views +from utils.views import SSHKeyCreateView, AskSSHKeyView from .views import ( DjangoHostingView, RailsHostingView, PaymentVMView, NodeJSHostingView, LoginView, SignupView, SignupValidateView, SignupValidatedView, IndexView, @@ -8,7 +9,7 @@ from .views import ( VirtualMachinesPlanListView, VirtualMachineView, OrdersHostingDeleteView, MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView, CreateVirtualMachinesView, HostingBillListView, - HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, + HostingBillDetailView, SSHKeyDeleteView, SSHKeyListView, SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, InvoiceListView, InvoiceDetailView, CheckUserVM ) @@ -27,6 +28,8 @@ urlpatterns = [ url(r'invoices/?$', InvoiceListView.as_view(), name='invoices'), url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(), name='order-confirmation'), + url(r'^add-ssh-key/?$', AskSSHKeyView.as_view(), + name='add_ssh_key'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), url(r'invoice/(?P[-\w]+)/?$', InvoiceDetailView.as_view(), diff --git a/hosting/views.py b/hosting/views.py index f5146fbf..97b7f42c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -523,67 +523,6 @@ class SSHKeyChoiceView(LoginRequiredMixin, View): return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar') -@method_decorator(decorators, name='dispatch') -class SSHKeyCreateView(LoginRequiredMixin, FormView): - form_class = UserHostingKeyForm - model = UserHostingKey - template_name = 'hosting/user_key.html' - login_url = reverse_lazy('hosting:login') - context_object_name = "virtual_machine" - success_url = reverse_lazy('hosting:ssh_keys') - - def get_form_kwargs(self): - kwargs = super(SSHKeyCreateView, self).get_form_kwargs() - kwargs.update({'request': self.request}) - return kwargs - - def form_valid(self, form): - form.save() - if settings.DCL_SSH_KEY_NAME_PREFIX in form.instance.name: - content = ContentFile(form.cleaned_data.get('private_key')) - filename = form.cleaned_data.get( - 'name') + '_' + str(uuid.uuid4())[:8] + '_private.pem' - form.instance.private_key.save(filename, content) - context = self.get_context_data() - - next_url = self.request.session.get( - 'next', - reverse('hosting:create_virtual_machine') - ) - - if 'next' in self.request.session: - context.update({ - 'next_url': next_url - }) - del (self.request.session['next']) - - if form.cleaned_data.get('private_key'): - context.update({ - 'private_key': form.cleaned_data.get('private_key'), - 'key_name': form.cleaned_data.get('name'), - 'form': UserHostingKeyForm(request=self.request), - }) - - owner = self.request.user - manager = OpenNebulaManager( - email=owner.email, - password=owner.password - ) - keys_to_save = get_all_public_keys(self.request.user) - manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) - return HttpResponseRedirect(self.success_url) - - def post(self, request, *args, **kwargs): - form = self.get_form() - required = 'add_ssh' in self.request.POST - form.fields['name'].required = required - form.fields['public_key'].required = required - if form.is_valid(): - return self.form_valid(form) - else: - return self.form_invalid(form) - - @method_decorator(decorators, name='dispatch') class SettingsView(LoginRequiredMixin, FormView): template_name = "hosting/settings.html" From 9b73fa71dc1c2018380523574717daf61a9a3e0c Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:10:37 +0530 Subject: [PATCH 49/79] Add datacenterlight add_ssh_key.html template file --- .../templates/datacenterlight/add_ssh_key.html | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 datacenterlight/templates/datacenterlight/add_ssh_key.html diff --git a/datacenterlight/templates/datacenterlight/add_ssh_key.html b/datacenterlight/templates/datacenterlight/add_ssh_key.html new file mode 100644 index 00000000..e083eece --- /dev/null +++ b/datacenterlight/templates/datacenterlight/add_ssh_key.html @@ -0,0 +1,8 @@ + +{% load staticfiles bootstrap3 i18n custom_tags humanize %} + +{% block content %} + {% block userkey_form %} + {% include 'hosting/user_key.html' with title="Your VM is almost ready!" sub_title="You just need to specify your public SSH key." %} + {% endblock userkey_form %} +{%endblock%} \ No newline at end of file From 5fcd0d6b18e08dc2bcc68b118f6d2d1347a42791 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:18:10 +0530 Subject: [PATCH 50/79] Remove add SSH key form in the order confirmation --- .../datacenterlight/order_detail.html | 32 ------------------- hosting/templates/hosting/order_detail.html | 29 ----------------- 2 files changed, 61 deletions(-) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index e1cc5853..31933e12 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -134,38 +134,6 @@
{% csrf_token %} - {% if generic_payment_details %} - {% else %} - {% comment %} - We are in VM buy flow and we want user to click the "Place order" button. - At this point, we also want the user to input the SSH key for the VM. - {% endcomment %} - - {% if messages %} -
- {% for message in messages %} - {{ message }} - {% endfor %} -
- {% endif %} -
-

 {% trans "Add your public SSH key" %}

-
-
- {% if keys|length > 0 %} -
Existing keys
- {% endif %} - {% for key in keys %} - -
- {% endfor %} -
- {% for field in form %} - {% bootstrap_field field %} - {% endfor %} - {% endif %}
{% if generic_payment_details %} diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 7ab79378..4a62e9fa 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -198,35 +198,6 @@ {% block submit_btn %} {% csrf_token %} - {% comment %} - We are in VM buy flow and we want user to click the "Place order" button. - At this point, we also want the user to input the SSH key for the VM. - {% endcomment %} - - {% if messages %} -
- {% for message in messages %} - {{ message }} - {% endfor %} -
- {% endif %} -
-

 {% trans "Add your public SSH key" %}

-
-
- {% if keys|length > 0 %} -
Existing keys
- {% endif %} - {% for key in keys %} - -
- {% endfor %} -
- {% for field in form %} - {% bootstrap_field field %} - {% endfor %}
{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.
From 79eba3b70c80c10d1f1a22713565d50ebd02a8e0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:28:28 +0530 Subject: [PATCH 51/79] Remove add SSH key form in order confirmation related code --- datacenterlight/views.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 99ceae8f..28372a89 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -588,31 +588,6 @@ class OrderConfirmationView(DetailView, FormView): return render(request, self.template_name, context) def post(self, request, *args, **kwargs): - # Check ssh public key and then proceed - form = self.get_form() - required = True - - # SSH key validation is required only if the user doesn't have an - # existing key and user has input some value in the add ssh key fields - if (len(get_all_public_keys(self.request.user)) > 0 and - (len(form.data.get('public_key')) == 0 and - len(form.data.get('name')) == 0)): - required = False - form.fields['name'].required = required - form.fields['public_key'].required = required - if not form.is_valid(): - response = { - 'status': False, - 'msg_title': str(_('SSH key related error occurred')), - 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), - } - return JsonResponse(response) - - if required: - # We have a valid SSH key from the user, save it in opennebula and - # db and proceed further - form.save() - user = request.session.get('user') stripe_api_cus_id = request.session.get('customer') stripe_utils = StripeUtils() From 2c74eae3f90dc049a15a1186fe007debfb657a5f Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:56:43 +0530 Subject: [PATCH 52/79] Remove commented code --- utils/views.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/utils/views.py b/utils/views.py index 93d778b4..00d97bbd 100644 --- a/utils/views.py +++ b/utils/views.py @@ -261,30 +261,4 @@ class AskSSHKeyView(SSHKeyCreateView): 'keys': get_all_public_keys(self.request.user) } return render(request, self.template_name, context) - # - # def post(self, request, *args, **kwargs): - # # Check ssh public key and then proceed - # form = self.get_form() - # required = True - # - # # SSH key validation is required only if the user doesn't have an - # # existing key and user has input some value in the add ssh key fields - # if (len(get_all_public_keys(self.request.user)) > 0 and - # (len(form.data.get('public_key')) == 0 and - # len(form.data.get('name')) == 0)): - # required = False - # form.fields['name'].required = required - # form.fields['public_key'].required = required - # if not form.is_valid(): - # response = { - # 'status': False, - # 'msg_title': str(_('SSH key related error occurred')), - # 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), - # } - # return JsonResponse(response) - # - # if required: - # # We have a valid SSH key from the user, save it in opennebula and - # # db and proceed further - # form.save() From 207c3a6c6c7930b710eadb351e65b542c5dfc46b Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 08:57:39 +0530 Subject: [PATCH 53/79] Skip SSH key page for generic products page --- datacenterlight/views.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 28372a89..edc95ee9 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -522,6 +522,12 @@ class PaymentOrderView(FormView): request.session['customer'] = customer.stripe_id else: request.session['customer'] = customer + + # For generic payment we take the user directly to confirmation + if ('generic_payment_type' in request.session and + self.request.session['generic_payment_type'] == 'generic'): + return HttpResponseRedirect( + reverse('datacenterlight:order_confirmation')) return HttpResponseRedirect( reverse('datacenterlight:add_ssh_key')) else: From c35bc79c5c642e2ba6e590a7158c9c45efa24f6c Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 20:28:26 +0530 Subject: [PATCH 54/79] Set success_url based on flow: hosting vs landing --- utils/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/views.py b/utils/views.py index 00d97bbd..e5eca845 100644 --- a/utils/views.py +++ b/utils/views.py @@ -262,3 +262,6 @@ class AskSSHKeyView(SSHKeyCreateView): } return render(request, self.template_name, context) + def post(self, request, *args, **kwargs): + self.success_url = self.request.get("order_confirm_url") + return super(AskSSHKeyView, self) \ No newline at end of file From c285e1d9eb8df95695bafe6c94e894338ed2ef0d Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 20:30:06 +0530 Subject: [PATCH 55/79] Set respective order_confirm_url for landing vs hosting flows For hosting flow also take the user to add_ssh_key after payment --- datacenterlight/views.py | 6 ++++-- hosting/views.py | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index edc95ee9..7b67acd5 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -528,8 +528,10 @@ class PaymentOrderView(FormView): self.request.session['generic_payment_type'] == 'generic'): return HttpResponseRedirect( reverse('datacenterlight:order_confirmation')) - return HttpResponseRedirect( - reverse('datacenterlight:add_ssh_key')) + else: + self.request.session['order_confirm_url'] = reverse('datacenterlight:order_confirmation') + return HttpResponseRedirect( + reverse('datacenterlight:add_ssh_key')) else: context = self.get_context_data() context['billing_address_form'] = address_form diff --git a/hosting/views.py b/hosting/views.py index 97b7f42c..9f7ce8cc 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -769,10 +769,10 @@ class PaymentVMView(LoginRequiredMixin, FormView): reverse('hosting:payment') + '#payment_error') request.session['token'] = token request.session['billing_address_data'] = billing_address_data - return HttpResponseRedirect("{url}?{query_params}".format( - url=reverse('hosting:order-confirmation'), - query_params='page=payment') - ) + 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')) else: return self.form_invalid(form) From d9a2c5216ce4afbd8031cfb6e165bef3f5725c66 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 20:30:35 +0530 Subject: [PATCH 56/79] Also remove order_confirm_url from session vars --- datacenterlight/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 208d39f3..4e56e812 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,8 +1,9 @@ import logging + import pyotp import requests -from django.contrib.sites.models import Site from django.conf import settings +from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task from hosting.models import HostingOrder, HostingBill, OrderDetail @@ -99,7 +100,8 @@ def clear_all_session_vars(request): for session_var in ['specs', 'template', 'billing_address', 'billing_address_data', 'card_id', 'token', 'customer', 'generic_payment_type', - 'generic_payment_details', 'product_id']: + 'generic_payment_details', 'product_id', + 'order_confirm_url']: if session_var in request.session: del request.session[session_var] From c8c5bb763a9dd46adcfb79548cc41c4d1fad301e Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 20:36:13 +0530 Subject: [PATCH 57/79] Remove Add SSH key form in "Order Confirm" page related code (not needed) --- hosting/views.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 9f7ce8cc..201d58e7 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -941,31 +941,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): @method_decorator(decorators) def post(self, request): - # Check ssh public key and then proceed - form = self.get_form() - required = True - - # SSH key validation is required only if the user doesn't have an - # existing key and user has input some value in the add ssh key fields - if (len(get_all_public_keys(self.request.user)) > 0 and - (len(form.data.get('public_key')) == 0 and - len(form.data.get('name')) == 0)): - required = False - form.fields['name'].required = required - form.fields['public_key'].required = required - if not form.is_valid(): - response = { - 'status': False, - 'msg_title': str(_('SSH key related error occurred')), - 'msg_body': "
".join([str(v) for k,v in form.errors.items()]), - } - return JsonResponse(response) - - if required: - # We have a valid SSH key from the user, save it in opennebula and - # db and proceed further - form.save() - template = request.session.get('template') specs = request.session.get('specs') stripe_utils = StripeUtils() From 561178e473a78834e2552d103ceae856624ad887 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 20:48:34 +0530 Subject: [PATCH 58/79] Set success_url from session and call super post method --- utils/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/views.py b/utils/views.py index e5eca845..01f4968c 100644 --- a/utils/views.py +++ b/utils/views.py @@ -263,5 +263,5 @@ class AskSSHKeyView(SSHKeyCreateView): return render(request, self.template_name, context) def post(self, request, *args, **kwargs): - self.success_url = self.request.get("order_confirm_url") - return super(AskSSHKeyView, self) \ No newline at end of file + self.success_url = self.request.session.get("order_confirm_url") + return super(AskSSHKeyView, self).post(self, request, *args, **kwargs) \ No newline at end of file From 8efe978b23824cd44abb5eeefab1002d5b5f3b51 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 21:01:41 +0530 Subject: [PATCH 59/79] Don't make SSHKeyCreateView with LoginRequiredMixin --- utils/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/views.py b/utils/views.py index 01f4968c..0429c7db 100644 --- a/utils/views.py +++ b/utils/views.py @@ -3,7 +3,6 @@ import uuid from django.conf import settings from django.contrib import messages from django.contrib.auth import authenticate, login -from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.tokens import default_token_generator from django.core.files.base import ContentFile from django.core.urlresolvers import reverse_lazy @@ -186,7 +185,7 @@ class PasswordResetConfirmViewMixin(FormView): return self.form_invalid(form) -class SSHKeyCreateView(LoginRequiredMixin, FormView): +class SSHKeyCreateView(FormView): form_class = UserHostingKeyForm model = UserHostingKey template_name = 'hosting/user_key.html' From a20dbc1f960ba22a2e1d59a5c8335027800ac4fd Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 23:08:35 +0530 Subject: [PATCH 60/79] Cleanup new_user_hosting_key_id session variable also --- datacenterlight/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 4e56e812..11d2b82e 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -101,7 +101,7 @@ def clear_all_session_vars(request): 'billing_address_data', 'card_id', 'token', 'customer', 'generic_payment_type', 'generic_payment_details', 'product_id', - 'order_confirm_url']: + 'order_confirm_url', 'new_user_hosting_key_id']: if session_var in request.session: del request.session[session_var] From 670c2b18a903ade0f4e0ccc11d7355658de047e2 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 23:09:42 +0530 Subject: [PATCH 61/79] Set user_hosting_key's user to the newly created user --- datacenterlight/views.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 7b67acd5..22294432 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -17,9 +17,10 @@ from hosting.forms import ( UserHostingKeyForm ) from hosting.models import ( - HostingBill, HostingOrder, UserCardDetail, GenericProduct + HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey ) from membership.models import CustomUser, StripeCustomer +from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VMTemplateSerializer from utils.forms import ( BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm, @@ -847,6 +848,18 @@ class OrderConfirmationView(DetailView, FormView): new_user = authenticate(username=custom_user.email, password=password) login(request, new_user) + if 'new_user_hosting_key_id' in self.request.session: + user_hosting_key = UserHostingKey.objects.get(self.request.session['new_user_hosting_key_id']) + user_hosting_key.user = new_user + user_hosting_key.save() + + owner = new_user + manager = OpenNebulaManager( + email=owner.email, + password=owner.password + ) + keys_to_save = get_all_public_keys(new_user) + manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) else: # We assume that if the user is here, his/her StripeCustomer # object already exists From 32de20aaba11e1899916525db437176ed7ac2292 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 23:11:14 +0530 Subject: [PATCH 62/79] Set unon authenticated user to NONE --- hosting/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/forms.py b/hosting/forms.py index 797bc700..1bc99b8f 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -222,7 +222,7 @@ class UserHostingKeyForm(forms.ModelForm): return self.data.get('name') def clean_user(self): - return self.request.user + return self.request.user if self.request.user.is_authenticated() else None def clean(self): cleaned_data = self.cleaned_data From ddaa3206285b5cf5aa422efb08030568c2bc85e5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 23:11:49 +0530 Subject: [PATCH 63/79] Set user foreign key to be blank allowing null values --- hosting/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index aead430c..311ee953 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -1,11 +1,11 @@ import json import logging import os -import pytz +from datetime import datetime +import pytz from Crypto.PublicKey import RSA from dateutil.relativedelta import relativedelta -from datetime import datetime from django.db import models from django.utils import timezone from django.utils.functional import cached_property @@ -187,7 +187,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model): class UserHostingKey(models.Model): - user = models.ForeignKey(CustomUser) + user = models.ForeignKey(CustomUser, blank=True, null=True) public_key = models.TextField() private_key = models.FileField(upload_to='private_keys', blank=True) created_at = models.DateTimeField(auto_now_add=True) From 26fab27c3fb476c255b02f5fec343d2e2d31d9e7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Mon, 1 Jul 2019 23:17:27 +0530 Subject: [PATCH 64/79] Set new_user_hosting_key_id session variable to track newly created key --- utils/views.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/utils/views.py b/utils/views.py index 0429c7db..05d0fdc2 100644 --- a/utils/views.py +++ b/utils/views.py @@ -225,13 +225,16 @@ class SSHKeyCreateView(FormView): 'form': UserHostingKeyForm(request=self.request), }) - owner = self.request.user - manager = OpenNebulaManager( - email=owner.email, - password=owner.password - ) - keys_to_save = get_all_public_keys(self.request.user) - manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) + if self.request.user.is_authenticated(): + owner = self.request.user + manager = OpenNebulaManager( + email=owner.email, + password=owner.password + ) + keys_to_save = get_all_public_keys(self.request.user) + manager.save_key_in_opennebula_user('\n'.join(keys_to_save)) + else: + self.request.session["new_user_hosting_key_id"] = form.instance.id return HttpResponseRedirect(self.success_url) def post(self, request, *args, **kwargs): From dfb16f0c25d0ca9b880efc14467c4d234d905055 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 05:13:02 +0530 Subject: [PATCH 65/79] Save new user's hosting key only in that case --- datacenterlight/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 22294432..3763d465 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -848,10 +848,10 @@ class OrderConfirmationView(DetailView, FormView): new_user = authenticate(username=custom_user.email, password=password) login(request, new_user) - if 'new_user_hosting_key_id' in self.request.session: - user_hosting_key = UserHostingKey.objects.get(self.request.session['new_user_hosting_key_id']) - user_hosting_key.user = new_user - user_hosting_key.save() + if 'new_user_hosting_key_id' in self.request.session: + user_hosting_key = UserHostingKey.objects.get(id=self.request.session['new_user_hosting_key_id']) + user_hosting_key.user = new_user + user_hosting_key.save() owner = new_user manager = OpenNebulaManager( From 921d832f9e9ab553771f3874778174e77ce07b80 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 06:44:31 +0530 Subject: [PATCH 66/79] Make user in UserHostingKey model nullable --- hosting/migrations/0055_auto_20190701_1614.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 hosting/migrations/0055_auto_20190701_1614.py diff --git a/hosting/migrations/0055_auto_20190701_1614.py b/hosting/migrations/0055_auto_20190701_1614.py new file mode 100644 index 00000000..4a2744fb --- /dev/null +++ b/hosting/migrations/0055_auto_20190701_1614.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2019-07-01 16:14 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0054_auto_20190508_2141'), + ] + + operations = [ + migrations.AlterField( + model_name='userhostingkey', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] From 44921014a21d074e9e2510186004d252e278336d Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 07:09:44 +0530 Subject: [PATCH 67/79] Get the correct opennebula user id --- opennebula_api/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 478758fc..f8ef6481 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -376,7 +376,7 @@ class OpenNebulaManager(): """ return_value = self.oneadmin_client.call( 'user.update', - self.opennebula_user.id, + self.opennebula_user if type(self.opennebula_user) == int else self.opennebula_user.id, '%s' % ssh_key, update_type ) From 6f49157ddd81522982f70ede42afe604d22d8411 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 16:27:12 +0530 Subject: [PATCH 68/79] Update add_ssh_key subtitle --- datacenterlight/templates/datacenterlight/add_ssh_key.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/add_ssh_key.html b/datacenterlight/templates/datacenterlight/add_ssh_key.html index e083eece..936bd782 100644 --- a/datacenterlight/templates/datacenterlight/add_ssh_key.html +++ b/datacenterlight/templates/datacenterlight/add_ssh_key.html @@ -3,6 +3,6 @@ {% block content %} {% block userkey_form %} - {% include 'hosting/user_key.html' with title="Your VM is almost ready!" sub_title="You just need to specify your public SSH key." %} + {% include 'hosting/user_key.html' with title="Your VM is almost ready!" sub_title="You need to specify your public SSH key to access your VM. You can either add your existing key, or generate a new key pair by clicking the generate button below. After choosing your public SSH key option you’ll be directed to the order confirmation page." %} {% endblock userkey_form %} {%endblock%} \ No newline at end of file From b0548f4cfa3f4d5b3fa9c86188ed4eae4eeec5c0 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 16:51:29 +0530 Subject: [PATCH 69/79] Translate ssh key form title and subtitles --- datacenterlight/templates/datacenterlight/add_ssh_key.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datacenterlight/templates/datacenterlight/add_ssh_key.html b/datacenterlight/templates/datacenterlight/add_ssh_key.html index 936bd782..44048bad 100644 --- a/datacenterlight/templates/datacenterlight/add_ssh_key.html +++ b/datacenterlight/templates/datacenterlight/add_ssh_key.html @@ -3,6 +3,8 @@ {% block content %} {% block userkey_form %} - {% include 'hosting/user_key.html' with title="Your VM is almost ready!" sub_title="You need to specify your public SSH key to access your VM. You can either add your existing key, or generate a new key pair by clicking the generate button below. After choosing your public SSH key option you’ll be directed to the order confirmation page." %} + {% with form_title=_("Your VM is almost ready!") form_sub_title=_("You need to specify your public SSH key to access your VM. You can either add your existing key, or generate a new key pair by clicking the generate button below. After choosing your public SSH key option you’ll be directed to the order confirmation page.") %} + {% include 'hosting/user_key.html' with title=form_title sub_title=form_sub_title %} + {% endwith %} {% endblock userkey_form %} {%endblock%} \ No newline at end of file From 69401a1cc6ee6cca7417b15de47afd38f81f3b3b Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 16:52:08 +0530 Subject: [PATCH 70/79] Make messages for datacenterlight and add some DE translations --- .../locale/de/LC_MESSAGES/django.po | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index d43e91ea..d6a25b53 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-26 20:44+0000\n" +"POT-Creation-Date: 2019-07-03 11:18+0000\n" "PO-Revision-Date: 2018-03-30 23:22+0000\n" "Last-Translator: b'Anonymous User '\n" "Language-Team: LANGUAGE \n" @@ -26,6 +26,22 @@ msgstr "" msgid "Your New VM %(vm_name)s at Data Center Light" msgstr "Deine neue VM %(vm_name)s bei Data Center Light" +msgid "Your VM is almost ready!" +msgstr "" + +msgid "" +"You need to specify your public SSH key to access your VM. You can either " +"add your existing key, or generate a new key pair by clicking the generate " +"button below. After choosing your public SSH key option you’ll be directed " +"to the order confirmation page." +msgstr "" +"Du musst deinen öffentlichen SSH-Schlüssel angeben, um auf deine VM " +"zugreifen zu können. Du kannst entweder deinen vorhandenen Schlüssel " +"hinzufügen oder ein neues Schlüsselpaar generieren, indem du auf die " +"Schaltfläche \"Generieren\" unten klickst. Nachdem du deine öffentliche SSH-" +"Schlüsseloption ausgewählt hast, wirst du zur Bestellbestätigungsseite " +"weitergeleitet. " + msgid "All Rights Reserved" msgstr "Alle Rechte vorbehalten" @@ -134,6 +150,10 @@ msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!" msgid "ORDER VM" msgstr "VM BESTELLEN" +#, python-format +msgid "Please enter a value in range %(min_ram)s - 200." +msgstr "Bitte gib einen Wert von %(min_ram)s bis 200 ein." + msgid "VM hosting" msgstr "" @@ -152,9 +172,6 @@ msgstr "Standort: Schweiz" msgid "Please enter a value in range 1 - 48." msgstr "Bitte gib einen Wert von 1 bis 48 ein." -msgid "Please enter a value in range 1 - 200." -msgstr "Bitte gib einen Wert von 1 bis 200 ein." - msgid "Please enter a value in range 10 - 2000." msgstr "Bitte gib einen Wert von 10 bis 2000 ein." @@ -413,6 +430,10 @@ msgstr "Zwischensumme" msgid "VAT" msgstr "Mehrwertsteuer" +#, fuzzy, python-format +#| msgid "" +#| "By clicking \"Place order\" this plan will charge your credit card " +#| "account with %(total_price)s CHF/month" msgid "" "By clicking \"Place order\" this plan will charge your credit card account " "with %(total_price)s CHF/month" @@ -420,6 +441,10 @@ msgstr "" "Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit " "%(vm_total_price)s CHF pro Monat belastet" +#, fuzzy, python-format +#| msgid "" +#| "By clicking \"Place order\" this payment will charge your credit card " +#| "account with a one time amount of %(total_price)s CHF" msgid "" "By clicking \"Place order\" this payment will charge your credit card " "account with a one time amount of %(total_price)s CHF" @@ -535,6 +560,9 @@ msgstr "Tagen sagen mehr als Worte – Teste jetzt unsere VM!" msgid "Invalid number of cores" msgstr "Ungültige Anzahle CPU-Kerne" +msgid "Invalid calculator properties" +msgstr "" + msgid "Invalid RAM size" msgstr "Ungültige RAM-Grösse" From 2a4fb8c8dee0372a9e547c016ff1e57b183eb6d3 Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 16:57:00 +0530 Subject: [PATCH 71/79] Add DE translation Your VM is almost ready! => Ihre VM ist fast fertig! --- datacenterlight/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index d6a25b53..07361e29 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -27,7 +27,7 @@ msgid "Your New VM %(vm_name)s at Data Center Light" msgstr "Deine neue VM %(vm_name)s bei Data Center Light" msgid "Your VM is almost ready!" -msgstr "" +msgstr "Ihre VM ist fast fertig!" msgid "" "You need to specify your public SSH key to access your VM. You can either " From d3e2074b1616020debbb7cca69b3fe709ddb19cc Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 17:29:42 +0530 Subject: [PATCH 72/79] Update DE translation Your VM is almost ready! => Deine VM ist fast fertig! --- datacenterlight/locale/de/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datacenterlight/locale/de/LC_MESSAGES/django.po b/datacenterlight/locale/de/LC_MESSAGES/django.po index 07361e29..b5ff3ca5 100644 --- a/datacenterlight/locale/de/LC_MESSAGES/django.po +++ b/datacenterlight/locale/de/LC_MESSAGES/django.po @@ -27,7 +27,7 @@ msgid "Your New VM %(vm_name)s at Data Center Light" msgstr "Deine neue VM %(vm_name)s bei Data Center Light" msgid "Your VM is almost ready!" -msgstr "Ihre VM ist fast fertig!" +msgstr "Deine VM ist fast fertig!" msgid "" "You need to specify your public SSH key to access your VM. You can either " From 3c63f26d31ce095b93b39f0b383fcfc686a155ce Mon Sep 17 00:00:00 2001 From: PCoder Date: Wed, 3 Jul 2019 20:59:48 +0530 Subject: [PATCH 73/79] Update Changelog for 2.6 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index cfefdd01..a9916173 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.6: 2019-07-03 + * #5509: Getting rid of our key by still supporting multiple user keys (MR!709) 2.5.11: 2019-06-11 * #6672: [api] Check VM belongs to user in the infrastructure directly (MR!707) * #bugfix: DE translation fix "Learn mehr" -> "Lerne mehr" (MR!708) From 59c45492a935048c942f588afa424e4aed0457ee Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 18:40:41 +0530 Subject: [PATCH 74/79] Add expiry year and month in the settings and order payment pages --- datacenterlight/templates/datacenterlight/order_detail.html | 1 + datacenterlight/views.py | 4 ++++ hosting/templates/hosting/order_detail.html | 1 + hosting/templates/hosting/settings.html | 1 + hosting/views.py | 4 ++++ 5 files changed, 11 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/order_detail.html b/datacenterlight/templates/datacenterlight/order_detail.html index 31933e12..8a444bef 100644 --- a/datacenterlight/templates/datacenterlight/order_detail.html +++ b/datacenterlight/templates/datacenterlight/order_detail.html @@ -41,6 +41,7 @@

{% trans "Payment method" %}:

{{cc_brand|default:_('Credit Card')}} {% trans "ending in" %} ****{{cc_last4}}
+ {% trans "Expiry" %} {{cc_exp_year}}/{{cc_exp_month}}
{{request.user.email}}

diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 3763d465..60938ac8 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -568,11 +568,15 @@ class OrderConfirmationView(DetailView, FormView): card_details_response = card_details['response_object'] context['cc_last4'] = card_details_response['last4'] context['cc_brand'] = card_details_response['brand'] + context['cc_exp_year'] = card_details_response['exp_year'] + context['cc_exp_month'] = card_details_response['exp_month'] 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 + context['cc_exp_year'] = card_detail.exp_year + context['cc_exp_month'] = card_detail.exp_month if ('generic_payment_type' in request.session and self.request.session['generic_payment_type'] == 'generic'): diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 4a62e9fa..a84e4e4f 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -82,6 +82,7 @@ {{user.email}} {% else %} {{cc_brand|default:_('Credit Card')}} {% trans "ending in" %} ****{{cc_last4}}
+ {% trans "Expiry" %} {{cc_exp_year}}/{{cc_exp_month}}
{% if request.user.is_authenticated %} {{request.user.email}} {% else %} diff --git a/hosting/templates/hosting/settings.html b/hosting/templates/hosting/settings.html index 56818cbf..5cdd830c 100644 --- a/hosting/templates/hosting/settings.html +++ b/hosting/templates/hosting/settings.html @@ -37,6 +37,7 @@
{% trans "Credit Card" %}
{% trans "Last" %} 4: ***** {{card.last4}}
{% trans "Type" %}: {{card.brand}}
+
{% trans "Expiry" %}: {{card.exp_month}}/{{card.exp_year}}
{% if card_list_len > 1 %} diff --git a/hosting/views.py b/hosting/views.py index 201d58e7..9a2e5b19 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -906,11 +906,15 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): card_details_response = card_details['response_object'] context['cc_last4'] = card_details_response['last4'] context['cc_brand'] = card_details_response['brand'] + context['cc_exp_year'] = card_details_response['exp_year'] + context['cc_exp_month'] = card_details_response['exp_month'] 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 + context['cc_exp_year'] = card_detail.exp_year + context['cc_exp_month'] = card_detail.exp_month context['site_url'] = reverse('hosting:create_virtual_machine') context['vm'] = self.request.session.get('specs') return context From fe44908868ae22336e803633751f4ec6bcc773a5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 18:47:54 +0530 Subject: [PATCH 75/79] Add expiry year and month to get_all_cards_list --- hosting/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hosting/models.py b/hosting/models.py index 311ee953..e6e29cbe 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -597,6 +597,8 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): for card in user_card_details: cards_list.append({ 'last4': card.last4, 'brand': card.brand, 'id': card.id, + 'exp_year': card.exp_year, + 'exp_month': card.exp_month, 'preferred': card.preferred }) return cards_list From 903ef48c758e5882ac74b514aa6f3865136568b8 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 19:03:09 +0530 Subject: [PATCH 76/79] Format cc month to 2 decimal places --- datacenterlight/views.py | 4 ++-- hosting/models.py | 2 +- hosting/views.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 60938ac8..ae649623 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -569,14 +569,14 @@ class OrderConfirmationView(DetailView, FormView): context['cc_last4'] = card_details_response['last4'] context['cc_brand'] = card_details_response['brand'] context['cc_exp_year'] = card_details_response['exp_year'] - context['cc_exp_month'] = card_details_response['exp_month'] + context['cc_exp_month'] = '{:02d}'.format(card_details_response['exp_month']) 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 context['cc_exp_year'] = card_detail.exp_year - context['cc_exp_month'] = card_detail.exp_month + context['cc_exp_month'] ='{:02d}'.format(card_detail.exp_month) if ('generic_payment_type' in request.session and self.request.session['generic_payment_type'] == 'generic'): diff --git a/hosting/models.py b/hosting/models.py index e6e29cbe..7b08948e 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -598,7 +598,7 @@ class UserCardDetail(AssignPermissionsMixin, models.Model): cards_list.append({ 'last4': card.last4, 'brand': card.brand, 'id': card.id, 'exp_year': card.exp_year, - 'exp_month': card.exp_month, + 'exp_month': '{:02d}'.format(card.exp_month), 'preferred': card.preferred }) return cards_list diff --git a/hosting/views.py b/hosting/views.py index 9a2e5b19..87cb948c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -914,7 +914,7 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView): context['cc_last4'] = card_detail.last4 context['cc_brand'] = card_detail.brand context['cc_exp_year'] = card_detail.exp_year - context['cc_exp_month'] = card_detail.exp_month + context['cc_exp_month'] = '{:02d}'.format(card_detail.exp_month) context['site_url'] = reverse('hosting:create_virtual_machine') context['vm'] = self.request.session.get('specs') return context From b6ec2ac95b4e269806db4c3dc1a04f301f15e66a Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 19:08:19 +0530 Subject: [PATCH 77/79] Add missing cc expiry year month in payment page --- datacenterlight/templates/datacenterlight/landing_payment.html | 1 + hosting/templates/hosting/payment.html | 1 + 2 files changed, 2 insertions(+) diff --git a/datacenterlight/templates/datacenterlight/landing_payment.html b/datacenterlight/templates/datacenterlight/landing_payment.html index fb6d51b0..4e71eab9 100644 --- a/datacenterlight/templates/datacenterlight/landing_payment.html +++ b/datacenterlight/templates/datacenterlight/landing_payment.html @@ -131,6 +131,7 @@
{% trans "Credit Card" %}
{% trans "Last" %} 4: ***** {{card.last4}}
{% trans "Type" %}: {{card.brand}}
+
{% trans "Expiry" %}: {{card.exp_month}}/{{card.exp_year}}
{% trans "SELECT" %} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index e09775cf..f0512fdb 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -131,6 +131,7 @@
{% trans "Credit Card" %}
{% trans "Last" %} 4: ***** {{card.last4}}
{% trans "Type" %}: {{card.brand}}
+
{% trans "Expiry" %}: {{card.exp_month}}/{{card.exp_year}}
{% trans "SELECT" %} From 728fd5850bae755cc3ba63ffe7eb002c97ecd949 Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 21:21:22 +0530 Subject: [PATCH 78/79] =?UTF-8?q?Update=20hosting=20django.po=20--=20add?= =?UTF-8?q?=20"Expiry"=20->=20G=C3=BCltig=20bis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hosting/locale/de/LC_MESSAGES/django.po | 214 +++++++++++++++--------- 1 file changed, 133 insertions(+), 81 deletions(-) diff --git a/hosting/locale/de/LC_MESSAGES/django.po b/hosting/locale/de/LC_MESSAGES/django.po index 0e337cfb..14d48da9 100644 --- a/hosting/locale/de/LC_MESSAGES/django.po +++ b/hosting/locale/de/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-08 08:45+0000\n" +"POT-Creation-Date: 2019-07-09 15:21+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -27,6 +27,30 @@ msgstr "Dein Account wurde noch nicht aktiviert." msgid "User does not exist" msgstr "Der Benutzer existiert nicht" +msgid "Choose a product" +msgstr "" + +msgid "Amount in CHF" +msgstr "Betrag" + +msgid "Recurring monthly" +msgstr "" + +msgid "Amount field does not match" +msgstr "" + +msgid "Recurring field does not match" +msgstr "" + +msgid "Product name" +msgstr "Produkt" + +msgid "Monthly subscription" +msgstr "" + +msgid "One time payment" +msgstr "" + msgid "Confirm Password" msgstr "Passwort Bestätigung" @@ -52,6 +76,9 @@ msgstr "Bitte verwende einen gültigen SSH-Key" msgid "This key exists already with the name \"%(name)s\"" msgstr "Der SSH-Key mit dem Name \"%(name)s\" existiert bereits" +msgid "Comma not accepted in the name of the key" +msgstr "" + msgid "All Rights Reserved" msgstr "Alle Rechte vorbehalten" @@ -209,7 +236,8 @@ msgstr "Du hast eine neue virtuelle Maschine bestellt!" #, python-format msgid "Your order of %(vm_name)s has been charged." -msgstr "Deine Bestellung von %(vm_name)s wurde entgegengenommen." +msgstr "" +"Deine Bestellung von %(vm_name)s wurde entgegengenommen." msgid "You can view your VM detail by clicking the button below." msgstr "Um die Rechnung zu sehen, klicke auf den Button unten." @@ -305,6 +333,100 @@ msgstr "Dashboard" msgid "Logout" msgstr "Abmelden" +#, python-format +msgid "%(page_header_text)s" +msgstr "" + +msgid "Invoice #" +msgstr "Rechnung" + +msgid "Date" +msgstr "Datum" + +msgid "Status" +msgstr "" + +msgid "Terminated" +msgstr "Beendet" + +msgid "Approved" +msgstr "Akzeptiert" + +msgid "Declined" +msgstr "Abgelehnt" + +msgid "Billed to" +msgstr "Rechnungsadresse" + +msgid "Payment method" +msgstr "Bezahlmethode" + +msgid "ending in" +msgstr "endend in" + +msgid "Invoice summary" +msgstr "" + +msgid "Product" +msgstr "Produkt" + +msgid "Period" +msgstr "Periode" + +msgid "Cores" +msgstr "Prozessorkerne" + +msgid "Memory" +msgstr "Arbeitsspeicher" + +msgid "Disk space" +msgstr "Festplattenkapazität" + +msgid "Subtotal" +msgstr "Zwischensumme" + +msgid "VAT" +msgstr "Mehrwertsteuer" + +msgid "Discount" +msgstr "Rabatt" + +msgid "Total" +msgstr "Gesamt" + +msgid "Amount" +msgstr "Betrag" + +msgid "Description" +msgstr "" + +msgid "Recurring" +msgstr "" + +msgid "of every month" +msgstr "" + +msgid "BACK TO LIST" +msgstr "ZURÜCK ZUR LISTE" + +msgid "Some problem encountered. Please try again later." +msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." + +msgid "VM ID" +msgstr "" + +msgid "IP Address" +msgstr "" + +msgid "See Invoice" +msgstr "Siehe Rechnung" + +msgid "Page" +msgstr "" + +msgid "of" +msgstr "" + msgid "Log in" msgstr "Anmelden" @@ -338,67 +460,15 @@ msgstr "Als gelesen markieren" msgid "All notifications" msgstr "Alle Benachrichtigungen" -#, python-format -msgid "%(page_header_text)s" -msgstr "" - -msgid "Date" -msgstr "Datum" - -msgid "Status" -msgstr "" - -msgid "Terminated" -msgstr "Beendet" - -msgid "Approved" -msgstr "Akzeptiert" - -msgid "Declined" -msgstr "Abgelehnt" - -msgid "Billed to" -msgstr "Rechnungsadresse" - -msgid "Payment method" -msgstr "Bezahlmethode" - -msgid "ending in" -msgstr "endend in" - msgid "Credit Card" msgstr "Kreditkarte" +msgid "Expiry" +msgstr "Gültig bis" + msgid "Order summary" msgstr "Bestellungsübersicht" -msgid "Product" -msgstr "Produkt" - -msgid "Period" -msgstr "Periode" - -msgid "Cores" -msgstr "Prozessorkerne" - -msgid "Memory" -msgstr "Arbeitsspeicher" - -msgid "Disk space" -msgstr "Festplattenkapazität" - -msgid "Subtotal" -msgstr "Zwischensumme" - -msgid "VAT" -msgstr "Mehrwertsteuer" - -msgid "Discount" -msgstr "Rabatt" - -msgid "Total" -msgstr "Gesamt" - #, python-format msgid "" "By clicking \"Place order\" this plan will charge your credit card account " @@ -410,9 +480,6 @@ msgstr "" msgid "Place order" msgstr "Bestellen" -msgid "BACK TO LIST" -msgstr "ZURÜCK ZUR LISTE" - msgid "Processing..." msgstr "Abarbeitung..." @@ -425,24 +492,9 @@ msgstr "" msgid "Close" msgstr "Schliessen" -msgid "Some problem encountered. Please try again later." -msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." - msgid "Order Nr." msgstr "Bestellung Nr." -msgid "Amount" -msgstr "Betrag" - -msgid "See Invoice" -msgstr "Siehe Rechnung" - -msgid "Page" -msgstr "" - -msgid "of" -msgstr "" - msgid "Your Order" msgstr "Deine Bestellung" @@ -539,9 +591,6 @@ msgstr "" "Wir nutzen Stripe für " "die Bezahlung und speichern keine Informationen in unserer Datenbank." -msgid "Add your public SSH key" -msgstr "Füge deinen öffentlichen SSH-Key hinzu" - msgid "Use your created key to access to the VM" msgstr "Benutze deinen erstellten SSH-Key um auf deine VM zugreifen zu können" @@ -783,6 +832,9 @@ msgstr "" msgid "Invalid number of cores" msgstr "Ungültige Anzahle CPU-Kerne" +msgid "Invalid calculator properties" +msgstr "" + msgid "Invalid RAM size" msgstr "Ungültige RAM-Grösse" @@ -821,6 +873,9 @@ msgstr "" "Es gab einen Fehler bei der Bearbeitung Deine Anfrage. Bitte versuche es " "noch einmal." +#~ msgid "Add your public SSH key" +#~ msgstr "Füge deinen öffentlichen SSH-Key hinzu" + #~ msgid "Do you want to cancel your Virtual Machine" #~ msgstr "Bist Du sicher, dass Du Deine virtuelle Maschine beenden willst" @@ -830,9 +885,6 @@ msgstr "" #~ msgid "My VM page" #~ msgstr "Meine VM page" -#~ msgid "Invoice Date" -#~ msgstr "Rechnung Datum" - #~ msgid "VM %(VM_ID)s terminated successfully" #~ msgstr "VM %(VM_ID)s erfolgreich beendet" From 39699da8eeea1cc811c66449a3a0a1728f9ac2df Mon Sep 17 00:00:00 2001 From: PCoder Date: Tue, 9 Jul 2019 21:34:00 +0530 Subject: [PATCH 79/79] Update Changelog for 2.6.1 --- Changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog b/Changelog index a9916173..63c59e11 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,5 @@ +2.6.1: 2019-07-09 + * #6941: [hosting dashboard] Show the card's expiry year & month too in the list of added cards (MR!710) 2.6: 2019-07-03 * #5509: Getting rid of our key by still supporting multiple user keys (MR!709) 2.5.11: 2019-06-11