Journal Ecrire une petite interface utilisateur ipmitool en 1h avec whiptail

Posté par  . Licence CC By‑SA.
Étiquettes :
26
1
août
2023

Sommaire

Bonjour,

ce matin j'avais besoin d'adresser des commandes ipmi à plusieurs hôtes pour pouvoir, notamment, paramétrer l'hôte pour qu'il démarre dans les options de BIOS, qu'il boote en PXE, qu'il démarre ou s'éteigne aussi, tout simplement. Et j'avais besoin de me simplifier la vie pour lancer ces commandes sur un grand nombre d'hôte.

Chercher dans l'historique des commandes, remplacer le nom d'hôte devient vite non satisfaisant. Ecrire un script shell, pourquoi pas; oh et puis pourquoi ne pas regarder comment faire simplement une petite interface utilisateur en shell ?!

Les commandes ipmitool utiles

Les commandes ipmitool qui m'étaient utiles sont les suivantes (en notant monhote le nom de l'hôte ciblé ) :

  • Aller dans les paramètres de BIOS au prochain boot :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis bootparam set bootflag force_bios

  • Partir en boot PXE au prochain boot :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis bootdev pxe

  • Eteindre le chassis :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis power off

  • Allumer le chassis :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis power on

  • Faire un power off/power on :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis power cycle

  • Faire un power reset quand l'extinction propre ne fonctionne plus :
    ipmitool -I lanplus -H imonhote -U root -P PASSWORD chassis power reset

Ces commandes très pratiques, je vais devoir les répéter pour environ 100 hôtes pour lancer des réinstallations PXE à distance et probablement de temps en temps pour d'autres raisons.

L'interface whiptail

J'ai commencé à regarder ce qu'il existe comme outils pour faire une petite interface. Et puis je suis tombé sur dialog et whiptail. Je n'ai pas suffisamment creusé pour avoir un avis sur l'un ou l'autres (et d'ailleurs dialog semble avoir eu 2 vies). Les syntaxes m'ont l'air similaires, j'avais whiptail installé sur le serveur d'administration que j'utilisai, je suis partie là dessus.

Pour ma petite interface, je vais utiliser une hiérarchie de menu :

  • une première liste de l'ensemble des hôtes parmi lesquel on peut sélectionner l'hôte cible, on appellera la fonction select_node $message, la variable message me permettant d'afficher un message arbitraire sur l'interface
  • une liste de commandes IPMI exécutables sur l'hôte, on appellera la commande ipmi_menu $selected_node, la variable selected_node étant le nom d'hôte ciblé

Boucle principale

Le script bash commence par une fonction principale

# Main loop
run=1
message=""
while [ $run = 1 ]; do
    selected_node=`select_node $message`
    exitstatus=$?
    if [ $exitstatus = 1 ]; then
        run=1
        message=`ipmi_menu $selected_node`
    else
        run=0
    fi
done

Menu de sélection de l'hôte

Pour la sélection de l'hôte, je définis un menu avec whiptail --menu. La commande complète est de la forme whiptail --menu text height width menu-height [tag item]. Les options à fournir à whiptail (ce qui est appelé tag item dans la documentation* sont de la forme "nom de l'option 1" "texte supplémentaire pour l'option 1" "nom de l'option 2" "texte supplémentaire pour l'option 2". J'ai un certain nombre d'hôte à mettre en option, j'utilise un array bash pour définir ces options.

function select_node() {
    local message=$1
    node_list=()
    for i in {01..68}; do
        node="kyle$i"
        node_list[${#node_list[@]}]=$node
        node_list[${#node_list[@]}]=""
    done

    OPTION=$(whiptail --title "IPMI interaction" --menu "Choose a node. Last message : $message" 30 90 10 "${node_list[@]}" 3>&1 1>&2 2>&3)

    exitstatus=$?
    if [ $exitstatus = 0 ]; then
        echo $OPTION
        return 1
    else
        return 0
    fi
}

Menu principal

Je n'ai pas creusé la raison des redirections 3>&1 1>&2 2>&3. Je profite de l'affichage de ce menu pour afficher un message arbitraire. Dans mon UI, je revies à ce menu chaque fois que j'ai lancé une commande ipmi et je voulai voir la sortie standard de la dernière commande exécutée.

Menu d'exécution des commandes ipmi

Lorsque l'hôte est sélectionné, je lance un menu pour les commandes ipmi :

function run_ipmi {
    local node=$1
    local cmd=$2
    result=`ipmitool -I lanplus -H i$node -U root -P $ipmipass $cmd`
    echo $result
}

function ipmi_menu {
    local node=$1

    pwr_status=`run_ipmi $node "power status"`

    options=("Force bios on reboot" "" "Force PXE on reboot" "" "Power cycle" "" "Power reset" "" "Power on" "" "Power off" "")
    OPTION=$(whiptail --title "IPMI interaction" --menu "Choose an ipmitool for node $node; Pwr: $pwr_status" 30 90 6 "${options[@]}" 3>&1 1>&2 2>&3)

    exitstatus=$?
    if [ $exitstatus = 0 ]; then
        case $OPTION in
            "Force bios on reboot")
                echo "Bios on reboot for $node"
                run_ipmi $node "chassis bootparam set bootflag force_bios"
                ;;
            "Force PXE on reboot")
                echo "PXE on reboot for $node"
                run_ipmi $node "chassis bootdev pxe"
                ;;
            "Power cycle")
                echo "Power cycle $node"
                run_ipmi $node "chassis power cycle"
                ;;
            "Power on")
                echo "Power reset $node"
                run_ipmi $node "chassis power on"
                ;;
            "Power off")
                echo "Power reset $node"
                run_ipmi $node "chassis power off"
                ;;
            "Power reset")
                echo "Power reset $node"
                run_ipmi $node "chassis power reset"
                ;;
            *)
                echo "unknown"
                ;;
        esac
    else
        return 0
    fi
}

