diff --git a/datacenterlight/forms.py b/datacenterlight/forms.py index 8371ebfd..90340e8d 100644 --- a/datacenterlight/forms.py +++ b/datacenterlight/forms.py @@ -7,7 +7,7 @@ class BetaAccessForm(forms.ModelForm): email = forms.CharField(widget=forms.EmailInput()) class Meta: - fields = ['email'] + fields = ['name', 'email'] model = BetaAccess diff --git a/datacenterlight/static/datacenterlight/css/landing-page.css b/datacenterlight/static/datacenterlight/css/landing-page.css index a1264ba3..33e563ba 100755 --- a/datacenterlight/static/datacenterlight/css/landing-page.css +++ b/datacenterlight/static/datacenterlight/css/landing-page.css @@ -820,4 +820,4 @@ a#forgotpassword { } .form-300{ width: 300px; -} \ No newline at end of file +} diff --git a/datacenterlight/static/datacenterlight/js/form.js b/datacenterlight/static/datacenterlight/js/form.js new file mode 100644 index 00000000..0e91b172 --- /dev/null +++ b/datacenterlight/static/datacenterlight/js/form.js @@ -0,0 +1,12 @@ +/*! + * jQuery Form Plugin + * version: 4.2.1 + * Requires jQuery v1.7 or later + * Copyright 2017 Kevin Morris + * Copyright 2006 M. Alsup + * Project repository: https://github.com/jquery-form/form + * Dual licensed under the MIT and LGPLv3 licenses. + * https://github.com/jquery-form/form#license + */ +!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=function(b,c){return void 0===c&&(c="undefined"!=typeof window?require("jquery"):require("jquery")(b)),a(c),c}:a(jQuery)}(function(a){"use strict";function b(b){var c=b.data;b.isDefaultPrevented()||(b.preventDefault(),a(b.target).closest("form").ajaxSubmit(c))}function c(b){var c=b.target,d=a(c);if(!d.is("[type=submit],[type=image]")){var e=d.closest("[type=submit]");if(0===e.length)return;c=e[0]}var f=c.form;if(f.clk=c,"image"===c.type)if(void 0!==b.offsetX)f.clk_x=b.offsetX,f.clk_y=b.offsetY;else if("function"==typeof a.fn.offset){var g=d.offset();f.clk_x=b.pageX-g.left,f.clk_y=b.pageY-g.top}else f.clk_x=b.pageX-c.offsetLeft,f.clk_y=b.pageY-c.offsetTop;setTimeout(function(){f.clk=f.clk_x=f.clk_y=null},100)}function d(){if(a.fn.ajaxSubmit.debug){var b="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(b):window.opera&&window.opera.postError&&window.opera.postError(b)}}var e={};e.fileapi=void 0!==a('').get(0).files,e.formdata=void 0!==window.FormData;var f=!!a.fn.prop;a.fn.attr2=function(){if(!f)return this.attr.apply(this,arguments);var a=this.prop.apply(this,arguments);return a&&a.jquery||"string"==typeof a?a:this.attr.apply(this,arguments)},a.fn.ajaxSubmit=function(b,c,g,h){function i(c){var d,e,f=a.param(c,b.traditional).split("&"),g=f.length,h=[];for(d=0;d',z).val(k.extraData[j].value).appendTo(x)[0]):i.push(a('',z).val(k.extraData[j]).appendTo(x)[0]));k.iframeTarget||p.appendTo(A),q.attachEvent?q.attachEvent("onload",h):q.addEventListener("load",h,!1),setTimeout(b,15);try{x.submit()}catch(a){var m=document.createElement("form").submit;m.apply(x)}}finally{x.setAttribute("action",f),x.setAttribute("enctype",g),c?x.setAttribute("target",c):o.removeAttr("target"),a(i).remove()}}function h(b){if(!r.aborted&&!F){if(E=e(q),E||(d("cannot access response document"),b=2),1===b&&r)return r.abort("timeout"),void y.reject(r,"timeout");if(2===b&&r)return r.abort("server abort"),void y.reject(r,"error","server abort");if(E&&E.location.href!==k.iframeSrc||v){q.detachEvent?q.detachEvent("onload",h):q.removeEventListener("load",h,!1);var c,f="success";try{if(v)throw"timeout";var g="xml"===k.dataType||E.XMLDocument||a.isXMLDoc(E);if(d("isXml="+g),!g&&window.opera&&(null===E.body||!E.body.innerHTML)&&--G)return d("requeing onLoad callback, DOM not available"),void setTimeout(h,250);var i=E.body?E.body:E.documentElement;r.responseText=i?i.innerHTML:null,r.responseXML=E.XMLDocument?E.XMLDocument:E,g&&(k.dataType="xml"),r.getResponseHeader=function(a){return{"content-type":k.dataType}[a.toLowerCase()]},i&&(r.status=Number(i.getAttribute("status"))||r.status,r.statusText=i.getAttribute("statusText")||r.statusText);var j=(k.dataType||"").toLowerCase(),l=/(json|script|text)/.test(j);if(l||k.textarea){var n=E.getElementsByTagName("textarea")[0];if(n)r.responseText=n.value,r.status=Number(n.getAttribute("status"))||r.status,r.statusText=n.getAttribute("statusText")||r.statusText;else if(l){var o=E.getElementsByTagName("pre")[0],s=E.getElementsByTagName("body")[0];o?r.responseText=o.textContent?o.textContent:o.innerText:s&&(r.responseText=s.textContent?s.textContent:s.innerText)}}else"xml"===j&&!r.responseXML&&r.responseText&&(r.responseXML=H(r.responseText));try{D=J(r,j,k)}catch(a){f="parsererror",r.error=c=a||f}}catch(a){d("error caught: ",a),f="error",r.error=c=a||f}r.aborted&&(d("upload aborted"),f=null),r.status&&(f=r.status>=200&&r.status<300||304===r.status?"success":"error"),"success"===f?(k.success&&k.success.call(k.context,D,"success",r),y.resolve(r.responseText,"success",r),m&&a.event.trigger("ajaxSuccess",[r,k])):f&&(void 0===c&&(c=r.statusText),k.error&&k.error.call(k.context,r,f,c),y.reject(r,"error",c),m&&a.event.trigger("ajaxError",[r,k,c])),m&&a.event.trigger("ajaxComplete",[r,k]),m&&!--a.active&&a.event.trigger("ajaxStop"),k.complete&&k.complete.call(k.context,r,f),F=!0,k.timeout&&clearTimeout(w),setTimeout(function(){k.iframeTarget?p.attr("src",k.iframeSrc):p.remove(),r.responseXML=null},100)}}}var i,j,k,m,n,p,q,r,t,u,v,w,x=o[0],y=a.Deferred();if(y.abort=function(a){r.abort(a)},c)for(j=0;j',z),p.css({position:"absolute",top:"-1000px",left:"-1000px"})),q=p[0],r={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(b){var c="timeout"===b?"timeout":"aborted";d("aborting upload... "+c),this.aborted=1;try{q.contentWindow.document.execCommand&&q.contentWindow.document.execCommand("Stop")}catch(a){}p.attr("src",k.iframeSrc),r.error=c,k.error&&k.error.call(k.context,r,c,b),m&&a.event.trigger("ajaxError",[r,k,c]),k.complete&&k.complete.call(k.context,r,c)}},m=k.global,m&&0==a.active++&&a.event.trigger("ajaxStart"),m&&a.event.trigger("ajaxSend",[r,k]),k.beforeSend&&k.beforeSend.call(k.context,r,k)===!1)return k.global&&a.active--,y.reject(),y;if(r.aborted)return y.reject(),y;(t=x.clk)&&(u=t.name)&&!t.disabled&&(k.extraData=k.extraData||{},k.extraData[u]=t.value,"image"===t.type&&(k.extraData[u+".x"]=x.clk_x,k.extraData[u+".y"]=x.clk_y));var B=a("meta[name=csrf-token]").attr("content"),C=a("meta[name=csrf-param]").attr("content");C&&B&&(k.extraData=k.extraData||{},k.extraData[C]=B),k.forceSync?g():setTimeout(g,10);var D,E,F,G=50,H=a.parseXML||function(a,b){return window.ActiveXObject?(b=new ActiveXObject("Microsoft.XMLDOM"),b.async="false",b.loadXML(a)):b=(new DOMParser).parseFromString(a,"text/xml"),b&&b.documentElement&&"parsererror"!==b.documentElement.nodeName?b:null},I=a.parseJSON||function(a){return window.eval("("+a+")")},J=function(b,c,d){var e=b.getResponseHeader("content-type")||"",f=("xml"===c||!c)&&e.indexOf("xml")>=0,g=f?b.responseXML:b.responseText;return f&&"parsererror"===g.documentElement.nodeName&&a.error&&a.error("parsererror"),d&&d.dataFilter&&(g=d.dataFilter(g,c)),"string"==typeof g&&(("json"===c||!c)&&e.indexOf("json")>=0?g=I(g):("script"===c||!c)&&e.indexOf("javascript")>=0&&a.globalEval(g)),g};return y}if(!this.length)return d("ajaxSubmit: skipping submit process - no element selected"),this;var l,m,n,o=this;"function"==typeof b?b={success:b}:"string"==typeof b||b===!1&&arguments.length>0?(b={url:b,data:c,dataType:g},"function"==typeof h&&(b.success=h)):void 0===b&&(b={}),l=b.method||b.type||this.attr2("method"),m=b.url||this.attr2("action"),n="string"==typeof m?a.trim(m):"",n=n||window.location.href||"",n&&(n=(n.match(/^([^#]+)/)||[])[1]),b=a.extend(!0,{url:n,success:a.ajaxSettings.success,type:l||a.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},b);var p={};if(this.trigger("form-pre-serialize",[this,b,p]),p.veto)return d("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(b.beforeSerialize&&b.beforeSerialize(this,b)===!1)return d("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var q=b.traditional;void 0===q&&(q=a.ajaxSettings.traditional);var r,s=[],t=this.formToArray(b.semantic,s,b.filtering);if(b.data){var u=a.isFunction(b.data)?b.data(t):b.data;b.extraData=u,r=a.param(u,q)}if(b.beforeSubmit&&b.beforeSubmit(t,this,b)===!1)return d("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[t,this,b,p]),p.veto)return d("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var v=a.param(t,q);r&&(v=v?v+"&"+r:r),"GET"===b.type.toUpperCase()?(b.url+=(b.url.indexOf("?")>=0?"&":"?")+v,b.data=null):b.data=v;var w=[];if(b.resetForm&&w.push(function(){o.resetForm()}),b.clearForm&&w.push(function(){o.clearForm(b.includeHidden)}),!b.dataType&&b.target){var x=b.success||function(){};w.push(function(c,d,e){var f=arguments,g=b.replaceTarget?"replaceWith":"html";a(b.target)[g](c).each(function(){x.apply(this,f)})})}else b.success&&(a.isArray(b.success)?a.merge(w,b.success):w.push(b.success));if(b.success=function(a,c,d){for(var e=b.context||this,f=0,g=w.length;f0,C="multipart/form-data",D=o.attr("enctype")===C||o.attr("encoding")===C,E=e.fileapi&&e.formdata;d("fileAPI :"+E);var F,G=(B||D)&&!E;b.iframe!==!1&&(b.iframe||G)?b.closeKeepAlive?a.get(b.closeKeepAlive,function(){F=k(t)}):F=k(t):F=(B||D)&&E?j(t):a.ajax(b),o.removeData("jqxhr").data("jqxhr",F);for(var H=0;H0)&&(e={url:e,data:f,dataType:g},"function"==typeof h&&(e.success=h)),e=e||{},e.delegation=e.delegation&&a.isFunction(a.fn.on),!e.delegation&&0===this.length){var i={s:this.selector,c:this.context};return!a.isReady&&i.s?(d("DOM not ready, queuing ajaxForm"),a(function(){a(i.s,i.c).ajaxForm(e)}),this):(d("terminating; zero elements found by selector"+(a.isReady?"":" (DOM not ready)")),this)}return e.delegation?(a(document).off("submit.form-plugin",this.selector,b).off("click.form-plugin",this.selector,c).on("submit.form-plugin",this.selector,e,b).on("click.form-plugin",this.selector,e,c),this):this.ajaxFormUnbind().on("submit.form-plugin",e,b).on("click.form-plugin",e,c)},a.fn.ajaxFormUnbind=function(){return this.off("submit.form-plugin click.form-plugin")},a.fn.formToArray=function(b,c,d){var f=[];if(0===this.length)return f;var g,h=this[0],i=this.attr("id"),j=b||void 0===h.elements?h.getElementsByTagName("*"):h.elements;if(j&&(j=a.makeArray(j)),i&&(b||/(Edge|Trident)\//.test(navigator.userAgent))&&(g=a(':input[form="'+i+'"]').get(),g.length&&(j=(j||[]).concat(g))),!j||!j.length)return f;a.isFunction(d)&&(j=a.map(j,d));var k,l,m,n,o,p,q;for(k=0,p=j.length;k + {% csrf_token %} + {{ form.non_field_errors }} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+
+
+ + {{ form.name.errors|striptags}} +
+
+ + {{ form.email.errors|striptags}} +
+
+ + + diff --git a/datacenterlight/templates/datacenterlight/beta_success.html b/datacenterlight/templates/datacenterlight/beta_success.html new file mode 100644 index 00000000..ffcfcce9 --- /dev/null +++ b/datacenterlight/templates/datacenterlight/beta_success.html @@ -0,0 +1,47 @@ +{% load i18n %} + + + + diff --git a/datacenterlight/templates/datacenterlight/index.html b/datacenterlight/templates/datacenterlight/index.html index 1f35f4b3..531bd235 100755 --- a/datacenterlight/templates/datacenterlight/index.html +++ b/datacenterlight/templates/datacenterlight/index.html @@ -227,7 +227,7 @@

