Python3. Automatisierung der Konfiguration von Netzwerkgeräten mehrerer Hersteller

Vorwort


Guten Tag an alle!

Ich arbeite als Netzwerktechniker für einen großen Telekommunikationsbetreiber, und unter meiner Kontrolle gibt es einen ganzen Zoo mit verschiedenen Netzwerkgeräten, aber wir werden über Zugangsschalter sprechen.

Dieser Artikel ist kein Leitfaden zum Handeln, er ist nicht die einzige Lösung und behauptet nicht ausdrücklich, das Drehbuch des Jahres zu sein, aber ich möchte diese Kreation teilen, und vielleicht ist sie für jemanden nützlich.

Der Artikel enthält einen Codeblock unter dem Spoiler und eine Beschreibung mit Ausschnitten und Erklärungen, warum dies so ist und wofür es gedacht ist.

Herausforderung


Verwenden Sie Python3 speziell. Das Skript sollte in der Lage sein, aus der Liste zu den Schaltern zu gelangen, festzustellen, welche Art von Anbieter es gibt, den erforderlichen Befehl zu geben und zu protokollieren.

Eigentlich, wie ich dazu gekommen bin


Angesichts des kleinen Problems, die Konfiguration einer großen Anzahl von Switches zu ändern, werde ich sofort eine Reservierung für das zentralisierte Verwaltungssystem für Netzwerkgeräte vornehmen. Viele Entwicklungen meiner Kollegen in Form von Skripten zur Erleichterung der manuellen Arbeit sind ebenfalls verfügbar, hauptsächlich Bash, Perl.

Aber für mich war es wichtig, etwas anderes zu machen, weil Ich habe vor kurzem angefangen, Python zu lernen, und ich muss meine Programmierkenntnisse verbessern, und mein Tool sollte wie ein Hammer aussehen (einfach zu bedienen und leicht zu warten). Wenn das Interesse noch besteht, bitte ich um eine Katze.

Als ich googelte und nicht die richtige Lösung fand (als ob diese Frage in den Foren gestellt wurde, aber dort alles falsch war), beschloss ich, mein eigenes Skript zu schreiben.

Wir haben folgende Schalter:

  • Raisecom
  • Qtech rev. 1.0
  • Qtech rev. 2.0 (Unterschied in der Syntax)
  • Eltex
  • D-Link
  • Spawn Frankenstein Schalter mit einer Mündung aus Qtech Rev. 1.0 und Eisen von Raisecom werden wir es ROS nennen

Importieren Sie zunächst die erforderlichen Bibliotheken:

import telnetlib import time import os import sys import getpass import pexpect from telnetlib import Telnet import datetime import subprocess 

Zunächst beschreiben wir die Herstellerdefinitionsfunktion, weil beschlossen, zwei Methoden zu verwenden:

1) anzunehmen - welche Art von Anbieter (bei der Einladung zur Eingabe des Logins), wie ich bemerkte, ist für jeden anders: Qtech (Login :), Raisecom und ROS (Login :), Eltex (Benutzername :), D-Link (Benutzername).

2) Stellen Sie nach dem Anmelden sicher, dass der erste Eintrag fehlerfrei abgeschlossen wurde und dass Sie besser erkennen können, an welchem ​​Schalter wir uns befinden.

Funktion
 def who_is(): #   global ver_status global sab_versus global versus sab_versus='' ver_status='' versus='' #  a = 'Serial No.:1405' # #qtech rev1 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 parser=open('servers_&_log/ver.txt', 'r') #  for line in parser.readlines(): line1 = line.find(a) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' if line4 != -1: ver_status='d-link' if line5 != -1: ver_status='raisecom' if line6 != -1: ver_status='raisecom-qtech' sab_versus=1 if line7 !=-1: ver_status='eltex' if line8 !=-1: ver_status='eltex' if line10 != -1: ver_status = 'qtech_rev_2.0' time.sleep(0.1) parser.close() os.remove('servers_&_log/ver.txt') return ver_status,sab_versus 


Die ganze Funktion ist unter dem Spoiler. Wir deklarieren globale Variablen, die für das gesamte Skript sichtbar sind:

 global ver_status global sab_versus global versus 

Die Variablen a, d, f, h, j, k, n speichern die Schlüsselwörter, anhand derer wir anschließend das Modell des Schalters bestimmen.

 a = 'Serial No.:1405' # #qtech rev1 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 

Nachdem wir die ver.txt-Datei geöffnet haben, lesen wir sie Zeile für Zeile und suchen nach dem Eintrag anhand von Schlüsselwörtern, weil Die Funktion find () gibt -1 zurück, wenn dieses Ergebnis negativ ist, und wir werden Zweige erstellen.

 parser=open('servers_&_log/ver.txt', 'r') #  for line in parser.readlines(): line1 = line.find(a) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' 

