Manual merge of levi/opennebula-integration
This commit is contained in:
		
				commit
				
					
						970ae1d0b2
					
				
			
		
					 31 changed files with 949 additions and 324 deletions
				
			
		
							
								
								
									
										12
									
								
								alplora/static/alplora/js/form.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								alplora/static/alplora/js/form.js
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										42
									
								
								alplora/templates/alplora/contact.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								alplora/templates/alplora/contact.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| {% load i18n %}  | ||||
| <div class="modal-dialog" role="document"> | ||||
| <div class="modal-content"> | ||||
|   <div class="modal-header"> | ||||
| 	<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||
| 	<h4 class="modal-title" id="exampleModalLabel">{% trans 'New message'%}</h4> | ||||
|   </div> | ||||
|   <form novalidate method="post" action="{% url 'alplora:contact'%}" id="contact_form"> | ||||
|   <div class="modal-body"> | ||||
| 		{% csrf_token %} | ||||
| 		{{ form.non_field_errors }} | ||||
| 	  <div class="form-group text-left"> | ||||
| 		<label for="recipient-name" class="control-label ">{% trans 'Name:'%}</label> | ||||
|         <input type="text" class="form-control" {%if form.name.errors%} | ||||
|         style="border-color: red" {%endif%}  name="name" placeholder="{% trans 'What is your name ?'%}" id="recipient-name" required> | ||||
|         {{ form.name.errors|striptags }} | ||||
| 	  </div> | ||||
| 	  <div class="form-group text-left"> | ||||
| 		<label for="recipient-name" class="control-label ">{% trans 'From:'%}</label> | ||||
| 		<input type="text" class="form-control" {%if form.email.errors%} | ||||
|         style="border-color: red" {%endif%}name="email" placeholder="{% trans 'You email'%}" id="recipient-name" required> | ||||
| 		{{ form.email.errors|striptags}} | ||||
| 	  </div> | ||||
| 	  <div class="form-group text-left"> | ||||
| 		<label for="message-text" class="control-label ">{% trans 'Message:'%}</label> | ||||
|         <textarea class="form-control" {%if form.message.errors %} style = | ||||
|             "border-color: red" {%endif%} name="message" placeholder="{% trans 'Leave us your message'%}" id="message-text" required></textarea> | ||||
|         {{ form.message.errors|striptags}} | ||||
| 	  </div> | ||||
|   </div> | ||||
|   <div class="modal-footer"> | ||||
| 	<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close'%}</button> | ||||
| 	<button type="submit" class="btn btn-warning">{% trans 'Send message'%}</button> | ||||
|   </div> | ||||
|   </form> | ||||
| </div> | ||||
| </div> | ||||
|     <script> | ||||
|     $('#contact_form').ajaxForm({ | ||||
|         target: '#modal', success: function(response) { }  | ||||
|     }); | ||||
|     </script> | ||||
							
								
								
									
										21
									
								
								alplora/templates/alplora/contact_success.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								alplora/templates/alplora/contact_success.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| {% load i18n %} | ||||
|  <div class="modal-dialog" role="document"> | ||||
|    <div class="modal-content"> | ||||
|      <div class="modal-header"> | ||||
|    	<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||
|    	<h4 class="modal-title">{% trans "Message Sent" %}</h4> | ||||
|      </div> | ||||
|      <div class="modal-body"> | ||||
|    	<p>{% trans "Thank you, we will contact you as soon as possible" %}</p> | ||||
|      </div> | ||||
|      <div class="modal-footer text-center"> | ||||
|    	<button type="submit" class="btn btn-primary" data-dismiss="modal">Ok</button> | ||||
|      </div> | ||||
|    </div><!-- /.modal-content --> | ||||
|  </div><!-- /.modal-dialog --> | ||||
|  <script> | ||||
| 	// close the modal after 3 seconds | ||||
| 	setTimeout(function() { | ||||
| 		$('#modal').modal('hide'); | ||||
| 	}, 3000); | ||||
| </script> | ||||
|  | @ -53,7 +53,6 @@ | |||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <div class="page-loader"> | ||||
|     <div class="loader"> | ||||
|       <div class="circle circle-1"></div> | ||||
|  | @ -419,7 +418,9 @@ | |||
|                       <h1>{% trans 'How do I get Alplora?'%}</h1> | ||||
|                         <h3>{% trans 'Click the button below and leave us your contact.'%}<p></p>{% trans 'Team Alplora will contact you and visit you with a tracking device.'%}</h3> | ||||
|                         <hr class="intro-divider"> | ||||
|                         <a href="#howitworks" class="btn btn-default btn-lg"><i class="#Services"></i> <span class="network-name" data-toggle="modal" data-target="#exampleModal" >{% trans 'Contact'%}</span></a> | ||||
|                         <a href="{% url 'alplora:contact' %}" data-toggle="modal" data-target="#modal" class="btn btn-default | ||||
|                             btn-lg"><i class="#Services"></i> <span | ||||
|                                 class="network-name"  >{% trans 'Contact'%}</span></a> | ||||
|                             | ||||
|                         </ul> | ||||
|                     </div> | ||||
|  | @ -429,60 +430,9 @@ | |||
|         </div> | ||||
| 
 | ||||
|         <!-- CONTACT FORM MODAL  --> | ||||
|         <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" style="color:black;"> | ||||
|           <div class="modal-dialog" role="document"> | ||||
|             <div class="modal-content"> | ||||
|               <div class="modal-header"> | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||
|                 <h4 class="modal-title" id="exampleModalLabel">{% trans 'New message'%}</h4> | ||||
|               </div> | ||||
|               <form method="POST" action=""> | ||||
|               <div class="modal-body"> | ||||
|                     {% csrf_token %} | ||||
|                     {{ form.non_field_errors }} | ||||
|                      | ||||
|                   <div class="form-group text-left"> | ||||
|                     <label for="recipient-name" class="control-label ">{% trans 'Name:'%}</label> | ||||
|                     <input type="text" class="form-control"  name="name" placeholder="{% trans 'What is your name ?'%}" id="recipient-name" required> | ||||
|                   </div> | ||||
|                   <div class="form-group text-left"> | ||||
|                     <label for="recipient-name" class="control-label ">{% trans 'From:'%}</label> | ||||
|                     <input type="text" class="form-control" name="email" placeholder="{% trans 'You email'%}" id="recipient-name" required> | ||||
|                     {{ form.email.errors|striptags}} | ||||
|                   </div> | ||||
|                   <div class="form-group text-left"> | ||||
|                     <label for="message-text" class="control-label ">{% trans 'Message:'%}</label> | ||||
|                     <textarea class="form-control" name="message" placeholder="{% trans 'Leave us your message'%}" id="message-text" required></textarea> | ||||
|                   </div> | ||||
|                  | ||||
|               </div> | ||||
|               <div class="modal-footer"> | ||||
|                 <button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close'%}</button> | ||||
|                 <button type="submit" class="btn btn-warning">{% trans 'Send message'%}</button> | ||||
|               </div> | ||||
|               </form> | ||||
|             </div> | ||||
|           </div> | ||||
|         <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" style="color:black;"> | ||||
|         </div> | ||||
| 
 | ||||
|         <!-- SUCCESS MODAL MESSAGE --> | ||||
|         <div class="modal fade bs-example-modal-sm" style="color:black;"  id="request-success-message" tabindex="-1" role="dialog"> | ||||
|           <div class="modal-dialog" role="document"> | ||||
|             <div class="modal-content"> | ||||
|               <div class="modal-header"> | ||||
|                 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> | ||||
|                 <h4 class="modal-title">{% trans "Message Sent" %}</h4> | ||||
|               </div> | ||||
|               <div class="modal-body"> | ||||
|                 <p>{% trans "Thank you, we will contact you as soon as possible" %}</p> | ||||
|               </div> | ||||
|               <div class="modal-footer text-center"> | ||||
|                 <button type="submit" class="btn btn-primary" data-dismiss="modal">Ok</button> | ||||
|               </div> | ||||
|             </div><!-- /.modal-content --> | ||||
|           </div><!-- /.modal-dialog --> | ||||
|         </div><!-- /.modal --> | ||||
| 
 | ||||
|         <!-- /.container --> | ||||
| 
 | ||||
|     </div> | ||||
|  | @ -560,6 +510,7 @@ | |||
| 
 | ||||
|     <script src="{% static 'alplora/js/main.js' %}"></script> | ||||
|      | ||||
|     <script src="{% static 'alplora/js/form.js' %}"></script> | ||||
| 
 | ||||
| <link rel="stylesheet" href="/static/debug_toolbar/css/print.css" type="text/css" media="print"> | ||||
| <link rel="stylesheet" href="/static/debug_toolbar/css/toolbar.css" type="text/css"> | ||||
|  | @ -572,14 +523,15 @@ | |||
| <script src="/static/debug_toolbar/js/toolbar.js"></script> | ||||
| 
 | ||||
