pexpectation/README.md

190 lines
8.7 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pexpectation
Surcouche à pexpect pour formaliser les enchaînements de questions et éviter certains problèmes dassociation question, réponse quand le contexte nest pas suffisant.
## Principe
On constitue une collection de questions, réponses.
Dans cette collection, certaines questions sont exposées et utilisées pour la méthode expect du module pexpect.
Ce premier lot de question est associé à un contexte suffisamment large pour les discriminer entre elles.
Dautres questions sont liées à ces premières questions et les réponses sont enchaînées.
Pour gérer les embranchements conditionnels, on peut associer à chaque question liée un déclencheur (la réponse à la question précédente).
## Composition
```
.
├── eole_module_expectations
│   ├── eolebase_instance_pexpectations.py
│   ├── __init__.py
│   ├── register_zephir_pexpectations.py
│   └── zephir_instance_pexpectations.py
├── __init__.py
└── pexpectation.py
```
Le corps du module est constitué de deux classes dans le fichier pexpectation.py
* ExpectationCollection ;
* Expectation.
Le sous-module `eole_module_expectations` contient des exemples dutilisation pour des procédures classiques des modules EOLE :
* instance ;
* enregistrement_zephir.
## Utilisation
Soit la transcription simplifiée de la procédure enregistrement_zephir (certains cheminement sont exclus parce quils nont pas de sens dans ce contexte dautomatisation) :
``` dot
digraph G {
subgraph cluster_0 {
style=filled;
color=lightgrey;
node [style=filled,color=white];
edge [style=dotted];
"Voulez-vous établir une configuration réseau minimale" -> "interface connectée sur l'extérieur" [label="oui"];
"interface connectée sur l'extérieur" -> "adresse IP" -> "masque de sous-réseau" -> "adresse de la passerelle";
}
subgraph cluster_1 {
node [style=filled,color=white];
edge [style=dotted];
"Serveur déjà enregistré";
color=lightgrey;
style=filled;
}
subgraph cluster_2 {
node [style=filled,color=white];
edge [style=dotted];
"créer le serveur dans la base du serveur Zéphir" -> "entrez le RNE de létablissement" [label="non"];
"entrez le RNE de létablissement" -> "entrez le n° identifiant le serveur l'application Zéphir" [label="rien"];
"entrez le RNE de létablissement" -> "choix du serveur" [label="RNE"];
color=lightgrey;
style=filled;
}
subgraph cluster_3 {
node [style=filled,color=white];
edge [style=dotted];
"Mise à jour des informations sur le matériel" -> "processeur" -> "disque dur";
color=lightgrey;
style=filled;
}
subgraph cluster_4 {
node [style=filled,color=white];
edge [style=dotted];
"une procédure d'enregistrement à déjà eu lieu pour ce serveur";
color=lightgrey;
style=filled;
}
subgraph cluster_5 {
node [style=filled,color=white];
edge [style=dotted];
"Entrez le numéro de votre choix";
color=lightgrey;
style=filled;
}
subgraph cluster_6 {
node [style=filled,color=white];
edge [style=dotted];
"Entrez l'adresse (nom DNS) du serveur Zéphir" -> "Entrez votre login pour l'application Zéphir" -> "Mot de passe pour l'application Zéphir";
color=lightgrey;
style=filled;
}
"Début de la procédure" -> "Voulez-vous établir une configuration réseau minimale";
"Début de la procédure" -> "Serveur déjà enregistré";
"Voulez-vous établir une configuration réseau minimale" -> "Entrez l'adresse (nom DNS) du serveur Zéphir" [label="non"];
"adresse de la passerelle" -> "Entrez l'adresse (nom DNS) du serveur Zéphir";
"Mot de passe pour l'application Zéphir" -> "créer le serveur dans la base du serveur Zéphir";
"entrez le n° identifiant le serveur l'application Zéphir" -> "une procédure d'enregistrement à déjà eu lieu pour ce serveur";
"entrez le n° identifiant le serveur l'application Zéphir" -> "Mise à jour des informations sur le matériel";
"disque dur" -> "Entrez le numéro de votre choix";
"Entrez le numéro de votre choix" -> "Fin de la procédure";
"Début de la procédure" [shape=Mdiamond];
"Fin de la procédure" [shape=Msquare];
}
```
Ce qui simplémente de la façon suivante :
``` python
expectations = ExpectationCollection()
already_registered = Expectation("""1 -> Désinscrire ce serveur du serveur Zéphir
2 -> Relancer l'enregistrement
3 -> Ne rien faire
Entrez le numéro de votre choix :""", response='3', name='already_registered')
network_configuration = Expectation(""" Procédure d'enregistrement sur le serveur Zéphir
Voulez-vous établir une configuration réseau minimale (O/N) :""", response='N', name='network_configuration')
interface_name = Expectation("""interface connectée sur l'extérieur""", response='ens0', name='interface_name')
network_address = Expectation("""adresse_ip {variable} :""", response='192.168.1.2', name='')
network_netmask = Expectation("""masque de réseau pour {variable} :""", response='255.255.255.0', name='network_netmask')
gateway = Expectation("""adresse de la passerelle :""", response='192.168.1.1', name='gateway')
zephir_address = Expectation("""Entrez l'adresse (nom DNS) du serveur Zéphir :""", response='zephir', name='zephir_address')
zephir_admin = Expectation("""Entrez votre login pour l'application Zéphir (rien pour sortir) :""", response='admin_zephir', name='zephir_admin')
zephir_admin_password = Expectation("""Mot de passe pour l'application Zéphir pour {variable} :""", response='eole', name='zephir_admin_password')
new_server = Expectation("""créer le serveur dans la base du serveur Zéphir (O/N) :""", response='N', name='new_server')
rne = Expectation("""entrez le RNE de l'établissement correspondant au serveur,
(rien pour saisir directement un n° de serveur) :""", response='', name='rne')
server_id = Expectation("""entrez le n° identifiant le serveur l'application Zéphir :""", response='1', name='server_id')
hardware = Expectation("""Mise à jour des informations sur le matériel
matériel (Standard PC (Q35 + ICH9, 2009) par défaut) :""", response='', name='hardware')
processor = Expectation("""processeur ( Intel Core Processor (Skylake, IBRS) 3191 MHz par défaut) :""", response='', name='processor')
harddrive = Expectation("""disque dur (19 Go par défaut) :""", response='', name='harddrive')
key_available = Expectation("""(une procédure d'enregistrement à déjà eu lieu pour ce serveur)
continuer l'enregistrement (O/N) ?""", response='O', name='key_available')
final = Expectation("""1 -> Ne rien faire
2 -> Utiliser la configuration définie sur le serveur Zéphir
3 -> Non disponible
4 -> Modifier la variante du serveur
Entrez le numéro de votre choix :""", response='2', name='final')
expectations.add_expectation(already_registered)
expectations.add_expectation(network_configuration)
expectations.add_expectation(new_server)
expectations.add_expectation(hardware)
expectations.add_expectation(key_available)
expectations.add_expectation(final)
network_configuration.set_next_expectation(interface_name, triggers=['O', 'Oui', 'OUI'])
interface_name.set_next_expectation(network_address)
network_address.set_next_expectation(network_netmask)
network_netmask.set_next_expectation(gateway)
network_configuration.set_next_expectation(zephir_address, triggers=['N', 'Non', 'NON'])
new_server.set_next_expectation(rne, triggers=['N', 'Non', 'NON'])
rne.set_next_expectation(server_id, triggers='')
zephir_address.set_next_expectation(zephir_admin)
zephir_admin.set_next_expectation(zephir_admin_password)
hardware.set_next_expectation(processor)
processor.set_next_expectation(harddrive)
```
Et sutilise comme suit :
``` python
patterns = expectations.get_patterns()
nb_expectations = expectations.count_expectations()
instance_process = pexpect.spawn('/usr/bin/enregistrement_zephir', encoding='utf-8', timeout=60, logfile=sys.stdout)
while some_index < nb_expectations:
p = instance_process.expect(patterns)
if p == 0:
break
if p == 1:
print(f'Some missing expectations for {instance_process.before}{instance_process.after}')
break
pattern = patterns[p]
for expectation in expectations.get_expectations_by_pattern(patterns[p]):
if expectation.is_the_one(instance_process.before):
expectation.answer(instance_process)
break
```
La méthode `get_patterns` renvoie uniquement les premières question des blocs (les questions quon peut discriminer avec le contexte ou qui ne suivent pas obligatoirement une autre question).
La méthode `is_the_one` est celle utilisant le contexte pour déterminer si la question est bien celle attendue.
La méthode `answer` envoie la réponse avec pexpect et enchaîne éventuellement sur les autres questions du bloc, en tenant compte des déclencheurs.