Merge branch 'develop' into hosting_i18n
This commit is contained in:
		
				commit
				
					
						31e9377818
					
				
			
		
					 14 changed files with 381 additions and 298 deletions
				
			
		|  | @ -5,9 +5,14 @@ | ||||||
|     * [datacenterlight] Fix initially shown price |     * [datacenterlight] Fix initially shown price | ||||||
| 1.0.2: 2017-05-28 | 1.0.2: 2017-05-28 | ||||||
|     * [datacenterlight] Fixed login redirecting to blank page after logout |     * [datacenterlight] Fixed login redirecting to blank page after logout | ||||||
|  | 1.0.3: 2017-06-02 | ||||||
|  |     * [datacenterlight] Hotfix, remove footer on mobile devices | ||||||
| 
 | 
 | ||||||
| next: | next: | ||||||
| 	* [datacenterlight] Add German translations | 	* [datacenterlight] Add German translations | ||||||
| 	* [datacenterlight] Change beta access to subscriptions  | 	* [datacenterlight] Change beta access to subscriptions  | ||||||
| 	* [hosting] Add German translations  | 	* [hosting] Add German translations  | ||||||
| 	* [blog] Add German translation for header | 	* [blog] Add German translation for header | ||||||
|  | 	* [opennebula_api] Improve testing, add ssh key functions | ||||||
|  | 	* [opennebula_api] Remove template views | ||||||
|  | 	* [datacenterlight] Allow user to have multiple ssh keys  | ||||||
|  |  | ||||||
|  | @ -235,7 +235,6 @@ | ||||||
|                 <div class="col-xs-12 col-md-6 text"> |                 <div class="col-xs-12 col-md-6 text"> | ||||||
|                     <h2 class="section-heading">{% trans "We are cutting down the costs significantly!" %}</h2> |                     <h2 class="section-heading">{% trans "We are cutting down the costs significantly!" %}</h2> | ||||||
|                     <p class="lead">{% trans "Affordable VM hosting based in Switzerland" %}</p> |                     <p class="lead">{% trans "Affordable VM hosting based in Switzerland" %}</p> | ||||||
|                      <a href="#" class="btn btn-info btn-lg">{% trans "More Info" %}</a>  |  | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|                 <div class="col-xs-12 col-md-6 hero-feature"> |                 <div class="col-xs-12 col-md-6 hero-feature"> | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ from django import forms | ||||||
| from membership.models import CustomUser | from membership.models import CustomUser | ||||||
| from django.contrib.auth import authenticate | from django.contrib.auth import authenticate | ||||||
| 
 | 
 | ||||||
|  | from django.utils.translation import ugettext_lazy as _ | ||||||
| 
 | 
 | ||||||
| from utils.stripe_utils import StripeUtils | from utils.stripe_utils import StripeUtils | ||||||
| 
 | 
 | ||||||
|  | @ -57,21 +58,19 @@ class HostingUserSignupForm(forms.ModelForm): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UserHostingKeyForm(forms.ModelForm): | class UserHostingKeyForm(forms.ModelForm): | ||||||
|     private_key = forms.CharField(widget=forms.PasswordInput(), required=False) |     private_key = forms.CharField(widget=forms.HiddenInput(), required=False) | ||||||
|     public_key = forms.CharField(widget=forms.PasswordInput(), required=False) |     public_key = forms.CharField(widget=forms.Textarea(), required=False, | ||||||
|     user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(), required=False) |             help_text=_('Paste here your public key')) | ||||||
|     name = forms.CharField(required=False) |     user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(), | ||||||
|  |             required=False, widget=forms.HiddenInput()) | ||||||
|  |     name = forms.CharField(required=True) | ||||||
| 
 | 
 | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         self.request = kwargs.pop("request") |         self.request = kwargs.pop("request") | ||||||
|         super(UserHostingKeyForm, self).__init__(*args, **kwargs) |         super(UserHostingKeyForm, self).__init__(*args, **kwargs) | ||||||
|         # self.initial['user'].initial = self.request.user.id |  | ||||||
|         # print(self.fields) |  | ||||||
| 
 | 
 | ||||||
|     def clean_name(self): |     def clean_name(self): | ||||||
|         return "dcl-priv-key-%s" % ( |         return self.data.get('name') | ||||||
|             ''.join(random.choice(string.ascii_lowercase) for i in range(7)) |  | ||||||
|         ) |  | ||||||
| 
 | 
 | ||||||
|     def clean_user(self): |     def clean_user(self): | ||||||
|         return self.request.user |         return self.request.user | ||||||
|  | @ -90,4 +89,4 @@ class UserHostingKeyForm(forms.ModelForm): | ||||||
| 
 | 
 | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = UserHostingKey |         model = UserHostingKey | ||||||
|         fields = ['user', 'public_key', 'name'] |         fields = ['user', 'name', 'public_key'] | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ msgid "" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: PACKAGE VERSION\n" | "Project-Id-Version: PACKAGE VERSION\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2017-05-30 13:47+0000\n" | "POT-Creation-Date: 2017-06-01 21:03+0000\n" | ||||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||||
|  | @ -18,6 +18,10 @@ msgstr "" | ||||||
| "Content-Transfer-Encoding: 8bit\n" | "Content-Transfer-Encoding: 8bit\n" | ||||||
| "Plural-Forms: nplurals=2; plural=(n != 1);\n" | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||||||
| 
 | 
 | ||||||
|  | #: hosting/forms.py:63 | ||||||
|  | msgid "Paste here your public key" | ||||||
|  | msgstr "Fügen Sie Ihren public key ein" | ||||||
|  | 
 | ||||||
| #: hosting/templates/hosting/base_short.html:68 | #: hosting/templates/hosting/base_short.html:68 | ||||||
| #: hosting/templates/hosting/base_short.html:139 | #: hosting/templates/hosting/base_short.html:139 | ||||||
| msgid "My Virtual Machines" | msgid "My Virtual Machines" | ||||||
|  | @ -143,7 +147,7 @@ msgid "Customers" | ||||||
| msgstr "Kunden" | msgstr "Kunden" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/bills.html:16 | #: hosting/templates/hosting/bills.html:16 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:45 | #: hosting/templates/hosting/virtual_machine_key.html:42 | ||||||
| msgid "Name" | msgid "Name" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | @ -330,7 +334,7 @@ msgstr "Betrag" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/orders.html:19 | #: hosting/templates/hosting/orders.html:19 | ||||||
| #: hosting/templates/hosting/virtual_machine_detail.html:30 | #: hosting/templates/hosting/virtual_machine_detail.html:30 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:47 | #: hosting/templates/hosting/virtual_machine_key.html:44 | ||||||
| #: hosting/templates/hosting/virtual_machines.html:31 | #: hosting/templates/hosting/virtual_machines.html:31 | ||||||
| msgid "Status" | msgid "Status" | ||||||
| msgstr "" | msgstr "" | ||||||
|  | @ -449,7 +453,7 @@ msgstr "Beenden" | ||||||
| msgid "Access Key" | msgid "Access Key" | ||||||
| msgstr "Zugriffsschlüssel" | msgstr "Zugriffsschlüssel" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:22 | #: hosting/templates/hosting/virtual_machine_key.html:25 | ||||||
| msgid "Upload your own key. " | msgid "Upload your own key. " | ||||||
| msgstr "Laden Sie ihren Schlüssel hoch" | msgstr "Laden Sie ihren Schlüssel hoch" | ||||||
| 
 | 
 | ||||||