...

Ich gebe einen Beispielteil des Codes, den Rest unter dem Spoiler oben.

Der Inhalt dieser Datei wird unten beschrieben. Löschen Sie nach allen Manipulationen und der Definition des Anbieters die Datei ver.txt.

Deklaration der Hauptvariablen, Hauptteil der Hauptschleife
 user='user' password='password' komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') ######### counter_komm=0 counter_dlink=0 counter_qtech=0 counter_eltex=0 counter_raisecom=0 counter_ROS=0 counter_qtech1_0=0 counter_qtech2_0=0 ########## for host in komm.readlines(): print('connect....',host) vend = '' response = os.system('ping -c 1 ' + host) verinfo = open('servers_&_log/ver.txt', 'w') ver_status = '' sab_versus = '' if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) ###   ######################################################### if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ... 


Ich habe beschlossen, mich nicht darum zu kümmern und die Variablen mit Benutzername und Passwort im Hauptteil festzulegen. Ich weiß, dass dies keine Sicherheit ist. Ich arbeite daran.

Öffnen Sie die Datei für die Protokolle und die Liste der Schalter

 komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') 

Nachdem wir die for-Schleife verwendet haben, lesen Sie die Zeile mit der IP-Adresse des Switch

 for host in komm.readlines(): print('connect....',host) vend = '' 

Wir prüfen die Verfügbarkeit

 response = os.system('ping -c 1 ' + host) 

Wenn der Zugriff über die pexpect-Bibliothek möglich ist, versuchen wir hier bei dieser Iteration, eine Verbindung über Telnet herzustellen, und die erste Überprüfung der Einladung erfolgt, über die ich zu Beginn geschrieben habe.

 if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) 

Die Vend-Variable erhält einen Wert von 0 bis einschließlich 3, und je nachdem, welche Anmeldeaufforderung sie sieht, wird ein weiterer Zweig gebildet.

Anhand dieses Codes kann ein aufmerksamer Leser feststellen, dass ich eine Verbindung zum Switch herstelle und die Verbindung sofort schließe, und dies ist nicht ohne Grund. Ich habe versucht, nur die Telnetlib-Bibliothek zu verwenden, aber beim ersten Test blieb das Skript regelmäßig hängen und fiel durch Timeout ab, und diese Krücke hilft sehr.

Nach dem Schließen der Verbindung stellen wir die Verbindung nur über die Telnetlib-Bibliothek wieder her.

Um zu vermeiden, dass Fehler auftreten, haben wir oben bereits überprüft, ob auf den Switch zugegriffen werden kann, und eine Skriptunterbrechung während des Betriebs aufgrund eines Leerlaufschalters ausgeschlossen. Alles in einen Versuch mit Ausnahme des Blocks einschließen.

Es gab wiederholte Fälle, in denen man sich von 100 Schaltern nicht rein ließ.

 try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) 

...
Wenn alles in Ordnung ist und wir verbunden sind, müssen wir einen Benutzernamen und ein Passwort eingeben.
Wir wissen mit Sicherheit, dass jede Doppelpunkt-Eingabeaufforderung einen Doppelpunkt verwendet.

Also warten wir auf ihn

 tn.read_until(b':') 

Nachdem wir den Login eingegeben haben

 tn.write((user +'\n').encode('ascii')) 

Warten auf einen Doppelpunkt aus dem Passwort

 tn.read_until(b':') 

Passwort eingeben

 tn.write((password + '\n').encode('ascii')) 

Und wir warten 3 Sekunden (Pause, sonst haben wir so viel Arbeit geleistet)

 time.sleep(3) 

Nach dem Warten auf die Einladung

 tn.read_until(b'#',timeout=20) 

In dieser Phase gehen wir zur zweiten Ebene über, um den Lieferanten zu überprüfen.

 if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() 

Weil Wir gingen davon aus, dass wir zu Qtech gekommen sind. Wenn Sie sorgfältig lesen, gibt es in unserem Zoo zwei Versionen von qtech, die sich in der Syntax unterscheiden. Wir müssen uns noch abstimmen.

Daher geben wir den Befehl show ver ein und fügen die gesamte Ausgabe in die Datei ver.txt ein
und rufen Sie die Prozedur who_is () auf, die ich oben beschrieben habe. Das Show-Ver-Team ist universell für alle Qtech, Raisecom, Eltex,

Leider versteht D-Link nicht und er muss Show Swich sagen, aber wir sind schlau und haben nicht umsonst eine Iteration mit der angeblichen Definition eines Anbieters eingeführt.

 if vend == 3:#   D-link tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() 

Sofort, eine kleine Bemerkung nach Eingabe von show swich, zeigt der Schalter unvollständige Informationen an und wartet darauf, dass der Benutzer eine beliebige Taste für die weitere Ausgabe drückt. Daher senden wir dann das Zeichen „a“, um vollständige Informationen anzuzeigen.

 tn.write(('a' + '\n').encode('ascii')) 

