45.paramiko python操控远程主机

1.paramiko:

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

密码连接方式:

pip install paramiko
paramiko有两个模块SSHClient()和SFTPClient()
1.SSHClient()
方式一:
import paramiko, re, time
class Linux(object):
    def __init__(self, info, port=22):
        self.ip = info['ip']
        self.user = info['user']
        self.passw = info['passw']
        self.port = port

    def linuxConnect(self):
        # 实例化一个transport对象
        try:
            # 创建SSH对象
            self.ssh = paramiko.SSHClient()
            # 允许连接不在know_hosts文件中的主机
            self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            # 建立ssh对象
            self.ssh.connect(hostname=self.ip, port=22, username=self.user, password=self.passw)
            # 执行命令
            stdin, stdout, stderr = self.ssh.exec_command('ls')
            if stdout.read() != '':
                print('连接成功')
                return self.ssh
            else:
                self.linuxConnect()
        except:
            print('连接失败')
            return 'fail'

    def linuxSend(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        if re.search('docker exec', info):
            return
        result = stdout.read().decode()
        return result

    def linuxReceive(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.readlines()
        return result

    # 关闭连接
    def linuxClose(self):
        self.ssh.close()


if __name__ == '__main__':

    linux = Linux({"ip": "10.20.70.11", "user": "root", "passw": "1"})
    linux.linuxConnect()
    rev = linux.linuxReceive('LANG=en_US.UTF-8;sar -u 1 1')
    print(rev[-1])
    print(float(re.findall('(S+)', rev[-1])[2]))
    v_CPU = 100 - float(re.findall('(S+)', rev[-1])[-1])
    print(v_CPU)
    
方式二:
import paramiko, re, time
class Linux(object):
    def __init__(self, info):
        self.ip = info['ip']
        self.user = info['user']
        self.passw = info['passw']

    def linuxConnect(self):
        # 实例化一个transport对象
        try:
            self.transport = paramiko.Transport((self.ip, 22))
            # 建立连接
            self.transport.connect(username=self.user, password=self.passw)
            # 建立ssh对象
            self.ssh = paramiko.SSHClient()
            # 绑定transport到ssh对象
            self.ssh._transport = self.transport
            stdin, stdout, stderr = self.ssh.exec_command('ls')
            if stdout.read() != '':
                print('连接成功')
                return self.ssh
            else:
                self.linuxConnect()
        except:
            print('连接失败')
            return 'fail'

    def linuxSend(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        if re.search('docker exec', info):
            return
        result = stdout.read().decode()
        return result

    def linuxReceive(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.readlines()
        return result

    def upload(self, local_path, target_path):
        # 连接,上传
        # file_name = self.create_file()
        sftp = paramiko.SFTPClient.from_transport(self.transport)
        # 将location.py 上传至服务器 /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 linuxClose(self):
        self.transport.close()


linux = Linux({"ip": "10.20.70.11", "user": "root", "passw": "1"})
linux.linuxConnect()
rev = linux.linuxReceive('LANG=en_US.UTF-8;sar -u 1 1')
print(rev)
v_CPU = 100 - float(re.findall('(S+)', rev[-1])[-1])
print(v_CPU)

SFTPClient()也是使用transport来实现的,因此如果有需求需要执行命令和上传文件糅合在一起的话,那么就需要使用transport的方式来实现。

秘钥连接方式:

方式一:
import paramiko, re, time
class Linux(object):
    def __init__(self, info):
        self.ip = info['ip']
        self.pub_key_file = info["id_rsa"]
        self.user = info["user"]

    def linuxConnect(self):
        # 实例化一个transport对象
        try:
            private_key = paramiko.RSAKey.from_private_key_file(self.pub_key_file)
            self.ssh = paramiko.SSHClient()
            self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self.ssh.connect(hostname=self.ip, port=22, username=self.user, pkey=private_key)
            # 绑定transport到ssh对象
            stdin, stdout, stderr = self.ssh.exec_command('ls')
            if stdout.read() != '':
                print('连接成功')
                return self.ssh
            else:
                self.linuxConnect()
        except:
            print('连接失败')
            return 'fail'

    def linuxSend(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        if re.search('docker exec', info):
            return
        result = stdout.read().decode()
        return result

    def linuxReceive(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.readlines()
        return result

    # 关闭连接
    def linuxClose(self):
        self.ssh.close()


linux = Linux({"ip": "10.20.70.11", "user": "root", "id_rsa": r"C:Usersyzt.sshid_rsa"})
linux.linuxConnect()
rev = linux.linuxReceive('LANG=en_US.UTF-8;sar -u 1 1')
print(rev)
v_CPU = 100 - float(re.findall('(S+)', rev[-1])[-1])
print(v_CPU)

方式二:
import paramiko, re, time
class Linux(object):
    def __init__(self, info):
        self.ip = info['ip']
        self.pub_key_file = info["id_rsa"]
        self.user = info["user"]

    def linuxConnect(self):
        # 实例化一个transport对象
        try:
            private_key = paramiko.RSAKey.from_private_key_file(self.pub_key_file)
            self.transport = paramiko.Transport((self.ip, 22))
            self.transport.connect(username=self.user, pkey=private_key)
            self.ssh = paramiko.SSHClient()
            # self.ssh._transport = self.transport
            stdin, stdout, stderr = self.ssh.exec_command('ls')
            if stdout.read() != '':
                print('连接成功')
                return self.ssh
            else:
                self.linuxConnect()
        except:
            print('连接失败')
            return 'fail'

    def linuxSend(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        if re.search('docker exec', info):
            return
        result = stdout.read().decode()
        return result

    def linuxReceive(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.readlines()
        return result

    def upload(self, local_path, target_path):
        # 连接,上传
        # file_name = self.create_file()
        sftp = paramiko.SFTPClient.from_transport(self.transport)
        # 将location.py 上传至服务器 /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 linuxClose(self):
        self.transport.close()


linux = Linux({"ip": "10.20.70.11", "user": "root", "id_rsa": r"C:Usersyzt.sshid_rsa"})
linux.linuxConnect()
# rev = linux.linuxReceive('LANG=en_US.UTF-8;sar -u 1 1')
# print(rev)
# v_CPU = 100 - float(re.findall('(S+)', rev[-1])[-1])
# print(v_CPU)
linux.upload(r"C:UsersyztDesktopworkInfosecTestPlatform
equirements.txt", "/tmp/requirements.txt")

2.关于paramiko上传文件速率问题:

这是paramiko我们操作定义好的类,我直接拿过来了

import paramiko, re, os, time
from PublicMethod.config import BASE_PATH
from InfosecTestPlatform.settings import BASE_DIR
class Linux(object):
    def __init__(self, ip,user='XXX',passw='XXXXX'):
        self.ip = ip
        self.user = user
        self.passw = passw
        self.num = 6
    def SSHConnect(self):
        try:
            self.transport = paramiko.Transport((self.ip, 22))
            # 建立连接
            self.transport.connect(username=self.user, password=self.passw)
            # 建立ssh对象
            self.ssh = paramiko.SSHClient()
            # 绑定transport到ssh对象
            self.ssh._transport = self.transport
            stdin, stdout, stderr = self.ssh.exec_command('ls')
            if stdout.read() != '':
                print('连接成功')
                return self.ssh
            else:
                if self.num > 0:
                    self.num -= 1
                    time.sleep(2)
                    self.SSHConnect()
        except:
            print('连接失败')
            return False


    # SFTP 连接 上传下载文件
    def SFTPConnect(self):
        try:
            # 实例化transport对象,并建立连接
            self.transport = paramiko.Transport((self.ip, 22))
            self.transport.connect(username=self.user, password=self.passw)
            # 实例化sftp对象,指定连接对象
            self.sftp = paramiko.SFTPClient.from_transport(self.transport)
            return True
        except:
            if self.num > 0:
                print('SFTP第 %s 次连接失败'% (4-self.num))
                self.num -= 1
                time.sleep(2)
                self.SFTPConnect()
            else:
                return False

    # 发送指令
    def LinuxSend(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.read().decode()
        return result

    # 发送指令 按行返回信息
    def LinuxReceive(self, info):
        stdin, stdout, stderr = self.ssh.exec_command(info)
        result = stdout.readlines()
        return result

    # 上传文件
    def LinuxPut(self, path, info):
        name = path.split('/')[-1]
        docker_path = os.path.join(info, name)
        self.sftp.put(localpath=path, remotepath=docker_path)

    # 下载文件 linux中 只能从/tmp 下载
    def LinuxGet(self, remotepath,local):
        local_path = os.path.join(BASE_PATH, local)
        remote_path='/tmp/%s'%remotepath
        self.sftp.get(remotepath=remote_path, localpath=local_path)

    # 关闭连接
    def LinuxClose(self):
        self.transport.close()

3.参数文件源码分析:

    # SFTP 连接 上传下载文件
    def SFTPConnect(self):
        try:
            # 实例化transport对象,并建立连接
            self.transport = paramiko.Transport((self.ip, 22))
            self.transport.connect(username=self.user, password=self.passw)
            # 实例化sftp对象,指定连接对象
            self.sftp = paramiko.SFTPClient.from_transport(self.transport)
            return True
        except:
            if self.num > 0:
                print('SFTP第 %s 次连接失败'% (4-self.num))
                self.num -= 1
                time.sleep(2)
                self.SFTPConnect()
            else:
                return False
                    
1.首先传输文件需要用这个方式去给self.sftp 赋值,但是你首先得创建一个通道self.transport,这个就是读取文件的船.
Transport:源码
 def __init__(
        self,
        sock,
        default_window_size=DEFAULT_WINDOW_SIZE,   # 默认的window读取文件size
        default_max_packet_size=DEFAULT_MAX_PACKET_SIZE, # 这个是传输的包的大小
        gss_kex=False,
        gss_deleg_creds=True,
        disabled_algorithms=None,
    )
由于self.sftp = paramiko.SFTPClient.from_transport(self.transport)将self.transport这个对象放进去了,我们直接去看传输文件时put方法
    
 2.put方法源码    
    def put(self, localpath, remotepath, callback=None, confirm=True):
        file_size = os.stat(localpath).st_size
        with open(localpath, "rb") as fl:
            return self.putfo(fl, remotepath, file_size, callback, confirm)
 3.putfo方法源码
    def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
        with self.file(remotepath, "wb") as fr:
            fr.set_pipelined(True)
            size = self._transfer_with_callback(
                reader=fl, writer=fr, file_size=file_size, callback=callback
            )
        if confirm:
            s = self.stat(remotepath)
            if s.st_size != size:
                raise IOError(
                    "size mismatch in put!  {} != {}".format(s.st_size, size)
                )
        else:
            s = SFTPAttributes()
        return s 
                
 4.继续看_transfer_with_callback
     这个方法才是真正获取数据的地方,注释部分是原来的代码,我加了上面的四行if判断部分代码,最大可以改为2M,我从网上看的博客说的.但是我发现改了之后效果并不是多好
     def _transfer_with_callback(self, reader, writer, file_size, callback):
        size = 0
        while True:
            if file_size > 10485760:
                read_size = 1048576
            else:
                read_size = 32768
            # data = reader.read(32768)
            data = reader.read(read_size)
            writer.write(data)
            size += len(data)
            if len(data) == 0:
                break
            if callback is not None:
                callback(size, file_size)
        return size
                    
5.默认参数,说实话下面的默认参数不知道啥意思
DEFAULT_WINDOW_SIZE = 64 * 2 ** 15   2M
DEFAULT_MAX_PACKET_SIZE = 2 ** 15    32K

# lower bound on the max packet size we'll accept from the remote host
# Minimum packet size is 32768 bytes according to
# http://www.ietf.org/rfc/rfc4254.txt
MIN_WINDOW_SIZE = 2 ** 15

# However, according to http://www.ietf.org/rfc/rfc4253.txt it is perfectly
# legal to accept a size much smaller, as OpenSSH client does as size 16384.
MIN_PACKET_SIZE = 2 ** 12

# Max windows size according to http://www.ietf.org/rfc/rfc4254.txt
MAX_WINDOW_SIZE = 2 ** 32 - 1    
    
参考:https://www.cnblogs.com/shengulong/p/9018968.html

6.第四步调了每次读取字节的大小,下面在改一个地方吧,在self.transport赋值的时候加上后面两个参数可能有用,反正我的没报错.
    def SFTPConnect(self):
        try:
            # 实例化transport对象,并建立连接
            self.transport = paramiko.Transport((self.ip, 22), 2 * 1024 * 1024, 1024 * 1024)
            self.transport.connect(username=self.user, password=self.passw)
            # 实例化sftp对象,指定连接对象
            self.sftp = paramiko.SFTPClient.from_transport(self.transport)
            return True
        except:
            if self.num > 0:
                print('SFTP第 %s 次连接失败'% (4-self.num))
                self.num -= 1
                time.sleep(2)
                self.SFTPConnect()
            else:
                return False
原文地址:https://www.cnblogs.com/liuzhanghao/p/12974559.html