Merge upstream master
This commit is contained in:
		
				commit
				
					
						5a2a134070
					
				
			
		
					 14 changed files with 202 additions and 46 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -36,3 +36,4 @@ secret-key | ||||||
| .env | .env | ||||||
| *.mo | *.mo | ||||||
| *.log | *.log | ||||||
|  | *.sql | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | Next: | ||||||
|  |     * #3764: [hosting] Show cancelled VMs' invoices  | ||||||
| 1.2.3: 2017-09-25 | 1.2.3: 2017-09-25 | ||||||
|     * #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email |     * #3484: [dcl, hosting] Refactored account activation, password reset, VM order and cancellation email | ||||||
|     * #3731: [dcl, hosting] Added cdist ssh key handler  |     * #3731: [dcl, hosting] Added cdist ssh key handler  | ||||||
|  | @ -8,6 +10,7 @@ | ||||||
|     * #3777: [hosting] Create new VM calculator added like dcl landing |     * #3777: [hosting] Create new VM calculator added like dcl landing | ||||||
|     * #3781: [hosting] Resend activation mail |     * #3781: [hosting] Resend activation mail | ||||||
|     * #3806: [hosting] Fix can not create VMs after password reset |     * #3806: [hosting] Fix can not create VMs after password reset | ||||||
|  |     * #3812: [hosting] Modal check icon made thin and font-size fixed | ||||||
|     * Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls |     * Feature: [cms, blog] Added /cms prefix for all the django-cms generated urls | ||||||
|     * Bugfix: [dcl, hosting] added host to celery error mails |     * Bugfix: [dcl, hosting] added host to celery error mails | ||||||
|     * Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch |     * Bugfix: [ungleich] Fixed wrong subdomain digitalglarus.ungleich.ch | ||||||
|  |  | ||||||
|  | @ -1653,3 +1653,20 @@ a.list-group-item-danger.active:focus { | ||||||
| .panel-danger > .panel-heading .badge { | .panel-danger > .panel-heading .badge { | ||||||
|     background-color: #eb4d5c; |     background-color: #eb4d5c; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .checkmark { | ||||||
|  |   display: inline-block; | ||||||
|  | } | ||||||
|  | .checkmark:after { | ||||||
|  |   /*Add another block-level blank space*/ | ||||||
|  |   content: ''; | ||||||
|  |   display: block; | ||||||
|  |   /*Make it a small rectangle so the border will create an L-shape*/ | ||||||
|  |   width: 25px; | ||||||
|  |   height: 60px; | ||||||
|  |   /*Add a white border on the bottom and left, creating that 'L' */ | ||||||
|  |   border: solid #777; | ||||||
|  |   border-width: 0 3px 3px 0; | ||||||
|  |   /*Rotate the L 45 degrees to turn it into a checkmark*/ | ||||||
|  |   transform: rotate(45deg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ from hosting.models import HostingOrder, HostingBill | ||||||
| from membership.models import StripeCustomer, CustomUser | from membership.models import StripeCustomer, CustomUser | ||||||
| from opennebula_api.models import OpenNebulaManager | from opennebula_api.models import OpenNebulaManager | ||||||
| from opennebula_api.serializers import VirtualMachineSerializer | from opennebula_api.serializers import VirtualMachineSerializer | ||||||
| from utils.hosting_utils import get_all_public_keys | from utils.hosting_utils import get_all_public_keys, get_or_create_vm_detail | ||||||
| from utils.forms import UserBillingAddressForm | from utils.forms import UserBillingAddressForm | ||||||
| from utils.mailer import BaseEmail | from utils.mailer import BaseEmail | ||||||
| from utils.models import BillingAddress | from utils.models import BillingAddress | ||||||
|  | @ -176,6 +176,7 @@ def create_vm_task(self, vm_template_id, user, specs, template, | ||||||
|             logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) |             logger.debug("New VM ID is {vm_id}".format(vm_id=vm_id)) | ||||||
|             if new_host is not None: |             if new_host is not None: | ||||||
|                 custom_user = CustomUser.objects.get(email=user.get('email')) |                 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: |                 if custom_user is not None: | ||||||
|                     public_keys = get_all_public_keys(custom_user) |                     public_keys = get_all_public_keys(custom_user) | ||||||
|                     keys = [{'value': key, 'state': True} for key in |                     keys = [{'value': key, 'state': True} for key in | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
|               <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> |               <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-body"> |             <div class="modal-body"> | ||||||
|               <div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div> |               <div class="modal-icon"><i class="checkmark" aria-hidden="true"></i></div> | ||||||
|               <h4 class="modal-title">{% trans "Request Sent" %}</h4> |               <h4 class="modal-title">{% trans "Request Sent" %}</h4> | ||||||
|               <p class="modal-text">{% trans "Thank you for your subscription! You will receive a confirmation mail from our team" %}</p> |               <p class="modal-text">{% trans "Thank you for your subscription! You will receive a confirmation mail from our team" %}</p> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  | @ -388,7 +388,7 @@ msgid "Processing..." | ||||||
| msgstr "Abarbeitung..." | msgstr "Abarbeitung..." | ||||||
| 
 | 
 | ||||||
| msgid "Hold tight, we are processing your request" | msgid "Hold tight, we are processing your request" | ||||||
| msgstr "Bitte warten - wir verbeiten Deine Anfrage gerade" | msgstr "Bitte warten - wir bearbeiten Deine Anfrage gerade" | ||||||
| 
 | 
 | ||||||
| msgid "Some problem encountered. Please try again later." | msgid "Some problem encountered. Please try again later." | ||||||
| msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." | msgstr "Ein Problem ist aufgetreten. Bitte versuche es später noch einmal." | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								hosting/migrations/0043_vmdetail.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								hosting/migrations/0043_vmdetail.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Generated by Django 1.9.4 on 2017-09-24 18:12 | ||||||
|  | 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 = [ | ||||||
|  |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|  |         ('hosting', '0042_hostingorder_subscription_id'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='VMDetail', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||||
|  |                 ('vm_id', models.IntegerField(default=0)), | ||||||
|  |                 ('disk_size', models.FloatField(default=0.0)), | ||||||
|  |                 ('cores', models.FloatField(default=0.0)), | ||||||
|  |                 ('memory', models.FloatField(default=0.0)), | ||||||
|  |                 ('configuration', models.CharField(default='', max_length=25)), | ||||||
|  |                 ('ipv4', models.TextField(default='')), | ||||||
|  |                 ('ipv6', models.TextField(default='')), | ||||||
|  |                 ('created_at', models.DateTimeField(auto_now_add=True)), | ||||||
|  |                 ('terminated_at', models.DateTimeField(null=True)), | ||||||
|  |                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -159,3 +159,16 @@ class HostingBill(AssignPermissionsMixin, models.Model): | ||||||
|         instance = cls.objects.create(customer=customer, |         instance = cls.objects.create(customer=customer, | ||||||
|                                       billing_address=billing_address) |                                       billing_address=billing_address) | ||||||
|         return instance |         return instance | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class VMDetail(models.Model): | ||||||
|  |     user = models.ForeignKey(CustomUser) | ||||||
|  |     vm_id = models.IntegerField(default=0) | ||||||
|  |     disk_size = models.FloatField(default=0.0) | ||||||
|  |     cores = models.FloatField(default=0.0) | ||||||
|  |     memory = models.FloatField(default=0.0) | ||||||
|  |     configuration = models.CharField(default='', max_length=25) | ||||||
|  |     ipv4 = models.TextField(default='') | ||||||
|  |     ipv6 = models.TextField(default='') | ||||||
|  |     created_at = models.DateTimeField(auto_now_add=True) | ||||||
|  |     terminated_at = models.DateTimeField(null=True) | ||||||
|  |  | ||||||
|  | @ -870,3 +870,41 @@ a.list-group-item-danger.active:focus { | ||||||
| .panel-danger > .panel-heading .badge { | .panel-danger > .panel-heading .badge { | ||||||
|     background-color: #eb4d5c; |     background-color: #eb4d5c; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .checkmark { | ||||||
|  |   display: inline-block; | ||||||
|  | } | ||||||
|  | .checkmark:after { | ||||||
|  |   /*Add another block-level blank space*/ | ||||||
|  |   content: ''; | ||||||
|  |   display: block; | ||||||
|  |   /*Make it a small rectangle so the border will create an L-shape*/ | ||||||
|  |   width: 25px; | ||||||
|  |   height: 60px; | ||||||
|  |   /*Add a white border on the bottom and left, creating that 'L' */ | ||||||
|  |   border: solid #777; | ||||||
|  |   border-width: 0 3px 3px 0; | ||||||
|  |   /*Rotate the L 45 degrees to turn it into a checkmark*/ | ||||||
|  |   transform: rotate(45deg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .closemark { | ||||||
|  |     display: inline-block; | ||||||
|  |     width: 50px; | ||||||
|  |     height: 50px; | ||||||
|  |     position: relative; | ||||||
|  | } | ||||||
|  | .closemark:before, .closemark:after { | ||||||
|  |   position: absolute; | ||||||
|  |   left: 25px; | ||||||
|  |   content: ' '; | ||||||
|  |   height: 50px; | ||||||
|  |   width: 2px; | ||||||
|  |   background-color: #777; | ||||||
|  | } | ||||||
|  | .closemark:before { | ||||||
|  |   transform: rotate(45deg); | ||||||
|  | } | ||||||
|  | .closemark:after { | ||||||
|  |   transform: rotate(-45deg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -79,7 +79,6 @@ $(document).ready(function() { | ||||||
|         $('html,body').scrollTop(scrollmem); |         $('html,body').scrollTop(scrollmem); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     $('.modal-text').removeClass('hide'); |  | ||||||
|     var create_vm_form = $('#virtual_machine_create_form'); |     var create_vm_form = $('#virtual_machine_create_form'); | ||||||
|     create_vm_form.submit(function () { |     create_vm_form.submit(function () { | ||||||
|         $('#btn-create-vm').prop('disabled', true); |         $('#btn-create-vm').prop('disabled', true); | ||||||
|  | @ -90,26 +89,28 @@ $(document).ready(function() { | ||||||
|             success: function (data) { |             success: function (data) { | ||||||
|                 if (data.status === true) { |                 if (data.status === true) { | ||||||
|                     fa_icon = $('.modal-icon > .fa'); |                     fa_icon = $('.modal-icon > .fa'); | ||||||
|                     fa_icon.attr('class', 'fa fa-check'); |                     fa_icon.attr('class', 'checkmark'); | ||||||
|                     $('.modal-header > .close').attr('class', 'close'); |                     // $('.modal-header > .close').removeClass('hidden');
 | ||||||
|                     $('#createvm-modal-title').text(data.msg_title); |                     $('#createvm-modal-title').text(data.msg_title); | ||||||
|                     $('#createvm-modal-body').text(data.msg_body); |                     $('#createvm-modal-body').text(data.msg_body); | ||||||
|                     $('#createvm-modal').on('hidden.bs.modal', function () { |                     $('#createvm-modal-done-btn') | ||||||
|                         window.location = data.redirect; |                         .attr('href', data.redirect) | ||||||
|                     }) |                         .removeClass('hide'); | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             error: function (xmlhttprequest, textstatus, message) { |             error: function (xmlhttprequest, textstatus, message) { | ||||||
|                     fa_icon = $('.modal-icon > .fa'); |                     fa_icon = $('.modal-icon > .fa'); | ||||||
|                     fa_icon.attr('class', 'fa fa-times'); |                     fa_icon.attr('class', 'fa fa-close'); | ||||||
|                     $('.modal-header > .close').attr('class', 'close'); |  | ||||||
|                     $('.modal-text').addClass('hide'); |  | ||||||
|                     if (typeof(create_vm_error_message) !== 'undefined') { |                     if (typeof(create_vm_error_message) !== 'undefined') { | ||||||
|                         $('#createvm-modal-title').text(create_vm_error_message); |                         $('#createvm-modal-text').text(create_vm_error_message); | ||||||
|                     } |                     } | ||||||
|                     $('#btn-create-vm').prop('disabled', false); |                     $('#btn-create-vm').prop('disabled', false); | ||||||
|  |                     $('#createvm-modal-close-btn').removeClass('hide'); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|         return false; |         return false; | ||||||
|     }); |     }); | ||||||
|  |     $('#createvm-modal').on('hidden.bs.modal', function () { | ||||||
|  |         $(this).find('.modal-footer .btn').addClass('hide'); | ||||||
|  |     }) | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -42,7 +42,9 @@ | ||||||
|                 <p> |                 <p> | ||||||
|                     <strong>{% trans "Status" %}: </strong> |                     <strong>{% trans "Status" %}: </strong> | ||||||
|                     <strong> |                     <strong> | ||||||
|                         {% if order.status == 'Approved' %} |                         {% if vm.terminated_at %} | ||||||
|  |                             <span class="vm-color-failed">{% trans "Terminated" %}</span> | ||||||
|  |                         {% elif order.status == 'Approved' %} | ||||||
|                             <span class="vm-color-online">{% trans "Approved" %}</span> |                             <span class="vm-color-online">{% trans "Approved" %}</span> | ||||||
|                         {% else %} |                         {% else %} | ||||||
|                             <span class="vm-status-failed">{% trans "Declined" %}</span> |                             <span class="vm-status-failed">{% trans "Declined" %}</span> | ||||||
|  | @ -160,22 +162,19 @@ | ||||||
|         <div class="modal-dialog"> |         <div class="modal-dialog"> | ||||||
|             <div class="modal-content"> |             <div class="modal-content"> | ||||||
|                 <div class="modal-header"> |                 <div class="modal-header"> | ||||||
|                     <button type="button" class="close hidden" data-dismiss="modal" |  | ||||||
|                             aria-label="create-vm-close"> |  | ||||||
|                         <span aria-hidden="true">×</span> |  | ||||||
|                     </button> |  | ||||||
|                 </div> |                 </div> | ||||||
|                 <div class="modal-body"> |                 <div class="modal-body"> | ||||||
|                     <div class="modal-icon"> |                     <div class="modal-icon"> | ||||||
|                         <i class="fa fa-cog fa-spin fa-3x fa-fw"></i> |                         <i class="fa fa-cog fa-spin fa-3x fa-fw"></i> | ||||||
|                         <span class="sr-only">{% trans "Processing..." %}</span> |                         <span class="sr-only">{% trans "Processing..." %}</span> | ||||||
|                     </div> |                     </div> | ||||||
|                     <h4 class="modal-title" id="createvm-modal-title"> |                     <h4 class="modal-title" id="createvm-modal-title"></h4> | ||||||
|                     </h4> |  | ||||||
|                     <div class="modal-text" id="createvm-modal-body"> |                     <div class="modal-text" id="createvm-modal-body"> | ||||||
|                         {% trans "Hold tight, we are processing your request" %} |                         {% trans "Hold tight, we are processing your request" %} | ||||||
|                     </div> |                     </div> | ||||||
|                     <div class="modal-footer"> |                     <div class="modal-footer"> | ||||||
|  |                         <a id="createvm-modal-done-btn" class="btn btn-danger btn-ok btn-wide hide" href="{% url 'hosting:virtual_machines' %}">{% trans "OK" %}</a> | ||||||
|  |                         <button id="createvm-modal-close-btn" type="button" class="btn btn-danger btn-ok btn-wide hide" data-dismiss="modal" aria-label="create-vm-close">{% trans "Close" %}</button> | ||||||
|                     </div> |                     </div> | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|  | @ -109,7 +109,7 @@ | ||||||
| 						<p><strong>{{virtual_machine.name}}</strong></p> | 						<p><strong>{{virtual_machine.name}}</strong></p> | ||||||
| 					</div> | 					</div> | ||||||
|           <div class="modal-footer"> |           <div class="modal-footer"> | ||||||
|               <a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a> |             <a class="btn btn-danger btn-ok btn-wide">{% trans "OK" %}</a> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | @ -123,8 +123,9 @@ | ||||||
| 				<div class="modal-header"> | 				<div class="modal-header"> | ||||||
| 				</div> | 				</div> | ||||||
|         <div class="modal-body"> |         <div class="modal-body"> | ||||||
| 					<div class="modal-icon"><i class="fa fa-check" aria-hidden="true"></i></div> | 					<div class="modal-icon"><i class="checkmark" aria-hidden="true"></i></div> | ||||||
| 					<h4 class="modal-title" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</h4> | 					<h4 class="modal-title"></h4> | ||||||
|  | 					<div class="modal-text" id="ModalLabel">{% blocktrans with machine_name=virtual_machine.name %}Your Virtual Machine <strong>{{machine_name}}</strong> is successfully terminated!{% endblocktrans %}</div> | ||||||
|           <div class="modal-footer"> |           <div class="modal-footer"> | ||||||
|             <a href="{% url 'hosting:virtual_machines' %}"	class="btn btn-success btn-wide">{% trans "OK" %}</a> |             <a href="{% url 'hosting:virtual_machines' %}"	class="btn btn-success btn-wide">{% trans "OK" %}</a> | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import json | import json | ||||||
| import logging | import logging | ||||||
| import uuid | import uuid | ||||||
|  | from datetime import datetime | ||||||
| from time import sleep | from time import sleep | ||||||
| 
 | 
 | ||||||
| from django import forms | from django import forms | ||||||
|  | @ -47,10 +48,11 @@ from utils.views import ( | ||||||
| from .forms import HostingUserSignupForm, HostingUserLoginForm, \ | from .forms import HostingUserSignupForm, HostingUserLoginForm, \ | ||||||
|     UserHostingKeyForm, generate_ssh_key_name |     UserHostingKeyForm, generate_ssh_key_name | ||||||
| from .mixins import ProcessVMSelectionMixin | from .mixins import ProcessVMSelectionMixin | ||||||
| from .models import HostingOrder, HostingBill, HostingPlan, UserHostingKey | from .models import ( | ||||||
|  |     HostingOrder, HostingBill, HostingPlan, UserHostingKey, VMDetail | ||||||
|  | ) | ||||||
| from datacenterlight.models import VMTemplate | from datacenterlight.models import VMTemplate | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \ | CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \ | ||||||
|  | @ -690,25 +692,29 @@ class OrdersHostingDetailView(LoginRequiredMixin, | ||||||
|         if obj is not None: |         if obj is not None: | ||||||
|             # invoice for previous order |             # invoice for previous order | ||||||
|             try: |             try: | ||||||
|                 manager = OpenNebulaManager( |                 vm_detail = VMDetail.objects.get(vm_id=obj.vm_id) | ||||||
|                     email=owner.email, password=owner.password |                 context['vm'] = vm_detail.__dict__ | ||||||
|                 ) |             except VMDetail.DoesNotExist: | ||||||
|                 vm = manager.get_vm(obj.vm_id) |                 try: | ||||||
|                 context['vm'] = VirtualMachineSerializer(vm).data |                     manager = OpenNebulaManager( | ||||||
|             except WrongIdError: |                         email=owner.email, password=owner.password | ||||||
|                 messages.error( |                     ) | ||||||
|                     self.request, |                     vm = manager.get_vm(obj.vm_id) | ||||||
|                     _('The VM you are looking for is unavailable at the ' |                     context['vm'] = VirtualMachineSerializer(vm).data | ||||||
|                       'moment. Please contact Data Center Light support.') |                 except WrongIdError: | ||||||
|                 ) |                     messages.error( | ||||||
|                 self.kwargs['error'] = 'WrongIdError' |                         self.request, | ||||||
|                 context['error'] = 'WrongIdError' |                         _('The VM you are looking for is unavailable at the ' | ||||||
|             except ConnectionRefusedError: |                           'moment. Please contact Data Center Light support.') | ||||||
|                 messages.error( |                     ) | ||||||
|                     self.request, |                     self.kwargs['error'] = 'WrongIdError' | ||||||
|                     _('In order to create a VM, you need to create/upload ' |                     context['error'] = 'WrongIdError' | ||||||
|                       'your SSH KEY first.') |                 except ConnectionRefusedError: | ||||||
|                 ) |                     messages.error( | ||||||
|  |                         self.request, | ||||||
|  |                         _('In order to create a VM, you need to create/upload ' | ||||||
|  |                           'your SSH KEY first.') | ||||||
|  |                     ) | ||||||
|         elif not card_details.get('response_object'): |         elif not card_details.get('response_object'): | ||||||
|             # new order, failed to get card details |             # new order, failed to get card details | ||||||
|             context['failed_payment'] = True |             context['failed_payment'] = True | ||||||
|  | @ -1055,6 +1061,10 @@ class VirtualMachineView(LoginRequiredMixin, View): | ||||||
|                 except WrongIdError: |                 except WrongIdError: | ||||||
|                     response['status'] = True |                     response['status'] = True | ||||||
|                     response['text'] = ugettext('Terminated') |                     response['text'] = ugettext('Terminated') | ||||||
|  |                     vm_detail_obj = VMDetail.objects.filter( | ||||||
|  |                         vm_id=opennebula_vm_id).first() | ||||||
|  |                     vm_detail_obj.terminated_at = datetime.utcnow() | ||||||
|  |                     vm_detail_obj.save() | ||||||
|                     break |                     break | ||||||
|                 except BaseException: |                 except BaseException: | ||||||
|                     break |                     break | ||||||
|  |  | ||||||
|  | @ -1,4 +1,10 @@ | ||||||
| from hosting.models import UserHostingKey | import logging | ||||||
|  | from oca.pool import WrongIdError | ||||||
|  | 
 | ||||||
|  | from hosting.models import UserHostingKey, VMDetail | ||||||
|  | from opennebula_api.serializers import VirtualMachineSerializer | ||||||
|  | 
 | ||||||
|  | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_all_public_keys(customer): | def get_all_public_keys(customer): | ||||||
|  | @ -11,6 +17,38 @@ def get_all_public_keys(customer): | ||||||
|         "public_key", flat=True) |         "public_key", flat=True) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def get_or_create_vm_detail(user, manager, vm_id): | ||||||
|  |     """ | ||||||
|  |     Returns VMDetail object related to given vm_id. Creates the object | ||||||
|  |      if it does not exist | ||||||
|  | 
 | ||||||
|  |     :param vm_id: The ID of the VM which should be greater than 0. | ||||||
|  |     :param user: The CustomUser object that owns this VM | ||||||
|  |     :param manager: The OpenNebulaManager object | ||||||
|  |     :return: The VMDetail object. None if vm_id is less than or equal to 0. | ||||||
|  |     Also, for the cases where the VMDetail does not exist and we can not | ||||||
|  |     fetch data about the VM from OpenNebula, the function returns None | ||||||
|  |     """ | ||||||
|  |     if vm_id <= 0: | ||||||
|  |         return None | ||||||
|  |     try: | ||||||
|  |         vm_detail_obj = VMDetail.objects.get(vm_id=vm_id) | ||||||
|  |     except VMDetail.DoesNotExist: | ||||||
|  |         try: | ||||||
|  |             vm_obj = manager.get_vm(vm_id) | ||||||
|  |         except (WrongIdError, ConnectionRefusedError) as e: | ||||||
|  |             logger.error(str(e)) | ||||||
|  |             return None | ||||||
|  |         vm = VirtualMachineSerializer(vm_obj).data | ||||||
|  |         vm_detail_obj = VMDetail.objects.create( | ||||||
|  |             user=user, vm_id=vm_id, disk_size=vm['disk_size'], | ||||||
|  |             cores=vm['cores'], memory=vm['memory'], | ||||||
|  |             configuration=vm['configuration'], ipv4=vm['ipv4'], | ||||||
|  |             ipv6=vm['ipv6'] | ||||||
|  |         ) | ||||||
|  |     return vm_detail_obj | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def get_vm_price(cpu, memory, disk_size): | def get_vm_price(cpu, memory, disk_size): | ||||||
|     """ |     """ | ||||||
|     A helper function that computes price of a VM from given cpu, ram and |     A helper function that computes price of a VM from given cpu, ram and | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue