파이썬 스크립트를 이용한 스위치 설정 정보 저장.(Cisco, extreme)
지원 스위치: Cisco IOS , Extreme EXOS 스위치
지원 프로토콜: telnet, ssh (익스트림 스위치 제외).
스크립트 실행 환경: python 3.6.5, netmiko, telnetlib가 필요하다.
기타사항: 시스코 스위치는 로그인 즉시 enable 상태가 되도록 설정되어 있어야함(설정 방법은 https://blog.boxcorea.com/wp/archives/2529를 참조).
스위치 접속 정보화일(switch.txt)은 아래와 같은 포맷이며, 각 열은 탭(\t) 하나로 구분한다.
# IP PORT USER PASSWD PROTOCOL VENDOR 172.16.10.1 23 admin password1 telnet cisco 172.16.10.2 22 root password2 ssh cisco 172.16.10.3 23 admin password3 telnet extreme
스크립트는 파이썬3.5, windows10에서 테스트했음.
파이썬 설치후, 아래 명령어로 netmiko를 설치한다.
pip install netmiko pip install telnetlib
*** ver 0.0.3 업데이트
변경사항:
1.extreme 스위치 ssh 지원, netmiko 대신 paramiko 사용
pip install paramiko
2. 접속 에러 처리. 접속 안될때, 내용을 error로 저장
***
** ver 0.0.4 업데이트
변경사항:
1. thread 사용으로 성능 대폭 향상
** ver 0.0.5 업데이트
변경사항:
1. 명령행 옵션으로 임의의 스위치 정보 화일을 지원 (예. backup_switch.py [my_switch_info.txt] )
스크립트는 아래와 같다.
import telnetlib import re import time import paramiko import concurrent.futures import sys # backup_switch.py. ver 0.0.5 # Script by snowffox. # # Document: https://blog.boxcorea.com/wp/archives/2277 # # Supported Switch is Extreme EXOS switch and Cisco IOS switch # Supported protocol is telnet and ssh. # # # switch info : ip, port, user_id, passwd, protocol, vendor(split by tab(\t) char) # # ver 0.0.2 : Error handling. Support SSH with netmiko # ver 0.0.3 : Full support SSH with paramiko and hostname support. # ver 0.0.4 : Improve performance by using Thread. # ver 0.0.5 : Support custom file name on command line option. def runSSH(switch, command): try: ssh=paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=switch[0], port=switch[1], username=switch[2], password=switch[3]) stdin, stdout, stderr=ssh.exec_command(command.encode('ascii')) data=((stdout.read()).decode('ascii')).replace('\r\n', '\n') except: print("ERROR 001: SSH Connection error...") data="error" return data def runTELNET(switch, command): try: tn=telnetlib.Telnet(switch[0], switch[1], 20) #tn.set_debuglevel(1) prompt=getPrompt(switch[5]) tn.read_until(prompt[0].encode('ascii')) tn.write(switch[2].encode('ascii')+b"\n") tn.read_until(prompt[1].encode("ascii")) tn.write(switch[3].encode('ascii')+b"\n") tn.write(command.encode('ascii')+b"\n") tn.write("exit".encode('ascii')+b"\n") data=tn.read_all().decode('ascii') tn.close() except: print("ERROR 002: TELNET Connection error...\n") data="error" return data def getPrompt(vendor): if vendor=='cisco': prompt=['Username: ','Password: '] elif vendor=='extreme': prompt=['login: ', 'password: '] elif vendor=='nexus': prompt=['login: ','Password: '] return prompt def decCOMMAND(switch): if switch[5]=='cisco' and switch[4]=='telnet': command="terminal length 0\r\nshow run" elif switch[5]=='cisco' and switch[4]=='ssh': command='show run' elif switch[5]=='extreme'and switch[4]=='telnet': command="disable clipaging\r\nshow config" elif switch[5]=='extreme' and switch[4]=='ssh': command='show config' elif switch[5]=='nexus' and switch[4]=='telnet': command="terminal length 0\r\nshow run" elif switch[5]=='nexus' and switch[4]=='ssh': command='show run' else : print("%s switch is not support..." %(switch[5])) command="not supported" return command def gethostNAME(vendors, s): if s=="error": return "error" l=[] h="" if not s: s="ERROR 003 can not extract hosname!!!" if str.lower(vendors)=="cisco": pattern="hostname" dli=" " elif str.lower(vendors)=="extreme": pattern="configure snmp sysName" dli="\"" else: print("ERR 004 vendors %s Hostname not support!" %(vendors)) hostname=vendors return hostname l=(s.strip()).split('\n') for i in l: p=re.compile(pattern) m=p.match(i) if m : h=i l=h.split(dli) hostname=l[1].strip() return hostname def saveCONFIG(switch, data): host=gethostNAME(switch[5], data) fname=host+'_'+switch[0]+".txt" f=open(fname, "w") f.write(data) f.close() def main(switch): print("switch information is...") print(switch) command=decCOMMAND(switch) if switch[4]=='ssh': result=runSSH(switch, command) elif switch[4]=='telnet': result=runTELNET(switch, command) else: print(switch[4]) print("Not supported...") result="Not supported..." saveCONFIG(switch, result) if __name__=="__main__": argn=len(sys.argv) if argn==1: print("use default switch info file 'switch.txt'...") fname="switch.txt" elif argn>2: print("use only one switch info file\nProgram ended...") exit() else: fname=sys.argv[1] try: f=open(fname, 'r') except: print("File '%s' not found" %(fname)) exit() while True: l=f.readline().strip('\n') if not l: break switch=l.split('\t') # print(switch) n=len(switch) if n !=6: print("switch information format is wrong...\n") print(switch) continue else: pool=concurrent.futures.ProcessPoolExecutor(4) future=pool.submit(main, (switch)) future.done() f.close()
스크립트 실행 결과는 hostname_ip(x.x.x.x).txt 로 각 스위치 별로 저장된다.
윈도우용 파이썬과 위 스크립트는 아래에서 다운로드 받을 수 있다.
ver 0.0.1: backup_switch_0.0.1.zip
ver 0.0.3: backup_switch_v0.0.3
ver 0.0.4: backup_switch_0.0.4
ver 0.0.5: backup_switch_0.0.5
13 comments
Skip to comment form
pip install telnetlib
Collecting telnetlib
Could not find a version that satisfies the requirement telnetlib (from versions: )
No matching distribution found for telnetlib
메시지 나오는 경우는,
telnetlib가 python에 이미 설치되어 있으므로, 다시 설치할 필요가 없음.
import telnetlib
import re
import time
import paramiko
import concurrent.futures
import sys
# backup_switch2.py. ver 0.0.5
# Script by snowffox.
#
# Document: https://blog.boxcorea.com/wp/archives/2277
#
# Supported switch2 is Extreme EXOS switch2 and Cisco IOS switch2
# Supported protocol is telnet and ssh.
#
#
# switch2 info : ip, port, user_id, passwd, protocol, vendor(split by tab(\t) char)
#
# ver 0.0.2 : Error handling. Support SSH with netmiko
# ver 0.0.3 : Full support SSH with paramiko and hostname support.
# ver 0.0.4 : Improve performance by using Thread.
# ver 0.0.5 : Support custom file name on command line option.
def runSSH(switch2, command):
try:
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=switch2[0], port=switch2[1], username=switch2[2], password=switch2[3])
# stdin, stdout, stderr=ssh.exec_command(command.encode(‘ascii’))
# data=((stdout.read()).decode(‘ascii’)).replace(‘\r\n’, ‘\n’)
channel = ssh.get_transport().open_session() #서버에 새 채널 요청
channel.get_pty(command.encode(‘ascii’)) # 이 줄 없으면 Welcome to Ubuntu는 뜨고 그 뒤로 쉘이 무반응
channel.invoke_shell()
data = channel.recv(1024).decode(‘ascii’).replace(‘\r\n’, ‘\n’)
print(data)
except:
print(“ERROR 001: SSH Connection error…”)
data=”error”
return data
def runTELNET(switch2, command):
try:
tn=telnetlib.Telnet(switch2[0], switch2[1], 20)
# tn.set_debuglevel(1)
prompt=getPrompt(switch2[5])
tn.read_until(prompt[0].encode(‘ascii’))
tn.write(switch2[2].encode(‘ascii’)+b”\n”)
tn.read_until(prompt[1].encode(“ascii”))
tn.write(switch2[3].encode(‘ascii’)+b”\n”)
tn.write(command.encode(‘ascii’)+b”\n”)
tn.write(“exit”.encode(‘ascii’)+b”\n”)
data=tn.read_all().decode(‘ascii’)
tn.close()
except:
print(“ERROR 002: TELNET Connection error…\n”)
data=”error”
return data
def getPrompt(vendor):
if vendor==’cisco’:
prompt=[‘username: ‘,’password: ‘]
elif vendor==’nexus’:
prompt=[‘login: ‘,’Password: ‘]
elif vendor==’cisco_ios’:
prompt=[‘login as: ‘,’| password: ‘]
return prompt
def decCOMMAND(switch2):
if switch2[5]==’cisco’ and switch2[4]==’telnet’:
command=”ter le 0\r\nshow run\r\nshow proce memo\r\nshow ver”
elif switch2[5]==’cisco_ios’ and switch2[4]==’ssh’:
command=’ter le 0\r\nshow run\r\nshow proce memo\r\nshow ver’
elif switch2[5]==’nexus’ and switch2[4]==’telnet’:
command=”terminal length 0\r\nshow run”
elif switch2[5]==’nexus’ and switch2[4]==’ssh’:
command=’show run’
else :
print(“%s switch2 is not support…” %(switch2[5]))
command=”not supported”
return command
def gethostNAME(vendors, s):
if s==”error”:
return “error”
l=[]
h=””
if not s:
s=”ERROR 003 can not extract hosname!!!”
if str.lower(vendors)==”cisco”:
pattern=”hostname”
dli=” ”
elif str.lower(vendors)==”cisco_ios”:
pattern=”hostname”
dli=” ”
else:
print(“ERR 004 vendors %s Hostname not support!” %(vendors))
hostname=vendors
return hostname
l=(s.strip()).split(‘\n’)
for i in l:
p=re.compile(pattern)
m=p.match(i)
if m :
h=i
l=h.split(dli)
hostname=l[1].strip()
return hostname
def saveCONFIG(switch2, data):
host=gethostNAME(switch2[5], data)
fname=host+’_’+switch2[0]+”.txt”
f=open(fname, “w”)
f.write(data)
f.close()
def main(switch2):
print(“switch2 information is…”)
print(switch2)
command=decCOMMAND(switch2)
if switch2[4]==’ssh’:
result=runSSH(switch2, command)
elif switch2[4]==’telnet’:
result=runTELNET(switch2, command)
else:
print(switch2[4])
print(“Not supported…”)
result=”Not supported…”
saveCONFIG(switch2, result)
if __name__==”__main__”:
argn=len(sys.argv)
if argn==1:
print(“use default switch2 info file ‘switch2.txt’…”)
fname=”switch2.txt”
elif argn>2:
print(“use only one switch2 info file\nProgram ended…”)
exit()
else:
fname=sys.argv[1]
try:
f=open(fname, ‘r’)
except:
print(“File ‘%s’ not found” %(fname))
exit()
while True:
l=f.readline().strip(‘\n’)
if not l:
break
switch2=l.split(‘\t’)
# print(switch2)
n=len(switch2)
if n !=6:
print(“switch2 information format is wrong…\n”)
print(switch2)
continue
else:
pool=concurrent.futures.ProcessPoolExecutor(4)
future=pool.submit(main, (switch2))
future.done()
f.close()
어떤게 문제일까요?
안녕하세요 좋은 정보 공유주셔서 감사합니다.
선생님의 스크립트 사용시 “show run” 명령 대신에 “show processes memory”
명령을 사용하고 싶어서 명령부부만 수정했는데 안되네요.;;
가능하게 하는방법이 있을까요?
혹시 에러 메시지가 나오나요?
나온다면 확인이 필요하구요.
아니면, https://blog.boxcorea.com/wp/archives/2383 가 스위치 점검 스크립트인데, 여기 명령실행 결과도 저정되도록 만들었기때문에 참고하셔도 좋을듯 합니다.
‘show processes memory\r\n’ 으로 한번 해 보시죠.
명령 입력뒤에 enter key 값이 없어서 그럴 수도 있을것 같네요.
show processes memory 명령은 이제 가능해요..ㅎㅎ
그런데 뒤에 \r\nshow vlan br\r\nshow int status 이렇게 넣어주면 오류가 생기네요.;;
cisco ssh는 명령을 하나만 넣을수 있나요?
시스코와 관계없습니다.
위 스크립트가 ssh 세션을 유지하지 않아서 명령어 하나 실행하고 연결이 종료되어서 발생하는 현상입니다.
ssh 세션을 유지하도록 스크립트를 변경하면 명령어 여러개 실행이 가능합니다.
파이썬 프로그래밍 가능하시다면 아래 코드 참고하시고요.
클래스 메소드의 일부분이라 그냥 작동하지는 않아요. 세션유지는 channel 부분 참고하시면 됩니다.
ssh경우 원하는 결과가 잘 안나올 수도 있고요. 파이썬 프로그래밍 가능고 자동화에 관심있으시면, nornir를 추천합니다.
—
def through_ssh(self):
raw = str()
if self.dev_info[‘conn_protocol’].lower() != ‘ssh’:
print(‘Connection Protocol %s is not SSH.’ % (self.dev_info[‘conn_protocol’]))
exit()
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
ssh.connect(hostname=self.dev_info[‘dev_ipa’], port=self.dev_info[‘conn_port’],
username=self.dev_info[‘admin_id’], password=self.dev_info[‘admin_passwd’])
channel = ssh.invoke_shell()
for i in self.command:
channel.send(i + ‘\n’)
time.sleep(1)
data = channel.recv(65535).decode(‘ascii’)
raw = raw +data.replace(‘\r\n’, ‘\n’)
except Exception as ex:
print(‘Device %s ssh connect error: %s’ % (self.dev_info[‘dev_ipa’], ex))
raw = raw + ‘Error’
finally:
ssh.close()
return raw
참고하겠습니다 .감사합니다
import telnetlib
import re
import time
import paramiko
import concurrent.futures
import sys
# backup_switch2.py. ver 0.0.5
# Script by snowffox.
#
# Document: https://blog.boxcorea.com/wp/archives/2277
#
# Supported switch2 is Extreme EXOS switch2 and Cisco IOS switch2
# Supported protocol is telnet and ssh.
#
#
# switch2 info : ip, port, user_id, passwd, protocol, vendor(split by tab(\t) char)
#
# ver 0.0.2 : Error handling. Support SSH with netmiko
# ver 0.0.3 : Full support SSH with paramiko and hostname support.
# ver 0.0.4 : Improve performance by using Thread.
# ver 0.0.5 : Support custom file name on command line option.
def runSSH(switch2, command):
try:
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=switch2[0], port=switch2[1], username=switch2[2], password=switch2[3])
# stdin, stdout, stderr=ssh.exec_command(command.encode(‘ascii’))
# data=((stdout.read()).decode(‘ascii’)).replace(‘\r\n’, ‘\n’)
channel = ssh.get_transport().open_session() #서버에 새 채널 요청
channel.get_pty(command.encode(‘ascii’)) # 이 줄 없으면 Welcome to Ubuntu는 뜨고 그 뒤로 쉘이 무반응
channel.invoke_shell()
data = channel.recv(1024).decode(‘ascii’).replace(‘\r\n’, ‘\n’)
print(data)
except:
print(“ERROR 001: SSH Connection error…”)
data=”error”
return data
def runTELNET(switch2, command):
try:
tn=telnetlib.Telnet(switch2[0], switch2[1], 20)
# tn.set_debuglevel(1)
prompt=getPrompt(switch2[5])
tn.read_until(prompt[0].encode(‘ascii’))
tn.write(switch2[2].encode(‘ascii’)+b”\n”)
tn.read_until(prompt[1].encode(“ascii”))
tn.write(switch2[3].encode(‘ascii’)+b”\n”)
tn.write(command.encode(‘ascii’)+b”\n”)
tn.write(“exit”.encode(‘ascii’)+b”\n”)
data=tn.read_all().decode(‘ascii’)
tn.close()
except:
print(“ERROR 002: TELNET Connection error…\n”)
data=”error”
return data
def getPrompt(vendor):
if vendor==’cisco’:
prompt=[‘username: ‘,’password: ‘]
elif vendor==’nexus’:
prompt=[‘login: ‘,’Password: ‘]
elif vendor==’cisco_ios’:
prompt=[‘login as: ‘,’| password: ‘]
return prompt
def decCOMMAND(switch2):
if switch2[5]==’cisco’ and switch2[4]==’telnet’:
command=”ter le 0\r\nshow run\r\nshow proce memo\r\nshow ver”
elif switch2[5]==’cisco_ios’ and switch2[4]==’ssh’:
command=’ter le 0\r\nshow run\r\nshow proce memo\r\nshow ver’
elif switch2[5]==’nexus’ and switch2[4]==’telnet’:
command=”terminal length 0\r\nshow run”
elif switch2[5]==’nexus’ and switch2[4]==’ssh’:
command=’show run’
else :
print(“%s switch2 is not support…” %(switch2[5]))
command=”not supported”
return command
def gethostNAME(vendors, s):
if s==”error”:
return “error”
l=[]
h=””
if not s:
s=”ERROR 003 can not extract hosname!!!”
if str.lower(vendors)==”cisco”:
pattern=”hostname”
dli=” ”
elif str.lower(vendors)==”cisco_ios”:
pattern=”hostname”
dli=” ”
else:
print(“ERR 004 vendors %s Hostname not support!” %(vendors))
hostname=vendors
return hostname
l=(s.strip()).split(‘\n’)
for i in l:
p=re.compile(pattern)
m=p.match(i)
if m :
h=i
l=h.split(dli)
hostname=l[1].strip()
return hostname
def saveCONFIG(switch2, data):
host=gethostNAME(switch2[5], data)
fname=host+’_’+switch2[0]+”.txt”
f=open(fname, “w”)
f.write(data)
f.close()
def main(switch2):
print(“switch2 information is…”)
print(switch2)
command=decCOMMAND(switch2)
if switch2[4]==’ssh’:
result=runSSH(switch2, command)
elif switch2[4]==’telnet’:
result=runTELNET(switch2, command)
else:
print(switch2[4])
print(“Not supported…”)
result=”Not supported…”
saveCONFIG(switch2, result)
if __name__==”__main__”:
argn=len(sys.argv)
if argn==1:
print(“use default switch2 info file ‘switch2.txt’…”)
fname=”switch2.txt”
elif argn>2:
print(“use only one switch2 info file\nProgram ended…”)
exit()
else:
fname=sys.argv[1]
try:
f=open(fname, ‘r’)
except:
print(“File ‘%s’ not found” %(fname))
exit()
while True:
l=f.readline().strip(‘\n’)
if not l:
break
switch2=l.split(‘\t’)
# print(switch2)
n=len(switch2)
if n !=6:
print(“switch2 information format is wrong…\n”)
print(switch2)
continue
else:
pool=concurrent.futures.ProcessPoolExecutor(4)
future=pool.submit(main, (switch2))
future.done()
f.close()
SSH 접속은 되는것 같아요 프롬프트 배너가 출력이 되는거보면요…
그런데 text 파일 생성도 안되고 로그도 저장이 안되네요…
혹시 ssh 접속을 하려는 대상 장비가 네트워크 스위치인가요? 아니면 리눅스 서버인가요?
주석에 ubuntu 부분이 있어서 여쭤봅니다.
만약 네트워크 장비가 아니라면, 어떤 동작을 원하시는 것인지요?
만약, 시스코 스위치라면, 접속시 enable 상태가 되도록 설정이 되어 있어야 합니다.
cisco switch 입니다. 주석은 여기저기에서 자료를 수집해서 엮다보니 남아있네요..
스위치는 접속시 enable 상태입니다.
현 문제는 invoke_shell 명령을 억지로 끼어넣다보니 def decCOMMAND 구간에서
명령이 session에 정상으로 들어가지 않는것 같은데 해결할 수 있는 방안이 있는지 문의드려요
감사합니다.
Author
일단, channel.recv(1024) 이 부분이 작아서 그럴 수 도 있을것 같네요.
숫자를 65535로 늘여 보시죠.
아, 잘 실행되는 코드를 메일로 첨부해 보냈습니다. 참고 하시기 바랍니다.
안녕하세요
첨부 보내주신 파일과 위 script를 조합해서 실행하였지만
원하는 결과가 생성이 되지않습니다.
조언 부탁드립니다.
수정파일은 메일로 첨부 드렸습니다.
감사합니다.