Merge branch 'opennebula-integration' into vm_bill

This commit is contained in:
Modulos 2017-05-07 06:29:29 +02:00
commit d146c25899
31 changed files with 889 additions and 326 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,42 @@
{% load i18n %}
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="exampleModalLabel">{% trans 'New message'%}</h4>
</div>
<form novalidate method="post" action="{% url 'alplora:contact'%}" id="contact_form">
<div class="modal-body">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="form-group text-left">
<label for="recipient-name" class="control-label ">{% trans 'Name:'%}</label>
<input type="text" class="form-control" {%if form.name.errors%}
style="border-color: red" {%endif%} name="name" placeholder="{% trans 'What is your name ?'%}" id="recipient-name" required>
{{ form.name.errors|striptags }}
</div>
<div class="form-group text-left">
<label for="recipient-name" class="control-label ">{% trans 'From:'%}</label>
<input type="text" class="form-control" {%if form.email.errors%}
style="border-color: red" {%endif%}name="email" placeholder="{% trans 'You email'%}" id="recipient-name" required>
{{ form.email.errors|striptags}}
</div>
<div class="form-group text-left">
<label for="message-text" class="control-label ">{% trans 'Message:'%}</label>
<textarea class="form-control" {%if form.message.errors %} style =
"border-color: red" {%endif%} name="message" placeholder="{% trans 'Leave us your message'%}" id="message-text" required></textarea>
{{ form.message.errors|striptags}}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close'%}</button>
<button type="submit" class="btn btn-warning">{% trans 'Send message'%}</button>
</div>
</form>
</div>
</div>
<script>
$('#contact_form').ajaxForm({
target: '#modal', success: function(response) { }
});
</script>

View file

@ -0,0 +1,21 @@
{% load i18n %}
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{% trans "Message Sent" %}</h4>
</div>
<div class="modal-body">
<p>{% trans "Thank you, we will contact you as soon as possible" %}</p>
</div>
<div class="modal-footer text-center">
<button type="submit" class="btn btn-primary" data-dismiss="modal">Ok</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
<script>
// close the modal after 3 seconds
setTimeout(function() {
$('#modal').modal('hide');
}, 3000);
</script>

View file

@ -53,7 +53,6 @@
</script> </script>
<div class="page-loader"> <div class="page-loader">
<div class="loader"> <div class="loader">
<div class="circle circle-1"></div> <div class="circle circle-1"></div>
@ -419,7 +418,9 @@
<h1>{% trans 'How do I get Alplora?'%}</h1> <h1>{% trans 'How do I get Alplora?'%}</h1>
<h3>{% trans 'Click the button below and leave us your contact.'%}<p></p>{% trans 'Team Alplora will contact you and visit you with a tracking device.'%}</h3> <h3>{% trans 'Click the button below and leave us your contact.'%}<p></p>{% trans 'Team Alplora will contact you and visit you with a tracking device.'%}</h3>
<hr class="intro-divider"> <hr class="intro-divider">
<a href="#howitworks" class="btn btn-default btn-lg"><i class="#Services"></i> <span class="network-name" data-toggle="modal" data-target="#exampleModal" >{% trans 'Contact'%}</span></a> <a href="{% url 'alplora:contact' %}" data-toggle="modal" data-target="#modal" class="btn btn-default
btn-lg"><i class="#Services"></i> <span
class="network-name" >{% trans 'Contact'%}</span></a>
</ul> </ul>
</div> </div>
@ -429,60 +430,9 @@
</div> </div>
<!-- CONTACT FORM MODAL --> <!-- CONTACT FORM MODAL -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" style="color:black;"> <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" style="color:black;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="exampleModalLabel">{% trans 'New message'%}</h4>
</div>
<form method="POST" action="">
<div class="modal-body">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="form-group text-left">
<label for="recipient-name" class="control-label ">{% trans 'Name:'%}</label>
<input type="text" class="form-control" name="name" placeholder="{% trans 'What is your name ?'%}" id="recipient-name" required>
</div>
<div class="form-group text-left">
<label for="recipient-name" class="control-label ">{% trans 'From:'%}</label>
<input type="text" class="form-control" name="email" placeholder="{% trans 'You email'%}" id="recipient-name" required>
{{ form.email.errors|striptags}}
</div>
<div class="form-group text-left">
<label for="message-text" class="control-label ">{% trans 'Message:'%}</label>
<textarea class="form-control" name="message" placeholder="{% trans 'Leave us your message'%}" id="message-text" required></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans 'Close'%}</button>
<button type="submit" class="btn btn-warning">{% trans 'Send message'%}</button>
</div>
</form>
</div>
</div>
</div> </div>
<!-- SUCCESS MODAL MESSAGE -->
<div class="modal fade bs-example-modal-sm" style="color:black;" id="request-success-message" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{% trans "Message Sent" %}</h4>
</div>
<div class="modal-body">
<p>{% trans "Thank you, we will contact you as soon as possible" %}</p>
</div>
<div class="modal-footer text-center">
<button type="submit" class="btn btn-primary" data-dismiss="modal">Ok</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- /.container --> <!-- /.container -->
</div> </div>
@ -560,6 +510,7 @@
<script src="{% static 'alplora/js/main.js' %}"></script> <script src="{% static 'alplora/js/main.js' %}"></script>
<script src="{% static 'alplora/js/form.js' %}"></script>
<link rel="stylesheet" href="/static/debug_toolbar/css/print.css" type="text/css" media="print"> <link rel="stylesheet" href="/static/debug_toolbar/css/print.css" type="text/css" media="print">
<link rel="stylesheet" href="/static/debug_toolbar/css/toolbar.css" type="text/css"> <link rel="stylesheet" href="/static/debug_toolbar/css/toolbar.css" type="text/css">
@ -572,14 +523,15 @@
<script src="/static/debug_toolbar/js/toolbar.js"></script> <script src="/static/debug_toolbar/js/toolbar.js"></script>
<script type="text/javascript"> <script type="text/javascript">
window.onload=function(){ $('#modal').on('show.bs.modal', function (event) {
var hash = window.location.hash.substr(1); var modal = $(this)
if (hash == 'requestformsuccess'){ $.ajax({
$('#request-success-message').modal('show'); url: "{% url 'alplora:contact' %}",
} context: document.body
}).done(function(response) {
}; modal.html(response);
});
});
</script> </script>
</body> </body>
</html> </html>

View file

