[paramiko] migrate from Telnet to SSH
James Bardin
jbardin at bu.edu
Thu Jul 12 09:41:31 PDT 2007
It seems the transport is being closed after the first command, probably
by the server.
This isn't the default behavior for openSSH, but it might be on cisco
devices.
I don't have anything cisco to connect to, so I can't offer much help on
testing.
I would step through a couple of the demos, and see where things might
go wrong. You may need to start a new transport for each command if you
don't want to use an interactive shell. The server may even be closing
the socket, in which case you'll need to reconnect entirely.
-jim
Chris Hallman wrote:
> Thanks! Ok, I was able to get a connection and retrieve the output of one
> command, however when I try a second command, I receive an error:
>
> program:
>
> import paramiko
>
> client = paramiko.SSHClient()
>
> # ignore host keys for the test
> client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
>
> client.connect('rtr3926', 22, 'cworks', '#####')
>
> (stdin, stdout, stderr) = client.exec_command('sh ver | i IOS')
> print stdout.read()
> (stdin, stdout, stderr) = client.exec_command('sh ver | i IOS')
> print stdout.read()
>
> output:
>
>> pythonw -u "sshclient.test.py"
>
> ********************************************************
> * *
> * WARNING WARNING WARNING *
> * *
> * IF YOU ARE NOT AUTHORIZED TO CONNECT TO *
> * THIS SYSTEM, YOU MUST DISCONNECT AT ONCE. *
> * UNAUTHORIZED ACCESS MAY BE PROSECUTED. *
> * *
> ********************************************************
> Cisco IOS Software, 2800 Software (C2800NM-ADVSECURITYK9-M), Version
> 12.4(8c),
> RELEASE SOFTWARE (fc3) (output from first command)
>
> Traceback (most recent call last):
> File "sshclient.test.py", line 12, in <module>
> (stdin, stdout, stderr) = client.exec_command('sh ver | i IOS')
> File "c:\python25\Lib\site-packages\paramiko\client.py", line 315, in
> exec_command
> chan.exec_command(command)
> AttributeError: 'NoneType' object has no attribute 'exec_command'
>> Exit code: 1
>
> What happened? I attached a copy one of my programs that currently uses
> Telnet so that you can get an idea of what I need to do via SSH.
>
>
> On 7/11/07, James Bardin <jbardin at bu.edu> wrote:
>>
>> Hi Chris,
>>
>> Take a look in the SSHClient class (download the most recent version
>> though). You don't have to use an interactive shell. The SSHClient
>> class also does nice things like handle an ssh-agent if available, and
>> try to use your private keys if they're not encrypted.
>>
>> A *real* basic example:
>>
>> ############################
>> import paramiko
>>
>> client = paramiko.SSHClient()
>>
>> # ignore host keys for the test
>> client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
>>
>> client.connect('hostname', 22, 'username', 'Passw0rd')
>>
>> (stdin, stdout, stderr) = client.exec_command('command')
>> print stdout.read()
>> ############################
>>
>>
>> -jim
>>
>>
>> Chris Hallman wrote:
>> > I've written numerous programs that utilize Telnet to gather data,
>> > reload,
>> > or reconfigure network devices. I didn't use an interactive session
>> > like the
>> > demo SSH scripts use. I had the program issuing the commands.
>> >
>> > We're migrating from Telnet to SSH. I was hoping to find some examples
>> > where
>> > the program is interacting with the remote device but isn't using a
>> > shell.
>> > Does anyone have a program similar to what I described that I could
>> > look at?
>> >
>> >
>> >
>> > Thanks!!
>> >
>>
>>
>
> ------------------------------------------------------------------------
>
> #
> # This script obtains a directory listing, strips the extensions and telnets to the
> # device (which is the filename in the directory). Then it writes the commands in the
> # file to the device, saves config and writes it back to SYSMAN. It can be run using:
> # python tftp.file.py
> #
> # Note: "os" is imported for future functionality.
> #
> # by: TCDH
> # on: 10/17/05
> # release#2
> # revised: 10/18/05 TCDH - Added logic to check for offline devices and sign-on failures.
> # 11/17/05 TCDH - Added logic to control the number of threads.
> # 01/23/06 TCDH - Moved variables and main logic to the bottom of the program.
> # 08/14/06 TCDH - Added tn.close() to properly close telnet sessions.
>
> import os, os.path, random, re, smtplib, socket, string, sys, telnetlib, threading, time
> from time import strftime
> from threading import Thread
>
>
> class SendFile(threading.Thread):
> def __init__(self, host):
> """ This instantiates the class and tries to open a connection to the device,
> with error checking. """
> Thread.__init__(self)
> self.host = host
> self.filename = filename
> self.filepath = filepath
> self.fileSize = fileSize
> try:
> self.tn = telnetlib.Telnet(self.host)
> except socket.error, err:
> if "Operation timed out" in err:
> configResult[self.host] = ["connection timed out"]
> return
> elif "getaddrinfo failed" in err:
> configResult[self.host] = ["DNS resolution failed"]
> return
> elif "No route to host" in err:
> configResult[self.host] = ["device unreachable"]
> return
> elif "Network is unreachable" in err:
> configResult[self.host] = ["device unreachable"]
> return
> else:
> configResult[self.host] = ["unspecified network error"]
> return
>
> def run(self):
> """ This function is the main function. It calls the Logon function and the
> TFTPfile function."""
> connect_status = self.Logon()
> if connect_status == "authfail":
> configResult[self.host] = ["authentication failed"]
> self.tn.close()
> return
> transmit = self.TFTPFile()
> self.tn.write("exit\n")
> self.tn.close()
> if transmit == "failed":
> return
> for x in range(6):
> try:
> os.remove(self.filepath)
> except:
> time.sleep(5)
> continue
> return
>
> def Logon(self):
> """ This function attempts to logon to the device 3 times."""
> for x in range(3):
> self.tn.read_until("Username:", 7)
> self.tn.write(user + "\n")
> (index, match, read) = self.tn.expect(["Password:"], 7)
> self.tn.write(pswd + "\n")
> (index, match, read) = self.tn.expect([self.host.upper()], 7)
> if match:
> return
> if not match:
> if x == 2:
> return "authfail"
> else:
> continue
>
> def TFTPFile(self):
> """ This functions does all the work. It does a write to make sure the
> startup-confg and running-confg are similar and copies the file into running-confg
> with error checking. If all is well, then it does a write to save the changes and
> does a write net (with error checking)."""
> self.tn.write("wr\n")
> (index, match, read) = self.tn.expect(["OK"], 15)
> if not match:
> self.tn.write("yes\n")
> time.sleep(random.uniform(0,2))
> self.tn.write("copy tf runn\n")
> self.tn.read_until("host []?", 7)
> time.sleep(random.uniform(0,2))
> self.tn.write("192.168.136.51\n")
> self.tn.read_until("filename []?", 7)
> time.sleep(random.uniform(0,2))
> self.tn.write(self.filename +"\n")
> time.sleep(random.uniform(0,2))
> self.tn.read_until("[running-config]?", 7)
> time.sleep(random.uniform(0,2))
> self.tn.write("\n")
> time.sleep(random.uniform(0,2))
> x = self.tn.read_until(self.host.upper() + "#", 35)
> if "% Incomplete command before pipe" in x:
> configResult[self.host] = ["command authentication failed"]
> return "failed"
> elif "% Invalid input detected" in x:
> configResult[self.host] = ["invalid command syntax"]
> return "failed"
> elif "^" in x:
> configResult[self.host] = ["unexpected command error"]
> return "failed"
> else:
> if "[OK" not in x:
> configResult[self.host] = ["TFTP get failed"]
> return "failed"
> elif str(self.fileSize) not in x:
> configResult[self.host] = ["TFTP filesize mismatch"]
> return "failed"
> else:
> pass
> self.tn.write("wr\n")
> self.tn.read_until(self.host.upper() + "#", 7)
> time.sleep(random.uniform(0,2))
> self.tn.write("wr net\n")
> self.tn.read_until("]?")
> time.sleep(random.uniform(0,2))
> self.tn.write("192.168.19.201\n")
> self.tn.read_until("]?")
> time.sleep(random.uniform(0,2))
> self.tn.write(self.host.lower() + "-confg\n")
> self.tn.read_until("[confirm]")
> time.sleep(random.uniform(0,2))
> self.tn.write("\n")
> (index, match, read) = self.tn.expect(["OK"], 30)
> if "Error opening tftp" in read:
> configResult[self.host] = ["write net failed"]
> return "failed"
>
> def count_active():
> """ This function returns the number of Getter threads that are alive """
> num_active = 0
> for thread in tlist:
> if thread.isAlive():
> num_active += 1
> return num_active
>
> def emailResult(e, s, r, m):
> """ This function send emails based on the input. """
> mailServer = smtplib.SMTP(e)
> mailServer.sendmail(s, r, m)
> mailServer.quit()
>
> configResult = {}
> emailsrvr = "mail.publix.com"
> threads = []
> dirPath = (r"c:\tftp")
> dirList = os.listdir(dirPath)
> Max_Threads = 20
> pswd = "#####"
> receiver_list = ["<chris.hallman at publix.com>"]
> sender = "<tftp.file at publix.com>"
> subject = "TFTP file results"
> text = ""
> tlist = []
> user = "cworks"
> logFile = (r"c:\logs\tftp.file.log")
> output = file(logFile, "a")
> output.write("\ntftp.file script started -" + strftime(" %H:%M:%S %x") + "\n")
> output.flush()
>
> for entry in dirList:
> host = entry.replace(".txt", "")
> filename = entry.lower()
> filepath = (dirPath + "\\" + filename)
> fileSize = str(os.path.getsize(filepath)) + " bytes"
> while count_active() >= Max_Threads:
> time.sleep(1)
> threads = SendFile(host)
> tlist.append(threads)
> threads.start()
>
> for thread in tlist:
> thread.join()
>
> configResult_sorted = sorted(configResult.items())
> for x in range(len(configResult_sorted)):
> text = text + str(configResult_sorted[x]) + "\n\r"
> text = text.replace("[", "").replace("]", "").replace("'", "").replace("(", "").replace(")", "").replace(",", "\t")
> output.write(text)
> output.flush()
>
> for receiver in receiver_list:
> header = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (sender, receiver, subject)
> heading = "If this email is blank, then no failures occurred.\r\n\r\n"
> msg = header + heading + text
> emailResult(emailsrvr, sender, receiver, msg)
>
> output.write("\ntftp.file script completed -" + strftime(" %H:%M:%S %x") + "\n")
> output.flush()
> output.close()
>
More information about the paramiko
mailing list