Python之paramiko模块

一、paramiko介绍

1、 用于帮助开发者通过代码远程连接服务器,并对服务器进行操作。

pip3 install paramiko

二、通过用户名密码方式远程执行命令

1、用户名密码

import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()

# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接服务器
ssh.connect(hostname='192.168.200.132', port=22, username='root', password='5740##')

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df -Th')

# 获取命令结果
result = stdout.read()

# 关闭连接
ssh.close()

print(result.decode('utf-8'))

补充:批量执行命令

import paramiko
import sys

def Ssh_exec_linux(ip, port, usernamme, password):
    ssh = paramiko.SSHClient() 
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh.connect(hostname=ip, port=port, username=usernamme, password=password)
    except Exception as e:
        print("服务器%s连接失败!" % ip)
        print(e)
        sys.exit(1)

    stdin, stdout, stderr = ssh.exec_command('df -Th')
    print("服务器%s的磁盘使用率如下:" % ip)
    print(stdout.read().decode('utf-8'))  # stdout.read()的结果是bytes,需要解码一下
    ssh.close()


if __name__ == '__main__':
    servers = {
        '192.168.200.133': {
            'port': '22',
            'username': 'root',
            'password': '5740##'
        },
        '192.168.200.134': {
            'port': '22',
            'username': 'root',
            'password': '5740##'
        },
        '192.168.200.135': {
            'port': '22',
            'username': 'root',
            'password': '5740##'
        },
    }
    for ip, info in servers.items():
        Ssh_exec_linux(ip,
                       info.get('port'),
                       info.get('username'),
                       info.get('password'))

三、通过用户名密码方式上传下载文件

1、上传下载文件

import paramiko

def sshFile():
    try:
        ssh_conn = paramiko.Transport(('192.168.200.133', 22))
        ssh_conn.connect(username='root', password='5740##')

        # 连接sftp客户端
        ftp_client = ssh_conn.open_sftp_client()

        # 下载
        source_path = "/root/calico.yaml"
        destination_path = "/Users/sanpangdan/Desktop/zjz_BBS/calico.yaml"  # 指定完整的目标路径,包括文件名
        ftp_client.get(source_path, destination_path)

        # 上传
        source_path = "/Users/sanpangdan/Desktop/zjz_BBS/Thumbs.db"
        destination_path = "/root/Thumbs.db"  # 指定完整的目标路径,包括文件名
        ftp_client.put(source_path, destination_path)

    except Exception as e:
        print(f"发生错误:{str(e)}")

    finally:
        if 'ftp_client' in locals():
            ftp_client.close()
        if 'ssh_conn' in locals():
            ssh_conn.close()

if __name__ == '__main__':
    sshFile()

2、通过用户名批量上传文件

import paramiko
import os

def sshPutFile(ip, port, username, password, localfile, remotedir):
    try:
        # 创建SSH连接
        ssh_conn = paramiko.Transport((ip, port))
        ssh_conn.connect(username=username, password=password)

        # 连接SFTP客户端
        ftp_client = ssh_conn.open_sftp_client()

        remotedir_file = os.path.join(remotedir, localfile)
        # 上传本地文件到远程服务器
        ftp_client.put(localfile, remotedir_file)

        ssh_conn.close()

    except Exception as e:
        print(f"上传文件到 {ip} 时发生错误: {str(e)}")


if __name__ == '__main__':
    servers = {
        '192.168.200.133': {
            'port': 22,
            'username': 'root',
            'password': '5740##',
            'localfile': 'pi_put.py',
            'remotedir': '/root/'
        },
        '192.168.200.134': {
            'port': 22,
            'username': 'root',
            'password': '5740##',
            'localfile': 'pi_put.py',
            'remotedir': '/root/'
        },
        '192.168.200.135': {
            'port': 22,
            'username': 'root',
            'password': '5740##',
            'localfile': 'pi_put.py',
            'remotedir': '/root/'
        },
    }

    for ip, info in servers.items():
        sshPutFile(
            ip=ip,
            port=info.get('port'),
            username=info.get('username'),
            password=info.get('password'),
            localfile=info.get('localfile'),
            remotedir=info.get('remotedir'))

四、通过公钥私钥远程执行命令

1、代码

import paramiko

private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa')

# 创建SSH对象
ssh = paramiko.SSHClient()

# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接服务器
ssh.connect(hostname='192.168.200.132', port=22, username='root', pkey=private_key)

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df -Th')

# 获取命令结果
result = stdout.read()