@ -1,11 +1,12 @@
from django.conf.urls import url from django.conf.urls import url
from .views import IndexView, LoginView from .views import IndexView, LoginView, ContactView
urlpatterns = [ urlpatterns = [
url(r'^/?$', IndexView.as_view(), name='index'), url(r'^/?$', IndexView.as_view(), name='index'),
url(r'/login/', LoginView.as_view(), name='login'), url(r'/login/', LoginView.as_view(), name='login'),
url(r'/contact', ContactView.as_view(), name='contact'),
# url(r'^/beta-program/?$', BetaProgramView.as_view(), name='beta'), # url(r'^/beta-program/?$', BetaProgramView.as_view(), name='beta'),
# url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'), # url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'),
] ]

View file

@ -5,13 +5,12 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
from django.shortcuts import render
from utils.forms import ContactUsForm from utils.forms import ContactUsForm
class IndexView(FormView): class IndexView(TemplateView):
template_name = "alplora/index.html" template_name = "alplora/index.html"
form_class = ContactUsForm
success_message = _('Message Successfully Sent')
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs) context = super(IndexView, self).get_context_data(**kwargs)
@ -19,17 +18,22 @@ class IndexView(FormView):
context.update(languages) context.update(languages)
return context return context
def get_success_url(self): class ContactView(FormView):
success_url = reverse('alplora:index') template_name = 'alplora/contact.html'
success_url += "#requestformsuccess" form_class = ContactUsForm
return success_url success_message = _('Message Successfully Sent')
def get_context_data(self, *args, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
languages = getlanguages()
context.update(languages)
return context
def form_valid(self, form): def form_valid(self, form):
form.save() form.save()
form.send_email(email_to='info@alplora.ch') form.send_email(email_to='info@alplora.ch')
messages.add_message(self.request, messages.SUCCESS, self.success_message) messages.add_message(self.request, messages.SUCCESS, self.success_message)
return super(IndexView, self).form_valid(form) return render(self.request, 'alplora/contact_success.html', {})
class LoginView(TemplateView): class LoginView(TemplateView):
template_name = "alplora/login.html" template_name = "alplora/login.html"

View file

@ -17,21 +17,10 @@
{% render_model post "abstract" "" "" 'truncatewords_html:10' %} {% render_model post "abstract" "" "" 'truncatewords_html:10' %}
</h2> </h2>
<span class="meta"> <span class="meta">
Posted Posted on {{ post.date_published|date:"DATE_FORMAT" }}
{% if post.author %}
by
<a href="{% url 'djangocms_blog:posts-author' post.author.get_username %}">
{% if post.author.get_full_name %}
{{ post.author.get_full_name }}
{% else %}
{{ post.author }}
{% endif %}
</a>
{% endif %}
on {{ post.date_published|date:"DATE_FORMAT" }}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</header> </header>

View file

@ -16,18 +16,7 @@
</h2> </h2>
</a> </a>
<p class="post-meta" style="font-size:0.9em;"> <p class="post-meta" style="font-size:0.9em;">
Posted Posted on {{ post.date_published|date:"DATE_FORMAT" }}
{% if post.author %}
by
<!-- <a href="{% url 'djangocms_blog:posts-author' post.author.get_username %}"> -->
{% if post.author.get_full_name %}
{{ post.author.get_full_name }}
{% else %}
{{ post.author }}
{% endif %}
<!-- </a> -->
{% endif %}
on {{ post.date_published|date:"DATE_FORMAT" }}
</p> </p>
<p class="post-subtitle"> <p class="post-subtitle">

View file

@ -1,10 +1,13 @@
import random
import string
from django import forms 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 utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from .models import HostingOrder, VirtualMachinePlan from .models import HostingOrder, VirtualMachinePlan, UserHostingKey
class HostingOrderAdminForm(forms.ModelForm): class HostingOrderAdminForm(forms.ModelForm):
@ -83,3 +86,40 @@ class HostingUserSignupForm(forms.ModelForm):
if not confirm_password == password: if not confirm_password == password:
raise forms.ValidationError("Passwords don't match") raise forms.ValidationError("Passwords don't match")
return confirm_password return confirm_password
class UserHostingKeyForm(forms.ModelForm):
private_key = forms.CharField(widget=forms.PasswordInput(), required=False)
public_key = forms.CharField(widget=forms.PasswordInput(), required=False)
user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(), required=False)
name = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(UserHostingKeyForm, self).__init__(*args, **kwargs)
# self.initial['user'].initial = self.request.user.id
# print(self.fields)
def clean_name(self):
return ''.join(random.choice(string.ascii_lowercase) for i in range(7))
def clean_user(self):
return self.request.user
def clean(self):
cleaned_data = self.cleaned_data
print(cleaned_data)
if not cleaned_data.get('public_key'):
private_key, public_key = UserHostingKey.generate_keys()
cleaned_data.update({
'private_key': private_key,
'public_key': public_key
})
return cleaned_data
class Meta:
model = UserHostingKey
fields = ['user', 'public_key', 'name']

View file

