Page suivante Page précédente Table des matières

4. Programmation avancée sous Expect : Une interface blindée !

Par David Fisher

Les liens qui pointent vers les scripts sont inclus dans la Linux Gazette. Ceux qui aboutissent à des pages de manuel Expect à l'extérieur sont repérés par une "(*)".

4.1 Introduction

Cet article implique du lecteur une connaissance approfondie des bases du langage de scripts Expect et la recherche de solutions avancées. Pour plus d'infos sur Expect, voir :

http://www.cotse.com/dlf/man/expect/index.html (*)

Lors de la conception d'automates basés sur le langage de programmation Expect, un des problèmes les plus épineux auxquels sont confrontés beaucoup de développeurs est d'assurer une communication avec des connexions récalcitrantes et des terminaux distants. La procédure send_expect détaillée ici fournit le moyen d'assurer cette communication avec des systèmes distants, ainsi que la gestion de l'édition et de la réémission de la ligne de commande. Là où un programmeur enverrait normalement une ligne de commande et en attendrait l'écho depuis le système distant, cette procédure remplace ces lignes de code et fournit l'interface la plus fiable qu'il m'ait été donné de voir.

Ses caractéristiques sont notamment :

Typiquement, la communication avec des processus locaux (c.à.d. ceux qui tournent sur la même station que le processus Expect) ne pose pas de problème et ne nécessite pas le recours aux solutions détaillées ici. Cependant, des processus externes peuvent apporter nombre de problèmes qui, s'ils n'affectent pas forcément la communication, auront un impact sur la capacité d'un automate à en déterminer le succès.

Les cas où la communication est corrompue ne sont pas toujours immédiatement évidents : une commande endommagée peut engendrer un message d'erreur mais des données qui l'ont été peuvent être malgré tout considérées comme valides, l'erreur ne ressortant pas tout de suite, et peuvent causer une multitude de problèmes. C'est pourquoi il est nécessaire de s'assurer que l'intégralité de la chaîne transmise est correctement reçue en écho du système distant.

L'idée qui sous-tend cette interface est d'envoyer la chaîne de commande, à l'exception de son caractère de terminaison (habituellement un retour-chariot) et d'en regarder l'écho. Si on peut faire correspondre les deux au moyen des expressions régulières dans les clauses Expect, alors le caractère de terminaison est envoyé et la transmission considérée comme bonne. En cas d'impossibilité à déterminer si tout s'est bien passé, la ligne de commande est mise à blanc au lieu d'être envoyée et d'autres modes de transmission sont utilisés.

Dans de nombreuses occasions, il suffit simplement d'attendre un écho exact de la chaîne. Cependant, si vous lisez cet article, j'imagine que vous vous êtes heurtés plus ou moins aux mêmes problèmes que moi en programmant sous Expect et que vous y recherchez la solution. Si c'est juste l'enrichissement de votre culture personnelle qui vous motive, les problèmes surviennent lors de l'automatisation d'une session sur une machine distante dans un labo ou à l'autre bout de la Terre. Des caractères étranges apparaissent sur la connexion et le terminal auquel vous êtes branché fait des choses bizarres avec son écho, mais à part ça, tout fonctionne. Il devient très difficile de savoir si ce qui a été envoyé a été correctement reçu dès l'instant où il y a du bruit sur la connexion, des codes de contrôle de terminal sont introduits dans l'écho, sans parler des dépassements de délais d'attente (timeout) dûs au serveur entre le programme d'automatisation et la session distante. Cette interface survit à tout cela et si elle n'arrive pas à transmettre avec succès la chaîne, c'est que la connexion avec le système distant est perdue.

Le code fourni ici est exécutable et doit être incorporé à chaque système sur lequel il doit être utilisé. D'ordinaire, on doit ajouter des commandes liées au système en fonction des besoins du système cible. De plus, ce code fait usage de simples appels à la commande puts (*) pour émettre des messages d'état - ceux-ci devraient être modifiés en fonction du mécanisme de journalisation employé sur le système considéré. Une dernière mise en garde, et je n'insisterai jamais assez : portez toujours des lunettes de sécurité.

4.2 Configuration de l'interface

Voici les procédures fournies dans cet article :

L'interface est initialisée avec la procédure send_expect_init qui met en place toutes les données globales requises par les autres procédures. Voyez la section sur le contrôle du comportement de l'interface pour avoir des explications sur les paramètres. Cette procédure est lancée une fois, en début d'exécution (avant que l'interface ne soit utilisée). On peut la lancer une deuxième fois si nécessaire, afin de restaurer les réglages.

