添加了删除快照的功能

This commit is contained in:
panjunlan
2026-02-20 21:36:58 +08:00
parent 7a0a51be4e
commit 9024e9c8e4
6 changed files with 213 additions and 31 deletions

View File

@@ -1,16 +1,137 @@
from getVmsSnapshots import collect_snapshot_data
import yaml
import os, time
from pyVmomi import vim
from pyVim.connect import SmartConnect, Disconnect
from config.settings import MANAGEMENT_NODES,YAML_OUTPUT_PATH
from utils.logger import logger
def delete_old_snapshots(dele_snapshots):
"""删除旧快照"""
def connect_vcenter(host):
"""根据 NodeHost 连接对应 vCenter"""
for node in MANAGEMENT_NODES:
if node['host'] == host:
return SmartConnect(
host=node['host'],
user=node['user'],
pwd=node['password'],
disableSslCertValidation=True
)
raise Exception(f"未找到 {host} 的连接信息")
def main(dele_snapshots):
"""根据 YAML 信息删除旧快照"""
# print(dele_snapshots)
if not dele_snapshots:
logger.info("没有需要删除的快照")
return
# 按 NodeHost 分组,避免重复连接
grouped = {}
for snapshot in dele_snapshots:
if snapshot['is_old']:
# 在这里执行删除操作,例如调用 API 或者其他逻辑
print(f"Deleting old snapshot: {snapshot['Snapshot Name']} (ID: {snapshot['ID']})")
# 示例:假设存在一个 delete_snapshot 函数
# delete_snapshot(snapshot['ID'])
grouped.setdefault(snapshot['NodeHost'], []).append(snapshot)
# 删除旧快照
print(collect_snapshot_data)
# delete_old_snapshots(dele_snapshots)
for host, snapshots in grouped.items():
logger.info(f"连接到 {host} 删除快照...")
si = connect_vcenter(host)
try:
for snapshot in snapshots:
delete_snapshot(si, snapshot) # 调用删除快照函数
finally:
Disconnect(si)
# 执行快照删除的(核心)函数
def delete_snapshot(si, snapshot_info):
"""执行快照删除"""
content = si.RetrieveContent()
snap_name = f"{snapshot_info['VMName']}-{snapshot_info['Snapshot Name']}-({snapshot_info['MOID']})"
vm = find_vm_by_name(content, snapshot_info['VMName']) # VMName即根据获取到的虚拟机名称查找虚拟机是否存放
if not vm:
logger.info(f"未找到 VM: {snapshot_info['VMName']}")
return
# 检查该虚拟机是否有快照
if not vm.snapshot:
logger.warning(snap_name,":快照不存在")
return
# 虚拟机的快照列表中找到具有指定 MOID 的快照对象
snapshot_obj = find_snapshot_by_moid(
vm.snapshot.rootSnapshotList,
snapshot_info['MOID']
)
if not snapshot_obj:
logger.warning(snap_name,":未找到")
return
logger.info(f"正在删除 Snapshot: {snap_name}")
try:
"""删除快照核心代码,调用快照对象的 RemoveSnapshot_Task 方法执行。removeChildren = False表示删除该快照时不删除其子快照。"""
task = snapshot_obj.RemoveSnapshot_Task(removeChildren=False)
# 改进的等待逻辑
while task.info.state in [vim.TaskInfo.State.running, vim.TaskInfo.State.queued]:
time.sleep(1) # 避免 CPU 空转,每秒检查一次
# 更完整的状态判断
if task.info.state == vim.TaskInfo.State.success:
logger.info(f"✅ 删除成功: {snap_name}")
elif task.info.state == vim.TaskInfo.State.error:
error_msg = task.info.error.msg if task.info.error else "未知错误"
logger.error(f"❌ 删除失败: {snap_name}, 错误: {error_msg}")
else:
# 处理其他状态(如 cancelled
logger.warning(f"⚠️ 任务未成功完成,状态: {task.info.state}")
except Exception as e:
logger.exception(f"删除快照时发生异常: {snap_name}")
raise
def find_vm_by_name(content, vm_name):
"""根据 VM 名称查找虚拟机对象"""
container = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True
)
for vm in container.view:
if vm.name == vm_name:
container.Destroy()
return vm
container.Destroy()
return None
def find_snapshot_by_moid(snapshot_tree, moid):
"""递归查找 snapshot 对象"""
for snapshot in snapshot_tree:
if snapshot.snapshot._moId == moid:
return snapshot.snapshot
if snapshot.childSnapshotList:
result = find_snapshot_by_moid(snapshot.childSnapshotList, moid)
if result:
return result
return None
def load_old_snapshots(file_path):
"""从 YAML 文件中加载旧快照"""
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as yaml_file:
return yaml.safe_load(yaml_file)
else:
logger.error(f"{file_path} 文件不存在.")
return []
if __name__ == "__main__":
old_snapshots = load_old_snapshots(YAML_OUTPUT_PATH)
main(old_snapshots)