@ -7,51 +7,100 @@ class Command(BaseCommand):
def get_data(self): def get_data(self):
return [
{
'base_price': 10,
'core_price': 5,
'memory_price': 2,
'disk_size_price': 0.6,
'cores': 1,
'memory': 2,
'disk_size': 10
},
{
'base_price': 10,
'core_price': 5,
'memory_price': 2,
'disk_size_price': 0.6,
'cores': 1,
'memory': 2,
'disk_size': 100
},
{
'base_price': 10,
'core_price': 5,
'memory_price': 2,
'disk_size_price': 0.6,
'cores': 2,
'memory': 4,
'disk_size': 20
},
{
'base_price': 10,
'core_price': 5,
'memory_price': 2,
'disk_size_price': 0.6,
'cores': 4,
'memory': 8,
'disk_size': 40
},
{
'base_price': 10,
'core_price': 5,
'memory_price': 2,
'disk_size_price': 0.6,
'cores': 16,
'memory': 8,
'disk_size': 40
},
]
hetzner = { hetzner = {
'base_price': 10, 'base_price': 10,
'core_price': 10, 'core_price': 5,
'memory_price': 5, 'memory_price': 2,
'disk_size_price': 1, 'disk_size_price': 0.6,
'description': 'VM auf einzelner HW, Raid1, kein HA', 'description': 'VM auf einzelner HW, Raid1, kein HA',
'location': 'DE' 'location': 'DE'
} }
return { # return {
# 'hetzner_nug': { # # 'hetzner_nug': {
# 'base_price': 5, # # 'base_price': 5,
# 'memory_price': 2, # # 'memory_price': 2,
# 'core_price': 2, # # 'core_price': 2,
# 'disk_size_price': 0.5, # # 'disk_size_price': 0.5,
# 'description': 'VM ohne Uptime Garantie' # # 'description': 'VM ohne Uptime Garantie'
# }, # # },
'hetzner': hetzner, # 'hetzner': hetzner,
# 'hetzner_raid6': { # # 'hetzner_raid6': {
# 'base_price': hetzner['base_price']*1.2, # # 'base_price': hetzner['base_price']*1.2,
# 'core_price': hetzner['core_price']*1.2, # # 'core_price': hetzner['core_price']*1.2,
# 'memory_price': hetzner['memory_price']*1.2, # # 'memory_price': hetzner['memory_price']*1.2,
# 'disk_size_price': hetzner['disk_size_price']*1.2, # # 'disk_size_price': hetzner['disk_size_price']*1.2,
# 'description': 'VM auf einzelner HW, Raid1, kein HA' # # 'description': 'VM auf einzelner HW, Raid1, kein HA'
# }, # # },
# 'hetzner_glusterfs': { # # 'hetzner_glusterfs': {
# 'base_price': hetzner['base_price']*1.4, # # 'base_price': hetzner['base_price']*1.4,
# 'core_price': hetzner['core_price']*1.4, # # 'core_price': hetzner['core_price']*1.4,
# 'memory_price': hetzner['memory_price']*1.4, # # 'memory_price': hetzner['memory_price']*1.4,
# 'disk_size_price': hetzner['disk_size_price']*1.4, # # 'disk_size_price': hetzner['disk_size_price']*1.4,
# 'description': 'VM auf einzelner HW, Raid1, kein HA' # # 'description': 'VM auf einzelner HW, Raid1, kein HA'
# }, # # },
'bern': { # 'bern': {
'base_price': 12, # 'base_price': 12,
'core_price': 25, # 'core_price': 25,
'memory_price': 7, # 'memory_price': 7,
'disk_size_price': 0.70, # 'disk_size_price': 0.70,
'description': "VM in Bern, HA Setup ohne HA Garantie", # 'description': "VM in Bern, HA Setup ohne HA Garantie",
'location': 'CH', # 'location': 'CH',
} # }
} # }
def handle(self, *args, **options): def handle(self, *args, **options):
data = self.get_data() vm_data = self.get_data()
[VirtualMachineType.objects.create(hosting_company=key, **data[key]) for vm in vm_data:
for key in data.keys()] VirtualMachineType.objects.create(**vm)

