Envoi de commandes CISCO publipostées via SSH/Telnet

Décembre 2016


Expect en action



Informations générales

  • Nom du script : publip.sh (publi(point)sh...publipSH...publipèssache...publipostage...bon ok, laissez tomber...)
  • Flexibilité : Quasi-totale
  • Contraintes : 1 seule, il faut que le mot de passe soit le même pour tout les équipements, sinon il faut rajouter quelques lignes de code...
  • Criticité : élevée. Il faut faire très attention avec les commandes entrées, vous pouvez geler votre réseau entier si vous ne savez pas ce que vous faites !
  • Connaissances requises en Linux : Vous savez ouvrir/éditer un fichier (vi nom_fichier) ? Rentrer des jolies choses dedans (i [pour insérer du texte], puis blablabla), l'enregistrer et le fermer (echap, puis :wq <entrée> ? Vous savez exécuter un script par la commande ./nom_script dans le dossier où il se trouve ? non ? Eh bien maintenant, oui ! Aucune connaissance n'est requise pour utiliser ce script :-)

publipmachin point cheu ? Mais c'est quoi au juste ?


Ce script permet pour un administrateur réseau, de rentrer des lignes de commandes Cisco, publipostées sur un nombre d'équipements distants choisi (routeur, firewall, switch, Point d'accès Wifi...), ce en SSH, où en Telnet (le script gère les deux types de connexion) et automatiquement.

Ces lignes de commandes seront tout d'abord saisies à la suite, une commande par ligne comme un batch, ou un fichier de configuration, dans un petit fichier nommé commandes.txt (il n'y a aucune limite de taille)
Ensuite, il va falloir rentrer toutes les IP (où alias DNS), des équipements où vous voulez que les commandes soient rentrées (il n'y a aucune limite de nombre d'équipements).

Enfin, vous exécutez le script, et là, il faudra pas vous tromper quand le script vous demandera de saisir le mot de passe, qui je le rappelle doit être impérativement le même pour tous les équipements.

Mais là où cela devient intéressant, c'est que ce script va beaucoup plus loin !

