From 085133fb53a2867789d32ad0602318b9b624fd27 Mon Sep 17 00:00:00 2001 From: Levi Date: Sat, 29 Apr 2017 12:39:55 -0500 Subject: [PATCH 01/13] integrating hosting app with opennebula integration --- hosting/models.py | 30 ++++++++++++++++++++++++------ hosting/views.py | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index b24dc855..2daab9f8 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -15,6 +15,9 @@ from .managers import VMPlansManager class VirtualMachineType(models.Model): + + BASE_PRICE = 0.5 + HETZNER_NUG = 'hetzner_nug' HETZNER = 'hetzner' HETZNER_R6 = 'hetzner_raid6' @@ -35,7 +38,6 @@ class VirtualMachineType(models.Model): (DE_LOCATION, 'Germany'), (CH_LOCATION, 'Switzerland'), ) - description = models.TextField() base_price = models.FloatField() memory_price = models.FloatField() @@ -52,11 +54,27 @@ class VirtualMachineType(models.Model): return [vm.get_serialized_data() for vm in cls.objects.all()] - def calculate_price(self, specifications): - price = float(specifications['cores']) * self.core_price - price += float(specifications['memory']) * self.memory_price - price += float(specifications['disk_size']) * self.disk_size_price - price += self.base_price + # def calculate_price(self, specifications): + # price = float(specifications['cores']) * self.core_price + # price += float(specifications['memory']) * self.memory_price + # price += float(specifications['disk_size']) * self.disk_size_price + # price += self.base_price + # return price + + @classmethod + def get_price(cls, vm_template): + return cls.BASE_PRICE * vm_template + + @classmethod + def get_specs(cls, vm_template): + return { + 'memory': 1024 * vm_template, + 'cores': 0.1 * vm_template, + 'disk_size': 10000 * vm_template + } + + def calculate_price(self, vm_template): + price = self.base_price * vm_template return price def defeault_price(self): diff --git a/hosting/views.py b/hosting/views.py index 6b21c91b..b2625853 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,3 +1,4 @@ +from collections import namedtuple from django.shortcuts import render from django.core.urlresolvers import reverse_lazy, reverse @@ -22,6 +23,7 @@ from utils.mailer import BaseEmail from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder from .forms import HostingUserSignupForm, HostingUserLoginForm from .mixins import ProcessVMSelectionMixin +from .opennebula_functions import HostingManageVMAdmin class DjangoHostingView(ProcessVMSelectionMixin, View): @@ -246,18 +248,26 @@ class PaymentVMView(LoginRequiredMixin, FormView): if form.is_valid(): context = self.get_context_data() specifications = request.session.get('vm_specs') - vm_type = specifications.get('hosting_company') - vm = VirtualMachineType.objects.get(hosting_company=vm_type) - final_price = vm.calculate_price(specifications) + + vm_template = specifications.get('vm_template', 1) + + vm_type = VirtualMachineType.objects.first() + + final_price = VirtualMachineType.get_price(vm_template) + + specs = VirtualMachineType.get_specs(vm_template) plan_data = { - 'vm_type': vm, - 'cores': specifications.get('cores'), - 'memory': specifications.get('memory'), - 'disk_size': specifications.get('disk_size'), - 'configuration': specifications.get('configuration'), + 'vm_type': vm_type, + 'configuration': specifications.get( + 'configuration', + 'django' + ), 'price': final_price } + + plan_data.update(specs) + token = form.cleaned_data.get('token') # Get or create stripe customer @@ -299,6 +309,19 @@ class PaymentVMView(LoginRequiredMixin, FormView): # If the Stripe payment was successed, set order status approved order.set_approved() + # Create VM using oppenebula functions + _request = namedtuple('request', 'POST user') + _request.user = request.user + # user = namedtuple('user', 'email') + # email + _request.POST = { + 'vm_template': vm_template + } + + hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) + hosting_admin.init_opennebula_client(_request) + hosting_admin.create(_request) + # Send notification to ungleich as soon as VM has been booked context = { 'vm': plan, From 3e755b3b68730aab8e49fe0cf1089c4036208564 Mon Sep 17 00:00:00 2001 From: Levi Date: Sat, 29 Apr 2017 12:46:56 -0500 Subject: [PATCH 02/13] added vm_template parameter to djangohosting view --- hosting/templates/hosting/includes/_pricing.html | 1 + hosting/views.py | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html index 92033be8..284f0717 100644 --- a/hosting/templates/hosting/includes/_pricing.html +++ b/hosting/templates/hosting/includes/_pricing.html @@ -24,6 +24,7 @@ {% csrf_token %} + diff --git a/hosting/views.py b/hosting/views.py index b2625853..f3afca94 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -310,17 +310,17 @@ class PaymentVMView(LoginRequiredMixin, FormView): order.set_approved() # Create VM using oppenebula functions - _request = namedtuple('request', 'POST user') - _request.user = request.user + # _request = namedtuple('request', 'POST user') + # _request.user = request.user # user = namedtuple('user', 'email') # email - _request.POST = { - 'vm_template': vm_template - } + # _request.POST = { + # 'vm_template': vm_template + # } hosting_admin = HostingManageVMAdmin.__new__(HostingManageVMAdmin) - hosting_admin.init_opennebula_client(_request) - hosting_admin.create(_request) + hosting_admin.init_opennebula_client(request) + hosting_admin.create(request) # Send notification to ungleich as soon as VM has been booked context = { From 4ec830550cfdfec4b4dea1f2a8dc3bca61e8148d Mon Sep 17 00:00:00 2001 From: Levi Date: Sat, 29 Apr 2017 12:51:30 -0500 Subject: [PATCH 03/13] showing VMS --- hosting/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hosting/views.py b/hosting/views.py index f3afca94..0173011e 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -381,6 +381,7 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): ordering = '-id' def get_queryset(self): + print(HostingManageVMAdmin.show_vms(self.request)) user = self.request.user self.queryset = VirtualMachinePlan.objects.active(user) return super(VirtualMachinesPlanListView, self).get_queryset() From 265820500883148753225e7fe0098a2fe6c0bbcb Mon Sep 17 00:00:00 2001 From: Levi Date: Sat, 29 Apr 2017 12:54:14 -0500 Subject: [PATCH 04/13] showing VMS --- hosting/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 0173011e..9634a032 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -381,7 +381,8 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): ordering = '-id' def get_queryset(self): - print(HostingManageVMAdmin.show_vms(self.request)) + 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() From ed806910e6aaded3c9ae601464e71a271524b665 Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 3 May 2017 23:19:32 -0500 Subject: [PATCH 05/13] adding integration opennebula-hosting app --- hosting/forms.py | 42 ++++- .../management/commands/create_vm_types.py | 125 +++++++++---- .../0028_managevm_userhostingkey.py | 35 ++++ .../0029_userhostingkey_created_at.py | 23 +++ .../migrations/0030_userhostingkey_name.py | 21 +++ hosting/migrations/0031_auto_20170503_0554.py | 23 +++ hosting/migrations/0032_auto_20170504_0315.py | 33 ++++ .../0033_virtualmachinetype_configuration.py | 20 ++ hosting/migrations/0034_auto_20170504_0331.py | 30 +++ hosting/mixins.py | 35 ++-- hosting/models.py | 172 +++++++++--------- hosting/templates/hosting/base_short.html | 5 + .../hosting/create_virtual_machine.html | 45 +++++ .../templates/hosting/includes/_pricing.html | 74 ++------ hosting/templates/hosting/order_detail.html | 2 +- hosting/templates/hosting/payment.html | 6 +- .../hosting/virtual_machine_key.html | 70 +++++-- .../templates/hosting/virtual_machines.html | 5 +- hosting/urls.py | 8 +- hosting/views.py | 106 ++++++++--- 20 files changed, 641 insertions(+), 239 deletions(-) create mode 100644 hosting/migrations/0028_managevm_userhostingkey.py create mode 100644 hosting/migrations/0029_userhostingkey_created_at.py create mode 100644 hosting/migrations/0030_userhostingkey_name.py create mode 100644 hosting/migrations/0031_auto_20170503_0554.py create mode 100644 hosting/migrations/0032_auto_20170504_0315.py create mode 100644 hosting/migrations/0033_virtualmachinetype_configuration.py create mode 100644 hosting/migrations/0034_auto_20170504_0331.py create mode 100644 hosting/templates/hosting/create_virtual_machine.html diff --git a/hosting/forms.py b/hosting/forms.py index 2a4d67e3..7323bdf3 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -1,10 +1,13 @@ +import random +import string from django import forms from membership.models import CustomUser from django.contrib.auth import authenticate + from utils.stripe_utils import StripeUtils -from .models import HostingOrder, VirtualMachinePlan +from .models import HostingOrder, VirtualMachinePlan, UserHostingKey class HostingOrderAdminForm(forms.ModelForm): @@ -83,3 +86,40 @@ class HostingUserSignupForm(forms.ModelForm): if not confirm_password == password: raise forms.ValidationError("Passwords don't match") return confirm_password + + +class UserHostingKeyForm(forms.ModelForm): + private_key = forms.CharField(widget=forms.PasswordInput(), required=False) + public_key = forms.CharField(widget=forms.PasswordInput(), required=False) + user = forms.models.ModelChoiceField(queryset=CustomUser.objects.all(), required=False) + name = forms.CharField(required=False) + + def __init__(self, *args, **kwargs): + self.request = kwargs.pop("request") + super(UserHostingKeyForm, self).__init__(*args, **kwargs) + # self.initial['user'].initial = self.request.user.id + # print(self.fields) + + def clean_name(self): + return ''.join(random.choice(string.ascii_lowercase) for i in range(7)) + + def clean_user(self): + return self.request.user + + def clean(self): + cleaned_data = self.cleaned_data + + print(cleaned_data) + + if not cleaned_data.get('public_key'): + private_key, public_key = UserHostingKey.generate_keys() + cleaned_data.update({ + 'private_key': private_key, + 'public_key': public_key + }) + + return cleaned_data + + class Meta: + model = UserHostingKey + fields = ['user', 'public_key', 'name'] diff --git a/hosting/management/commands/create_vm_types.py b/hosting/management/commands/create_vm_types.py index 6be49f19..f92cc3a1 100644 --- a/hosting/management/commands/create_vm_types.py +++ b/hosting/management/commands/create_vm_types.py @@ -7,51 +7,100 @@ class Command(BaseCommand): def get_data(self): + return [ + { + 'base_price': 10, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, + 'cores': 1, + 'memory': 2, + 'disk_size': 10 + }, + { + 'base_price': 10, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, + 'cores': 1, + 'memory': 2, + 'disk_size': 100 + }, + { + 'base_price': 10, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, + 'cores': 2, + 'memory': 4, + 'disk_size': 20 + }, + { + 'base_price': 10, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, + 'cores': 4, + 'memory': 8, + 'disk_size': 40 + }, + { + 'base_price': 10, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, + 'cores': 16, + 'memory': 8, + 'disk_size': 40 + }, + ] + + hetzner = { 'base_price': 10, - 'core_price': 10, - 'memory_price': 5, - 'disk_size_price': 1, + 'core_price': 5, + 'memory_price': 2, + 'disk_size_price': 0.6, 'description': 'VM auf einzelner HW, Raid1, kein HA', 'location': 'DE' } - return { - # 'hetzner_nug': { - # 'base_price': 5, - # 'memory_price': 2, - # 'core_price': 2, - # 'disk_size_price': 0.5, - # 'description': 'VM ohne Uptime Garantie' - # }, - 'hetzner': hetzner, - # 'hetzner_raid6': { - # 'base_price': hetzner['base_price']*1.2, - # 'core_price': hetzner['core_price']*1.2, - # 'memory_price': hetzner['memory_price']*1.2, - # 'disk_size_price': hetzner['disk_size_price']*1.2, - # 'description': 'VM auf einzelner HW, Raid1, kein HA' + # return { + # # 'hetzner_nug': { + # # 'base_price': 5, + # # 'memory_price': 2, + # # 'core_price': 2, + # # 'disk_size_price': 0.5, + # # 'description': 'VM ohne Uptime Garantie' + # # }, + # 'hetzner': hetzner, + # # 'hetzner_raid6': { + # # 'base_price': hetzner['base_price']*1.2, + # # 'core_price': hetzner['core_price']*1.2, + # # 'memory_price': hetzner['memory_price']*1.2, + # # 'disk_size_price': hetzner['disk_size_price']*1.2, + # # 'description': 'VM auf einzelner HW, Raid1, kein HA' - # }, - # 'hetzner_glusterfs': { - # 'base_price': hetzner['base_price']*1.4, - # 'core_price': hetzner['core_price']*1.4, - # 'memory_price': hetzner['memory_price']*1.4, - # 'disk_size_price': hetzner['disk_size_price']*1.4, - # 'description': 'VM auf einzelner HW, Raid1, kein HA' - # }, - 'bern': { - 'base_price': 12, - 'core_price': 25, - 'memory_price': 7, - 'disk_size_price': 0.70, - 'description': "VM in Bern, HA Setup ohne HA Garantie", - 'location': 'CH', - } - } + # # }, + # # 'hetzner_glusterfs': { + # # 'base_price': hetzner['base_price']*1.4, + # # 'core_price': hetzner['core_price']*1.4, + # # 'memory_price': hetzner['memory_price']*1.4, + # # 'disk_size_price': hetzner['disk_size_price']*1.4, + # # 'description': 'VM auf einzelner HW, Raid1, kein HA' + # # }, + # 'bern': { + # 'base_price': 12, + # 'core_price': 25, + # 'memory_price': 7, + # 'disk_size_price': 0.70, + # 'description': "VM in Bern, HA Setup ohne HA Garantie", + # 'location': 'CH', + # } + # } def handle(self, *args, **options): - data = self.get_data() - [VirtualMachineType.objects.create(hosting_company=key, **data[key]) - for key in data.keys()] + vm_data = self.get_data() + for vm in vm_data: + VirtualMachineType.objects.create(**vm) diff --git a/hosting/migrations/0028_managevm_userhostingkey.py b/hosting/migrations/0028_managevm_userhostingkey.py new file mode 100644 index 00000000..75bf591a --- /dev/null +++ b/hosting/migrations/0028_managevm_userhostingkey.py @@ -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)), + ], + ), + ] diff --git a/hosting/migrations/0029_userhostingkey_created_at.py b/hosting/migrations/0029_userhostingkey_created_at.py new file mode 100644 index 00000000..6ab968fd --- /dev/null +++ b/hosting/migrations/0029_userhostingkey_created_at.py @@ -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, + ), + ] diff --git a/hosting/migrations/0030_userhostingkey_name.py b/hosting/migrations/0030_userhostingkey_name.py new file mode 100644 index 00000000..7405d66f --- /dev/null +++ b/hosting/migrations/0030_userhostingkey_name.py @@ -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, + ), + ] diff --git a/hosting/migrations/0031_auto_20170503_0554.py b/hosting/migrations/0031_auto_20170503_0554.py new file mode 100644 index 00000000..acf9ae62 --- /dev/null +++ b/hosting/migrations/0031_auto_20170503_0554.py @@ -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', + ), + ] diff --git a/hosting/migrations/0032_auto_20170504_0315.py b/hosting/migrations/0032_auto_20170504_0315.py new file mode 100644 index 00000000..c16b382a --- /dev/null +++ b/hosting/migrations/0032_auto_20170504_0315.py @@ -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, + ), + ] diff --git a/hosting/migrations/0033_virtualmachinetype_configuration.py b/hosting/migrations/0033_virtualmachinetype_configuration.py new file mode 100644 index 00000000..ecd6d5f4 --- /dev/null +++ b/hosting/migrations/0033_virtualmachinetype_configuration.py @@ -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), + ), + ] diff --git a/hosting/migrations/0034_auto_20170504_0331.py b/hosting/migrations/0034_auto_20170504_0331.py new file mode 100644 index 00000000..0dd66012 --- /dev/null +++ b/hosting/migrations/0034_auto_20170504_0331.py @@ -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'), + ), + ] diff --git a/hosting/mixins.py b/hosting/mixins.py index 2f8de3a5..404c4cb9 100644 --- a/hosting/mixins.py +++ b/hosting/mixins.py @@ -1,23 +1,32 @@ from django.shortcuts import redirect from django.core.urlresolvers import reverse -from .models import VirtualMachinePlan +from .models import VirtualMachinePlan, VirtualMachineType class ProcessVMSelectionMixin(object): def post(self, request, *args, **kwargs): - hosting = request.POST.get('configuration') - configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(hosting) - vm_specs = { - 'cores': request.POST.get('cores'), - 'memory': request.POST.get('memory'), - 'disk_size': request.POST.get('disk_space'), - 'hosting_company': request.POST.get('hosting_company'), - 'location_code': request.POST.get('location_code'), - 'configuration': hosting, - 'configuration_detail': configuration_detail, - 'final_price': request.POST.get('final_price') - } + configuration = request.POST.get('configuration') + configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration) + vm_template = request.POST.get('vm_template') + vm_type = VirtualMachineType.objects.get(id=vm_template) + vm_specs = vm_type.get_specs() + vm_specs.update({ + 'configuration_display': configuration_display, + 'configuration': configuration, + 'final_price': vm_type.final_price, + 'vm_template': vm_template + }) + # vm_specs = { + # # 'cores': request.POST.get('cores'), + # # 'memory': request.POST.get('memory'), + # # 'disk_size': request.POST.get('disk_space'), + # # 'hosting_company': request.POST.get('hosting_company'), + # # 'location_code': request.POST.get('location_code'), + # # 'configuration': hosting, + # # 'configuration_detail': configuration_detail, + # 'final_price': request.POST.get('final_price') + # } request.session['vm_specs'] = vm_specs if not request.user.is_authenticated(): request.session['vm_specs'] = vm_specs diff --git a/hosting/models.py b/hosting/models.py index 2daab9f8..ec746e22 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -7,7 +7,7 @@ from django.utils.functional import cached_property from Crypto.PublicKey import RSA from stored_messages.settings import stored_messages_settings -from membership.models import StripeCustomer +from membership.models import StripeCustomer, CustomUser from utils.models import BillingAddress from utils.mixins import AssignPermissionsMixin from .managers import VMPlansManager @@ -15,88 +15,71 @@ from .managers import VMPlansManager class VirtualMachineType(models.Model): - - BASE_PRICE = 0.5 - - HETZNER_NUG = 'hetzner_nug' - HETZNER = 'hetzner' - HETZNER_R6 = 'hetzner_raid6' - HETZNER_G = 'hetzner_glusterfs' - BERN = 'bern' - DE_LOCATION = 'DE' - CH_LOCATION = 'CH' - - HOSTING_TYPES = ( - (HETZNER_NUG, 'Hetzner No Uptime Guarantee'), - (HETZNER, 'Hetzner'), - (HETZNER_R6, 'Hetzner Raid6'), - (HETZNER_G, 'Hetzner Glusterfs'), - (BERN, 'Bern'), - ) - - LOCATIONS_CHOICES = ( - (DE_LOCATION, 'Germany'), - (CH_LOCATION, 'Switzerland'), - ) description = models.TextField() base_price = models.FloatField() memory_price = models.FloatField() core_price = models.FloatField() disk_size_price = models.FloatField() - hosting_company = models.CharField(max_length=30, choices=HOSTING_TYPES) - location = models.CharField(max_length=3, choices=LOCATIONS_CHOICES) + cores = models.IntegerField() + memory = models.IntegerField() + disk_size = models.IntegerField() def __str__(self): - return "%s" % (self.get_hosting_company_display()) + return "VM Type %s" % (self.id) + + @cached_property + def final_price(self): + price = self.cores * self.core_price + price += self.memory * self.memory_price + price += self.disk_size * self.disk_size_price + return price @classmethod def get_serialized_vm_types(cls): return [vm.get_serialized_data() for vm in cls.objects.all()] - # def calculate_price(self, specifications): - # price = float(specifications['cores']) * self.core_price - # price += float(specifications['memory']) * self.memory_price - # price += float(specifications['disk_size']) * self.disk_size_price - # price += self.base_price - # return price + def calculate_price(self): + price = self.cores * self.core_price + price += self.memory * self.memory_price + price += self.disk_size * self.disk_size_price + # price += self.base_price + return price - @classmethod - def get_price(cls, vm_template): - return cls.BASE_PRICE * vm_template + # @classmethod + # def get_price(cls, vm_template): + # return cls.BASE_PRICE * vm_template - @classmethod - def get_specs(cls, vm_template): + def get_specs(self): return { - 'memory': 1024 * vm_template, - 'cores': 0.1 * vm_template, - 'disk_size': 10000 * vm_template + '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 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 defeault_price(self): + # price = self.base_price + # price += self.core_price + # price += self.memory_price + # price += self.disk_size_price * 10 + # return price def get_serialized_data(self): return { 'description': self.description, - 'base_price': self.base_price, 'core_price': self.core_price, 'disk_size_price': self.disk_size_price, 'memory_price': self.memory_price, - 'hosting_company_name': self.get_hosting_company_display(), - 'hosting_company': self.hosting_company, - 'default_price': self.defeault_price(), - 'location_code': self.location, - 'location': self.get_location_display(), 'id': self.id, + 'final_price': self.final_price, + 'cores': self.cores, + 'memory': self.memory, + 'disk_size': self.disk_size + } @@ -112,14 +95,21 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): (CANCELED_STATUS, 'Canceled') ) - DJANGO = 'django' - RAILS = 'rails' - NODEJS = 'nodejs' + # DJANGO = 'django' + # RAILS = 'rails' + # NODEJS = 'nodejs' + + # VM_CONFIGURATION = ( + # (DJANGO, 'Ubuntu 14.04, Django'), + # (RAILS, 'Ubuntu 14.04, Rails'), + # (NODEJS, 'Debian, NodeJS'), + # ) VM_CONFIGURATION = ( - (DJANGO, 'Ubuntu 14.04, Django'), - (RAILS, 'Ubuntu 14.04, Rails'), - (NODEJS, 'Debian, NodeJS'), + ('debian', 'Debian 8'), + ('ubuntu', 'Ubuntu 16.06'), + ('devuan', 'Devuan 1'), + ('centos', 'CentOS 7') ) permissions = ('view_virtualmachineplan', @@ -129,7 +119,7 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): cores = models.IntegerField() memory = models.IntegerField() disk_size = models.IntegerField() - vm_type = models.ForeignKey(VirtualMachineType) + vm_type = models.ForeignKey(VirtualMachineType, null=True) price = models.FloatField() public_key = models.TextField(blank=True) status = models.CharField(max_length=20, choices=VM_STATUS_CHOICES, default=PENDING_STATUS) @@ -147,13 +137,13 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): def __str__(self): return self.name - @cached_property - def hosting_company_name(self): - return self.vm_type.get_hosting_company_display() + # @cached_property + # def hosting_company_name(self): + # return self.vm_type.get_hosting_company_display() - @cached_property - def location(self): - return self.vm_type.get_location_display() + # @cached_property + # def location(self): + # return self.vm_type.get_location_display() @cached_property def name(self): @@ -173,24 +163,6 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): instance.assign_permissions(user) return instance - @staticmethod - def generate_RSA(bits=2048): - ''' - Generate an RSA keypair with an exponent of 65537 in PEM format - param: bits The key length in bits - Return private key and public key - ''' - new_key = RSA.generate(2048, os.urandom) - public_key = new_key.publickey().exportKey("OpenSSH") - private_key = new_key.exportKey("PEM") - return private_key, public_key - - def generate_keys(self): - private_key, public_key = self.generate_RSA() - self.public_key = public_key - self.save(update_fields=['public_key']) - return private_key, public_key - def cancel_plan(self): self.status = self.CANCELED_STATUS self.save(update_fields=['status']) @@ -242,6 +214,32 @@ class HostingOrder(AssignPermissionsMixin, models.Model): self.save() +class UserHostingKey(models.Model): + user = models.ForeignKey(CustomUser) + public_key = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + name = models.CharField(max_length=100) + + @staticmethod + def generate_RSA(bits=2048): + ''' + Generate an RSA keypair with an exponent of 65537 in PEM format + param: bits The key length in bits + Return private key and public key + ''' + new_key = RSA.generate(2048, os.urandom) + public_key = new_key.publickey().exportKey("OpenSSH") + private_key = new_key.exportKey("PEM") + return private_key, public_key + + @classmethod + def generate_keys(cls): + private_key, public_key = cls.generate_RSA() + # self.public_key = public_key + # self.save(update_fields=['public_key']) + return private_key, public_key + + class ManageVM(models.Model): def has_add_permission(self, request): return False diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 1eacd26a..c6d1772e 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -72,6 +72,11 @@ {% trans "My Orders"%} +
  • + + {% trans "Keys"%} + +
  • {% trans "Notifications "%} diff --git a/hosting/templates/hosting/create_virtual_machine.html b/hosting/templates/hosting/create_virtual_machine.html new file mode 100644 index 00000000..8f85c486 --- /dev/null +++ b/hosting/templates/hosting/create_virtual_machine.html @@ -0,0 +1,45 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 i18n %} +{% block content %} +
    +
    +
    +
    +

    {% trans "New Virtual Machine"%}

    +
    + +
    + {% csrf_token %} +
    + Select VM Type: + +
    +
    + Select VM Configuration: + +
    +
    + +
    +
    + +
    + +
    +
    + +
    + +{%endblock%} \ No newline at end of file diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html index 284f0717..4c95a73e 100644 --- a/hosting/templates/hosting/includes/_pricing.html +++ b/hosting/templates/hosting/includes/_pricing.html @@ -24,59 +24,17 @@ {% csrf_token %} - +
      -
    • - -

      {{vm.location_code}}

      -
      - - -
    • +
    • - - {{vm.location}} -
      -
      -
    • -
    • - - {% if select_configuration %} - - {% else %} - - - -
      -
      - - {{configuration_detail}} -
      -
      - {% endif %} -
    • -
    • - -
      -
      - - +
      @@ -84,30 +42,28 @@
    • - - - GiB +
    • - - - GiB +
    • - -

      {{vm.default_price|floatformat}}CHF

      + + +
    • +
    • + +

      {{vm.final_price|floatformat}}CHF

      per month
    • diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 09e81ba2..5eb9bac1 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -65,7 +65,7 @@ {% url 'hosting:payment' as payment_url %} {% if payment_url in request.META.HTTP_REFERER %} {% endif %} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 6dd711ab..b0a09812 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -86,11 +86,11 @@

      Billing Amount


      -

      Type {{request.session.vm_specs.location_code}}

      -
      + +

      Cores {{request.session.vm_specs.cores}}


      -

      Configuration {{request.session.vm_specs.configuration_detail}}

      +

      Configuration {{request.session.vm_specs.configuration_display}}


      Memory {{request.session.vm_specs.memory}} GiB


      diff --git a/hosting/templates/hosting/virtual_machine_key.html b/hosting/templates/hosting/virtual_machine_key.html index c302576a..8a0221ae 100644 --- a/hosting/templates/hosting/virtual_machine_key.html +++ b/hosting/templates/hosting/virtual_machine_key.html @@ -6,30 +6,75 @@
      - -

      {% trans "SSH Private Key"%}

      +
      + {% csrf_token %} +

      {% trans "Access Key"%}


      + {% if not user_key %} +
      + {% trans "Upload your own key. "%} +
      +
      + + +
      +
      +
      + +
      + {% trans "Or generate a new key pair."%} + +
      +
      +
      + {% else %} +
      Use your created key to access to the machine. If you lost it, contact us.
      + +
      + + + + + + + + + + + + + + + +
      {% trans "Name"%}{% trans "Created at"%} {% trans "Status"%}
      {{user_key.name}}{{user_key.created_at}} + Active + +
      + {% endif %} +
      + {% if private_key %}
      {% trans "Warning!"%}{% 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."%}
      - - +
      -
      + {% else %} -
      + {% endif %} +
      @@ -42,11 +87,12 @@