View file

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-04-29 18:28
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('hosting', '0027_auto_20160711_0210'),
]
operations = [
migrations.CreateModel(
name='ManageVM',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
options={
'managed': False,
},
),
migrations.CreateModel(
name='UserHostingKey',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('public_key', models.TextField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-04-30 19:04
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('hosting', '0028_managevm_userhostingkey'),
]
operations = [
migrations.AddField(
model_name='userhostingkey',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2017, 4, 30, 19, 4, 20, 780173, tzinfo=utc)),
preserve_default=False,
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-04-30 19:09
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0029_userhostingkey_created_at'),
]
operations = [
migrations.AddField(
model_name='userhostingkey',
name='name',
field=models.CharField(default='', max_length=100),
preserve_default=False,
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-03 05:54
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('hosting', '0030_userhostingkey_name'),
]
operations = [
migrations.RemoveField(
model_name='virtualmachinetype',
name='hosting_company',
),
migrations.RemoveField(
model_name='virtualmachinetype',
name='location',
),
]

View file

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-04 03:15
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0031_auto_20170503_0554'),
]
operations = [
migrations.AddField(
model_name='virtualmachinetype',
name='cores',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinetype',
name='disk_size',
field=models.IntegerField(default=0),
preserve_default=False,
),
migrations.AddField(
model_name='virtualmachinetype',
name='memory',
field=models.IntegerField(default=0),
preserve_default=False,
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-04 03:23
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0032_auto_20170504_0315'),
]
operations = [
migrations.AddField(
model_name='virtualmachinetype',
name='configuration',
field=models.CharField(choices=[('debian', 'Debian 8'), ('ubuntu', 'Ubuntu 16.06'), ('devuan', 'Devuan 1'), ('centos', 'CentOS 7')], default='ubuntu', max_length=10),
),
]

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-04 03:31
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hosting', '0033_virtualmachinetype_configuration'),
]
operations = [
migrations.RemoveField(
model_name='virtualmachinetype',
name='configuration',
),
migrations.AlterField(
model_name='virtualmachineplan',
name='configuration',
field=models.CharField(choices=[('debian', 'Debian 8'), ('ubuntu', 'Ubuntu 16.06'), ('devuan', 'Devuan 1'), ('centos', 'CentOS 7')], max_length=20),
),
migrations.AlterField(
model_name='virtualmachineplan',
name='vm_type',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachineType'),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-06 23:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0034_auto_20170504_0331'),
]
operations = [
migrations.AddField(
model_name='virtualmachineplan',
name='opennebula_id',
field=models.IntegerField(default=0),
preserve_default=False,
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-05-06 23:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0035_virtualmachineplan_opennebula_id'),
]
operations = [
migrations.AlterField(
model_name='virtualmachineplan',
name='opennebula_id',
field=models.IntegerField(null=True),
),
]

View file

@ -1,23 +1,32 @@
from django.shortcuts import redirect from django.shortcuts import redirect
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from .models import VirtualMachinePlan from .models import VirtualMachinePlan, VirtualMachineType
class ProcessVMSelectionMixin(object): class ProcessVMSelectionMixin(object):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
hosting = request.POST.get('configuration') configuration = request.POST.get('configuration')
configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(hosting) configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration)
vm_specs = { vm_template = request.POST.get('vm_template')
'cores': request.POST.get('cores'), vm_type = VirtualMachineType.objects.get(id=vm_template)
'memory': request.POST.get('memory'), vm_specs = vm_type.get_specs()
'disk_size': request.POST.get('disk_space'), vm_specs.update({
'hosting_company': request.POST.get('hosting_company'), 'configuration_display': configuration_display,
'location_code': request.POST.get('location_code'), 'configuration': configuration,
'configuration': hosting, 'final_price': vm_type.final_price,
'configuration_detail': configuration_detail, 'vm_template': vm_template
'final_price': request.POST.get('final_price') })
} # vm_specs = {
# # 'cores': request.POST.get('cores'),
# # 'memory': request.POST.get('memory'),
# # 'disk_size': request.POST.get('disk_space'),
# # 'hosting_company': request.POST.get('hosting_company'),
# # 'location_code': request.POST.get('location_code'),
# # 'configuration': hosting,
# # 'configuration_detail': configuration_detail,
# 'final_price': request.POST.get('final_price')
# }
request.session['vm_specs'] = vm_specs request.session['vm_specs'] = vm_specs
if not request.user.is_authenticated(): if not request.user.is_authenticated():
request.session['vm_specs'] = vm_specs request.session['vm_specs'] = vm_specs

View file

@ -8,7 +8,7 @@ from django.utils.functional import cached_property
from Crypto.PublicKey import RSA from Crypto.PublicKey import RSA
from stored_messages.settings import stored_messages_settings from stored_messages.settings import stored_messages_settings
from membership.models import StripeCustomer from membership.models import StripeCustomer, CustomUser
from utils.models import BillingAddress from utils.models import BillingAddress
from utils.mixins import AssignPermissionsMixin from utils.mixins import AssignPermissionsMixin
from .managers import VMPlansManager from .managers import VMPlansManager
@ -16,70 +16,71 @@ from .managers import VMPlansManager
class VirtualMachineType(models.Model): class VirtualMachineType(models.Model):
HETZNER_NUG = 'hetzner_nug'
HETZNER = 'hetzner'
HETZNER_R6 = 'hetzner_raid6'
HETZNER_G = 'hetzner_glusterfs'
BERN = 'bern'
DE_LOCATION = 'DE'
CH_LOCATION = 'CH'
HOSTING_TYPES = (
(HETZNER_NUG, 'Hetzner No Uptime Guarantee'),
(HETZNER, 'Hetzner'),
(HETZNER_R6, 'Hetzner Raid6'),
(HETZNER_G, 'Hetzner Glusterfs'),
(BERN, 'Bern'),
)
LOCATIONS_CHOICES = (
(DE_LOCATION, 'Germany'),
(CH_LOCATION, 'Switzerland'),
)
description = models.TextField() description = models.TextField()
base_price = models.FloatField() base_price = models.FloatField()
memory_price = models.FloatField() memory_price = models.FloatField()
core_price = models.FloatField() core_price = models.FloatField()
disk_size_price = models.FloatField() disk_size_price = models.FloatField()
hosting_company = models.CharField(max_length=30, choices=HOSTING_TYPES) cores = models.IntegerField()
location = models.CharField(max_length=3, choices=LOCATIONS_CHOICES) memory = models.IntegerField()
disk_size = models.IntegerField()
def __str__(self): def __str__(self):
return "%s" % (self.get_hosting_company_display()) return "VM Type %s" % (self.id)
@cached_property
def final_price(self):
price = self.cores * self.core_price
price += self.memory * self.memory_price
price += self.disk_size * self.disk_size_price
return price
@classmethod @classmethod
def get_serialized_vm_types(cls): def get_serialized_vm_types(cls):
return [vm.get_serialized_data() return [vm.get_serialized_data()
for vm in cls.objects.all()] for vm in cls.objects.all()]
def calculate_price(self, specifications): def calculate_price(self):
price = float(specifications['cores']) * self.core_price price = self.cores * self.core_price
price += float(specifications['memory']) * self.memory_price price += self.memory * self.memory_price
price += float(specifications['disk_size']) * self.disk_size_price price += self.disk_size * self.disk_size_price
price += self.base_price # price += self.base_price
return price return price
def defeault_price(self): # @classmethod
price = self.base_price # def get_price(cls, vm_template):
price += self.core_price # return cls.BASE_PRICE * vm_template
price += self.memory_price
price += self.disk_size_price * 10 def get_specs(self):
return price return {
'memory': self.memory,
'cores': self.cores,
'disk_size': self.disk_size
}
# def calculate_price(self, vm_template):
# price = self.base_price * vm_template
# return price
# def defeault_price(self):
# price = self.base_price
# price += self.core_price
# price += self.memory_price
# price += self.disk_size_price * 10
# return price
def get_serialized_data(self): def get_serialized_data(self):
return { return {
'description': self.description, 'description': self.description,
'base_price': self.base_price,
'core_price': self.core_price, 'core_price': self.core_price,
'disk_size_price': self.disk_size_price, 'disk_size_price': self.disk_size_price,
'memory_price': self.memory_price, 'memory_price': self.memory_price,
'hosting_company_name': self.get_hosting_company_display(),
'hosting_company': self.hosting_company,
'default_price': self.defeault_price(),
'location_code': self.location,
'location': self.get_location_display(),
'id': self.id, 'id': self.id,
'final_price': self.final_price,
'cores': self.cores,
'memory': self.memory,
'disk_size': self.disk_size
} }
@ -95,14 +96,21 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model):
(CANCELED_STATUS, 'Canceled') (CANCELED_STATUS, 'Canceled')
) )
DJANGO = 'django' # DJANGO = 'django'
RAILS = 'rails' # RAILS = 'rails'
NODEJS = 'nodejs' # NODEJS = 'nodejs'
# VM_CONFIGURATION = (
# (DJANGO, 'Ubuntu 14.04, Django'),
# (RAILS, 'Ubuntu 14.04, Rails'),
# (NODEJS, 'Debian, NodeJS'),
# )
VM_CONFIGURATION = ( VM_CONFIGURATION = (
(DJANGO, 'Ubuntu 14.04, Django'), ('debian', 'Debian 8'),
(RAILS, 'Ubuntu 14.04, Rails'), ('ubuntu', 'Ubuntu 16.06'),
(NODEJS, 'Debian, NodeJS'), ('devuan', 'Devuan 1'),
('centos', 'CentOS 7')
) )
permissions = ('view_virtualmachineplan', permissions = ('view_virtualmachineplan',
@ -112,12 +120,13 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model):
cores = models.IntegerField() cores = models.IntegerField()
memory = models.IntegerField() memory = models.IntegerField()
disk_size = models.IntegerField() disk_size = models.IntegerField()
vm_type = models.ForeignKey(VirtualMachineType) vm_type = models.ForeignKey(VirtualMachineType, null=True)
price = models.FloatField() price = models.FloatField()
public_key = models.TextField(blank=True) public_key = models.TextField(blank=True)
status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS) status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS)
ip = models.CharField(max_length=50, blank=True) ip = models.CharField(max_length=50, blank=True)
configuration = models.CharField(max_length=20, choices=VM_CONFIGURATION) configuration = models.CharField(max_length=20, choices=VM_CONFIGURATION)
opennebula_id = models.IntegerField(null=True)
objects = VMPlansManager() objects = VMPlansManager()
@ -130,13 +139,13 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@cached_property # @cached_property
def hosting_company_name(self): # def hosting_company_name(self):
return self.vm_type.get_hosting_company_display() # return self.vm_type.get_hosting_company_display()
@cached_property # @cached_property
def location(self): # def location(self):
return self.vm_type.get_location_display() # return self.vm_type.get_location_display()
@cached_property @cached_property
def name(self): def name(self):
@ -156,24 +165,6 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model):
instance.assign_permissions(user) instance.assign_permissions(user)
return instance return instance
@staticmethod
def generate_RSA(bits=2048):
'''
Generate an RSA keypair with an exponent of 65537 in PEM format
param: bits The key length in bits
Return private key and public key
'''
new_key = RSA.generate(2048, os.urandom)
public_key = new_key.publickey().exportKey("OpenSSH")
private_key = new_key.exportKey("PEM")
return private_key, public_key
def generate_keys(self):
private_key, public_key = self.generate_RSA()
self.public_key = public_key
self.save(update_fields=['public_key'])
return private_key, public_key
def cancel_plan(self): def cancel_plan(self):
self.status = self.CANCELED_STATUS self.status = self.CANCELED_STATUS
self.save(update_fields=['status']) self.save(update_fields=['status'])
@ -225,6 +216,32 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
self.save() self.save()
class UserHostingKey(models.Model):
user = models.ForeignKey(CustomUser)
public_key = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=100)
@staticmethod
def generate_RSA(bits=2048):
'''
Generate an RSA keypair with an exponent of 65537 in PEM format
param: bits The key length in bits
Return private key and public key
'''
new_key = RSA.generate(2048, os.urandom)
public_key = new_key.publickey().exportKey("OpenSSH")
private_key = new_key.exportKey("PEM")
return private_key, public_key
@classmethod
def generate_keys(cls):
private_key, public_key = cls.generate_RSA()
# self.public_key = public_key
# self.save(update_fields=['public_key'])
return private_key, public_key
class ManageVM(models.Model): class ManageVM(models.Model):
def has_add_permission(self, request): def has_add_permission(self, request):
return False return False