La procédure send_only est une enveloppe pour la commande exp_send (*) et est utilisée par send_expect pour transmettre des chaînes. Le seul cas où elle est invoquée directement concerne les chaînes non renvoyées en écho, comme les mots de passe et les constantes de type caractère sur plusieurs octets, comme le break du telnet (control-]).

La procédure send_expect, détaillée au prochain paragraphe, constitue en fait l'interface entre l'automate et ses processus distants.

Au bout du compte, la procédure send_expect_report sert en fin d'exécution à produire les statistiques de l'interface aux fins de débogage. On peut aussi la lancer en cours de traitement si des états incrémentaux sont nécesaires.

4.3 Utilisation de la procédure send_expect

Une fois que l'interface a été initialisée grâce à send_expect_init et qu'un processus a été engendré, elle est prête à l'emploi en utilisant la syntaxe :


send_expect id commande;


id = le "spawn id" de la session vers laquelle envoyer la commande
commande = toute la ligne de commande, en incluant le retour-chariot terminal, s'il y en a un.

Cette syntaxe, ainsi que l'implémentation des listes d'expressions-actions, s'accommodent des applications à sessions multiples.

Beaucoup de gens qui suivent les exemples fournis dans la documentation ont tendance à produire du code sujet aux mêmes erreurs, car ils les considèrent comme les meilleurs exemples, alors qu'ils ne sont que cela, de simples exemples. Ce sont les petits détails que l'on omet par soucis de simplification qui font la différence entre du code en béton et du code qui finira par s'écrouler. Ceux que vous trouverez ici sont simples mais mettent l'accent sur les détails, et là où on vous l'assure, une implémentation complète est fournie à titre d'exemple. Généralement, la procédure send_expect ne remplace que deux lignes de code dans un système installé.

Voici la syntaxe complète qui permet d'utiliser de façon correcte l'interface :


if { [send_expect $id $command] != 0} {
    ## ici, votre gestion des erreurs
}

4.4 Comment ça marche

L'interface fait appel à quatre modes de transmission différents, dans l'ordre :

Si un mode échoue, la ligne de commande est nettoyée par un envoi du control-U standard, le tampon est vidé, et le mode suivant essayé. Pour chacun d'eux, à l'exception du dernier, on peut spécifier une tolérance d'échec avec : sendGlobals(ModeXFailMax), où X est au choix 1,2 ou 3.

Si cette valeur maximale est positive, une fois que le nombre d'échecs pour ce mode l'a dépassé, celui-ci n'est plus utilisé. S'il est égal à 0, alors chaque mode est essayé pour chaque transmission, indépendamment du nombre d'échecs. Chacun des modes fait appel à la procédure send_only en tant qu'enveloppe pour exp_send. Si elle retourne une erreur, la connexion a très probablement été perdue et le spawn id est vérifié pour s'assurer que la session est toujours active. L'erreur est renvoyée à send_expect, qui à son tour en répercute une à la procédure appelante.

Pour des processus locaux et des connexions distantes robustes, le mode 1 suffit généralement. Le mode 2 peut être nécessaire si le système distant est un peu lent. Lors de connexions à des routeurs et des clusters qui fournissent un contrôle de terminal rudimentaire, le mode 3 s'est révélé inestimable. Le mode 4 sert rarement et fait simplement office de secours pour le mode 3.

4.5 Diagnostics à fenêtre dynamique

Expect permet de contrôler la sortie de ses diagnostics internes et de ses tentatives de résolution d'expressions grâce à la commande " exp_internal" (*). L'interface send_expect s'en sert pour créer un fichier de diagnostics correspondant à chaque essai de transmission - à chaque fois, un nouveau fichier de log de diagnostic est créé avec exp_internal -f. Si la transmission est couronnée de succès, le fichier est supprimé. Dans le cas contraire, il est renommé à l'aide de la syntaxe send.n.i.command.diags


n = le numéro de l'échec
i = le "spawn id" du canal où cela s'est produit
command = le premier mot de la ligne de commande qui n'a pas pu être envoyée correctement.

