python3 远程Linux服务器执行命令和上传下载文件

一、简介

paramiko是一个基于SSH用于连接远程服务器并执行相关操作(SSHClient和SFTPClinet,即一个是远程连接,一个是上传下载服务),使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。

二、使用

1、下载

pip3 install paramiko

2、模块使用

SSHClient:

远程连接分为两种:(1)基于用户名密码连接 (2)基于公钥秘钥连接

通过是用paramiko远程操作,其实本质也分为两种:(1)只用SSHClient (2)自己创建一个transport

(1)基于用户名和密码的连接

import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname=‘c1.salt.com‘, port=22, username=‘GSuser‘, password=‘123‘)
# 执行命令
stdin, stdout, stderr = ssh.exec_command(‘ls‘)
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()

SSHClient 封装 Transport

import paramiko

transport = paramiko.Transport((‘hostname‘, 22))
transport.connect(username=‘GSuser‘, password=‘123‘)

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

stdin, stdout, stderr = ssh.exec_command(‘df‘)
print(stdout.read())

transport.close()

(2)基于公钥秘钥连接

import paramiko

# 添加公钥密钥文件
private_key = paramiko.RSAKey.from_private_key_file(‘/home/auto/.ssh/id_rsa‘)
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname=‘192.168.1.105‘, port=22, username=‘666‘, key=private_key)
# 执行命令
# 执行多条命令时需要将各个命令用‘;’隔开,最后将get_pty设为True;如果是单条命令的则只传入命令即可
stdin, stdout, stderr = ssh.exec_command(‘cd dw/2020-06-02/;ls‘,get_pty=True)
# 多条命令的话会将执行结果一起返回,所以建议遍历打印。也可以直接打印:print(stdout.read().decode(‘utf-8‘))
files = stdout.readlines()
for i in files:
    # 打印执行反馈结果
    print(i.encode().decode(‘utf-8‘).split()[0])
# 打印报错信息
print(stderr.read().decode(‘utf-8‘))
# 关闭连接
ssh.close()

  SSHClient 封装Transport

import paramiko

private_key = paramiko.RSAKey.from_private_key_file(‘/home/auto/.ssh/id_rsa‘)
transport = paramiko.Transport((‘192.168.1.105‘, 22))
transport.connect(username=‘666‘, pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
# 执行多条命令时需要将各个命令用‘;’隔开,最后将get_pty设为True;如果是单条命令的则只传入命令即可
stdin, stdout, stderr = ssh.exec_command(‘cd dw/2020-06-02/;ls‘,get_pty=True)
# 多条命令的话会将执行结果一起返回,所以建议遍历打印。也可以直接打印:print(stdout.read().decode(‘utf-8‘))
files = stdout.readlines()
for i in files:
    # 打印执行反馈结果
    print(i.encode().decode(‘utf-8‘).split()[0])
# 打印报错信息
print(stderr.read().decode(‘utf-8‘))
transport.close()

SFTPClient:

用于连接远程服务器并进行上传下载功能。

(1)基于用户名密码上传下载

import paramiko

transport = paramiko.Transport((‘192.168.1.105‘,22))
transport.connect(username=‘666‘,password=‘123‘)

sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put(‘/tmp/location.py‘, ‘/tmp/test.py‘)
# 将remove_path 下载到本地 local_path
sftp.get(‘remove_path‘, ‘local_path‘)

transport.close()

(2)基于公钥秘钥上传下载

import paramiko

private_key = paramiko.RSAKey.from_private_key_file(‘/home/auto/.ssh/id_rsa‘)

transport = paramiko.Transport((‘192.168.1.105‘, 22))
transport.connect(username=‘666‘, pkey=private_key )

sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put(‘/tmp/location.py‘, ‘/tmp/test.py‘)
# 将remove_path 下载到本地 local_path
sftp.get(‘remove_path‘, ‘local_path‘)

transport.close()

Demo: 实现远程命令执行和文件上传

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko

class SSHConnection(object):

    def __init__(self, host=‘192.168.12.68‘, port=22, username=‘locojoy‘,pwd=‘123321QQ!‘):
        self.host = host
        self.port = port
        self.username = username
        self.pwd = pwd
        self.__k = None

    def run(self):
        self.connect()  # 连接远程服务器
        self.upload(‘db.py‘,‘/tmp/1.py‘)  # 将本地的db.py文件上传到远端服务器的/tmp/目录下并改名为1.py
        self.cmd(‘df‘)  # 执行df 命令
        self.close()    # 关闭连接

    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):
        sftp = paramiko.SFTPClient.from_transport(self.__transport)
        sftp.put(local_path,target_path)

    def cmd(self, command):
        ssh = paramiko.SSHClient()
        ssh._transport = self.__transport
        # 执行命令
        stdin, stdout, stderr = ssh.exec_command(command)
        # 获取命令结果
        result = stdout.read()
        print(result)
        return result