View file

@ -87,6 +87,74 @@ class HostingManageVMAdmin(admin.ModelAdmin):
) )
return TemplateResponse(request, "hosting/managevms.html", context) return TemplateResponse(request, "hosting/managevms.html", context)
# Function that shows the VMs of the current user
def show_vms_view(self, request):
"""
Implemented by Levi for the API
"""
vm_pool = None
try:
self.init_opennebula_client(request)
vm_pool = oca.VirtualMachinePool(self.client)
vm_pool.info()
except socket.timeout as socket_err:
logger.error("Socket timeout error.".format(socket_err))
except OpenNebulaException as opennebula_err:
logger.error("OpenNebulaException error: {0}".format(opennebula_err))
except OSError as os_err:
logger.error("OSError : {0}".format(os_err))
except ValueError as value_err:
logger.error("ValueError : {0}".format(value_err))
context = dict(
# Include common variables for rendering the admin template.
# self.admin_site.each_context(request),
vms=vm_pool,
)
return context
def create_vm_view(self, specs):
vm_id = None
try:
# We do have the vm_template param set. Get and parse it
# and check it to be in the desired range.
# We have 8 possible VM templates for the moment which are 1x, 2x, 4x ...
# the basic template of 10GB disk, 1GB ram, 1 vcpu, 0.1 cpu
vm_string_formatter = """<VM>
<MEMORY>{memory}</MEMORY>
<VCPU>{vcpu}</VCPU>
<CPU>{cpu}</CPU>
<DISK>
<TYPE>{disk_type}</TYPE>
<SIZE>{size}</SIZE>
</DISK>
</VM>
"""
vm_id = oca.VirtualMachine.allocate(
self.client,
vm_string_formatter.format(
memory=1024 * specs.get('memory'),
vcpu=specs.get('cores'),
cpu=0.1 * specs.get('cores'),
disk_type='fs',
size=10000 * specs.get('disk_size')
)
)
# message = _("Created with id = " + str(vm_id))
# messages.add_message(request, messages.SUCCESS, message)
except socket.timeout as socket_err:
logger.error("Socket timeout error: {0}".format(socket_err))
except OpenNebulaException as opennebula_err:
logger.error("OpenNebulaException error: {0}".format(opennebula_err))
except OSError as os_err:
logger.error("OSError : {0}".format(os_err))
except ValueError as value_err:
logger.error("ValueError : {0}".format(value_err))
return vm_id
# Creating VM by using method allocate(client, template) # Creating VM by using method allocate(client, template)
def create_vm(self, request): def create_vm(self, request):
# check if the request contains the template parameter, if it is # check if the request contains the template parameter, if it is

View file

@ -72,6 +72,11 @@
<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 "%}

View file

@ -0,0 +1,45 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 i18n %}
{% block content %}
<div>
<div class="container dashboard-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h3><i class="fa fa-server" aria-hidden="true"></i> {% trans "New Virtual Machine"%} </h3>
<hr/>
<form method="POST" action="">
{% csrf_token %}
<div class="form-group">
Select VM:
<select name="vm_template">
{% for vm in vm_types %}
<option value="{{vm.id}}">CORE: {{vm.cores}}, RAM: {{vm.memory}}, SSD: {{vm.disk_size}} </option>
{% endfor %}
</select>
</div>
<div class="form-group">
Select VM Configuration:
<select name="configuration">
{% for config in configuration_options %}
<option value="{{config.0}}">{{config.1}} </option>
{% endfor %}
</select>
</div>
<div class="form-group">
<button class="btn btn-success" >{% trans "Start VM"%} </button>
</div>
</form>
</div>
</div>
</div>
</div>
{%endblock%}

View file