S'il vous est déjà arrivé de lire un fichier de log de 30 Méga-octets avec toutes les traces du début à la fin de l'exécution, vous comprendrez pourquoi ceci est nécessaire. Les fichiers de diagnostics créés grâce à cette méthode font en général moins de 2 kilo-octets, et comme ils sont directement associés aux échecs (le fichier fenêtré étant supprimé dans le cas de transmissions réussies), le débogage est bien plus efficace.

Le fichier de diagnostics à fenêtre dynamique représente le moyen le plus rapide et le plus compact d'implanter une sortie de diagnostics complets pendant l'exécution d'une commande send. Si la transmission réussit, le fichier est supprimé. En cas d'échec, il est fermé et renommé, et lors de la prochaine invocation de la commande send, un nouveau fichier sera créé. De ce fait, on obtient de très petits fichiers (comparativement) incluant tous les diagnostics d'Expect ainsi que les messages définis par l'utilisateur, depuis le tout début de la tentative d'envoi de la commande.

Dans l'idéal, sans échec en cours d'exécution, il ne devrait y avoir qu'un seul fichier de diagnostics à un moment donné, nommé send.diagnostics. S'il en exite plusieurs, chacun est associé à un échec particulier et devrait être utilisé pour en effectuer le débogage.

4.6 Contrôle du comportement de l'interface

Le tableau sendGlobals contient tous les paramètres employés par l'interface et peut être modifié lors de l'exécution afin d'en contrôler le fonctionnement. Cette section s'attachera à expliquer le sens de ces paramètres et la façon dont ils peuvent être modifiés. Voir send_expect_init pour connaître leurs valeurs initiales.

Les éléments limite d'échec (Mode1FailMax, Mode2FailMax, et Mode3FailMax) déterminent le nombre d'échecs permis pour modes 1, 2 et 3 (respectivement). Une valeur égale à zéro enlève cette limitation, et tout entier positif fixe le nombre maximal d'échecs pour ce mode avant qu'il ne soit plus utilisé par l'interface. Le dernier mode ne comporte pas de limite.

L'élément useMode permet au système de déterminer le mode de transmission à utiliser en premier, afin que les moins fiables (le premier et le deuxième) soient ignorés. Les valeurs autorisées pour ce paramètre sont 1, 2, 3 et 4. Le mode par défaut (1) remplacera toute valeur invalide.

Si l'on ne considère pas les erreurs de transmission comme fatales, on peut attribuer à l'élément sendErrorSeverity une valeur plus tolérante. Notez que ce paramètre n'est pas employé de façon interne, de ce fait, si l'automate ne l'utilise pas, cela n'affectera pas l'interface.

L'élément kill définit le caractère qui permet de tuer la ligne de commande, par défaut selon le standard Gnu, control-U.

Le paramètre diagFile donne son nom au fichier de diagnostics interne temporaire (tiré de exp_internal).

logDiags permet aussi de désactiver toute sortie de diagnostic afin d'accélérer l'exécution, mais soyez prévenu que cette option rendra le débogage bien plus ardu.

Le paramètre sleepTime, lorsqu'il est affecté d'une valeur entière positive, endort l'interface pendant la durée de temps indiquée avant de lancer la transmission. C'est utile si l'automate semble plus rapide que ce que peut gérer le système distant - une perte continue de caractères dans la phase de transmission indique généralement un problème de vitesse et de synchronisation, et cette option est fournie pour y remédier.

Les éléments interval et delay représentent les deux options de la liste send_slow, laquelle est utilisée par les deuxième et troisième modes.

A titre expérimental, il est recommandé de modifier ces paramètres par le biais de l'automate, lors de l'exécution, plutôt qu'éditer directement les valeurs par défaut dans la procédure d'initialisation. Une fois que des réglages corrects ont été trouvés, ces dernières peuvent être changées pour les refléter.

4.7 Echecs

Quand la procédure send_expect renvoit un échec, c'est l'indication d'une connexion au système distant non fiable, ce que confirmera une vérification manuelle. Ce genre d'échec est fatal pour la fiabilité de tout automate et doit donc être corrigé pour que le système tourne correctement.

Si la procédure elle-même semble mal fonctionner, les fichiers de diagnostics créés lors de l'échec devraient aider au débogage. Cette interface n'a pas encore été mise en défaut avec une connexion fiable.

Copyright © 1999, David Fisher. Paru dans le n° 48 de la Linux Gazette de Décembre 1999.

Traduction française de Joel Sagnes.


Page suivante Page précédente Table des matières