[python script] 스위치 설정 저장

파이썬 스크립트를 이용한 스위치 설정 정보 저장.(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

    • snowfox on 2018년 8월 3일 at 10:40 오전
    • Reply

    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에 이미 설치되어 있으므로, 다시 설치할 필요가 없음.

      • jams on 2022년 8월 19일 at 11:14 오전
      • Reply

      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()
      어떤게 문제일까요?

    • 김동민 on 2022년 6월 10일 at 11:06 오전
    • Reply

    안녕하세요 좋은 정보 공유주셔서 감사합니다.
    선생님의 스크립트 사용시 “show run” 명령 대신에 “show processes memory”
    명령을 사용하고 싶어서 명령부부만 수정했는데 안되네요.;;

    가능하게 하는방법이 있을까요?

      • snowffox on 2022년 6월 10일 at 11:28 오전
      • Reply

      혹시 에러 메시지가 나오나요?
      나온다면 확인이 필요하구요.

      아니면, https://blog.boxcorea.com/wp/archives/2383 가 스위치 점검 스크립트인데, 여기 명령실행 결과도 저정되도록 만들었기때문에 참고하셔도 좋을듯 합니다.

      • snowffox on 2022년 6월 10일 at 11:33 오전
      • Reply

      ‘show processes memory\r\n’ 으로 한번 해 보시죠.
      명령 입력뒤에 enter key 값이 없어서 그럴 수도 있을것 같네요.

    • 김동민 on 2022년 6월 16일 at 9:49 오전
    • Reply

    show processes memory 명령은 이제 가능해요..ㅎㅎ
    그런데 뒤에 \r\nshow vlan br\r\nshow int status 이렇게 넣어주면 오류가 생기네요.;;

    cisco ssh는 명령을 하나만 넣을수 있나요?

      • snowffox on 2022년 6월 16일 at 10:07 오전
      • Reply

      시스코와 관계없습니다.

      위 스크립트가 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

    • 김동민 on 2022년 6월 16일 at 12:20 오후
    • Reply

    참고하겠습니다 .감사합니다

    • 김동민 on 2022년 8월 19일 at 11:16 오전
    • Reply

    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 파일 생성도 안되고 로그도 저장이 안되네요…

      • snowffox on 2022년 8월 19일 at 11:51 오전
      • Reply

      혹시 ssh 접속을 하려는 대상 장비가 네트워크 스위치인가요? 아니면 리눅스 서버인가요?
      주석에 ubuntu 부분이 있어서 여쭤봅니다.
      만약 네트워크 장비가 아니라면, 어떤 동작을 원하시는 것인지요?

      만약, 시스코 스위치라면, 접속시 enable 상태가 되도록 설정이 되어 있어야 합니다.

        • jams on 2022년 8월 19일 at 4:03 오후
        • Reply

        cisco switch 입니다. 주석은 여기저기에서 자료를 수집해서 엮다보니 남아있네요..

        스위치는 접속시 enable 상태입니다.
        현 문제는 invoke_shell 명령을 억지로 끼어넣다보니 def decCOMMAND 구간에서
        명령이 session에 정상으로 들어가지 않는것 같은데 해결할 수 있는 방안이 있는지 문의드려요

        감사합니다.

        1. 일단, channel.recv(1024) 이 부분이 작아서 그럴 수 도 있을것 같네요.
          숫자를 65535로 늘여 보시죠.
          아, 잘 실행되는 코드를 메일로 첨부해 보냈습니다. 참고 하시기 바랍니다.

    • jams on 2022년 8월 26일 at 11:12 오전
    • Reply

    안녕하세요

    첨부 보내주신 파일과 위 script를 조합해서 실행하였지만
    원하는 결과가 생성이 되지않습니다.
    조언 부탁드립니다.

    수정파일은 메일로 첨부 드렸습니다.

    감사합니다.

답글 남기기

Your email address will not be published.