|  | @ -461,7 +465,7 @@ msgstr "Schlüssel hochladen" | ||||||
| msgid "Or generate a new key pair." | msgid "Or generate a new key pair." | ||||||
| msgstr "Oder erstellen Sie ein neues Schlüsselpaar" | msgstr "Oder erstellen Sie ein neues Schlüsselpaar" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:37 | #: hosting/templates/hosting/virtual_machine_key.html:31 | ||||||
| msgid "Generate Key Pair" | msgid "Generate Key Pair" | ||||||
| msgstr "Schlüsselpaar generieren" | msgstr "Schlüsselpaar generieren" | ||||||
| 
 | 
 | ||||||
|  | @ -472,30 +476,30 @@ msgstr "" | ||||||
| "Verwenden Sie Ihren privaten SSH Schlüssel um sich mit Ihren Maschinen zu " | "Verwenden Sie Ihren privaten SSH Schlüssel um sich mit Ihren Maschinen zu " | ||||||
| "verbinden. Falls Sie ihn verloren haben kontaktieren Sie uns." | "verbinden. Falls Sie ihn verloren haben kontaktieren Sie uns." | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:46 | #: hosting/templates/hosting/virtual_machine_key.html:43 | ||||||
| msgid "Created at" | msgid "Created at" | ||||||
| msgstr "Erstellt am" | msgstr "Erstellt am" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:68 | #: hosting/templates/hosting/virtual_machine_key.html:66 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:81 | #: hosting/templates/hosting/virtual_machine_key.html:79 | ||||||
| msgid "Warning!" | msgid "Warning!" | ||||||
| msgstr "Achtung!" | msgstr "Achtung!" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:68 | #: hosting/templates/hosting/virtual_machine_key.html:66 | ||||||
| msgid "You can download your SSH  private key once. Don't lost your key" | msgid "You can download your SSH  private key once. Don't lost your key" | ||||||
| msgstr "" | msgstr "" | ||||||
| "Sie können ihren privaten SSH Schlüssel nur einmal herunterladen. Bewaren " | "Sie können ihren privaten SSH Schlüssel nur einmal herunterladen. Bewaren " | ||||||
| "Sie ihn sicher auf." | "Sie ihn sicher auf." | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:76 | #: hosting/templates/hosting/virtual_machine_key.html:74 | ||||||
| msgid "Copy to Clipboard" | msgid "Copy to Clipboard" | ||||||
| msgstr "Kopieren" | msgstr "Kopieren" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:77 | #: hosting/templates/hosting/virtual_machine_key.html:75 | ||||||
| msgid "Download" | msgid "Download" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:81 | #: hosting/templates/hosting/virtual_machine_key.html:79 | ||||||
| msgid "" | msgid "" | ||||||
| "Your SSH private key was already generated and downloaded, if you lost it, " | "Your SSH private key was already generated and downloaded, if you lost it, " | ||||||
| "contact us. " | "contact us. " | ||||||
|  | @ -503,7 +507,7 @@ msgstr "" | ||||||
| "Ihr privater SSH Schlüssel wurde bereits generiert und heruntergeladen, " | "Ihr privater SSH Schlüssel wurde bereits generiert und heruntergeladen, " | ||||||
| "falls Sie ihn verloren haben kontaktieren Sie uns." | "falls Sie ihn verloren haben kontaktieren Sie uns." | ||||||
| 
 | 
 | ||||||
| #: hosting/templates/hosting/virtual_machine_key.html:84 | #: hosting/templates/hosting/virtual_machine_key.html:82 | ||||||
| msgid "Generate my key" | msgid "Generate my key" | ||||||
| msgstr "Generiere meinen Schlüssel" | msgstr "Generiere meinen Schlüssel" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -73,11 +73,7 @@ | ||||||
|                                 <i class="fa fa-credit-card"></i> {% trans "My Orders"%} |                                 <i class="fa fa-credit-card"></i> {% trans "My Orders"%} | ||||||
|                             </a> |                             </a> | ||||||
|                         </li> |                         </li> | ||||||
|                         <li> |                          | ||||||
|                             <a href="{% url 'hosting:key_pair' %}"> |  | ||||||
|                                 <i class="fa fa-key" aria-hidden="true"></i> {% trans "Keys"%} |  | ||||||
|                             </a> |  | ||||||
|                         </li> |  | ||||||
|                         <li> |                         <li> | ||||||
|                             <a href="{% url 'hosting:notifications' %}"> |                             <a href="{% url 'hosting:notifications' %}"> | ||||||
|                                 <i class="fa fa-bell"></i> {% trans "Notifications "%} |                                 <i class="fa fa-bell"></i> {% trans "Notifications "%} | ||||||
|  | @ -88,6 +84,11 @@ | ||||||
|                             <i class="glyphicon glyphicon-user"></i> {{request.user.name}} <span class="caret"></span></a> |                             <i class="glyphicon glyphicon-user"></i> {{request.user.name}} <span class="caret"></span></a> | ||||||
|                           <ul id="g-account-menu" class="dropdown-menu" role="menu"> |                           <ul id="g-account-menu" class="dropdown-menu" role="menu"> | ||||||
|                             <li><a href="{% url 'hosting:logout' %}"><i class="glyphicon glyphicon-lock"></i>{% trans "Logout"%} </a></li> |                             <li><a href="{% url 'hosting:logout' %}"><i class="glyphicon glyphicon-lock"></i>{% trans "Logout"%} </a></li> | ||||||
|  | 							<li> | ||||||
|  |                             <a href="{% url 'hosting:key_pair' %}"> | ||||||
|  |                                 <i class="fa fa-key"></i> {% trans "Keys"%} | ||||||
|  |                             </a> | ||||||
|  |                         	</li> | ||||||
|                           </ul> |                           </ul> | ||||||
|                         </li> |                         </li> | ||||||
|                         <!--  |                         <!--  | ||||||
|  | @ -128,7 +129,7 @@ | ||||||
|     <footer class="navbar-fixed-bottom"> |     <footer class="navbar-fixed-bottom"> | ||||||
|         <div class="container"> |         <div class="container"> | ||||||
|             <div class="row"> |             <div class="row"> | ||||||
|                 <div class="col-lg-12"> |                 <div class="col-lg-12 hidden-xs"> | ||||||
|                     <ul class="list-inline"> |                     <ul class="list-inline"> | ||||||
|                         <li> |                         <li> | ||||||
|                             <a href="#">{% trans "Home"%}</a> |                             <a href="#">{% trans "Home"%}</a> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
| 		<div class="row"> | 		<div class="row"> | ||||||
| 			<div class="col-md-9 col-md-offset-2"> | 			<div class="col-md-9 col-md-offset-2"> | ||||||
| 				 <div  class="col-sm-12"> | 				 <div  class="col-sm-12"> | ||||||
|                     <form method="POST" action="" > |                     <form method="POST" action="" novalidate> | ||||||
|                         {% csrf_token %} |                         {% csrf_token %} | ||||||
| 				        <h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3> | 				        <h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3> | ||||||
|                         {% if messages %} |                         {% if messages %} | ||||||
|  | @ -15,26 +15,24 @@ | ||||||
|                             <span>{{ message }}</span> |                             <span>{{ message }}</span> | ||||||
|                             {% endfor %} |                             {% endfor %} | ||||||
|                         </div> |                         </div> | ||||||
|                         {% endif %} | 						{% endif %} | ||||||
| 				        <hr/>	 |                         {% for field in form %} | ||||||
|                         {% if not user_key %} | 							 | ||||||
|                             <h3> |                             {% bootstrap_field field %} | ||||||
|  |                         {% endfor %} | ||||||
|  |                         {% buttons %} | ||||||
|  |                             <button type="submit" class="btn btn-success"> | ||||||
|                                 {% trans "Upload your own key. "%}  |                                 {% trans "Upload your own key. "%}  | ||||||
|                             </h3> |                             </button> | ||||||
|                             <div class="form-group"> | <br /> | ||||||
|                               <label for="comment">Paste here your public key</label> | <br /> | ||||||
|                               <textarea class="form-control" rows="6" name="public_key"></textarea> | 							{% trans "Or generate a new key pair."%} <br /> | ||||||
|                             </div> | <br /> | ||||||
|                             <div class="form-group"> |  | ||||||
|                                 <button class="btn btn-success">{% trans "Upload Key"%} </a> |  | ||||||
|                             </div> |  | ||||||
|                          |  | ||||||
|                             <h3> |  | ||||||
|                                 {% trans "Or generate a new key pair."%}  |  | ||||||
| 
 |  | ||||||
