Get Vms Snapshots and Export Excel correct info.

This commit is contained in:
panjunlan
2026-02-19 23:23:51 +08:00
parent 3332e3b865
commit 199cbab4aa
3 changed files with 456 additions and 158 deletions

329
README.md
View File

@@ -6,12 +6,11 @@
- [x] 获取所有 vms - [x] 获取所有 vms
- [x] 获取所有 snapshots - [x] 获取所有 snapshots
- [x] 筛选出15天半个月前的 snapshots - [x] 筛选出15天半个月前的 snapshots
- [x] 以上内容以Excel表格的形式导出超出15天的快照底标识 - [x] 以上内容以 Excel 表格的形式导出,超出 15 天的快照填充蓝色底标识
- [ ] 最后删除 15 天前的 snapshot并同时记录删除的 snapshot 日志信息 - [ ] Outlook 邮箱发送超出 15 天的快照
- [ ] 需要控制每台 vCenter 不可以同时删除超过4个快照
- [ ] 增加排除不能删除的快照 - [ ] 增加排除不能删除的快照
- [ ] 需要控制每台 vCenter 不可以同时删除超过 4 个快照
- [ ] 最后删除 15 天前的 snapshot并同时记录删除的 snapshot 日志信息
@@ -29,6 +28,21 @@
| **存储路径** | `vm.config.files.vmPathName` | `"[Datastore1] WebServer-01/WebServer-01.vmx"` | | **存储路径** | `vm.config.files.vmPathName` | `"[Datastore1] WebServer-01/WebServer-01.vmx"` |
| **快照数量** | `len(vm.snapshot.rootSnapshotList)` | `3` | | **快照数量** | `len(vm.snapshot.rootSnapshotList)` | `3` |
## 输出所有可用的属性和方法
```
vm.config.createDate # 虚拟机的创建时间
vm.runtime.bootTime # 虚拟机上次启动的时间
```
>以下这些方法和属性主要用于操作虚拟机VM、快照、存储和其他资源。
>
>vm >vm
>├── 基础标识 >├── 基础标识
>│ ├── name VM名称 >│ ├── name VM名称
@@ -61,9 +75,308 @@
>└── summary 【快速汇总】 >└── summary 【快速汇总】
>├── overallStatus 整体健康状态 >├── overallStatus 整体健康状态
>└── quickStats 实时性能数据 >└── 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']
```
[{'name': 'snap-02', 'description': 'test-Ansible snapshot', 'createTime': '2026-02-17 08:57:39', 'state': 'poweredOff', 'id': 2, 'moId': 'snapshot-27', 'sizeMB': None, 'quiesced': False, 'children': []}] ### 获取虚拟机属性
[{'name': 'snap-01', 'description': 'Ansible snapshot', 'createTime': '2026-02-17 03:26:08', 'state': 'poweredOff', 'id': 1, 'moId': 'snapshot-26', 'sizeMB': None, 'quiesced': False, 'children': [{'name': 'snap-02', 'description': 'test-Ansible snapshot', 'createTime': '2026-02-17 08:57:39', 'state': 'poweredOff', 'id': 2, 'moId': 'snapshot-27', 'sizeMB': None, 'quiesced': False, 'children': []}]}]
获取到 2 台VM 获取和打印出虚拟机 (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)
```
``` json
(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
}
]
}
```
### 打印快照树
``` 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("无快照")
```

View File

