Dans le cadre d'un récent Redimensionner hackday, je souhaitais intégrer une fonctionnalité de bureau à distance dans le navigateur pour les nœuds lancés au nom de l'utilisateur. La plupart des flux de travail soumis à Rescale sont des tâches de traitement par lots qui s'exécutent sans surveillance, mais la possibilité de se connecter à une machine et d'exécuter une interface graphique pour surveiller la simulation en cours peut être utile pour certains utilisateurs.
Une recherche rapide sur Google a révélé une bibliothèque appelée Guacamole cela existe depuis quelques années maintenant. Guacamole se compose de trois composants principaux : une application démon appelée guacd qui traduit entre divers protocoles de bureau distant (VNC, RDC, SSH) et un protocole guacamole personnalisé, un ensemble de bibliothèques Javascript qui restituent le bureau distant en un élément de canevas HTML5 et écoutent les événements de souris et de clavier, et enfin, une application web légère qui reçoit les requêtes HTTP des bibliothèques js et communique avec le démon guacd(*).
Malheureusement, il existe un léger décalage entre l'application Web fournie, un servlet Java et notre pile Web Django existante. Il serait beaucoup plus simple d'intégrer Guacamole dans notre produit existant s'il existait une version python de l'application Web. Heureusement, le docs déclarez que cela devrait être relativement simple à faire. Le code source est bien écrit et il a été assez facile d'obtenir rapidement une version de qualité hackday portée sur python.
Les bibliothèques guacamole js démarrent en faisant une demande de connexion à l'application web. Un socket est ouvert pour guacd à ce stade et se voit attribuer un ID de tunnel qui est ensuite renvoyé au client. Le point clé à souligner ici est que ce socket doit être partagé entre plusieurs requêtes HTTP incluant le même ID de tunnel. Pour ce faire en toute sécurité, une paire de verrous est utilisée pour garantir qu'un seul thread lit ou écrit sur le socket à la fois. L'extrait de code suivant montre comment les instructions guacd sont renvoyées au client via une fonction d'assistance de vue Django :
sockets = {} sockets_lock = threading.RLock() read_lock = threading.RLock() write_lock = threading.RLock() ending_read_request = threading.Event() def _do_read(request, cache_key) : ending_read_request.set() def content() : avec sockets_lock : guac = sockets[cache_key] avec read_lock : ending_read_request.clear() while True : content = guac.read() if content : rendement du contenu else : break if ending_read_request.is_set() : logger.info('Laisser une autre requête prendre le relais.') break # Le marqueur de fin d'instruction donne '0.;' réponse = StreamingHttpResponse( content(), content_type='application/octet-stream') réponse['Cache-Control'] = réponse de retour 'no-cache'
Un aspect intéressant à souligner est que bibliothèques guacamole js diffusera les données du serveur en utilisant une paire alternée de requêtes AJAX. Tout d’abord, une première requête AJAX est effectuée. À un moment donné, une fois cette première connexion ouverte et les données revenant du serveur, une deuxième tentative de connexion est effectuée. L'application Web doit être capable de détecter cela côté serveur, de mettre fin à la première connexion, puis de commencer à renvoyer des données sur la deuxième connexion. Le ReentrantLock utilisé dans le code Java offre un moyen simple de savoir s'il existe d'autres threads en attente, mais le python RLock n'expose pas de méthode similaire. Un événement est utilisé comme mécanisme de signalisation rapide et sale entre les deux requêtes HTTP en cours.
De toute évidence, cela a été piraté le plus rapidement possible et n'est pas adapté à une utilisation en production et contient probablement plus d'un bug. L'une des principales limitations à l'heure actuelle est que les sockets connectées à guacd sont simplement stockées dans un dictionnaire global partagé. En tant que tel, cela ne convient qu'à un serveur HTTP qui s'exécute comme un seul processus avec plusieurs threads gérant les requêtes.
Dans l’ensemble, c’était un petit bout de code amusant à porter. Félicitations aux auteurs de Guacamole pour tout leur travail acharné. Un exemple de projet Django / Guacamole est disponible sur github pour les curieux.
(*) Bien que Guacamole prenne en charge les websockets, l'approche de tunneling HTTP qu'il utilise prête à l'emploi est en fait un avantage lorsque l'on essaie de prendre en charge des navigateurs plus anciens. Malgré les différentes fonctionnalités disponibles pour émuler les comportements des navigateurs modernes, moins vous avez de dépendances aux fonctionnalités HTML5, meilleure est la compatibilité avec les navigateurs existants.
