Collaborations
inter-machines
Dans un réseau de machines fortement
connectées, il est naturel de parfois souhaiter
que plusieurs machines participent à
l'exécution d'un seul programme, en
collaboration. Parfois, un programme sur une
machine donnée peut désirer prendre avantage de
la puissance de calcul d'une autre machine,
accéder à la partie non-partagée du système de
fichiers d'une autre machine, utiliser des
programmes qui ne sont exploitables que sur
certaines machines seulement, vérifier les
idiosyncrasies d'une machine particulière.
Par exemple, on peut vouloir offrir divers
services aux usagers de l'hypertoile ailleurs
que sur le seul serveur Web d'un réseau, de
manière à ne pas surcharger la machine qui
roule ce serveur. Les scripts CGI peuvent alors
faire en sorte d'exécuter l'essentiel de
l'application d'un service particulier sur une
machine sur lequel ce service est dédié.
Ce module Local.remote
implante une architecture client-serveur par
laquelle il est assez facile au programmeur de
distribuer une application sur plusieurs
machines. Une première machine peut demander de
l'aide ou des services à d'autres machine en
démarrant un serveur sur chacune d'entre elles,
cette première machine est alors un client pour
tous ces serveurs. Ces serveurs éloignés sont
capables, sur demande, d'évaluer des
expressions Python, d'appeler des fonctions ou
d'exécuter des énoncés Python. Par le jeu
d'installations préalables et des importations
de Python, on peut sans grand effort déclencher
des actions même assez lourdes sur des machines
de service.
À chaque instance de la classe
Serveur, correspond une
connexion de réseau vers un serveur sur une
autre machine. Après avoir été établie, la
connexion est en général gardée active afin de
servir plusieurs requêtes sur la machine
éloignée. La connexion vers une machine
particulière n'est abandonnée qu'une fois
accompli l'ensemble du travail à effectuer par
cette machine.
Pour fabriquer un objet
Serveur, on utilise:
from Local import remote
serveur = remote.Serveur(CHEMIN, TRACE)
Par cette action, le serveur est
automatiquement téléchargé au besoin sur
toutes les machines mentionnées dans
CHEMIN et sauvé dans le
fichier ~USAGER/.python-remote-VERSION,
où VERSION identifie la
version du protocole utilisé. Ce fichier est
réutilisé s'il existe déjà, pourvu qu'il
démarre correctement et identifie de son côté
la bonne version du protocole.
Si CHEMIN n'est pas fourni
ou vaut None, la machine courante est
utilisée, dans installer ni utiliser un
serveur éloigné. Sinon, CHEMIN doit être une
chaîne qui contient une ou plusieurs
spécifications USAGER@MACHINE séparées
par des deux-points. Dans ce cas, tous les
USAGER@MACHINE à
l'exception du dernier définissent des
connexions intermédiaires successives vers
la destination finale. Si la partie
USAGER@ est absente, on
présume le même usager que celui de la
machine précédente dans la chaîne des
connexions.
Si la première machine apparaît dans le
fichier ~/.ssh/config ou dans le
fichier ~/.ssh/known_hosts, alors
la première connexion est établie en
utilisant SSH. On présume alors que
l'échange de clés SSH a déjà été effectué
et qu'aucun mot de passe explicite n'est
requis. Dans les autres cas, le fichier
~/.netrc doit associer à
cet machine une identification de compte et
un mot de passe, et la connexion utilise
alors un mélange de Telnet et de FTP, tout
en inhibant les techniques traditionnelles
de contrôle de flot de terminal.
Dans le cas le plus habituel, qui est
aussi le plus simple, l'argument CHEMIN est le nom de la
machine où doit rouler le serveur.
Si TRACE n'est pas donné ou
vaut zéro, il n'y aura aucune filature du
protocole. Sinon, TRACE possède une valeur
entre 1 et 3.
- La valeur 1 provoque un rapport de
filature sur l'erreur standard locale
pour l'aspect client du protocole.
- La valeur 2 a le même effet que la
valeur 1, mais en plus, produit en plus
un fichier sur MACHINE pour l'aspect
serveur du protocole.
- La valeur 3 a la même effet que les
valeurs 1 et 2, mais en plus, produit
en plus une copie de la communication
Telnet sur l'erreur standard
locale.
Un contexte d'évaluation est établi pour
la connexion courante sur le serveur éloigné.
Le module Python pickle
sérialise toute l'information transmise dans
les deux directions, l'utilisateur doit donc
se limiter aux structures de programmations
que ce module peut manipuler.
Voici les méthodes d'un objet de classe
Serveur:
serveur.appeler(FONCTION, [ARGUMENT]...)
serveur.appliquer(FONCTION, ARGUMENTS)
serveur.evaluer(EXPRESSION)
serveur.executer(ÉNONCÉ)
serveur.quitter()
La méthode appeler
appelle FONCTION sur la machine
éloignée en lui passant zéro, un ou plusieurs
ARGUMENTs, puis retourne le
résultat de cet appel. FONCTION est une chaîne
contenant une expression Python qui, évaluée,
donne une fonction appelable, et chaque
ARGUMENT doit pouvoir être
traité par le module pickle.
La méthode appliquer fait la
même chose, sauf que tous les ARGUMENTS sont fournis dans
une seule séquence plutôt que séparément. La
méthode evaluer évalue
EXPRESSION sur la machine
éloignée et retourne la valeur de cette
expression. La méthode
executer provoque
l'exécution de ÉNONCÉ sur la machine
éloignée et retourne None. EXPRESSION, aussi bien que
ÉNONCÉ, sont des chaînes
contenant un fragment de programme Python.
Finalement, la méthode
quitter coupe la connexion
réseau au serveur éloigné.
Voici un exemple simplet. Supposons que
griffon est le nom d'une
machine pour laquelle nous avons déjà un
accès SSH garanti par l'échange approprié de
clés. Pour faire calculer la valeur de
l'expression Python 2
+ 3 sur
griffon, on peut faire:
from Local import remote
serveur = remote.Serveur('griffon')
print serveur.evaluer('2 + 3')
serveur.quitter()
L'information donnée ci-après peut faciliter
la compréhension du fonctionnement interne de
ce module, mais elle n'est pas nécessaire pour
l'utiliser.
Voici une description du protocole de
communication entre le client et le
serveur.
- Dès son démarrage, le serveur
s'identifie par une ligne sur la sortie
standard, suivie d'une ligne blanche.
Cette identification contient le niveau
de protocole utilisé.
- Une fois démarré, le serveur s'engage
dans une boucle dans laquelle il lit une
requête en provenance du client sur son
entrée standard suivie d'une ligne vide,
et après avoir calculé une réponse à
destination du client, la retourne sur sa
sortie standard, suivie d'une ligne
vide.
- Les requêtes, aussi bien que les
réponses, utilisent les chaînes dans le
format produit par le module
pickle, puis comprimés
par le module zlib, puis
transformées par le module
base64, le résultat est
un bloc de lignes de moins de 80
caractères chacune et constituées de
caractères imprimables.
- Toutes les requêtes sont traitées
dans un même contexte de variables
locales Python, du côté du serveur;
l'effet des requêtes s'y accumule donc et
une requête peut prendre avantage de
l'effet des requêtes antérieures.
- Une requête vide amène une réponse
vide, et l'arrêt du serveur.
Le serveur est démarré automatiquement sur
la machine éloignée au moment de la
fabrication d'instance de
Serveur. Pour y parvenir, un
shell est établi via SSH ou Telnet, et la
commande suivante est lancée:
~USAGER/.python-remote-VERSION [OPTION]... [CHEMIN]
Les variables USAGER et VERSION ont déjà été
expliquées dans ce document. Quant aux
options, elles peuvent être:
- -i
- Utiliser des réponses indirectes
lorsque les réponses sont grosses. Dans
le cas de Telnet en particulier, une
grosse réponse sera plus efficacement
transmise par la création d'un fichier
pour le contenir, qui sera ensuite obtenu
par FTP.
- -t
- Prendre des dispositions pour
produire une filature du protocole dans
un fichier, sur la machine où ce serveur
s'exécute.
Si CHEMIN n'est pas fourni,
alors le serveur est destiné à la machine
courante. Sinon, ce serveur est simplement un
mandataire vers le serveur final sur une
autre machine et ne fait que transiter
l'information dans les deux directions:
CHEMIN indique alors
comment lancer ce serveur final à partir de
la machine du serveur courant.
|
|
|