Files
RemoveWeeklyShapshot/README.md
2026-02-22 16:24:29 +08:00

614 lines
28 KiB
Markdown
Raw Permalink 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] 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 = <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 镜像
### 新建数据目录
``` 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
ff84aa4fde3b 35 minutes ago CMD ["python3" "/app/removeweeklysnapshot/ma… 0B buildkit.dockerfile.v0
<missing> 35 minutes ago ENV PYTHONPATH=/app/removeweeklysnapshot 0B buildkit.dockerfile.v0
<missing> 35 minutes ago RUN /bin/sh -c apt-get update && apt-get ins… 216MB buildkit.dockerfile.v0
<missing> 36 minutes ago WORKDIR /app/removeweeklysnapshot 0B buildkit.dockerfile.v0
<missing> 36 minutes ago RUN /bin/sh -c tar -xzf /app/Dev.tar.gz -C /… 51.4kB buildkit.dockerfile.v0
<missing> 36 minutes ago ADD https://gitcode.junlan.site/junlan/Remov… 16.6kB buildkit.dockerfile.v0
<missing> 36 minutes ago RUN /bin/sh -c ln -fs /usr/share/zoneinfo/$T… 1.65MB buildkit.dockerfile.v0
<missing> 36 minutes ago ENV TZ=Asia/Shanghai 0B buildkit.dockerfile.v0
<missing> 2 weeks ago CMD ["python3"] 0B buildkit.dockerfile.v0
<missing> 2 weeks ago RUN /bin/sh -c set -eux; for src in idle3 p… 36B buildkit.dockerfile.v0
<missing> 2 weeks ago RUN /bin/sh -c set -eux; savedAptMark="$(a… 36.6MB buildkit.dockerfile.v0
<missing> 2 weeks ago ENV PYTHON_SHA256=a97d5549e9ad81fe17159ed02c… 0B buildkit.dockerfile.v0
<missing> 2 weeks ago ENV PYTHON_VERSION=3.14.3 0B buildkit.dockerfile.v0
<missing> 2 weeks ago RUN /bin/sh -c set -eux; apt-get update; a… 3.81MB buildkit.dockerfile.v0
<missing> 2 weeks ago ENV PATH=/usr/local/bin:/usr/local/sbin:/usr… 0B buildkit.dockerfile.v0
<missing> 2 weeks ago # debian.sh --arch 'amd64' out/ 'trixie' '@1… 78.6MB 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 -f
removeweeklysnapshot | 2026-02-22 16:01:50,113 - INFO - ✅ 成功加载配置,共 1 个管理节点
removeweeklysnapshot | 2026-02-22 16:01:50,954 - INFO - ✓ 导出任务已设置: 每周 sun 16:05
removeweeklysnapshot | 2026-02-22 16:01:50,954 - INFO - ✓ 删除任务已设置: 每周 sun 16:10
removeweeklysnapshot | 2026-02-22 16:01:50,956 - INFO - 调度器已启动,等待执行任务...
removeweeklysnapshot | 2026-02-22 16:05:00,002 - INFO - 🔍 开始收集VM和快照信息...
removeweeklysnapshot | 2026-02-22 16:05:00,269 - INFO - 成功连接到节点: vcsa8.snimay.com
removeweeklysnapshot | 2026-02-22 16:05:01,251 - INFO - 获取到 5 台虚拟机
removeweeklysnapshot | 2026-02-22 16:05:01,252 - INFO - 📝 开始导出Excel报表...
removeweeklysnapshot | 2026-02-22 16:05:01,257 - INFO - 总共有 4 个快照
removeweeklysnapshot | 2026-02-22 16:05:01,377 - DEBUG - Excel 文件已生成: /app/removeweeklysnapshot/output/vm_snapshots_report-2026-02-22.xlsx
removeweeklysnapshot | 2026-02-22 16:05:01,377 - INFO - 📝 开始导出 Yaml 文件...
removeweeklysnapshot | 2026-02-22 16:05:01,378 - INFO - 可删除的快照有 0 个
removeweeklysnapshot | 2026-02-22 16:05:01,379 - DEBUG - YAML 文件已生成: /app/removeweeklysnapshot/output/old_snapshots-2026-02-22.yaml
removeweeklysnapshot | 2026-02-22 16:05:01,379 - INFO - ========== Excel和Yaml文件导出完成 ==========
[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: 1 # 可选,默认值 15 天
# 定时任务配置
schedule:
export: # 导出 Excel 和 Yaml 文件的时间
day_of_week: 'sun' # 星期几mon,tue,wed,thu,fri,sat,sun
hour: 15 # 小时 (0-23)
minute: 58 # 分钟 (0-59)
second: 0 # 秒 (可选)
delete: # 删除快照的时间
day_of_week: 'sun'
hour: 15
minute: 59
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快照清理任务执行完成 ==========
```