@ -24,58 +24,17 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="hosting_company" value="{{vm.hosting_company}}"> <input type="hidden" name="hosting_company" value="{{vm.hosting_company}}">
<input type="hidden" name="location_code" value="{{vm.location_code}}"> <input type="hidden" name="location_code" value="{{vm.location_code}}">
<input type="hidden" name="vm_template" value="{{vm.id}}">
<ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}"> <ul class="pricing {% cycle 'p-red' 'p-black' 'p-red' 'p-yel' %}">
<li class="type">
<!-- <img src="http://bread.pp.ua/n/settings_g.svg" alt=""> -->
<h3 >{{vm.location_code}}</h3>
<br/>
<img class="img-responsive" src="{{ STATIC_URL }}hosting/img/{{vm.location_code}}_flag.png" alt="">
</li>
<li> <li>
<!-- Single button --> <!-- Single button -->
<div class="btn-group"> <div class="btn-group">
<div class="form-group"> <div class="form-group">
<label for="cores">Location: </label> <label for="cores">Cores: {{vm.cores}}</label>
{{vm.location}}
</div>
</div>
</li>
<li>
<label for="configuration">Configuration: </label>
{% if select_configuration %}
<select class="form-control" name="configuration" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}">
{% for key,value in configuration_options.items %}
<option value="{{key}}">{{ value }}</option>
{% endfor %}
</select>
{% else %}
<input type="hidden" name="configuration_detail" value="{{configuration_detail}}">
<input type="hidden" name="configuration" value="{{hosting}}">
<!-- Single button -->
<div class="btn-group">
<div class="form-group">
<label>Configuration: </label>
{{configuration_detail}}
</div>
</div>
{% endif %}
</li>
<li>
<!-- Single button -->
<div class="btn-group">
<div class="form-group">
<label for="cores">Cores: </label>
<select class="form-control cores-selector" name="cores" id="{{vm.hosting_company}}-cores" data-vm-type="{{vm.hosting_company}}">
{% with ''|center:10 as range %}
{% for _ in range %}
<option>{{ forloop.counter }}</option>
{% endfor %}
{% endwith %}
</select>
</div> </div>
</div> </div>
@ -83,30 +42,28 @@
<li> <li>
<div class="form-group"> <div class="form-group">
<div class="btn-group"> <div class="btn-group">
<label for="memory">Memory: </label> <label for="memory">Memory: {{vm.memory}} GiB</label>
<select class="form-control memory-selector" name="memory" id="{{vm.hosting_company}}-memory" data-vm-type="{{vm.hosting_company}}">
{% with ''|center:50 as range %}
{% for _ in range %}
<option>{{ forloop.counter }}</option>
{% endfor %}
{% endwith %}
</select>
<span>GiB</span>
</div> </div>
</div> </div>
</li> </li>
<li> <li>
<div class="form-group row"> <div class="form-group row">
<div class="col-xs-offset-1 col-xs-9 col-sm-12 col-md-12 col-md-offset-0"> <div class="col-xs-offset-1 col-xs-9 col-sm-12 col-md-12 col-md-offset-0">
<label for="Disk Size">Disk Size: </label> <label for="Disk Size">Disk Size: {{vm.disk_size}} GiB</label>
<input class="form-control short-input text-center disk-space-selector" name="disk_space" type="number" id="{{vm.hosting_company}}-disk_space" min="10" value="10" step="10" data-vm-type="{{vm.hosting_company}}"/>
<span>GiB</span>
</div> </div>
</div> </div>
</li> </li>
<li> <li>
<input id="{{vm.hosting_company}}-final-price-input" type="hidden" name="final_price" value="{{vm.default_price|floatformat}}"> <label for="configuration">Configuration: </label>
<h3 id="{{vm.hosting_company}}-final-price">{{vm.default_price|floatformat}}CHF</h3> <select class="form-control" name="configuration" id="{{vm.hosting_company}}-configuration" data-vm-type="{{vm.hosting_company}}">
{% for key,value in configuration_options.items %}
<option value="{{key}}">{{ value }}</option>
{% endfor %}
</select>
</li>
<li>
<input type="hidden" name="final_price" value="{{vm.final_price|floatformat}}">
<h3 id="{{vm.hosting_company}}-final-price">{{vm.final_price|floatformat}}CHF</h3>
<span>per month</span> <span>per month</span>
</li> </li>
<li> <li>

View file

@ -65,7 +65,7 @@
{% url 'hosting:payment' as payment_url %} {% url 'hosting:payment' as payment_url %}
{% if payment_url in request.META.HTTP_REFERER %} {% if payment_url in request.META.HTTP_REFERER %}
<div class=" content pull-right"> <div class=" content pull-right">
<a href="{% url 'hosting:virtual_machine_key' order.vm_plan.id %}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a> <a href="{% url 'hosting:key_pair'%}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View file

@ -86,11 +86,11 @@
<h3><b>Billing Amount</b></h3> <h3><b>Billing Amount</b></h3>
<hr> <hr>
<div class="content"> <div class="content">
<p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> <!-- <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.location_code}}</span></p> -->
<hr> <!-- <hr> -->
<p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p> <p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p>
<hr> <hr>
<p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_detail}}</span></p> <p><b>Configuration</b> <span class="pull-right">{{request.session.vm_specs.configuration_display}}</span></p>
<hr> <hr>
<p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p> <p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p>
<hr> <hr>

View file

