# RemoveWeeklySnapshot Vmware 虚拟机自动化程序:自动化导出虚拟机和快照信息,自动化删除旧快照。 ## Todo List - [x] 连接vCenter/Esxi/Hyper-V - [x] 获取所有 vms - [x] 获取所有 snapshots - [x] 筛选出15天(半个月)前的 snapshots - [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照蓝色底填充标识 - [ ] 增加排除不能删除的 snapshots 信息,用红色底填充标识 - [ ] Outlook 邮箱发送统计超过 15 天的 snapshots 信息(即要删除的快照列表) - [ ] 需要控制每台 vCenter 不可以同时删除超过 4 个快照(需要同时获取删除成功的信息) - [x] 最后删除 15 天前的 snapshot,并同时记录删除的 snapshot 日志信息 - [ ] 删除成功后发送邮件通知 - [ ] 设置计划任务,每 2 周(半个月)执行一次 ✅ 多线程删除 ✅ 任务进度显示 ✅ 自动重试机制 ✅ 企业级日志 ✅ 删除前后发送邮件通知 ## 输出所有可用的属性和方法 | 你想获取 | 代码 | 示例输出 | | ------------ | ----------------------------------- | ---------------------------------------------- | | **名称** | `vm.name` | `"WebServer-01"` | | **MOID** | `vm._moId` | `"vm-12"` | | **电源状态** | `vm.runtime.powerState` | `poweredOn` / `poweredOff` | | **开机时间** | `vm.runtime.bootTime` | `datetime` 对象 | | **CPU数** | `vm.config.hardware.numCPU` | `4` | | **内存(MB)** | `vm.config.hardware.memoryMB` | `8192` | | **操作系统** | `vm.config.guestFullName` | `"CentOS 7 (64-bit)"` | | **IP地址** | `vm.guest.ipAddress` | `"192.168.1.100"` | | **主机名** | `vm.guest.hostName` | `"webserver01.local"` | | **存储路径** | `vm.config.files.vmPathName` | `"[Datastore1] WebServer-01/WebServer-01.vmx"` | | **快照数量** | `len(vm.snapshot.rootSnapshotList)` | `3` | >以下这些方法和属性主要用于操作虚拟机(VM)、快照、存储和其他资源。 > >vm >├── 基础标识 >│ ├── name VM名称 >│ └── _moId 内部ID (vm-12) >│ >├── runtime 【运行状态】 >│ ├── powerState poweredOn/Off/Suspended >│ ├── bootTime 开机时间 >│ └── host 所在物理机 >│ >├── config 【硬件配置】 >│ ├── hardware CPU/内存/硬盘 >│ ├── guestFullName 操作系统 >│ └── files VMX文件路径 >│ >├── guest 【客户机内部信息】 >│ ├── hostName 主机名 >│ ├── ipAddress IP地址 >│ └── toolsStatus VMware Tools状态 >│ >├── snapshot 【快照】 >│ └── rootSnapshotList 快照树 >│ >├── storage 【存储】 >│ └── perDatastoreUsage 各数据存储用量 >│ >├── network 【网络】 >│ └── [Network] 连接的端口组 >│ >└── summary 【快速汇总】 >├── overallStatus 整体健康状态 >└── quickStats 实时性能数据 > >1. **AcquireMksTicket / AcquireTicket**:获取访问虚拟机控制台的票据,允许用户通过 Web 界面访问 VM。 >2. **Clone / CloneVM_Task**:克隆一个虚拟机,创建其副本。 >3. **CreateSnapshot / CreateSnapshot_Task**:创建虚拟机快照,以保存当前 VM 的状态。 >4. **Destroy / Destroy_Task**:删除虚拟机及其所有数据。 >5. **PowerOn / PowerOnVM_Task**:启动虚拟机。 >6. **PowerOff / PowerOffVM_Task**:关闭虚拟机。 >7. **Reset / ResetVM_Task**:重置虚拟机,相当于按下重启按钮。 >8. **RevertToCurrentSnapshot / RevertToCurrentSnapshot_Task**:将虚拟机恢复到当前快照的状态。 >9. **Migrate / MigrateVM_Task**:迁移虚拟机到不同的主机或数据存储。 >10. **SetCustomValue**:设置自定义属性值,以便在虚拟机上存储额外信息。 > >### 属性列表 > >这些属性通常包含关于虚拟机或其他资源的状态和配置信息。以下是一些关键属性的说明: > >1. **name**:虚拟机的名称。 >2. **guest**:关于虚拟机操作系统的信息,如操作系统类型、版本等。 >3. **config**:虚拟机的配置详情,例如 CPU、内存、硬盘等。 >4. **runtime**:虚拟机的运行时状态,包括电源状态(开机、关机、挂起等)。 >5. **snapshot**:当前虚拟机的快照信息。 >6. **summary**:虚拟机的概述信息,包括状态、资源使用情况等。 >7. **resourcePool**:虚拟机所在的资源池。 >8. **datastore**:虚拟机的存储位置,指向其使用的存储库。 >9. **overallStatus**:虚拟机的总体状态,可能是正常、警告、错误等。 >10. **recentTask**:最近执行的任务列表。 > >### 其他属性 > >- **capability**:虚拟机支持的功能,如支持的虚拟硬件版本。 >- **declaredAlarmState**:声明的警报状态,用于监控虚拟机的健康状况。 >- **triggeredAlarmState**:触发的警报状态,显示当前激活的警报。 ```python for vm in vm_view.view: print(dir(vm)) # 输出所有可用的属性和方法 # print(vm.summary) # print(vm.snapshot) # print(vars(vm.summary)) ``` ``` 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'] ``` ### 获取虚拟机属性 获取和打印出虚拟机 (VM) 概要信息的所有属性,提供了关于虚拟机的基本信息,`vm.summary` 会返回一个字典,包含常见属性。可以使用 vm.guest.ipAddress 方法获取到虚拟机的IP地址等。 ```python print(vm.summary) ``` > (vim.vm.Summary) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > vm = 'vim.VirtualMachine:1', > runtime = (vim.vm.RuntimeInfo) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > device = (vim.vm.DeviceRuntimeInfo) [ > (vim.vm.DeviceRuntimeInfo) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > runtimeState = (vim.vm.DeviceRuntimeInfo.VirtualEthernetCardRuntimeState) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > vmDirectPathGen2Active = false, > vmDirectPathGen2InactiveReasonVm = (str) [ > 'vmNptDisabledOrDisconnectedAdapter' > ], > vmDirectPathGen2InactiveReasonOther = (str) [], > vmDirectPathGen2InactiveReasonExtended = , > uptv2Active = , > uptv2InactiveReasonVm = (str) [], > uptv2InactiveReasonOther = (str) [], > reservationStatus = , > attachmentStatus = 'red', > featureRequirement = (vim.vm.FeatureRequirement) [] > }, > key = 4000 > } > ], > host = 'vim.HostSystem:ha-host', > connectionState = 'connected', > powerState = 'poweredOff', > vmFailoverInProgress = , > faultToleranceState = 'notConfigured', > dasVmProtection = , > toolsInstallerMounted = false, > suspendTime = , > bootTime = , > suspendInterval = 0, > question = , > memoryOverhead = , > maxCpuUsage = 5184, > maxMemoryUsage = 4096, > numMksConnections = 0, > recordReplayState = 'inactive', > cleanPowerOff = false, > needSecondaryReason = , > onlineStandby = false, > minRequiredEVCModeKey = , > consolidationNeeded = false, > offlineFeatureRequirement = (vim.vm.FeatureRequirement) [ > (vim.vm.FeatureRequirement) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > key = 'cpuid.lm', > featureName = 'cpuid.lm', > value = 'Bool:Min:1' > } > ], > featureRequirement = (vim.vm.FeatureRequirement) [], > featureMask = (vim.host.FeatureMask) [], > vFlashCacheAllocation = , > paused = false, > snapshotInBackground = false, > quiescedForkParent = , > instantCloneFrozen = false, > cryptoState = , > suspendedToMemory = , > opNotificationTimeout = , > iommuActive = > }, > guest = (vim.vm.Summary.GuestSummary) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > guestId = , > guestFullName = , > toolsStatus = 'toolsNotRunning', > toolsVersionStatus = 'guestToolsUnmanaged', > toolsVersionStatus2 = 'guestToolsUnmanaged', > toolsRunningStatus = 'guestToolsNotRunning', > hostName = , > ipAddress = , > hwVersion = > }, > config = (vim.vm.Summary.ConfigSummary) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > name = 'VMware vCenter Server Appliance', > template = false, > vmPathName = '[datastore1] VMware vCenter Server Appliance/VMware vCenter Server Appliance.vmx', > memorySizeMB = 4096, > cpuReservation = 0, > memoryReservation = 0, > numCpu = 2, > numEthernetCards = 1, > numVirtualDisks = 13, > uuid = '564d7d77-f37f-64a7-47f2-a131a337c070', > instanceUuid = '529e8be2-58ca-617c-ad7e-7f66de667471', > guestId = 'other3xLinux64Guest', > guestFullName = 'Other 3.x Linux (64-bit)', > annotation = 'VMware vCenter Server Appliance', > product = , > installBootRequired = , > ftInfo = , > managedBy = , > tpmPresent = false, > numVmiopBackings = 0, > hwVersion = > }, > storage = (vim.vm.Summary.StorageSummary) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > committed = 37476296711, > uncommitted = 303123661815, > unshared = 37476296711, > timestamp = 2026-02-19T09:11:27.701184Z > }, > quickStats = (vim.vm.Summary.QuickStats) { > dynamicType = , > dynamicProperty = (vmodl.DynamicProperty) [], > overallCpuUsage = , > overallCpuDemand = , > overallCpuReadiness = , > guestMemoryUsage = , > hostMemoryUsage = , > guestHeartbeatStatus = 'gray', > distributedCpuEntitlement = , > distributedMemoryEntitlement = , > staticCpuEntitlement = , > staticMemoryEntitlement = , > grantedMemory = , > privateMemory = , > sharedMemory = , > swappedMemory = , > balloonedMemory = , > consumedOverheadMemory = , > ftLogBandwidth = , > ftSecondaryLatency = , > ftLatencyStatus = , > compressedMemory = , > uptimeSeconds = , > ssdSwappedMemory = , > activeMemory = , > memoryTierStats = (vim.vm.Summary.QuickStats.MemoryTierStats) [] > }, > overallStatus = 'green', > customValue = (vim.CustomFieldsManager.Value) [] > } ### 获取快照属性 输出快照所有信息 ```python print(vm.snapshot) ``` ``` json (vim.vm.SnapshotInfo) { dynamicType = , dynamicProperty = (vmodl.DynamicProperty) [], currentSnapshot = 'vim.vm.Snapshot:1-snapshot-3', rootSnapshotList = (vim.vm.SnapshotTree) [ (vim.vm.SnapshotTree) { dynamicType = , dynamicProperty = (vmodl.DynamicProperty) [], snapshot = 'vim.vm.Snapshot:1-snapshot-1', vm = 'vim.VirtualMachine:1', name = 'snap-01', # 第一层快照 description = 'Ansible snapshot', id = 1, createTime = 2026-02-17T03:26:08.834505Z, state = 'poweredOff', quiesced = false, backupManifest = , childSnapshotList = (vim.vm.SnapshotTree) [ (vim.vm.SnapshotTree) { dynamicType = , dynamicProperty = (vmodl.DynamicProperty) [], snapshot = 'vim.vm.Snapshot:1-snapshot-2', vm = 'vim.VirtualMachine:1', name = 'snap-02', # 第二层快照 description = 'test-Ansible snapshot', id = 2, createTime = 2026-02-17T08:57:39.122511Z, state = 'poweredOff', quiesced = false, backupManifest = , childSnapshotList = (vim.vm.SnapshotTree) [ (vim.vm.SnapshotTree) { dynamicType = , dynamicProperty = (vmodl.DynamicProperty) [], snapshot = 'vim.vm.Snapshot:1-snapshot-3', vm = 'vim.VirtualMachine:1', name = '虚拟机快照 2026%252f2%252f19 12:28:36', # 第二层快照 description = '', id = 3, createTime = 2026-02-19T04:28:38.007703Z, state = 'poweredOn', quiesced = false, backupManifest = , childSnapshotList = (vim.vm.SnapshotTree) [], replaySupported = false } ], replaySupported = false } ], replaySupported = false } ] } ``` ### 打印快照树 ``` python def print_snapshot_tree(snapshot_info, level=0): """递归打印快照树(辅助函数)""" indent = " " * level print(f"{indent}├─ {snapshot_info['name']}") print(f"{indent}│ ├─ 创建时间: {snapshot_info['createTime']}") print(f"{indent}│ ├─ 描述: {snapshot_info['description']}") print(f"{indent}│ ├─ 状态: {snapshot_info['state']}") if snapshot_info['sizeMB']: print(f"{indent}│ ├─ 大小: {snapshot_info['sizeMB']} MB") for child in snapshot_info['children']: print_snapshot_tree(child, level + 1) if __name__ == '__main__': vms = get_all_vms() # 打印示例:显示每个VM的快照信息 for vm in vms: print(f"\nVM: {vm['name']}") if vm['snapshots']: print(f"快照数量: {len(vm['snapshots'])}") for snapshot in vm['snapshots']: print_snapshot_tree(snapshot) else: print("无快照") ``` ## PY 文件作用描述 ``` powershell 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 # 日志配置 ``` ## 所用到的 Python 库 ``` powershell PS D:\PycharmProjects\RemoveWeeklyShapshot> pip freeze > requirements.txt openpyxl==3.2.0b1 pandas==3.0.1 pyvmomi==9.0.0.0 PyYAML==6.0.3 ``` ``` shell pip install -r requirements.txt ``` ## **定时执行**(Linux crontab): ```bash # 每月1号凌晨2点执行 0 2 1 * * /usr/bin/python3 /var/RemoveWeeklyShapshot/main.py 2>&1 ```