From 1f10f04a9d971ed1db7ad6183659ef777c382251 Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 11 May 2017 00:11:33 -0500 Subject: [PATCH 1/3] Fixed ssh key error, Now an existing user is not ask for his credit card again --- hosting/models.py | 22 ++++- hosting/opennebula_functions.py | 8 ++ hosting/static/hosting/js/payment.js | 21 +++++ hosting/templates/hosting/payment.html | 22 ++++- .../hosting/virtual_machine_key.html | 3 +- hosting/views.py | 94 ++++++++++++++++--- utils/forms.py | 2 +- 7 files changed, 155 insertions(+), 17 deletions(-) diff --git a/hosting/models.py b/hosting/models.py index b59b7f5a..9fa4a100 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -91,6 +91,18 @@ class VirtualMachineType(models.Model): } + @classmethod + def get_vm_templates(self, user): + opennebula_client = OpenNebulaManager( + email=user.email, + password=user.password, + ) + + templates = opennebula_client.get_vm_templates() + for template in templates: + print(OpenNebulaManager.parse_vm(template)) + return templates + class VirtualMachinePlan(AssignPermissionsMixin, models.Model): @@ -196,7 +208,7 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): # Get opennebula client opennebula_client = OpenNebulaManager( email=user.email, - password=user.password[:20], + password=user.password, ) # Get vm given the id @@ -216,7 +228,7 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): # Get opennebula client opennebula_client = OpenNebulaManager( email=user.email, - password=user.password[:20], + password=user.password, ) # Get vm pool @@ -279,6 +291,12 @@ class HostingOrder(AssignPermissionsMixin, models.Model): self.cc_brand = stripe_charge.source.brand 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): user = models.ForeignKey(CustomUser) diff --git a/hosting/opennebula_functions.py b/hosting/opennebula_functions.py index a00bb037..d9421d48 100644 --- a/hosting/opennebula_functions.py +++ b/hosting/opennebula_functions.py @@ -171,6 +171,14 @@ class OpenNebulaManager: return vm_id + def get_vm_templates(self): + template_pool = oca.VmTemplatePool(self.oneadmin_client) + template_pool.info() + a = template_pool[0] + import pdb + pdb.set_trace() + return template_pool + def get_vm(self, email, vm_id): # Get vm's vms = self.get_vms(email) diff --git a/hosting/static/hosting/js/payment.js b/hosting/static/hosting/js/payment.js index c75e3d48..d06a5a3d 100644 --- a/hosting/static/hosting/js/payment.js +++ b/hosting/static/hosting/js/payment.js @@ -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'); $form.submit(payWithStripe); diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index b0a09812..90da3870 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -8,7 +8,7 @@

Billing Address


-
+ {% for field in form %} {% csrf_token %} {% bootstrap_field field show_label=False type='fields'%} @@ -23,6 +23,17 @@
+ {% if credit_card_data.last4 %} + +
Credit Card
+
Last 4: *****{{credit_card_data.last4}}
+
Type: {{credit_card_data.cc_brand}}
+ + + + {% else %} + +
@@ -76,6 +87,7 @@ + {% endif %}
@@ -110,6 +122,7 @@
+ {% if stripe_key %} {%endif%} +{% if credit_card_data.last4 and credit_card_data.cc_brand %} + + +{%endif%} + {%endblock%} diff --git a/hosting/templates/hosting/virtual_machine_key.html b/hosting/templates/hosting/virtual_machine_key.html index 8a0221ae..0062e90b 100644 --- a/hosting/templates/hosting/virtual_machine_key.html +++ b/hosting/templates/hosting/virtual_machine_key.html @@ -58,7 +58,7 @@ {% if private_key %}
- {% trans "Warning!"%}{% trans "You can view your SSH private key once. Copy it or if it wasn't downloaded automatically, just click on Download to start it."%} + {% trans "Warning!"%}{% trans "You can view your SSH private key once. Don't lost your key"%}
@@ -101,6 +101,7 @@ // Remove anchor from body document.body.removeChild(a); + {%endif%} diff --git a/hosting/views.py b/hosting/views.py index cf1cf879..f5be0fb4 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -22,7 +22,7 @@ from stored_messages.api import mark_read from membership.models import CustomUser, StripeCustomer 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.mailer import BaseEmail from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder, HostingBill, UserHostingKey @@ -160,7 +160,7 @@ class SignupView(CreateView): model = CustomUser 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 def form_valid(self, form): @@ -226,18 +226,19 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): context_object_name = "virtual_machine" def get_context_data(self, **kwargs): - try: - user_key = UserHostingKey.objects.get( - user=self.request.user - ) - except UserHostingKey.DoesNotExist: - user_key = None - context = super( GenerateVMSSHKeysView, self ).get_context_data(**kwargs) + try: + user_key = UserHostingKey.objects.get( + user=self.request.user + ) + + except UserHostingKey.DoesNotExist: + user_key = None + context.update({ 'user_key': user_key }) @@ -256,21 +257,75 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): if form.cleaned_data.get('private_key'): context.update({ '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) + del(context['form']) + context.update({ + 'form': form + }) + form = UserHostingKeyForm(request=self.request) + print("context", context) + + # return HttpResponseRedirect(reverse('hosting:key_pair')) 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): template_name = 'hosting/payment.html' login_url = reverse_lazy('hosting:login') 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): 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({ 'stripe_key': settings.STRIPE_API_PUBLIC_KEY }) @@ -281,7 +336,12 @@ class PaymentVMView(LoginRequiredMixin, FormView): form = self.get_form() if form.is_valid(): + + # Get billing address data + billing_address_data = form.cleaned_data + context = self.get_context_data() + specifications = request.session.get('vm_specs') vm_template = specifications.get('vm_template', 1) @@ -318,6 +378,15 @@ class PaymentVMView(LoginRequiredMixin, FormView): # Create Billing Address billing_address = form.save() + # 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() + # Create a Hosting Order order = HostingOrder.create(vm_plan=plan, customer=customer, billing_address=billing_address) @@ -384,6 +453,7 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai permission_required = ['view_hostingorder'] model = HostingOrder + class OrdersHostingListView(LoginRequiredMixin, ListView): template_name = "hosting/orders.html" login_url = reverse_lazy('hosting:login') @@ -468,7 +538,7 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, View): login_url = reverse_lazy('hosting:login') # model = VirtualMachinePlan # context_object_name = "virtual_machine" - permission_required = ['view_virtualmachineplan', 'cancel_virtualmachineplan'] + permission_required = [] # fields = '__all__' # def get_context_data(self, **kwargs): diff --git a/utils/forms.py b/utils/forms.py index dd6f7a85..c50b1a0b 100644 --- a/utils/forms.py +++ b/utils/forms.py @@ -100,7 +100,7 @@ class EditCreditCardForm(forms.Form): class BillingAddressForm(forms.ModelForm): - token = forms.CharField(widget=forms.HiddenInput()) + token = forms.CharField(widget=forms.HiddenInput(), required=False) class Meta: model = BillingAddress From 1a6e1a44d86aab66446f7179e53c39583b734123 Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 11 May 2017 00:38:53 -0500 Subject: [PATCH 2/3] fixing formatting --- hosting/opennebula_functions.py | 6 ------ .../hosting/virtual_machine_detail.html | 17 +++++++++-------- hosting/templates/hosting/virtual_machines.html | 4 ++-- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/hosting/opennebula_functions.py b/hosting/opennebula_functions.py index d9421d48..d2796ec8 100644 --- a/hosting/opennebula_functions.py +++ b/hosting/opennebula_functions.py @@ -174,9 +174,6 @@ class OpenNebulaManager: def get_vm_templates(self): template_pool = oca.VmTemplatePool(self.oneadmin_client) template_pool.info() - a = template_pool[0] - import pdb - pdb.set_trace() return template_pool def get_vm(self, email, vm_id): @@ -206,9 +203,6 @@ class OpenNebulaManager: return vm_pool - - - class HostingManageVMAdmin(admin.ModelAdmin): client = None oneadmin_client = None diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index e0909b24..06e5a53c 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -85,7 +85,7 @@
{% trans "Disk"%}
- {{virtual_machine.disk_size}} GiB + {{virtual_machine.disk_size|floatformat:2}} GiB
@@ -150,14 +150,15 @@

{% trans "Current status"%}

+
- {% if virtual_machine.status == 'pending' %} - {{virtual_machine.get_status_display}} - {% elif virtual_machine.status == 'online' %} - {{virtual_machine.get_status_display}} - {% elif virtual_machine.status == 'canceled'%} - {{virtual_machine.get_status_display}} - {% endif %} + {% if virtual_machine.state == 'ACTIVE' %} + {{virtual_machine.state}} + {% elif virtual_machine.state == 'POWEROFF' %} + {{virtual_machine.state}} + {% else %} + {{virtual_machine.state}} + {% endif %}
diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index 2de8aa48..431dcb82 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -22,8 +22,8 @@ {% for vm in vms_opennebula %} - {{vm.deploy_id}} - {{vm.price}} CHF + {{vm.name}} + {{vm.price|floatformat:2}} CHF {% if vm.state == 'ACTIVE' %} From 387354084942a47401704a72d04588ae4416957a Mon Sep 17 00:00:00 2001 From: Levi Date: Fri, 12 May 2017 00:56:35 -0500 Subject: [PATCH 3/3] =?UTF-8?q?Force=20user=20to=20generate=20ssh=20key=20?= =?UTF-8?q?in=20order=20to=20create=20a=20VM=20#3147.=20As=20user=20I=20wa?= =?UTF-8?q?nt=20to=20terminate=20a=20VM=20using=20web=20interface=C2=A0#31?= =?UTF-8?q?48.=20Change=20password=20in=20opennebula=20when=20user=20chang?= =?UTF-8?q?e=20his=20password=20on=20hosting=20app=20#3149?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hosting/forms.py | 6 +- hosting/models.py | 19 ++- hosting/opennebula_functions.py | 46 ++++-- .../hosting/virtual_machine_detail.html | 16 +- .../hosting/virtual_machine_key.html | 17 ++- .../templates/hosting/virtual_machines.html | 10 ++ hosting/views.py | 143 +++++++++++++++--- 7 files changed, 214 insertions(+), 43 deletions(-) diff --git a/hosting/forms.py b/hosting/forms.py index 7323bdf3..b143140d 100644 --- a/hosting/forms.py +++ b/hosting/forms.py @@ -101,7 +101,9 @@ class UserHostingKeyForm(forms.ModelForm): # print(self.fields) 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): return self.request.user @@ -109,8 +111,6 @@ class UserHostingKeyForm(forms.ModelForm): def clean(self): cleaned_data = self.cleaned_data - print(cleaned_data) - if not cleaned_data.get('public_key'): private_key, public_key = UserHostingKey.generate_keys() cleaned_data.update({ diff --git a/hosting/models.py b/hosting/models.py index 9fa4a100..892aa45d 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -185,18 +185,31 @@ class VirtualMachinePlan(AssignPermissionsMixin, models.Model): instance.assign_permissions(user) return instance - def cancel_plan(self): + def cancel_plan(self, vm_id): self.status = self.CANCELED_STATUS self.save(update_fields=['status']) + @classmethod + def terminate_opennebula_vm(self, user, vm_id): + + opennebula_client = OpenNebulaManager( + user.email, + user.password, + ) + + return opennebula_client.terminate_vm(vm_id) + + @classmethod def create_opennebula_vm(self, user, specs): + # import pdb + # pdb.set_trace() + # Init opennebula manager using given user opennebula_client = OpenNebulaManager( user.email, - user.password[0:20], - create_user=True + user.password, ) # Create a vm in opennebula using given specs diff --git a/hosting/opennebula_functions.py b/hosting/opennebula_functions.py index d2796ec8..aca5424d 100644 --- a/hosting/opennebula_functions.py +++ b/hosting/opennebula_functions.py @@ -35,7 +35,7 @@ class OpenNebulaManager: '11': 'CLONING_FAILURE', } - def __init__(self, email=None, password=None, create_user=True): + def __init__(self, email=None, password=None): # Get oneadmin client self.oneadmin_client = self._get_opennebula_client( @@ -43,9 +43,6 @@ class OpenNebulaManager: settings.OPENNEBULA_PASSWORD ) - if not create_user: - return - # Get or create oppenebula user using given credentials self.opennebula_user = self._get_or_create_user( email, @@ -121,9 +118,17 @@ class OpenNebulaManager: 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): vm_id = None try: + # We do have the vm_template param set. Get and parse it # and check it to be in the desired range. # We have 8 possible VM templates for the moment which are 1x, 2x, 4x ... @@ -136,6 +141,9 @@ class OpenNebulaManager: {disk_type} {size} + + {ssh_key} + """ vm_id = oca.VirtualMachine.allocate( @@ -145,7 +153,8 @@ class OpenNebulaManager: vcpu=specs.get('cores'), cpu=0.1 * specs.get('cores'), 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.group_ids[0] ) - # oca.VirtualMachine.chown( - # vm_id, - - # ) except socket.timeout as socket_err: logger.error("Socket timeout error: {0}".format(socket_err)) @@ -171,6 +176,29 @@ class OpenNebulaManager: 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() diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 06e5a53c..04c50279 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -170,16 +170,28 @@ {% csrf_token %} - + + +
+
+ {% if messages %} +
+ {% for message in messages %} + {{ message }} + {% endfor %} +
+ {% endif %} +
+