@@ -1,12 +1,12 @@
# 管理节点配置包含vCenter和ESXi # 管理节点配置包含vCenter和ESXi
management_nodes: management_nodes:
# vCenter节点 # vCenter节点
- type: vcenter # 标记类型为vcenter # - type: vcenter # 标记类型为vcenter
name: vc1 # 节点名称(用于日志) # name: vc1 # 节点名称(用于日志)
host: 192.168.40.134 # 地址 # host: 192.168.40.134 # 地址
user: administrator@lan.com # user: administrator@lan.com
password: Root@2025 # password: Root@2025
max_delete_concurrent: 4 # 该节点最大并发删除数 # max_delete_concurrent: 4 # 该节点最大并发删除数
# ESXi节点未接入 vCenter 的 Esxi 主机) # ESXi节点未接入 vCenter 的 Esxi 主机)
- type: esxi # 标记类型为esxi - type: esxi # 标记类型为esxi
@@ -16,12 +16,11 @@ management_nodes:
password: Root@2025 password: Root@2025
max_delete_concurrent: 2 # ESXi性能较弱并发数可设小些 max_delete_concurrent: 2 # ESXi性能较弱并发数可设小些
# 另一个ESXi节点
- type: esxi - type: esxi
name: esxi2 name: esxi2
host: esxi2.example.com host: 192.168.40.135
user: root user: root
password: esxi2_password password: Root@2025
max_delete_concurrent: 2 max_delete_concurrent: 2
# 全局策略配置 # 全局策略配置

View File