obj = SSHConnection()
obj.run()

paramiko在堡垒机中的应用

(1)简单实例:远程连接一台主机,操作命令,linux版本,输入终端为回车则发送命令。不支持tab补全功能。

import paramiko, sys, os, socket, select, getpass
from paramiko.py3compat import u   # 在python3中是这样使用的,如果在Python2中则注释这行

# 这个程序依赖于终端,只能在Liunx下运行,windows用其他的方式

tran = paramiko.Transport((‘192.168.12.68‘, 22,))
tran.start_client()
tran.auth_password(‘locojoy‘, ‘123321QQ!‘)

# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()

# 原始的方法利用终端进行收发消息
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容

while True:
    # 监视用户输入和服务器返回数据
    # sys.stdin 处理用户输入
    # chan 是之前创建的通道,用于接收服务器返回信息
    # 通过select监听终端(输入输出),一旦变化,就将拿到的数据发送给服务器
    # 通过监听socket句柄,如果有变化表示服务器要给我发消息
    readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1)
    # 通过select.select 监听chan(打开的通道(和远程服务器连接的状态)), sys.stdin(输入),一旦变化就写入readable
    # 当chan变化时,加入到readable,远程服务器发送内容过来
    if chan in readable:
        try:
            x = u(chan.recv(1024))  # Python3用这个
            # x = chan.recv(1024)  Python2使用这个
            if len(x) == 0:
                print(‘\r\n*** EOF\r\n‘)
                break
            sys.stdout.write(x)   # 写入缓冲区
            sys.stdout.flush()    # 刷新,将缓冲区内容显示出来
        except socket.timeout:
            pass
    # 当sys.stdin 放入readable中时,将获取到的内容发送到远程服务器
    if sys.stdin in readable:
        inp = sys.stdin.readline()
        chan.sendall(inp)

chan.close()
tran.close()

(2)每按一个键就发送记录,并支持tab自动补全

import paramiko, sys, os, socket, select, getpass, termios, tty
from paramiko.py3compat import u

tran = paramiko.Transport((‘10.211.55.4‘, 22,))
tran.start_client()
tran.auth_password(‘wupeiqi‘, ‘123‘)
# 打开一个通道
chan = tran.open_session()
# 获取一个终端
chan.get_pty()
# 激活器
chan.invoke_shell()

# 获取原tty属性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 为tty设置新属性
    # 默认当前tty设备属性:
    #   输入一行回车,执行
    #   CTRL+C 进程退出,遇到特殊字符,特殊处理。
    # 这是为原始模式,不认识所有特殊符号
    # 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
    tty.setraw(sys.stdin.fileno())  # 恢复终端原始状态,每按一个键就发送
    chan.settimeout(0.0)

    while True:
        # 监视 用户输入 和 远程服务器返回数据(socket)
        # 阻塞,直到句柄可读
        r, w, e = select.select([chan, sys.stdin], [], [], 1)
        if chan in r:  # 获取服务返回的内容
            try:
                x = u(chan.recv(1024))
                if len(x) == 0:
                    print(‘\r\n*** EOF\r\n‘)
                    break
                sys.stdout.write(x)
                sys.stdout.flush()
            except socket.timeout:
                pass
        if sys.stdin in r: # 发送命令
            x = sys.stdin.read(1) # 读取一个字符
            if len(x) == 0:
                break
            chan.send(x) # 发送一个字符

finally:
    # 重新设置终端属性,将终端状态还原
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

chan.close()
tran.close()