Um dies zu vermeiden, können Sie das Clipadding deaktivieren

Hier endet die Überprüfung des Anbieters. Mit einer Wahrscheinlichkeit von 99% können wir davon ausgehen, dass wir das Modell des Switch korrekt identifiziert haben, und mit der Konfiguration fortfahren.

Für jeden Schalter haben wir eine separate Datei mit einer Reihe von Befehlen.

Konfigurationsblock
 ... elif ver_status == 'qtech_rev_1.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech1_0+=1 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) tn.write(('exit' + '\n').encode('ascii')) print(tn.read_all().decode('ascii'), file=log) print('   qtech1.0') print('Qtech rev1.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'qtech_rev_2.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech2_0+=1 komand_qtech2_0=open('servers_&_log/komand_qtech_ver2.0.txt') print('   qtech2.0') for kommand in komand_qtech2_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('Qtech rev2.0 ' + host, file=log) print('#' * 100, file=log) ... 


Nachdem die Funktion die Variable ver_status ausgearbeitet und zurückgegeben hat, können wir weiter mit der Verzweigung arbeiten, weil Wir wissen genau, welcher Schalter gerade in der Leitung ist.
Zunächst geben wir dem Schalter den Befehl show ver und schreiben die Ausgabe in das Protokoll (über d-link, denken Sie daran, wir geben ihm den Befehl sh sw).

 tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) 

Stellen Sie sicher, dass Sie die Zähler aufbewahren, um Abweichungen festzustellen.
 counter_qtech1_0+=1 

Wir öffnen eine Datei mit Befehlen
 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') 

Die Reihenfolge der Befehle in der Datei muss mit der Reihenfolge übereinstimmen, in der der Administrator sie manuell eingibt

Ein Beispiel:

conf
vlan 2525
name SPD
int ethe 1/1
sw mode access
sw acc vl 2525
exit
exit
save
y

Dann ist alles dem Szenario entsprechend - wir lesen die Datei bis zum Ende der Zeilen und führen sie aus

 for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) 

Nach dem Verlassen der Schleife teilen wir dem Switch das Beenden mit und lesen die gesamte Ausgabe in eine Datei. Daher verwenden wir nur qtech1.0, weil Manchmal bleibt ein Skript in Erwartung von etwas hängen und es ist mit diesem Schalter, um keine Aufregung zu machen, schien mir diese Lösung eleganter.

Nachdem der Switch konfiguriert wurde, erhöhen wir den Gesamtzähler um eins.

 counter_komm+=1 

Und wir fangen von vorne an, lesen die nächste Zeile aus der Datei mit den Schaltern, bestimmen das Modell und nehmen die Konfiguration vor.

Nach dem Verlassen des Hauptzyklus müssen Sie eine Zusammenfassung der geleisteten Arbeit erstellen.
Am Ende des Protokolls geben wir alle Modelle ein, die wir verarbeitet haben, und das Datum ist obligatorisch. Wie könnte es ohne dieses Protokoll sein?

 print('\n\n\n : ', counter_komm,file=log) print('\n\nD-link:', counter_dlink,'\nQtech ver1.0:', counter_qtech1_0,'\nROS:', counter_ROS,'\nRaisecom:',counter_raisecom,'\nEltex:', counter_eltex,'\nQtech ver2.0 :', counter_qtech2_0,file=log) print('\n: ', datetime.datetime.now().isoformat(),'\n', '#'*100,'\n\n\n\n\n',file=log) verinfo.close() log.close() 

Dieses Skript wurde wiederholt verarbeitet und der Prozess wird dort nicht gestoppt.
Vielen Dank für Ihre Aufmerksamkeit. Konstruktive Kritik ist willkommen.

Der gesamte Code unter dem Spoiler.