@ -6,30 +6,75 @@
<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="" >
<h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "SSH Private Key"%} </h3> {% csrf_token %}
<h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3>
<hr/> <hr/>
{% if not user_key %}
<div class="alert alert-warning">
{% trans "Upload your own key. "%}
</div>
<div class="form-group">
<label for="comment">Paste here your public key</label>
<textarea class="form-control" rows="6" name="public_key"></textarea>
</div>
<div class="form-group">
<button class="btn btn-success">{% trans "Upload Key"%} </a>
</div>
<div class="alert alert-warning">
{% trans "Or generate a new key pair."%}
</div>
<div class="form-group">
<button class="btn btn-success">{% trans "Generate Key Pair"%} </a>
</div>
{% else %}
<h5> Use your created key to access to the machine. If you lost it, contact us. </h5>
<table class="table borderless table-hover">
<br/>
<thead>
<tr>
<th>{% trans "Name"%}</th>
<th>{% trans "Created at"%} </th>
<th>{% trans "Status"%} </th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">{{user_key.name}}</td>
<td>{{user_key.created_at}}</td>
<td>
<span class="h3 label label-success"><strong>Active</strong></span>
</td>
</tr>
</tbody>
</table>
{% endif %}
</form>
{% if private_key %} {% if private_key %}
<div class="alert alert-warning"> <div class="alert alert-warning">
<strong>{% trans "Warning!"%}</strong>{% trans "You can view your SSH private key once. Copy it or if it wasn't downloaded automatically, just click on Download to start it."%} <strong>{% trans "Warning!"%}</strong>{% trans "You can view your SSH private key once. Copy it or if it wasn't downloaded automatically, just click on Download to start it."%}
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="comment">private_key.pem</label> <textarea class="form-control" rows="6" id="ssh_key" type="hidden" style="display:none">{{private_key}}</textarea>
<textarea class="form-control" rows="6" id="ssh_key">{{private_key}}</textarea>
</div> </div>
<div class="form-group pull-right"> <!-- <div class="form-group pull-right">
<button type="button" id="copy_to_clipboard" data-clipboard-target="#ssh_key" class="btn btn-warning" <button type="button" id="copy_to_clipboard" data-clipboard-target="#ssh_key" class="btn btn-warning"
data-toggle="tooltip" data-placement="bottom" title="Copied" data-trigger="click">{% trans "Copy to Clipboard"%}</button> data-toggle="tooltip" data-placement="bottom" title="Copied" data-trigger="click">{% trans "Copy to Clipboard"%}</button>
<button type="button" id="download_ssh_key" class="btn btn-warning">{% trans "Download"%}</button> <button type="button" id="download_ssh_key" class="btn btn-warning">{% trans "Download"%}</button>
</div> </div> -->
{% else %} {% else %}
<div class="alert alert-warning"> <!-- <div class="alert alert-warning">
<strong>{% trans "Warning!"%}</strong>{% trans "Your SSH private key was already generated and downloaded, if you lost it, contact us. "%} <strong>{% trans "Warning!"%}</strong>{% trans "Your SSH private key was already generated and downloaded, if you lost it, contact us. "%}
</div> </div>
{% endif %} --> {% endif %}
<a class="btn btn-success" href="{% url 'hosting:virtual_machines' virtual_machine.id %}">{% trans "Go to my Virtual Machine Dashboard"%} </a> <!-- <a class="btn btn-success" href="{% url 'hosting:virtual_machines' %}">{% trans "Generate my key"%} </a> -->
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
</div> </div>
@ -42,11 +87,12 @@
<!-- Force to download ssh key on page load --> <!-- Force to download ssh key on page load -->
<script type="text/javascript"> <script type="text/javascript">
var key = window.document.getElementById('ssh_key'); var key = window.document.getElementById('ssh_key');
var a = window.document.createElement('a'); var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob(['key'], {type: 'text'})); a.href = window.URL.createObjectURL(new Blob([key.value], {type: 'text'}));
a.download = 'private_key.pem'; a.download = '{{key_name}}.pem';
// Append anchor to body. // Append anchor to body.
document.body.appendChild(a); document.body.appendChild(a);

View file

@ -6,12 +6,14 @@
<div class="row"> <div class="row">
<div class="col-md-8 col-md-offset-2"> <div class="col-md-8 col-md-offset-2">
<table class="table borderless table-hover"> <table class="table borderless table-hover">
<h3><i class="fa fa-server" aria-hidden="true"></i>{% trans "Virtual Machines"%} </h3> <h3 class="pull-left"><i class="fa fa-server" aria-hidden="true"></i> {% trans "Virtual Machines"%} </h3>
<p class="pull-right">
<a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>
</p>
<br/> <br/>
<thead> <thead>
<tr> <tr>
<th>{% trans "ID"%}</th> <th>{% trans "ID"%}</th>
<th>{% trans "Location"%} </th>
<th>{% trans "Amount"%}</th> <th>{% trans "Amount"%}</th>
<th>{% trans "Status"%}</th> <th>{% trans "Status"%}</th>
<th></th> <th></th>
@ -21,7 +23,6 @@
{% for vm in vms %} {% for vm in vms %}
<tr> <tr>
<td scope="row">{{vm.name}}</td> <td scope="row">{{vm.name}}</td>
<td>{{vm.location}}</td>
<td>{{vm.price}} CHF</td> <td>{{vm.price}} CHF</td>
<td> <td>

View file

@ -4,8 +4,8 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
NodeJSHostingView, LoginView, SignupView, IndexView, \ NodeJSHostingView, LoginView, SignupView, IndexView, \
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \ VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, \ MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView, HostingPricingView,\
HostingPricingView, HostingBillListView, HostingBillDetailView CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView
urlpatterns = [ urlpatterns = [
url(r'index/?$', IndexView.as_view(), name='index'), url(r'index/?$', IndexView.as_view(), name='index'),
@ -19,13 +19,14 @@ urlpatterns = [
url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/?$', HostingBillListView.as_view(), name='bills'),
url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'), url(r'bills/(?P<pk>\d+)/?$', HostingBillDetailView.as_view(), name='bills'),
url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), url(r'cancel_order/(?P<pk>\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'),
url(r'create-virtual-machine/?$', CreateVirtualMachinesView.as_view(), name='create-virtual-machine'),
url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'),
url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(), url(r'my-virtual-machines/(?P<pk>\d+)/?$', VirtualMachineView.as_view(),
name='virtual_machines'), name='virtual_machines'),
# url(r'my-virtual-machines/(?P<pk>\d+)/delete/?$', VirtualMachineCancelView.as_view(), # url(r'my-virtual-machines/(?P<pk>\d+)/delete/?$', VirtualMachineCancelView.as_view(),
# name='virtual_machines_cancel'), # name='virtual_machines_cancel'),
url(r'my-virtual-machines/(?P<pk>\d+)/key/?$', GenerateVMSSHKeysView.as_view(), url(r'vm-key-pair/?$', GenerateVMSSHKeysView.as_view(),
name='virtual_machine_key'), name='key_pair'),
url(r'^notifications/$', NotificationsView.as_view(), name='notifications'), url(r'^notifications/$', NotificationsView.as_view(), name='notifications'),
url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(), url(r'^notifications/(?P<pk>\d+)/?$', MarkAsReadNotificationView.as_view(),
name='read_notification'), name='read_notification'),

View file

