添加了删除快照的功能

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

@@ -9,14 +9,27 @@ Vmware 虚拟机自动化程序:自动化导出虚拟机和快照信息,自
- [x] 获取所有 snapshots - [x] 获取所有 snapshots
- [x] 筛选出15天半个月前的 snapshots - [x] 筛选出15天半个月前的 snapshots
- [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照蓝色底填充标识 - [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照蓝色底填充标识
- [ ] 增加排除不能删除的快照信息,用红色底填充标识 - [ ] 增加排除不能删除的 snapshots 信息,用红色底填充标识
- [ ] Outlook 邮箱发送超出 15 天的快照信息 - [ ] Outlook 邮箱发送统计超过 15 天的 snapshots 信息(即要删除的快照列表)
- [ ] 需要控制每台 vCenter 不可以同时删除超过 4 个快照(需要同时获取删除成功的信息) - [ ] 需要控制每台 vCenter 不可以同时删除超过 4 个快照(需要同时获取删除成功的信息)
- [ ] 最后删除 15 天前的 snapshot并同时记录删除的 snapshot 日志信息 - [x] 最后删除 15 天前的 snapshot并同时记录删除的 snapshot 日志信息
- [ ] 删除成功后发送邮件通知
- [ ] 设置计划任务,每 2 周(半个月)执行一次 - [ ] 设置计划任务,每 2 周(半个月)执行一次
✅ 多线程删除
✅ 任务进度显示
✅ 自动重试机制
✅ 企业级日志
✅ 删除前后发送邮件通知
## 输出所有可用的属性和方法 ## 输出所有可用的属性和方法
| 你想获取 | 代码 | 示例输出 | | 你想获取 | 代码 | 示例输出 |
@@ -112,7 +125,6 @@ Vmware 虚拟机自动化程序:自动化导出虚拟机和快照信息,自
``` json ``` json
['AcquireMksTicket', 'AcquireTicket', 'Answer', 'AnswerVM', 'ApplyEvcMode', 'ApplyEvcModeVM_Task', 'Array', 'AttachDisk', 'AttachDisk_Task', 'CheckCustomizationSpec', 'Clone', 'CloneVM_Task', 'ConsolidateDisks', 'ConsolidateVMDisks_Task', 'CreateScreenshot', 'CreateScreenshot_Task', 'CreateSecondary', 'CreateSecondaryEx', 'CreateSecondaryVMEx_Task', 'CreateSecondaryVM_Task', 'CreateSnapshot', 'CreateSnapshotEx', 'CreateSnapshotEx_Task', 'CreateSnapshot_Task', 'CryptoUnlock', 'CryptoUnlock_Task', 'Customize', 'CustomizeVM_Task', 'DefragmentAllDisks', 'Destroy', 'Destroy_Task', 'DetachDisk', 'DetachDisk_Task', 'DisableSecondary', 'DisableSecondaryVM_Task', 'DropConnections', 'EnableSecondary', 'EnableSecondaryVM_Task', 'EstimateStorageForConsolidateSnapshots_Task', 'EstimateStorageRequirementForConsolidate', 'ExportVm', 'ExtractOvfEnvironment', 'InstantClone', 'InstantClone_Task', 'MakePrimary', 'MakePrimaryVM_Task', 'MarkAsTemplate', 'MarkAsVirtualMachine', 'Migrate', 'MigrateVM_Task', 'MountToolsInstaller', 'PowerOff', 'PowerOffVM_Task', 'PowerOn', 'PowerOnVM_Task', 'PromoteDisks', 'PromoteDisks_Task', 'PutUsbScanCodes', 'QueryChangedDiskAreas', 'QueryConnections', 'QueryFaultToleranceCompatibility', 'QueryFaultToleranceCompatibilityEx', 'QueryUnownedFiles', 'RebootGuest', 'ReconfigVM_Task', 'Reconfigure', 'RefreshStorageInfo', 'Reload', 'ReloadFromPath', 'Relocate', 'RelocateVM_Task', 'RemoveAllSnapshots', 'RemoveAllSnapshots_Task', 'Rename', 'Rename_Task', 'Reset', 'ResetGuestInformation', 'ResetVM_Task', 'RevertToCurrentSnapshot', 'RevertToCurrentSnapshot_Task', 'SendNMI', 'SetCustomValue', 'SetDisplayTopology', 'SetScreenResolution', 'ShutdownGuest', 'StandbyGuest', 'StartRecording', 'StartRecording_Task', 'StartReplaying', 'StartReplaying_Task', 'StopRecording', 'StopRecording_Task', 'StopReplaying', 'StopReplaying_Task', 'Suspend', 'SuspendVM_Task', 'Terminate', 'TerminateFaultTolerantVM', 'TerminateFaultTolerantVM_Task', 'TerminateVM', 'TurnOffFaultTolerance', 'TurnOffFaultToleranceForVM_Task', 'UnmountToolsInstaller', 'Unregister', 'UnregisterVM', 'UpgradeTools', 'UpgradeTools_Task', 'UpgradeVM_Task', 'UpgradeVirtualHardware', '_GetMethodInfo', '_GetMethodList', '_GetMoId', '_GetPropertyInfo', '_GetPropertyList', '_GetServerGuid', '_GetStub', '_InvokeAccessor', '_InvokeMethod', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__static_attributes__', '__str__', '__subclasshook__', '__weakref__', '_methodInfo', '_moId', '_propInfo', '_propList', '_serverGuid', '_stub', '_version', '_wsdlName', 'alarmActionsEnabled', 'availableField', 'capability', 'config', 'configIssue', 'configStatus', 'customValue', 'datastore', 'declaredAlarmState', 'disabledMethod', 'effectiveRole', 'environmentBrowser', 'guest', 'guestHeartbeatStatus', 'layout', 'layoutEx', 'name', 'network', 'overallStatus', 'parent', 'parentVApp', 'permission', 'recentTask', 'reloadVirtualMachineFromPath_Task', 'resourceConfig', 'resourcePool', 'rootSnapshot', 'runtime', 'setCustomValue', 'snapshot', 'storage', 'summary', 'tag', 'triggeredAlarmState', 'value'] ['AcquireMksTicket', 'AcquireTicket', 'Answer', 'AnswerVM', 'ApplyEvcMode', 'ApplyEvcModeVM_Task', 'Array', 'AttachDisk', 'AttachDisk_Task', 'CheckCustomizationSpec', 'Clone', 'CloneVM_Task', 'ConsolidateDisks', 'ConsolidateVMDisks_Task', 'CreateScreenshot', 'CreateScreenshot_Task', 'CreateSecondary', 'CreateSecondaryEx', 'CreateSecondaryVMEx_Task', 'CreateSecondaryVM_Task', 'CreateSnapshot', 'CreateSnapshotEx', 'CreateSnapshotEx_Task', 'CreateSnapshot_Task', 'CryptoUnlock', 'CryptoUnlock_Task', 'Customize', 'CustomizeVM_Task', 'DefragmentAllDisks', 'Destroy', 'Destroy_Task', 'DetachDisk', 'DetachDisk_Task', 'DisableSecondary', 'DisableSecondaryVM_Task', 'DropConnections', 'EnableSecondary', 'EnableSecondaryVM_Task', 'EstimateStorageForConsolidateSnapshots_Task', 'EstimateStorageRequirementForConsolidate', 'ExportVm', 'ExtractOvfEnvironment', 'InstantClone', 'InstantClone_Task', 'MakePrimary', 'MakePrimaryVM_Task', 'MarkAsTemplate', 'MarkAsVirtualMachine', 'Migrate', 'MigrateVM_Task', 'MountToolsInstaller', 'PowerOff', 'PowerOffVM_Task', 'PowerOn', 'PowerOnVM_Task', 'PromoteDisks', 'PromoteDisks_Task', 'PutUsbScanCodes', 'QueryChangedDiskAreas', 'QueryConnections', 'QueryFaultToleranceCompatibility', 'QueryFaultToleranceCompatibilityEx', 'QueryUnownedFiles', 'RebootGuest', 'ReconfigVM_Task', 'Reconfigure', 'RefreshStorageInfo', 'Reload', 'ReloadFromPath', 'Relocate', 'RelocateVM_Task', 'RemoveAllSnapshots', 'RemoveAllSnapshots_Task', 'Rename', 'Rename_Task', 'Reset', 'ResetGuestInformation', 'ResetVM_Task', 'RevertToCurrentSnapshot', 'RevertToCurrentSnapshot_Task', 'SendNMI', 'SetCustomValue', 'SetDisplayTopology', 'SetScreenResolution', 'ShutdownGuest', 'StandbyGuest', 'StartRecording', 'StartRecording_Task', 'StartReplaying', 'StartReplaying_Task', 'StopRecording', 'StopRecording_Task', 'StopReplaying', 'StopReplaying_Task', 'Suspend', 'SuspendVM_Task', 'Terminate', 'TerminateFaultTolerantVM', 'TerminateFaultTolerantVM_Task', 'TerminateVM', 'TurnOffFaultTolerance', 'TurnOffFaultToleranceForVM_Task', 'UnmountToolsInstaller', 'Unregister', 'UnregisterVM', 'UpgradeTools', 'UpgradeTools_Task', 'UpgradeVM_Task', 'UpgradeVirtualHardware', '_GetMethodInfo', '_GetMethodList', '_GetMoId', '_GetPropertyInfo', '_GetPropertyList', '_GetServerGuid', '_GetStub', '_InvokeAccessor', '_InvokeMethod', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__firstlineno__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__static_attributes__', '__str__', '__subclasshook__', '__weakref__', '_methodInfo', '_moId', '_propInfo', '_propList', '_serverGuid', '_stub', '_version', '_wsdlName', 'alarmActionsEnabled', 'availableField', 'capability', 'config', 'configIssue', 'configStatus', 'customValue', 'datastore', 'declaredAlarmState', 'disabledMethod', 'effectiveRole', 'environmentBrowser', 'guest', 'guestHeartbeatStatus', 'layout', 'layoutEx', 'name', 'network', 'overallStatus', 'parent', 'parentVApp', 'permission', 'recentTask', 'reloadVirtualMachineFromPath_Task', 'resourceConfig', 'resourcePool', 'rootSnapshot', 'runtime', 'setCustomValue', 'snapshot', 'storage', 'summary', 'tag', 'triggeredAlarmState', 'value']
``` ```
@@ -382,6 +394,29 @@ if __name__ == '__main__':
``` powershell ``` powershell
PS D:\PycharmProjects\RemoveWeeklyShapshot> tree /F PS D:\PycharmProjects\RemoveWeeklyShapshot> tree /F
卷 Date 的文件夹 PATH 列表
卷序列号为 0E45-0F72
D:.
│ README.md # 项目描述
├─config # 项目程序配置文件
│ │ config.yaml
│ │ settings.py
├─core # 核心程序
│ │ deleteSnapshots.py
│ │ getVmsSnapshots.py
├─logs # 日志文件
│ 20260220-RemoveWeeklyShapshot.log
├─output # 数据输出文件
│ 2026-02-20-old_snapshots.yaml
│ 2026-02-20_20-36-45-VMsSnapShots_report.xlsx
├─utils # 日志输出格式设置
│ │ logger.py
``` ```

View File

@@ -25,7 +25,8 @@ management_nodes:
# 全局策略配置 # 全局策略配置
global: global:
snapshot_retention_days: 15 # 可选,使用默认值 15 天 snapshot_retention_days: 0 # 可选,使用默认值 15 天
# excel_output_path: ./vm_snapshots_report.xlsx # 可选,使用默认值,如:/logs/2026-02-20_14-00-21-VMsSnapShots_report.xlsx # excel_output_path: ./vm_snapshots_report.xlsx # 可选,使用默认值,如:/logs/2026-02-20_14-00-21-VMsSnapShots_report.xlsx
# 'excel_output_path',: ./vm_snapshots_report.xlsx # 可选,使用默认值,如:/logs/2026-02-20_14-00-21-VMsSnapShots_report.xlsx
# ESXi连接特殊配置禁用SSL验证ESXi默认自签证书 # ESXi连接特殊配置禁用SSL验证ESXi默认自签证书
disable_ssl_verify: true disable_ssl_verify: true

View File

@@ -17,7 +17,8 @@ def load_config():
"SNAPSHOT_RETENTION_DAYS": int(global_config.get('snapshot_retention_days', 15)), "SNAPSHOT_RETENTION_DAYS": int(global_config.get('snapshot_retention_days', 15)),
# "EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'/logs/{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'), # "EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'/logs/{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'),
# "LOG_FILE_PATH": global_config.get('log_file_path', f'/logs/{datetime.now().strftime('%Y%m%d_%H%M%S')}-VMsSnapShots_cleanup.logs'), "EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'/logs/{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'), # "LOG_FILE_PATH": global_config.get('log_file_path', f'/logs/{datetime.now().strftime('%Y%m%d_%H%M%S')}-VMsSnapShots_cleanup.logs'), "EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'/logs/{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'),
"EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'D:\\PycharmProjects\\RemoveWeeklyShapshot\\logs\\{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'), "EXCEL_OUTPUT_PATH": global_config.get('excel_output_path', f'D:\\PycharmProjects\\RemoveWeeklyShapshot\\output\\{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-VMsSnapShots_report.xlsx'),
"YAML_OUTPUT_PATH": global_config.get('yaml_output_path', f'D:\\PycharmProjects\\RemoveWeeklyShapshot\\output\\{datetime.now().strftime('%Y-%m-%d')}-old_snapshots.yaml'),
"DISABLE_SSL_VERIFY": global_config.get('disable_ssl_verify', True), "DISABLE_SSL_VERIFY": global_config.get('disable_ssl_verify', True),
# 算出的过期时间点 # 算出的过期时间点
} }
@@ -49,6 +50,7 @@ config = load_config() # 模块导入时立即执行
MANAGEMENT_NODES = config["MANAGEMENT_NODES"] MANAGEMENT_NODES = config["MANAGEMENT_NODES"]
SNAPSHOT_RETENTION_DAYS = config["SNAPSHOT_RETENTION_DAYS"] SNAPSHOT_RETENTION_DAYS = config["SNAPSHOT_RETENTION_DAYS"]
EXCEL_OUTPUT_PATH = config["EXCEL_OUTPUT_PATH"] EXCEL_OUTPUT_PATH = config["EXCEL_OUTPUT_PATH"]
YAML_OUTPUT_PATH = config["YAML_OUTPUT_PATH"]
#LOG_FILE_PATH = config["LOG_FILE_PATH"] #LOG_FILE_PATH = config["LOG_FILE_PATH"]
DISABLE_SSL_VERIFY = config["DISABLE_SSL_VERIFY"] DISABLE_SSL_VERIFY = config["DISABLE_SSL_VERIFY"]

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: for snapshot in dele_snapshots:
if snapshot['is_old']: if snapshot['is_old']:
# 在这里执行删除操作,例如调用 API 或者其他逻辑 grouped.setdefault(snapshot['NodeHost'], []).append(snapshot)
print(f"Deleting old snapshot: {snapshot['Snapshot Name']} (ID: {snapshot['ID']})")
# 示例:假设存在一个 delete_snapshot 函数
# delete_snapshot(snapshot['ID'])
# 删除旧快照 for host, snapshots in grouped.items():
print(collect_snapshot_data) logger.info(f"连接到 {host} 删除快照...")
# delete_old_snapshots(dele_snapshots) 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 pandas as pd
import yaml
from pyVmomi import vim from pyVmomi import vim
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pyVim.connect import SmartConnect, Disconnect from pyVim.connect import SmartConnect, Disconnect
from openpyxl.styles import Border, Side, Font, PatternFill from openpyxl.styles import Border, Side, Font, PatternFill
from utils.logger import logger 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(): def get_all_vms():
@@ -30,10 +31,8 @@ def get_all_vms():
vm_list.append(vm_info) vm_list.append(vm_info)
except vim.fault.InvalidLogin as e: except vim.fault.InvalidLogin as e:
print(f"登录 {node['host']} 失败,请检查用户名和密码:{e.msg}")
logger.info(f"登录 {node['host']} 失败,请检查用户名和密码:{e.msg}") logger.info(f"登录 {node['host']} 失败,请检查用户名和密码:{e.msg}")
except Exception as e: except Exception as e:
print(f"无法连接到 {node['host']}{e}")
logger.error(f"无法连接到 {node['host']}{e}") logger.error(f"无法连接到 {node['host']}{e}")
finally: finally:
if 'si' in locals(): # 确保 si 是定义过的 if 'si' in locals(): # 确保 si 是定义过的
@@ -146,12 +145,12 @@ def get_virtual_disk_size(vm):
return total_size return total_size
def collect_snapshot_data(snapshot, vm, snapshot_data): def collect_snapshot_data(snapshot, vm, snapshot_data, old_snapshots):
"""递归扁平化快照数据用于Excel""" """递归扁平化快照数据用于Excel,并收集旧快照"""
create_time = datetime.strptime(snapshot['createTime'], '%Y-%m-%d %H:%M:%S') create_time = datetime.strptime(snapshot['createTime'], '%Y-%m-%d %H:%M:%S')
is_old = create_time < (datetime.now() - timedelta(days=SNAPSHOT_RETENTION_DAYS)) is_old = create_time < (datetime.now() - timedelta(days=SNAPSHOT_RETENTION_DAYS))
snapshot_data.append({ snapshot_info = {
'NodeHost': vm['NodeHost'], 'NodeHost': vm['NodeHost'],
'VMName': vm['name'], 'VMName': vm['name'],
'Snapshot Name': snapshot['name'], 'Snapshot Name': snapshot['name'],
@@ -162,16 +161,23 @@ def collect_snapshot_data(snapshot, vm, snapshot_data):
'MOID': snapshot['moId'], 'MOID': snapshot['moId'],
'Quiesced': snapshot['quiesced'], 'Quiesced': snapshot['quiesced'],
'is_old': is_old 'is_old': is_old
}) }
# 如果是旧快照,添加到旧快照列表
if is_old:
old_snapshots.append(snapshot_info)
snapshot_data.append(snapshot_info)
for child in snapshot['children']: for child in snapshot['children']:
collect_snapshot_data(child, vm, snapshot_data) collect_snapshot_data(child, vm, snapshot_data, old_snapshots)
# 输出数据到 Excel 文件 # 输出数据到 Excel 文件
def create_excel_report(vms): def create_excel_report(vms):
vm_data = [] vm_data = []
snapshot_data = [] snapshot_data = []
old_snapshots = [] # 用于存储旧快照的信息
for vm in vms: for vm in vms:
vm_data.append({ vm_data.append({
@@ -192,7 +198,7 @@ def create_excel_report(vms):
if vm['snapshots']: if vm['snapshots']:
for snapshot in 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) vm_df = pd.DataFrame(vm_data)
snapshot_df = pd.DataFrame(snapshot_data) snapshot_df = pd.DataFrame(snapshot_data)
@@ -203,13 +209,30 @@ def create_excel_report(vms):
workbook = writer.book workbook = writer.book
style_sheet(workbook['VMs']) 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__': if __name__ == '__main__':
vms = get_all_vms() # 主函数入口,获取虚拟机信息 vms = get_all_vms() # 主函数入口,获取虚拟机信息
# print(vms) # print(vms)
create_excel_report(vms) # 生成Excel报告 old_snapshots = create_excel_report(vms) # 生成Excel报告并获取旧快照
export_yaml(old_snapshots)
# print(old_snapshots)

View File

@@ -16,7 +16,7 @@ def get_logger():
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 文件处理器(写入日志文件) # 文件处理器(写入日志文件)
file_handler = logging.FileHandler(f'D:\\PycharmProjects\\RemoveWeeklyShapshot\\logs\\{datetime.now().strftime('%Y%m%d')}-VMsSnapShots_cleanup.log', encoding='utf-8') file_handler = logging.FileHandler(f'D:\\PycharmProjects\\RemoveWeeklyShapshot\\logs\\{datetime.now().strftime('%Y%m%d')}-RemoveWeeklyShapshot.log', encoding='utf-8')
file_handler.setFormatter(formatter) # 应用格式化器 file_handler.setFormatter(formatter) # 应用格式化器
# 控制台处理器(输出到终端) # 控制台处理器(输出到终端)
@@ -26,7 +26,7 @@ def get_logger():
# 添加处理器 # 添加处理器
logger.addHandler(file_handler) logger.addHandler(file_handler)
logger.addHandler(console_handler) logger.addHandler(console_handler)
logger.setLevel(logging.DEBUG) # 默认只会记录Info以上级别的日志
return logger return logger