文章目录
一、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