@ -1,5 +1,4 @@
import oca from collections import namedtuple
import socket
from django.shortcuts import render from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy, reverse from django.core.urlresolvers import reverse_lazy, reverse
@ -10,6 +9,8 @@ from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.contrib import messages from django.contrib import messages
from django.conf import settings from django.conf import settings
from django.shortcuts import redirect
from guardian.mixins import PermissionRequiredMixin from guardian.mixins import PermissionRequiredMixin
from stored_messages.settings import stored_messages_settings from stored_messages.settings import stored_messages_settings
@ -17,14 +18,16 @@ from stored_messages.models import Message
from stored_messages.api import mark_read from stored_messages.api import mark_read
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm, PasswordResetRequestForm from utils.forms import BillingAddressForm, PasswordResetRequestForm
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin, LoginViewMixin
from utils.mailer import BaseEmail from utils.mailer import BaseEmail
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill, UserHostingKey
from .forms import HostingUserSignupForm, HostingUserLoginForm from .forms import HostingUserSignupForm, HostingUserLoginForm, UserHostingKeyForm
from .mixins import ProcessVMSelectionMixin from .mixins import ProcessVMSelectionMixin
from .opennebula_functions import HostingManageVMAdmin
from oca.exceptions import OpenNebulaException from oca.exceptions import OpenNebulaException
from oca.pool import WrongNameError from oca.pool import WrongNameError
@ -44,6 +47,7 @@ class DjangoHostingView(ProcessVMSelectionMixin, View):
'google_analytics': "UA-62285904-6", 'google_analytics': "UA-62285904-6",
'email': "info@django-hosting.ch", 'email': "info@django-hosting.ch",
'vm_types': VirtualMachineType.get_serialized_vm_types(), 'vm_types': VirtualMachineType.get_serialized_vm_types(),
'configuration_options': dict(VirtualMachinePlan.VM_CONFIGURATION)
} }
return context return context
@ -212,26 +216,52 @@ class MarkAsReadNotificationView(LoginRequiredMixin, UpdateView):
return HttpResponseRedirect(reverse('hosting:notifications')) return HttpResponseRedirect(reverse('hosting:notifications'))
class GenerateVMSSHKeysView(LoginRequiredMixin, DetailView): class GenerateVMSSHKeysView(LoginRequiredMixin, FormView):
model = VirtualMachinePlan form_class = UserHostingKeyForm
model = UserHostingKey
template_name = 'hosting/virtual_machine_key.html' template_name = 'hosting/virtual_machine_key.html'
success_url = reverse_lazy('hosting:orders') success_url = reverse_lazy('hosting:orders')
login_url = reverse_lazy('hosting:login') login_url = reverse_lazy('hosting:login')
context_object_name = "virtual_machine" context_object_name = "virtual_machine"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
try:
user_key = UserHostingKey.objects.get(
user=self.request.user
)
except UserHostingKey.DoesNotExist:
user_key = None
context = super(
GenerateVMSSHKeysView,
self
).get_context_data(**kwargs)
context.update({
'user_key': user_key
})
context = super(GenerateVMSSHKeysView, self).get_context_data(**kwargs)
vm = self.get_object()
if not vm.public_key:
private_key, public_key = vm.generate_keys()
context.update({
'private_key': private_key,
'public_key': public_key
})
return context
return context return context
def get_form_kwargs(self):
kwargs = super(GenerateVMSSHKeysView, self).get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.save()
context = self.get_context_data()
if form.cleaned_data.get('private_key'):
context.update({
'private_key': form.cleaned_data.get('private_key'),
'key_name': form.cleaned_data.get('name')
})
# print("form", form.cleaned_data)
return render(self.request, self.template_name, context)
class PaymentVMView(LoginRequiredMixin, FormView): class PaymentVMView(LoginRequiredMixin, FormView):
template_name = 'hosting/payment.html' template_name = 'hosting/payment.html'
@ -252,18 +282,26 @@ class PaymentVMView(LoginRequiredMixin, FormView):
if form.is_valid(): if form.is_valid():
context = self.get_context_data() context = self.get_context_data()
specifications = request.session.get('vm_specs') specifications = request.session.get('vm_specs')
vm_type = specifications.get('hosting_company')
vm = VirtualMachineType.objects.get(hosting_company=vm_type) vm_template = specifications.get('vm_template', 1)
final_price = vm.calculate_price(specifications)
vm_type = VirtualMachineType.objects.get(id=vm_template)
specs = vm_type.get_specs()
final_price = vm_type.calculate_price()
plan_data = { plan_data = {
'vm_type': vm, 'vm_type': vm_type,
'cores': specifications.get('cores'), 'configuration': specifications.get(
'memory': specifications.get('memory'), 'configuration',
'disk_size': specifications.get('disk_size'), 'django'
'configuration': specifications.get('configuration'), ),
'price': final_price 'price': final_price
} }
plan_data.update(specs)
token = form.cleaned_data.get('token') token = form.cleaned_data.get('token')
# Get or create stripe customer # Get or create stripe customer
@ -305,6 +343,20 @@ class PaymentVMView(LoginRequiredMixin, FormView):
# If the Stripe payment was successed, set order status approved # If the Stripe payment was successed, set order status approved
order.set_approved() order.set_approved()
# Create VM using oppenebula functions
# _request = namedtuple('request', 'POST user')
# _request.user = request.user
# user = namedtuple('user', 'email')
# email
# _request.POST = {
# 'vm_template': vm_template
# }
hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin)
hosting_admin.init_opennebula_client(request)
oppennebula_vm_id = hosting_admin.create_vm_view(vm_type.get_specs())
plan.oppenebula_id = oppennebula_vm_id
# Send notification to ungleich as soon as VM has been booked # Send notification to ungleich as soon as VM has been booked
context = { context = {
'vm': plan, 'vm': plan,
@ -368,11 +420,48 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
ordering = '-id' ordering = '-id'
def get_queryset(self): def get_queryset(self):
hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin)
print(hosting_admin.show_vms_view(self.request))
user = self.request.user user = self.request.user
self.queryset = VirtualMachinePlan.objects.active(user) self.queryset = VirtualMachinePlan.objects.active(user)
return super(VirtualMachinesPlanListView, self).get_queryset() return super(VirtualMachinesPlanListView, self).get_queryset()
class CreateVirtualMachinesView(LoginRequiredMixin, View):
template_name = "hosting/create_virtual_machine.html"
login_url = reverse_lazy('hosting:login')
def get(self, request, *args, **kwargs):
context = {
'vm_types': VirtualMachineType.get_serialized_vm_types(),
'configuration_options': VirtualMachinePlan.VM_CONFIGURATION
}
# context = {}
return render(request, self.template_name, context)
def post(self, request):
configuration = request.POST.get('configuration')
configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration)
vm_template = request.POST.get('vm_template')
vm_type = VirtualMachineType.objects.get(id=vm_template)
vm_specs = vm_type.get_specs()
vm_specs.update({
'configuration_display': configuration_display,
'configuration': configuration,
'final_price': vm_type.final_price,
'vm_template': vm_template
})
request.session['vm_specs'] = vm_specs
return redirect(reverse('hosting:payment'))
# def get_queryset(self):
# # hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin)
# # print(hosting_admin.show_vms(self.request))
# user = self.request.user
# self.queryset = VirtualMachinePlan.objects.active(user)
# return super(VirtualMachinesPlanListView, self).get_queryset()
class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView): class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView):
template_name = "hosting/virtual_machine_detail.html" template_name = "hosting/virtual_machine_detail.html"
login_url = reverse_lazy('hosting:login') login_url = reverse_lazy('hosting:login')