| <script type="text/javascript"> | ||||
|       window.onload=function(){ | ||||
|         var hash = window.location.hash.substr(1); | ||||
|         if (hash == 'requestformsuccess'){ | ||||
|             $('#request-success-message').modal('show'); | ||||
|         } | ||||
| 
 | ||||
|        }; | ||||
| $('#modal').on('show.bs.modal', function (event) { | ||||
|     var modal = $(this) | ||||
|     $.ajax({ | ||||
|         url: "{% url 'alplora:contact' %}", | ||||
|         context: document.body | ||||
|     }).done(function(response) { | ||||
|         modal.html(response); | ||||
|     }); | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| </body> | ||||
| </html> | ||||
|  |  | |||
|  | @ -1,11 +1,12 @@ | |||
| from django.conf.urls import url | ||||
| 
 | ||||
| from .views import IndexView, LoginView | ||||
| from .views import IndexView, LoginView, ContactView | ||||
| 
 | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|     url(r'^/?$', IndexView.as_view(), name='index'), | ||||
|     url(r'/login/', LoginView.as_view(), name='login'), | ||||
|     url(r'/contact', ContactView.as_view(), name='contact'), | ||||
| #     url(r'^/beta-program/?$', BetaProgramView.as_view(), name='beta'), | ||||
| #     url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'), | ||||
| ] | ||||
|  |  | |||
|  | @ -5,13 +5,12 @@ from django.utils.translation import ugettext_lazy as _ | |||
| from django.views.generic.edit import FormView | ||||
| from django.contrib import messages | ||||
| from django.core.urlresolvers import reverse_lazy, reverse | ||||
| from django.shortcuts import render | ||||
| 
 | ||||
| from utils.forms import ContactUsForm | ||||
| 
 | ||||
| class IndexView(FormView): | ||||
| class IndexView(TemplateView): | ||||
|     template_name = "alplora/index.html" | ||||
|     form_class = ContactUsForm | ||||
|     success_message = _('Message Successfully Sent') | ||||
| 
 | ||||
|     def get_context_data(self, *args, **kwargs): | ||||
|         context = super(IndexView, self).get_context_data(**kwargs) | ||||
|  | @ -19,17 +18,22 @@ class IndexView(FormView): | |||
|         context.update(languages) | ||||
|         return context | ||||
| 
 | ||||
|     def get_success_url(self): | ||||
|         success_url = reverse('alplora:index') | ||||
|         success_url += "#requestformsuccess" | ||||
|         return success_url | ||||
| class ContactView(FormView): | ||||
|     template_name = 'alplora/contact.html' | ||||
|     form_class = ContactUsForm | ||||
|     success_message = _('Message Successfully Sent') | ||||
| 
 | ||||
|     def get_context_data(self, *args, **kwargs): | ||||
|         context = super(ContactView, self).get_context_data(**kwargs) | ||||
|         languages = getlanguages() | ||||
|         context.update(languages) | ||||
|         return context | ||||
| 
 | ||||
|     def form_valid(self, form): | ||||
|         form.save() | ||||
|         form.send_email(email_to='info@alplora.ch') | ||||
|         messages.add_message(self.request, messages.SUCCESS, self.success_message) | ||||
|         return super(IndexView, self).form_valid(form) | ||||
| 
 | ||||
|         return render(self.request, 'alplora/contact_success.html', {})  | ||||
| 
 | ||||
| class LoginView(TemplateView): | ||||
|     template_name = "alplora/login.html" | ||||
|  |  | |||
|  | @ -17,18 +17,7 @@ | |||
| 	    {% render_model post "abstract" "" "" 'truncatewords_html:10'  %} | ||||
| 	  </h2> | ||||
| 	  <span class="meta"> | ||||
| 	    Posted | ||||
| 	    {% if post.author %} | ||||
| 	    by | ||||
| 	    <a href="{% url 'djangocms_blog:posts-author' post.author.get_username %}"> | ||||
| 	      {% if post.author.get_full_name %} | ||||
| 	      {{ post.author.get_full_name }} | ||||
| 	      {% else %} | ||||
| 	      {{ post.author }} | ||||
| 	      {% endif %} | ||||
| 	    </a> | ||||
| 	    {% endif %} | ||||
| 	    on {{ post.date_published|date:"DATE_FORMAT" }} | ||||
| 	    Posted on {{ post.date_published|date:"DATE_FORMAT" }} | ||||
| 	  </span> | ||||
| 	</div> | ||||
|       </div> | ||||
|  |  | |||
|  | @ -16,18 +16,7 @@ | |||
|         </h2> | ||||
|       </a> | ||||
|       <p class="post-meta" style="font-size:0.9em;"> | ||||
|         Posted | ||||
|         {% if post.author %} | ||||
|         by | ||||
|         <!-- <a href="{% url 'djangocms_blog:posts-author' post.author.get_username %}"> --> | ||||
|           {% if post.author.get_full_name %} | ||||
|           {{ post.author.get_full_name }} | ||||
|           {% else %} | ||||
|           {{ post.author }} | ||||
|           {% endif %} | ||||
|         <!-- </a> --> | ||||
|         {% endif %} | ||||
|         on {{ post.date_published|date:"DATE_FORMAT" }} | ||||
|         Posted on {{ post.date_published|date:"DATE_FORMAT" }} | ||||
|       </p> | ||||
| 
 | ||||
|       <p class="post-subtitle"> | ||||
|  |  | |||
|  | @ -1,10 +1,13 @@ | |||
| import random | ||||
| import string | ||||
| from django import forms | ||||
| from membership.models import CustomUser | ||||
| from django.contrib.auth import authenticate | ||||
| 
 | ||||
| 
 | ||||
| from utils.stripe_utils import StripeUtils | ||||
| 
 | ||||
| from .models import HostingOrder, VirtualMachinePlan | ||||
| from .models import HostingOrder, VirtualMachinePlan, UserHostingKey | ||||
| 
 | ||||
| 
 | ||||
| class HostingOrderAdminForm(forms.ModelForm): | ||||
|  | @ -83,3 +86,40 @@ class HostingUserSignupForm(forms.ModelForm): | |||
|         if not confirm_password == password: | ||||
|             raise forms.ValidationError("Passwords don't match") | ||||
|         return confirm_password | ||||
| 
 | ||||
| 
 | ||||
| class UserHostingKeyForm(forms.ModelForm): | ||||
|     private_key = forms.CharField(widget=forms.PasswordInput(), required=False) | ||||
|     public_key = forms.CharField(widget=forms.PasswordInput(), required=False) | ||||
|     user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(), required=False) | ||||
|     name = forms.CharField(required=False) | ||||
| 
 | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         self.request = kwargs.pop("request") | ||||
|         super(UserHostingKeyForm, self).__init__(*args, **kwargs) | ||||
|         # self.initial['user'].initial = self.request.user.id | ||||
|         # print(self.fields) | ||||
| 
 | ||||
|     def clean_name(self): | ||||
|         return ''.join(random.choice(string.ascii_lowercase) for i in range(7)) | ||||
| 
 | ||||
|     def clean_user(self): | ||||
|         return self.request.user | ||||
| 
 | ||||
|     def clean(self): | ||||
|         cleaned_data = self.cleaned_data | ||||
| 
 | ||||
|         print(cleaned_data) | ||||
| 
 | ||||
|         if not cleaned_data.get('public_key'): | ||||
|             private_key, public_key = UserHostingKey.generate_keys() | ||||
|             cleaned_data.update({ | ||||
|                 'private_key': private_key, | ||||
|                 'public_key': public_key | ||||
|             }) | ||||
| 
 | ||||
|         return cleaned_data | ||||
| 
 | ||||
|     class Meta: | ||||
|         model = UserHostingKey | ||||
|         fields = ['user', 'public_key', 'name'] | ||||
|  |  | |||
|  | @ -7,51 +7,100 @@ class Command(BaseCommand): | |||
| 
 | ||||
|     def get_data(self): | ||||
| 
 | ||||
|         return [ | ||||
|             { | ||||
|                 'base_price': 10, | ||||
|                 'core_price': 5, | ||||
|                 'memory_price': 2, | ||||
|                 'disk_size_price': 0.6, | ||||
|                 'cores': 1, | ||||
|                 'memory': 2, | ||||
|                 'disk_size': 10 | ||||
|             }, | ||||
|             { | ||||
|                 'base_price': 10, | ||||
|                 'core_price': 5, | ||||
|                 'memory_price': 2, | ||||
|                 'disk_size_price': 0.6, | ||||
|                 'cores': 1, | ||||
|                 'memory': 2, | ||||
|                 'disk_size': 100 | ||||
|             }, | ||||
|             { | ||||
|                 'base_price': 10, | ||||
|                 'core_price': 5, | ||||
|                 'memory_price': 2, | ||||
|                 'disk_size_price': 0.6, | ||||
|                 'cores': 2, | ||||
|                 'memory': 4, | ||||
|                 'disk_size': 20 | ||||
|             }, | ||||
|             { | ||||
|                 'base_price': 10, | ||||
|                 'core_price': 5, | ||||
|                 'memory_price': 2, | ||||
|                 'disk_size_price': 0.6, | ||||
|                 'cores': 4, | ||||
|                 'memory': 8, | ||||
|                 'disk_size': 40 | ||||
|             }, | ||||
|             { | ||||
|                 'base_price': 10, | ||||
|                 'core_price': 5, | ||||
|                 'memory_price': 2, | ||||
|                 'disk_size_price': 0.6, | ||||
|                 'cores': 16, | ||||
|                 'memory': 8, | ||||
|                 'disk_size': 40 | ||||
|             }, | ||||
|         ] | ||||
| 
 | ||||
| 
 | ||||
|         hetzner = { | ||||
|             'base_price': 10, | ||||
|             'core_price': 10, | ||||
|             'memory_price': 5, | ||||
|             'disk_size_price': 1, | ||||
|             'core_price': 5, | ||||
|             'memory_price': 2, | ||||
|             'disk_size_price': 0.6, | ||||
|             'description': 'VM auf einzelner HW, Raid1, kein HA', | ||||
|             'location': 'DE' | ||||
|         } | ||||
| 
 | ||||
|         return { | ||||
|             # 'hetzner_nug': { | ||||
|             #     'base_price': 5, | ||||
|             #     'memory_price': 2, | ||||
|             #     'core_price': 2, | ||||
|             #     'disk_size_price': 0.5, | ||||
|             #     'description': 'VM ohne Uptime Garantie' | ||||
|             # }, | ||||
|             'hetzner': hetzner, | ||||
|             # 'hetzner_raid6': { | ||||
|             #     'base_price': hetzner['base_price']*1.2, | ||||
|             #     'core_price': hetzner['core_price']*1.2, | ||||
|             #     'memory_price': hetzner['memory_price']*1.2, | ||||
|             #     'disk_size_price': hetzner['disk_size_price']*1.2, | ||||
|             #     'description': 'VM auf einzelner HW, Raid1, kein HA' | ||||
|         # return { | ||||
|         #     # 'hetzner_nug': { | ||||
|         #     #     'base_price': 5, | ||||
|         #     #     'memory_price': 2, | ||||
|         #     #     'core_price': 2, | ||||
|         #     #     'disk_size_price': 0.5, | ||||
|         #     #     'description': 'VM ohne Uptime Garantie' | ||||
|         #     # }, | ||||
|         #     'hetzner': hetzner, | ||||
|         #     # 'hetzner_raid6': { | ||||
|         #     #     'base_price': hetzner['base_price']*1.2, | ||||
|         #     #     'core_price': hetzner['core_price']*1.2, | ||||
|         #     #     'memory_price': hetzner['memory_price']*1.2, | ||||
|         #     #     'disk_size_price': hetzner['disk_size_price']*1.2, | ||||
|         #     #     'description': 'VM auf einzelner HW, Raid1, kein HA' | ||||
| 
 | ||||
|             # }, | ||||
|             # 'hetzner_glusterfs': { | ||||
|             #     'base_price': hetzner['base_price']*1.4, | ||||
|             #     'core_price': hetzner['core_price']*1.4, | ||||
|             #     'memory_price': hetzner['memory_price']*1.4, | ||||
|             #     'disk_size_price': hetzner['disk_size_price']*1.4, | ||||
|             #     'description': 'VM auf einzelner HW, Raid1, kein HA' | ||||
|             # }, | ||||
|             'bern': { | ||||
|                 'base_price': 12, | ||||
|                 'core_price': 25, | ||||
|                 'memory_price': 7, | ||||
|                 'disk_size_price': 0.70, | ||||
|                 'description': "VM in Bern, HA Setup ohne HA Garantie", | ||||
|                 'location': 'CH', | ||||
|             } | ||||
|         } | ||||
|         #     # }, | ||||
|         #     # 'hetzner_glusterfs': { | ||||
|         #     #     'base_price': hetzner['base_price']*1.4, | ||||
|         #     #     'core_price': hetzner['core_price']*1.4, | ||||
|         #     #     'memory_price': hetzner['memory_price']*1.4, | ||||
|         #     #     'disk_size_price': hetzner['disk_size_price']*1.4, | ||||
|         #     #     'description': 'VM auf einzelner HW, Raid1, kein HA' | ||||
|         #     # }, | ||||
|         #     'bern': { | ||||
|         #         'base_price': 12, | ||||
|         #         'core_price': 25, | ||||
|         #         'memory_price': 7, | ||||
|         #         'disk_size_price': 0.70, | ||||
|         #         'description': "VM in Bern, HA Setup ohne HA Garantie", | ||||
|         #         'location': 'CH', | ||||
|         #     } | ||||
|         # } | ||||
| 
 | ||||
|     def handle(self, *args, **options): | ||||
| 
 | ||||
|         data = self.get_data() | ||||
|         [VirtualMachineType.objects.create(hosting_company=key, **data[key]) | ||||
|             for key in data.keys()] | ||||
|         vm_data = self.get_data() | ||||
|         for vm in vm_data: | ||||
|             VirtualMachineType.objects.create(**vm) | ||||
|  |  | |||
							
								
								
									
										35
									
								
								hosting/migrations/0028_managevm_userhostingkey.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								hosting/migrations/0028_managevm_userhostingkey.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-04-29 18:28 | ||||
| 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', '0027_auto_20160711_0210'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.CreateModel( | ||||
|             name='ManageVM', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||
|             ], | ||||
|             options={ | ||||
|                 'managed': False, | ||||
|             }, | ||||
|         ), | ||||
|         migrations.CreateModel( | ||||
|             name='UserHostingKey', | ||||
|             fields=[ | ||||
|                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||||
|                 ('public_key', models.TextField()), | ||||
|                 ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), | ||||
|             ], | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										23
									
								
								hosting/migrations/0029_userhostingkey_created_at.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								hosting/migrations/0029_userhostingkey_created_at.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-04-30 19:04 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| import datetime | ||||
| from django.db import migrations, models | ||||
| from django.utils.timezone import utc | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0028_managevm_userhostingkey'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='userhostingkey', | ||||
|             name='created_at', | ||||
|             field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2017, 4, 30, 19, 4, 20, 780173, tzinfo=utc)), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										21
									
								
								hosting/migrations/0030_userhostingkey_name.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								hosting/migrations/0030_userhostingkey_name.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-04-30 19:09 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0029_userhostingkey_created_at'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='userhostingkey', | ||||
|             name='name', | ||||
|             field=models.CharField(default='', max_length=100), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										23
									
								
								hosting/migrations/0031_auto_20170503_0554.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								hosting/migrations/0031_auto_20170503_0554.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-03 05:54 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0030_userhostingkey_name'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.RemoveField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='hosting_company', | ||||
|         ), | ||||
|         migrations.RemoveField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='location', | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										33
									
								
								hosting/migrations/0032_auto_20170504_0315.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								hosting/migrations/0032_auto_20170504_0315.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-04 03:15 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0031_auto_20170503_0554'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='cores', | ||||
|             field=models.IntegerField(default=0), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='disk_size', | ||||
|             field=models.IntegerField(default=0), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|         migrations.AddField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='memory', | ||||
|             field=models.IntegerField(default=0), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										20
									
								
								hosting/migrations/0033_virtualmachinetype_configuration.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								hosting/migrations/0033_virtualmachinetype_configuration.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-04 03:23 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0032_auto_20170504_0315'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='configuration', | ||||
|             field=models.CharField(choices=[('debian', 'Debian 8'), ('ubuntu', 'Ubuntu 16.06'), ('devuan', 'Devuan 1'), ('centos', 'CentOS 7')], default='ubuntu', max_length=10), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										30
									
								
								hosting/migrations/0034_auto_20170504_0331.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								hosting/migrations/0034_auto_20170504_0331.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-04 03:31 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| import django.db.models.deletion | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0033_virtualmachinetype_configuration'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.RemoveField( | ||||
|             model_name='virtualmachinetype', | ||||
|             name='configuration', | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='virtualmachineplan', | ||||
|             name='configuration', | ||||
|             field=models.CharField(choices=[('debian', 'Debian 8'), ('ubuntu', 'Ubuntu 16.06'), ('devuan', 'Devuan 1'), ('centos', 'CentOS 7')], max_length=20), | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='virtualmachineplan', | ||||
|             name='vm_type', | ||||
|             field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachineType'), | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										21
									
								
								hosting/migrations/0035_virtualmachineplan_opennebula_id.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								hosting/migrations/0035_virtualmachineplan_opennebula_id.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-06 23:02 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0034_auto_20170504_0331'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AddField( | ||||
|             model_name='virtualmachineplan', | ||||
|             name='opennebula_id', | ||||
|             field=models.IntegerField(default=0), | ||||
|             preserve_default=False, | ||||
|         ), | ||||
|     ] | ||||
							
								
								
									
										20
									
								
								hosting/migrations/0036_auto_20170506_2312.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								hosting/migrations/0036_auto_20170506_2312.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # Generated by Django 1.9.4 on 2017-05-06 23:12 | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| from django.db import migrations, models | ||||
| 
 | ||||
| 
 | ||||
| class Migration(migrations.Migration): | ||||
| 
 | ||||
|     dependencies = [ | ||||
|         ('hosting', '0035_virtualmachineplan_opennebula_id'), | ||||
|     ] | ||||
| 
 | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name='virtualmachineplan', | ||||