{% trans "VM hosting" %}

- 15CHF + 15CHF/month
@@ -243,7 +243,7 @@

{% trans "15 GiB storage(SSD)" %}

- {% trans "Buy Now!" %} + {% trans "Order Now!" %} @@ -261,43 +261,9 @@
-
-
- {% csrf_token %} - {{ form.non_field_errors }} - {{ form.email.errors|striptags}} -
- {% for message in messages %} - {{ message }} - {% endfor %} -
-
-
- -
-
- -
-
- -
-
- + +
+
@@ -389,12 +355,12 @@ windowPadding: 10, }); - var hash = window.location.hash.substr(1); - console.log(hash); - if (hash == 'requestform'){ - $('#reques-success-message').modal('show'); - } - + $.ajax({ + url: "{% url 'datacenterlight:beta_access' %}", + context: document.body + }).done(function(response) { + $('#beta_access_form').html(response); + }); }; @@ -403,6 +369,8 @@ + + diff --git a/datacenterlight/urls.py b/datacenterlight/urls.py index 90faa579..9c3c84fb 100644 --- a/datacenterlight/urls.py +++ b/datacenterlight/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import url -from .views import IndexView, BetaProgramView, LandingProgramView, PricingView +from .views import IndexView, BetaProgramView, LandingProgramView, BetaAccessView, PricingView urlpatterns = [ @@ -8,4 +8,5 @@ urlpatterns = [ url(r'^/beta-program/?$', BetaProgramView.as_view(), name='beta'), url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'), url(r'^/pricing/?$', PricingView.as_view(), name='pricing'), + url(r'^/beta_access?$', BetaAccessView.as_view(), name='beta_access'), ] diff --git a/datacenterlight/views.py b/datacenterlight/views.py index 29a17a0a..000e15ac 100644 --- a/datacenterlight/views.py +++ b/datacenterlight/views.py @@ -5,6 +5,7 @@ from .models import BetaAccess, BetaAccessVMType, BetaAccessVM from django.contrib import messages from django.core.urlresolvers import reverse_lazy, reverse from utils.mailer import BaseEmail +from django.shortcuts import render from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineTemplateSerializer @@ -15,6 +16,43 @@ class LandingProgramView(TemplateView): class PricingView(TemplateView): template_name = "datacenterlight/pricing.html" +class BetaAccessView(FormView): + template_name = "datacenterlight/beta_access.html" + form_class = BetaAccessForm + success_message = "Thank you, we will contact you as soon as possible" + + def form_valid(self, form): + + context = { + 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()) + } + + email_data = { + 'subject': 'DatacenterLight Beta Access Request', + 'to': form.cleaned_data.get('email'), + 'context': context, + 'template_name': 'request_access_confirmation', + 'template_path': 'datacenterlight/emails/' + } + email = BaseEmail(**email_data) + email.send() + + context.update({ + 'email': form.cleaned_data.get('email') + }) + + email_data = { + 'subject': 'DatacenterLight Beta Access Request', + 'to': 'info@ungleich.ch', + 'context': context, + 'template_name': 'request_access_notification', + 'template_path': 'datacenterlight/emails/' + } + email = BaseEmail(**email_data) + email.send() + + messages.add_message(self.request, messages.SUCCESS, self.success_message) + return render(self.request, 'datacenterlight/beta_success.html', {}) class BetaProgramView(CreateView): template_name = "datacenterlight/beta.html" diff --git a/hosting/mixins.py b/hosting/mixins.py index c8539eee..666cbcbe 100644 --- a/hosting/mixins.py +++ b/hosting/mixins.py @@ -4,15 +4,21 @@ from django.core.urlresolvers import reverse from opennebula_api.serializers import VirtualMachineTemplateSerializer from opennebula_api.models import OpenNebulaManager +from .models import HostingPlan + class ProcessVMSelectionMixin(object): def post(self, request, *args, **kwargs): template_id = int(request.POST.get('vm_template_id')) + configuration_id = int(request.POST.get('configuration')) template = OpenNebulaManager().get_template(template_id) data = VirtualMachineTemplateSerializer(template).data + configuration = HostingPlan.objects.get(id=configuration_id) + request.session['template'] = data + request.session['specs'] = configuration.serialize() if not request.user.is_authenticated(): request.session['next'] = reverse('hosting:payment') diff --git a/hosting/templates/hosting/create_virtual_machine.html b/hosting/templates/hosting/create_virtual_machine.html index af618740..ac72046c 100644 --- a/hosting/templates/hosting/create_virtual_machine.html +++ b/hosting/templates/hosting/create_virtual_machine.html @@ -35,7 +35,8 @@ {% endfor %} diff --git a/hosting/templates/hosting/includes/_pricing.html b/hosting/templates/hosting/includes/_pricing.html index c1b2fa11..c5cb2bf1 100644 --- a/hosting/templates/hosting/includes/_pricing.html +++ b/hosting/templates/hosting/includes/_pricing.html @@ -18,13 +18,13 @@
- {% for vm in vm_types %} + {% for vm in configuration_options %}
{% csrf_token %} - + @@ -53,14 +53,14 @@
- +
  • {{vm.price|floatformat}} CHF

    diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 0b17878d..04b964a0 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -41,11 +41,17 @@

    {{virtual_machine.hosting_company_name}}

    - {% if virtual_machine.ip %} + {% if virtual_machine.ipv6 %}
    - +
    {% else %} @@ -98,7 +104,7 @@

    {% trans "Current pricing"%}

    - {{virtual_machine.price|floatformat}} CHF/mo + {{virtual_machine.price|floatformat}} CHF/month
    diff --git a/hosting/templates/hosting/virtual_machine_key.html b/hosting/templates/hosting/virtual_machine_key.html index 0ea4abe5..157ceea8 100644 --- a/hosting/templates/hosting/virtual_machine_key.html +++ b/hosting/templates/hosting/virtual_machine_key.html @@ -113,6 +113,12 @@ {%endif%} +{% if next_url %} + +{% endif %} + {%endblock%} diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index e7dee999..f71dac2a 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -19,14 +19,15 @@
    {% if not error %}

    - {% trans "Create VM"%} + {% trans "Create VM"%}


    {% trans "ID"%} - {% trans "Amount"%} + {% trans "Ipv4"%} + {% trans "Ipv6"%} {% trans "Status"%} @@ -35,7 +36,12 @@ {% for vm in vms %} {{vm.vm_id}} - {{vm.price}} CHF + {% if vm.ipv6 %} + {{vm.ipv4}} + + {{vm.ipv6}} + {% endif %} + {% if vm.state == 'ACTIVE' %} diff --git a/hosting/urls.py b/hosting/urls.py index abfb8d5f..d5923cf4 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -19,7 +19,7 @@ urlpatterns = [ url(r'bills/?$', HostingBillListView.as_view(), name='bills'), url(r'bills/(?P\d+)/?$', HostingBillDetailView.as_view(), name='bills'), url(r'cancel_order/(?P\d+)/?$', OrdersHostingDeleteView.as_view(), name='delete_order'), - url(r'create-virtual-machine/?$', CreateVirtualMachinesView.as_view(), name='create-virtual-machine'), + url(r'create_virtual_machine/?$', CreateVirtualMachinesView.as_view(), name='create_virtual_machine'), url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineView.as_view(), name='virtual_machines'), diff --git a/hosting/views.py b/hosting/views.py index 10116079..b8b04b7d 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -47,6 +47,7 @@ class DjangoHostingView(ProcessVMSelectionMixin, View): HOSTING = 'django' templates = OpenNebulaManager().get_templates() data = VirtualMachineTemplateSerializer(templates, many=True).data + configuration_options = HostingPlan.get_serialized_configs() # configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING) context = { @@ -57,8 +58,8 @@ class DjangoHostingView(ProcessVMSelectionMixin, View): 'google_analytics': "UA-62285904-6", 'vm_types': data, 'email': "info@django-hosting.ch", - # 'vm_types': VirtualMachineType.get_serialized_vm_types(), - # 'configuration_options': dict(VirtualMachinePlan.VM_CONFIGURATION) + 'configuration_options': configuration_options, + 'templates': templates, } return context @@ -77,7 +78,7 @@ class RailsHostingView(ProcessVMSelectionMixin, View): HOSTING = 'rails' templates = OpenNebulaManager().get_templates() - data = VirtualMachineTemplateSerializer(templates, many=True).data + configuration_options = HostingPlan.get_serialized_configs() context = { 'hosting': HOSTING, @@ -85,7 +86,8 @@ class RailsHostingView(ProcessVMSelectionMixin, View): 'domain': "rails-hosting.ch", 'google_analytics': "UA-62285904-5", 'email': "info@rails-hosting.ch", - 'vm_types': data, + 'configuration_options': configuration_options, + 'templates': templates, } return context @@ -102,7 +104,7 @@ class NodeJSHostingView(ProcessVMSelectionMixin, View): HOSTING = 'nodejs' # configuration_detail = dict(VirtualMachinePlan.VM_CONFIGURATION).get(HOSTING) templates = OpenNebulaManager().get_templates() - data = VirtualMachineTemplateSerializer(templates, many=True).data + configuration_options = HostingPlan.get_serialized_configs() context = { 'hosting': HOSTING, @@ -111,7 +113,9 @@ class NodeJSHostingView(ProcessVMSelectionMixin, View): 'domain': "node-hosting.ch", 'google_analytics': "UA-62285904-7", 'email': "info@node-hosting.ch", - 'vm_types': data, + 'templates': templates, + 'configuration_options': configuration_options, + } return context @@ -128,12 +132,15 @@ class HostingPricingView(ProcessVMSelectionMixin, View): def get_context_data(self, **kwargs): # configuration_options = dict(VirtualMachinePlan.VM_CONFIGURATION) templates = OpenNebulaManager().get_templates() - data = VirtualMachineTemplateSerializer(templates, many=True).data + configuration_options = HostingPlan.get_serialized_configs() context = { # 'configuration_options': configuration_options, 'email': "info@django-hosting.ch", - 'vm_types': data, + 'templates': templates, + 'configuration_options': configuration_options, + + } return context @@ -280,7 +287,6 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): form_class = UserHostingKeyForm model = UserHostingKey template_name = 'hosting/virtual_machine_key.html' - success_url = reverse_lazy('hosting:orders') login_url = reverse_lazy('hosting:login') context_object_name = "virtual_machine" @@ -317,7 +323,8 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): context.update({ 'private_key': form.cleaned_data.get('private_key'), 'key_name': form.cleaned_data.get('name'), - 'form': UserHostingKeyForm(request=self.request) + 'form': UserHostingKeyForm(request=self.request), + 'next_url': reverse('hosting:create_virtual_machine') }) # return HttpResponseRedirect(reverse('hosting:key_pair')) @@ -384,6 +391,8 @@ class PaymentVMView(LoginRequiredMixin, FormView): return context def get(self, request, *args, **kwargs): + if 'next' in request.session: + del request.session['next'] try: UserHostingKey.objects.get( diff --git a/opennebula_api/models.py b/opennebula_api/models.py index bf5b036e..1745fb49 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -82,6 +82,7 @@ class OpenNebulaManager(): try: vm_pool = oca.VirtualMachinePool(self.client) vm_pool.info() + return vm_pool except AttributeError: logger.info('Could not connect via client, using oneadmin instead') try: diff --git a/opennebula_api/serializers.py b/opennebula_api/serializers.py index c8878120..6c86776a 100644 --- a/opennebula_api/serializers.py +++ b/opennebula_api/serializers.py @@ -1,4 +1,5 @@ import oca +import ipaddress from rest_framework import serializers @@ -12,7 +13,7 @@ class VirtualMachineTemplateSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) set_name = serializers.CharField(read_only=True, label='Name') name = serializers.SerializerMethodField() - cores = serializers.IntegerField(source='template.vcpu') + cores = serializers.SerializerMethodField() disk = serializers.IntegerField(write_only=True) disk_size = serializers.SerializerMethodField() set_memory = serializers.IntegerField(write_only=True, label='Memory') @@ -41,6 +42,12 @@ class VirtualMachineTemplateSerializer(serializers.Serializer): return manager.get_template(template_id=opennebula_id) + def get_cores(self, obj): + if hasattr(obj.template, 'vcpu'): + return obj.template.vcpu + + return '' + def get_disk_size(self, obj): template = obj.template disk_size = 0 @@ -67,8 +74,7 @@ class VirtualMachineTemplateSerializer(serializers.Serializer): return int(obj.template.memory)/1024 def get_name(self, obj): - # TODO: Filter public- away - return obj.name + return obj.name.strip('public-') class VirtualMachineSerializer(serializers.Serializer): """Serializer to map the virtual machine instance into JSON format.""" @@ -81,9 +87,8 @@ class VirtualMachineSerializer(serializers.Serializer): disk_size = serializers.SerializerMethodField() - ip = serializers.CharField(read_only=True, - source='user_template.ungleich_public_ip', - default='-') + ipv4 = serializers.SerializerMethodField() + ipv6 = serializers.SerializerMethodField() vm_id = serializers.IntegerField(read_only=True, source='id') state = serializers.CharField(read_only=True, source='str_state') price = serializers.SerializerMethodField() @@ -146,4 +151,34 @@ class VirtualMachineSerializer(serializers.Serializer): def get_configuration(self, obj): template_id = obj.template.template_id template = OpenNebulaManager().get_template(template_id) - return template.name + return template.name.strip('public-') + + def get_ipv4(self, obj): + nic = obj.template.nics[0] + if 'vm-ipv6-nat64-ipv4' in nic.network and is_in_v4_range(nic.mac): + return str(v4_from_mac(nic.mac)) + else: + return '-' + + def get_ipv6(self, obj): + nic = obj.template.nics[0] + return nic.ip6_global + + +def hexstr2int(string): + return int(string.replace(':', ''), 16) + +FIRST_MAC = hexstr2int('02:00:b3:39:79:4d') +FIRST_V4 = ipaddress.ip_address('185.203.112.2') +COUNT = 1000 + +def v4_from_mac(mac): + """Calculates the IPv4 address from a MAC address. + + mac: string (the colon-separated representation) + returns: ipaddress.ip_address object with the v4 address + """ + return FIRST_V4 + (hexstr2int(mac) - FIRST_MAC) + +def is_in_v4_range(mac): + return FIRST_MAC <= hexstr2int(mac) < FIRST_MAC + 1000