This commit is contained in:
Noe Thalheim 2017-05-25 18:38:58 +02:00
commit 1ff411e426
9 changed files with 228 additions and 135 deletions

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-20 11:12-0500\n" "POT-Creation-Date: 2017-05-23 17:26-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,6 +18,20 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: templates/datacenterlight/beta_access.html:21
msgid "Request Beta Access"
msgstr "Beantrage Beta-Zugang"
#: templates/datacenterlight/beta_success.html:9
#, fuzzy
#| msgid "Request Beta Access"
msgid "Request Sent"
msgstr "Anfrage verschickt"
#: templates/datacenterlight/beta_success.html:12
msgid "Thank you, we will contact you as soon as possible"
msgstr "Vielen Dank, wir werden Sie sobald als möglich kontaktieren."
#: templates/datacenterlight/emails/request_access_confirmation.html:99 #: templates/datacenterlight/emails/request_access_confirmation.html:99
#: templates/datacenterlight/emails/request_access_confirmation.txt:99 #: templates/datacenterlight/emails/request_access_confirmation.txt:99
msgid "Thank you for your request." msgid "Thank you for your request."
@ -52,27 +66,35 @@ msgid "Thank you!"
msgstr "Vielen Dank!" msgstr "Vielen Dank!"
#: templates/datacenterlight/index.html:62 #: templates/datacenterlight/index.html:62
#: templates/datacenterlight/pricing.html:62
msgid "What is it" msgid "What is it"
msgstr "Was ist es?" msgstr "Was ist es?"
#: templates/datacenterlight/index.html:65 #: templates/datacenterlight/index.html:65
#: templates/datacenterlight/index.html:171 #: templates/datacenterlight/index.html:171
#: templates/datacenterlight/index.html:362 #: templates/datacenterlight/index.html:331
#: templates/datacenterlight/pricing.html:65
#: templates/datacenterlight/pricing.html:188
msgid "Scale out" msgid "Scale out"
msgstr "Skalierung" msgstr "Skalierung"
#: templates/datacenterlight/index.html:68 #: templates/datacenterlight/index.html:68
#: templates/datacenterlight/index.html:197 #: templates/datacenterlight/index.html:197
#: templates/datacenterlight/index.html:365 #: templates/datacenterlight/index.html:334
#: templates/datacenterlight/pricing.html:68
#: templates/datacenterlight/pricing.html:191
msgid "Reliable and light" msgid "Reliable and light"
msgstr "Zuverlässig und leicht" msgstr "Zuverlässig und leicht"
#: templates/datacenterlight/index.html:71 #: templates/datacenterlight/index.html:71
#: templates/datacenterlight/pricing.html:71
msgid "Buy VM" msgid "Buy VM"
msgstr "VM Kaufen" msgstr "VM Kaufen"
#: templates/datacenterlight/index.html:74 #: templates/datacenterlight/index.html:74
#: templates/datacenterlight/index.html:372 #: templates/datacenterlight/index.html:341
#: templates/datacenterlight/pricing.html:74
#: templates/datacenterlight/pricing.html:198
msgid "Contact" msgid "Contact"
msgstr "Kontakt" msgstr "Kontakt"
@ -89,7 +111,8 @@ msgid "I want it!"
msgstr "Das will ich haben!" msgstr "Das will ich haben!"
#: templates/datacenterlight/index.html:142 #: templates/datacenterlight/index.html:142
#: templates/datacenterlight/index.html:359 #: templates/datacenterlight/index.html:328
#: templates/datacenterlight/pricing.html:185
msgid "How it works" msgid "How it works"
msgstr "Wie es funktioniert" msgstr "Wie es funktioniert"
@ -132,6 +155,7 @@ msgstr ""
"Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig." "Angebot ist aufgrund unserer leichten Infrastruktur überaus kostengünstig."
#: templates/datacenterlight/index.html:218 #: templates/datacenterlight/index.html:218
#: templates/datacenterlight/pricing.html:101
msgid "We are cutting down the costs significantly!" msgid "We are cutting down the costs significantly!"
msgstr "Wir sorgen dafür, dass die Kosten für Sie signifikant abnehmen" msgstr "Wir sorgen dafür, dass die Kosten für Sie signifikant abnehmen"
@ -143,60 +167,70 @@ msgstr "Bezahlbares VM Hosting in der Schweiz"
msgid "More Info" msgid "More Info"
msgstr "Weitere Informationen" msgstr "Weitere Informationen"
#: templates/datacenterlight/index.html:226 #: templates/datacenterlight/index.html:227
#: templates/datacenterlight/pricing.html:114
msgid "VM hosting" msgid "VM hosting"
msgstr "" msgstr ""
#: templates/datacenterlight/index.html:233 #: templates/datacenterlight/index.html:234
msgid "Based in Switzerland" msgid "Based in Switzerland"
msgstr "Standort des Datacenters ist in der Schweiz" msgstr "Standort des Datacenters ist in der Schweiz"
#: templates/datacenterlight/index.html:242 #: templates/datacenterlight/index.html:243
msgid "15 GiB storage(SSD)" msgid "15 GB Storage (SSD)"
msgstr "" msgstr "15 GB Storage (SSD)"
#: templates/datacenterlight/index.html:245 #: templates/datacenterlight/index.html:246
msgid "Buy Now!" #: templates/datacenterlight/pricing.html:156
msgstr "Kaufe jetzt!" msgid "Order Now!"
msgstr "Bestelle jetzt!"
#: templates/datacenterlight/index.html:259 #: templates/datacenterlight/index.html:262
msgid "I want to try!" msgid "Want to know more? Subscribe to our newsletter!"
msgstr "Das möchte ich haben" msgstr "Willst du mehr wissen? Abonniere unseren Newsletter!"
#: templates/datacenterlight/index.html:281
msgid "Request Beta Access"
msgstr "Beantrage Beta-Zugang"
#: templates/datacenterlight/index.html:289 #: templates/datacenterlight/index.html:289
#, fuzzy
#| msgid "Request Beta Access"
msgid "Request Sent"
msgstr "Anfrage verschickt"
#: templates/datacenterlight/index.html:292
msgid "Thank you, we will contact you as soon as possible"
msgstr "Vielen Dank, wir werden Sie sobald als möglich kontaktieren."
#: templates/datacenterlight/index.html:320
msgid "Switzerland " msgid "Switzerland "
msgstr "Schweiz" msgstr "Schweiz"
#: templates/datacenterlight/index.html:337 #: templates/datacenterlight/index.html:306
msgid "Questions?" msgid "Questions?"
msgstr "Fragen?" msgstr "Fragen?"
#: templates/datacenterlight/index.html:337 #: templates/datacenterlight/index.html:306
msgid "Contact us!" msgid "Contact us!"
msgstr "Kontaktiere uns!" msgstr "Kontaktiere uns!"
#: templates/datacenterlight/index.html:355 #: templates/datacenterlight/index.html:324
#: templates/datacenterlight/pricing.html:181
msgid "Home" msgid "Home"
msgstr "Home" msgstr "Home"
#: templates/datacenterlight/index.html:368 #: templates/datacenterlight/index.html:337
#: templates/datacenterlight/pricing.html:194
msgid "Pricing" msgid "Pricing"
msgstr "Preise" msgstr "Preise"
#: templates/datacenterlight/pricing.html:122
#, fuzzy
#| msgid "Based in Switzerland"
msgid "Hosted in Switzerland"
msgstr "Standort des Datacenters ist in der Schweiz"
#: templates/datacenterlight/pricing.html:136
msgid "GB Storage (SSD)"
msgstr "GB Storage (SSD)"
#: templates/datacenterlight/pricing.html:163
msgid "Simple and affordable: Try our virtual machine with featherlight price."
msgstr ""
#~ msgid "Buy Now!"
#~ msgstr "Kaufe jetzt!"
#~ msgid "I want to try!"
#~ msgstr "Das möchte ich haben"
#~ msgid "How it works:" #~ msgid "How it works:"
#~ msgstr "Warum können wir diese Leistung so günstig anbieten:" #~ msgstr "Warum können wir diese Leistung so günstig anbieten:"

