Cette page contient la documentation technique du code du logiciel GUIA.
Les sources sont disponibles ici : http://trac.sn.auf.org/guia
Le code est développé par OusmaneWilane. JérômeSantini, JeanChristopheAndré et ThomasNoël définissent avec lui le cahier des charges, et le font/feront évoluer au fur et à mesure des besoins.
Sommaire
Vue générale (rappel)
L'application est bâtie autour de trois composantes principales:
Noyau de l'application : il gère les données de base, c'est-à-dire les données fonctionnelles comme définit dans l'analyse (utilisateur, groupe, abonnement, groupes, extra_*, etc.).
Interface utilisateur : il s'agit de l'interface de gestion des données de base. Pour l'instant c'est surtout l'interface Web qui nous intéresse, mais toute l'API est disponible en Python pour d'autres interfaces.
Greffons : les application ajoutées pour les traitements métiers associés aux données de base. Par exemple : la gestion de la base MySQL pour l'authentification, la gestion d'une base MySQL pour la messagerie, etc.
Le noyau et ses greffons communiquent via des signaux (noyau vers greffon) et exceptions (greffon vers noyau).
GUIA est développé en Python dans le cadre de travail Django. Ce cadre nous permet d'éviter de perdre du temps en réinventant la roue.
Greffons
Principes
Chaque greffon dispose d'un sous-répertoire contenant un constructeur __init__.py pour l'initialisation de la classe métier principale.
La classe principale du greffon est liée à la classe utilisateur par un lien foreign key Django.
Si elle doit gérer des champs extra, elle invoque la classe extra_utilisateur pour la persistance des données (variable/valeur) en invoquant la méthode save() de celle-ci et en inhibant la sienne.
Ensuite la classe définit sa logique métier qu'elle connaît mieux que personne dans son usine (usine.py justement).
Chargement des greffons par le noyau
Lors du démarrage de l'application, tous les greffons sont chargés main/models.py par :
# à la fin de main/models.py : modules = map(__import__, greffons.GREFFONS_INSTALLES)
sachant de greffons.GREFFONS_INSTALLES est défini dans main/greffons/__init__.py
Coté interface admin : ce qu'affiche le greffon
Les greffons sont des applications Django, plus exactement des modèles Django. Comme Django est intelligent, si le modèle du greffon est lié à un objet du noyau par une clé distante, l'interface admin de l'objet noyau sera automatiquement complétée par les champs proposée par le modèle du greffon. C'est tout. C'est un peu magique, c'est Django.
Voir par exemple : noyau/greffons/auth/__init__.py
Réaction d'un greffon lors d'une action sur le noyau : émission de signaux par le noyau
L'usine reçoit les notifications de modifications d'objets leurs pemettant de réagir. Ces notifications sont acheminés via des signaux, qui peuvent être ceux intégrés à Django (pre_save, post_save, etc.) ou des signaux personnalisés. Chaque signal vient avec l'objet qui a émit le signal.
Pour recevoir un signal, les méthodes de l'usine s'enregistrent au niveau du signal auquel elles veulent réagir. Par exemple, au niveau de l'usine du greffon «auth» :
dispatcher.connect(Passwd, sender=utilisateur, signal=signals.models.post_save)
Dans cet exemple, à chaque fois qu'un utilisateur sera enregistré (c'est-à-dire après l'appel à utilisateur.save) la methode Passwd du greffon sera lancée. Cette méthode recevra en argument l'objet modifié, et devra alors faire son traitement métier ; dans le cas présent mettre à jour la base nss-mysql.
L'émission de signaux reste dans le seul sens (coeur => greffons) et chaque greffon utilise un mécanisme adapté de mise à jour asynchrones de ses données pour éviter que le coeur plante à cause des problèmes des greffons.
Les greffons sont autonomes en termes de gestion de leurs données internes, la seule contrainte qu'on leurs impose est la cohérence des interfaces qu'ils fournissent au modèle de base. Autrement dis ils sont libres d'organiser leurs objets comme ils veulent. Ils peuvent stocker leurs données de configuration générale dans la base principale de l'API tant qu'ils sont les seuls à y accéder.
En cas de problème sur un greffon : levée d'une exception par le greffon
Si un greffon rencontre un problème lors de son travail, il lève une exception. Le noyau est programmé pour réagir en retenant cette erreur dans un journal des erreurs (voir ci-dessous).
Reprise après erreur : journalisation des problèmes
Principe : le noyau maintient un journal des erreurs survenues lors de l'appel aux greffons. Pour cela, lors de chaque émission d'un signal vers les greffon, si une exception est retournée par un greffon, le noyau stocke dans le journal :
- le type d'objet en cause : utilisateur, groupe, abonnement, ...
- l'identifiant concerné (autrement dit, on ne stocke par l'objet lui-même mais une référence vers cet objet)
- le greffon qui a provoqué l'erreur
- le signal qui a provoqué l'erreur, c'est-à-dire l'action demandée au greffon : ajout/modification, suppression, ...
à vérifier : chaque triplet { ref_objet , greffon , action } doit être unique dans le journal (la clé). Si un objet a provoqué une erreur lors de deux appels consécutifs identiques à un greffon, on ne doit stocker que la dernière erreur survenue (OusmaneWilane: En fait on stocke une réfèrence à l'objet donc on fait rien s'il y a déjà une réfèrence)
Le journal est un ensemble d'objets "journal" décrit dans le modèle Django du noyau. Cet objet possède une méthode reprise(self) qui sait rappeler le greffon en cause en lui renvoyant l'objet et l'action à effectuer (via un signal, comme toujours). Si ce nouvel appel se passe bien, le status se met à "True" via un flag).
Les greffons doivent donc savoir qu'ils peuvent recevoir un signal pour un objet qui avait échoué (il faut faire un peu attention dans la programmation des actions des greffons, qu'ils soient bien protégés à ce niveau).
A coté de cela :
lors de la suppression d'un objet du noyau (méthode delete), celui-ci doit vider les entrées du journal correspondantes à l'objet, s'il y en a
si un objet dans le noyau est créé ou modifié (méthode save) et que cela ne soulève aucune exception de la part des greffons, il faut également vider le journal des entrées correspondantes
on pourra disposer au niveau du noyau d'une fonction reprise_totale qui effectuera une reprise sur chaque élément signalé du journal, c'est-à-dire qu'on considère le journal comme une liste de tâches à reprendre
Vérification de cohérence / consistence
Une autre idée à développer est que chaque greffon puisse recevoir un signal verification qui lui demandera de vérifier qu'un objet est bien pris en compte dans sa base métier. Il lévera une exception dans le cas contraire, et le noyau pourra journaliser cette erreur (ou bien juste balancer un gros message d'avertissement).
Cela permettra d'avoir une technique de vérification globale de toute la base, via un cron nocturne par exemple... Mais le signal verification ne sera sans doute pas facile à programmer pour tous les greffons. On verra.
Accès ligne de commande (CLI)
Les outils lignes de commande, programmés en Python, accèdent directement au modèle proposé par Django. Les signaux sont donc automatiquement déclenchés pour la notification des greffons et chaque greffon prend ses responsabilités.
Dans un premier temps nous fournissons toutes les commandes type adduser, addgroup et passwd les plus proches possibles des classiques, fournissant les propriétés intéressantes ajoutées au modèle de données que vous avez retenu. Ces commandes sont écrites de sortes qu'elles puissent être invoquées comme commande ou comme modules à importer dans d'autres applications Python (et toujours aucun soucis à se faire pour la propagation des signaux).
Les systèmes d'extraction
À partir d'un système comme Django, on peut extraire les données assez facilement via des templates. Pour la plupart des extractions il ne sera pas nécessaire à l'administrateur système de savoir programmer en Python.
Chaque système d'extraction disposera d'un fichier de configuration par défaut. Par exemple pour un système qui générera le fichier /etc/passwd via une extraction, on indiquera le shell par défaut. Ce shell par défaut pourra être éventuellement écrasé pour tel ou tel utilisateur s'il existe des informations spécifiques pour cet utilisateur (voir plus loin la notion de champ extra dans la base de donnée).