前言
本项目开发一个综合性的ssh远程服务器管理工具,用户可添加多台服务器信息,可通过新建、删除、修改来管理服务器信息。具有监控服务器的cpu、内存、进程的功能。可以执行交互性指令,通过vim工具来实现对于文件的编辑。实现了对于文件的上传和下载功能,使用户轻松的管理文件,并提供了批量下载日志文件的功能,以实现对于日志的批量收集,更好的掌握服务器的日志信息。
项目概述
本项目旨在创建一个用户友好的SSH客户端,它集成了服务器连接、命令执行、文件传输等功能。通过图形界面,用户可以轻松地管理多个服务器,执行远程命令,以及上传和下载文件。(注意:本项目使用vim需要先touch创建一个文件,vim一个文件时会自动打开本地的默认文本编辑器,保存会自动 同步内容到Linux中)
项目展示
使用的技术
Python:版本为3.11。
paramiko:用于SSH连接和SFTP文件传输的Python库。
tkinter:Python的标准GUI库,用于构建用户界面。
功能特点
服务器列表管理:支持服务器信息的增删改查。
SSH连接与命令执行:通过图形界面连接服务器并执行命令。
文件传输:实现文件的上传和下载。
实时监控:实时显示服务器的CPU、内存、磁盘和网络状态。
代码
MySSH 类
MySSH 类负责管理SSH连接和执行相关操作。
# -*- ecoding: utf-8 -*-
# @ModuleName: project
# @Function:
# @Author: liweijia
# @Time: 2024/5/22 15:21
# #!/usr/bin/python
import os
import re
import paramiko
import tkinter as tk
from tkinter import messagebox
from tkinter import filedialog
import ttkbootstrap as ttk
import json
class MySSH:
def __init__(self):
self.servers = [] # 读取server.json。 选择/添加/删除
self.server = {
}
# 服务器是否连接
self.connected = False
# 用来保存当前路径
self.path = ""
# 实例化并且建立一个sshclient对象
self.ssh = paramiko.SSHClient()
# 将信任的主机自动加入到host_allow列表,需放在connect方法前面
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
self.cpu_percent = 0.0
self.memory_percent = 0.0
self.file_name = None
self.processes = []
self.load_server()
# 此线程用于vim文件编辑功能
t = threading.Thread(target=self.file_save)
t.start()
def load_server(self):
# 打开文件
with open("server.json", encoding='utf-8') as fp:
self.servers = json.load(fp)
def connect(self):
# 调用connect方法连接服务器
self.connected = None
try:
# 通过用户名和密码进行连接
self.ssh.connect(hostname=self.server["ip"], port=self.server["port"], username=self.server["username"],
password=self.server["password"])
print(f'连接到服务器{
self.server["ip"]}成功!!!')
self.connected = True
# 创建一个交互式的shell窗口
self.chan = self.ssh.invoke_shell()
self.chan.settimeout(1000)
# 刚进入linux服务器等待一会,否则直接通过chan.recv获取的信息不完整
time.sleep(0.6)
self.endSymbol = ['# ', '$ ', '> ', '* '] # 设置我们定义的结束符
except Exception as e:
self.connected = False
print(f"连接到服务器【{
self.server['ip']}】失败,失败的原因:{
e}")
def transport(self, server_path, local_path, num):
'''
文件上传 如果传入num==1文件上传 num==0文件下载
server_path:服务器路径
local_path:本地路径
num:1为上传操作 0为下载操作
'''
sftp = self.ssh.open_sftp() # 初始化sftp
file_name = ''
pattern = r'[^\\\/]+(?=\.[^\\\/.]*$|$)' # 正则表达式用于将文件名称从路径里抽取出来
if num == 1:
try:
file_name = re.search(pattern, local_path).group(
0)
except Exception as e:
print(e)
print(f"{
server_path}/{
file_name}")
sftp.put(local_path, f"{
server_path}/{
file_name}")
if num == 0:
try:
file_name = re.search(pattern, server_path).group(
0)
except Exception as e:
print(e)
print(f'{
server_path}')
sftp.get(server_path, f'{
local_path}/{
file_name}')
def more_transport(self, local_dir):
sftp = self.ssh.open_sftp() # 初始化sftp
try:
# 获取服务器的日志文件
stdin, stdout, stderr = self.ssh.exec_command("ls /var/log/*.log | xargs -I {} basename '{}'")
file_list = stdout.read().decode("utf-8").strip().split('\n')
# 遍历文件列表并下载
for file_name in file_list:
remote_path = f"/var/log/{
file_name}"
local_path = f"{
local_dir}/{
file_name}"
print(f"Downloading {
remote_path} to {
local_path}")
sftp.get(remote_path, local_path)
finally:
# 关闭SFTP和SSH连接
sftp.close()
def runCommand(self, chanT, command, endSymbol):
# 指令后加 '\n' 表示换行
chanT.send(command + '\n')
results = ''
while True:
result = chanT.recv(1024).decode('utf-8')
if 'Last login:' in result: # 如果指令里包含Last...字段就再发送一次请求
result = chanT.recv(1024).decode('utf-8')
results += result
# 判断最后两个字符是否是我们定义的结束符
if results[-2:] in endSymbol:
break
re1 = results.split('\n')[1:] # 第一行是我们输入的指令,丢弃
re1 = '\n'.join(re1)
clean_string = re.sub(r'\x1b\[[0-?]*[ -/]*[@-~]', '', re1) # 由于我们的GUI只能展示纯文本信息所以选择 通过正则表达式对返回结果的ANSI进行匹配去除
return clean_string+'\n'
def vim_cat(self, command):
'''
:param command:
用户编辑文件自动下载到本地,再自动打开本地编辑
'''
self.file_name = command.split(' ')[-1]
self.chan.send('pwd'+'\n')
path = ''
while True:
# 读取一行输出
result = self.chan.recv(1024).decode('utf-8')
if 'Last login:' in result: # 如果指令里包含Last...字段就再发送一次请求
result = self.chan.recv(1024).decode('utf-8')
path += result
# 判断最后两个字符是否是我们定义的结束符
if path[-2:] in path:
break
re1 = path.split