python运维自动化Paramiko

文章目录

一、Paramiko

1、安装

bash 复制代码
pip install paramiko

2、常用的类

SSHClient: 最常用的类,表示一个SSH客户端连接。

SFTPClient: 用于SFTP传输操作。

Transport: 低级别的模块,可用来实现SSH2协议。

SSHClient主要方法

connect(hostname, port=22, username=None, password=None, pkey=None): 用于连接到远程服务器。可以选择使用用户名和密码或密钥进行认证。

exec_command(command): 在远程服务器上执行指令。

open_sftp(): 返回一个SFTPClient对象,可用于文件的上传与下载。

SFTPClient主要方法

get(remotepath, localpath, callback=None): 下载远程文件。

put(localpath, remotepath, callback=None, confirm=True): 上传本地文件。

listdir(path="."): 列出远程目录的内容。

3、使用

(1)SSH执行远程命令

py 复制代码
import paramiko

# 创建SSH客户端
client = paramiko.SSHClient()

# 自动添加未知的服务器密钥及策略
'''
AutoAddPolicy:自动添加主机名及主机密钥到本地的known_hosts,不依赖load_system_host_key的配置。即新建立ssh连接时不需要再输入yes或no进行确认。最为常用。
WarningPolicy 用于记录一个未知的主机密钥的python警告。并接受,功能上和AutoAddPolicy类似,但是会提示是新连接。
RejectPolicy 自动拒绝未知的主机名和密钥,依赖load_system_host_key的配置。此为默认选项
'''
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# 连接SSH服务端
'''
hostname(str类型),连接的目标主机地址;
port(int类型),连接目标主机的端口,默认为22;
username(str类型),校验的用户名(默认为当前的本地用户名);
password(str类型),密码用于身份校验或解锁私钥;
pkey(Pkey类型),私钥方式用于身份验证;
key_filename(str or list(str)类型),一个文件名或文件名列表,用于私钥的身份验证;
timeout(float类型),一个可选的超时时间(以秒为单位)的TCP连接;
allow_agent(bool类型),设置为False时用于禁用连接到SSH代理;
look_for_keys(bool类型),设置为False时用于来禁用在~/.ssh中搜索私钥文件;
compress(bool类型),设置为True时打开压缩。
'''
client.connect('192.168.56.10', port=22, username='root', password='vagrant')

# 执行命令
stdin, stdout, stderr = client.exec_command('ls -l')

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

print(result.decode())

# 关闭连接
client.close()

(2)SFTP上传/下载文件

py 复制代码
import paramiko

# 创建SSH客户端
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('192.168.56.10', port=22, username='root', password='vagrant')

# 创建SFTP会话
sftp = client.open_sftp()

# 上传文件
sftp.put('localfile.txt', '/remote/path/remote.txt')

# 下载文件
sftp.get('/remote/path/remote.txt', 'localfile.txt')

'''
mkdir,在SFTP服务端创建目录,如sftp.mkdir("/home/userdir",mode=0777),默认模式是0777(八进制),在某些系统上,mode被忽略。在使用它的地方,当前的umask值首先被屏蔽掉。
remove,删除SFTP服务端指定目录,如sftp.remove("/home/userdir")。
rename,重命名SFTP服务端文件或目录,如sftp.rename("/home/test.sh","/home/testfile.sh")
stat,获取远程SFTP服务端指定文件信息,如sftp.stat("/home/testfile.sh")。
listdir,获取远程SFTP服务端指定目录列表,以Python的列表(List)形式返回,如sftp.listdir("/home")。
'''


# 关闭SFTP会话和SSH连接
sftp.close()
client.close()

(3)SSH简单封装

py 复制代码
import paramiko

