添加了删除快照的功能
This commit is contained in:
43
README.md
43
README.md
@@ -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
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user