vmware的python自动化:获取集群内虚拟机信息
·
自动化需求
获取集群内虚拟机的信息,便于统计以及查询(支持多集群)
版本:
python3.11
pyvmomi 9.0.0.0
pyvim 3.0.3
代码
import ssl
import datetime
import concurrent.futures
import pandas as pd
from pyVim.connect import SmartConnect, Disconnect
from pyVmomi import vim
from pyVmomi import vmodl
# ================= 配置区域 =================
VC_CONFIGS = {
'192.168.1.250': {
'user': 'administrator@vsphere.local',
'pwd': 'xiaozhou@666.com'
},
# 示例:添加第二个vCenter
# '192.168.1.100': {
# 'user': 'administrator@vsphere.local',
# 'pwd': 'YourPasswordHere'
# }
}
# 导出文件名称
today = datetime.datetime.now().strftime("%Y%m%d")
OUTPUT_FILE = f"虚拟机巡检_{today}.xlsx"
# =======================================================================
def create_property_filter_spec(container, vim_type, properties):
"""创建 PropertyCollector 过滤器规范"""
obj_spec = vmodl.query.PropertyCollector.ObjectSpec()
obj_spec.obj = container
obj_spec.selectSet = [
vmodl.query.PropertyCollector.TraversalSpec(
name='traverseEntities',
path='view',
skip=False,
type=vim.view.ContainerView
)
]
prop_spec = vmodl.query.PropertyCollector.PropertySpec()
prop_spec.type = vim_type
prop_spec.pathSet = properties
filter_spec = vmodl.query.PropertyCollector.FilterSpec()
filter_spec.objectSet = [obj_spec]
filter_spec.propSet = [prop_spec]
return filter_spec
def get_cluster_name(host_mor, cluster_cache):
"""获取集群名称"""
if not host_mor:
return ''
if host_mor in cluster_cache:
return cluster_cache[host_mor]
try:
parent = host_mor.parent
while parent:
if isinstance(parent, vim.ClusterComputeResource):
cluster_name = parent.name
cluster_cache[host_mor] = cluster_name
return cluster_name
if hasattr(parent, 'parent'):
parent = parent.parent
else:
break
cluster_cache[host_mor] = ''
return ''
except Exception:
cluster_cache[host_mor] = ''
return ''
def calculate_uptime_days(vm, power_state, boot_time):
"""
【核心修复】多重方式计算虚拟机运行天数
优先使用 bootTime,失败时尝试其他方式
"""
if power_state != 'poweredOn':
return 0
# 方式1:使用 runtime.bootTime(首选)
if boot_time:
try:
if boot_time.tzinfo:
boot_time = boot_time.replace(tzinfo=None)
now = datetime.datetime.now()
delta = now - boot_time
days = round(delta.total_seconds() / 86400, 1)
if days > 0:
return days
except Exception as e:
pass
# 方式2:如果 bootTime 失败,尝试从 guest 获取信息
try:
if hasattr(vm, 'guest') and hasattr(vm.guest, 'toolsRunningStatus'):
# 如果 tools 正在运行,我们可以尝试其他方式
# 这里作为备选方案,暂时返回 None 表示无法获取
pass
except Exception:
pass
# 方式3:尝试使用 config.modified(最后修改时间,仅作参考)
try:
if hasattr(vm, 'config') and hasattr(vm.config, 'modified'):
modified_time = vm.config.modified
if modified_time:
if modified_time.tzinfo:
modified_time = modified_time.replace(tzinfo=None)
now = datetime.datetime.now()
delta = now - modified_time
days = round(delta.total_seconds() / 86400, 1)
# 注意:modified 不是启动时间,仅作为兜底
return days
except Exception:
pass
# 如果所有方式都失败,返回 None(在Excel中显示为空)
return None
def get_vm_ip(guest_net, guest_ip):
"""获取IP地址"""
try:
if guest_ip and guest_ip != '127.0.0.1':
return guest_ip
if guest_net:
for nic in guest_net:
if hasattr(nic, 'ipAddress') and nic.ipAddress:
for ip in nic.ipAddress:
if ip and not ip.startswith('127.') and not ip.startswith('169.254.'):
return ip
return ''
except Exception:
return ''
def get_datastore_names(datastores):
"""获取存储名称列表"""
try:
if not datastores:
return ''
ds_names = sorted(list({ds.name for ds in datastores}))
return ', '.join(ds_names)
except Exception:
return ''
def get_vm_tools_status(tools_status):
"""Tools状态映射"""
status_map = {
'toolsNotInstalled': '未安装',
'toolsNotRunning': '未运行',
'toolsOld': '版本过旧',
'toolsOk': '正常',
'toolsBlacklisted': '已黑名单'
}
return status_map.get(tools_status, '')
def get_power_state(power_state):
"""电源状态映射"""
state_map = {
'poweredOn': '已开机',
'poweredOff': '已关机',
'suspended': '已挂起'
}
return state_map.get(power_state, '')
def process_single_vc(vc_ip, vc_cred):
"""使用 PropertyCollector 处理单个 vCenter"""
print(f"正在连接 vCenter: {vc_ip} ...")
si = None
context = ssl._create_unverified_context()
all_vm_info = []
cluster_cache = {}
debug_count = {'boot_time_null': 0, 'total': 0}
try:
# 连接vCenter
si = SmartConnect(
host=vc_ip,
user=vc_cred['user'],
pwd=vc_cred['pwd'],
port=443,
sslContext=context
)
content = si.content
print(f"✅ 成功连接 vCenter: {vc_ip}")
# 创建 ContainerView
print(f"正在创建虚拟机视图...")
container = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True
)
# 【修改】增加 config.modified 作为备选时间
properties = [
'name',
'runtime.host',
'runtime.powerState',
'runtime.bootTime',
'guest.ipAddress',
'guest.net',
'guest.toolsStatus',
'config.hardware.memoryMB',
'config.hardware.numCPU',
'config.instanceUuid',
'config.annotation',
'config.modified', # 新增:最后修改时间作为备选
'datastore',
'summary.storage.committed'
]
# 创建 PropertyCollector 过滤器
filter_spec = create_property_filter_spec(container, vim.VirtualMachine, properties)
print(f"🚀 开始批量获取虚拟机属性 (PropertyCollector)...")
pc = content.propertyCollector
result = pc.RetrieveProperties(specSet=[filter_spec])
container.Destroy()
print(f"📊 {vc_ip} 共获取 {len(result)} 台虚拟机数据")
# 解析结果
print(f"正在解析数据...")
for i, obj_content in enumerate(result, 1):
try:
# 将属性转换为字典
props = {p.name: p.val for p in obj_content.propSet}
# 获取主机和集群
host_mor = props.get('runtime.host')
host_name = host_mor.name if host_mor else ''
cluster_name = get_cluster_name(host_mor, cluster_cache) if host_mor else ''
# 获取电源状态
power_state = props.get('runtime.powerState')
# 【核心修复】计算运行天数
debug_count['total'] += 1
boot_time = props.get('runtime.bootTime')
if power_state == 'poweredOn' and not boot_time:
debug_count['boot_time_null'] += 1
# 传递完整 props 给计算函数
uptime_days = calculate_uptime_days(
None, # 这里不需要完整 vm 对象
power_state,
boot_time
)
# 如果 bootTime 失败,尝试使用 config.modified
if uptime_days is None and power_state == 'poweredOn':
modified_time = props.get('config.modified')
if modified_time:
try:
if modified_time.tzinfo:
modified_time = modified_time.replace(tzinfo=None)
now = datetime.datetime.now()
delta = now - modified_time
uptime_days = round(delta.total_seconds() / 86400, 1)
except Exception:
pass
# 计算已用空间(GB)
committed_bytes = props.get('summary.storage.committed', 0)
used_space_gb = round(committed_bytes / (1024 ** 3), 2) if committed_bytes else 0
# 构建信息字典
vm_info = {
'vCenter IP': vc_ip,
'虚拟机名称': props.get('name', ''),
'所在集群': cluster_name,
'所在主机': host_name,
'电源状态': get_power_state(power_state),
'IP地址': get_vm_ip(props.get('guest.net'), props.get('guest.ipAddress')),
'内存大小(GB)': round(props.get('config.hardware.memoryMB', 0) / 1024, 2),
'CPU数量': props.get('config.hardware.numCPU', 0),
'UUID': props.get('config.instanceUuid', ''),
'备注信息': props.get('config.annotation', '').strip() if props.get('config.annotation') else '',
'vmtools状态': get_vm_tools_status(props.get('guest.toolsStatus')),
'运行天数': uptime_days, # 可能是数字或None
'存储名称': get_datastore_names(props.get('datastore')),
'已用空间(GB)': used_space_gb
}
all_vm_info.append(vm_info)
if i % 1000 == 0:
print(f" {vc_ip} 已解析: {i}/{len(result)}")
except Exception as e:
print(f" ⚠️ 解析第 {i} 台虚拟机时出错: {str(e)}")
continue
# 打印调试信息
print(f"\n📊 {vc_ip} 运行天数统计:")
print(f" 总计虚拟机: {debug_count['total']}")
print(f"✅ {vc_ip} 处理完成,共收集 {len(all_vm_info)} 台")
except Exception as e:
print(f"❌ 处理 vCenter {vc_ip} 失败: {str(e)}")
import traceback
traceback.print_exc()
finally:
if si:
Disconnect(si)
print(f"已断开 vCenter {vc_ip} 连接\n")
return all_vm_info
def main():
print("=" * 60)
print("🚀 多vCenter虚拟机信息收集工具")
print("=" * 60)
start_time = datetime.datetime.now()
total_vm_list = []
# 多 vCenter 并发处理
print(f"\n开始处理 {len(VC_CONFIGS)} 个 vCenter...")
with concurrent.futures.ThreadPoolExecutor(max_workers=len(VC_CONFIGS)) as executor:
future_to_vc = {
executor.submit(process_single_vc, vc_ip, vc_cred): vc_ip
for vc_ip, vc_cred in VC_CONFIGS.items()
}
for future in concurrent.futures.as_completed(future_to_vc):
vc_ip = future_to_vc[future]
try:
vc_vm_list = future.result()
total_vm_list.extend(vc_vm_list)
except Exception as e:
print(f"❌ vCenter {vc_ip} 处理异常: {str(e)}")
# 导出到Excel
if total_vm_list:
print(f"\n📝 正在导出 {len(total_vm_list)} 台虚拟机信息到 {OUTPUT_FILE} ...")
df = pd.DataFrame(total_vm_list)
# 按要求的列顺序排列
column_order = [
'vCenter IP', '虚拟机名称', '所在集群', '所在主机',
'电源状态', 'IP地址', '内存大小(GB)', 'CPU数量',
'UUID', '备注信息', 'vmtools状态', '运行天数', '存储名称', '已用空间(GB)'
]
df = df[column_order]
# 使用 openpyxl 引擎
with pd.ExcelWriter(OUTPUT_FILE, engine='openpyxl') as writer:
df.to_excel(writer, index=False, sheet_name='虚拟机清单')
# 调整列宽
worksheet = writer.sheets['虚拟机清单']
column_widths = {
'A': 15, 'B': 30, 'C': 20, 'D': 25, 'E': 10,
'F': 18, 'G': 12, 'H': 10, 'I': 35, 'J': 30,
'K': 12, 'L': 12, 'M': 40, 'N': 15
}
for col, width in column_widths.items():
worksheet.column_dimensions[col].width = width
end_time = datetime.datetime.now()
elapsed = (end_time - start_time).total_seconds()
print(f"✅ 导出完成!文件: {OUTPUT_FILE}")
print(f"⏱️ 总耗时: {elapsed:.2f} 秒")
else:
print("⚠️ 未收集到任何虚拟机信息")
if __name__ == "__main__":
main()
使用方法
在代码内填入vc的有关信息,然后运行脚本即可得到虚拟机信息的xlsx文件
200台虚拟机获取速度约1s

获取的信息包括vCenter IP 虚拟机名称 所在集群 所在主机 电源状态 IP地址 内存大小(GB) CPU数量 UUID 备注信息 vmtools状态 运行天数 存储名称 已用空间(GB)
效果如下图:

脚本为ai编写,使用前请使用测试环境进行测试
更多推荐
所有评论(0)