From 9753dfd2fc879e0611f750774601ca20bf810dfa Mon Sep 17 00:00:00 2001 From: modulos Date: Thu, 25 May 2017 11:27:49 +0200 Subject: [PATCH 1/2] Add UserHosting public key to opennebula user --- hosting/views.py | 69 +++++++++++++-------- opennebula_api/models.py | 129 ++++++++++++++++++++------------------- 2 files changed, 110 insertions(+), 88 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 3999037b..5ba88e10 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -31,7 +31,7 @@ from .mixins import ProcessVMSelectionMixin from opennebula_api.models import OpenNebulaManager from opennebula_api.serializers import VirtualMachineSerializer,\ - VirtualMachineTemplateSerializer + VirtualMachineTemplateSerializer 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. please try again in a few minutes." + class DjangoHostingView(ProcessVMSelectionMixin, View): template_name = "hosting/django.html" @@ -189,7 +190,8 @@ class SignupView(CreateView): model = CustomUser def get_success_url(self): - next_url = self.request.session.get('next', reverse_lazy('hosting:virtual_machines')) + next_url = self.request.session.get( + 'next', reverse_lazy('hosting:virtual_machines')) return next_url def form_valid(self, form): @@ -243,12 +245,14 @@ class PasswordResetConfirmView(PasswordResetConfirmViewMixin): return self.form_valid(form) 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.') return self.form_invalid(form) 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.') return self.form_invalid(form) @@ -337,6 +341,20 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): 'form': UserHostingKeyForm(request=self.request), }) + 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, + '{ssh_key}'.format(ssh_key=user_key.public_key)) + # return HttpResponseRedirect(reverse('hosting:key_pair')) return render(self.request, self.template_name, context) @@ -385,9 +403,11 @@ class PaymentVMView(LoginRequiredMixin, FormView): user = self.request.user # 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: credit_card_data = last_hosting_order.get_cc_data() context.update({ @@ -475,12 +495,11 @@ class PaymentVMView(LoginRequiredMixin, FormView): except UserHostingKey.DoesNotExist: pass - # Create a vm using logged user vm_id = manager.create_vm( template_id=vm_template_id, - #XXX: Confi + # XXX: Confi specs=specs, ssh_key=user_key.public_key, ) @@ -494,14 +513,16 @@ class PaymentVMView(LoginRequiredMixin, FormView): ) # 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 = UserBillingAddressForm( + billing_address_data) billing_address_user_form.is_valid() billing_address_user_form.save() @@ -553,9 +574,9 @@ class OrdersHostingDetailView(PermissionRequiredMixin, LoginRequiredMixin, Detai vm = manager.get_vm(obj.vm_id) context['vm'] = VirtualMachineSerializer(vm).data except ConnectionRefusedError: - messages.error( request, - 'In order to create a VM, you need to create/upload your SSH KEY first.' - ) + messages.error(request, + 'In order to create a VM, you need to create/upload your SSH KEY first.' + ) return context @@ -595,20 +616,19 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): serializer = VirtualMachineSerializer(queryset, many=True) return serializer.data except ConnectionRefusedError: - messages.error( self.request, - 'We could not load your VMs due to a backend connection \ + messages.error(self.request, + 'We could not load your VMs due to a backend connection \ error. Please try again in a few minutes' - ) + ) self.kwargs['error'] = 'connection' return [] - def get_context_data(self, **kwargs): error = self.kwargs.get('error') if error is not None: print(error) - context = { 'error' : 'connection' } + context = {'error': 'connection'} else: context = super(ListView, self).get_context_data(**kwargs) return context @@ -658,9 +678,10 @@ class CreateVirtualMachinesView(LoginRequiredMixin, View): template = manager.get_template(template_id) configuration_id = int(request.POST.get('configuration')) 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')) @@ -680,10 +701,10 @@ class VirtualMachineView(LoginRequiredMixin, View): vm = manager.get_vm(vm_id) return vm except ConnectionRefusedError: - messages.error( self.request, - 'We could not load your VM due to a backend connection \ + messages.error(self.request, + 'We could not load your VM due to a backend connection \ error. Please try again in a few minutes' - ) + ) return None except Exception as error: print(error) @@ -695,7 +716,7 @@ class VirtualMachineView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): vm = self.get_object() - try: + try: serializer = VirtualMachineSerializer(vm) context = { 'virtual_machine': serializer.data, diff --git a/opennebula_api/models.py b/opennebula_api/models.py index 600b2bd6..3eac0b6e 100644 --- a/opennebula_api/models.py +++ b/opennebula_api/models.py @@ -10,17 +10,18 @@ from oca.pool import WrongNameError from oca.exceptions import OpenNebulaException logger = logging.getLogger(__name__) + class OpenNebulaManager(): """This class represents an opennebula manager.""" def __init__(self, email=None, password=None): - + # Get oneadmin client self.oneadmin_client = self._get_opennebula_client( settings.OPENNEBULA_USERNAME, settings.OPENNEBULA_PASSWORD ) - + # Get or create oppenebula user using given credentials try: 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, password, 'core') logger.debug( - "User {0} does not exist. Created the user. User id = {1}", - email, - opennebula_user - ) + "User {0} does not exist. Created the user. User id = {1}", + email, + opennebula_user + ) return opennebula_user except ConnectionRefusedError: logger.info('Could not connect to host: {host} via protocol {protocol}'.format( - host=settings.OPENNEBULA_DOMAIN, - protocol=settings.OPENNEBULA_PROTOCOL) - ) + host=settings.OPENNEBULA_DOMAIN, + protocol=settings.OPENNEBULA_PROTOCOL) + ) raise ConnectionRefusedError + def _get_user_pool(self): try: user_pool = oca.UserPool(self.oneadmin_client) user_pool.info() except ConnectionRefusedError: logger.info('Could not connect to host: {host} via protocol {protocol}'.format( - host=settings.OPENNEBULA_DOMAIN, - protocol=settings.OPENNEBULA_PROTOCOL) - ) + host=settings.OPENNEBULA_DOMAIN, + protocol=settings.OPENNEBULA_PROTOCOL) + ) raise ConnectionRefusedError return user_pool @@ -84,7 +86,7 @@ class OpenNebulaManager(): vm_pool.info() return vm_pool except AttributeError: - logger.info('Could not connect via client, using oneadmin instead') + logger.info('Could not connect via client, using oneadmin instead') try: vm_pool = oca.VirtualMachinePool(self.oneadmin_client) vm_pool.info(filter=-2) @@ -94,9 +96,9 @@ class OpenNebulaManager(): except ConnectionRefusedError: logger.info('Could not connect to host: {host} via protocol {protocol}'.format( - host=settings.OPENNEBULA_DOMAIN, - protocol=settings.OPENNEBULA_PROTOCOL) - ) + host=settings.OPENNEBULA_DOMAIN, + protocol=settings.OPENNEBULA_PROTOCOL) + ) raise ConnectionRefusedError # For now we'll just handle all other errors as connection errors except: @@ -107,8 +109,7 @@ class OpenNebulaManager(): return self._get_vm_pool() except ConnectionRefusedError: raise ConnectionRefusedError - - + def get_vm(self, vm_id): vm_id = int(vm_id) try: @@ -118,7 +119,7 @@ class OpenNebulaManager(): raise ConnectionRefusedError 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. :param name: A string representation describing the template. Used as label in view. @@ -130,17 +131,17 @@ class OpenNebulaManager(): :param disk_size_price: Price of disk space for VM per GB :param ssh: User public ssh key """ - + template_id = oca.VmTemplate.allocate( self.oneadmin_client, template_string_formatter.format( name=name, vcpu=cores, - cpu=0.1*cores, + cpu=0.1 * cores, size=1024 * disk_size, memory=1024 * memory, # * 10 because we set cpu to *0.1 - cpu_cost=10*core_price, + cpu_cost=10 * core_price, memory_cost=memory_price, disk_cost=disk_size_price, ssh=ssh @@ -159,11 +160,11 @@ class OpenNebulaManager(): disk = template.template.disks[0] image_id = disk.image_id vm_specs = vm_specs_formatter.format( - vcpu=int(specs['cpu']), - cpu=0.1* int(specs['cpu']), - memory=1024 * int(specs['memory']), - - ) + vcpu=int(specs['cpu']), + cpu=0.1 * int(specs['cpu']), + memory=1024 * int(specs['memory']), + + ) vm_specs += """ fs {size} @@ -180,11 +181,11 @@ class OpenNebulaManager(): image_uname = disk.image_uname vm_specs = vm_specs_formatter.format( - vcpu=int(specs['cpu']), - cpu=0.1* int(specs['cpu']), - memory=1024 * int(specs['memory']), - - ) + vcpu=int(specs['cpu']), + cpu=0.1 * int(specs['cpu']), + memory=1024 * int(specs['memory']), + + ) vm_specs += """ fs {size} @@ -197,11 +198,11 @@ class OpenNebulaManager(): image=image, image_uname=image_uname) vm_id = self.client.call(oca.VmTemplate.METHODS['instantiate'], - template.id, - '', - True, - vm_specs, - False) + template.id, + '', + True, + vm_specs, + False) self.oneadmin_client.call( 'vm.update', @@ -210,7 +211,7 @@ class OpenNebulaManager(): {ssh} """.format(ssh=ssh_key) - ) + ) try: self.oneadmin_client.call( oca.VirtualMachine.METHODS['chown'], @@ -219,13 +220,14 @@ class OpenNebulaManager(): self.opennebula_user.group_ids[0] ) 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 - ) + oca.VirtualMachine.METHODS['action'], + 'release', + vm_id + ) return vm_id def delete_vm(self, vm_id): @@ -241,7 +243,8 @@ class OpenNebulaManager(): except socket.timeout as socket_err: logger.info("Socket timeout error: {0}".format(socket_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: logger.info("OSError : {0}".format(os_err)) except ValueError as value_err: @@ -251,27 +254,26 @@ class OpenNebulaManager(): def _get_template_pool(self): try: - template_pool = oca.VmTemplatePool(self.oneadmin_client) - template_pool.info() - return template_pool + template_pool = oca.VmTemplatePool(self.oneadmin_client) + template_pool.info() + return template_pool except ConnectionRefusedError: logger.info('Could not connect to host: {host} via protocol {protocol}'.format( - host=settings.OPENNEBULA_DOMAIN, - protocol=settings.OPENNEBULA_PROTOCOL) - ) + host=settings.OPENNEBULA_DOMAIN, + protocol=settings.OPENNEBULA_PROTOCOL) + ) raise ConnectionRefusedError except: raise ConnectionRefusedError - def get_templates(self): try: public_templates = [ - template - for template in self._get_template_pool() - if 'public-' in template.name - ] - return public_templates + template + for template in self._get_template_pool() + if 'public-' in template.name + ] + return public_templates except ConnectionRefusedError: raise ConnectionRefusedError except: @@ -291,10 +293,8 @@ class OpenNebulaManager(): except: raise ConnectionRefusedError - - 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. :param name: A string representation describing the template. Used as label in view. @@ -327,11 +327,11 @@ class OpenNebulaManager(): template_string_formatter.format( name=name, vcpu=cores, - cpu=0.1*cores, + cpu=0.1 * cores, size=1024 * disk_size, memory=1024 * memory, # * 10 because we set cpu to *0.1 - cpu_cost=10*core_price, + cpu_cost=10 * core_price, memory_cost=memory_price, disk_cost=disk_size_price, ssh=ssh @@ -341,11 +341,12 @@ class OpenNebulaManager(): return 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): self.oneadmin_client.call( oca.User.METHODS['passwd'], self.opennebula_user.id, new_password - ) + ) From 35f129a4c7666df88a596fae0e3abac38845ba2f Mon Sep 17 00:00:00 2001 From: modulos Date: Thu, 25 May 2017 15:50:10 +0200 Subject: [PATCH 2/2] Add user generated public ssh keys as well --- hosting/views.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index 5ba88e10..af86b825 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -341,21 +341,20 @@ class GenerateVMSSHKeysView(LoginRequiredMixin, FormView): 'form': UserHostingKeyForm(request=self.request), }) - 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) + 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, - '{ssh_key}'.format(ssh_key=user_key.public_key)) + # 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, + '{ssh_key}'.format(ssh_key=user_key.public_key)) - # return HttpResponseRedirect(reverse('hosting:key_pair')) return render(self.request, self.template_name, context) def post(self, request, *args, **kwargs):