|             name='opennebula_id', | ||||
|             field=models.IntegerField(null=True), | ||||
|         ), | ||||
|     ] | ||||
|  | @ -1,23 +1,32 @@ | |||
| from django.shortcuts import redirect | ||||
| from django.core.urlresolvers import reverse | ||||
| from .models import VirtualMachinePlan | ||||
| from .models import VirtualMachinePlan, VirtualMachineType | ||||
| 
 | ||||
| 
 | ||||
| class ProcessVMSelectionMixin(object): | ||||
| 
 | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         hosting = request.POST.get('configuration') | ||||
|         configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(hosting) | ||||
|         vm_specs = { | ||||
|             'cores': request.POST.get('cores'), | ||||
|             'memory': request.POST.get('memory'), | ||||
|             'disk_size': request.POST.get('disk_space'), | ||||
|             'hosting_company': request.POST.get('hosting_company'), | ||||
|             'location_code': request.POST.get('location_code'), | ||||
|             'configuration': hosting, | ||||
|             'configuration_detail': configuration_detail, | ||||
|             'final_price': request.POST.get('final_price') | ||||
|         } | ||||
|         configuration = request.POST.get('configuration') | ||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||
|         vm_template = request.POST.get('vm_template') | ||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) | ||||
|         vm_specs = vm_type.get_specs() | ||||
|         vm_specs.update({ | ||||
|             'configuration_display': configuration_display, | ||||
|             'configuration': configuration, | ||||
|             'final_price': vm_type.final_price, | ||||
|             'vm_template': vm_template | ||||
|         }) | ||||
|         # vm_specs = { | ||||
|         #     # 'cores': request.POST.get('cores'), | ||||
|         #     # 'memory': request.POST.get('memory'), | ||||
|         #     # 'disk_size': request.POST.get('disk_space'), | ||||
|         #     # 'hosting_company': request.POST.get('hosting_company'), | ||||
|         #     # 'location_code': request.POST.get('location_code'), | ||||
|         #     # 'configuration': hosting, | ||||
|         #     # 'configuration_detail': configuration_detail, | ||||
|         #     'final_price': request.POST.get('final_price') | ||||
|         # } | ||||
|         request.session['vm_specs'] = vm_specs | ||||
|         if not request.user.is_authenticated(): | ||||
|             request.session['vm_specs'] = vm_specs | ||||
|  |  | |||
|  | @ -1,13 +1,16 @@ | |||
| import os | ||||
| import oca | ||||
| 
 | ||||
| from django.db import models | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.utils.functional import cached_property | ||||
| 
 | ||||
| from django.conf import settings | ||||
| 
 | ||||
| from Crypto.PublicKey import RSA | ||||
| from stored_messages.settings import stored_messages_settings | ||||
| 
 | ||||
| from membership.models import StripeCustomer | ||||
| from membership.models import StripeCustomer, CustomUser | ||||
| from utils.models import BillingAddress | ||||
| from utils.mixins import AssignPermissionsMixin | ||||
| from .managers import VMPlansManager | ||||
|  | @ -15,70 +18,71 @@ from .managers import VMPlansManager | |||
| 
 | ||||
| class VirtualMachineType(models.Model): | ||||
| 
 | ||||
|     HETZNER_NUG = 'hetzner_nug' | ||||
|     HETZNER = 'hetzner' | ||||
|     HETZNER_R6 = 'hetzner_raid6' | ||||
|     HETZNER_G = 'hetzner_glusterfs' | ||||
|     BERN = 'bern' | ||||
|     DE_LOCATION = 'DE' | ||||
|     CH_LOCATION = 'CH' | ||||
| 
 | ||||
|     HOSTING_TYPES = ( | ||||
|         (HETZNER_NUG, 'Hetzner No Uptime Guarantee'), | ||||
|         (HETZNER, 'Hetzner'), | ||||
|         (HETZNER_R6, 'Hetzner Raid6'), | ||||
|         (HETZNER_G, 'Hetzner Glusterfs'), | ||||
|         (BERN, 'Bern'), | ||||
|     ) | ||||
| 
 | ||||
|     LOCATIONS_CHOICES = ( | ||||
|         (DE_LOCATION, 'Germany'), | ||||
|         (CH_LOCATION, 'Switzerland'), | ||||
|     ) | ||||
| 
 | ||||
|     description = models.TextField() | ||||
|     base_price = models.FloatField() | ||||
|     memory_price = models.FloatField() | ||||
|     core_price = models.FloatField() | ||||
|     disk_size_price = models.FloatField() | ||||
|     hosting_company = models.CharField(max_length=30, choices=HOSTING_TYPES) | ||||
|     location = models.CharField(max_length=3, choices=LOCATIONS_CHOICES) | ||||
|     cores = models.IntegerField() | ||||
|     memory = models.IntegerField() | ||||
|     disk_size = models.IntegerField() | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s" % (self.get_hosting_company_display()) | ||||
|         return "VM Type %s" % (self.id) | ||||
| 
 | ||||
|     @cached_property | ||||
|     def final_price(self): | ||||
|         price = self.cores * self.core_price | ||||
|         price += self.memory * self.memory_price | ||||
|         price += self.disk_size * self.disk_size_price | ||||
|         return price | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_serialized_vm_types(cls): | ||||
|         return [vm.get_serialized_data() | ||||
|                 for vm in cls.objects.all()] | ||||
| 
 | ||||
|     def calculate_price(self, specifications): | ||||
|         price = float(specifications['cores']) * self.core_price | ||||
|         price += float(specifications['memory']) * self.memory_price | ||||
|         price += float(specifications['disk_size']) * self.disk_size_price | ||||
|         price += self.base_price | ||||
|     def calculate_price(self): | ||||
|         price = self.cores * self.core_price | ||||
|         price += self.memory * self.memory_price | ||||
|         price += self.disk_size * self.disk_size_price | ||||
|         # price += self.base_price | ||||
|         return price | ||||
| 
 | ||||
|     def defeault_price(self): | ||||
|         price = self.base_price | ||||
|         price += self.core_price | ||||
|         price += self.memory_price | ||||
|         price += self.disk_size_price * 10 | ||||
|         return price | ||||
|     # @classmethod | ||||
|     # def get_price(cls, vm_template): | ||||
|     #     return cls.BASE_PRICE * vm_template | ||||
| 
 | ||||
|     def get_specs(self): | ||||
|         return { | ||||
|             'memory': self.memory, | ||||
|             'cores': self.cores, | ||||
|             'disk_size': self.disk_size | ||||
|         } | ||||
| 
 | ||||
|     # def calculate_price(self, vm_template): | ||||
|     #     price = self.base_price * vm_template | ||||
|     #     return price | ||||
| 
 | ||||
|     # def defeault_price(self): | ||||
|     #     price = self.base_price | ||||
|     #     price += self.core_price | ||||
|     #     price += self.memory_price | ||||
|     #     price += self.disk_size_price * 10 | ||||
|     #     return price | ||||
| 
 | ||||
|     def get_serialized_data(self): | ||||
|         return { | ||||
|             'description': self.description, | ||||
|             'base_price': self.base_price, | ||||
|             'core_price': self.core_price, | ||||
|             'disk_size_price': self.disk_size_price, | ||||
|             'memory_price': self.memory_price, | ||||
|             'hosting_company_name': self.get_hosting_company_display(), | ||||
|             'hosting_company': self.hosting_company, | ||||
|             'default_price': self.defeault_price(), | ||||
|             'location_code': self.location, | ||||
|             'location': self.get_location_display(), | ||||
|             'id': self.id, | ||||
|             'final_price': self.final_price, | ||||
|             'cores': self.cores, | ||||
|             'memory': self.memory, | ||||
|             'disk_size': self.disk_size | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -94,14 +98,21 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): | |||
|         (CANCELED_STATUS, 'Canceled') | ||||
|     ) | ||||
| 
 | ||||
|     DJANGO = 'django' | ||||
|     RAILS = 'rails' | ||||
|     NODEJS = 'nodejs' | ||||
|     # DJANGO = 'django' | ||||
|     # RAILS = 'rails' | ||||
|     # NODEJS = 'nodejs' | ||||
| 
 | ||||
|     # VM_CONFIGURATION = ( | ||||
|     #     (DJANGO, 'Ubuntu 14.04, Django'), | ||||
|     #     (RAILS, 'Ubuntu 14.04, Rails'), | ||||
|     #     (NODEJS, 'Debian, NodeJS'), | ||||
|     # ) | ||||
| 
 | ||||
|     VM_CONFIGURATION = ( | ||||
|         (DJANGO, 'Ubuntu 14.04, Django'), | ||||
|         (RAILS, 'Ubuntu 14.04, Rails'), | ||||
|         (NODEJS, 'Debian, NodeJS'), | ||||
|         ('debian', 'Debian 8'), | ||||
|         ('ubuntu', 'Ubuntu 16.06'), | ||||
|         ('devuan', 'Devuan 1'), | ||||
|         ('centos', 'CentOS 7') | ||||
|     ) | ||||
| 
 | ||||
