# RemoveWeeklySnapshot Vmware 虚拟机自动化程序:自动化导出虚拟机和快照信息,自动化删除旧快照。 ## Todo List - [x] Yaml 文件保存配置信息 - [x] 通过 Yaml 文件信息连接vCenter/Esxi - [x] 文件获取所有 vms - [x] 获取所有 snapshots - [x] 筛选出15天(半个月)前的 snapshots - [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照蓝色底填充标识 - [x] 最后删除 15 天前的 snapshot,并同时记录删除的 snapshot 日志信息 - [x] 设置计划任务,每周六(或 每15 天)执行一次 - [ ] 增加排除不能删除的 snapshots 信息,用红色底填充标识 - [ ] 删除快照前/后发送邮件通知 - [ ] 多线程删除(控制每台 vCenter 不可以同时删除超过 4 个快照) - [ ] 改用数据库保存管理节点账号密码(加密) ## 输出所有可用的属性和方法 | 你想获取 | 代码 | 示例输出 | | ------------ | ----------------------------------- | ---------------------------------------------- | | **名称** | `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) ``` >(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 >} >] >} ## PY 文件作用 ``` powershell PS D:\PycharmProjects\RemoveWeeklySnapshot> tree /F 卷 Date 的文件夹 PATH 列表 卷序列号为 0E45-0F72 D:. │ main.py │ README.md │ requirements.txt │ ├─config │ │ config.yaml │ │ settings.py │ ├─core │ │ data_exporter.py │ │ get_vm_snapshots.py │ │ remove_snapshots.py │ │ vm_connector.py │ ├─logs │ 2026-02-21-vm_snapshot_cleanup.log │ ├─output │ old_snapshots-2026-02-21.yaml │ vm_snapshots_report-2026-02-21.xlsx │ ├─utils │ │ logger.py ``` ## 所用到的 Python 库 ``` powershell PS D:\PycharmProjects\RemoveWeeklyShapshot> pip freeze > requirements.txt APScheduler==3.11.2 openpyxl==3.2.0b1 pandas==3.0.1 pyvmomi==9.0.0.0 PyYAML==6.0.3 ``` ``` shell pip install -r requirements.txt ``` ## 构建 Docker 镜像 ### 新建数据目录 ``` shell mkdir -p ~/removeweeklysnapshot/{config,logs,output} && cd ~/removeweeklysnapshot ``` ### 安装 Docker ``` shell sudo curl https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker.repo sudo dnf install docker-ce -y && docker -v sudo systemctl enable --now docker && systemctl status docker ``` ### Dockerfile 文件 ``` dockerfile cat << 'EOF' > Dockerfile FROM python:3.14.3-slim # 配置时区 ENV TZ=Asia/Shanghai RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && dpkg-reconfigure -f noninteractive tzdata # 下载代码 ADD https://gitcode.junlan.site/junlan/RemoveWeeklyShapshot/archive/Dev.tar.gz /app/ # 解压压缩文件(最后 mv 命令改名) RUN tar -xzf /app/Dev.tar.gz -C /app/ && rm /app/Dev.tar.gz && mv /app/removeweeklyshapshot /app/removeweeklysnapshot # 设置工作目录 WORKDIR /app/removeweeklysnapshot # 安装必要的软件和 python 库 RUN apt-get update && apt-get install procps tzdata -y && pip install -r requirements.txt && chmod +x main.py # 添加项目根目录到 Python 路径 ENV PYTHONPATH=/app/removeweeklysnapshot # 容器内执行启动程序 CMD ["python3", "/app/removeweeklysnapshot/main.py"] EOF ``` ### 执行构建 Docker 镜像 ``` shell sudo docker build --no-cache -t removeweeklysnapshot . ``` ### 构建 Compose 文件 ``` yaml cat << 'EOF' > compose.yaml services: removeweeklysnapshot: container_name: removeweeklysnapshot image: removeweeklysnapshot volumes: - /home/junlan/removeweeklysnapshot/logs:/app/removeweeklysnapshot/logs - /home/junlan/removeweeklysnapshot/output:/app/removeweeklysnapshot/output # - /home/junlan/removeweeklysnapshot/config/config.yaml:/app/removeweeklysnapshot/config/config.yaml restart: always stdin_open: true tty: true EOF ``` ### 运行容器 ``` shell sudo docker compose up -d ``` ### 查看状态 ``` shell [junlan@localhost ~]$ sudo docker history removeweeklysnapshot IMAGE CREATED CREATED BY SIZE COMMENT 87caa20a7e92 51 seconds ago CMD ["python3" "/app/removeweeklyshapshot/ma… 0B buildkit.dockerfile.v0 51 seconds ago RUN /bin/sh -c apt-get update && apt-get ins… 240MB buildkit.dockerfile.v0 About a minute ago WORKDIR /app/removeweeklyshapshot 0B buildkit.dockerfile.v0 40 minutes ago RUN /bin/sh -c tar -xzf /app/Dev.tar.gz -C /… 73.7kB buildkit.dockerfile.v0 59 minutes ago ADD https://gitcode.junlan.site/junlan/Remov… 16.4kB buildkit.dockerfile.v0 59 minutes ago RUN /bin/sh -c ln -fs /usr/share/zoneinfo/$T… 1.66MB buildkit.dockerfile.v0 59 minutes ago ENV TZ=Asia/Shanghai 0B buildkit.dockerfile.v0 2 weeks ago CMD ["python3"] 0B buildkit.dockerfile.v0 2 weeks ago RUN /bin/sh -c set -eux; for src in idle3 p… 0B buildkit.dockerfile.v0 2 weeks ago RUN /bin/sh -c set -eux; savedAptMark="$(a… 40.5MB buildkit.dockerfile.v0 2 weeks ago ENV PYTHON_SHA256=a97d5549e9ad81fe17159ed02c… 0B buildkit.dockerfile.v0 2 weeks ago ENV PYTHON_VERSION=3.14.3 0B buildkit.dockerfile.v0 2 weeks ago RUN /bin/sh -c set -eux; apt-get update; a… 4.35MB buildkit.dockerfile.v0 2 weeks ago ENV PATH=/usr/local/bin:/usr/local/sbin:/usr… 0B buildkit.dockerfile.v0 2 weeks ago # debian.sh --arch 'amd64' out/ 'trixie' '@1… 85.9MB debuerreotype 0.17 [junlan@localhost removeweeklyshapshot]$ sudo docker images IMAGE ID DISK USAGE CONTENT SIZE EXTRA python:3.14.3-slim 486b8092bfb1 176MB 45.5MB removeweeklysnapshot:latest 6f17fcaaef99 512MB 140MB [junlan@localhost removeweeklyshapshot]$ sudo docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS removeweeklysnapshot removeweeklysnapshot "python3 /removeweek…" removeweeklysnapshot 34 seconds ago Up 15 seconds [junlan@localhost removeweeklyshapshot]$ sudo docker compose logs removeweeklysnapshot | 2026-02-21 15:12:51,150 - INFO - ✅ 成功加载配置,共 2 个管理节点 removeweeklysnapshot | 2026-02-21 15:12:51,922 - INFO - 定时任务已设置:每周六凌晨4点导出文件,晚上7点删除快照 [junlan@localhost removeweeklyshapshot]$ sudo docker exec -it removeweeklysnapshot bash root@07c30da6408a:/removeweeklysnapshot# ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 15:12 pts/0 00:00:01 python3 /removeweeklysnapshot/main.py root 11 0 0 15:15 pts/1 00:00:00 bash root 17 11 0 15:15 pts/1 00:00:00 ps -ef ``` ### 修改代码进行临时测试 - 修改 ==config.yaml== 文件 - `snapshot_retention_days: 0` :定义删除多少天前的快照 - `schedule`:自定义改参数下的时间 ``` yaml cat << 'EOF' > ~/removeweeklysnapshot/config/config.yaml # 管理节点配置(包含vCenter和ESXi) management_nodes: # vCenter节点 - type: vcenter # 标记类型为vcenter name: vc1 # 节点名称(用于日志) host: vcsa8.xxx.com # 地址 user: administrator@vcsa.local password: Root@2020 max_delete_concurrent: 4 # 该节点最大并发删除数 # 全局策略配置 global: disable_ssl_verify: true # ESXi连接特殊配置(禁用SSL验证,ESXi默认自签证书) snapshot_retention_days: 0 # 可选,默认值 15 天 # 定时任务配置 schedule: export: # 导出 Excel 和 Yaml 文件的时间 day_of_week: 'sun' # 星期几:mon,tue,wed,thu,fri,sat,sun hour: 15 # 小时 (0-23) minute: 35 # 分钟 (0-59) second: 0 # 秒 (可选) delete: # 删除快照的时间 day_of_week: 'sun' hour: 15 minute: 40 second: 0 EOF ================================================================================================== cat << 'EOF' > compose.yaml services: removeweeklysnapshot: container_name: removeweeklysnapshot image: removeweeklysnapshot volumes: - ~/removeweeklysnapshot/logs:/app/removeweeklysnapshot/logs - ~/removeweeklysnapshot/output:/app/removeweeklysnapshot/output - ~/removeweeklysnapshot/config/config.yaml:/app/removeweeklysnapshot/config/config.yaml restart: always stdin_open: true tty: true EOF ``` #### 进入容器测试 手动执行 `main.py` 运行 ``` shell [junlan@localhost removeweeklysnapshot]$ sudo docker exec -it removeweeklysnapshot bash root@63869672d333:/app/removeweeklyshapshot# python main.py 2026-02-21 15:42:56,070 - INFO - ✅ 成功加载配置,共 2 个管理节点 2026-02-21 15:42:56,556 - INFO - 定时任务已设置:每周六凌晨4点导出文件,晚上7点删除快照 2026-02-21 15:43:00,000 - INFO - 🔍 开始收集VM和快照信息... 2026-02-21 15:43:00,054 - INFO - 成功连接到节点: 192.168.40.133 2026-02-21 15:43:03,319 - ERROR - 处理节点 192.168.40.135 失败:[Errno 113] No route to host 2026-02-21 15:43:03,319 - INFO - 获取到 2 台虚拟机 2026-02-21 15:43:03,319 - INFO - 📝 开始导出Excel报表... 2026-02-21 15:43:03,322 - INFO - 总共有 3 个快照 2026-02-21 15:43:03,340 - DEBUG - Excel 文件已生成: /removeweeklysnapshot/output/vm_snapshots_report.xlsx 2026-02-21 15:43:03,340 - INFO - 📝 开始导出 Yaml 文件... 2026-02-21 15:43:03,340 - INFO - 可删除的快照有 3 个 2026-02-21 15:43:03,342 - DEBUG - YAML 文件已生成: /removeweeklysnapshot/output/old_snapshots.yaml 2026-02-21 15:43:03,342 - INFO - ========== Excel和Yaml文件导出完成 ========== 2026-02-21 15:44:00,001 - INFO - 🗑️ 开始删除过旧快照... 2026-02-21 15:44:00,004 - INFO - 连接 192.168.40.133 进行删除快照... 2026-02-21 15:44:00,050 - INFO - 成功连接到节点: 192.168.40.133 2026-02-21 15:44:00,081 - INFO - 正在删除 Snapshot: VMware vCenter Server Appliance-快照测试-(1-snapshot-7) 2026-02-21 15:44:01,100 - INFO - ✅ 删除成功: VMware vCenter Server Appliance-快照测试-(1-snapshot-7) 2026-02-21 15:44:01,125 - INFO - 正在删除 Snapshot: VMware vCenter Server Appliance-快照测试-第二层快照-(1-snapshot-8) 2026-02-21 15:44:02,139 - INFO - ✅ 删除成功: VMware vCenter Server Appliance-快照测试-第二层快照-(1-snapshot-8) 2026-02-21 15:44:02,164 - INFO - 正在删除 Snapshot: test-vm-01-snap-01-(4-snapshot-9) 2026-02-21 15:44:03,178 - INFO - ✅ 删除成功: test-vm-01-snap-01-(4-snapshot-9) 2026-02-21 15:44:03,184 - INFO - ========== VM快照清理任务执行完成 ========== ```