class SSHClient:
    def __init__(self, hostname, port=22, username=None, password=None, pkey=None, use_proxy=False, proxy_hostname=None,
                 proxy_port=None, proxy_username=None, proxy_password=None, timeout=5):
        """
        初始化SSH客户端。

        :param hostname: 主机名或IP地址
        :param port: 端口号,默认是22
        :param username: 用户名
        :param password: 密码
        :param pkey: 私钥文件对象,用于密钥认证
        :param use_proxy: 是否使用代理标志
        :param proxy_hostname: 代理的主机名称
        :param proxy_port: 代理的端口号
        :param proxy_username: 代理的用户名
        :param proxy_password: 代理的密码
        """
        self.hostname = hostname
        self.port = port
        self.username = username
        self.password = password
        self.pkey = pkey
        self.use_proxy = use_proxy
        self.proxy_hostname = proxy_hostname
        self.proxy_port = proxy_port
        self.proxy_username = proxy_username
        self.proxy_password = proxy_password
        self.client = None
        self.timeout = timeout

    def _setup_proxy(self):
        """
        配置代理。
        """
        if self.use_proxy:
            proxy_ssh = paramiko.SSHClient()
            proxy_ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            proxy_ssh.connect(hostname=self.proxy_hostname, port=self.proxy_port, username=self.proxy_username,
                              password=self.proxy_password, timeout=self.timeout)
            vm_transport = proxy_ssh.get_transport()
            remote_address = (self.hostname, self.port)
            local_address = (self.proxy_hostname, self.proxy_port)
            vm_channel = vm_transport.open_channel("direct-tcpip", remote_address, local_address)
            return vm_channel
        else:
            return None

    def _connect(self):
        """
        创建SSH连接。
        """
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        proxy = self._setup_proxy()
        self.client.connect(self.hostname, port=self.port, username=self.username, password=self.password,
                            pkey=self.pkey, sock=proxy, timeout=self.timeout)

    def exec_command(self, command):
        """
        在远程服务器上执行命令。
        :param command: 要执行的命令字符串
        :return: 命令输出结果的标准输出和标准错误
        """
        if self.client is None:
            self._connect()
        stdin, stdout, stderr = self.client.exec_command(command)
        return stdout.read().decode(), stderr.read().decode()

    def close(self):
        """
        关闭SSH连接。
        """
        if self.client:
            self.client.close()
            self.client = None


# 使用示例
if __name__ == "__main__":
    # 直接连接单个服务器
    '''
    ssh = SSHClient(hostname='192.168.56.10', port=22, username='root', password='vagrant')
    stdout, stderr = ssh.exec_command('ls -l')
    print(stdout)
    ssh.close()
    '''

    # 通过代理连接
    '''
    ssh_with_proxy = SSHClient(hostname='hostname', port=22, username='username', password='password', use_proxy=True,
                               proxy_hostname='proxy_hostname', proxy_port=22, proxy_username='proxy_username',
                               proxy_password='proxy_password')
    stdout, stderr = ssh_with_proxy.exec_command('ls -l')
    print(stdout)
    ssh_with_proxy.close()
    '''




    # 连接多个服务器执行多个命令
    servers = {
        '192.168.56.10': {
            'port': 22,
            'username': 'root',
            'password': '123'
        },
        '192.168.56.11': {
            'port': 22,
            'username': 'root',
            'password': '123'
        },
        '192.168.56.12': {
            'port': 22,
            'username': 'root',
            'password': '123'
        },
    }
    
    commands = [
        'ls -l',
        'cd xxx',
    ]

    for ip, info in servers.items():
        ssh = SSHClient(hostname=ip, port=info.get('port'), username=info.get('username'), password=info.get('password'))
        for command in commands:
            stdout, stderr = ssh.exec_command(command)
            print(stdout)
        
        ssh.close()
        
        

(4)SFTP简单封装

py 复制代码
#!/usr/bin/env python
# coding:utf-8
from stat import *
import os
import paramiko