Menu IPMI

Cette fois-ci, je défini explicitement les options pour le menu. Je profite de ce menu pour afficher l'état électrique du châssis. La sortie standard de cette commande est capturée dans la boucle principale qui va afficher le premier menu avec commande message arbitraire cette sortie standard.

Et voila, je trouve ce petit script bricolé assez pratique et relativement efficace pour piloter mes hôtes par ipmi.

  • # kiss

    Posté par  . Évalué à 3.

    Merci de la découverte, je n'avais pas réalisé que c'était des boites de dialogue du style installateur de distro, à l'ancienne, c'est vraiment tout simple et semble empaqueté pour Debian & dérivés & openwrt.

    Des exemples ici https://en.wikibooks.org/wiki/Bash_Shell_Scripting/Whiptail

    From the Linux Dictionary: whiptail is a "dialog" replacement using newt instead of ncurses.

    Je ne connais pas assez pour savoir ce que cela change concrètement, je ne connaissais que ncurses.

    • [^] # Re: kiss

      Posté par  . Évalué à 1.

      Je ne connais pas assez pour savoir ce que cela change concrètement, je ne connaissais que ncurses.
      

      De ce que j'ai pu lire, il y a la parenté curses d'une part, avec dialog et ses dérivés, et la parenté slang d'autre part, avec newt et whiptail.

      Whiptail est fourni dans la librairie Newt (première version il y a 27 ans !) qui est basée sur la libraire slang.

      Dialog est basé sur curses/ncurses.

      Github: https://github.com/jeremyfix

  • # Une image vaut 1000 mots

    Posté par  . Évalué à 7.

    Une petite capture d'écran aurait été pas mal en fin d'article pour voir le résultat final :)
    Mais sinon merci pour le partage ;)

    • [^] # Re: Une image vaut 1000 mots

      Posté par  . Évalué à 3.

      Oh oui, tout à fait !

      Le premier menu :

      Menu principal

      Et le second menu une fois l'hôte sélectionné :

      Menu IPMI

      Github: https://github.com/jeremyfix

      • [^] # Re: Une image vaut 1000 mots

        Posté par  (Mastodon) . Évalué à 6.

        Je me suis permis de le rajouter dans le journal.

        Et si t'es motivé pour faire une dépêche sur cet outil je suis ton homme pour la relecture ;)

        En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

        • [^] # Re: Une image vaut 1000 mots

          Posté par  . Évalué à 4.

          Je me suis permis de le rajouter dans le journal.
          

          Merci !

          Et si t'es motivé pour faire une dépêche sur cet outil je suis ton homme pour la relecture ;)
          

          Ok, je vais y réfléchir.

          Github: https://github.com/jeremyfix

  • # 3>&1 1>&2 2>&3

    Posté par  . Évalué à 6.

    Ce code shell redirige la sortie de la commande donnée.

    1. 3>&1 : redirige le descripteur de fichier 3 vers le descripteur de fichier 1. En d'autres termes, cela redirige la sortie du descripteur de fichier 3 vers le même emplacement que le descripteur de fichier 1 (qui est généralement la sortie standard).

    2. 1>&2 : redirige le descripteur de fichier 1 vers le descripteur de fichier 2. Cela redirige la sortie standard vers la sortie d'erreur.

    3. 2>&3 : redirige le descripteur de fichier 2 vers le descripteur de fichier 3. Cela redirige la sortie d'erreur vers l'emplacement de sortie du descripteur de fichier 3 (qui était initialement la sortie standard).

    En résumé, le code échange les flux de sortie standard et d'erreur standard. Ainsi, lorsque la commande est exécutée, la sortie qui irait normalement vers la sortie standard est redirigée vers la sortie d'erreur, et la sortie qui irait normalement vers la sortie d'erreur est redirigée vers la sortie standard.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.