|     permissions = ('view_virtualmachineplan', | ||||
|  | @ -111,12 +122,13 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): | |||
|     cores = models.IntegerField() | ||||
|     memory = models.IntegerField() | ||||
|     disk_size = models.IntegerField() | ||||
|     vm_type = models.ForeignKey(VirtualMachineType) | ||||
|     vm_type = models.ForeignKey(VirtualMachineType, null=True) | ||||
|     price = models.FloatField() | ||||
|     public_key = models.TextField(blank=True) | ||||
|     status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS) | ||||
|     ip = models.CharField(max_length=50, blank=True) | ||||
|     configuration = models.CharField(max_length=20, choices=VM_CONFIGURATION) | ||||
|     opennebula_id = models.IntegerField(null=True) | ||||
| 
 | ||||
|     objects = VMPlansManager() | ||||
| 
 | ||||
|  | @ -129,13 +141,13 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): | |||
|     def __str__(self): | ||||
|         return self.name | ||||
| 
 | ||||
|     @cached_property | ||||
|     def hosting_company_name(self): | ||||
|         return self.vm_type.get_hosting_company_display() | ||||
|     # @cached_property | ||||
|     # def hosting_company_name(self): | ||||
|     #     return self.vm_type.get_hosting_company_display() | ||||
| 
 | ||||
|     @cached_property | ||||
|     def location(self): | ||||
|         return self.vm_type.get_location_display() | ||||
|     # @cached_property | ||||
|     # def location(self): | ||||
|     #     return self.vm_type.get_location_display() | ||||
| 
 | ||||
|     @cached_property | ||||
|     def name(self): | ||||
|  | @ -155,28 +167,65 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): | |||
|         instance.assign_permissions(user) | ||||
|         return instance | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def generate_RSA(bits=2048): | ||||
|         ''' | ||||
|         Generate an RSA keypair with an exponent of 65537 in PEM format | ||||
|         param: bits The key length in bits | ||||
|         Return private key and public key | ||||
|         ''' | ||||
|         new_key = RSA.generate(2048, os.urandom) | ||||
|         public_key = new_key.publickey().exportKey("OpenSSH") | ||||
|         private_key = new_key.exportKey("PEM") | ||||
|         return private_key, public_key | ||||
| 
 | ||||
|     def generate_keys(self): | ||||
|         private_key, public_key = self.generate_RSA() | ||||
|         self.public_key = public_key | ||||
|         self.save(update_fields=['public_key']) | ||||
|         return private_key, public_key | ||||
| 
 | ||||
|     def cancel_plan(self): | ||||
|         self.status = self.CANCELED_STATUS | ||||
|         self.save(update_fields=['status']) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_vms(self, email): | ||||
|         # Get User | ||||
|         user_email = email | ||||
| 
 | ||||
|         # Connect to open nebula server | ||||
|         # TODO: handle potential connection error | ||||
|         client = oca.Client("{0}:{1}".format( | ||||
|             settings.OPENNEBULA_USERNAME, | ||||
|             settings.OPENNEBULA_PASSWORD), | ||||
|             "{protocol}://{domain}:{port}{endpoint}".format( | ||||
|                 protocol=settings.OPENNEBULA_PROTOCOL, | ||||
|                 domain=settings.OPENNEBULA_DOMAIN, | ||||
|                 port=settings.OPENNEBULA_PORT, | ||||
|                 endpoint=settings.OPENNEBULA_ENDPOINT | ||||
|         )) | ||||
|         # Get open nebula user id for given email | ||||
|         user_pool = oca.UserPool(client) | ||||
|         user_pool.info() | ||||
|         # TODO: handle potential name error | ||||
|         user_id = user_pool.get_by_name(user_email).id | ||||
| 
 | ||||
|         # Get vm_pool for given user_id | ||||
|         vm_pool = oca.VirtualMachinePool(client) | ||||
|         vm_pool.info(filter=user_id) | ||||
| 
 | ||||
|         # Reset total price | ||||
|         self.total_price = 0 | ||||
|         vms = [] | ||||
|         # Add vm in vm_pool to context | ||||
|         for vm in vm_pool: | ||||
|             name = vm.name | ||||
|             cores = int(vm.template.vcpu) | ||||
|             memory = int(vm.template.memory) / 1024 | ||||
|             # Check if vm has more than one disk | ||||
|             if 'DISK' in vm.template.multiple: | ||||
|                 disk_size = 0 | ||||
|                 for disk in vm.template.disks: | ||||
|                     disk_size += int(disk.size) / 1024 | ||||
|             else: | ||||
|                 disk_size = int(vm.template.disk.size) / 1024 | ||||
| 
 | ||||
|             #TODO: Replace with vm plan | ||||
|             price = 0.6 * disk_size + 2 * memory + 5 * cores | ||||
|             vm = {} | ||||
|             vm['name'] = name | ||||
|             vm['price'] = price | ||||
|             vm['disk_size'] = disk_size | ||||
|             vm['cores'] = cores | ||||
|             vm['memory'] = memory | ||||
|             vms.append(vm) | ||||
|             # self.total_price += price | ||||
|         # self.save() | ||||
|         return vms | ||||
| 
 | ||||
| 
 | ||||
| class HostingOrder(AssignPermissionsMixin, models.Model): | ||||
| 
 | ||||
|  | @ -224,6 +273,32 @@ class HostingOrder(AssignPermissionsMixin, models.Model): | |||
|         self.save() | ||||
| 
 | ||||
| 
 | ||||
| class UserHostingKey(models.Model): | ||||
|     user = models.ForeignKey(CustomUser) | ||||
|     public_key = models.TextField() | ||||
|     created_at = models.DateTimeField(auto_now_add=True) | ||||
|     name = models.CharField(max_length=100) | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def generate_RSA(bits=2048): | ||||
|         ''' | ||||
|         Generate an RSA keypair with an exponent of 65537 in PEM format | ||||
|         param: bits The key length in bits | ||||
|         Return private key and public key | ||||
|         ''' | ||||
|         new_key = RSA.generate(2048, os.urandom) | ||||
|         public_key = new_key.publickey().exportKey("OpenSSH") | ||||
|         private_key = new_key.exportKey("PEM") | ||||
|         return private_key, public_key | ||||
| 
 | ||||
|     @classmethod | ||||
|     def generate_keys(cls): | ||||
|         private_key, public_key = cls.generate_RSA() | ||||
|         # self.public_key = public_key | ||||
|         # self.save(update_fields=['public_key']) | ||||
|         return private_key, public_key | ||||
| 
 | ||||
| 
 | ||||
| class ManageVM(models.Model): | ||||
|     def has_add_permission(self, request): | ||||
|         return False | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ class HostingManageVMAdmin(admin.ModelAdmin): | |||
|         if self.client is None: | ||||
|             opennebula_user = request.user.email | ||||
|             # TODO: get the password stored in django | ||||
|             opennebula_user_password = get_random_password() | ||||
|             opennebula_user_password ='19737450' | ||||
|             self.client = oca.Client("{0}:{1}".format(opennebula_user, opennebula_user_password), | ||||
|                                      "{protocol}://{domain}:{port}{endpoint}".format( | ||||
|                                          protocol=settings.OPENNEBULA_PROTOCOL, | ||||
|  | @ -90,6 +90,74 @@ class HostingManageVMAdmin(admin.ModelAdmin): | |||
|         ) | ||||
|         return TemplateResponse(request, "hosting/managevms.html", context) | ||||
| 
 | ||||
|     # Function that shows the VMs of the current user | ||||
|     def show_vms_view(self, request): | ||||
|         """ | ||||
|             Implemented by Levi for the API | ||||
|         """  | ||||
|         vm_pool = None | ||||
|         try: | ||||
|             self.init_opennebula_client(request) | ||||
|             vm_pool = oca.VirtualMachinePool(self.client) | ||||
|             vm_pool.info() | ||||
|         except socket.timeout as socket_err: | ||||
|             logger.error("Socket timeout error.".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)) | ||||
|         context = dict( | ||||
|             # Include common variables for rendering the admin template. | ||||
|             # self.admin_site.each_context(request), | ||||
|             vms=vm_pool, | ||||
|         ) | ||||
|         return context | ||||
| 
 | ||||
| 
 | ||||