|                             </h3> |  | ||||||
|                             <div class="form-group"> |  | ||||||
|                                 <button class="btn btn-success">{% trans "Generate Key Pair"%} </a> |                                 <button class="btn btn-success">{% trans "Generate Key Pair"%} </a> | ||||||
|  |                             </button> | ||||||
|  | 
 | ||||||
|  |                         {% endbuttons %}  | ||||||
|  |                             <div class="form-group"> | ||||||
|                             </div> |                             </div> | ||||||
|                         {% else %} |                         {% else %} | ||||||
| 						<h5> {% trans "Use your created key to access to the machine. If you lost it, contact us." %} </h5> | 						<h5> {% trans "Use your created key to access to the machine. If you lost it, contact us." %} </h5> | ||||||
|  | @ -49,6 +47,7 @@ | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                                 </thead> |                                 </thead> | ||||||
|                                 <tbody>  |                                 <tbody>  | ||||||
|  | 									{% for user_key in keys %} | ||||||
|                                     <tr>  |                                     <tr>  | ||||||
|                                         <td scope="row">{{user_key.name}}</td>  |                                         <td scope="row">{{user_key.name}}</td>  | ||||||
|                                         <td>{{user_key.created_at}}</td>  |                                         <td>{{user_key.created_at}}</td>  | ||||||
|  | @ -57,9 +56,9 @@ | ||||||
| 
 | 
 | ||||||
|                                         </td>  |                                         </td>  | ||||||
|                                     </tr> |                                     </tr> | ||||||
|  | 									{% endfor %} | ||||||
|                                 </tbody>  |                                 </tbody>  | ||||||
|                             </table> |                             </table> | ||||||
|                         {% endif %} |  | ||||||
|                     </form> |                     </form> | ||||||
| 
 | 
 | ||||||
| 				        {% if private_key %} | 				        {% if private_key %} | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| 
 | 
 | ||||||
| # Create your tests here. | # Create your tests here. | ||||||
|  | 
 | ||||||
|  | test_user_can_add_key() | ||||||
|  |  | ||||||
|  | @ -301,16 +301,12 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): | ||||||
|             self |             self | ||||||
|         ).get_context_data(**kwargs) |         ).get_context_data(**kwargs) | ||||||
| 
 | 
 | ||||||
|         try: |         user_keys = UserHostingKey.objects.filter( | ||||||
|             user_key = UserHostingKey.objects.get( |             user=self.request.user | ||||||
|                 user=self.request.user |         ) | ||||||
|             ) |  | ||||||
| 
 |  | ||||||
|         except UserHostingKey.DoesNotExist: |  | ||||||
|             user_key = None |  | ||||||
| 
 | 
 | ||||||
