Files
RemoveWeeklyShapshot/README.md
2026-02-21 16:13:16 +08:00

645 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RemoveWeeklySnapshot
Vmware 虚拟机自动化程序:自动化导出虚拟机和快照信息,自动化删除旧快照。
## Todo List
- [x] 连接vCenter/Esxi/Hyper-V
- [x] 获取所有 vms
- [x] 获取所有 snapshots
- [x] 筛选出15天半个月前的 snapshots
- [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照蓝色底填充标识
- [x] 最后删除 15 天前的 snapshot并同时记录删除的 snapshot 日志信息
- [x] 设置计划任务,每周六(或 每15 天)执行一次
- [ ] 增加排除不能删除的 snapshots 信息,用红色底填充标识
- [ ] Outlook 邮箱发送统计超过 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 = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> vm = 'vim.VirtualMachine:1',
> runtime = (vim.vm.RuntimeInfo) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> device = (vim.vm.DeviceRuntimeInfo) [
> (vim.vm.DeviceRuntimeInfo) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> runtimeState = (vim.vm.DeviceRuntimeInfo.VirtualEthernetCardRuntimeState) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> vmDirectPathGen2Active = false,
> vmDirectPathGen2InactiveReasonVm = (str) [
> 'vmNptDisabledOrDisconnectedAdapter'
> ],
> vmDirectPathGen2InactiveReasonOther = (str) [],
> vmDirectPathGen2InactiveReasonExtended = <unset>,
> uptv2Active = <unset>,
> uptv2InactiveReasonVm = (str) [],
> uptv2InactiveReasonOther = (str) [],
> reservationStatus = <unset>,
> attachmentStatus = 'red',
> featureRequirement = (vim.vm.FeatureRequirement) []
> },
> key = 4000
> }
> ],
> host = 'vim.HostSystem:ha-host',
> connectionState = 'connected',
> powerState = 'poweredOff',
> vmFailoverInProgress = <unset>,
> faultToleranceState = 'notConfigured',
> dasVmProtection = <unset>,
> toolsInstallerMounted = false,
> suspendTime = <unset>,
> bootTime = <unset>,
> suspendInterval = 0,
> question = <unset>,
> memoryOverhead = <unset>,
> maxCpuUsage = 5184,
> maxMemoryUsage = 4096,
> numMksConnections = 0,
> recordReplayState = 'inactive',
> cleanPowerOff = false,
> needSecondaryReason = <unset>,
> onlineStandby = false,
> minRequiredEVCModeKey = <unset>,
> consolidationNeeded = false,
> offlineFeatureRequirement = (vim.vm.FeatureRequirement) [
> (vim.vm.FeatureRequirement) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> key = 'cpuid.lm',
> featureName = 'cpuid.lm',
> value = 'Bool:Min:1'
> }
> ],
> featureRequirement = (vim.vm.FeatureRequirement) [],
> featureMask = (vim.host.FeatureMask) [],
> vFlashCacheAllocation = <unset>,
> paused = false,
> snapshotInBackground = false,
> quiescedForkParent = <unset>,
> instantCloneFrozen = false,
> cryptoState = <unset>,
> suspendedToMemory = <unset>,
> opNotificationTimeout = <unset>,
> iommuActive = <unset>
> },
> guest = (vim.vm.Summary.GuestSummary) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> guestId = <unset>,
> guestFullName = <unset>,
> toolsStatus = 'toolsNotRunning',
> toolsVersionStatus = 'guestToolsUnmanaged',
> toolsVersionStatus2 = 'guestToolsUnmanaged',
> toolsRunningStatus = 'guestToolsNotRunning',
> hostName = <unset>,
> ipAddress = <unset>,
> hwVersion = <unset>
> },
> config = (vim.vm.Summary.ConfigSummary) {
> dynamicType = <unset>,
> 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 = <unset>,
> installBootRequired = <unset>,
> ftInfo = <unset>,
> managedBy = <unset>,
> tpmPresent = false,
> numVmiopBackings = 0,
> hwVersion = <unset>
> },
> storage = (vim.vm.Summary.StorageSummary) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> committed = 37476296711,
> uncommitted = 303123661815,
> unshared = 37476296711,
> timestamp = 2026-02-19T09:11:27.701184Z
> },
> quickStats = (vim.vm.Summary.QuickStats) {
> dynamicType = <unset>,
> dynamicProperty = (vmodl.DynamicProperty) [],
> overallCpuUsage = <unset>,
> overallCpuDemand = <unset>,
> overallCpuReadiness = <unset>,
> guestMemoryUsage = <unset>,
> hostMemoryUsage = <unset>,
> guestHeartbeatStatus = 'gray',
> distributedCpuEntitlement = <unset>,
> distributedMemoryEntitlement = <unset>,
> staticCpuEntitlement = <unset>,
> staticMemoryEntitlement = <unset>,
> grantedMemory = <unset>,
> privateMemory = <unset>,
> sharedMemory = <unset>,
> swappedMemory = <unset>,
> balloonedMemory = <unset>,
> consumedOverheadMemory = <unset>,
> ftLogBandwidth = <unset>,
> ftSecondaryLatency = <unset>,
> ftLatencyStatus = <unset>,
> compressedMemory = <unset>,
> uptimeSeconds = <unset>,
> ssdSwappedMemory = <unset>,
> activeMemory = <unset>,
> memoryTierStats = (vim.vm.Summary.QuickStats.MemoryTierStats) []
> },
> overallStatus = 'green',
> customValue = (vim.CustomFieldsManager.Value) []
> }
### 获取快照属性
输出快照所有信息
```python
print(vm.snapshot)
```
>(vim.vm.SnapshotInfo) {
>dynamicType = <unset>,
>dynamicProperty = (vmodl.DynamicProperty) [],
>currentSnapshot = 'vim.vm.Snapshot:1-snapshot-3',
>rootSnapshotList = (vim.vm.SnapshotTree) [
>(vim.vm.SnapshotTree) {
> dynamicType = <unset>,
> 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 = <unset>,
> childSnapshotList = (vim.vm.SnapshotTree) [
> (vim.vm.SnapshotTree) {
> dynamicType = <unset>,
> 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 = <unset>,
> childSnapshotList = (vim.vm.SnapshotTree) [
> (vim.vm.SnapshotTree) {
> dynamicType = <unset>,
> 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 = <unset>,
> 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 镜像
### 安装 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
```
### 下载并解压代码文件
``` shell
wget https://gitcode.junlan.site/junlan/RemoveWeeklyShapshot/archive/Dev.zip && unzip Dev.zip && cd removeweeklyshapshot/
```
``` shell
[junlan@localhost removeweeklyshapshot]$ tree
.
├── compose.yaml
├── config
│   ├── config.yaml
│   └── settings.py
├── core
│   ├── data_exporter.py
│   ├── get_vm_snapshots.py
│   ├── remove_snapshots.py
│   └── vm_connector.py
├── Dockerfile
├── main.py
├── README.md
├── requirements.txt
├── utils
│   └── logger.py
```
### 构建 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
# 切换目录
WORKDIR /removeweeklysnapshot/
# 复制代码
COPY ./ /removeweeklysnapshot/
# 安装必要的软件和 python 库
RUN apt-get update && apt-get install procps tzdata -y && pip install -r requirements.txt && chmod +x /removeweeklysnapshot/main.py && mkdir output
# 容器内执行启动程序
CMD ["python3", "/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:
- '/var/removeweeklysnapshot/logs:/removeweeklysnapshot/logs'
- '/var/removeweeklysnapshot/output:/removeweeklysnapshot/output'
# - '/var/removeweeklysnapshot/config:/removeweeklysnapshot/config'
restart: always
stdin_open: true
tty: true
EOF
```
### 运行容器
``` shell
sudo docker compose up -d
```
### 查看状态
``` shell
[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
```
### 修改代码进行临时测试
> excel_output_path: /removeweeklysnapshot/output/vm_snapshots_report.xlsx # 可选,使用默认值,如:/logs/2026-02-20_14-00-21-VMsSnapShots_report.xlsx
> yaml_output_path: /removeweeklysnapshot/output/old_snapshots.yaml # 可选
>
> ```python
> # 每周六凌晨4点导出Excel和Yaml文件
> scheduler.add_job(export_files, 'cron', day_of_week='sat', hour=15, minute=43) # 修改时间进行测试
>
> # 每周六晚上7点执行删除快照任务
> scheduler.add_job(delete_old_snapshots, 'cron', day_of_week='sat', hour=15, minute=44) # 修改时间进行测试
> ```
``` shell
root@07c30da6408a:/removeweeklysnapshot# cat << 'EOF' > config/config.yaml
management_nodes:
- type: esxi # 标记类型为esxi
name: esxi1
host: 192.168.40.133
user: root # ESXi默认用root
password: Root@2025
max_delete_concurrent: 2 # ESXi性能较弱并发数可设小些
- type: esxi
name: esxi2
host: 192.168.40.135
user: root
password: Root@2025
max_delete_concurrent: 2
global:
disable_ssl_verify: true # ESXi连接特殊配置禁用SSL验证ESXi默认自签证书
snapshot_retention_days: 0 # 可选,默认值 15 天
excel_output_path: /removeweeklysnapshot/output/vm_snapshots_report.xlsx # 可选,使用默认值,如:/logs/2026-02-20_14-00-21-VMsSnapShots_report.xlsx
yaml_output_path: /removeweeklysnapshot/output/old_snapshots.yaml # 可选
EOF
root@75ce49380cc0:/removeweeklysnapshot# cat << 'EOF' > main.py
import time
from apscheduler.schedulers.background import BackgroundScheduler
from utils.logger import logger
from config.settings import YAML_OUTPUT_PATH
from core.get_vm_snapshots import get_all_vms
from core.data_exporter import create_excel_report, export_yaml
from core.remove_snapshots import load_old_snapshots, remove_snapshot
def export_files():
"""导出Excel和Yaml文件的函数"""
logger.info("🔍 开始收集VM和快照信息...")
vms = get_all_vms() # 主函数入口,获取虚拟机信息
# 导出Excel报表
logger.info("📝 开始导出Excel报表...")
old_snapshots = create_excel_report(vms) # 生成Excel报告并获取旧快照
# 导出Yaml文件
logger.info("📝 开始导出 Yaml 文件...")
export_yaml(old_snapshots)
logger.info("========== Excel和Yaml文件导出完成 ==========")
def delete_old_snapshots():
"""删除旧快照的函数"""
logger.info("🗑️ 开始删除过旧快照...")
old_snapshots = load_old_snapshots(YAML_OUTPUT_PATH)
remove_snapshot(old_snapshots)
logger.info("========== VM快照清理任务执行完成 ==========")
def main():
"""主执行函数"""
# 设置定时任务
scheduler = BackgroundScheduler()
# 每周六凌晨4点导出Excel和Yaml文件
scheduler.add_job(export_files, 'cron', day_of_week='sat', hour=15, minute=43) # 修改时间进行测试
# 每周六晚上7点执行删除快照任务
scheduler.add_job(delete_old_snapshots, 'cron', day_of_week='sat', hour=15, minute=44) # 修改时间进行测试
scheduler.start()
logger.info("定时任务已设置每周六凌晨4点导出文件晚上7点删除快照")
try:
# 保持主程序运行,以便调度器能正常工作
while True:
time.sleep(1)
except (KeyboardInterrupt, SystemExit):
scheduler.shutdown()
if __name__ == "__main__":
main()
EOF
# 进入容器进行测试
[junlan@localhost removeweeklyshapshot]$ sudo docker exec -it removeweeklysnapshot bash
root@75ce49380cc0:/removeweeklysnapshot# 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快照清理任务执行完成 ==========
```