@@ -1,125 +1,13 @@
from pyVmomi import vim from pyVmomi import vim
from pyVim.connect import SmartConnect, Disconnect from pyVim.connect import SmartConnect, Disconnect
from config.settings import MANAGEMENT_NODES from config.settings import MANAGEMENT_NODES
from datetime import timedelta
import pandas as pd import pandas as pd
import openpyxl
from openpyxl.styles import Border, Side, Font, PatternFill from openpyxl.styles import Border, Side, Font, PatternFill
from datetime import datetime, timedelta from datetime import datetime, timedelta
"""计算虚拟机的总磁盘大小(仅是虚拟磁盘的分配空间)"""
def get_virtual_disk_size(vm):
total_size = 0
for device in vm.config.hardware.device:
if isinstance(device, vim.vm.device.VirtualDisk):
# 获取每个虚拟磁盘的容量
total_size += device.capacityInKB / (1024 * 1024) # 转换为GB
return total_size
"""设置表格样式"""
"""获取所有虚拟机"""
def get_all_vms():
# 取第一个节点
node = MANAGEMENT_NODES[0]
# 连接vCenter
si = SmartConnect(
host=node['host'],
user=node['user'],
pwd=node['password'],
disableSslCertValidation=True
)
# 获取所有VM
content = si.RetrieveContent()
vm_view = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True
)
vm_list = []
for vm in vm_view.view:
# 初始化VM信息字典
vm_info = {
'name': vm.name,
'moId': vm._moId,
'powerState': str(vm.runtime.powerState),
'system': vm.config.guestFullName,
'ipAddress': vm.guest.ipAddress,
'hostName': vm.guest.hostName,
'vmPath': vm.config.files.vmPathName,
'Host': vm.runtime.host.name, # 拿到 Host 主机名
'snapshots': [], # 添加快照信息
'diskSpaceGB': get_virtual_disk_size(vm) # 添加虚拟机占用的磁盘空间
}
# 获取快照信息
if vm.snapshot is not None:
current_snapshot = vm.snapshot.currentSnapshot
root_snapshots = vm.snapshot.rootSnapshotList
# 处理根快照列表
for snapshot in root_snapshots:
snapshot_info = get_snapshot_info(snapshot)
vm_info['snapshots'].extend(snapshot_info)
# 添加当前快照ID如果有
if current_snapshot:
vm_info['currentSnapshotId'] = current_snapshot._moId
else:
vm_info['snapshots'] = None
vm_info['currentSnapshotId'] = None
vm_list.append(vm_info)
vm_view.Destroy()
Disconnect(si)
print(f"获取到 {len(vm_list)} 台VM")
return vm_list
"""递归获取快照信息"""
def get_snapshot_info(snapshot):
snapshot_list = []
# 当前快照信息
snapshot_info = {
'name': snapshot.name,
'description': snapshot.description,
'createTime': (snapshot.createTime + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'),
'state': str(snapshot.state),
'id': snapshot.id,
'moId': snapshot.snapshot._moId, # 快照的Managed Object ID
'sizeMB': snapshot.diskSizeMB if hasattr(snapshot, 'diskSizeMB') else None, # 快照大小
'quiesced': snapshot.quiesced if hasattr(snapshot, 'quiesced') else None, # 是否静默快照
'children': [] # 子快照
}
# 递归处理子快照
if snapshot.childSnapshotList:
for child in snapshot.childSnapshotList:
snapshot_info['children'].extend(get_snapshot_info(child))
snapshot_list.append(snapshot_info)
return snapshot_list
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)
"""表格样式"""
def style_sheet(sheet, is_old_data=None): def style_sheet(sheet, is_old_data=None):
"""设置工作表样式:列宽、边框、加粗标题、冻结首行和设置背景颜色""" """设置工作表样式:列宽、边框、加粗标题、冻结首行和设置背景颜色"""
# 定义边框样式 # 定义边框样式
@@ -156,17 +44,121 @@ def style_sheet(sheet, is_old_data=None):
cell.fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid') cell.fill = PatternFill(start_color='ADD8E6', end_color='ADD8E6', fill_type='solid')
"""获取虚拟机的总磁盘大小(仅是虚拟磁盘的分配空间)"""
def get_virtual_disk_size(vm):
total_size = 0
for device in vm.config.hardware.device:
if isinstance(device, vim.vm.device.VirtualDisk):
# 获取每个虚拟磁盘的容量
total_size += device.capacityInKB / (1024 * 1024) # 转换为GB
return total_size
"""获取所有虚拟机"""
def get_all_vms():
vm_list = []
for node in MANAGEMENT_NODES:
# 连接vCenter
si = SmartConnect(
host=node['host'],
user=node['user'],
pwd=node['password'],
disableSslCertValidation=True
)
# 获取所有VM
content = si.RetrieveContent()
vm_view = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True
)
for vm in vm_view.view:
# print(dir(vm)) # 输出所有可用的属性和方法
# print(vm.runtime)
# print(vm.summary)
# print(vm.snapshot)
# 初始化VM信息字典
# print(vars(vm.summary))
# print(vm.summary)
vm_info = {
'NodeHost': node['host'],
'name': vm.name,
'moId': vm._moId,
'powerState': str(vm.runtime.powerState),
'system': vm.config.guestFullName,
'ipAddress': vm.guest.ipAddress,
'hostName': vm.guest.hostName,
'vmPath': vm.config.files.vmPathName,
'Host': vm.runtime.host.name, # 拿到 Host 主机名,如果是从 Esxi 主机直接获取的可能拿不到正确信息
'snapshots': [], # 添加快照信息
'diskSpaceGB': get_virtual_disk_size(vm) # 添加虚拟机占用的磁盘空间
}
# print(vm_info)
# 获取快照信息
if vm.snapshot is not None:
current_snapshot = vm.snapshot.currentSnapshot
root_snapshots = vm.snapshot.rootSnapshotList
# 处理根快照列表
for snapshot in root_snapshots:
snapshot_info = get_snapshot_info(snapshot)
vm_info['snapshots'].extend(snapshot_info)
# 添加当前快照ID如果有
if current_snapshot:
vm_info['currentSnapshotId'] = current_snapshot._moId
else:
vm_info['snapshots'] = None
vm_info['currentSnapshotId'] = None
vm_list.append(vm_info)
# print(vm_info)
vm_view.Destroy()
Disconnect(si)
print(f"获取到 {len(vm_list)} 台VM")
return vm_list
"""递归获取快照信息"""
def get_snapshot_info(snapshot):
snapshot_list = []
# 当前快照信息
snapshot_info = {
'name': snapshot.name,
'description': snapshot.description,
'createTime': (snapshot.createTime + timedelta(hours=8)).strftime('%Y-%m-%d %H:%M:%S'), # + timedelta(hours=8) 是时间运算,转换为北京时间
'state': str(snapshot.state),
'id': snapshot.id,
'moId': snapshot.snapshot._moId, # 快照的Managed Object ID
# 'sizeMB': snapshot.diskSizeMB if hasattr(snapshot, 'diskSizeMB') else None, # 快照大小
'quiesced': snapshot.quiesced if hasattr(snapshot, 'quiesced') else None, # 是否静默快照
'children': [] # 子快照
}
# 递归处理子快照
if snapshot.childSnapshotList:
for child in snapshot.childSnapshotList:
snapshot_info['children'].extend(get_snapshot_info(child))
snapshot_list.append(snapshot_info)
return snapshot_list
"""将虚拟机和快照信息写入Excel文件并标记创建时间在15天前的快照""" """将虚拟机和快照信息写入Excel文件并标记创建时间在15天前的快照"""
def create_excel_report(vms): def create_excel_report(vms):
vm_data = [] vm_data = []
snapshot_data = [] snapshot_data = []
def add_snapshots_to_report(snapshot, vm_name): def add_snapshots(snapshot, vm_name):
"""递归将快照信息加入报告"""
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=1)) is_old = create_time < (datetime.now() - timedelta(days=1))
snapshot_data.append({ snapshot_data.append({
'NodeHost': vm['NodeHost'],
'VMName': vm_name, 'VMName': vm_name,
'Snapshot Name': snapshot['name'], 'Snapshot Name': snapshot['name'],
'Description': snapshot['description'], 'Description': snapshot['description'],
@@ -174,31 +166,34 @@ def create_excel_report(vms):
'State': snapshot['state'], 'State': snapshot['state'],
'ID': snapshot['id'], 'ID': snapshot['id'],
'MOID': snapshot['moId'], 'MOID': snapshot['moId'],
'Size (MB)': snapshot['sizeMB'], # 'Size(MB)': snapshot['sizeMB'],
'Quiesced': snapshot['quiesced'], 'Quiesced': snapshot['quiesced'],
'is_old': is_old 'is_old': is_old
}) })
"""递归将快照信息加入报告"""
for child in snapshot['children']: for child in snapshot['children']:
add_snapshots_to_report(child, vm_name) add_snapshots(child, vm_name)
for vm in vms: for vm in vms:
# print(vm['NodeHost'])
vm_data.append({ vm_data.append({
'NodeHost': vm['NodeHost'],
'VMName': vm['name'], 'VMName': vm['name'],
'MOID': vm['moId'], 'MOID': vm['moId'],
'PowerState': vm['powerState'], 'PowerState': vm['powerState'],
'System': vm['system'], 'System': vm['system'],
'IPAddress': vm['ipAddress'], 'IPAddress': vm['ipAddress'],
'HostName': vm['hostName'], 'HostName': vm['hostName'],
'VM Path': vm['vmPath'],
'Host': vm['Host'],
'CurrentSnapshotID': vm.get('currentSnapshotId', None), 'CurrentSnapshotID': vm.get('currentSnapshotId', None),
'DiskSpace/GB': vm['diskSpaceGB'] 'DiskSpace/GB': vm['diskSpaceGB'],
'Host': vm['Host'],
'VMPath': vm['vmPath']
}) })
if vm['snapshots']: if vm['snapshots']:
# print(vm['snapshots'])
for snapshot in vm['snapshots']: for snapshot in vm['snapshots']:
add_snapshots_to_report(snapshot, vm['name']) add_snapshots(snapshot, vm['name'])
vm_df = pd.DataFrame(vm_data) vm_df = pd.DataFrame(vm_data)
snapshot_df = pd.DataFrame(snapshot_data) snapshot_df = pd.DataFrame(snapshot_data)
@@ -222,16 +217,7 @@ def create_excel_report(vms):
if __name__ == '__main__': if __name__ == '__main__':
vms = get_all_vms() vms = get_all_vms()
# print(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("无快照")
# 生成Excel报告 # 生成Excel报告
create_excel_report(vms) create_excel_report(vms)