pythonparamikociscotelnetlib

Python : get running_config from cisco switch by SSH(Paramiko) OR TELNET(Telnetlib)


I need some help for a script in Python. it works for few times, but I just added some time.sleep() and now the script won't work : It did not connect to switch by SSH or Telnet.

I also need some tips to optimize it, because i'm not pro and i would like to learn more on scripting.

Thank you !

(Sorry commentaries in french :/)

import paramiko, time, re, os
from ciscoconfparse import CiscoConfParse
import telnetlib

###cherche hotes depuis fichier hosts.txt###
with open("./hosts/hosts.txt","r") as f:
    hosts = re.findall(r'(\d+.\d+.\d+.\d+)', f.read())
    f.close()
###boucle pour chaque hotes###
for host in hosts:
    state = ""
    running_config = ""
    try:
        ###Connexion SSH switch###
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(host, username='admin', password='XXXXX')
        ###création shell interactif###
        connection = client.invoke_shell()
        ###commande enable###
        connection.send("enable\n")
        time.sleep(1)
        connection.send("XXXX\n")
        ###commande running-config###
        connection.send("terminal length 0\n") ###Permet l'affichage de l'intégralité des commande###
        time.sleep(1)
        connection.send("show running-config\n")
        ###récupération commande running-config###
        resp_run = connection.recv(10000).decode(encoding='utf-8')
        ###fermeture sessions SSH###
        connection.close()
        client.close()
        ###Traitement running-config###
        regex = re.compile(r'(Current configuration : (.+\n)+end)')
        res = re.findall (regex, resp_run)
        running_config = res[0][0] ###aide appel variable
    except:
        ###si fail SSH test telnet###
        state = "SSH NOK "###Permet génération rapport###
        try:
            ###connexion telnet si SSH NOK###
            session = telnetlib.Telnet(host, 23)
            session.write(b"admin\n")
            session.read_until(b"Password: ")
            session.write(b"XXXXXX\n")
            time.sleep(1)
            session.write(b"enable\n")
            session.read_until(b"Password: ")
            session.write(b"XXXXXX\n")
            session.read_until(b"#")
            session.write(b"term len 0\n")
            session.write(b"show run\n")
            res = session.read_until(b"\nend").decode('utf-8')
            ###fermeture session telnet###
            session.close()
            ###récupération commande running-config###
            regex = re.compile(r'(Current configuration : (.+\n)+end)')
            res = re.findall(regex, res)
            running_config = res[0][0] ###aide appel variable###
        except:
            state += "TELNET NOK"###Permet génération rapport###

    ###Création fichier running_config.txt + dir selon host###
    newpath = ('./config_switch/'+host+'/')
    if not os.path.exists(newpath):
        os.makedirs(newpath)
    f = open("./config_switch/"+host+"/running_config.txt", "w+")
    f.write(running_config)
    f.close()
    ###test ssh telnet pour rapport###
    if not state:
        print (host+" OK")
    else:
        print (host+" : "+state+" ERREUR")

    ###generation rapport###    
    f = open("./rapport.txt","a")
    f.write(state)
    f.close()
    ###arrêt de 2sec par sécurité###
    time.sleep(2)

Solution

  • You should have a good error handling mechanism if you have different version/model devices in your network. So we use below functions for some of operations these might help you.

    Code (SSH Connection):

    #Make Connection To Device Through SSH (If returns None Do Not Proceed)
    def connectToCPESSH(ip, uname, pin, CI_LOCAL_ID, CI_Name, CI_Org_Name, runIDnull):
        ip =  ip.strip()
        SSHCliente = None
        try:
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            client.connect(ip, port=22, username=uname, password=pin, 
                           timeout=240,banner_timeout=250, auth_timeout=500)
            SSHCliente = client.invoke_shell()
            return SSHCliente
        except paramiko.ssh_exception.SSHException as ssh1Err:
            return "Equipment SSH version error : " + str(ssh1Err)
            if isinstance(SSHCliente, paramiko.channel.Channel):
                SSHCliente.close()
            sys.exit()
            return None
        except Exception as e:
            if isinstance(SSHCliente, paramiko.channel.Channel):
                SSHCliente.close()
            try:
                tln = telnetlib.Telnet(ip)
                print("Use Telnet Access to " + str(e) + " ### " + str(t))
            except Exception as t:
                print("Crab! Both Telnet and SSH Connection Failed ! " + str(e) + " ### " + str(t))
            return None
    

    So, in above code we try to connect via SSH and if we get an error about SSHException we log it and if we get any error try Telnet (this is optional, we use Telnet for some old devices).

    Code (Wait For Prompt: Only HW and Cisco)

    #Wait Prompt For The First Connection
    def waitForPrompt(cxn):
        appendedScrnRslt = ""
        lastScrnRslt = ""
        routerType = ""
        outerLoop = True
        timerx = 0
        while outerLoop == True:
            tempScrn = cxn.recv(65100).decode('ascii')
            if(lastScrnRslt != tempScrn):
                appendedScrnRslt += tempScrn
                lastScrnRslt = tempScrn
            if("#" in tempScrn or ">" in tempScrn or "]" in tempScrn):
                if("#" in tempScrn):
                    routerType = "Cisco"
                if(("<" in tempScrn  and ">" in tempScrn) or ("[" in tempScrn  and "]" in tempScrn) ):
                    routerType = "Huawei"
                break
            timerx += 1
            if(timerx >= 100):
                logging.warn("Uppss! No Connection")
                routerType = "N/A"
                break
        return routerType
    

    Waiting prompt is curicial, if you act early your command will not be send to device, and if you act late your cxn might be terminated. So, just checking for prompt (HW: , Cisco: your-router-name#) is better.

    Code ( Send and Receive ):

    #Send Command and Recevie CIdatafromSQL
    def sendCmdToSSH(cxn, cmd, routerType, timeout):
        appendedScrnRslt = ""
        lastScrnRslt = ""
        cxn.send(bytes(cmd+ "\x0D", 'utf-8'))
        time.sleep(2)
        timery = time.perf_counter()
        while time.perf_counter() - timery <= timeout:
            if(routerType == "Cisco"):
                tempScrn = cxn.recv(65100).decode('ascii')
                if(lastScrnRslt != tempScrn):
                    appendedScrnRslt += tempScrn
                    lastScrnRslt = tempScrn
                arrTmp = tempScrn.split('\r\n')
                arrTmp.reverse()
                if("#" in arrTmp[0]):
                    break
                arrTmp = []
            if(routerType == "Huawei"):
                tempScrn = cxn.recv(65100).decode('ascii')
                if(lastScrnRslt != tempScrn):
                    appendedScrnRslt += tempScrn
                    lastScrnRslt = tempScrn
                arrTmp = tempScrn.split('\r\n')
                arrTmp.reverse()
                if(">" in arrTmp[0] or "]" in arrTmp[0] ):
                    break
                arrTmp = []
        return appendedScrnRslt
    

    Send and receive needs a timeout in order to break connection if some error occurs and we definetly need screen results too.

    Code (To Get All Running Config From Cisco):

    singleSSHCxn = connectToCPESSH(ip, uname, pin, CI_LOCAL_ID, CI_Name,
                                   CI_Org_Name, runIDnull) 
    sendCmdToSSH(singleSSHCxn, "terminal length 0", "Cisco", 120)
    cliResult = sendCmdToSSH(singleSSHCxn, "show running-config", "Cisco", 200)
    sendCmdToSSH(singleSSHCxn, "exit", "Cisco", 120)
    

    Hope this will solve your issue.