Python3. Automatización de la configuración de equipos de red de múltiples proveedores.

Prólogo


Buen dia a todos!

Trabajo como ingeniero de redes para un gran operador de telecomunicaciones, y bajo mi control hay un zoológico completo de varios equipos de red, pero hablaremos sobre los interruptores de acceso.

Este artículo no es una guía de acción, no es la única solución y no pretende explícitamente ser el guión del año, pero quiero compartir esta creación, y tal vez sea útil para alguien.

El artículo proporcionará un bloque de código debajo del spoiler, y debajo encontrará una descripción con recortes y explicaciones de por qué es así y para qué sirve.

Desafío


Use python3 específicamente. El script debe poder acceder a los conmutadores de la lista, determinar qué tipo de proveedor, dar el comando requerido, iniciar sesión.

En realidad cómo llegué a esto


Ante un pequeño problema de cambiar la configuración en una gran cantidad de conmutadores, inmediatamente haré una reserva sobre el sistema de administración centralizado para el equipo de red que tenemos, muchos desarrollos de mis colegas en forma de scripts para facilitar el trabajo manual también están disponibles, principalmente bash, perl.

Pero para mí era importante hacer algo diferente, porque Comencé a aprender Python recientemente y necesito actualizar mis habilidades en programación, y mi herramienta debe verse como un martillo (fácil de usar y fácil de mantener). Si aún queda interés, pido un gato.

Buscando en Google y no encontrando la solución adecuada (como si en los foros se planteara esta pregunta, pero todo estaba mal allí), decidí comenzar a escribir mi propio script.

Tenemos los siguientes interruptores:

  • Raisecom
  • Qtech rev. 1.0
  • Qtech rev. 2.0 (diferencia de sintaxis)
  • Eltex
  • Enlace D
  • Genera el interruptor de Frankenstein con un hocico de Qtech rev 1.0, y hierro de Raisecom lo llamaremos ROS

Primero, importe las bibliotecas requeridas:

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

Inicialmente, describimos la función de definición de proveedor, porque decidió usar dos métodos:

1) Suponga qué tipo de proveedor es (en la invitación para ingresar al inicio de sesión) ya que noté que es diferente para todos: Qtech (inicio de sesión :), Raisecom y ROS (Inicio de sesión :), Eltex (Nombre de usuario :), D-link (Nombre de usuario).

2) después de iniciar sesión, asegúrese de que el primer elemento se completó sin errores y para una mejor identificación de qué interruptor estamos encendidos.

Función
 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 


Toda la función está bajo el spoiler. Declaramos variables globales que serán visibles para todo el script:

 global ver_status global sab_versus global versus 

Las variables a, d, f, h, j, k, n almacenan las palabras clave por las cuales posteriormente determinaremos el modelo del interruptor.

 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 

Después de abrir el archivo ver.txt, lo leemos línea por línea y verificamos la entrada por palabras clave, porque la función find () devuelve -1 si este resultado es negativo, y construiremos ramas.

 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' 

...

Doy un ejemplo de parte del código, el resto debajo del spoiler anterior.

El contenido de este archivo se describirá a continuación. Después de todas las manipulaciones y la definición del proveedor, elimine el archivo ver.txt.

Declaración de variables principales, cuerpo del bucle principal.
 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() ... 


Decidí no molestarme y establecer las variables con nombre de usuario y contraseña en el cuerpo, sé que no es seguridad, estoy trabajando en ello.

Abra el archivo para los registros y la lista de interruptores.

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

Después de usar el bucle for, lea la línea con la dirección IP del conmutador

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

Comprobamos disponibilidad

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

Si es accesible usando la biblioteca de pexpect, intentaremos conectarnos a través de telnet aquí en esta iteración y se produce la primera comprobación de la invitación, sobre la que escribí al principio.

 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) 

La variable vend obtendrá un valor de 0 a 3 inclusive, y dependiendo de la solicitud de inicio de sesión que vea, se formará otra rama.

A partir de este fragmento de código, un lector atento puede notar que estoy haciendo una conexión al conmutador e inmediatamente cerrando la conexión, y esto no es sin razón. Traté de usar solo la biblioteca telnetlib, pero en la primera prueba, el script se atascaba periódicamente y se caía por tiempo de espera, y esta muleta ayuda mucho.

