Implementation of the function of forcing paramiko library to exit at a given time when executing commands

  • 2021-09-12 01:37:22
  • OfStack

When using paramiko library ssh to connect to a remote cloud host, it is very occasionally stuck, and the connection cannot be exited (it can be caused by the restart of the cloud host when executing commands, etc.). It takes a given period of time to exit the connection regardless of whether the command execution is stuck or not, showing a command execution timeout error.

Implementation method:

Thread + event, execute ssh command in thread, and configure timeout time for event.

Code example:

1 from threading import Thread, Event

2 import paramiko


class SshClient(object):

  def __init__(self, ip, port, username, password):
    self.ip = ip
    self.host = host
    self.username = username
    self.password = password

  def exec_command(cmd, timeout):
    log.info(u" In ip:%s Execute commands on %s" % (self.ip, cmd))
    sc = paramiko.SSHClient()
    sc.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    #  Used to receive stdout stderror status Information 
    res = [None, None, None]

    def get_return(start_event, res_list):
      _, cmd_stdout, cmd_stderr = sc.exec_command(command=cmd, timeout=timeout)
      channel = cmd_stdout.channel
      cmd_status = channel.recv_exit_status()
      res_list[0] = cmd_stdout
      res_list[1] = cmd_stderr
      res_list[2] = cmd_status
      start_event.set()  #  Indicates that the thread has finished executing 

    try:
      sc.connect(hostname=self.ip, port=self.port, username=self.username, password=self.password, timeout=30)  #  Here's timeout Is used by connection, which is different from what we want 
      start_evt = Event()
      t = Thread(target=get_return, args=(start_evt, res))
      t.start()
      start_evt.wait(timeout=timeout)
      #  Execution here indicates that the thread has exited 
      if None in res:
        raise Exception(u" Command timed out ")
      stdout, stderr, status = res
      if status != 0:
        raise Exception(u" Command execution returns non- 0 ! The return value is %s, The error message is %s" % (status, stdout.read() + stderr.read()))
      return stdout.read() + stderr.read()
    finally:
      sc.close()
}

Knowledge point supplement:

Introduction of python paramiko

1: Use paramiko

# Set the remote host address and port for an ssh connection
t=paramiko.Transport((ip,port))
# Set the login name and password
t.connect(username=username,password=password)
# Open 1 channel after successful connection
chan=t.open_session()
# Set session timeout
chan.settimeout(session_timeout)
# Open remote terminal
chan.get_pty()
# Activate terminal
chan.invoke_shell()

You can then execute commands remotely and get feedback locally through chan. send ('command') and chan. recv (recv_buffer).

2: Introduction to two modules of paramiko

paramiko has two modules, SSHClient () and SFTPClient ()

Use code for SSHClient ():


import paramiko

ssh = paramiko.SSHClient() #  Create SSH Object 
#  Allow connection not in know_hosts Host in file 
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#  Connect to the server 
ssh.connect(hostname='192.168.2.103', port=22, username='root', password='123456')

stdin, stdout, stderr = ssh.exec_command('ls') #  Execute a command 
result = stdout.read() #  Get command results 
print (str(result,encoding='utf-8'))
ssh.close() #  Close the connection 

There is an transport variable in SSHClient (), which is used to get the connection. We can also get the transport variable separately and then perform the connection operation


import paramiko

transport = paramiko.Transport(('192.168.2.103', 22))
transport.connect(username='root', password='123456')

ssh = paramiko.SSHClient()
ssh._transport = transport

stdin, stdout, stderr = ssh.exec_command('df')
print (str(stdout.read(),encoding='utf-8'))

transport.close()

Upload, download and command execution with transport:


#coding:utf-8
import paramiko
import uuid

class SSHConnection(object):

def __init__(self, host='192.168.2.103', port=22, username='root',pwd='123456'):
self.host = host
self.port = port
self.username = username
self.pwd = pwd
self.__k = None

def connect(self):
transport = paramiko.Transport((self.host,self.port))
transport.connect(username=self.username,password=self.pwd)
self.__transport = transport

def close(self):
self.__transport.close()

def upload(self,local_path,target_path):
#  Connect, upload 
# file_name = self.create_file()
sftp = paramiko.SFTPClient.from_transport(self.__transport)
#  Will location.py  Upload to server  /tmp/test.py
sftp.put(local_path, target_path)

def download(self,remote_path,local_path):
sftp = paramiko.SFTPClient.from_transport(self.__transport)
sftp.get(remote_path,local_path)

def cmd(self, command):
ssh = paramiko.SSHClient()
ssh._transport = self.__transport
#  Execute a command 
stdin, stdout, stderr = ssh.exec_command(command)
#  Get command results 
result = stdout.read()
print (str(result,encoding='utf-8'))
return result

ssh = SSHConnection()
ssh.connect()
ssh.cmd("ls")
ssh.upload('s1.py','/tmp/ks77.py')
ssh.download('/tmp/test.py','kkkk',)
ssh.cmd("df")
ssh.close()

Related articles: