Inspecter son hardware en Python
Lucidiot —
Informatique —
2019-09-24
Encore un autre projet, cette fois-ci pour jouer avec la norme PCI.
Continuons la petite série présentant mes projets avec un projet que je considère comme quasiment terminé : pylspci
. C'est un package Python que j'ai créé suite à un besoin dans mon entreprise, pour un projet que nous rendrons open-source un jour ou l'autre.
Nous avions au départ besoin de connaître la fréquence du processeur : nous avons simplement utilisé py-cpuinfo
qui abstrait la collecte des informations du CPU pour nous. Cependant, ce package n'est dédié qu'au CPU, et nous avions besoin de plus que ça ; il nous fallait aussi lister les cartes graphiques disponibles sur la machine. Le framework de machine learning Tensorflow inclut des fonctionnalités en C++ permettant de lister les cartes graphiques, mais on ne va pas s'embêter à installer quelque chose d'aussi lourd juste pour ça.
J'avais finalement résolu ce problème en utilisant lspci
, un outil présent dans la majorité des distributions Linux et fourni par pciutils
, avec des modules de la librairie standard Python : subprocess
pour appeler lspci -m
et shlex
pour traduire la sortie en un tuple nommé, me permettant d'accéder de manière plus facile à comprendre au numéro d'emplacement, au nom du vendeur ou du périphérique. Étant donné qu'il existe py-cpuinfo
pour abstraire la lecture d'informations CPU, en utilisant notamment sous Linux lscpu
qui donne des informations bien plus détaillées que lspci
, je me suis dit qu'il manquait peut-être quelque chose pour pouvoir utiliser lspci
en Python sans tant de gêne.
C'est donc ainsi qu'est venu pylspci
, mon package Python permettant d'obtenir des objets plus faciles à utiliser à partir des informations renvoyées par lspci
. Une démonstration assez simple des capacités de ce package vient avec la commande fournie, pylspci
, qui prend des arguments similaires à lspci
et donne du JSON. Voici par exemple le retour de pylspci -vkP -nn
pour ma carte graphique (je n'ai pas mis tous les périphériques par simplification) :
[
{
"slot": {
"parent": {
"domain": 0,
"bus": 0,
"device": 1,
"function": 0
},
"domain": 0,
"bus": 1,
"device": 0,
"function": 0
},
"cls": {
"id": 768,
"name": "VGA compatible controller"
},
"vendor": {
"id": 4318,
"name": "NVIDIA Corporation"
},
"device": {
"id": 5121,
"name": "GM206 [GeForce GTX 960]"
},
"subsystem_vendor": {
"id": 5218,
"name": "Micro-Star International Co., Ltd. [MSI]"
},
"subsystem_device": {
"id": 12801,
"name": "GM206 [GeForce GTX 960]"
},
"revision": 161,
"progif": null,
"driver": "nvidia",
"kernel_modules": [
"nouveau",
"nvidia_drm",
"nvidia"
]
}
]
L'outil prend en charge les arguments -P
ou -PP
qui demandent à lspci
d'afficher les chemins à travers les ponts PCI, d'où la notion de parent
dans un slot
. -nn
force l'affichage du nom du périphérique et de l'ID, et les identifiants de vendeurs ou périphériques sont traités en hexadécimal et rendus en décimal dans le JSON, tout comme le numéro de révision et d'interface s'ils sont disponibles. -v
active le mode verbeux pour obtenir plus d'informations sur le périphérique, et -k
, exclusivement sous Linux, liste les modules du kernel compatibles avec le périphérique et le pilote actuellement utilisé. Une bonne partie des fonctions du lspci
original sont préservées comme l'utilisation d'un autre fichier d'identification (pour traduire les ID de périphérique en noms) ou d'un dump hexadécimal de lspci
quand on en reçoit un d'un utilisateur dans le cadre d'un diagnostic.
Dans le code Python, on peut appeler lspci
directement et récupérer sa sortie sans traitement :
>>> from pylspci.command import lspci
>>> print(lspci(kernel_drivers=True, verbose=True))
...
Slot: 01:00.0
Class: VGA compatible controller [0300]
...
On peut aussi utiliser un builder pattern pour préparer ses appels :
>>> from pylspci.command import CommandBuilder
>>> cmd = CommandBuilder().include_kernel_drivers()
>>> print(next(iter(cmd)))
...
Slot: 01:00.0
Class: VGA compatible controller [0300]
...
Et on peut faire la même chose avec le parsing :
>>> from pylspci.parsers import VerboseParser
>>> print(VerboseParser().run())
[Device(slot=Slot('0000:01:00.0'), cls=NameWithID('VGA compatible controller [0300]'), vendor=NameWithID('NVIDIA Corporation [10de]'), ..., driver='nvidia', kernel_modules=['nouveau', 'nvidia_drm', 'nvidia']),
...]
>>> print(list(CommandBuilder().with_default_parser().include_kernel_drivers()))
[Device(slot=Slot('0000:01:00.0'), cls=NameWithID('VGA compatible controller [0300]'), vendor=NameWithID('NVIDIA Corporation [10de]'), ..., driver='nvidia', kernel_modules=['nouveau', 'nvidia_drm', 'nvidia']),
...]
Je ne peux que vous inviter à aller lire la documentation pour en voir plus et éviter de me répéter.
J'en ai aussi profité pour expérimenter pour la première fois avec typing
, le récent module de typage statique de Python. Bien que du typage en Python aille probablement dans le sens contraire de ce que j'ai appris sur la philosophie du langage, c'est un mal nécessaire pour aider à travailler sur des bases de code assez lourdes, puisqu'on évite d'avoir à utiliser par exemple la notation hongroise ou d'avoir à lire le corps des fonctions qu'on veut utiliser. L'intégration a l'air déjà assez mature et ça n'enlaidit pas le code, donc c'est un plutôt bon point. Je pense que je vais commencer progressivement à utiliser ce système un peu partout dans mes projets.
Bien évidemment, ce package va beaucoup plus loin que le simple besoin initial de lister des cartes graphiques ; c'était aussi pour moi une petite expérience pour faire de la programmation légèrement plus proche du système, et pas seulement jouer avec des API non documentées. Jouer de ce côté-là est plus enrichissant que faire encore une fois du web, vu que j'en ai largement assez fait pour que Django n'aie plus beaucoup de secrets pour moi. Je recommencerai peut-être avec un autre outil, n'hésitez pas à suggérer dans les commentaires pour remplir ma liste de projets qui est déjà pleine à craquer.
Commentaires
Il n'y a pour l'instant aucun commentaire. Soyez le premier !