Después de cerrar la conexión, solo hacemos la reconexión utilizando la biblioteca telnetlib.

Para evitar la caída de errores, ya verificamos anteriormente que el interruptor sea accesible y excluimos la interrupción del script durante la operación debido a un interruptor inactivo, envuelva todo en un intento, excepto el bloque.

Hubo casos repetidos cuando de 100 interruptores uno no se dejó entrar.

 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) 

...
Si todo está bien y estamos conectados, entonces necesitamos ingresar un nombre de usuario y contraseña,
sabemos con certeza que cualquier solicitud de dos puntos utiliza dos puntos.

Entonces lo estamos esperando

 tn.read_until(b':') 

Después de ingresar al inicio de sesión

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

Esperando dos puntos de la contraseña

 tn.read_until(b':') 

Ingrese contraseña

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

Y esperamos 3 segundos (respiro, de lo contrario, hemos hecho tanto trabajo)

 time.sleep(3) 

Después de esperar la invitación

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

Aquí, en esta etapa, pasamos al segundo nivel para verificar el proveedor.

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

Porque supusimos que llegamos a Qtech, y si lees con cuidado, en nuestro zoológico hay dos versiones de qtech que difieren en la sintaxis, aún debemos reconciliarnos.

Por lo tanto, le damos al comando show ver, colocamos toda la salida en el archivo ver.txt
y llame al procedimiento who_is (), que describí anteriormente. El show ver team es universal para todos los Qtech, Raisecom, Eltex,

Desafortunadamente, D-link no entiende y necesita decir show swich, pero somos inteligentes y no en vano introdujimos una iteración con la supuesta definición de vendedor.

 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() 

Entonces, inmediatamente, un pequeño comentario después de ingresar show swich, el interruptor muestra información incompleta y espera a que el usuario presione cualquier tecla para obtener más resultados, y por lo tanto enviamos el carácter "a" para mostrar información completa.

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

Para evitar esto, puede desactivar clipadding

Aquí es donde termina la verificación del proveedor y con una probabilidad del 99% podemos suponer que hemos identificado correctamente el modelo del conmutador y podemos proceder con la configuración.

Para cada cambio, tenemos un archivo separado con un conjunto de comandos.

Bloque de configuración
 ... 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) ... 


Después de que la función funcionó y devolvió la variable ver_status, podemos continuar trabajando con la ramificación, porque sabemos exactamente qué interruptor está actualmente en la línea.
En primer lugar, le damos al interruptor el comando show ver y escribimos la salida en el registro (sobre d-link, recuerde que le damos el comando sh sw)

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

Asegúrese de mantener contadores para identificar las no conformidades.
 counter_qtech1_0+=1 

Abrimos un archivo con comandos
 komand_qtech1_0=open('servers_&_log/komand_qtech_ver1.0.txt') 

El orden de los comandos en el archivo debe ser el mismo que el administrador los ingresaría manualmente

Un ejemplo:

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

Entonces todo está de acuerdo con el escenario: leemos el archivo hasta que las líneas terminan y las ejecutamos

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

Después de salir del bucle, le decimos al switch que salga y leemos toda la salida a un archivo, por lo que solo lo hacemos con qtech1.0, porque a veces un guión se pega en anticipación de algo y es con este interruptor, para no hacer un escándalo, esta solución me pareció más elegante.

Después de configurar el interruptor, aumentamos el contador total en uno.

 counter_komm+=1 

Y comenzamos de nuevo, leemos la siguiente línea del archivo con los interruptores, determinamos el modelo y hacemos la configuración.

Después de salir del ciclo principal, debe hacer un resumen del trabajo realizado,
Al final del registro, ingresamos todos los modelos que procesamos y la fecha es obligatoria, bueno, ¿cómo podría ser sin ella?

 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() 

Este script ha sido procesado repetidamente y el proceso no se detendrá allí.
Gracias a todos por su atención. La crítica constructiva es bienvenida.

Todo el código debajo del spoiler.

Código fuente
 #!/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/es415453/


All Articles