|     def create_vm_view(self, specs): | ||||
|         vm_id = None | ||||
|         try: | ||||
|             # We do have the vm_template param set. Get and parse it | ||||
|             # and check it to be in the desired range. | ||||
|             # We have 8 possible VM templates for the moment which are 1x, 2x, 4x ... | ||||
|             # the basic template of 10GB disk, 1GB ram, 1 vcpu, 0.1 cpu | ||||
|             vm_string_formatter = """<VM> | ||||
|                                       <MEMORY>{memory}</MEMORY> | ||||
|                                       <VCPU>{vcpu}</VCPU> | ||||
|                                       <CPU>{cpu}</CPU> | ||||
|                                       <DISK> | ||||
|                                         <TYPE>{disk_type}</TYPE> | ||||
|                                         <SIZE>{size}</SIZE> | ||||
|                                       </DISK> | ||||
|                                     </VM> | ||||
|                                     """ | ||||
|             vm_id = oca.VirtualMachine.allocate( | ||||
|                 self.client, | ||||
|                 vm_string_formatter.format( | ||||
|                     memory=1024 * specs.get('memory'), | ||||
|                     vcpu=specs.get('cores'), | ||||
|                     cpu=0.1 * specs.get('cores'), | ||||
|                     disk_type='fs', | ||||
|                     size=10000 * specs.get('disk_size') | ||||
|                 ) | ||||
|             ) | ||||
|                 # message = _("Created with id = " + str(vm_id)) | ||||
|                 # messages.add_message(request, messages.SUCCESS, message) | ||||
|         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_id | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     # Creating VM by using method allocate(client, template) | ||||
|     def create_vm(self, request): | ||||
|         # check if the request contains the template parameter, if it is | ||||
|  |  | |||
|  | @ -72,6 +72,11 @@ | |||
|                                 <i class="fa fa-credit-card"></i> {% trans "My Orders"%} | ||||
|                             </a> | ||||
|                         </li> | ||||
|                         <li> | ||||
|                             <a href="{% url 'hosting:key_pair' %}"> | ||||
|                                 <i class="fa fa-key" aria-hidden="true"></i> {% trans "Keys"%} | ||||
|                             </a> | ||||
|                         </li> | ||||
|                         <li> | ||||
|                             <a href="{% url 'hosting:notifications' %}"> | ||||
|                                 <i class="fa fa-bell"></i> {% trans "Notifications "%} | ||||
|  |  | |||
							
								
								
									
										45
									
								
								hosting/templates/hosting/create_virtual_machine.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								hosting/templates/hosting/create_virtual_machine.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| {% extends "hosting/base_short.html" %} | ||||
| {% load staticfiles bootstrap3 i18n %} | ||||
| {% block content %}  | ||||
| <div> | ||||
|     <div class="container dashboard-container"> | ||||
|         <div class="row"> | ||||
|             <div class="col-md-8 col-md-offset-2"> | ||||
|                 <h3><i class="fa fa-server" aria-hidden="true"></i> {% trans "New Virtual Machine"%} </h3> | ||||
|                 <hr/> | ||||
|                  | ||||
|                 <form  method="POST" action=""> | ||||
|                     {% csrf_token %} | ||||
|                     <div class="form-group"> | ||||
|                         Select VM: | ||||
|                         <select name="vm_template"> | ||||
|                             {% for vm in vm_types %} | ||||
|                                 | ||||
|                                   <option value="{{vm.id}}">CORE: {{vm.cores}}, RAM: {{vm.memory}}, SSD: {{vm.disk_size}}  </option> | ||||
|                                  | ||||
|                             {% endfor %} | ||||
|                         </select> | ||||
|                     </div>  | ||||
|                     <div class="form-group"> | ||||
|                         Select VM Configuration: | ||||
|                         <select name="configuration"> | ||||
|                             {% for config in configuration_options %} | ||||
|                                  | ||||
|                                 <option value="{{config.0}}">{{config.1}} </option> | ||||
|                                  | ||||
|                             {% endfor %} | ||||
|                         </select> | ||||
|                     </div>                           | ||||
|                     <div class="form-group"> | ||||
|                         <button class="btn btn-success" >{% trans "Start VM"%} </button>                          | ||||
|                     </div> | ||||
|                 </form> | ||||
| 
 | ||||
|             </div> | ||||
| 
 | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| {%endblock%} | ||||
|  | @ -24,58 +24,17 @@ | |||
|                 {% csrf_token %} | ||||
|                 <input type="hidden" name="hosting_company" value="{{vm.hosting_company}}"> | ||||
|                 <input type="hidden" name="location_code" value="{{vm.location_code}}"> | ||||
|                 <input type="hidden" name="vm_template" value="{{vm.id}}"> | ||||
|                 | ||||
|                  | ||||
|                  | ||||
|                 <ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}"> | ||||
|                   <li class="type"> | ||||
|                     <!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> --> | ||||
|                     <h3 >{{vm.location_code}}</h3> | ||||
|                     <br/> | ||||
|                     <img class="img-responsive" src="{{ STATIC_URL }}hosting/img/{{vm.location_code}}_flag.png" alt=""> | ||||
| 
 | ||||
|                   </li> | ||||
|                   <li> | ||||
|                     <!-- Single button --> | ||||
|                     <div class="btn-group"> | ||||
|                       <div class="form-group"> | ||||
|                         <label for="cores">Location: </label> | ||||
|                         {{vm.location}} | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </li> | ||||
|                   <li> | ||||
|                   <label for="configuration">Configuration: </label> | ||||
|                     {% if select_configuration %} | ||||
|                         <select class="form-control" name="configuration" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}"> | ||||
|                         {% for key,value in configuration_options.items   %} | ||||
|                             <option  value="{{key}}">{{ value }}</option> | ||||
|                         {% endfor %} | ||||
|                         </select> | ||||
|                     {% else %} | ||||
|                       <input type="hidden" name="configuration_detail" value="{{configuration_detail}}"> | ||||
|                       <input type="hidden" name="configuration" value="{{hosting}}"> | ||||
|                       <!-- Single button --> | ||||
|                       <div class="btn-group"> | ||||
|                         <div class="form-group"> | ||||
|                           <label>Configuration: </label> | ||||
|                           {{configuration_detail}} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     {% endif %} | ||||
|                   </li> | ||||
|                   <li> | ||||
|                     <!-- Single button --> | ||||
|                     <div class="btn-group"> | ||||
|                       <div class="form-group"> | ||||
|                         <label for="cores">Cores: </label>  | ||||
|                         <select class="form-control cores-selector" name="cores" id="{{vm.hosting_company}}-cores" data-vm-type="{{vm.hosting_company}}"> | ||||
|                         {% with ''|center:10 as range %} | ||||
|                         {% for _ in range %} | ||||
|                             <option>{{ forloop.counter }}</option> | ||||
|                         {% endfor %} | ||||
|                         {% endwith %} | ||||
|                         </select> | ||||
|                         <label for="cores">Cores: {{vm.cores}}</label>  | ||||
|                       </div> | ||||
|                     </div> | ||||
| 
 | ||||
|  | @ -83,30 +42,28 @@ | |||
|                   <li> | ||||
|                     <div class="form-group"> | ||||
|                       <div class="btn-group"> | ||||
|                         <label for="memory">Memory: </label>  | ||||
|                         <select class="form-control memory-selector" name="memory" id="{{vm.hosting_company}}-memory" data-vm-type="{{vm.hosting_company}}"> | ||||
|                         {% with ''|center:50 as range %} | ||||
|                         {% for _ in range %} | ||||
|                             <option>{{ forloop.counter }}</option> | ||||
|                         {% endfor %} | ||||
|                         {% endwith %} | ||||
|                         </select> | ||||
|                         <span>GiB</span> | ||||
|                         <label for="memory">Memory: {{vm.memory}} GiB</label>  | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </li>            | ||||
|                   <li> | ||||
|                     <div class="form-group row"> | ||||
|                       <div class="col-xs-offset-1 col-xs-9 col-sm-12 col-md-12 col-md-offset-0"> | ||||
|                         <label for="Disk Size">Disk Size: </label> | ||||
|                         <input class="form-control short-input text-center disk-space-selector" name="disk_space" type="number" id="{{vm.hosting_company}}-disk_space" min="10" value="10" step="10" data-vm-type="{{vm.hosting_company}}"/> | ||||
|                         <span>GiB</span> | ||||
|                         <label for="Disk Size">Disk Size: {{vm.disk_size}} GiB</label> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </li> | ||||
|                   <li> | ||||
|                     <input id="{{vm.hosting_company}}-final-price-input" type="hidden" name="final_price" value="{{vm.default_price|floatformat}}"> | ||||
|                     <h3 id="{{vm.hosting_company}}-final-price">{{vm.default_price|floatformat}}CHF</h3> | ||||
|                   <label for="configuration">Configuration: </label> | ||||
|                         <select class="form-control" name="configuration" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}"> | ||||
|                         {% for key,value in configuration_options.items   %} | ||||
|                             <option  value="{{key}}">{{ value }}</option> | ||||
|                         {% endfor %} | ||||
|                         </select> | ||||
|                   </li> | ||||
|                   <li> | ||||
|                     <input type="hidden" name="final_price" value="{{vm.final_price|floatformat}}"> | ||||
|                     <h3 id="{{vm.hosting_company}}-final-price">{{vm.final_price|floatformat}}CHF</h3> | ||||
|                     <span>per month</span> | ||||
|                   </li> | ||||
|                   <li> | ||||
|  |  | |||
|  | @ -65,7 +65,7 @@ | |||
|             {% url 'hosting:payment' as payment_url %} | ||||
|             {% if payment_url in request.META.HTTP_REFERER  %} | ||||
|             <div class=" content pull-right"> | ||||
|                 <a href="{% url 'hosting:virtual_machine_key' order.vm_plan.id %}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a> | ||||
|                 <a href="{% url 'hosting:key_pair'%}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a> | ||||
|             </div> | ||||
|             {% endif %} | ||||
|         </div> | ||||
|  |  | |||
|  | @ -86,11 +86,11 @@ | |||
| 							<h3><b>Billing Amount</b></h3> | ||||
| 							<hr> | ||||
| 							<div class="content"> | ||||
| 								<p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> | ||||
| 								<hr> | ||||
| 								<!-- <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> --> | ||||
| 								<!-- <hr> --> | ||||
| 								<p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p> | ||||
| 								<hr> | ||||
| 								<p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_detail}}</span></p> | ||||
| 								<p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_display}}</span></p> | ||||
| 								<hr>								 | ||||
| 								<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p> | ||||
| 								<hr> | ||||
|  |  | |||
|  | @ -6,30 +6,75 @@ | |||
| 		<div class="row"> | ||||
| 			<div class="col-md-9 col-md-offset-2"> | ||||
| 				 <div  class="col-sm-12"> | ||||
| 
 | ||||