View file

@ -123,7 +123,6 @@ h6 {
} }
.navbar-transparent .navbar-nav>li>a { .navbar-transparent .navbar-nav>li>a {
color: #fff; color: #fff;
font-size: 17px;
cursor: pointer; cursor: pointer;
} }
.navbar-transparent .navbar-nav>li>a:hover { .navbar-transparent .navbar-nav>li>a:hover {
@ -139,6 +138,12 @@ h6 {
.navbar-right { .navbar-right {
margin-right: 0px; margin-right: 0px;
} }
.navbar-default .btn-link {
color: #fff;
}
.navbar-default .btn-link:hover {
color: #fff !important;
}
.intro-header { .intro-header {
height: 100vh; height: 100vh;
text-align: center; text-align: center;
@ -344,6 +349,13 @@ h6 {
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
padding-bottom: 40px; padding-bottom: 40px;
border-radius: 7px; border-radius: 7px;
position: relative;
}
.pricing-section .card .img-beta{
position: absolute;
top: 5px;
width: 60px;
left: 3px;
} }
.pricing-section .card .title{ .pricing-section .card .title{
padding: 15px 40px; padding: 15px 40px;
@ -403,7 +415,7 @@ h6 {
} }
.request-section .title h2{ .request-section .title h2{
font-family: 'Montserrat-Bold'; font-family: 'Montserrat-Bold';
font-size: 65px; font-size: 45px;
margin: 0; margin: 0;
color: #fff; color: #fff;
padding-bottom: 25px; padding-bottom: 25px;
@ -551,6 +563,13 @@ h6 {
text-align: center; text-align: center;
/* margin-right: auto; */ /* margin-right: auto; */
max-width: 400px; max-width: 400px;
position: relative;
}
.price-calc-section .card .img-beta{
position: absolute;
top: 5px;
width: 60px;
left: 3px;
} }
.price-calc-section .card .title{ .price-calc-section .card .title{
padding: 15px 40px; padding: 15px 40px;

View file

@ -9,17 +9,23 @@
'cpu': { 'cpu': {
'id': 'coreValue', 'id': 'coreValue',
'value': 1, 'value': 1,
'limit': 48 'min':1,
'max': 48,
'interval': 1
}, },
'ram': { 'ram': {
'id': 'ramValue', 'id': 'ramValue',
'value': 1, 'value': 1,
'limit': 200 'min':1,
'max': 200,
'interval': 1
}, },
'storage': { 'storage': {
'id': 'storageValue', 'id': 'storageValue',
'value': 1, 'value': 10,
'limit': 500 'min': 10,
'max': 500,
'interval': 10
} }
} }
$(window).load(function(){ $(window).load(function(){
@ -87,15 +93,15 @@
$('.fa-minus-circle.left').click(function(event){ $('.fa-minus-circle.left').click(function(event){
var data = $(this).data('minus'); var data = $(this).data('minus');
if(cardPricing[data].value > 1){ if(cardPricing[data].value > cardPricing[data].min){
cardPricing[data].value --; cardPricing[data].value --;
} }
_fetchPricing(); _fetchPricing();
}); });
$('.fa-plus-circle.right').click(function(event){ $('.fa-plus-circle.right').click(function(event){
var data = $(this).data('plus'); var data = $(this).data('plus');
if(cardPricing[data].value < cardPricing[data].limit){ if(cardPricing[data].value < cardPricing[data].max){
cardPricing[data].value ++; cardPricing[data].value = cardPricing[data].value + cardPricing[data].interval;
} }
_fetchPricing(); _fetchPricing();
}); });
@ -110,6 +116,7 @@
function _calcPricing(){ function _calcPricing(){
var total = (cardPricing['cpu'].value * 5) + (2* cardPricing['ram'].value) + (0.6* cardPricing['storage'].value) var total = (cardPricing['cpu'].value * 5) + (2* cardPricing['ram'].value) + (0.6* cardPricing['storage'].value)
total = parseFloat(total.toFixed(2));
$("#total").text(total); $("#total").text(total);
$('input[name=total]').val(total); $('input[name=total]').val(total);

View file

@ -227,25 +227,27 @@
<h3>{% trans "VM hosting" %} </h3> <h3>{% trans "VM hosting" %} </h3>
</div> </div>
<div class="price"> <div class="price">
<span>15CHF/month</span> <span>15 CHF/month</span>
</div> </div>
<div class="descriptions"> <div class="descriptions">
<div class="description"> <div class="description">
<p>{% trans "Based in Switzerland" %}</p> <p>{% trans "Based in Switzerland" %}</p>
</div> </div>
<div class="description"> <div class="description">
<p>1 core, </p> <p>1 Core, </p>
</div> </div>
<div class="description"> <div class="description">
<p>2 GiB RAM, </p> <p>2 GB RAM, </p>
</div> </div>
<div class="description"> <div class="description">
<p>{% trans "15 GiB storage(SSD)" %}</p> <p>{% trans "15 GB Storage (SSD)" %}</p>
</div> </div>
</div> </div>
<a href="{% url 'datacenterlight:pricing' %}" class="btn btn-primary">{% trans "Order Now!" %}</a> <a href="{% url 'datacenterlight:pricing' %}" class="btn btn-primary">{% trans "Order Now!" %}</a>
</div> </div>
<img class="img-beta" src="{% static 'datacenterlight/img/beta.png' %}" alt="">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -257,7 +259,7 @@
<div class="row"> <div class="row">
<div class="col-sm-6 col-md-6"> <div class="col-sm-6 col-md-6">
<div class="title"> <div class="title">
<h2>{% trans "I want to try!" %}</h2> <h2>{% trans "Want to know more? Subscribe to our newsletter!" %}</h2>
</div> </div>
</div> </div>
<div class="col-sm-6 col-md-6"> <div class="col-sm-6 col-md-6">

View file

@ -105,6 +105,7 @@
<div class="price-calc-section"> <div class="price-calc-section">
<div class="card"> <div class="card">
<img class="img-beta" src="{% static 'datacenterlight/img/beta.png' %}" alt="">
<div class="caption"> <div class="caption">
<form method="POST" action=""> <form method="POST" action="">
{% csrf_token %} {% csrf_token %}
@ -122,7 +123,7 @@
</div> </div>
<div class="description"> <div class="description">
<i class="fa fa-minus-circle left" data-minus="cpu" aria-hidden="true"></i> <i class="fa fa-minus-circle left" data-minus="cpu" aria-hidden="true"></i>
<span id="coreValue">1</span><span> core</span> <span id="coreValue">1</span><span> Core</span>
<i class="fa fa-plus-circle right" data-plus="cpu" aria-hidden="true"></i> <i class="fa fa-plus-circle right" data-plus="cpu" aria-hidden="true"></i>
</div> </div>
<div class="description"> <div class="description">
@ -132,14 +133,13 @@
</div> </div>
<div class="description"> <div class="description">
<i class="fa fa-minus-circle left" data-minus="storage" aria-hidden="true"></i> <i class="fa fa-minus-circle left" data-minus="storage" aria-hidden="true"></i>
<span id="storageValue">15</span><span>{% trans "GiB storage(SSD)" %}</span> <span id="storageValue">15</span><span>{% trans "GB Storage (SSD)" %}</span>
<i class="fa fa-plus-circle right" data-plus="storage" aria-hidden="true"></i> <i class="fa fa-plus-circle right" data-plus="storage" aria-hidden="true"></i>
</div> </div>
<div class="description select-configuration"> <div class="description select-configuration">
<select name="config" id=""> <select name="config" id="">
<option value="" disabled selected>Configuration</option>
{% for template in templates %} {% for template in templates %}
<option value="{{template.id}}">{{template.name}} </option> <option value="{{template.id}}">{{template.name}} </option>
{% endfor %} {% endfor %}

View file

@ -61,7 +61,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:key_pair'%}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a> <a href="{% url 'hosting:virtual_machines'%}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View file

@ -31,7 +31,7 @@ from .mixins import ProcessVMSelectionMixin
from opennebula_api.models import OpenNebulaManager from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineSerializer,\ from opennebula_api.serializers import VirtualMachineSerializer,\
VirtualMachineTemplateSerializer VirtualMachineTemplateSerializer
from oca.exceptions import OpenNebulaException from oca.exceptions import OpenNebulaException
@ -40,6 +40,7 @@ from oca.pool import WrongNameError
CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a backend \ CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a backend \
connection error. please try again in a few minutes." connection error. please try again in a few minutes."
class DjangoHostingView(ProcessVMSelectionMixin, View): class DjangoHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/django.html" template_name = "hosting/django.html"
@ -187,9 +188,11 @@ class SignupView(CreateView):
template_name = 'hosting/signup.html' template_name = 'hosting/signup.html'
form_class = HostingUserSignupForm form_class = HostingUserSignupForm
model = CustomUser model = CustomUser
success_url = reverse_lazy('hosting:key_pair')
def get_success_url(self): def get_success_url(self):
next_url = self.request.session.get('next', reverse_lazy('hosting:virtual_machines')) next_url = self.request.session.get(
'next', self.success_url)
return next_url return next_url
def form_valid(self, form): def form_valid(self, form):
@ -243,12 +246,14 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
return self.form_valid(form) return self.form_valid(form)
else: else:
messages.error(request, 'Password reset has not been successful.') messages.error(
request, 'Password reset has not been successful.')
form.add_error(None, 'Password reset has not been successful.') form.add_error(None, 'Password reset has not been successful.')
return self.form_invalid(form) return self.form_invalid(form)
else: else:
messages.error(request, 'The reset password link is no longer valid.') messages.error(
request, 'The reset password link is no longer valid.')
form.add_error(None, '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) return self.form_invalid(form)
@ -337,7 +342,20 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView):
'form': UserHostingKeyForm(request=self.request), 'form': UserHostingKeyForm(request=self.request),
}) })
# return HttpResponseRedirect(reverse('hosting:key_pair')) owner = self.request.user
# Create OpenNebulaManager
manager = OpenNebulaManager(email=owner.email,
password=owner.password)
# Get OpenNebula user id
user_pool = manager._get_user_pool()
opennebula_user = user_pool.get_by_name(owner.email)
# Get user ssh key
user_key = UserHostingKey.objects.get(user=owner)
# Add ssh key to user
manager.oneadmin_client.call('user.update', opennebula_user.id,
'<CONTEXT><SSH_PUBLIC_KEY>{ssh_key}</SSH_PUBLIC_KEY></CONTEXT>'.format(ssh_key=user_key.public_key))
return render(self.request, self.template_name, context) return render(self.request, self.template_name, context)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -385,9 +403,11 @@ class PaymentVMView(LoginRequiredMixin, FormView):
user = self.request.user user = self.request.user
# Get user last order # Get user last order
last_hosting_order = HostingOrder.objects.filter(customer__user=user).last() 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 user has already an hosting order, get the credit card data from
# it
if last_hosting_order: if last_hosting_order:
credit_card_data = last_hosting_order.get_cc_data() credit_card_data = last_hosting_order.get_cc_data()
context.update({ context.update({
@ -475,12 +495,11 @@ class PaymentVMView(LoginRequiredMixin, FormView):
except UserHostingKey.DoesNotExist: except UserHostingKey.DoesNotExist:
pass pass
# Create a vm using logged user # Create a vm using logged user
vm_id = manager.create_vm( vm_id = manager.create_vm(
template_id=vm_template_id, template_id=vm_template_id,
#XXX: Confi # XXX: Confi
specs=specs, specs=specs,
ssh_key=user_key.public_key, ssh_key=user_key.public_key,
) )
@ -494,14 +513,16 @@ class PaymentVMView(LoginRequiredMixin, FormView):
) )
# 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 # Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count(): if not customer.user.billing_addresses.count():
billing_address_data.update({ billing_address_data.update({
'user': customer.user.id 'user': customer.user.id
}) })
billing_address_user_form = UserBillingAddressForm(billing_address_data) billing_address_user_form = UserBillingAddressForm(
billing_address_data)
billing_address_user_form.is_valid() billing_address_user_form.is_valid()
billing_address_user_form.save() billing_address_user_form.save()
@ -553,9 +574,9 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai
vm = manager.get_vm(obj.vm_id) vm = manager.get_vm(obj.vm_id)
context['vm'] = VirtualMachineSerializer(vm).data context['vm'] = VirtualMachineSerializer(vm).data
except ConnectionRefusedError: except ConnectionRefusedError:
messages.error( request, messages.error(request,
'In order to create a VM, you need to create/upload your SSH KEY first.' 'In order to create a VM, you need to create/upload your SSH KEY first.'
) )
return context return context
@ -595,20 +616,19 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView):
serializer = VirtualMachineSerializer(queryset, many=True) serializer = VirtualMachineSerializer(queryset, many=True)
return serializer.data return serializer.data
except ConnectionRefusedError: except ConnectionRefusedError:
messages.error( self.request, messages.error(self.request,
'We could not load your VMs due to a backend connection \ 'We could not load your VMs due to a backend connection \
error. Please try again in a few minutes' error. Please try again in a few minutes'
) )
self.kwargs['error'] = 'connection' self.kwargs['error'] = 'connection'
return [] return []
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
error = self.kwargs.get('error') error = self.kwargs.get('error')
if error is not None: if error is not None:
print(error) print(error)
context = { 'error' : 'connection' } context = {'error': 'connection'}
else: else:
context = super(ListView, self).get_context_data(**kwargs) context = super(ListView, self).get_context_data(**kwargs)
return context return context
@ -658,9 +678,10 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View):
template = manager.get_template(template_id) template = manager.get_template(template_id)
configuration_id = int(request.POST.get('configuration')) configuration_id = int(request.POST.get('configuration'))
configuration = HostingPlan.objects.get(id=configuration_id) configuration = HostingPlan.objects.get(id=configuration_id)
request.session['template'] = VirtualMachineTemplateSerializer(template).data request.session['template'] = VirtualMachineTemplateSerializer(
template).data
request.session['specs'] = configuration.serialize() request.session['specs'] = configuration.serialize()
return redirect(reverse('hosting:payment')) return redirect(reverse('hosting:payment'))
@ -680,10 +701,10 @@ class VirtualMachineView(LoginRequiredMixin, View):
vm = manager.get_vm(vm_id) vm = manager.get_vm(vm_id)
return vm return vm
except ConnectionRefusedError: except ConnectionRefusedError:
messages.error( self.request, messages.error(self.request,
'We could not load your VM due to a backend connection \ 'We could not load your VM due to a backend connection \
error. Please try again in a few minutes' error. Please try again in a few minutes'
) )
return None return None
except Exception as error: except Exception as error:
print(error) print(error)
@ -695,7 +716,7 @@ class VirtualMachineView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
vm = self.get_object() vm = self.get_object()
try: try:
serializer = VirtualMachineSerializer(vm) serializer = VirtualMachineSerializer(vm)
context = { context = {
'virtual_machine': serializer.data, 'virtual_machine': serializer.data,

View file

@ -10,17 +10,18 @@ from oca.pool import WrongNameError
from oca.exceptions import OpenNebulaException from oca.exceptions import OpenNebulaException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OpenNebulaManager(): class OpenNebulaManager():
"""This class represents an opennebula manager.""" """This class represents an opennebula manager."""
def __init__(self, email=None, password=None): 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(
settings.OPENNEBULA_USERNAME, settings.OPENNEBULA_USERNAME,
settings.OPENNEBULA_PASSWORD settings.OPENNEBULA_PASSWORD
) )
# Get or create oppenebula user using given credentials # Get or create oppenebula user using given credentials
try: try:
self.opennebula_user = self._get_or_create_user( self.opennebula_user = self._get_or_create_user(
@ -55,26 +56,27 @@ class OpenNebulaManager():
opennebula_user = self.oneadmin_client.call(oca.User.METHODS['allocate'], email, opennebula_user = self.oneadmin_client.call(oca.User.METHODS['allocate'], email,
password, 'core') password, 'core')
logger.debug( logger.debug(
"User {0} does not exist. Created the user. User id = {1}", "User {0} does not exist. Created the user. User id = {1}",
email, email,
opennebula_user opennebula_user
) )
return opennebula_user return opennebula_user
except ConnectionRefusedError: except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format( logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
host=settings.OPENNEBULA_DOMAIN, host=settings.OPENNEBULA_DOMAIN,
protocol=settings.OPENNEBULA_PROTOCOL) protocol=settings.OPENNEBULA_PROTOCOL)
) )
raise ConnectionRefusedError raise ConnectionRefusedError
def _get_user_pool(self): def _get_user_pool(self):
try: try:
user_pool = oca.UserPool(self.oneadmin_client) user_pool = oca.UserPool(self.oneadmin_client)
user_pool.info() user_pool.info()
except ConnectionRefusedError: except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format( logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
host=settings.OPENNEBULA_DOMAIN, host=settings.OPENNEBULA_DOMAIN,
protocol=settings.OPENNEBULA_PROTOCOL) protocol=settings.OPENNEBULA_PROTOCOL)
) )
raise ConnectionRefusedError raise ConnectionRefusedError
return user_pool return user_pool
@ -84,7 +86,7 @@ class OpenNebulaManager():
vm_pool.info() vm_pool.info()
return vm_pool return vm_pool
except AttributeError: except AttributeError:
logger.info('Could not connect via client, using oneadmin instead') logger.info('Could not connect via client, using oneadmin instead')
try: try:
vm_pool = oca.VirtualMachinePool(self.oneadmin_client) vm_pool = oca.VirtualMachinePool(self.oneadmin_client)
vm_pool.info(filter=-2) vm_pool.info(filter=-2)
@ -94,9 +96,9 @@ class OpenNebulaManager():
except ConnectionRefusedError: except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format( logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
host=settings.OPENNEBULA_DOMAIN, host=settings.OPENNEBULA_DOMAIN,
protocol=settings.OPENNEBULA_PROTOCOL) protocol=settings.OPENNEBULA_PROTOCOL)
) )
raise ConnectionRefusedError raise ConnectionRefusedError
# For now we'll just handle all other errors as connection errors # For now we'll just handle all other errors as connection errors
except: except:
@ -107,8 +109,7 @@ class OpenNebulaManager():
return self._get_vm_pool() return self._get_vm_pool()
except ConnectionRefusedError: except ConnectionRefusedError:
raise ConnectionRefusedError raise ConnectionRefusedError
def get_vm(self, vm_id): def get_vm(self, vm_id):
vm_id = int(vm_id) vm_id = int(vm_id)
try: try:
@ -118,7 +119,7 @@ class OpenNebulaManager():
raise ConnectionRefusedError raise ConnectionRefusedError
def create_template(self, name, cores, memory, disk_size, core_price, memory_price, def create_template(self, name, cores, memory, disk_size, core_price, memory_price,
disk_size_price, ssh='' ): disk_size_price, ssh=''):
"""Create and add a new template to opennebula. """Create and add a new template to opennebula.
:param name: A string representation describing the template. :param name: A string representation describing the template.
Used as label in view. Used as label in view.
@ -130,17 +131,17 @@ class OpenNebulaManager():
:param disk_size_price: Price of disk space for VM per GB :param disk_size_price: Price of disk space for VM per GB
:param ssh: User public ssh key :param ssh: User public ssh key
""" """
template_id = oca.VmTemplate.allocate( template_id = oca.VmTemplate.allocate(
self.oneadmin_client, self.oneadmin_client,
template_string_formatter.format( template_string_formatter.format(
name=name, name=name,
vcpu=cores, vcpu=cores,
cpu=0.1*cores, cpu=0.1 * cores,
size=1024 * disk_size, size=1024 * disk_size,
memory=1024 * memory, memory=1024 * memory,
# * 10 because we set cpu to *0.1 # * 10 because we set cpu to *0.1
cpu_cost=10*core_price, cpu_cost=10 * core_price,
memory_cost=memory_price, memory_cost=memory_price,
disk_cost=disk_size_price, disk_cost=disk_size_price,
ssh=ssh ssh=ssh
@ -159,11 +160,11 @@ class OpenNebulaManager():
disk = template.template.disks[0] disk = template.template.disks[0]
image_id = disk.image_id image_id = disk.image_id
vm_specs = vm_specs_formatter.format( vm_specs = vm_specs_formatter.format(
vcpu=int(specs['cpu']), vcpu=int(specs['cpu']),
cpu=0.1* int(specs['cpu']), cpu=0.1 * int(specs['cpu']),
memory=1024 * int(specs['memory']), memory=1024 * int(specs['memory']),
) )
vm_specs += """<DISK> vm_specs += """<DISK>
<TYPE>fs</TYPE> <TYPE>fs</TYPE>
<SIZE>{size}</SIZE> <SIZE>{size}</SIZE>
@ -180,11 +181,11 @@ class OpenNebulaManager():
image_uname = disk.image_uname image_uname = disk.image_uname
vm_specs = vm_specs_formatter.format( vm_specs = vm_specs_formatter.format(
vcpu=int(specs['cpu']), vcpu=int(specs['cpu']),
cpu=0.1* int(specs['cpu']), cpu=0.1 * int(specs['cpu']),
memory=1024 * int(specs['memory']), memory=1024 * int(specs['memory']),
) )
vm_specs += """<DISK> vm_specs += """<DISK>
<TYPE>fs</TYPE> <TYPE>fs</TYPE>
<SIZE>{size}</SIZE> <SIZE>{size}</SIZE>
@ -196,18 +197,21 @@ class OpenNebulaManager():
""".format(size=1024 * int(specs['disk_size']), """.format(size=1024 * int(specs['disk_size']),
image=image, image=image,
image_uname=image_uname) image_uname=image_uname)
vm_id = template.instantiate(name ='', vm_id = self.client.call(oca.VmTemplate.METHODS['instantiate'],
pending=False, template.id,
extra_template=vm_specs, ) '',
True,
vm_specs,
False)
self.oneadmin_client.call( self.oneadmin_client.call(
'vm.updateconf', 'vm.update',
vm_id, vm_id,
"""<CONTEXT> """<CONTEXT>
<SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY> <SSH_PUBLIC_KEY>{ssh}</SSH_PUBLIC_KEY>
</CONTEXT> </CONTEXT>
""".format(ssh=ssh_key) """.format(ssh=ssh_key)
) )
try: try:
self.oneadmin_client.call( self.oneadmin_client.call(
oca.VirtualMachine.METHODS['chown'], oca.VirtualMachine.METHODS['chown'],
@ -216,7 +220,14 @@ class OpenNebulaManager():
self.opennebula_user.group_ids[0] self.opennebula_user.group_ids[0]
) )
except AttributeError: except AttributeError:
logger.info('Could not change owner for vm with id: {}.'.format(vm_id)) logger.info(
'Could not change owner for vm with id: {}.'.format(vm_id))
self.oneadmin_client.call(
oca.VirtualMachine.METHODS['action'],
'release',
vm_id
)
return vm_id return vm_id
def delete_vm(self, vm_id): def delete_vm(self, vm_id):
@ -232,7 +243,8 @@ class OpenNebulaManager():
except socket.timeout as socket_err: except socket.timeout as socket_err:
logger.info("Socket timeout error: {0}".format(socket_err)) logger.info("Socket timeout error: {0}".format(socket_err))
except OpenNebulaException as opennebula_err: except OpenNebulaException as opennebula_err:
logger.info("OpenNebulaException error: {0}".format(opennebula_err)) logger.info(
"OpenNebulaException error: {0}".format(opennebula_err))
except OSError as os_err: except OSError as os_err:
logger.info("OSError : {0}".format(os_err)) logger.info("OSError : {0}".format(os_err))
except ValueError as value_err: except ValueError as value_err:
@ -242,27 +254,26 @@ class OpenNebulaManager():
def _get_template_pool(self): def _get_template_pool(self):
try: try:
template_pool = oca.VmTemplatePool(self.oneadmin_client) template_pool = oca.VmTemplatePool(self.oneadmin_client)
template_pool.info() template_pool.info()
return template_pool return template_pool
except ConnectionRefusedError: except ConnectionRefusedError:
logger.info('Could not connect to host: {host} via protocol {protocol}'.format( logger.info('Could not connect to host: {host} via protocol {protocol}'.format(
host=settings.OPENNEBULA_DOMAIN, host=settings.OPENNEBULA_DOMAIN,
protocol=settings.OPENNEBULA_PROTOCOL) protocol=settings.OPENNEBULA_PROTOCOL)
) )
raise ConnectionRefusedError raise ConnectionRefusedError
except: except:
raise ConnectionRefusedError raise ConnectionRefusedError
def get_templates(self): def get_templates(self):
try: try:
public_templates = [ public_templates = [
template template
for template in self._get_template_pool() for template in self._get_template_pool()
if 'public-' in template.name if 'public-' in template.name
] ]
return public_templates return public_templates
except ConnectionRefusedError: except ConnectionRefusedError:
raise ConnectionRefusedError raise ConnectionRefusedError
except: except:
@ -282,10 +293,8 @@ class OpenNebulaManager():
except: except:
raise ConnectionRefusedError raise ConnectionRefusedError
def create_template(self, name, cores, memory, disk_size, core_price, memory_price, def create_template(self, name, cores, memory, disk_size, core_price, memory_price,
disk_size_price, ssh='' ): disk_size_price, ssh=''):
"""Create and add a new template to opennebula. """Create and add a new template to opennebula.
:param name: A string representation describing the template. :param name: A string representation describing the template.
Used as label in view. Used as label in view.
@ -318,11 +327,11 @@ class OpenNebulaManager():
template_string_formatter.format( template_string_formatter.format(
name=name, name=name,
vcpu=cores, vcpu=cores,
cpu=0.1*cores, cpu=0.1 * cores,
size=1024 * disk_size, size=1024 * disk_size,
memory=1024 * memory, memory=1024 * memory,
# * 10 because we set cpu to *0.1 # * 10 because we set cpu to *0.1
cpu_cost=10*core_price, cpu_cost=10 * core_price,
memory_cost=memory_price, memory_cost=memory_price,
disk_cost=disk_size_price, disk_cost=disk_size_price,
ssh=ssh ssh=ssh
@ -332,11 +341,12 @@ class OpenNebulaManager():
return template_id return template_id
def delete_template(self, template_id): def delete_template(self, template_id):
self.oneadmin_client.call(oca.VmTemplate.METHODS['delete'], template_id, False) self.oneadmin_client.call(oca.VmTemplate.METHODS[
'delete'], template_id, False)
def change_user_password(self, new_password): def change_user_password(self, new_password):
self.oneadmin_client.call( self.oneadmin_client.call(
oca.User.METHODS['passwd'], oca.User.METHODS['passwd'],
self.opennebula_user.id, self.opennebula_user.id,
new_password new_password
) )