# 关闭连接
ssh.close()

print(result)  

注:

在 Paramiko 中,pkey 选项用于指定用于身份验证的私钥。具体来说,这个选项允许你提供一个 paramiko.RSAKeyparamiko.DSSKey 对象,这些对象表示了你的私钥文件

如果你使用 RSA 密钥作为私钥,你可以按照以下方式设置 pkey 选项:

private_key = paramiko.RSAKey.from_private_key_file('/path/to/private/key')

如果你使用 DSA 密钥,可以这样设置:

private_key = paramiko.DSSKey.from_private_key_file('/path/to/private/key')

在连接服务器时,你可以将 private_key 对象传递给 pkey 参数,

五、通过公钥私钥远程上传下载文件

1、sftp.put

import paramiko

private_key = paramiko.RSAKey.from_private_key_file(r'/root/.ssh/id_rsa')

transport = paramiko.Transport(('192.168.200.132', 22))

transport.connect(username='root', pkey=private_key)

sftp = paramiko.SFTPClient.from_transport(transport)

# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/data/123.py', '/root/123.py')

# 将remove_path 下载到本地 local_path
# sftp.get('123.py', '123.py')

transport.close()

六、通过私钥字符串远程连接服务器

# 也可以是存在于数据库中
key = """-----BEGIN RSA PRIVATE KEY-----

-----END RSA PRIVATE KEY-----"""


import paramiko
from io import StringIO

private_key = paramiko.RSAKey(file_obj=StringIO(key))

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='192.168.16.85', port=22, username='root', pkey=private_key)

# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()

# 关闭连接
ssh.close()

print(result)

七、实际案例 

1、代码

def query_mysql_error_log_count(host_ip):
    """通过ssh远程到主机,在MySQL容器中执行过滤mysql.err的Slave SQL错误日志,返回错误日志行数"""
    datetime.datetime.strftime((datetime.datetime.now() + datetime.timedelta(-0)), '%Y-%m-%d')
    date_list = [datetime.datetime.strftime((datetime.datetime.now() + datetime.timedelta(-i)),
                                            '%Y-%m-%d') for i in range(2)]
    date_list_str = r'\|'.join(date_list)
    try:
        with paramiko.SSHClient() as ssh:
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            ssh.connect(hostname=host_ip, port=port, username=username, password=password)
            cmd_str = "docker ps --format='{{.Names}}' | egrep 'mysql.*([standby|slave|master])'"
            _, stdout, stderr = ssh.exec_command(cmd_str)
            out_lines, err_lines = stdout.readlines(), "".join(stderr.readlines())
            if len(out_lines) < 1:
                print('Can\'t find mysql container by name regrex mysql.*([standby|slave|master])')
                return -1
            container_name = out_lines[0].strip()
            cmd_str = f"docker exec -i {container_name} tail -10000 /var/log/mysql/mysql.err" \
                      f" | grep -e '{date_list_str}' | egrep 'Slave SQL for.*Error_code:' | wc -l"
            _, stdout, stderr = ssh.exec_command(cmd_str)
            out_line, err_lines = stdout.readline(), "".join(stderr.readlines())
            number_re = re.compile(r'[0-9]{1,}')

            if number_re.match(out_line):
                return int(out_line)
            elif len(err_lines) > 1:
                print(f'Can\'t get mysql err info, err:{err_lines}')
            else:
                print(f'Error output:{out_lines}')
    except Exception as e:
        print(f'SSH login host:{host_ip} error:{e}')
    return -1

2、 使用 with 上下文管理器(如 with paramiko.SSHClient() as ssh:)的优点包括:

  • 自动关闭连接:在执行完所有 SSH 命令后,即使发生异常,SSHClient 实例也会自动关闭。这防止了未关闭的连接堆积,这些未关闭的连接可能会导致端口耗尽或远程服务器上的资源泄露。

  • 代码清晰性:使用 with 语句使得代码更加清晰易读,因为读者不需要在代码中寻找 SSHClient 实例的关闭调用。

  • 错误处理:如果在执行 SSH 命令时发生异常,上下文管理器会保证在退出 with 块之前执行清理工作。这简化了错误处理,因为你只需要处理异常情况,而不需要担心资源的清理。

 

 

 

 

 

Python之SSH-paramiko模块的使用 - 刘清政 - 博客园 (cnblogs.com)

posted @ 2023-08-29 10:31  凡人半睁眼  阅读(181)  评论(0编辑  收藏  举报