commit
8f3125d2f7
15 changed files with 424 additions and 95 deletions
|
@ -69,7 +69,9 @@ class UserHostingKeyForm(forms.ModelForm):
|
||||||
# print(self.fields)
|
# print(self.fields)
|
||||||
|
|
||||||
def clean_name(self):
|
def clean_name(self):
|
||||||
return ''.join(random.choice(string.ascii_lowercase) for i in range(7))
|
return "dcl-priv-key-%s" % (
|
||||||
|
''.join(random.choice(string.ascii_lowercase) for i in range(7))
|
||||||
|
)
|
||||||
|
|
||||||
def clean_user(self):
|
def clean_user(self):
|
||||||
return self.request.user
|
return self.request.user
|
||||||
|
@ -77,8 +79,6 @@ class UserHostingKeyForm(forms.ModelForm):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = self.cleaned_data
|
cleaned_data = self.cleaned_data
|
||||||
|
|
||||||
print(cleaned_data)
|
|
||||||
|
|
||||||
if not cleaned_data.get('public_key'):
|
if not cleaned_data.get('public_key'):
|
||||||
private_key, public_key = UserHostingKey.generate_keys()
|
private_key, public_key = UserHostingKey.generate_keys()
|
||||||
cleaned_data.update({
|
cleaned_data.update({
|
||||||
|
|
21
hosting/migrations/0039_hostingorder_price.py
Normal file
21
hosting/migrations/0039_hostingorder_price.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2017-05-12 16:37
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('hosting', '0038_auto_20170512_1006'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='hostingorder',
|
||||||
|
name='price',
|
||||||
|
field=models.FloatField(default=0),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
import oca
|
import oca
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -19,9 +21,9 @@ from .managers import VMPlansManager
|
||||||
from oca.exceptions import OpenNebulaException
|
from oca.exceptions import OpenNebulaException
|
||||||
from oca.pool import WrongNameError
|
from oca.pool import WrongNameError
|
||||||
|
|
||||||
import logging
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HostingOrder(AssignPermissionsMixin, models.Model):
|
class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
|
|
||||||
ORDER_APPROVED_STATUS = 'Approved'
|
ORDER_APPROVED_STATUS = 'Approved'
|
||||||
|
@ -35,6 +37,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
last4 = models.CharField(max_length=4)
|
last4 = models.CharField(max_length=4)
|
||||||
cc_brand = models.CharField(max_length=10)
|
cc_brand = models.CharField(max_length=10)
|
||||||
stripe_charge_id = models.CharField(max_length=100, null=True)
|
stripe_charge_id = models.CharField(max_length=100, null=True)
|
||||||
|
price = models.FloatField()
|
||||||
|
|
||||||
permissions = ('view_hostingorder',)
|
permissions = ('view_hostingorder',)
|
||||||
|
|
||||||
|
@ -51,9 +54,13 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS
|
return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, vm_plan=None, customer=None, billing_address=None):
|
def create(cls, price=None, vm_id=None, customer=None, billing_address=None):
|
||||||
instance = cls.objects.create(vm_plan=vm_plan, customer=customer,
|
instance = cls.objects.create(
|
||||||
billing_address=billing_address)
|
price=price,
|
||||||
|
vm_id=vm_id,
|
||||||
|
customer=customer,
|
||||||
|
billing_address=billing_address
|
||||||
|
)
|
||||||
instance.assign_permissions(customer.user)
|
instance.assign_permissions(customer.user)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@ -67,6 +74,12 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
|
||||||
self.cc_brand = stripe_charge.source.brand
|
self.cc_brand = stripe_charge.source.brand
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def get_cc_data(self):
|
||||||
|
return {
|
||||||
|
'last4': self.last4,
|
||||||
|
'cc_brand': self.cc_brand,
|
||||||
|
} if self.last4 and self.cc_brand else None
|
||||||
|
|
||||||
|
|
||||||
class UserHostingKey(models.Model):
|
class UserHostingKey(models.Model):
|
||||||
user = models.ForeignKey(CustomUser)
|
user = models.ForeignKey(CustomUser)
|
||||||
|
|
|
@ -35,7 +35,7 @@ class OpenNebulaManager:
|
||||||
'11': 'CLONING_FAILURE',
|
'11': 'CLONING_FAILURE',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, email=None, password=None, create_user=True):
|
def __init__(self, email=None, password=None):
|
||||||
|
|
||||||
# Get oneadmin client
|
# Get oneadmin client
|
||||||
self.oneadmin_client = self._get_opennebula_client(
|
self.oneadmin_client = self._get_opennebula_client(
|
||||||
|
@ -43,9 +43,6 @@ class OpenNebulaManager:
|
||||||
settings.OPENNEBULA_PASSWORD
|
settings.OPENNEBULA_PASSWORD
|
||||||
)
|
)
|
||||||
|
|
||||||
if not create_user:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get or create oppenebula user using given credentials
|
# Get or create oppenebula user using given credentials
|
||||||
self.opennebula_user = self._get_or_create_user(
|
self.opennebula_user = self._get_or_create_user(
|
||||||
email,
|
email,
|
||||||
|
@ -121,9 +118,17 @@ class OpenNebulaManager:
|
||||||
|
|
||||||
return vm_data
|
return vm_data
|
||||||
|
|
||||||
|
def change_user_password(self, new_password):
|
||||||
|
self.oneadmin_client.call(
|
||||||
|
oca.User.METHODS['passwd'],
|
||||||
|
self.opennebula_user.id,
|
||||||
|
new_password
|
||||||
|
)
|
||||||
|
|
||||||
def create_vm(self, specs):
|
def create_vm(self, specs):
|
||||||
vm_id = None
|
vm_id = None
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# We do have the vm_template param set. Get and parse it
|
# We do have the vm_template param set. Get and parse it
|
||||||
# and check it to be in the desired range.
|
# and check it to be in the desired range.
|
||||||
# We have 8 possible VM templates for the moment which are 1x, 2x, 4x ...
|
# We have 8 possible VM templates for the moment which are 1x, 2x, 4x ...
|
||||||
|
@ -136,6 +141,9 @@ class OpenNebulaManager:
|
||||||
<TYPE>{disk_type}</TYPE>
|
<TYPE>{disk_type}</TYPE>
|
||||||
<SIZE>{size}</SIZE>
|
<SIZE>{size}</SIZE>
|
||||||
</DISK>
|
</DISK>
|
||||||
|
<CONTEXT>
|
||||||
|
<SSH_PUBLIC_KEY>{ssh_key}</SSH_PUBLIC_KEY>
|
||||||
|
</CONTEXT>
|
||||||
</VM>
|
</VM>
|
||||||
"""
|
"""
|
||||||
vm_id = oca.VirtualMachine.allocate(
|
vm_id = oca.VirtualMachine.allocate(
|
||||||
|
@ -145,7 +153,8 @@ class OpenNebulaManager:
|
||||||
vcpu=specs.get('cores'),
|
vcpu=specs.get('cores'),
|
||||||
cpu=0.1 * specs.get('cores'),
|
cpu=0.1 * specs.get('cores'),
|
||||||
disk_type='fs',
|
disk_type='fs',
|
||||||
size=10000 * specs.get('disk_size')
|
size=10000 * specs.get('disk_size'),
|
||||||
|
ssh_key=specs.get('ssh_key')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -155,10 +164,6 @@ class OpenNebulaManager:
|
||||||
self.opennebula_user.id,
|
self.opennebula_user.id,
|
||||||
self.opennebula_user.group_ids[0]
|
self.opennebula_user.group_ids[0]
|
||||||
)
|
)
|
||||||
# oca.VirtualMachine.chown(
|
|
||||||
# vm_id,
|
|
||||||
|
|
||||||
# )
|
|
||||||
|
|
||||||
except socket.timeout as socket_err:
|
except socket.timeout as socket_err:
|
||||||
logger.error("Socket timeout error: {0}".format(socket_err))
|
logger.error("Socket timeout error: {0}".format(socket_err))
|
||||||
|
@ -171,6 +176,34 @@ class OpenNebulaManager:
|
||||||
|
|
||||||
return vm_id
|
return vm_id
|
||||||
|
|
||||||
|
def terminate_vm(self, vm_id):
|
||||||
|
|
||||||
|
TERMINATE_ACTION = 'terminate'
|
||||||
|
vm_terminated = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.oneadmin_client.call(
|
||||||
|
oca.VirtualMachine.METHODS['action'],
|
||||||
|
TERMINATE_ACTION,
|
||||||
|
int(vm_id),
|
||||||
|
)
|
||||||
|
vm_terminated = True
|
||||||
|
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_terminated
|
||||||
|
|
||||||
|
def get_vm_templates(self):
|
||||||
|
template_pool = oca.VmTemplatePool(self.oneadmin_client)
|
||||||
|
template_pool.info()
|
||||||
|
return template_pool
|
||||||
|
|
||||||
def get_vm(self, email, vm_id):
|
def get_vm(self, email, vm_id):
|
||||||
# Get vm's
|
# Get vm's
|
||||||
vms = self.get_vms(email)
|
vms = self.get_vms(email)
|
||||||
|
@ -198,9 +231,6 @@ class OpenNebulaManager:
|
||||||
|
|
||||||
return vm_pool
|
return vm_pool
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HostingManageVMAdmin(admin.ModelAdmin):
|
class HostingManageVMAdmin(admin.ModelAdmin):
|
||||||
client = None
|
client = None
|
||||||
oneadmin_client = None
|
oneadmin_client = None
|
||||||
|
|
|
@ -25,6 +25,27 @@ $( document ).ready(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var hasCreditcard = window.hasCreditcard || false;
|
||||||
|
console.log("has creditcard", hasCreditcard);
|
||||||
|
// hasCreditcard= true;
|
||||||
|
|
||||||
|
var submit_form_btn = $('#payment_button_with_creditcard');
|
||||||
|
submit_form_btn.on('click', submit_payment);
|
||||||
|
|
||||||
|
|
||||||
|
function submit_payment(e){
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("creditcard sdasd");
|
||||||
|
// if (hasCreditcard) {
|
||||||
|
$('#billing-form').submit();
|
||||||
|
console.log("has creditcard2");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// $form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var $form = $('#payment-form');
|
var $form = $('#payment-form');
|
||||||
$form.submit(payWithStripe);
|
$form.submit(payWithStripe);
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,14 @@
|
||||||
|
|
||||||
<option value="{{template.id}}">
|
<option value="{{template.id}}">
|
||||||
CORE: {{template.cores}},
|
CORE: {{template.cores}},
|
||||||
RAM: {{template.memory}},r
|
RAM: {{template.memory}} GiB,
|
||||||
SSD: {{template.disk_size}}
|
SSD: {{template.disk_size}} GiB
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<!-- <div class="form-group">
|
||||||
Select VM Configuration:
|
Select VM Configuration:
|
||||||
<select name="configuration">
|
<select name="configuration">
|
||||||
{% for config in configuration_options %}
|
{% for config in configuration_options %}
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-success" >{% trans "Start VM"%} </button>
|
<button class="btn btn-success" >{% trans "Start VM"%} </button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="col-xs-12 col-md-4 col-md-offset-2 billing">
|
<div class="col-xs-12 col-md-4 col-md-offset-2 billing">
|
||||||
<h3><b>Billing Address</b></h3>
|
<h3><b>Billing Address</b></h3>
|
||||||
<hr>
|
<hr>
|
||||||
<form role="form" id="billing-form" method="post" action="{% url 'hosting:payment' %}" novalidate>
|
<form role="form" id="billing-form" method="post" action="" novalidate>
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_field field show_label=False type='fields'%}
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
@ -23,6 +23,17 @@
|
||||||
<hr>
|
<hr>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
|
{% if credit_card_data.last4 %}
|
||||||
|
<form role="form" id="payment-form-with-creditcard"novalidate>
|
||||||
|
<h5 class="billing-head">Credit Card</h5>
|
||||||
|
<h5 class="membership-lead">Last 4: *****{{credit_card_data.last4}}</h5>
|
||||||
|
<h5 class="membership-lead">Type: {{credit_card_data.cc_brand}}</h5>
|
||||||
|
<input type="hidden" name="credit_card_needed" value="false"/>
|
||||||
|
</form>
|
||||||
|
<button id="payment_button_with_creditcard" class="btn btn-success btn-lg btn-block" type="submit">Submit Payment</button>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
|
||||||
<form role="form" id="payment-form" novalidate>
|
<form role="form" id="payment-form" novalidate>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-9 col-md-12">
|
<div class="col-xs-9 col-md-12">
|
||||||
|
@ -76,6 +87,7 @@
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -109,6 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- stripe key data -->
|
<!-- stripe key data -->
|
||||||
{% if stripe_key %}
|
{% if stripe_key %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -116,6 +129,13 @@
|
||||||
</script>
|
</script>
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
|
||||||
|
{% if credit_card_data.last4 and credit_card_data.cc_brand %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {window.hasCreditcard = true;})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{%endif%}
|
||||||
|
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="well text-center">
|
<div class="well text-center">
|
||||||
<i class="fa fa-hdd-o" aria-hidden="true"></i> {% trans "Disk"%} <br/>
|
<i class="fa fa-hdd-o" aria-hidden="true"></i> {% trans "Disk"%} <br/>
|
||||||
<span class="label label-success">{{virtual_machine.disk_size}} GiB</span>
|
<span class="label label-success">{{virtual_machine.disk_size|floatformat:2}} GiB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!--/row-->
|
</div><!--/row-->
|
||||||
|
@ -107,6 +107,7 @@
|
||||||
<div class="row ">
|
<div class="row ">
|
||||||
<div class="col-md-12 inline-headers">
|
<div class="col-md-12 inline-headers">
|
||||||
<h3>{% trans "Current status"%}</h3>
|
<h3>{% trans "Current status"%}</h3>
|
||||||
|
|
||||||
<div class="pull-right space-above">
|
<div class="pull-right space-above">
|
||||||
{% if virtual_machine.state == 'PENDING' %}
|
{% if virtual_machine.state == 'PENDING' %}
|
||||||
<span class="label
|
<span class="label
|
||||||
|
@ -130,17 +131,28 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button type="text"
|
<button type="text" data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-danger">{% trans "Terminate Virtual Machine"%}</button>
|
||||||
data-href="{% url 'hosting:virtual_machines' virtual_machine.vm_id %}" data-toggle="modal" data-target="#confirm-cancel" class="btn btn-danger">{% trans "Cancel Virtual Machine"%}</button>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<br/>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% for message in messages %}
|
||||||
|
<span>{{ message }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Cancel Modal -->
|
<!-- Cancel Modal -->
|
||||||
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade" id="confirm-cancel" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
{% trans "Cancel your Virtual Machine"%}
|
{% trans "Terminate your Virtual Machine"%}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{% trans "Are you sure do you want to cancel your Virtual Machine "%} {{virtual_machine.name}} {% trans "plan?"%}
|
{% trans "Are you sure do you want to cancel your Virtual Machine "%} {{virtual_machine.name}} {% trans "plan?"%}
|
||||||
|
|
|
@ -9,11 +9,18 @@
|
||||||
<form method="POST" action="" >
|
<form method="POST" action="" >
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3>
|
<h3><i class="fa fa-key" aria-hidden="true"></i>{% trans "Access Key"%} </h3>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% for message in messages %}
|
||||||
|
<span>{{ message }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<hr/>
|
<hr/>
|
||||||
{% if not user_key %}
|
{% if not user_key %}
|
||||||
<div class="alert alert-warning">
|
<h3>
|
||||||
{% trans "Upload your own key. "%}
|
{% trans "Upload your own key. "%}
|
||||||
</div>
|
</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="comment">Paste here your public key</label>
|
<label for="comment">Paste here your public key</label>
|
||||||
<textarea class="form-control" rows="6" name="public_key"></textarea>
|
<textarea class="form-control" rows="6" name="public_key"></textarea>
|
||||||
|
@ -22,10 +29,10 @@
|
||||||
<button class="btn btn-success">{% trans "Upload Key"%} </a>
|
<button class="btn btn-success">{% trans "Upload Key"%} </a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-warning">
|
<h3>
|
||||||
{% trans "Or generate a new key pair."%}
|
{% trans "Or generate a new key pair."%}
|
||||||
|
|
||||||
</div>
|
</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-success">{% trans "Generate Key Pair"%} </a>
|
<button class="btn btn-success">{% trans "Generate Key Pair"%} </a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +65,7 @@
|
||||||
{% 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 download your SSH private key once. Don't lost your key"%}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<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" type="hidden" style="display:none">{{private_key}}</textarea>
|
||||||
|
@ -101,6 +108,7 @@
|
||||||
// Remove anchor from body
|
// Remove anchor from body
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,16 @@
|
||||||
<a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>
|
<a class="btn btn-success" href="{% url 'hosting:create-virtual-machine' %}" >{% trans "Create VM"%} </a>
|
||||||
</p>
|
</p>
|
||||||
<br/>
|
<br/>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<br/>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% for message in messages %}
|
||||||
|
<span>{{ message }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "ID"%}</th>
|
<th>{% trans "ID"%}</th>
|
||||||
|
@ -28,7 +38,7 @@
|
||||||
|
|
||||||
{% if vm.state == 'ACTIVE' %}
|
{% if vm.state == 'ACTIVE' %}
|
||||||
<span class="h3 label label-success"><strong> {{vm.state}}</strong></span>
|
<span class="h3 label label-success"><strong> {{vm.state}}</strong></span>
|
||||||
{% elif vm.state == 'POWEROFF' %}
|
{% elif vm.state == 'FAILED' %}
|
||||||
<span class="h3 label label-danger"><strong>{{vm.state}}</strong></span>
|
<span class="h3 label label-danger"><strong>{{vm.state}}</strong></span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="h3 label label-warning"><strong>{{vm.state}}</strong></span>
|
<span class="h3 label label-warning"><strong>{{vm.state}}</strong></span>
|
||||||
|
|
247
hosting/views.py
247
hosting/views.py
|
@ -11,7 +11,8 @@ 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 django.shortcuts import redirect
|
||||||
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
|
||||||
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
|
||||||
|
@ -21,7 +22,7 @@ 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, UserBillingAddressForm
|
||||||
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 HostingOrder, HostingBill, UserHostingKey
|
from .models import HostingOrder, HostingBill, UserHostingKey
|
||||||
|
@ -162,7 +163,7 @@ class SignupView(CreateView):
|
||||||
model = CustomUser
|
model = CustomUser
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
next_url = self.request.session.get('next', reverse_lazy('hosting:signup'))
|
next_url = self.request.session.get('next', reverse_lazy('hosting:virtual_machines'))
|
||||||
return next_url
|
return next_url
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
@ -188,6 +189,43 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
|
||||||
template_name = 'hosting/confirm_reset_password.html'
|
template_name = 'hosting/confirm_reset_password.html'
|
||||||
success_url = reverse_lazy('hosting:login')
|
success_url = reverse_lazy('hosting:login')
|
||||||
|
|
||||||
|
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
||||||
|
try:
|
||||||
|
uid = urlsafe_base64_decode(uidb64)
|
||||||
|
user = CustomUser.objects.get(pk=uid)
|
||||||
|
|
||||||
|
opennebula_client = OpenNebulaManager(
|
||||||
|
email=user.email,
|
||||||
|
password=user.password,
|
||||||
|
)
|
||||||
|
|
||||||
|
except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
|
||||||
|
user = None
|
||||||
|
opennebula_client = None
|
||||||
|
|
||||||
|
form = self.form_class(request.POST)
|
||||||
|
|
||||||
|
if user is not None and default_token_generator.check_token(user, token):
|
||||||
|
if form.is_valid():
|
||||||
|
new_password = form.cleaned_data['new_password2']
|
||||||
|
user.set_password(new_password)
|
||||||
|
user.save()
|
||||||
|
messages.success(request, 'Password has been reset.')
|
||||||
|
|
||||||
|
# Change opennebula password
|
||||||
|
opennebula_client.change_user_password(new_password)
|
||||||
|
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
messages.error(request, 'Password reset has not been successful.')
|
||||||
|
form.add_error(None, 'Password reset has not been successful.')
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
else:
|
||||||
|
messages.error(request, 'The reset password link is no longer valid.')
|
||||||
|
form.add_error(None, 'The reset password link is no longer valid.')
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class NotificationsView(LoginRequiredMixin, TemplateView):
|
class NotificationsView(LoginRequiredMixin, TemplateView):
|
||||||
template_name = 'hosting/notifications.html'
|
template_name = 'hosting/notifications.html'
|
||||||
|
@ -228,18 +266,19 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView):
|
||||||
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(
|
context = super(
|
||||||
GenerateVMSSHKeysView,
|
GenerateVMSSHKeysView,
|
||||||
self
|
self
|
||||||
).get_context_data(**kwargs)
|
).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user_key = UserHostingKey.objects.get(
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
except UserHostingKey.DoesNotExist:
|
||||||
|
user_key = None
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'user_key': user_key
|
'user_key': user_key
|
||||||
})
|
})
|
||||||
|
@ -258,32 +297,98 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView):
|
||||||
if form.cleaned_data.get('private_key'):
|
if form.cleaned_data.get('private_key'):
|
||||||
context.update({
|
context.update({
|
||||||
'private_key': form.cleaned_data.get('private_key'),
|
'private_key': form.cleaned_data.get('private_key'),
|
||||||
'key_name': form.cleaned_data.get('name')
|
'key_name': form.cleaned_data.get('name'),
|
||||||
|
'form': UserHostingKeyForm(request=self.request)
|
||||||
})
|
})
|
||||||
|
|
||||||
# print("form", form.cleaned_data)
|
# return HttpResponseRedirect(reverse('hosting:key_pair'))
|
||||||
|
|
||||||
return render(self.request, self.template_name, context)
|
return render(self.request, self.template_name, context)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
try:
|
||||||
|
UserHostingKey.objects.get(
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(reverse('hosting:key_pair'))
|
||||||
|
|
||||||
|
except UserHostingKey.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid():
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class PaymentVMView(LoginRequiredMixin, FormView):
|
class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
template_name = 'hosting/payment.html'
|
template_name = 'hosting/payment.html'
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
form_class = BillingAddressForm
|
form_class = BillingAddressForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
current_billing_address = self.request.user.billing_addresses.first()
|
||||||
|
form_kwargs = super(PaymentVMView, self).get_form_kwargs()
|
||||||
|
if not current_billing_address:
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
|
form_kwargs.update({
|
||||||
|
'initial': {
|
||||||
|
'street_address': current_billing_address.street_address,
|
||||||
|
'city': current_billing_address.city,
|
||||||
|
'postal_code': current_billing_address.postal_code,
|
||||||
|
'country': current_billing_address.country,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(PaymentVMView, self).get_context_data(**kwargs)
|
context = super(PaymentVMView, self).get_context_data(**kwargs)
|
||||||
|
# Get user
|
||||||
|
user = self.request.user
|
||||||
|
|
||||||
|
# Get user last order
|
||||||
|
last_hosting_order = HostingOrder.objects.filter(customer__user=user).last()
|
||||||
|
|
||||||
|
# If user has already an hosting order, get the credit card data from it
|
||||||
|
if last_hosting_order:
|
||||||
|
credit_card_data = last_hosting_order.get_cc_data()
|
||||||
|
context.update({
|
||||||
|
'credit_card_data': credit_card_data if credit_card_data else None,
|
||||||
|
})
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||||
})
|
})
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
try:
|
||||||
|
UserHostingKey.objects.get(
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
|
except UserHostingKey.DoesNotExist:
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
'In order to create a VM, you create/upload your SSH KEY first.'
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(reverse('hosting:key_pair'))
|
||||||
|
|
||||||
|
return self.render_to_response(self.get_context_data())
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
|
|
||||||
|
# Get billing address data
|
||||||
|
billing_address_data = form.cleaned_data
|
||||||
|
|
||||||
context = self.get_context_data()
|
context = self.get_context_data()
|
||||||
|
|
||||||
specifications = request.session.get('template')
|
specifications = request.session.get('template')
|
||||||
|
|
||||||
vm_template_id = specifications.get('id', 1)
|
vm_template_id = specifications.get('id', 1)
|
||||||
|
@ -321,20 +426,45 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
charge = charge_response.get('response_object')
|
charge = charge_response.get('response_object')
|
||||||
|
|
||||||
# Create OpenNebulaManager
|
# Create OpenNebulaManager
|
||||||
|
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
template = manager.get_template(vm_template_id)
|
template = manager.get_template(vm_template_id)
|
||||||
|
|
||||||
|
# Get user ssh key
|
||||||
|
try:
|
||||||
|
user_key = UserHostingKey.objects.get(
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
except UserHostingKey.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
# Create a vm using logged user
|
# Create a vm using logged user
|
||||||
vm_id = manager.create_vm(vm_template_id)
|
vm_id = manager.create_vm(
|
||||||
|
vm_template_id,
|
||||||
|
ssh_key=user_key.public_key
|
||||||
|
)
|
||||||
|
|
||||||
# Create a Hosting Order
|
# Create a Hosting Order
|
||||||
order = HostingOrder.create(vm_id=vm_id, customer=customer,
|
order = HostingOrder.create(
|
||||||
billing_address=billing_address)
|
price=final_price,
|
||||||
|
vm_id=vm_id,
|
||||||
|
customer=customer,
|
||||||
|
billing_address=billing_address
|
||||||
|
)
|
||||||
|
|
||||||
# Create a Hosting Bill
|
# Create a Hosting Bill
|
||||||
bill = HostingBill.create(customer=customer, billing_address=billing_address)
|
bill = HostingBill.create(customer=customer, billing_address=billing_address)
|
||||||
|
|
||||||
|
# Create Billing Address for User if he does not have one
|
||||||
|
if not customer.user.billing_addresses.count():
|
||||||
|
billing_address_data.update({
|
||||||
|
'user': customer.user.id
|
||||||
|
})
|
||||||
|
billing_address_user_form = UserBillingAddressForm(billing_address_data)
|
||||||
|
billing_address_user_form.is_valid()
|
||||||
|
billing_address_user_form.save()
|
||||||
|
|
||||||
# Associate an order with a stripe payment
|
# Associate an order with a stripe payment
|
||||||
order.set_stripe_charge(charge)
|
order.set_stripe_charge(charge)
|
||||||
|
@ -344,7 +474,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
|
|
||||||
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
|
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
|
||||||
|
|
||||||
|
|
||||||
# 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': vm,
|
'vm': vm,
|
||||||
|
@ -380,10 +509,11 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
vm = manager.get_vm(obj.vm_id)
|
vm = manager.get_vm(obj.vm_id)
|
||||||
context['vm'] = VirtualMachineSerializer(vm).data
|
context['vm'] = VirtualMachineSerializer(vm).data
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class OrdersHostingListView(LoginRequiredMixin, ListView):
|
class OrdersHostingListView(LoginRequiredMixin, ListView):
|
||||||
|
@ -416,7 +546,7 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
queryset = manager.get_vms()
|
queryset = manager.get_vms()
|
||||||
serializer = VirtualMachineSerializer(queryset, many=True)
|
serializer = VirtualMachineSerializer(queryset, many=True)
|
||||||
|
@ -428,28 +558,32 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
try:
|
||||||
|
UserHostingKey.objects.get(
|
||||||
|
user=self.request.user
|
||||||
|
)
|
||||||
|
except UserHostingKey.DoesNotExist:
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
'In order to create a VM, you need to create/upload your SSH KEY first.'
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(reverse('hosting:key_pair'))
|
||||||
|
|
||||||
#TODO: Replace with OpenNebulaManager.get_apps
|
#TODO: Replace with OpenNebulaManager.get_apps
|
||||||
templates = OpenNebulaManager().get_templates()
|
templates = OpenNebulaManager().get_templates()
|
||||||
data = VirtualMachineTemplateSerializer(templates, many=True).data
|
data = VirtualMachineTemplateSerializer(templates, many=True).data
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'templates': data,
|
'templates': data,
|
||||||
#'configuration_options': VirtualMachinePlan.VM_CONFIGURATION
|
|
||||||
}
|
}
|
||||||
# context = {}
|
# context = {}
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
#XXX: Fix this!
|
|
||||||
#configuration = request.POST.get('configuration')
|
|
||||||
#configuration_display = dict(VirtualMachinePlan.VM_CONFIGURATION).get(configuration)
|
|
||||||
template_id = int(request.POST.get('vm_template_id'))
|
template_id = int(request.POST.get('vm_template_id'))
|
||||||
template = OpenNebulaManager().get_template(template_id)
|
template = OpenNebulaManager().get_template(template_id)
|
||||||
data = VirtualMachineTemplateSerializer(template).data
|
data = VirtualMachineTemplateSerializer(template).data
|
||||||
vm_specs = {
|
|
||||||
#'configuration_display': configuration_display,
|
|
||||||
#'configuration': configuration,
|
|
||||||
'template': data,
|
|
||||||
}
|
|
||||||
request.session['template'] = data
|
request.session['template'] = data
|
||||||
return redirect(reverse('hosting:payment'))
|
return redirect(reverse('hosting:payment'))
|
||||||
|
|
||||||
|
@ -458,28 +592,56 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
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')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get_object(self):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
vm = None
|
||||||
password=owner.password[0:20],
|
manager = OpenNebulaManager(
|
||||||
create_user=True)
|
email=owner.email,
|
||||||
|
password=owner.password,
|
||||||
|
create_user=True
|
||||||
|
)
|
||||||
vm_id = self.kwargs.get('pk')
|
vm_id = self.kwargs.get('pk')
|
||||||
try:
|
try:
|
||||||
vm = manager.get_vm(vm_id)
|
vm = manager.get_vm(vm_id)
|
||||||
serializer = VirtualMachineSerializer(vm)
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
print(error)
|
print(error)
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
return vm
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
final_url = reverse('hosting:virtual_machines')
|
||||||
|
return final_url
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
vm = self.get_object()
|
||||||
|
serializer = VirtualMachineSerializer(vm)
|
||||||
context = {
|
context = {
|
||||||
'virtual_machine': serializer.data,
|
'virtual_machine': serializer.data,
|
||||||
}
|
}
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
def post(self, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
#TODO: add api to OpenNebulaManager
|
owner = self.request.user
|
||||||
vm = self.get_object()
|
vm = self.get_object()
|
||||||
vm.cancel_plan()
|
|
||||||
|
opennebula_vm_id = self.kwargs.get('pk')
|
||||||
|
|
||||||
|
manager = OpenNebulaManager(
|
||||||
|
email=owner.email,
|
||||||
|
password=owner.password,
|
||||||
|
create_user=True
|
||||||
|
)
|
||||||
|
|
||||||
|
terminated = manager.delete_vm(
|
||||||
|
vm.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if not terminated:
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
'Error terminating VM %s' % (opennebula_vm_id)
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'vm': vm,
|
'vm': vm,
|
||||||
|
@ -495,8 +657,14 @@ class VirtualMachineView(LoginRequiredMixin, View):
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
|
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
'VM %s terminated successfully' % (opennebula_vm_id)
|
||||||
|
)
|
||||||
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
class HostingBillListView(LoginRequiredMixin, ListView):
|
class HostingBillListView(LoginRequiredMixin, ListView):
|
||||||
template_name = "hosting/bills.html"
|
template_name = "hosting/bills.html"
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
@ -505,6 +673,7 @@ class HostingBillListView(LoginRequiredMixin, ListView):
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
ordering = '-id'
|
ordering = '-id'
|
||||||
|
|
||||||
|
|
||||||
class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailView):
|
class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailView):
|
||||||
template_name = "hosting/bill_detail.html"
|
template_name = "hosting/bill_detail.html"
|
||||||
login_url = reverse_lazy('hosting:login')
|
login_url = reverse_lazy('hosting:login')
|
||||||
|
@ -526,7 +695,7 @@ class HostingBillDetailView(PermissionRequiredMixin, LoginRequiredMixin, DetailV
|
||||||
|
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
# Get vms
|
# Get vms
|
||||||
queryset = manager.get_vms()
|
queryset = manager.get_vms()
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import oca
|
import oca
|
||||||
import socket
|
import socket
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from oca.pool import WrongNameError
|
from oca.pool import WrongNameError
|
||||||
|
from oca.exceptions import OpenNebulaException
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class OpenNebulaManager():
|
class OpenNebulaManager():
|
||||||
"""This class represents an opennebula manager."""
|
"""This class represents an opennebula manager."""
|
||||||
|
@ -99,13 +103,16 @@ class OpenNebulaManager():
|
||||||
return vm_pool.get_by_id(int(vm_id))
|
return vm_pool.get_by_id(int(vm_id))
|
||||||
|
|
||||||
#TODO: get app with id
|
#TODO: get app with id
|
||||||
def create_vm(self, template_id, app_id=None):
|
def create_vm(self, template_id, app_id=None, ssh_key=None):
|
||||||
|
extra_template = "<CONTEXT><SSH_PUBLIC_KEY>{ssh_key}</SSH_PUBLIC_KEY></CONTEXT>".format(
|
||||||
|
ssh_key=ssh_key
|
||||||
|
)
|
||||||
vm_id = self.oneadmin_client.call(
|
vm_id = self.oneadmin_client.call(
|
||||||
oca.VmTemplate.METHODS['instantiate'],
|
oca.VmTemplate.METHODS['instantiate'],
|
||||||
template_id,
|
template_id,
|
||||||
'',
|
'',
|
||||||
False,
|
False,
|
||||||
''
|
extra_template
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
self.oneadmin_client.call(
|
self.oneadmin_client.call(
|
||||||
|
@ -119,11 +126,29 @@ class OpenNebulaManager():
|
||||||
return vm_id
|
return vm_id
|
||||||
|
|
||||||
def delete_vm(self, vm_id):
|
def delete_vm(self, vm_id):
|
||||||
|
TERMINATE_ACTION = 'terminate'
|
||||||
|
vm_terminated = False
|
||||||
|
try:
|
||||||
self.oneadmin_client.call(
|
self.oneadmin_client.call(
|
||||||
oca.VirtualMachine.METHODS['action'],
|
oca.VirtualMachine.METHODS['action'],
|
||||||
'terminate',
|
TERMINATE_ACTION,
|
||||||
int(vm_id)
|
int(vm_id),
|
||||||
)
|
)
|
||||||
|
vm_terminated = True
|
||||||
|
except socket.timeout as socket_err:
|
||||||
|
logger.info("Socket timeout error: {0}".format(socket_err))
|
||||||
|
print("Socket timeout error: {0}".format(socket_err))
|
||||||
|
except OpenNebulaException as opennebula_err:
|
||||||
|
logger.info("OpenNebulaException error: {0}".format(opennebula_err))
|
||||||
|
print("OpenNebulaException error: {0}".format(opennebula_err))
|
||||||
|
except OSError as os_err:
|
||||||
|
logger.info("OSError : {0}".format(os_err))
|
||||||
|
print("OSError : {0}".format(os_err))
|
||||||
|
except ValueError as value_err:
|
||||||
|
logger.info("ValueError : {0}".format(value_err))
|
||||||
|
print("ValueError : {0}".format(value_err))
|
||||||
|
|
||||||
|
return vm_terminated
|
||||||
|
|
||||||
def _get_template_pool(self):
|
def _get_template_pool(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -91,7 +91,7 @@ class VirtualMachineSerializer(serializers.Serializer):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user = True)
|
create_user = True)
|
||||||
opennebula_id = manager.create_vm(template_id)
|
opennebula_id = manager.create_vm(template_id)
|
||||||
except OpenNebulaException as err:
|
except OpenNebulaException as err:
|
||||||
|
|
|
@ -47,7 +47,7 @@ class VmCreateView(generics.ListCreateAPIView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
return manager.get_vms()
|
return manager.get_vms()
|
||||||
|
|
||||||
|
@ -64,21 +64,21 @@ class VmDetailsView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
return manager.get_vms()
|
return manager.get_vms()
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user=True)
|
create_user=True)
|
||||||
return manager.get_vm(self.kwargs.get('pk'))
|
return manager.get_vm(self.kwargs.get('pk'))
|
||||||
|
|
||||||
def perform_destroy(self, instance):
|
def perform_destroy(self, instance):
|
||||||
owner = self.request.user
|
owner = self.request.user
|
||||||
manager = OpenNebulaManager(email=owner.email,
|
manager = OpenNebulaManager(email=owner.email,
|
||||||
password=owner.password[0:20],
|
password=owner.password,
|
||||||
create_user = True)
|
create_user = True)
|
||||||
manager.delete_vm(instance.id)
|
manager.delete_vm(instance.id)
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ class EditCreditCardForm(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class BillingAddressForm(forms.ModelForm):
|
class BillingAddressForm(forms.ModelForm):
|
||||||
token = forms.CharField(widget=forms.HiddenInput())
|
token = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BillingAddress
|
model = BillingAddress
|
||||||
|
|
Loading…
Reference in a new issue