| 				        <h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "SSH Private Key"%} </h3> | ||||
|                     <form method="POST" action="" > | ||||
|                         {% csrf_token %} | ||||
| 				        <h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3> | ||||
| 				        <hr/>	 | ||||
|                         {% if not user_key %} | ||||
|                             <div class="alert alert-warning"> | ||||
|                                 {% trans "Upload your own key. "%}  | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                               <label for="comment">Paste here your public key</label> | ||||
|                               <textarea class="form-control" rows="6" name="public_key"></textarea> | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <button class="btn btn-success">{% trans "Upload Key"%} </a> | ||||
|                             </div> | ||||
|                          | ||||
|                             <div class="alert alert-warning"> | ||||
|                                 {% trans "Or generate a new key pair."%}  | ||||
| 
 | ||||
|                             </div> | ||||
|                             <div class="form-group"> | ||||
|                                 <button class="btn btn-success">{% trans "Generate Key Pair"%} </a> | ||||
|                             </div> | ||||
|                         {% else %} | ||||
|                             <h5> Use your created key to access to the machine. If you lost it, contact us. </h5> | ||||
|                             <table class="table borderless table-hover">  | ||||
|                                 <br/> | ||||
|                                 <thead>  | ||||
|                                 <tr>  | ||||
|                                     <th>{% trans "Name"%}</th> | ||||
|                                     <th>{% trans "Created at"%} </th> | ||||
|                                     <th>{% trans "Status"%} </th> | ||||
|                                     <th></th> | ||||
|                                 </tr> | ||||
|                                 </thead> | ||||
|                                 <tbody>  | ||||
|                                     <tr>  | ||||
|                                         <td scope="row">{{user_key.name}}</td>  | ||||
|                                         <td>{{user_key.created_at}}</td>  | ||||
|                                         <td> | ||||
|                                             <span class="h3 label label-success"><strong>Active</strong></span> | ||||
| 
 | ||||
|                                         </td>  | ||||
|                                     </tr> | ||||
|                                 </tbody>  | ||||
|                             </table> | ||||
|                         {% endif %} | ||||
|                     </form> | ||||
| 
 | ||||
| 				        {% if private_key %} | ||||
| 				 		<div class="alert alert-warning"> | ||||
| 				 			  | ||||
|   							<strong>{% trans "Warning!"%}</strong>{% trans "You can view your SSH  private key once. Copy it or if it wasn't downloaded automatically, just click on Download to start it."%}   | ||||
| 						</div> | ||||
| 						<div class="form-group"> | ||||
| 						  <label for="comment">private_key.pem</label> | ||||
| 						  <textarea class="form-control" rows="6" id="ssh_key">{{private_key}}</textarea> | ||||
| 						  <textarea class="form-control" rows="6" id="ssh_key" type="hidden" style="display:none">{{private_key}}</textarea> | ||||
| 
 | ||||
| 						</div> | ||||
| 							<div  class="form-group pull-right"> | ||||
| <!-- 							<div  class="form-group pull-right"> | ||||
| 								<button type="button" id="copy_to_clipboard" data-clipboard-target="#ssh_key" class="btn btn-warning" | ||||
| 									data-toggle="tooltip"  data-placement="bottom" title="Copied"  data-trigger="click">{% trans "Copy to Clipboard"%}</button> | ||||
| 								<button type="button" id="download_ssh_key" class="btn btn-warning">{% trans "Download"%}</button>   | ||||
| 							</div>				         | ||||
| 							</div> -->				         | ||||
| 						{% else %} | ||||
| 					 		<div class="alert alert-warning"> | ||||
| <!-- 					 		<div class="alert alert-warning"> | ||||
| 	  							<strong>{% trans "Warning!"%}</strong>{% trans "Your SSH private key was already generated and downloaded, if you lost it, contact us. "%}  | ||||
| 							</div> | ||||
| 						{% endif %} | ||||
| 						<a class="btn btn-success" href="{% url 'hosting:virtual_machines' virtual_machine.id %}">{% trans "Go to my Virtual Machine Dashboard"%} </a> | ||||
|  -->						{% endif %} | ||||
| 						<!-- <a class="btn btn-success" href="{% url 'hosting:virtual_machines' %}">{% trans "Generate my key"%} </a> --> | ||||
| 						<div class="clearfix"></div> | ||||
| 				</div> | ||||
| 			</div> | ||||
|  | @ -42,11 +87,12 @@ | |||
| <!-- Force to download ssh key on page load --> | ||||
| <script type="text/javascript">  | ||||
| 
 | ||||
| 		var key = window.document.getElementById('ssh_key'); | ||||
|         var key = window.document.getElementById('ssh_key'); | ||||
| 
 | ||||
| 		var a = window.document.createElement('a'); | ||||
| 
 | ||||
| 		a.href = window.URL.createObjectURL(new Blob(['key'], {type: 'text'})); | ||||
| 		a.download = 'private_key.pem'; | ||||
| 		a.href = window.URL.createObjectURL(new Blob([key.value], {type: 'text'})); | ||||
| 		a.download = '{{key_name}}.pem'; | ||||
| 
 | ||||
| 		// Append anchor to body. | ||||
| 		document.body.appendChild(a); | ||||
|  |  | |||
|  | @ -6,12 +6,14 @@ | |||
| 		<div class="row"> | ||||
| 			<div class="col-md-8 col-md-offset-2"> | ||||
| 				<table class="table borderless table-hover">  | ||||
| 				<h3><i class="fa fa-server" aria-hidden="true"></i>{% trans "Virtual Machines"%} </h3>  | ||||
| 				<h3 class="pull-left"><i class="fa fa-server" aria-hidden="true"></i> {% trans "Virtual Machines"%} </h3> | ||||
|                 <p class="pull-right"> | ||||
|                     <a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>                     | ||||
|                 </p> | ||||
| 				<br/> | ||||
| 				<thead>  | ||||
| 				<tr>  | ||||
| 					<th>{% trans "ID"%}</th> | ||||
| 					<th>{% trans "Location"%} </th> | ||||
| 					<th>{% trans "Amount"%}</th> | ||||
| 					<th>{% trans "Status"%}</th> | ||||
| 					<th></th> | ||||
|  | @ -21,7 +23,6 @@ | |||
| 					{% for vm in vms %} | ||||
| 					<tr>  | ||||
| 						<td scope="row">{{vm.name}}</td>  | ||||
| 						<td>{{vm.location}}</td>  | ||||
| 						<td>{{vm.price}} CHF</td>  | ||||
| 						<td> | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\ | |||
|     NodeJSHostingView, LoginView, SignupView, IndexView, \ | ||||
|     OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ | ||||
|     VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \ | ||||
|     MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView | ||||
|     MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView,\ | ||||
|     CreateVirtualMachinesView | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|     url(r'index/?$', IndexView.as_view(), name='index'), | ||||
|  | @ -16,13 +17,14 @@ urlpatterns = [ | |||
|     url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), | ||||
|     url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), | ||||
|     url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), | ||||
|     url(r'create-virtual-machine/?$', CreateVirtualMachinesView.as_view(), name='create-virtual-machine'), | ||||
|     url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), | ||||
|     url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(), | ||||
|         name='virtual_machines'), | ||||
|     # url(r'my-virtual-machines/(?P<pk>\d+)/delete/?$', VirtualMachineCancelView.as_view(), | ||||
|         # name='virtual_machines_cancel'), | ||||
|     url(r'my-virtual-machines/(?P<pk>\d+)/key/?$', GenerateVMSSHKeysView.as_view(), | ||||
|         name='virtual_machine_key'), | ||||
|     url(r'vm-key-pair/?$', GenerateVMSSHKeysView.as_view(), | ||||
|         name='key_pair'), | ||||
|     url(r'^notifications/$', NotificationsView.as_view(), name='notifications'), | ||||
|     url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(), | ||||
|         name='read_notification'), | ||||
|  |  | |||
							
								
								
									
										134
									
								
								hosting/views.py
									
										
									
									
									
								
							
							
						
						
									
										134
									
								
								hosting/views.py
									
										
									
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| from collections import namedtuple | ||||
| 
 | ||||
| from django.shortcuts import render | ||||
| from django.core.urlresolvers import reverse_lazy, reverse | ||||
|  | @ -7,6 +8,8 @@ from django.views.generic import View, CreateView, FormView, ListView, DetailVie | |||
| from django.http import HttpResponseRedirect | ||||
| from django.contrib.auth import authenticate, login | ||||
| from django.conf import settings | ||||
| from django.shortcuts import redirect | ||||
| 
 | ||||
