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…
Reference in a new issue