En effet, il prends en charge et ce de façon totale les erreurs de connexion, et de saisie, pour les rentrer ensuite dans un fichier log. Ce script identifie également les équipements pour lequel il est parvenu à se connecter en SSH, où en telnet, et enfin il conserve une archive des procédures de saisies générant des erreurs critiques (plantage du script, erreur d'interprétation d'une commande par l'équipement). Les différents types d'erreurs pris en charge sont :
  • 1 - Connexion en timeout
  • 2 - Erreur de log-in, où de mot de passe
  • 3 - Alias DNS ou IP inexistante
  • 4 - Plantage du script Expect (création de fichier log de l'équipement)
  • 5 - Connexion à distance désactivée
  • 6 - Alias DNS correct mais associée a une IP qui n'existe pas (no route to host)
  • 7 - Équipement non Cisco (HP Procurve)
  • 8 - Équipement non Cisco (X1000)
  • 9 - Équipement non Cisco (Alcatel)
  • 10 - Commande publipostée non reconnu par l'équipement (création de fichier log de l'équipement)
  • 11 - Erreur non répertoriée çi-dessus ($? = 1)


Vous pourrez en plus d'opérer sur tous vos équipements en même temps, recueillir des informations précieuses sur votre réseau ! Elle est pas belle la vie ?

Le contenu des fichiers annexes

commandes.txt


Vous devrez absolument rentrer des commandes à la suite...Le fichier doit pour toute modification de la configuration du routeur commencer par "conf t" et finir par "end". N'oubliez pas de procéder de manière hiérarchique, pour ne pas rentrer des commandes là où il ne faut pas...et surtout...n'oubliez pas d'enregistrer vos modifications ! Petit exemple :

#commandes.txt 

conf t 
 router ospf 100 
  network 50.50.100.0 0.0.0.255 area 0 
 exit 
 interface fa0/0 
  ip ospf hello-interval 5 
  ip ospf dead-interval 20 
 exit 
 area 0 authentication message-digest 
end 
write mem 

liste.txt


Un jeu d'enfant ! Rentrez juste les IP, ou les alias DNS, des équipements concernés, et c'est tout ! Un équipement par ligne...petit exemple encore :

# liste.txt 

10.25.85.46 
routeur-marseille 

switch-assemblee-generale 
80.54.136.105 


J'ai laissé volontairement un espace, pour vous montrer que le script ne prends pas en compte les espaces.

THE Script


Le script sera légendé par des petits #[1], qui seront commentés tout en bas du script

#!/bin/bash 
#script.sh 

echo "veuillez donner le mot de passe" 
stty -echo                                                                       #[1] 
read password 
stty echo 

export ssh='./ssh.sh'                                                            #[2] 
export telnet='./telnet.sh' 
export erreur='./rapport_erreurs.log' 
export temp='./tmp_routeur.log' 
export cmdcisco='./commandes.txt' 
export liste='./liste.txt' 
export password 
export routeur 
export commande 

rm -f $erreur                                                                    #[3] 
rm -f $ssh 
rm -f $telnet 

cat $liste | while read routeur; 
do 
        if [ "$routeur" != "" ] 
        then 
                if[ ! -f $ssh ]                                                  #[4] 
                then 
                        echo 'expect 2>&1 << EOF'>> $ssh 
                        echo 'spawn ssh admin@$routeur' >> $ssh 
                        echo 'expect {' >> $ssh 
                        echo '"Password:" {send "$password\r"}' >> $ssh 
                        echo 'timeout {exit}' >> $ssh 
                        echo '        }' >> $ssh 
                        echo 'expect "#"' >> $ssh 

                        cat $cmdcisco | while read commande 
                        do 
                                echo "send \"$commande\r\"" 
                                echo 'expect "#"' 
                        done >> $ssh 

                        echo 'send "exit\r"' >> $ssh 
                        echo 'expect "closed"' >> $ssh 
                        echo 'exit' >> $ssh 
                        echo 'EOF' >> $ssh 

                        chmod +x $ssh                                            #[5] 
                fi 
                time -p $ssh > $temp 2>&1                                        #[6] 

                COD_RET=$? 

                auth='cat $temp | grep -c "Password: "'                          #[7] 
                if [ "$auth" -gt "1" ] 
                then 
                        echo "Problème d'authentification sur $routeur !" 
                        echo "$routeur : wrong log-in/password" >> $erreur 
                        continue 
                fi 

                temps='grep 'real ' $temp | sed 's/real /§/' | cut -d'§' -f2 | cut -d' ' -f1 | cut -d'.' -f1' 
                if [ $temps -ge 10 -a ! "'grep 'closed' $temp'" ]                #[8] 
                then 
                        echo "L'equipement $routeur ne réponds pas !"; 
                        echo "$routeur : connection timed out"  >> $erreur 
                        continue 
                fi 

                if [ "$COD_RET" != "0" ]                                          #[9] 
                then 
                        #Erreur de connexion a l'équipement en SSH 
                        if [ ! -f $telnet ] 
                        then 
                                echo 'expect 2>&1 << EOF'>> $telnet 
                                echo 'spawn telnet $routeur' >> $telnet 
                                echo 'send "admin\r"' >> $telnet 
                                echo 'expect "Password:"' >> $telnet 
                                echo 'send "$password\r"' >> $telnet 
                                echo 'expect "#"' >> $telnet 

                                cat $cmdcisco | while read commande 
                                do 
                                        echo "send \"$commande\r\"" 
                                        echo 'expect "#"' 
                                done >> $telnet 

                                echo 'send "exit\r"' >> $telnet 
                                echo 'expect "closed"' >> $telnet 
                                echo 'exit' >> $telnet 
                                echo 'EOF' >> $telnet 

                                chmod +x $telnet 
                        fi 
                        $telnet > $temp 2>&1 
                fi 
                COD_RET=$? 

                auth='cat $temp | grep -c "Password: "'                          #[10] 
                if [ "$auth" -gt "1" ] 
                then 
                        echo "Problème d'authentification sur $routeur !" 
                        echo "$routeur : wrong log-in/password" >> $erreur 
                elif [ "'grep 'Host name lookup failure' $temp'"  ] 
                then 
                        echo "l'equipement $routeur n'existe pas !" 
                        echo "$routeur : does not exist"  >> $erreur 
                elif [ "'grep 'Unknown host' $temp'" ] 
                then 
                        echo "la saisie de l'ip ou du nom $routeur est incorrecte !" 
                        echo "$routeur : wrong spelling" >> $erreur 
                elif [ "'grep 'send: spawn id exp4 not open' $temp'" ] 
                then 
                        echo "  ERREUR dans la procédure. Consultez le fichier log de $routeur !!!" 
                        echo "$routeur : Expect script execution failed !" >> $erreur 
                        cp $temp $routeur.error.log 
                elif [ "'grep 'Authentication failed' $temp'" ] 
                then 
                        echo "Mot de passe erroné pour $routeur !" 
                        echo "$routeur : wrong log-in/password" >> $erreur 
                elif [ "'grep 'Connection refused' $temp'" ] 
                then 
                        echo "Connexion à distance sur $routeur désactivé !" 
                        echo "$routeur : vty connection disabled" >> $erreur 
                elif [ "'grep 'No route to host' $temp'" ] 
                then 
                        echo "Alias DNS $routeur existant mais IP invalide !" 
                        echo "$routeur : No route to host" >> $erreur 
                elif [ "'grep 'ProCurve' $temp'" ] 
                then 
                        echo "routeur $routeur HP et non Cisco !" 
                        echo "$routeur : non Cisco router (HP ProCurve)" >> $erreur 
                elif [ "'grep 'Alcatel' $temp'" ] 
                then 
                        echo "routeur $routeur Alcatel et non Cisco !" 
                        echo "$routeur : non Cisco router (Alcatel)" >> $erreur 
                elif [ "'grep 'Welcome to X1000' $temp'" ] 
                then 
                        echo "routeur $routeur X1000 et non Cisco !" 
                        echo "$routeur : non Cisco equipement (X1000)" >> $erreur 
                elif [ "'grep '% Unknown command' $temp'" -o "'grep '% Invalid' $temp'" ] 
                then 
                        echo "  Commandes Cisco non reconnues par l'equipement. Consultez le fichier log de $routeur !!!" 
                        echo "$routeur : Unrecognized commands found" >> $erreur 
                        cp $temp $routeur.error.log 
                elif [ "'grep 'Connected to ' $temp'" -o "'grep 'Connection closed by foreign host.' $temp'" ] 
                then 
                        echo "$routeur Telnet OK !" 
                elif [ "'grep 'Connexion enregistree sur le terminal' $temp'" -o "'grep 'Connection to ' $temp'" ] 
                then 
                        echo "$routeur SSH OK !" 
                elif [ "$COD_RET" != "0" ] 
                then 
                        echo "Problème de connexion a l'equipement $routeur !" 
                        echo "$routeur : connection problem" >> $erreur 
                fi 
        fi 
done 
rm -f $temp                                                                      #[11] 
exit 

Commentaires

  • 1 : On cache la saisie du mot de passe
  • 2 : Tous les fichiers sont stockés dans des variables en chemin relatif, pour que le script puisse être exécuté de n'importe où
  • 3 : on supprime les fichiers générés existants si le script à déjà été exécuté
  • 4 : on génère le fichier Expect si ce n'est pas déjà fait, en rentrant la procédure de connexion SSH ainsi que les commandes provenant de commandes.txt
  • 5 : on attribue les droits d'exécution au script Expect généré, si on veut qu'il s'exécute correctement
  • 6 : on execute le script expect, en regroupant la sortie d'erreur avec la sortie standard, en calculant le temps d'exécution pour gérer le timeout, et en crachant le tout dans un fichier temp
  • 7 : on vérifie qu'il n'y ai pas un problème d'authentification en comptant le nombre d'occurrence "Password:" dans le fichier temp
  • 8 : on récupère le nombre du temps d'execution, et on vérifie qu'il ne soit pas supérieur à 10 (valeur du timeout du expect)
  • 9 : En cas d'erreur de connexion en SSH, on refait la procédure en Telnet
  • 10 : On gère tous les cas d'erreur pris en compte par le script. (c.f. II)
  • 11 : On supprime le fichier temp, qui devient inutile

Remerciements


Je remercie en particlier jipicy, pour son aide cruciale à la réalisation de ce script, et j'espère qu'il servira à plus d'un ;)

Questions / Report de Bugs / Modifications


M'étant réorienté de mon précédent poste, je n'ai malheureusement plus les compétences pour pouvoir vous aider sur le débogage du script. Je vous invite cependant à faire part de vos soucis dans la section forum développement de CCM, en prenant bien soin de linker cet article (n'oubliez pas de formuler poliment votre demande).

Bonne journée !

A voir également :

Ce document intitulé «  Envoi de commandes CISCO publipostées via SSH/Telnet  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.