| 
 | ||||
| from guardian.mixins import PermissionRequiredMixin | ||||
| from stored_messages.settings import stored_messages_settings | ||||
|  | @ -14,14 +17,16 @@ from stored_messages.models import Message | |||
| from stored_messages.api import mark_read | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| from membership.models import CustomUser, StripeCustomer | ||||
| from utils.stripe_utils import StripeUtils | ||||
| from utils.forms import BillingAddressForm, PasswordResetRequestForm | ||||
| from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin | ||||
| from utils.mailer import BaseEmail | ||||
| from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder | ||||
| from .forms import HostingUserSignupForm, HostingUserLoginForm | ||||
| from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, UserHostingKey | ||||
| from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm | ||||
| from .mixins import ProcessVMSelectionMixin | ||||
| from .opennebula_functions import HostingManageVMAdmin | ||||
| 
 | ||||
| 
 | ||||
| class DjangoHostingView(ProcessVMSelectionMixin, View): | ||||
|  | @ -38,6 +43,7 @@ class DjangoHostingView(ProcessVMSelectionMixin, View): | |||
|             'google_analytics': "UA-62285904-6", | ||||
|             'email': "info@django-hosting.ch", | ||||
|             'vm_types': VirtualMachineType.get_serialized_vm_types(), | ||||
|             'configuration_options': dict(VirtualMachinePlan.VM_CONFIGURATION) | ||||
|         } | ||||
| 
 | ||||
|         return context | ||||
|  | @ -206,26 +212,52 @@ class MarkAsReadNotificationView(LoginRequiredMixin, UpdateView): | |||
|         return HttpResponseRedirect(reverse('hosting:notifications')) | ||||
| 
 | ||||
| 
 | ||||
| class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView): | ||||
|     model = VirtualMachinePlan | ||||
| class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): | ||||
|     form_class = UserHostingKeyForm | ||||
|     model = UserHostingKey | ||||
|     template_name = 'hosting/virtual_machine_key.html' | ||||
|     success_url = reverse_lazy('hosting:orders') | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
|     context_object_name = "virtual_machine" | ||||
| 
 | ||||
|     def get_context_data(self, **kwargs): | ||||
|         try: | ||||
|             user_key = UserHostingKey.objects.get( | ||||
|                 user=self.request.user | ||||
|             ) | ||||
|         except UserHostingKey.DoesNotExist: | ||||
|             user_key = None | ||||
| 
 | ||||
|         context = super( | ||||
|             GenerateVMSSHKeysView, | ||||
|             self | ||||
|         ).get_context_data(**kwargs) | ||||
| 
 | ||||
|         context.update({ | ||||
|             'user_key': user_key | ||||
|         }) | ||||
| 
 | ||||
|         context = super(GenerateVMSSHKeysView, self).get_context_data(**kwargs) | ||||
|         vm = self.get_object() | ||||
|         if not vm.public_key: | ||||
|             private_key, public_key = vm.generate_keys() | ||||
|             context.update({ | ||||
|                 'private_key': private_key, | ||||
|                 'public_key': public_key | ||||
|             }) | ||||
|             return context | ||||
|         return context | ||||
| 
 | ||||
|     def get_form_kwargs(self): | ||||
|         kwargs = super(GenerateVMSSHKeysView, self).get_form_kwargs() | ||||
|         kwargs.update({'request': self.request}) | ||||
|         return kwargs | ||||
| 
 | ||||
|     def form_valid(self, form): | ||||
|         form.save() | ||||
|         context = self.get_context_data() | ||||
| 
 | ||||
|         if form.cleaned_data.get('private_key'): | ||||
|             context.update({ | ||||
|                 'private_key': form.cleaned_data.get('private_key'), | ||||
|                 'key_name': form.cleaned_data.get('name') | ||||
|             }) | ||||
| 
 | ||||
|         # print("form", form.cleaned_data) | ||||
| 
 | ||||
|         return render(self.request, self.template_name, context) | ||||
| 
 | ||||
| 
 | ||||
| class PaymentVMView(LoginRequiredMixin, FormView): | ||||
|     template_name = 'hosting/payment.html' | ||||
|  | @ -246,18 +278,26 @@ class PaymentVMView(LoginRequiredMixin, FormView): | |||
|         if form.is_valid(): | ||||
|             context = self.get_context_data() | ||||
|             specifications = request.session.get('vm_specs') | ||||
|             vm_type = specifications.get('hosting_company') | ||||
|             vm = VirtualMachineType.objects.get(hosting_company=vm_type) | ||||
|             final_price = vm.calculate_price(specifications) | ||||
| 
 | ||||
|             vm_template = specifications.get('vm_template', 1) | ||||
| 
 | ||||
|             vm_type = VirtualMachineType.objects.get(id=vm_template) | ||||
| 
 | ||||
|             specs = vm_type.get_specs() | ||||
| 
 | ||||
|             final_price = vm_type.calculate_price() | ||||
| 
 | ||||
|             plan_data = { | ||||
|                 'vm_type': vm, | ||||
|                 'cores': specifications.get('cores'), | ||||
|                 'memory': specifications.get('memory'), | ||||
|                 'disk_size': specifications.get('disk_size'), | ||||
|                 'configuration': specifications.get('configuration'), | ||||
|                 'vm_type': vm_type, | ||||
|                 'configuration': specifications.get( | ||||
|                     'configuration', | ||||
|                     'django' | ||||
|                 ), | ||||
|                 'price': final_price | ||||
|             } | ||||
| 
 | ||||
|             plan_data.update(specs) | ||||
| 
 | ||||
|             token = form.cleaned_data.get('token') | ||||
| 
 | ||||
|             # Get or create stripe customer | ||||
|  | @ -299,6 +339,20 @@ class PaymentVMView(LoginRequiredMixin, FormView): | |||
|             # If the Stripe payment was successed, set order status approved | ||||
|             order.set_approved() | ||||
| 
 | ||||
|             # Create VM using oppenebula functions  | ||||
|             # _request = namedtuple('request', 'POST user') | ||||
|             # _request.user = request.user | ||||
|             # user = namedtuple('user', 'email') | ||||
|             # email  | ||||
|             # _request.POST = { | ||||
|                 # 'vm_template': vm_template | ||||
|             # } | ||||
| 
 | ||||
|             hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) | ||||
|             hosting_admin.init_opennebula_client(request) | ||||
|             oppennebula_vm_id = hosting_admin.create_vm_view(vm_type.get_specs()) | ||||
|             plan.oppenebula_id = oppennebula_vm_id | ||||
| 
 | ||||
|             # Send notification to ungleich as soon as VM has been booked | ||||
|             context = { | ||||
|                 'vm': plan, | ||||
|  | @ -358,11 +412,49 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): | |||
|     ordering = '-id' | ||||
| 
 | ||||
|     def get_queryset(self): | ||||
|         # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) | ||||
|         # print(hosting_admin.show_vms_view(self.request)) | ||||
|         print(VirtualMachinePlan.get_vms(self.request.user.email)) | ||||
|         user = self.request.user | ||||
|         self.queryset = VirtualMachinePlan.objects.active(user) | ||||
|         return super(VirtualMachinesPlanListView, self).get_queryset() | ||||
| 
 | ||||
| 
 | ||||
| class CreateVirtualMachinesView(LoginRequiredMixin, View): | ||||
|     template_name = "hosting/create_virtual_machine.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
| 
 | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         context = { | ||||
|             'vm_types': VirtualMachineType.get_serialized_vm_types(), | ||||
|             'configuration_options': VirtualMachinePlan.VM_CONFIGURATION | ||||
|         } | ||||
|         # context = {} | ||||
|         return render(request, self.template_name, context) | ||||
| 
 | ||||
|     def post(self, request): | ||||
|         configuration = request.POST.get('configuration') | ||||
|         configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) | ||||
|         vm_template = request.POST.get('vm_template') | ||||
|         vm_type = VirtualMachineType.objects.get(id=vm_template) | ||||
|         vm_specs = vm_type.get_specs() | ||||
|         vm_specs.update({ | ||||
|             'configuration_display': configuration_display, | ||||
|             'configuration': configuration, | ||||
|             'final_price': vm_type.final_price, | ||||
|             'vm_template': vm_template | ||||
|         }) | ||||
|         request.session['vm_specs'] = vm_specs | ||||
|         return redirect(reverse('hosting:payment')) | ||||
| 
 | ||||
|     # def get_queryset(self): | ||||
|     #     # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) | ||||
|     #     # print(hosting_admin.show_vms(self.request)) | ||||
|     #     user = self.request.user | ||||
|     #     self.queryset = VirtualMachinePlan.objects.active(user) | ||||
|     #     return super(VirtualMachinesPlanListView, self).get_queryset() | ||||
| 
 | ||||
| 
 | ||||
| class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView): | ||||
|     template_name = "hosting/virtual_machine_detail.html" | ||||
|     login_url = reverse_lazy('hosting:login') | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue