def logout_idle_users(self):
     """Logs off idle users for all nodes in this cluster."""
     for node in self.active:
         try:
             osutil_node = osutils.get_os_object(node.ip, settings.MEDIA_ROOT + str(self.app.ssh_key))
             osutil_node.user_cleanup(10)
         except HostNotConnectableError:
             # Ignore this host that doesn't seem to be ssh'able, but log it as an error
             log.warning('Node %s is NOT sshable and should be looked into')
    def get_stats(self):
        """
        Count number of sessions on all nodes. Get available headroom for cluster.
        Return two as a tuple (number_of_users, available_headroom)
        """
        number_of_users = 0
        for node in self.active:
            try:
                osutil_node = osutils.get_os_object(node.ip, settings.MEDIA_ROOT + str(self.app.ssh_key))
                number_of_users += len(osutil_node.sessions)
            except HostNotConnectableError:
                # Ignore this host that doesn't seem to be ssh'able, but log it as an error
                log.warning('Node %s is NOT sshable and should be looked into')

        return (number_of_users, self.avail_headroom)
 def _map_app_cluster_inuse(self, app_pk):
     """
     Returns a list of tuples, sorted by 'priority' from lowest to highest: [ (ip,numInUse), (ip,numInUse), .... ]
     Index 0 of the returned list has the priority closest to 0.  Here a low number indicates high priority.
     numInUse are the number of clients currently using a given host
     This function only considers instances in state '2'
     """
     app_map = []
     nodes = self.active.order_by('priority')
     for host in nodes:
         try:
             osutil_node = osutils.get_os_object(host.ip, settings.MEDIA_ROOT + str(self.app.ssh_key))
             cur_users = len(osutil_node.sessions)
             app_map.append((host, cur_users))
         except HostNotConnectableError:
             # Ignore this host that doesn't seem to be ssh'able, but log it as an error
             log.warning('Node %s is NOT sshable and should be looked into')
     return app_map
Example #4
0
    def run(self, app):
        # Create an instance of the logger
        log = get_logger('vdi')

        # Create the cluster object to help us manage the cluster
        cluster = AppCluster(app.pk)

        # Clean up all idle users on all nodes for this application cluster
        log.debug('APP NAME %s' % app.name)
        cluster.logout_idle_users()

        log.debug("Checking for active clusters")
        for node in cluster.active:
            log.debug("Found active host")
            osutil_node = osutils.get_os_object(
                node.ip, settings.MEDIA_ROOT + str(node.application.ssh_key))
            user_experience_tools.process_user_connections(osutil_node)

        # Handle vms we were waiting on to boot up
        booting = driver_tools.get_instances(cluster.booting)
        for vm in booting:
            dns_name = vm.public_addresses[0]
            log.debug('ASDF = %s' % dns_name)
            if dns_name.find("amazonaws.com") > -1:
                # Resolve the domain name into an IP address
                # This adds a dependancy on the 'host' command
                output = Popen(["host", dns_name],
                               stdout=PIPE).communicate()[0]
                ip = '.'.join(
                    re.findall(
                        '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',
                        output)[0])
                try:
                    # TODO: remove the hard coded '3389' & '22' below.  '3389' is for RDP and '22' is for SSH
                    # TODO: remove the arbitrary '3' second timeout below
                    socket.create_connection((ip, 22), 3)
                    #socket.create_connection((ip,3389),3)
                except Exception as e:
                    log.debug("Server %s is not yet available" % ip)
                    pass
                else:
                    instance = Instance.objects.filter(instanceId=vm.id)[0]
                    booting.remove(vm)
                    instance.ip = ip
                    instance.state = 2
                    instance.save()
                    log.debug(
                        "Moving instance %s into enabled state with ip %s" %
                        (instance.instanceId, ip))
        num_booting = len(booting)
        if num_booting > 0:
            log.debug(
                "Application cluster '%s' is still waiting for %s cluster nodes to boot"
                % (cluster.name, len(booting)))

        # Consider if the cluster needs to be scaled
        log.debug('Considering %s app cluster for scaling ...' % cluster.name)
        # Should I scale up?
        log.debug(
            '%s is avail (%s) < req (%s)?' %
            (cluster.app.name, cluster.avail_headroom, cluster.req_headroom))
        if cluster.avail_headroom < cluster.req_headroom:
            # Yes I should scale up
            space_needed = cluster.req_headroom - cluster.avail_headroom
            servers_needed = int(
                math.ceil(space_needed / float(cluster.app.users_per_small)))
            log.debug(
                'Available headroom (%s) is less than the cluster headroom goal (%s).  Starting %s additional cluster nodes now'
                %
                (cluster.avail_headroom, cluster.req_headroom, servers_needed))
            for i in range(servers_needed):
                cluster.start_node()

        # Handle instances we are supposed to shut down
        toTerminate = []
        for host in cluster.shutting_down:
            log.debug('ASDASDASD    %s' % host.instanceId)
            try:
                osutil_node = osutils.get_os_object(
                    host.ip,
                    settings.MEDIA_ROOT + str(self.host.application.ssh_key))
                log.debug(
                    'Node %s is waiting to be shut down and has %s connections'
                    % (host.ip, osutil_node.sessions))
                if osutil_node.sessions == []:
                    toTerminate.append(host)
                    host.shutdownDateTime = datetime.now()
                    host.save()
            except HostNotConnectableError:
                # Ignore this host that doesn't seem to be ssh'able, but log it as an error
                log.warning(
                    'Node %s is NOT sshable and should be looked into.  It is currently waiting to shutdown'
                )
        driver_tools.terminate_instances(toTerminate)

        # Should I scale down?
        overprov_num = cluster.avail_headroom - cluster.req_headroom
        log.debug('overprov (%s) avail (%s) required(%s)' %
                  (overprov_num, cluster.avail_headroom, cluster.req_headroom))
        # Reverse the list to try to remove the servers at the end of the waterfall
        inuse_reverse = cluster.inuse_map
        inuse_reverse.reverse()
        for (host, inuse) in inuse_reverse:
            # The node must have 0 sessions and the cluster must be able to be smaller while still leaving enough headroom
            if int(inuse) == 0 and overprov_num >= cluster.app.users_per_small:
                overprov_num = overprov_num - cluster.app.users_per_small
                host.state = 4
                host.save()
                log.debug(
                    'Application Server %s has no sessions.  Removing that node from the cluster!'
                    % host.ip)
        return 'scaling complete @TODO put scaling event summary in this output'
Example #5
0
    def run(self, app):
        # Create an instance of the logger
        log = get_logger('vdi')

        # Create the cluster object to help us manage the cluster
        cluster = AppCluster(app.pk)

        # Clean up all idle users on all nodes for this application cluster
        log.debug('APP NAME %s'%app.name)
        cluster.logout_idle_users()

        log.debug("Checking for active clusters")
        for node in cluster.active:
            log.debug("Found active host")
            osutil_node = osutils.get_os_object(node.ip, settings.MEDIA_ROOT + str(node.application.ssh_key))
            user_experience_tools.process_user_connections(osutil_node)

        # Handle vms we were waiting on to boot up
        booting = driver_tools.get_instances(cluster.booting)
        for vm in booting:
            dns_name = vm.public_addresses[0]
            log.debug('ASDF = %s' % dns_name)
            if dns_name.find("amazonaws.com") > -1:
                # Resolve the domain name into an IP address
                # This adds a dependancy on the 'host' command
                output = Popen(["host", dns_name], stdout=PIPE).communicate()[0]
                ip = '.'.join(re.findall('(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)', output)[0])
                try:
                    # TODO: remove the hard coded '3389' & '22' below.  '3389' is for RDP and '22' is for SSH
                    # TODO: remove the arbitrary '3' second timeout below
                    socket.create_connection((ip,22),3)
                    #socket.create_connection((ip,3389),3)
                except Exception as e:
                    log.debug("Server %s is not yet available" % ip)
                    pass
                else:
                    instance = Instance.objects.filter(instanceId=vm.id)[0]
                    booting.remove(vm)
                    instance.ip = ip
                    instance.state = 2
                    instance.save()
                    log.debug("Moving instance %s into enabled state with ip %s" % (instance.instanceId,ip))
        num_booting = len(booting)
        if num_booting > 0:
            log.debug("Application cluster '%s' is still waiting for %s cluster nodes to boot" % (cluster.name,len(booting)))


        # Consider if the cluster needs to be scaled
        log.debug('Considering %s app cluster for scaling ...' % cluster.name)
        # Should I scale up?
        log.debug('%s is avail (%s) < req (%s)?' % (cluster.app.name, cluster.avail_headroom, cluster.req_headroom))
        if cluster.avail_headroom < cluster.req_headroom:
            # Yes I should scale up
            space_needed = cluster.req_headroom - cluster.avail_headroom
            servers_needed = int(math.ceil(space_needed / float(cluster.app.users_per_small)))
            log.debug('Available headroom (%s) is less than the cluster headroom goal (%s).  Starting %s additional cluster nodes now' % (cluster.avail_headroom,cluster.req_headroom,servers_needed))
            for i in range(servers_needed):
                cluster.start_node()


        # Handle instances we are supposed to shut down
        toTerminate = []
        for host in cluster.shutting_down:
            log.debug('ASDASDASD    %s' % host.instanceId)
            try:
                osutil_node = osutils.get_os_object(host.ip, settings.MEDIA_ROOT + str(self.host.application.ssh_key))
                log.debug('Node %s is waiting to be shut down and has %s connections' % (host.ip, osutil_node.sessions))
                if osutil_node.sessions == []:
                    toTerminate.append(host)
                    host.shutdownDateTime = datetime.now()
                    host.save()
            except HostNotConnectableError:
                # Ignore this host that doesn't seem to be ssh'able, but log it as an error
                log.warning('Node %s is NOT sshable and should be looked into.  It is currently waiting to shutdown')
        driver_tools.terminate_instances(toTerminate)


        # Should I scale down?
        overprov_num = cluster.avail_headroom - cluster.req_headroom
        log.debug('overprov (%s) avail (%s) required(%s)' % (overprov_num,cluster.avail_headroom,cluster.req_headroom))
        # Reverse the list to try to remove the servers at the end of the waterfall
        inuse_reverse = cluster.inuse_map
        inuse_reverse.reverse()
        for (host,inuse) in inuse_reverse:
            # The node must have 0 sessions and the cluster must be able to be smaller while still leaving enough headroom
            if int(inuse) == 0 and overprov_num >= cluster.app.users_per_small:
                overprov_num = overprov_num - cluster.app.users_per_small
                host.state = 4
                host.save()
                log.debug('Application Server %s has no sessions.  Removing that node from the cluster!' % host.ip)
        return 'scaling complete @TODO put scaling event summary in this output'
Example #6
0
def connect(request, app_pk=None, conn_type=None):
    # Check that the user has permissions to access this
    app = Application.objects.filter(pk=app_pk)[0]
    if not request.user.has_perm('vdi.use_%s' % app.name):
        return HttpResponseRedirect(settings.LOGIN_URL)

    # Get an AppCluster instance
    cluster = AppCluster(app_pk)

    if conn_type == None:
        # A conn_type was not explicitly requested, so let's decide which one to have the user use
        if request.META["HTTP_USER_AGENT"].find('MSIE') == -1:
            # User is not running IE, give them the default connection type
            conn_type = settings.DEFAULT_CONNECTION_PROTOCOL
        else:
            # User is running IE, give them the rdpweb connection type
            conn_type = 'rdpweb'

    if request.method == 'GET':
        user_experience = UserExperience.objects.create(user=request.user,
                                                        application=app)
        user_experience.access_date = datetime.today()
        user_experience.save()
        try:
            # Determine which host this user should use
            host = cluster.select_host()
        except NoHostException:
            # Start a new instance immedietly and redirect the user back to this page after 20 seconds
            # Only boot a new node if there are none currently booting up
            if len(cluster.booting) == 0:
                cluster.start_node()
            return render_to_response(
                'vdi/app_not_ready.html', {
                    'app': cluster.app,
                    'reload_s': settings.USER_WAITING_PAGE_RELOAD_TIME,
                    'reload_ms': settings.USER_WAITING_PAGE_RELOAD_TIME * 1000
                })

        # Random Password Generation string
        chars = string.ascii_letters + string.digits
        password = ''.join(choice(chars) for x in range(6))
        log.debug("THE PASSWORD IS: %s" % password)

        # Get IP of user
        # Implement firewall manipulation of instance
        log.debug('Found user ip of %s' % request.META["REMOTE_ADDR"])

        # Grab the proper osutils object
        osutil_obj = osutils.get_os_object(
            host.ip, settings.OPUS_SECURE_UPLOADS + str(cluster.app.ssh_key))
        if osutil_obj:
            status, error_string = osutil_obj.add_user(request.user.username,
                                                       password)
            if status == False:
                return HttpResponse(error_string)
        else:
            return HttpResponse('Your server was not reachable')

        if osutil_obj.__class__.__name__ == 'Linux':
            conn_type = 'nx'
        elif osutil_obj.__class__.__name__ == 'Windows':
            # For windows only, ddd the created user to the Remote Desktop Users group
            status = osutil_obj.enable_rdp_for_user(request.user.username)
            if status == False:
                HttpResponse(error_string)
            else:
                log.debug("Added user %s to the 'Remote Desktop Users' group" %
                          request.user.username)

        # This is a hack for NC WISE only, and should be handled through a more general mechanism
        # TODO refactor this to be more secure
        rdesktopPid = Popen([
            "rdesktop", "-u", request.user.username, "-p", password, "-s",
            cluster.app.path, host.ip
        ],
                            env={
                                "DISPLAY": ":1"
                            }).pid
        # Wait for rdesktop to logon
        sleep(3)

        if conn_type == 'rdp':
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response('vdi/connect.html', {
                'username': request.user.username,
                'password': password,
                'app': cluster.app,
                'ip': host.ip
            },
                                      context_instance=RequestContext(request))
            '''
            This code is commented out because it really compliments nxproxy.  Originally nxproxy and vdi were developed
            together but nxproxy has not been touched in a while.  I'm leaving this here for now because it is was hard to
            write, and it would be easy to refactor (probably into the nxproxy module) if anyone felt the need to do so.
            NOTE: There is a vestige of this code in the vdi URLconf

            elif conn_type == 'nxweb':
                return _nxweb(host.ip,user.username,password,cluster.app)
            elif conn_type == 'nx':
                # TODO -- This url should not be hard coded
                session_url = 'https://opus-dev.cnl.ncsu.edu:9001/nxproxy/conn_builder?' + urlencode({'dest' : host.ip, 'dest_user' : user.username, 'dest_pass' : password, 'app_path' : cluster.app.path})
                return HttpResponseRedirect(session_url)
            '''
        elif conn_type == 'rdpweb':
            tsweb_url = '/media/vdi/TSWeb/'
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response(
                'vdi/rdpweb.html', {
                    'tsweb_url': tsweb_url,
                    'app': cluster.app,
                    'ip': host.ip,
                    'username': request.user.username,
                    'password': password
                })
        elif conn_type == 'nx':
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response('vdi/connect.html', {
                'username': request.user.username,
                'password': password,
                'app': cluster.app,
                'ip': host.ip
            },
                                      context_instance=RequestContext(request))
    elif request.method == 'POST':
        # Handle POST request types
        if conn_type == 'rdp':
            return _create_rdp_conn_file(request.POST["ip"],
                                         request.user.username,
                                         request.POST["password"], cluster.app)
        elif conn_type == 'nx':
            return connection_tools.nx_conn_builder(request.POST["ip"],
                                                    request.user.username,
                                                    request.POST["password"],
                                                    cluster.app)
    '''
Example #7
0
def connect(request, app_pk=None, conn_type=None):
    # Check that the user has permissions to access this
    app = Application.objects.filter(pk=app_pk)[0]
    if not request.user.has_perm('vdi.use_%s' % app.name):
        return HttpResponseRedirect(settings.LOGIN_URL)

    # Get an AppCluster instance
    cluster = AppCluster(app_pk)
    
    if conn_type == None:
        # A conn_type was not explicitly requested, so let's decide which one to have the user use
        if request.META["HTTP_USER_AGENT"].find('MSIE') == -1:
            # User is not running IE, give them the default connection type
            conn_type = settings.DEFAULT_CONNECTION_PROTOCOL
        else:
            # User is running IE, give them the rdpweb connection type
            conn_type = 'rdpweb'

    if request.method == 'GET':
        user_experience = UserExperience.objects.create(user=request.user, application=app)
        user_experience.access_date = datetime.today()
        user_experience.save()
        try:
            # Determine which host this user should use
            host = cluster.select_host()
        except NoHostException:
            # Start a new instance immedietly and redirect the user back to this page after 20 seconds
            # Only boot a new node if there are none currently booting up
            if len(cluster.booting) == 0:
                cluster.start_node()
            return render_to_response('vdi/app_not_ready.html',
                {'app': cluster.app,
                'reload_s': settings.USER_WAITING_PAGE_RELOAD_TIME,
                'reload_ms': settings.USER_WAITING_PAGE_RELOAD_TIME * 1000})

        # Random Password Generation string
        chars=string.ascii_letters+string.digits
        password = ''.join(choice(chars) for x in range(6))
        log.debug("THE PASSWORD IS: %s" % password)

        # Get IP of user
        # Implement firewall manipulation of instance
        log.debug('Found user ip of %s' % request.META["REMOTE_ADDR"])

        # Grab the proper osutils object
        osutil_obj = osutils.get_os_object(host.ip, settings.OPUS_SECURE_UPLOADS + str(cluster.app.ssh_key))
        if osutil_obj:    
            status, error_string = osutil_obj.add_user(request.user.username, password)
            if status == False:
                return HttpResponse(error_string)
        else:
            return HttpResponse('Your server was not reachable')

        if osutil_obj.__class__.__name__ == 'Linux':
            conn_type = 'nx'
        elif osutil_obj.__class__.__name__ == 'Windows':
            # For windows only, ddd the created user to the Remote Desktop Users group
            status = osutil_obj.enable_rdp_for_user(request.user.username)
            if status == False:
                HttpResponse(error_string)
            else:
                log.debug("Added user %s to the 'Remote Desktop Users' group" % request.user.username)

        # This is a hack for NC WISE only, and should be handled through a more general mechanism
        # TODO refactor this to be more secure
        rdesktopPid = Popen(["rdesktop","-u",request.user.username,"-p",password, "-s", cluster.app.path, host.ip], env={"DISPLAY": ":1"}).pid
        # Wait for rdesktop to logon
        sleep(3)

        if conn_type == 'rdp':
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response('vdi/connect.html', {'username' : request.user.username,
                                                        'password' : password,
                                                        'app' : cluster.app,
                                                        'ip' : host.ip},
                                                        context_instance=RequestContext(request))
            '''
            This code is commented out because it really compliments nxproxy.  Originally nxproxy and vdi were developed
            together but nxproxy has not been touched in a while.  I'm leaving this here for now because it is was hard to
            write, and it would be easy to refactor (probably into the nxproxy module) if anyone felt the need to do so.
            NOTE: There is a vestige of this code in the vdi URLconf

            elif conn_type == 'nxweb':
                return _nxweb(host.ip,user.username,password,cluster.app)
            elif conn_type == 'nx':
                # TODO -- This url should not be hard coded
                session_url = 'https://opus-dev.cnl.ncsu.edu:9001/nxproxy/conn_builder?' + urlencode({'dest' : host.ip, 'dest_user' : user.username, 'dest_pass' : password, 'app_path' : cluster.app.path})
                return HttpResponseRedirect(session_url)
            '''
        elif conn_type == 'rdpweb':
            tsweb_url = '/media/vdi/TSWeb/'
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response('vdi/rdpweb.html', {'tsweb_url' : tsweb_url,
                                                    'app' : cluster.app,
                                                    'ip' : host.ip,
                                                    'username' : request.user.username,
                                                    'password' : password})
        elif conn_type == 'nx':
            user_experience.file_presented = datetime.today()
            user_experience.save()
            return render_to_response('vdi/connect.html', {'username' : request.user.username,
                                                        'password' : password,
                                                        'app' : cluster.app,
                                                        'ip' : host.ip},
                                                        context_instance=RequestContext(request))
    elif request.method == 'POST':
        # Handle POST request types
        if conn_type == 'rdp':
            return _create_rdp_conn_file(request.POST["ip"], request.user.username, request.POST["password"], cluster.app)
        elif conn_type =='nx':
            return connection_tools.nx_conn_builder(request.POST["ip"], request.user.username, request.POST["password"], cluster.app)

    '''