View File

@@ -1,10 +1,11 @@
import pandas as pd
import yaml
from pyVmomi import vim
from datetime import datetime, timedelta
from pyVim.connect import SmartConnect, Disconnect
from openpyxl.styles import Border, Side, Font, PatternFill
from utils.logger import logger
from config.settings import MANAGEMENT_NODES, SNAPSHOT_RETENTION_DAYS, EXCEL_OUTPUT_PATH
from config.settings import MANAGEMENT_NODES, SNAPSHOT_RETENTION_DAYS, EXCEL_OUTPUT_PATH, YAML_OUTPUT_PATH
def get_all_vms():
@@ -30,10 +31,8 @@ def get_all_vms():
vm_list.append(vm_info)
except vim.fault.InvalidLogin as e:
print(f"登录 {node['host']} 失败,请检查用户名和密码:{e.msg}")
logger.info(f"登录 {node['host']} 失败,请检查用户名和密码:{e.msg}")
except Exception as e:
print(f"无法连接到 {node['host']}{e}")
logger.error(f"无法连接到 {node['host']}{e}")
finally:
if 'si' in locals(): # 确保 si 是定义过的
@@ -146,12 +145,12 @@ def get_virtual_disk_size(vm):
return total_size
def collect_snapshot_data(snapshot, vm, snapshot_data):
"""递归扁平化快照数据用于Excel"""
def collect_snapshot_data(snapshot, vm, snapshot_data, old_snapshots):
"""递归扁平化快照数据用于Excel,并收集旧快照"""
create_time = datetime.strptime(snapshot['createTime'], '%Y-%m-%d %H:%M:%S')
is_old = create_time < (datetime.now() - timedelta(days=SNAPSHOT_RETENTION_DAYS))
snapshot_data.append({
snapshot_info = {
'NodeHost': vm['NodeHost'],
'VMName': vm['name'],
'Snapshot Name': snapshot['name'],
@@ -162,16 +161,23 @@ def collect_snapshot_data(snapshot, vm, snapshot_data):
'MOID': snapshot['moId'],
'Quiesced': snapshot['quiesced'],
'is_old': is_old
})
}
# 如果是旧快照,添加到旧快照列表
if is_old:
old_snapshots.append(snapshot_info)
snapshot_data.append(snapshot_info)
for child in snapshot['children']:
collect_snapshot_data(child, vm, snapshot_data)
collect_snapshot_data(child, vm, snapshot_data, old_snapshots)
# 输出数据到 Excel 文件
def create_excel_report(vms):
vm_data = []
snapshot_data = []
old_snapshots = [] # 用于存储旧快照的信息
for vm in vms:
vm_data.append({
@@ -192,7 +198,7 @@ def create_excel_report(vms):
if vm['snapshots']:
for snapshot in vm['snapshots']:
collect_snapshot_data(snapshot, vm, snapshot_data)
collect_snapshot_data(snapshot, vm, snapshot_data, old_snapshots)
vm_df = pd.DataFrame(vm_data)
snapshot_df = pd.DataFrame(snapshot_data)
@@ -203,13 +209,30 @@ def create_excel_report(vms):
workbook = writer.book
style_sheet(workbook['VMs'])
style_sheet(workbook['Snapshots'], snapshot_df['is_old'].tolist())
# style_sheet(workbook['Snapshots'], snapshot_df['is_old'].tolist())
# 判断 DataFrame 是否有数据,没有数据则跳过,有数据则写入 excel 文件
if 'is_old' in snapshot_df.columns and snapshot_df['is_old'].tolist():
style_sheet(workbook['Snapshots'], snapshot_df['is_old'].tolist())
print("报告已生成:", EXCEL_OUTPUT_PATH)
logger.debug(f"Excel 文件已生成: {EXCEL_OUTPUT_PATH}")
# 返回旧快照的数据
return old_snapshots
# 输出待删除的旧快照到 YAML 文件
def export_yaml(old_snapshots):
print(old_snapshots)
# 将旧快照信息存储到 YAML 文件
with open(YAML_OUTPUT_PATH, 'w', encoding='utf-8') as yaml_file:
# allow_unicode输出 Unicode 字符中文等allow_unicode使用块样式多行缩进sort_keys不按键名排序保留原始插入顺序
yaml.dump(old_snapshots, yaml_file, allow_unicode=True, default_flow_style=False, sort_keys=False)
logger.debug(f"YAML 文件已生成: {YAML_OUTPUT_PATH}")
if __name__ == '__main__':
vms = get_all_vms() # 主函数入口,获取虚拟机信息
# print(vms)
create_excel_report(vms) # 生成Excel报告
old_snapshots = create_excel_report(vms) # 生成Excel报告并获取旧快照
export_yaml(old_snapshots)
# print(old_snapshots)