class RemotLHost(object):
    '''封装一个远程Linux主机类,并将paramiko远程上传下载单个文件或目录的行为封装为其相应的方法'''

    # 通过IP, 用户名,密码,超时时间初始化一个远程Linux主机
    def __init__(self, ip, username, password, timeout=30):
        self.ip = ip
        self.username = username
        self.password = password
        self.timeout = timeout
        # transport
        self.t = None
        # 链接失败的重试次数
        self.conn_times = 3

    # get单个文件
    def sftp_get(self, remotefile, localfile):
        t = paramiko.Transport((self.ip, 22))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.get(remotefile, localfile)
        t.close()

    # put单个文件
    def sftp_put(self, localfile, remotefile):
        t = paramiko.Transport((self.ip, 22))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        sftp.put(localfile, remotefile)
        t.close()

    # 获取远端linux主机上指定目录及其子目录下的所有文件
    def __get_all_files_in_remote_dir(self, sftp, remote_dir):
        # 保存所有文件的列表
        all_files = list()

        # 去掉路径字符串最后的字符'/',如果有的话
        if remote_dir[-1] == '/':
            remote_dir = remote_dir[0:-1]

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = sftp.listdir_attr(remote_dir)
        for x in files:
            
            # remote_dir目录中每一个文件或目录的完整路径
            filename = remote_dir + '/' + x.filename
            print("remote file is "+ filename)
            # 如果是目录,则递归处理该目录,这里用到了stat库中的S_ISDIR方法,与linux中的宏的名字完全一致
            if S_ISDIR(x.st_mode):
                all_files.extend(
                    self.__get_all_files_in_remote_dir(sftp, filename))
            else:
                all_files.append(filename)
        return all_files

    def sftp_get_dir(self, remote_dir, local_dir):
        t = paramiko.Transport((self.ip, 22))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)

        # 获取远端linux主机上指定目录及其子目录下的所有文件
        all_files = self.__get_all_files_in_remote_dir(sftp, remote_dir)
        # 依次get每一个文件
        for x in all_files:
            filename = x.replace(remote_dir, "")
            if os.name=="nt":
                filename = filename.replace('/',os.sep).lstrip(os.sep)
            local_filename = os.path.join(local_dir, filename)
            filepath=os.sep.join(local_filename.split(os.sep)[0:-1])

            if not os.path.exists(filepath):
                os.mkdir(filepath)
           
            print(u'Get  <------- %s'% x)
            sftp.get(x, local_filename)

    #获取本地指定目录及其子目录下的所有文件
    def __get_all_files_in_local_dir(self, local_dir):
        # 保存所有文件的列表
        all_files = list()

        # 获取当前指定目录下的所有目录及文件,包含属性值
        files = os.listdir(local_dir)
        for x in files:
            # local_dir目录中每一个文件或目录的完整路径
            filename = os.path.join(local_dir, x)
            # 如果是目录,则递归处理该目录
            if os.path.isdir(filename):
                all_files.extend(self.__get_all_files_in_local_dir(filename))
            else:
                all_files.append(filename)
        return all_files

    def sftp_put_dir(self, local_dir, remote_dir):
        if remote_dir[-1] != '/':
            remote_dir = remote_dir + "/"
        t = paramiko.Transport((self.ip, 22))
        t.connect(username=self.username, password=self.password)
        sftp = paramiko.SFTPClient.from_transport(t)
        for root, dirs, files in os.walk(local_dir):
            for filespath in files:
                local_file = os.path.join(root, filespath)
                a=local_file.replace(local_dir, '')
                if os.name=="nt":
                    a = a.replace('\\','/').lstrip('/')

                remote_file = os.path.join(remote_dir, a)
                try:
                    sftp.put(local_file,remote_file)
                except Exception as e:
                    sftp.mkdir(os.path.split(remote_file)[0])
                    sftp.put(local_file,remote_file)
                print("Put %s to remote %s" % (local_file, remote_file))
            for name in dirs:
                local_path = os.path.join(root, name)
                
                a = local_path.replace(local_dir, '')
                if os.name=="nt":
                    a = a.replace('\\','/').lstrip('/')

                remote_path = os.path.join(remote_dir, a)
                try:
                    sftp.mkdir(remote_path)
                    print("mkdir path %s" % remote_path)
                except Exception as e:
                    print(e)

        t.close()

if __name__ == '__main__':


    host = RemotLHost('192.168.56.10', 'root', 'vagrant')

    remote_path = '/root/doc'
    local_path = 'D:\\test'

    # 将远端remote_path目录中的所有文件get到本地local_path目录
    host.sftp_get_dir(remote_path, local_path)
    # 将本地local_path目录中的所有文件put到远端remote_path目录
    local_path= 'D:\ltest'
    remote_path = '/root/doc2'
    host.sftp_put_dir(local_path, remote_path)

参考资料

https://blog.csdn.net/weixin_41238626/article/details/138603792

相关推荐
测开小菜鸟29 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
心灵彼岸-诗和远方1 小时前
DevOps业务价值流:架构设计最佳实践
运维·产品经理·devops
一只哒布刘1 小时前
NFS服务器
运维·服务器
萧鼎2 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸2 小时前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农2 小时前
Python 继承、多态、封装、抽象
开发语言·python
苹果醋32 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
二十雨辰2 小时前
[linux]docker基础
linux·运维·docker
Python大数据分析@2 小时前
python操作CSV和excel,如何来做?
开发语言·python·excel
黑叶白树2 小时前
简单的签到程序 python笔记
笔记·python