Quellcode
 #!/usr/bin/python3 #22/06/2017 17:17 import telnetlib import time import os import sys import getpass import pexpect from telnetlib import Telnet import datetime import subprocess def who_is(): #     ,      . global ver_status global sab_versus global versus sab_versus='' ver_status='' versus='' a = 'Serial No.:1405' # #qtech rev1 #b = 'Serial No.:3922' # #qtech rev2 #c = 'Serial No.:5436' # #qtech rev2 #l = 'Serial No.:16101' # #qtech rev2 d = 'Command: show switch' # #d-link f = 'Raisecom Operating System Software' # #raisecom h = 'QOS' # #raisecom-qtech j = 'MES1124M' # #eltex k = 'eltex' # #eltex n = 'Build245' #qtech2.0 parser=open('servers_&_log/ver.txt', 'r') for line in parser.readlines(): line1 = line.find(a) #line2 = line.find(b) #line3 = line.find(c) line4 = line.find(d) line5 = line.find(f) line6 = line.find(h) line7 = line.find(j) line8 = line.find(k) #line9 = line.find(l) line10 = line.find(n) if line1 != -1: ver_status='qtech_rev_1.0' #if line2 != -1 or line3 != -1 or line9 !=-1: #ver_status='qtech_rev_2.0' if line4 != -1: ver_status='d-link' if line5 != -1: ver_status='raisecom' if line6 != -1: ver_status='raisecom-qtech' sab_versus=1 if line7 !=-1: ver_status='eltex' if line8 !=-1: ver_status='eltex' if line10 != -1: ver_status = 'qtech_rev_2.0' time.sleep(0.1) parser.close() os.remove('servers_&_log/ver.txt') return ver_status,sab_versus user='user' password='password' komm=open('servers_&_log/komm.txt') log=open('servers_&_log/log.txt','a') counter_komm=0 counter_dlink=0 counter_qtech=0 counter_eltex=0 counter_raisecom=0 counter_ROS=0 # counter_qtech1_0=0 counter_qtech2_0=0 for host in komm.readlines(): print('connect....',host) vend = '' #tn = Telnet(host.replace('\n', ''), 23, 20) response = os.system('ping -c 1 ' + host) verinfo = open('servers_&_log/ver.txt', 'w') ver_status = '' sab_versus = '' if response == 0: telnet = pexpect.spawn('telnet ' + host,timeout=40) vend = telnet.expect(['login:', 'Login:', 'User Name:', 'Username']) telnet.close() tn = Telnet(host.replace('\n', ''), 23,30) try: print('Ok'+'\n') tn.read_until(b':') tn.write((user +'\n').encode('ascii')) tn.read_until(b':') tn.write((password + '\n').encode('ascii')) time.sleep(3) tn.read_until(b'#',timeout=20) except: print('connection refused' + '\n') f = open('servers_&_log/log.txt', 'a') print(host, 'connection refused', file=log) print('#' * 100, host, file=log) ###   ################################################################################ if vend == 0:#    Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 1: #   Raisecom,Raisecom-Qtech tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 2:#   Eltex tn.write(('show system' + '\n').encode('ascii')) tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() ################################################################################ if vend == 3:#   D-link tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=verinfo) verinfo.close() who_is() # ###  # ################################################################################ if ver_status == 'd-link': tn.read_until(b'#', timeout=20) tn.write(('show sw' + '\n').encode('ascii')) tn.write(('a' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('D-link ' + host,file=log) print('#'*100,file=log) counter_dlink+=1 ################################################################################ elif ver_status == 'qtech_rev_1.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech1_0+=1 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') for kommand in komand_qtech1_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) #print((tn.read_until(b'#').decode('ascii')), file=log) tn.write(('exit' + '\n').encode('ascii')) print(tn.read_all().decode('ascii'), file=log) print('Qtech rev1.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'qtech_rev_2.0': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_qtech2_0+=1 komand_qtech2_0=open('servers_&_log/komand_qtech_ver2.0.txt') for kommand in komand_qtech2_0.readlines(): tn.write((kommand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) time.sleep(1) print('Qtech rev2.0 ' + host, file=log) print('#' * 100, file=log) ################################################################################ elif ver_status == 'raisecom-qtech': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) raisecom_command = open('servers_&_log/komand_raisecom.txt') for komand in raisecom_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('ROS '+ host,file=log) print('#'*100,file=log) counter_ROS+=1 ################################################################################ elif ver_status == 'raisecom': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) counter_raisecom+=1 raisecom_command = open('servers_&_log/komand_raisecom.txt') for komand in raisecom_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print(host, ':', 'RAISECOM', file=log) print('#' * 100, host, file=log) ################################################################################ elif ver_status == 'eltex': tn.write(('show ver' + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) eltex_command=open('servers_&_log/komand_eltex.txt') for komand in eltex_command.readlines(): tn.write((komand.replace('\n', '') + '\n').encode('ascii')) print((tn.read_until(b'#').decode('ascii')), file=log) print('Eltex ' + host, file=log) print('#' * 100, file=log) counter_eltex+=1 else: print('no ping') counter_komm+=1 print('\n\n\n : ', counter_komm,file=log) print('\n\nD-link:', counter_dlink,'\nQtech ver1.0:', counter_qtech1_0,'\nROS:', counter_ROS,'\nRaisecom:',counter_raisecom,'\nEltex:', counter_eltex,'\nQtech ver2.0 :', counter_qtech2_0,file=log) print('\n: ', datetime.datetime.now().isoformat(),'\n', '#'*100,'\n\n\n\n\n',file=log) verinfo.close() log.close() 

Source: https://habr.com/ru/post/de415453/


All Articles