Administration du Raspberry Pi en Python
Introduction
Administrer le Raspberry Pi en utilisant seulement la ligne de commande et le shell peut s’avérer frustrant et parfois compliqué. La programmation et la syntaxe shell montrent rapidement leurs limites dès qu’il s’agit de créer du code complexe basé sur des classes, des modules et des fonctions. Une fois de plus, comme nous allons le découvrir au sein de ce chapitre, Python simplifie l’administration du Raspberry Pi.
Python est souvent utilisé pour l’écriture de scripts d’administration système dont le but est de nettoyer des fichiers obsolètes, lancer des commandes externes, lister des fichiers, créer des répertoires, etc. Ces opérations sont facilement gérées via le module os car il intègre une panoplie de fonctions permettant de facilement communiquer avec le système d’exploitation. Aussi, les fonctions que contient ce module sont nombreuses et cette introduction ne couvre que les plus importantes.
Naviguer dans le système de fichiers avec les modules os et pwd
Les scripts précédemment écrits sont simples et très peu interactifs. L’information recherchée y est la plupart du temps écrite en dur. Nous utiliserons dans ce qui suit la fonction input() pour rendre les prochains scripts interactifs. Cette fonction accepte en paramètre une chaîne de caractères à afficher à l’écran . Elle retourne ce que l’utilisateur tape au clavier. Dans le REPL, cela donne le résultat suivant :
>>> jour = input('Quel jour sommes-nous ? ')
Quel jour sommes-nous ? Lundi
>>> print(jour)
Lundi
Cette fonction rend ainsi le programme dynamique en posant à l’utilisateur des questions et en lui demandant des informations. Au cours de votre usage du Raspberry Pi, vous serez amené à gérer des utilisateurs, à en ajouter et à en supprimer. Sur un système UNIX comme Raspbian, la base de données des utilisateurs est représentée par le fichier /etc/passwd. Ce fichier plat contient la liste des utilisateurs habilités à se connecter au système, ainsi que d’autres informations telles que le shell par défaut, le répertoire personnel, etc. Encore une fois, Python est doté d’un module pour vous assister dans cette tâche. Il s’agit du module pwd.
Le premier exemple consiste à interroger la base et à récupérer des informations concernant un utilisateur. Son nom sera donné depuis la ligne de commande. pwd dispose de deux fonctions pour interroger la base : getpwnam() et getpwuid(), les deux fonctions retournant une instance de la classe struct_passwd. À son tour, cette classe expose les informations de l’utilisateur sous forme d’attributs : pw_name, pw_passwd... (Chapitre_3/user_1.py) :
2 import pwd
3 import sys
4
5
6 def main():
7 utilisateur = input("Tapez l'utilisateur à rechercher: ")
8 infos = None
9
10 try:
11 infos = pwd.getpwnam(utilisateur)
12 except KeyError:
13 print("L'utilisateur '{0}'...
Interagir avec l’interpréteur Python via le module sys
Un autre module très largement utilisé pour l’écriture de scripts est le module sys. En effet, celui-ci permet de communiquer avec l’interpréteur Python afin de récupérer le nom du système d’exploitation, la version de Python utilisée, etc.
Essayons d’importer le module et d’interroger l’interpréteur Python :
>>> import sys
>>> print(sys.version)
3.7.3 (default, Jul 25 2020, 13:03:44)
[GCC 8.3.0]
>>> print(sys.version_info)
sys.version_info(major=3, minor=7, micro=3, releaselevel='final', serial=0)
>>> print(sys.executable)
/usr/bin/python3
>>> sys.platform
'linux'
>>> sys.prefix
'/usr'
Nous pouvons aussi obtenir la liste des modules Python installés sur notre Raspberry Pi :
>>> print(sys.modules.keys())
dict_keys(['sys', 'builtins', '_frozen_importlib', '_imp', '_thread',
'_warnings', '_weakref', 'zipimport',
Ainsi que les différents chemins depuis lesquels l’interpréteur Python charge ces modules et où ceux-ci sont installés :
>>> print(sys.path)
['', '/usr/lib/python37.zip', '/usr/lib/python3.7'...
Lancer des commandes shell avec le module subprocess
Depuis un script, il est souvent nécessaire d’exécuter des commandes externes, ou commandes UNIX, exécutables via un shell Bash. Le module subprocess permet d’exécuter une commande et de récupérer le code de retour de celle-ci. Le module subprocess permet de récupérer le résultat généré sous forme de flux texte via l’exécution d’une commande afin de pouvoir travailler avec celui-ci. Ce module va nous permettre de lancer des commandes shell pour récupérer, encore une fois, des informations sur le Raspberry Pi. Pour pouvoir lancer une commande, il faut utiliser la fonction call() du module comme ceci :
>>> import subprocess
>>> subprocess.call(['ls', '/'], shell=False)
bin boot dev etc home lib lib32 lib64 lost+found media
mnt opt proc root run sbin sys tmp usr var
0
Notez l’appel à la fonction avec comme argument shell=False. Cela signifie que Python n’invoque pas de shell pour exécuter la commande passée en paramètre mais appelle directement la commande. Notez aussi le 0 après le résultat de la commande. Ce chiffre correspond au code retour...
Chercher des fichiers avec le module glob
Python propose plusieurs modules pour rechercher des fichiers dans le système de fichiers. Nous abordons ici le module glob. Quelle est l’origine de ce nom ? Il fait référence à un vieux programme UNIX utilisé pour trier un ou plusieurs fichiers en fonction d’expressions régulières. À cette époque, celles-ci étaient assez basiques, en voici quelques-unes :
-
Le caractère * capture n’importe quel caractère, y compris une chaîne de caractères vide une ou plusieurs fois.
-
Le caractère ? capture n’importe quel caractère une seule fois.
-
Le caractère ** exécute la recherche récursivement.
La recherche de caractères s’est depuis nettement améliorée grâce notamment aux expressions régulières PCRE (Perl Compatibles Regular Expressions) qui sont largement plus expressives et performantes. Néanmoins, même si les expressions régulières PCRE ont supplanté le globbing dans les programmes complexes, celui-ci est encore très largement utilisé dans le shell. En effet, lorsque vous tapez ce qui suit dans la console, vous utilisez sans le savoir le globbing :
pi@raspberrypi:~/python-raspberrypi-3e-edition/Chapitre_3 $ ls /etc/*.conf
/etc/adduser.conf /etc/host.conf ...
Comparer des fichiers ou répertoires avec le module filecmp
La bibliothèque standard de Python propose le module filecmp pour vérifier si deux fichiers ou deux répertoires sont identiques. L’utilisation du module s’articule essentiellement autour de deux utilitaires qui sont :
-
la fonction cmp pour comparer deux fichiers
-
le constructeur dircmp pour comparer deux répertoires
La fonction cmp prend en paramètre deux arguments qui sont les deux fichiers à comparer (Chapitre_3/filecmp_1.py) :
1 #!/usr/bin/env python3
2 import filecmp
3
4
5 def main():
6 f1 = "/etc/passwd"
7 f2 = "/etc/group"
8 q = "f1 est-il similaire à f2 ?"
9 print(q)
10 print(filecmp.cmp(f1, f2))
11 print("f1 = " + f1)
12 print("f2 = " + f2)
13
14
15 if __name__ == "__main__":
16 main()
cmp compare le contenu des deux fichiers et retourne un booléen True lorsqu’ils sont identiques ou False si ce n’est pas le cas :
pi@raspberrypi:~/python-raspberrypi-3e-edition/Chapitre_3 $ python3 filecmp_1.py
f1 est-il similaire à f2 ?
False
f1 = /etc/passwd
f2 = /etc/group
L’appel au constructeur de la classe dircmp nécessite de renseigner...
Capturer des signaux UNIX avec le module signal
Les signaux UNIX forment une part importante de la programmation système. Un signal est un message asynchrone qui notifie un événement particulier à un programme. La bibliothèque standard de Python propose un module pour exploiter l’utilisation des signaux, le module signal. Avant d’aborder la partie programmation, un bref rappel de la manipulation des signaux est nécessaire.
Depuis la ligne de commande, l’outil kill est généralement utilisé pour envoyer un signal à un programme :
patrice@raspberrypi ~ $ kill
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ...
or kill -l [sigspec]
patrice@pi ~ $ kill -SIGTERM 1
bash: kill: (1) - Operation not permitted
Dans cet exemple, la commande kill est utilisée pour envoyer le signal SIGTERM au programme init dont le PID (Processus ID) est toujours 1. Cependant, le système d’exploitation retourne une erreur. En effet, le comportement par défaut d’un programme est d’accepter un signal seulement si l’utilisateur qui l’envoie est habilité à modifier l’état du programme. Généralement, le propriétaire du programme ou bien le super utilisateur root. Par exemple, le programme init, processus parent de tous les processus courants, ne peut recevoir de signaux d’un utilisateur autre que root, et ceci afin de garantir la stabilité du système.
Il existe de nombreux signaux. Voici les plus couramment utilisés :...
Écriture de scripts avec le module argparse
Pour clore ce chapitre, nous allons étudier le module argparse destiné à faciliter l’écriture de scripts Python en ligne de commande. Bien souvent, lors de l’écriture de ce type de programme, la problématique de personnalisation de celui-ci va rapidement se poser : comment définir une option ? Une fois définie, comment récupérer cette valeur ? Comment afficher l’aide ? Comment afficher la version actuelle du script ?
À noter que le module argparse est une refonte majeure d’un autre module appelé optparse. Avec la transition vers Python 3, et son inclusion dans la bibliothèque standard, le module optparse est tombé en désuétude bien que toujours disponible.
Pour répondre à ces interrogations, plongeons directement dans un exemple pour expliquer de quoi ce module est capable. Comme d’habitude, l’écriture de ce script se déroule dans IDLE (Chapitre_3/argparse_1.py) :
1 #!/usr/bin/env python3
2 import argparse
3
4
5 def main():
6 version = """%(prog)s 0.1"""
7 description = """Démonstration du module argparse."""
8
9 parseur = argparse.ArgumentParser(description=description)
10
11 parseur.add_argument("--version",
12 action="version",
13 version=version)
14 parseur.add_argument("-n", "--nom",
15 dest="nom",
16 default="Python",
17 help="nom par défaut",
18 ...
Conclusion
Ce chapitre explique les différents modules pouvant être utilisés pour administrer le Raspberry Pi.