Files
Hyper-V_Web_Manager/python/app.py
elmgates 70e042f73a 上传文件至 python
python源码位于此文件夹
2024-10-08 19:14:44 +08:00

186 lines
6.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import tkinter as tk
from tkinter import scrolledtext
import logging
import pystray
from PIL import Image, ImageDraw
from pystray import MenuItem as item
import threading
import subprocess
import json
from flask import Flask, render_template, request, jsonify
app = Flask(__name__)
# 配置日志记录
logging.basicConfig(
filename='app.log',
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# 托盘应用类
class TrayApp:
def __init__(self, root):
self.root = root
self.root.title("虚拟机管理控制面板")
self.root.geometry("300x200")
# 左侧控制面板
control_frame = tk.Frame(self.root, width=300, bg="lightgray")
control_frame.pack(side="left", fill="y")
# 最小化按钮
minimize_button = tk.Button(control_frame, text="最小化到托盘", command=self.hide_window)
minimize_button.pack(pady=20)
# 启动 Flask 服务器的按钮
start_server_button = tk.Button(control_frame, text="启动 Flask 服务器", command=self.start_flask_server)
start_server_button.pack(pady=10)
# 后台线程运行托盘图标
self.tray_thread = threading.Thread(target=self.create_tray_icon, daemon=True)
self.tray_thread.start()
def hide_window(self):
"""隐藏窗口到托盘"""
self.root.withdraw() # 隐藏主窗口
def show_window(self, icon, item):
"""从托盘显示窗口"""
self.root.deiconify() # 显示主窗口
def exit_app(self, icon, item):
"""退出应用程序"""
icon.stop() # 停止托盘图标
self.root.quit() # 退出Tkinter主循环
self.root.destroy() # 销毁窗口
def create_tray_icon(self):
"""创建系统托盘图标"""
image = self.create_image()
menu = (item('显示', self.show_window), item('退出', self.exit_app))
tray_icon = pystray.Icon("VM Manager", image, "虚拟机管理", menu)
tray_icon.run()
def create_image(self, width=64, height=64, color1="black", color2="white"):
"""生成托盘图标"""
image = Image.new("RGB", (width, height), color1)
dc = ImageDraw.Draw(image)
dc.rectangle((width // 2, 0, width, height // 2), fill=color2)
dc.rectangle((0, height // 2, width // 2, height), fill=color2)
return image
def start_flask_server(self):
"""启动 Flask 服务器"""
flask_thread = threading.Thread(target=self.run_flask, daemon=True)
flask_thread.start()
def run_flask(self):
"""在后台运行 Flask 服务器"""
app.run(host='0.0.0.0', port=5000)
def log_message(self, message):
"""更新日志到GUI中"""
self.log_box.config(state="normal")
self.log_box.insert(tk.END, message + '\n')
self.log_box.yview(tk.END)
self.log_box.config(state="disabled")
# Hyper-V 相关操作
def run_powershell(command):
"""运行 PowerShell 命令并返回输出"""
try:
result = subprocess.run(
["powershell", "-Command", command],
capture_output=True, text=True, shell=True
)
# 返回标准输出和错误输出合并的结果
print(result.stdout.strip() + "\n" + result.stderr.strip())
return result.stdout.strip() + "\n" + result.stderr.strip()
except Exception as e:
print(f"运行 PowerShell 命令时出错: {e}")
return ""
def get_vms():
"""
获取所有Hyper-V虚拟机及其状态
"""
try:
command = "Get-VM | Select-Object Name, State | ConvertTo-Json"
output = run_powershell(command)
# 找到JSON开始的部分第一个 '[' 出现的位置)
json_start = output.find('[')
if json_start != -1:
json_data = output[json_start:] # 从 '[' 开始提取
vms = json.loads(json_data)
else:
raise ValueError("未找到有效的JSON数据")
# 如果只有一个VM转换为列表
if isinstance(vms, dict):
vms = [vms]
return vms
except subprocess.CalledProcessError as e:
print("Error fetching VMs:", e)
return []
except json.JSONDecodeError as e:
print("JSON 解析错误:", e)
print("原始输出:", output)
return []
@app.route('/')
def index():
"""显示虚拟机状态的网页"""
vms = get_vms()
return render_template('index.html', vms=vms)
@app.route('/control_vm', methods=['POST'])
def control_vm():
"""控制虚拟机的操作(启动、停止等)"""
try:
vm_name = request.json['name']
action = request.json['action']
if action == 'start':
command = f"Start-VM -Name '{vm_name}'"
elif action == 'stop':
command = f"Stop-VM -Name '{vm_name}' -Force"
else:
return jsonify({'status': 'error', 'message': '无效操作'})
output = run_powershell(command)
print(output)
if "虚拟机已处于指定的状态" not in output :
result = "操作成功"
else:
result = "操作失败"
logging.info(f"虚拟机 {vm_name} 执行操作: {result}")
return jsonify({'status': 'success', 'message': result})
except Exception as e:
logging.error(f"控制虚拟机时出错: {e}")
return jsonify({'status': 'error', 'message': '操作失败'})
# 启动 Flask 和 GUI 的主程序
def gui_thread():
root = tk.Tk()
app = TrayApp(root)
# 自动最小化到托盘
app.hide_window() # 启动时隐藏窗口
# 在后台启动 Flask 服务器
flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start()
root.mainloop()
def run_flask():
"""在后台运行 Flask 服务器"""
app.run(host='0.0.0.0', port=5050)
if __name__ == "__main__":
gui_thread()