|         context.update({ |         context.update({ | ||||||
|             'user_key': user_key |             'keys': user_keys | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|         return context |         return context | ||||||
|  | @ -351,24 +347,14 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): | ||||||
|         opennebula_user = user_pool.get_by_name(owner.email) |         opennebula_user = user_pool.get_by_name(owner.email) | ||||||
| 
 | 
 | ||||||
|         # Get user ssh key |         # Get user ssh key | ||||||
|         user_key = UserHostingKey.objects.get(user=owner) |         public_key = form.cleaned_data.get('public_key') | ||||||
|         # Add ssh key to user |         # Add ssh key to user | ||||||
|         manager.oneadmin_client.call('user.update', opennebula_user.id, |         manager.oneadmin_client.call('user.update', opennebula_user.id, | ||||||
|                                      '<CONTEXT><SSH_PUBLIC_KEY>{ssh_key}</SSH_PUBLIC_KEY></CONTEXT>'.format(ssh_key=user_key.public_key)) |                                      '<CONTEXT><SSH_PUBLIC_KEY>{key}</SSH_PUBLIC_KEY></CONTEXT>'.format(key=public_key)) | ||||||
| 
 | 
 | ||||||
|         return render(self.request, self.template_name, context) |         return render(self.request, self.template_name, context) | ||||||
| 
 | 
 | ||||||
|     def post(self, request, *args, **kwargs): |     def post(self, request, *args, **kwargs): | ||||||
| 
 |  | ||||||
|         try: |  | ||||||
|             UserHostingKey.objects.get( |  | ||||||
|                 user=self.request.user |  | ||||||
|             ) |  | ||||||
|             return HttpResponseRedirect(reverse('hosting:key_pair')) |  | ||||||
| 
 |  | ||||||
|         except UserHostingKey.DoesNotExist: |  | ||||||
|             pass |  | ||||||
| 
 |  | ||||||
|         form = self.get_form() |         form = self.get_form() | ||||||
|         if form.is_valid(): |         if form.is_valid(): | ||||||
|             return self.form_valid(form) |             return self.form_valid(form) | ||||||
|  | @ -421,11 +407,7 @@ class PaymentVMView(LoginRequiredMixin, FormView): | ||||||
|         return context |         return context | ||||||
| 
 | 
 | ||||||
|     def get(self, request, *args, **kwargs): |     def get(self, request, *args, **kwargs): | ||||||
|         try: |         if not UserHostingKey.objects.filter( user=self.request.user).exists(): | ||||||
|             UserHostingKey.objects.get( |  | ||||||
|                 user=self.request.user |  | ||||||
|             ) |  | ||||||
|         except UserHostingKey.DoesNotExist: |  | ||||||
|             messages.success( |             messages.success( | ||||||
|                 request, |                 request, | ||||||
|                 'In order to create a VM, you create/upload your SSH KEY first.' |                 'In order to create a VM, you create/upload your SSH KEY first.' | ||||||
|  | @ -487,13 +469,15 @@ class PaymentVMView(LoginRequiredMixin, FormView): | ||||||
|             manager = OpenNebulaManager(email=owner.email, |             manager = OpenNebulaManager(email=owner.email, | ||||||
|                                         password=owner.password) |                                         password=owner.password) | ||||||
|             # Get user ssh key |             # Get user ssh key | ||||||
|             try: |             if not UserHostingKey.objects.filter( user=self.request.user).exists(): | ||||||
|                 user_key = UserHostingKey.objects.get( |                 context.update({ | ||||||
|                     user=self.request.user |                     'sshError': 'error', | ||||||
|                 ) |                     'form': form | ||||||
| 
 |                 }) | ||||||
|             except UserHostingKey.DoesNotExist: |                 return render(request, self.template_name, context) | ||||||
|                 pass |             # For now just get first one | ||||||
|  |             user_key = UserHostingKey.objects.filter( | ||||||
|  |                     user=self.request.user).first() | ||||||
|              |              | ||||||
|             # Create a vm using logged user |             # Create a vm using logged user | ||||||
|             vm_id = manager.create_vm( |             vm_id = manager.create_vm( | ||||||
|  | @ -639,11 +623,7 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): | ||||||
| 
 | 
 | ||||||
|     def get(self, request, *args, **kwargs): |     def get(self, request, *args, **kwargs): | ||||||
| 
 | 
 | ||||||
|         try: |         if not UserHostingKey.objects.filter( user=self.request.user).exists(): | ||||||
|             UserHostingKey.objects.get( |  | ||||||
|                 user=self.request.user |  | ||||||
|             ) |  | ||||||
|         except UserHostingKey.DoesNotExist: |  | ||||||
|             messages.success( |             messages.success( | ||||||
|                 request, |                 request, | ||||||
|                 'In order to create a VM, you need to create/upload your SSH KEY first.' |                 'In order to create a VM, you need to create/upload your SSH KEY first.' | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								opennebula_api/exceptions.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								opennebula_api/exceptions.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | 
 | ||||||
|  | class KeyExistsError(Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class UserExistsError(Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | class UserCredentialError(Exception): | ||||||
|  |     pass | ||||||
|  | @ -2,12 +2,15 @@ import oca | ||||||
| import socket | import socket | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
|  | from oca.pool import WrongNameError | ||||||
|  | from oca.exceptions import OpenNebulaException | ||||||
| 
 | 
 | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.utils.functional import cached_property | from django.utils.functional import cached_property | ||||||
| 
 | 
 | ||||||
| from oca.pool import WrongNameError | from utils.models import CustomUser | ||||||
| from oca.exceptions import OpenNebulaException | from .exceptions import KeyExistsError, UserExistsError, UserCredentialError | ||||||
|  | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -35,10 +38,33 @@ class OpenNebulaManager(): | ||||||
|             ) |             ) | ||||||
|         except: |         except: | ||||||
|             pass |             pass | ||||||
|  |     def _get_client(self, user): | ||||||
|  |         """Get a opennebula client object for a CustomUser object  | ||||||
|  |          | ||||||
|  |         Args: | ||||||
|  |             user (CustomUser): dynamicweb CustomUser object | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             oca.Client: Opennebula client object | ||||||
|  | 
 | ||||||
|  |         Raise: | ||||||
|  |             ConnectionError: If the connection to the opennebula server can't be | ||||||
|  |                 established  | ||||||
|  |         """ | ||||||
|  |         return oca.Client("{0}:{1}".format( | ||||||
|  |             user.email, | ||||||
|  |             user.password), | ||||||
|  |             "{protocol}://{domain}:{port}{endpoint}".format( | ||||||
|  |                 protocol=settings.OPENNEBULA_PROTOCOL, | ||||||
|  |                 domain=settings.OPENNEBULA_DOMAIN, | ||||||
|  |                 port=settings.OPENNEBULA_PORT, | ||||||
|  |                 endpoint=settings.OPENNEBULA_ENDPOINT | ||||||
|  |         )) | ||||||
| 
 | 
 | ||||||
|     def _get_opennebula_client(self, username, password): |     def _get_opennebula_client(self, username, password): | ||||||
|         return oca.Client("{0}:{1}".format( |         return oca.Client("{0}:{1}".format( | ||||||
|             username, |             username, | ||||||
|  | 
 | ||||||
|             password), |             password), | ||||||
|             "{protocol}://{domain}:{port}{endpoint}".format( |             "{protocol}://{domain}:{port}{endpoint}".format( | ||||||
|                 protocol=settings.OPENNEBULA_PROTOCOL, |                 protocol=settings.OPENNEBULA_PROTOCOL, | ||||||
|  | @ -47,6 +73,69 @@ class OpenNebulaManager(): | ||||||
|                 endpoint=settings.OPENNEBULA_ENDPOINT |                 endpoint=settings.OPENNEBULA_ENDPOINT | ||||||
|         )) |         )) | ||||||
| 
 | 
 | ||||||
|  |     def _get_user(self, user): | ||||||
|  |         """Get the corresponding opennebula user for a CustomUser object  | ||||||
|  |          | ||||||
|  |         Args: | ||||||
|  |             user (CustomUser): dynamicweb CustomUser object | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             oca.User: Opennebula user object | ||||||
|  | 
 | ||||||
|  |         Raise: | ||||||
|  |             WrongNameError: If no openebula user with this credentials exists | ||||||
|  |             ConnectionError: If the connection to the opennebula server can't be | ||||||
|  |                 established  | ||||||
|  |         """ | ||||||
|  |         user_pool = self._get_user_pool() | ||||||
|  |         return user_pool.get_by_name(user.email) | ||||||
|  | 
 | ||||||
|  |     def create_user(self, user: CustomUser): | ||||||
|  |         """Create a new opennebula user or a corresponding CustomUser object | ||||||
|  | 
 | ||||||
|  |          | ||||||
|  |         Args: | ||||||
|  |             user (CustomUser): dynamicweb CustomUser object | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             int: Return the opennebula user id | ||||||
|  |              | ||||||
|  |         Raises: | ||||||
|  |             ConnectionError: If the connection to the opennebula server can't be | ||||||
|  |                 established  | ||||||
|  |             UserExistsError: If a user with this credeintals already exits on the | ||||||
|  |                 server | ||||||
|  |             UserCredentialError: If a user with this email exists but the | ||||||
|  |                 password is worng | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|  |         try: | ||||||
|  |             self._get_user(user) | ||||||
|  |             try:  | ||||||
|  |                 self._get_client(self, user) | ||||||
|  |                 logger.debug('User already exists') | ||||||
|  |                 raise UserExistsError() | ||||||
|  |             except OpenNebulaException as err: | ||||||
|  |                 logger.error('OpenNebulaException error: {0}'.format(err)) | ||||||
|  |                 logger.debug('User exists but password is wrong') | ||||||
|  |                 raise UserCredentialError() | ||||||
|  | 
 | ||||||
|  |         except WrongNameError: | ||||||
|  |             user_id = self.oneadmin_client.call(oca.User.METHODS['allocate'], | ||||||
|  |                 user.email, user.password, 'core') | ||||||
|  |             logger.debug('Created a user for CustomObject: {user} with user id = {u_id}', | ||||||
|  |                 user=user, | ||||||
|  |                 u_id=user_id | ||||||
|  |             ) | ||||||
|  |             return user_id  | ||||||
|  |         except ConnectionRefusedError: | ||||||
|  |             logger.error('Could not connect to host: {host} via protocol {protocol}'.format( | ||||||
|  |                 host=settings.OPENNEBULA_DOMAIN, | ||||||
|  |                 protocol=settings.OPENNEBULA_PROTOCOL) | ||||||
|  |             ) | ||||||
|  |             raise ConnectionRefusedError | ||||||
|  |          | ||||||
|  | 
 | ||||||
|     def _get_or_create_user(self, email, password): |     def _get_or_create_user(self, email, password): | ||||||
|         try: |         try: | ||||||
|             user_pool = self._get_user_pool() |             user_pool = self._get_user_pool() | ||||||
|  | @ -77,7 +166,7 @@ class OpenNebulaManager(): | ||||||
|                 host=settings.OPENNEBULA_DOMAIN, |                 host=settings.OPENNEBULA_DOMAIN, | ||||||
|                 protocol=settings.OPENNEBULA_PROTOCOL) |                 protocol=settings.OPENNEBULA_PROTOCOL) | ||||||
|             ) |             ) | ||||||
|             raise ConnectionRefusedError |             raise  | ||||||
|         return user_pool |         return user_pool | ||||||
| 
 | 
 | ||||||
|     def _get_vm_pool(self): |     def _get_vm_pool(self): | ||||||
|  | @ -171,7 +260,6 @@ class OpenNebulaManager(): | ||||||
|                                   <DEV_PREFIX>vd</DEV_PREFIX> |                                   <DEV_PREFIX>vd</DEV_PREFIX> | ||||||
|                                   <IMAGE_ID>{image_id}</IMAGE_ID> |                                   <IMAGE_ID>{image_id}</IMAGE_ID> | ||||||
|                            </DISK> |                            </DISK> | ||||||
|                           </TEMPLATE> |  | ||||||
|                         """.format(size=1024 * int(specs['disk_size']), |                         """.format(size=1024 * int(specs['disk_size']), | ||||||
|                                    image_id=image_id) |                                    image_id=image_id) | ||||||
| 
 | 
 | ||||||
|  | @ -193,10 +281,18 @@ class OpenNebulaManager(): | ||||||
|                                   <IMAGE>{image}</IMAGE> |                                   <IMAGE>{image}</IMAGE> | ||||||
|                                   <IMAGE_UNAME>{image_uname}</IMAGE_UNAME> |                                   <IMAGE_UNAME>{image_uname}</IMAGE_UNAME> | ||||||
|                            </DISK> |                            </DISK> | ||||||
|                           </TEMPLATE> |  | ||||||
|                         """.format(size=1024 * int(specs['disk_size']), |                         """.format(size=1024 * int(specs['disk_size']), | ||||||
|                                    image=image, |                                    image=image, | ||||||
|                                    image_uname=image_uname) |                                    image_uname=image_uname) | ||||||
|  |                          | ||||||
|  |                                  | ||||||
|  |         if ssh_key: | ||||||
|  |             vm_specs += """<CONTEXT> | ||||||
|  |                     <SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY> | ||||||
|  |                     <NETWORK>YES</NETWORK> | ||||||
|  |                    </CONTEXT> | ||||||
|  |                               </TEMPLATE> | ||||||
|  |                 """.format(ssh=public_key) | ||||||
|         vm_id = self.client.call(oca.VmTemplate.METHODS['instantiate'], |         vm_id = self.client.call(oca.VmTemplate.METHODS['instantiate'], | ||||||
|                                  template.id, |                                  template.id, | ||||||
|                                  '', |                                  '', | ||||||
|  | @ -204,25 +300,6 @@ class OpenNebulaManager(): | ||||||
|                                  vm_specs, |                                  vm_specs, | ||||||
|                                  False) |                                  False) | ||||||
| 
 | 
 | ||||||
|         self.oneadmin_client.call( |  | ||||||
|             'vm.update', |  | ||||||
|             vm_id, |  | ||||||
|             """<CONTEXT> |  | ||||||
|                 <SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY> |  | ||||||
|                </CONTEXT> |  | ||||||
|             """.format(ssh=ssh_key) |  | ||||||
|         ) |  | ||||||
|         try: |  | ||||||
|             self.oneadmin_client.call( |  | ||||||
|                 oca.VirtualMachine.METHODS['chown'], |  | ||||||
|                 vm_id, |  | ||||||
|                 self.opennebula_user.id, |  | ||||||
|                 self.opennebula_user.group_ids[0] |  | ||||||
|             ) |  | ||||||
|         except AttributeError: |  | ||||||
|             logger.info( |  | ||||||
|                 'Could not change owner for vm with id: {}.'.format(vm_id)) |  | ||||||
| 
 |  | ||||||
|         self.oneadmin_client.call( |         self.oneadmin_client.call( | ||||||
|             oca.VirtualMachine.METHODS['action'], |             oca.VirtualMachine.METHODS['action'], | ||||||
|             'release', |             'release', | ||||||
|  | @ -350,3 +427,85 @@ class OpenNebulaManager(): | ||||||
|             self.opennebula_user.id, |             self.opennebula_user.id, | ||||||
|             new_password |             new_password | ||||||
|         ) |         ) | ||||||
|  | 
 | ||||||
|  |     def add_public_key(self, user, public_key='', merge=False): | ||||||
|  |         """  | ||||||
|  | 
 | ||||||
|  |         Args:  | ||||||
|  |             user (CustomUser): Dynamicweb user  | ||||||
|  |             public_key (string): Public key to add to the user | ||||||
|  |             merge (bool): Optional if True the new public key replaces the old | ||||||
|  | 
 | ||||||
|  |         Raises: | ||||||
|  |             KeyExistsError: If replace is False and the user already has a | ||||||
|  |                 public key  | ||||||
|  |             WrongNameError: If no openebula user with this credentials exists | ||||||
|  |             ConnectionError: If the connection to the opennebula server can't be | ||||||
|  |                 established  | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             True if public_key was added | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|  |         # TODO: Check if we can remove this first try because we basically just | ||||||
|  |         # raise the possible Errors  | ||||||
|  |         try: | ||||||
|  |             open_user = self._get_user(user) | ||||||
|  |             try: | ||||||
|  |                 old_key = open_user.template.ssh_public_key  | ||||||
|  |                 if not merge: | ||||||
|  |                     raise KeyExistsError() | ||||||
|  |                 public_key += '\n{key}'.format(key=old_key) | ||||||
|  | 
 | ||||||
|  |             except AttributeError: | ||||||
|  |                 pass | ||||||
|  |             self.oneadmin_client.call('user.update', open_user.id, | ||||||
|  |                          '<CONTEXT><SSH_PUBLIC_KEY>{key}</SSH_PUBLIC_KEY></CONTEXT>'.format(key=public_key)) | ||||||
|  |             return True | ||||||
|  |         except WrongNameError: | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  |         except ConnectionError: | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  |     def remove_public_key(self, user, public_key=''): | ||||||
|  |         """  | ||||||
|  | 
 | ||||||
|  |         Args:  | ||||||
|  |             user (CustomUser): Dynamicweb user  | ||||||
|  |             public_key (string): Public key to be removed to the user | ||||||
|  | 
 | ||||||
|  |         Raises: | ||||||
|  |             KeyDoesNotExistsError: If replace is False and the user already has a | ||||||
|  |                 public key  | ||||||
|  |             WrongNameError: If no openebula user with this credentials exists | ||||||
|  |             ConnectionError: If the connection to the opennebula server can't be | ||||||
|  |                 established  | ||||||
|  | 
 | ||||||
|  |         Returns: | ||||||
|  |             True if public_key was removed | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         try: | ||||||
|  |             open_user = self._get_user(user) | ||||||
|  |             try: | ||||||
|  |                 old_key = open_user.template.ssh_public_key  | ||||||
|  |                 if public_key not in old_key: | ||||||
|  |                     raise KeyDoesNotExistsError() | ||||||
|  |                 if '\n{}'.format(public_key) in old_key: | ||||||
|  |                     public_key = old_key.replace('\n{}'.format(public_key), '') | ||||||
|  |                 else:  | ||||||
|  |                     public_key = old_key.replace(public_key, '') | ||||||
|  | 
 | ||||||
|  |             except AttributeError: | ||||||
|  |                 raise KeyDoesNotExistsError() | ||||||
|  |                  | ||||||
|  |             self.oneadmin_client.call('user.update', open_user.id, | ||||||
|  |                          '<CONTEXT><SSH_PUBLIC_KEY>{key}</SSH_PUBLIC_KEY></CONTEXT>'.format(key=public_key)) | ||||||
|  |             return True | ||||||
|  |         except WrongNameError: | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  |         except ConnectionError: | ||||||
|  |             raise | ||||||
|  |  | ||||||
|  | @ -11,36 +11,10 @@ from .models import OpenNebulaManager | ||||||
| class VirtualMachineTemplateSerializer(serializers.Serializer): | class VirtualMachineTemplateSerializer(serializers.Serializer): | ||||||
|     """Serializer to map the virtual machine template instance into JSON format.""" |     """Serializer to map the virtual machine template instance into JSON format.""" | ||||||
|     id          = serializers.IntegerField(read_only=True) |     id          = serializers.IntegerField(read_only=True) | ||||||
|     set_name    = serializers.CharField(read_only=True, label='Name') |  | ||||||
|     name        = serializers.SerializerMethodField() |     name        = serializers.SerializerMethodField() | ||||||
|     cores       = serializers.SerializerMethodField()  |     cores       = serializers.SerializerMethodField()  | ||||||
|     disk        = serializers.IntegerField(write_only=True) |  | ||||||
|     disk_size   = serializers.SerializerMethodField() |     disk_size   = serializers.SerializerMethodField() | ||||||
|     set_memory      = serializers.IntegerField(write_only=True, label='Memory') |  | ||||||
|     memory      = serializers.SerializerMethodField() |     memory      = serializers.SerializerMethodField() | ||||||
|     price       = serializers.SerializerMethodField() |  | ||||||
| 
 |  | ||||||
|     def create(self, validated_data): |  | ||||||
|         data = validated_data |  | ||||||
|         template = data.pop('template') |  | ||||||
| 
 |  | ||||||
|         cores = template.pop('vcpu') |  | ||||||
|         name    = data.pop('name') |  | ||||||
|         disk_size = data.pop('disk')  |  | ||||||
|         memory  = template.pop('memory') |  | ||||||
|         manager = OpenNebulaManager() |  | ||||||
|          |  | ||||||
|         try: |  | ||||||
|             opennebula_id = manager.create_template(name=name, cores=cores, |  | ||||||
|                                                     memory=memory, |  | ||||||
|                                                     disk_size=disk_size, |  | ||||||
|                                                     core_price=core_price, |  | ||||||
|                                                     disk_size_price=disk_size_price, |  | ||||||
|                                                     memory_price=memory_price) |  | ||||||
|         except OpenNebulaException as err: |  | ||||||
|             raise serializers.ValidationError("OpenNebulaException occured. {0}".format(err)) |  | ||||||
|          |  | ||||||
|         return manager.get_template(template_id=opennebula_id) |  | ||||||
| 
 | 
 | ||||||
|     def get_cores(self, obj): |     def get_cores(self, obj): | ||||||
|         if hasattr(obj.template, 'vcpu'): |         if hasattr(obj.template, 'vcpu'): | ||||||
|  | @ -58,24 +32,14 @@ class VirtualMachineTemplateSerializer(serializers.Serializer): | ||||||
|         except: |         except: | ||||||
|             return 0 |             return 0 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_price(self, obj): |  | ||||||
|         template = obj.template |  | ||||||
|         price = float(template.cpu) * 5.0 |  | ||||||
|         price += (int(template.memory)/1024 * 2.0) |  | ||||||
|         try: |  | ||||||
|             for disk in template.disks: |  | ||||||
|                 price += int(disk.size)/1024 * 0.6 |  | ||||||
|         except: |  | ||||||
|             pass |  | ||||||
|         return price |  | ||||||
| 
 |  | ||||||
|     def get_memory(self, obj): |     def get_memory(self, obj): | ||||||
|         return int(obj.template.memory)/1024 |         return int(obj.template.memory)/1024 | ||||||
| 
 | 
 | ||||||
|     def get_name(self, obj): |     def get_name(self, obj): | ||||||
|         return obj.name.strip('public-') |         return obj.name.strip('public-') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class VirtualMachineSerializer(serializers.Serializer): | class VirtualMachineSerializer(serializers.Serializer): | ||||||
|     """Serializer to map the virtual machine instance into JSON format.""" |     """Serializer to map the virtual machine instance into JSON format.""" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,35 +1,54 @@ | ||||||
|  | import socket | ||||||
|  | import random | ||||||
|  | import string | ||||||
|  | 
 | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from .models import VirtualMachine, VirtualMachineTemplate, OpenNebulaManager | 
 | ||||||
|  | from .models import OpenNebulaManager | ||||||
|  | from .serializers import VirtualMachineSerializer | ||||||
|  | from utils.models import CustomUser | ||||||
| 
 | 
 | ||||||
| class OpenNebulaManagerTestCases(TestCase): | class OpenNebulaManagerTestCases(TestCase): | ||||||
|     """This class defines the test suite for the opennebula manager model.""" |     """This class defines the test suite for the opennebula manager model.""" | ||||||
| 
 | 
 | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         """Define the test client and other test variables.""" |         """Define the test client and other test variables.""" | ||||||
|         self.cores = 1  |  | ||||||
|         self.memory = 1 |  | ||||||
|         self.disk_size = 10.0 |  | ||||||
|          |  | ||||||
|         self.email = 'test@test.com' |  | ||||||
|         self.password = 'testtest' |  | ||||||
| 
 |  | ||||||
|         self.manager = OpenNebulaManager(email=None, password=None, create_user=False)  |  | ||||||
|          |          | ||||||
| 
 | 
 | ||||||
|     def test_model_can_connect_to_server(self): |         self.email = '{}@ungleich.ch'.format(''.join(random.choices(string.ascii_uppercase, k=10))) | ||||||
|         """Test the opennebula manager model can connect to a server.""" |         self.password = ''.join(random.choices(string.ascii_uppercase + string.digits, k=20))  | ||||||
|  | 
 | ||||||
|  |         self.user = CustomUser.objects.create(name='test', email=self.email, | ||||||
|  |                 password=self.password)  | ||||||
|  | 
 | ||||||
|  |         self.vm_specs = {} | ||||||
|  |         self.vm_specs['cpu'] = 1 | ||||||
|  |         self.vm_specs['memory'] = 2 | ||||||
|  |         self.vm_specs['disk_size'] = 10 | ||||||
|  | 
 | ||||||
|  |         self.manager = OpenNebulaManager()  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def test_connect_to_server(self): | ||||||
|  |         """Test the opennebula manager can connect to a server.""" | ||||||
|         try: |         try: | ||||||
|             user_pool = self.manager._get_user_pool() |             ver = self.manager.oneadmin_client.version() | ||||||
|         except:  |         except:  | ||||||
|             user_pool = None |             ver = None | ||||||
|         self.assertFalse(user_pool is None) |         self.assertTrue(ver is not None) | ||||||
| 
 | 
 | ||||||
|     def test_model_can_create_user(self): |     def test_get_user(self): | ||||||
|         """Test the opennebula manager model can create a new user.""" |         """Test the opennebula manager can get a existing user.""" | ||||||
|  |         self.manager.create_user(self.user) | ||||||
|  |         user = self.manager._get_user(self.user) | ||||||
|  |         name = user.name | ||||||
|  |         self.assertNotEqual(name, None) | ||||||
|  | 
 | ||||||
|  |     def test_create_and_delete_user(self): | ||||||
|  |         """Test the opennebula manager can create and delete a new user.""" | ||||||
|         old_count = len(self.manager._get_user_pool()) |         old_count = len(self.manager._get_user_pool()) | ||||||
|         self.manager = OpenNebulaManager(email=self.email, |         self.manager = OpenNebulaManager(email=self.email, | ||||||
|                                          password=self.password, |                                          password=self.password) | ||||||
|                                          create_user=True) |  | ||||||
|         user_pool = self.manager._get_user_pool() |         user_pool = self.manager._get_user_pool() | ||||||
|         new_count = len(user_pool) |         new_count = len(user_pool) | ||||||
|         # Remove the user afterwards |         # Remove the user afterwards | ||||||
|  | @ -38,105 +57,85 @@ class OpenNebulaManagerTestCases(TestCase): | ||||||
|          |          | ||||||
|         self.assertNotEqual(old_count, new_count) |         self.assertNotEqual(old_count, new_count) | ||||||
| 
 | 
 | ||||||
|  |     def test_user_can_login(self): | ||||||
|  |         """ Test the manager can login to a new created user""" | ||||||
|  |         self.manager.create_user(self.user) | ||||||
|  |         user = self.manager._get_user(self.user) | ||||||
|  |         client = self.manager._get_client(self.user) | ||||||
|  |         version = client.version() | ||||||
| 
 | 
 | ||||||
| class VirtualMachineTemplateTestCase(TestCase): |         # Cleanup  | ||||||
|     """This class defines the test suite for the virtualmachine template model.""" |         user.delete() | ||||||
|  |         self.assertNotEqual(version, None) | ||||||
| 
 | 
 | ||||||
|  |     def test_add_public_key_to_user(self): | ||||||
|  |         """ Test the manager can add a new public key to an user """ | ||||||
|  |         self.manager.create_user(self.user) | ||||||
|  |         user = self.manager._get_user(self.user) | ||||||
|  |         public_key = 'test' | ||||||
|  |         self.manager.add_public_key(self.user, public_key) | ||||||
|  |         # Fetch new user information from opennebula | ||||||
|  |         user.info() | ||||||
|  |         user_public_key = user.template.ssh_public_key | ||||||
|  |         # Cleanup  | ||||||
|  |         user.delete() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(user_public_key, public_key) | ||||||
|  |          | ||||||
|  |     def test_append_public_key_to_user(self): | ||||||
|  |         """ Test the manager can append a new public key to an user """ | ||||||
|  |         self.manager.create_user(self.user) | ||||||
|  |         user = self.manager._get_user(self.user) | ||||||
|  |         public_key = 'test' | ||||||
|  |         self.manager.add_public_key(self.user, public_key) | ||||||
|  |         # Fetch new user information from opennebula | ||||||
|  |         user.info() | ||||||
|  |         old_public_key = user.template.ssh_public_key | ||||||
|  |         self.manager.add_public_key(self.user, public_key, merge=True) | ||||||
|  |         user.info() | ||||||
|  |         new_public_key = user.template.ssh_public_key | ||||||
|  |         # Cleanup  | ||||||
|  |         user.delete() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(new_public_key, '{}\n{}'.format(old_public_key, | ||||||
|  |             public_key)) | ||||||
|  | 
 | ||||||
|  |     def test_remove_public_key_to_user(self): | ||||||
|  |         """ Test the manager can remove a public key from an user """ | ||||||
|  |         self.manager.create_user(self.user) | ||||||
|  |         user = self.manager._get_user(self.user) | ||||||
|  |         public_key = 'test' | ||||||
|  |         self.manager.add_public_key(self.user, public_key) | ||||||
|  |         self.manager.add_public_key(self.user, public_key, merge=True) | ||||||
|  |         user.info() | ||||||
|  |         old_public_key = user.template.ssh_public_key | ||||||
|  |         self.manager.remove_public_key(self.user, public_key) | ||||||
|  |         user.info() | ||||||
|  |         new_public_key = user.template.ssh_public_key | ||||||
|  |         # Cleanup  | ||||||
|  |         user.delete() | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(new_public_key, | ||||||
|  |                 old_public_key.replace('{}\n'.format(public_key), '', 1)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def test_requires_ssh_key_for_new_vm(self): | ||||||
|  |         """Test the opennebula manager requires the user to have a ssh key when | ||||||
|  |         creating a new vm""" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class VirtualMachineSerializerTestCase(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         """Define the test client and other test variables.""" |         """Define the test client and other test variables.""" | ||||||
|         self.template_name = "Standard" |         self.manager = OpenNebulaManager(email=None, password=None) | ||||||
|         self.base_price = 0.0 |  | ||||||
|         self.core_price = 5.0 |  | ||||||
|         self.memory_price = 2.0 |  | ||||||
|         self.disk_size_price = 0.6 |  | ||||||
|                                             |                                             | ||||||
|         self.cores = 1  |  | ||||||
|         self.memory = 1 |  | ||||||
|         self.disk_size = 10.0 |  | ||||||
| 
 |  | ||||||
|         self.manager = OpenNebulaManager(email=None, password=None, create_user=False) |  | ||||||
|         self.opennebula_id = self.manager.create_template(name=self.template_name, |  | ||||||
|                                                           cores=self.cores, |  | ||||||
|                                                           memory=self.memory, |  | ||||||
|                                                           disk_size=self.disk_size) |  | ||||||
| 
 |  | ||||||
|         self.template = VirtualMachineTemplate(opennebula_id=self.opennebula_id, |  | ||||||
|                                                base_price=self.base_price, |  | ||||||
|                                                memory_price=self.memory_price, |  | ||||||
|                                                core_price=self.core_price, |  | ||||||
|                                                disk_size_price=self.disk_size_price) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def test_model_can_create_a_virtualmachine_template(self): |  | ||||||
|         """Test the virtualmachine template model can create a template.""" |  | ||||||
|         old_count = VirtualMachineTemplate.objects.count() |  | ||||||
|         self.template.save() |  | ||||||
|         new_count = VirtualMachineTemplate.objects.count() |  | ||||||
|         # Remove the template afterwards |  | ||||||
|         template = self.manager._get_template(self.template.opennebula_id) |  | ||||||
|         template.delete() |  | ||||||
|         self.assertNotEqual(old_count, new_count) |  | ||||||
| 
 |  | ||||||
|     def test_model_can_calculate_price(self): |  | ||||||
|         price = self.cores * self.core_price |  | ||||||
|         price += self.memory * self.memory_price |  | ||||||
|         price += self.disk_size * self.disk_size_price  |  | ||||||
|         self.assertEqual(price, self.template.calculate_price()) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VirtualMachineTestCase(TestCase): |  | ||||||
|     def setUp(self): |  | ||||||
|         """Define the test client and other test variables.""" |  | ||||||
|         self.template_name = "Standard" |  | ||||||
|         self.base_price = 0.0 |  | ||||||
|         self.core_price = 5.0 |  | ||||||
|         self.memory_price = 2.0 |  | ||||||
|         self.disk_size_price = 0.6 |  | ||||||
| 
 |  | ||||||
|         self.cores = 1  |  | ||||||
|         self.memory = 1 |  | ||||||
|         self.disk_size = 10.0 |  | ||||||
|         self.manager = OpenNebulaManager(email=None, password=None, create_user=False) |  | ||||||
|         self.opennebula_id = self.manager.create_template(name=self.template_name, |  | ||||||
|                                                           cores=self.cores, |  | ||||||
|                                                           memory=self.memory, |  | ||||||
|                                                           disk_size=self.disk_size) |  | ||||||
| 
 |  | ||||||
|         self.template = VirtualMachineTemplate(opennebula_id=self.opennebula_id, |  | ||||||
|                                                base_price=self.base_price, |  | ||||||
|                                                memory_price=self.memory_price, |  | ||||||
|                                                core_price=self.core_price, |  | ||||||
|                                                disk_size_price=self.disk_size_price) |  | ||||||
|         self.template_id = self.template.opennebula_id() |  | ||||||
|         self.opennebula_id = self.manager.create_virtualmachine(template_id=self.template_id) |  | ||||||
|                                             |  | ||||||
|         self.virtualmachine = VirtualMachine(opennebula_id=self.opennebula_id, |  | ||||||
|                                              template=self.template) |  | ||||||
| 
 | 
 | ||||||
|     def test_serializer_strips_of_public(self): |     def test_serializer_strips_of_public(self): | ||||||
|         """ Test the serialized object contains no 'public-'."""  |         """ Test the serialized virtual machine object contains no 'public-'."""  | ||||||
| 
 | 
 | ||||||
|         template = self.manager.get_templates().first() |         for vm in self.manager.get_vms(): | ||||||
|         serialized = VirtualMachineTemplateSerializer(template) |             serialized = VirtualMachineSerializer(vm) | ||||||
|         self.assertEqual(serialized.data.name, template.name.strip('public-')) |             self.assertEqual(serialized.data.get('name'), vm.name.strip('public-')) | ||||||
|  |             break | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|          |  | ||||||
|     def test_model_can_create_a_virtualmachine(self): |  | ||||||
|         """Test the virtualmachine model can create a virtualmachine.""" |  | ||||||
|         old_count = VirtualMachine.objects.count() |  | ||||||
|         self.virtualmachine.save() |  | ||||||
|         new_count = VirtualMachine.objects.count() |  | ||||||
|         self.assertNotEqual(old_count, new_count) |  | ||||||
| 
 |  | ||||||
|     def test_model_can_create_a_virtualmachine_for_user(self): |  | ||||||
|         pass |  | ||||||
| 
 |  | ||||||
|     def test_model_can_delete_a_virtualmachine(self): |  | ||||||
|         """Test the virtualmachine model can delete a virtualmachine.""" |  | ||||||
|         self.virtualmachine.save() |  | ||||||
|         old_count = VirtualMachine.objects.count() |  | ||||||
|         VirtualMachine.objects.first().delete() |  | ||||||
|         new_count = VirtualMachine.objects.count() |  | ||||||
|         self.assertNotEqual(old_count, new_count) |  | ||||||
|  |  | ||||||
|  | @ -1,15 +1,10 @@ | ||||||
| from django.conf.urls import url, include | from django.conf.urls import url, include | ||||||
| from rest_framework.urlpatterns import format_suffix_patterns | from rest_framework.urlpatterns import format_suffix_patterns | ||||||
| from .views import TemplateCreateView, TemplateDetailsView,\ | from .views import VmCreateView, VmDetailsView | ||||||
|                    VmCreateView, VmDetailsView |  | ||||||
| 
 | 
 | ||||||
| urlpatterns = { | urlpatterns = { | ||||||
|     url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), |     url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), | ||||||
| 
 | 
 | ||||||
|     url(r'^templates/$', TemplateCreateView.as_view(), name="template_create"), |  | ||||||
|     url(r'^templates/(?P<pk>[0-9]+)/$', TemplateDetailsView.as_view(), |  | ||||||
|         name="templates_details"), |  | ||||||
|      |  | ||||||
|     url(r'^vms/$', VmCreateView.as_view(), name="vm_create"), |     url(r'^vms/$', VmCreateView.as_view(), name="vm_create"), | ||||||
|     url(r'^vms/(?P<pk>[0-9]+)/$', VmDetailsView.as_view(), |     url(r'^vms/(?P<pk>[0-9]+)/$', VmDetailsView.as_view(), | ||||||
|         name="vm_details"), |         name="vm_details"), | ||||||
|  |  | ||||||
|  | @ -20,38 +20,6 @@ class ServiceUnavailable(APIException): | ||||||
|     default_code = 'service_unavailable' |     default_code = 'service_unavailable' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TemplateCreateView(generics.ListCreateAPIView): |  | ||||||
|     """This class handles the GET and POST requests.""" |  | ||||||
| 
 |  | ||||||
|     serializer_class = VirtualMachineTemplateSerializer |  | ||||||
|     permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser) |  | ||||||
| 
 |  | ||||||
|     def get_queryset(self): |  | ||||||
|         manager = OpenNebulaManager() |  | ||||||
|         return manager.get_templates() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def perform_create(self, serializer): |  | ||||||
|         """Save the post data when creating a new template.""" |  | ||||||
|         serializer.save() |  | ||||||
| 
 |  | ||||||
| class TemplateDetailsView(generics.RetrieveUpdateDestroyAPIView): |  | ||||||
|     """This class handles the http GET, PUT and DELETE requests.""" |  | ||||||
| 
 |  | ||||||
|     serializer_class = VirtualMachineTemplateSerializer |  | ||||||
|     permission_classes = (permissions.IsAuthenticated) |  | ||||||
| 
 |  | ||||||
|     def get_queryset(self): |  | ||||||
|         manager = OpenNebulaManager() |  | ||||||
|         # We may have ConnectionRefusedError if we don't have a  |  | ||||||
|         # connection to OpenNebula. For now, we raise ServiceUnavailable |  | ||||||
|         try: |  | ||||||
|             templates = manager.get_templates() |  | ||||||
|         except ConnectionRefusedError: |  | ||||||
|             raise ServiceUnavailable             |  | ||||||
|          |  | ||||||
|         return templates |  | ||||||
| 
 |  | ||||||
| class VmCreateView(generics.ListCreateAPIView): | class VmCreateView(generics.ListCreateAPIView): | ||||||
|     """This class handles the GET and POST requests.""" |     """This class handles the GET and POST requests.""" | ||||||
|     serializer_class = VirtualMachineSerializer |     serializer_class = VirtualMachineSerializer | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue