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'
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) '''
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'
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) '''