vSphere通 python API接口创建虚拟机及修改配置
背景:跟OA流程打通,自动化创建虚拟机,减少系统工程师重复工作时间,通过vSphere 提供的接口创建虚拟机,笔者通过 vsphere-automation-sdk-python 创建虚拟机,pyvmomi 给VM添加磁盘,打开VM电源,获取虚拟机IP地址
笔者系统环境:windows
python版本:python3.8

完整代码如下:
import urllib3
import requests
import time
import calendar
from com.vmware.vcenter.ovf_client import LibraryItem, DiskProvisioningType, ImportFlag
from com.vmware.vcenter_client import Folder, ResourcePool, Cluster, Network, Datastore
from vmware.vapi.vsphere.client import create_vsphere_client
from com.vmware.content_client import Library, LibraryModel, LocalLibrary, SubscribedLibrary
from com.vmware.content.library_client import Item, ItemModel, StorageBacking, SubscribedItem
from pyVim.connect import SmartConnectNoSSL
from pyVmomi import vim
def create_vm(user, ip, password, network_name, cluster_name,datastore_name, folder_name, content_library_name,
template_name, vm_name, annotation):
""" 通过 vsphere-automation-sdk-python 从模板库复制虚拟机 """
session = requests.session()
session.verify = False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
client = create_vsphere_client(server=ip, username=user, password=password, session=session)
stub_config = client._stub_config
library_item = LibraryItem(stub_config)
network_id = client.vcenter.Network.list(
Network.FilterSpec(names={network_name},
types={Network.Type.DISTRIBUTED_PORTGROUP}))[0].network
cluster_id = client.vcenter.Cluster.list(Cluster.FilterSpec(names={cluster_name}))[0].cluster
resource_pool_id = client.vcenter.ResourcePool.list(ResourcePool.FilterSpec(clusters={cluster_id}))[
0].resource_pool
datastore_id = client.vcenter.Datastore.list(Datastore.FilterSpec(names={datastore_name}))[0].datastore
library_service = Library(stub_config)
library_id = library_service.find(Library.FindSpec(name=content_library_name))[0]
library_item_service = Item(stub_config)
library_item_id = library_item_service.find(Item.FindSpec(name=template_name, library_id=library_id))[0]
ovf_lib_item_service = LibraryItem(stub_config)
folder_id = client.vcenter.Folder.list(Folder.FilterSpec(names={folder_name}))[0].folder
deployment_target = library_item.DeploymentTarget(resource_pool_id=resource_pool_id, folder_id=folder_id)
# ovf_lib_item_service.filter(library_item_id, deployment_target)
storage_group_mapping = ovf_lib_item_service.StorageGroupMapping(
type=ovf_lib_item_service.StorageGroupMapping.Type('DATASTORE'),
datastore_id=datastore_id,
provisioning=DiskProvisioningType('thin')
)
deployment_spec = library_item.ResourcePoolDeploymentSpec(
name=vm_name,
annotation=annotation,
accept_all_eula=True,
network_mappings={"Your configuration": network_id},
storage_mappings={"Your configuration": storage_group_mapping},
)
# Execute this statement and start deploying. It will last for about 2 minutes and you can see the specific progress on VMware.
print("开始从内容库复制虚拟机,请稍等......")
result = library_item.deploy(library_item_id, deployment_target, deployment_spec)
return result
def get_vm_state(user, ip, password, vm_name):
session = requests.session()
session.verify = False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
client = create_vsphere_client(server=ip, username=user, password=password, session=session)
vm = client.vcenter.VM.list(client.vcenter.VM.FilterSpec(names={vm_name}))[0]
vm_state = client.vcenter.vm.Power.get(vm.vm).state
if vm_state == 'POWERED_OFF':
client.vcenter.vm.Power.start(vm.vm)
print(f'虚拟机 {vm_name} 现在启动')
time.sleep(30)
return f'{vm_name} 现在启动'
return f'{vm_name} 电源状态已开启'
def get_vm_network(network_name):
""" 获取指定网段, vlan-22-10.0.22.* 为分布式交换机VLAN名称 """
network_name = str.lower(network_name)
if network_name == "10.0.22.0/24":
return "vlan-22-10.0.22.*"
if network_name == "10.0.23.0/24":
return "VLAN-10"
return "子网不存在,请先创建子网掩码"
def get_vm_cluster_name(cluster_name):
""" 获取数据中心位置,目前只有一个方便后期扩容 """
cluster_name = str.lower(cluster_name)
if cluster_name == "shenzhen":
return "shenzhen"
return "集群名称不存在,请先创建集群"
def get_vm_datastore_name(datastore_name):
""" 获取存储位置,目前只有一个方便后期扩容 """
datastore_name = str.lower(datastore_name)
if datastore_name == "datastore1":
return "datastore1"
return "存储池不存在,请先创建存储池"
def get_vm_folder_name(folder_name):
""" 获取虚拟机存放位置,目前只有一个方便后期扩容 """
folder_name = str(folder_name)
if folder_name == "EID":
return "EID"
return "虚拟机文件不存在,请先创建虚拟机文件"
def get_vm_template_name(template_name):
""" 获取数据库模板,返回模板名称及模板账户密码 """
template_name = str.lower(template_name)
if template_name == "centos":
return {'system': "contos7-01-temp",
'system_user': 'root',
'system_pwd': '123.com',
}
if template_name == "ubuntu":
return {'system': "Ubuntu-temp",
'system_user': 'root',
'system_pwd': '123@com',
}
if template_name == "windows":
return {'system': "windows-temp",
'system_user': 'administrator',
'system_pwd': '123#com',
}
return "系统模板不存在,请先创建模板"
class VmManage(object):
def __init__(self, host, user, password, port, ssl):
self.config = None
self.host = host
self.user = user
self.pwd = password
self.port = port
self.sslContext = ssl
try:
self.client = SmartConnectNoSSL(host=host,
user=user,
pwd=password,
port=443
)
self.content = self.client.RetrieveContent()
self.result = True
except Exception as e:
self.result = e
def wait_for_task(self, task):
""" wait for a vCenter task to finish """
task_done = False
while not task_done:
print("task.....%s " % task.info.state)
time.sleep(2)
if task.info.state == 'success':
return {'message': u'执行成功', 'status': True}
if task.info.state == 'error':
print("there was an error", f'message:{task.info.error.msg}')
return {'message': task.info.error.msg, 'status': True}
def _get_all_objs(self, obj_type, folder=None):
"""
根据对象类型获取这一类型的所有对象
"""
if folder is None:
container = self.content.viewManager.CreateContainerView(self.content.rootFolder, obj_type, True)
else:
container = self.content.viewManager.CreateContainerView(folder, obj_type, True)
return container.view
def _get_obj(self, obj_type, name):
"""
根据对象类型和名称来获取具体对象
"""
obj = None
content = self.client.RetrieveContent()
container = content.viewManager.CreateContainerView(content.rootFolder, obj_type, True)
for c in container.view:
if c.name == name:
obj = c
break
return obj
def add_disk(self, vm_obj, capacity):
spec = vim.vm.ConfigSpec()
dev_changes = []
# capacity 为 存储盘容量将单位改为 G
new_disk_kb = capacity * 1024 * 1024
unit_number = 0
# 遍历所有的硬件设备,找合适的位置添加
for dev in vm_obj.config.hardware.device:
if hasattr(dev.backing, 'fileName'):
unit_number = int(dev.unitNumber) + 1
# unit_number 7 reserved for scsi controller
if unit_number == 7:
unit_number += 1
if unit_number >= 16:
logging.error('we don\'t support this many disks')
if isinstance(dev, vim.vm.device.VirtualSCSIController):
controller = dev
disk_spec = vim.vm.device.VirtualDeviceSpec()
disk_spec.fileOperation = "create"
disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
disk_spec.device = vim.vm.device.VirtualDisk()
disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
disk_spec.device.backing.thinProvisioned = True
disk_spec.device.backing.diskMode = 'persistent'
disk_spec.device.unitNumber = unit_number
disk_spec.device.capacityInKB = new_disk_kb
disk_spec.device.controllerKey = controller.key
dev_changes.append(disk_spec)
spec.deviceChange = dev_changes
task = vm_obj.ReconfigVM_Task(spec=spec)
result1 = self.wait_for_task(task)
if result1['status']:
data = {'message': u'硬盘添加成功', 'result': True}
else:
data = {'message': u'硬盘添加失败: %s' % result1['message'], 'result': False}
return data
def change_vm_cpu_and_memory(self, vm_obj, cpu, memory ):
cspec = vim.vm.ConfigSpec()
cspec.numCPUs = cpu
# 将 4 核心分配在两个插槽,一个插槽两核心
# cspec.numCoresPerSocket = int(cspec.numCPUs / 2)
# 8G
cspec.memoryMB = 1024 * memory
# 启动 cpu 热添加
cspec.cpuHotAddEnabled = True
# 启动内存在线扩缩
cspec.memoryHotAddEnabled = True
task = vm_obj.Reconfigure(cspec)
result1 = self.wait_for_task(task)
if result1['status']:
data = {'message': u'cpu 和 内存变更成功', 'result': True}
else:
data = {'message': u'cpu 和 内存变更添加失败: %s' % result1['message'], 'result': False}
return data
def get_vm_ip(self, vm_obj ):
""" 获取 VM 所有 IP 地址 """
ips = []
for i in vm_obj.guest.net:
if i.network:
for b in range(6):
if i.ipAddress:
ips.extend(i.ipAddress)
return ips
else:
print("VM 没有获取到IP地址等待10秒")
time.sleep(10)
else:
print('VM 未配置网卡')
return 'VM 未获取到IP地址,请检测网络状态或者 DHCP 服务器'
def main(cluster_name, network_name, template_name, capacity, cpu,memory, annotation, datastore_name, folder_name,
content_library_name,):
""" user为vSphere账户,password为密码, ip为登录地址, port为端口号 """
user = 'Administrator@TEST-VSPHERE.COM'
ip = '192.168.3.100'
password = '123.com'
port = 443
# 虚拟机名称为 vm- 加上 时间戳
vm_name = f"vm-{calendar.timegm(time.gmtime())}"
network_name = get_vm_network(network_name)
cluster_name = get_vm_cluster_name(cluster_name)
datastore_name = get_vm_datastore_name(datastore_name)
folder_name = get_vm_folder_name(folder_name)
template_name = get_vm_template_name(template_name)
vm_info = {
"子网段": network_name,
"集群名称": cluster_name,
"datastore_name": datastore_name,
"folder_name": folder_name,
"template_name": template_name['system'],
"template_user": template_name['system_user'],
"template_pwd": template_name['system_pwd'],
"annotation": annotation,
}
print(vm_info)
# 记录当前时间
now = time.time()
try:
# 通过 vsphere-automation-sdk 开始从内容库复制虚拟机
create_vm_01 = create_vm(user, ip, password, network_name, cluster_name, datastore_name, folder_name,
content_library_name, template_name['system'], vm_name, annotation)
print(create_vm_01)
# pyvmomi 登录设备
vm = VmManage(host=ip,
user=user,
password=password,
port=port, ssl=None)
vm_obj = vm._get_obj([vim.VirtualMachine], vm_name)
# 虚拟机添加硬盘
vm_obj_add_disk = vm.add_disk(vm_obj, capacity)
print(vm_obj_add_disk)
# 改变虚拟机 cpu 和 内存
vm_obj_cpu_memory = vm.change_vm_cpu_and_memory(vm_obj, cpu, memory)
print(vm_obj_cpu_memory)
# 虚拟机开机,等待60秒,待虚拟机开机启动获取IP地址
task = vm_obj.PowerOn()
print("启动 VM 电源")
print(vm.wait_for_task(task))
time.sleep(60)
# 获取IP地址
print("开始获取 VM IP地址")
vm_obj_ip = vm.get_vm_ip(vm_obj)
# print(vm_obj_ip)
vm_uid = str(vm_obj).replace('\'', '').split(':')[-1]
results = {
"errcode": 0,
"errmsg": "ok",
'vm_id': vm_uid,
'vm_name': vm_name,
'ip_add': vm_obj_ip[0],
'vm_user': template_name['system_user'],
'vm_password': template_name['system_pwd'],
}
# 计算虚拟机创建消耗时间
print(time.time() - now)
except Exception as err:
raise err
else:
# save_log(results)
return results
安装依赖, 需要先安装git
""" 安装依赖 """
pip install requests
pip install urllib3
pip install chinesecalendar
pip install pyvmomi
# 安装SDK
pip install --upgrade setuptools==60.10.0
pip install --upgrade git+https://github.com/vmware/vsphere-automation-sdk-python.git
笔者发现一个问题,通过pyvmomi修改虚拟机的IP地址后脚本没有问题,但是虚拟机IP地址未修改且网卡切换至未连接状态,研究了几天没有解决相关问题,只能妥协使用DHCP服务下发地址,如有大佬有相关解决方案可以给笔者一些建议跟思路。
笔者脚本能力有限写的不好望大佬勿喷。
参考:
https://github.com/vmware/pyvmomi-community-samples
https://github.com/vmware/vsphere-automation-sdk-python
https://juejin.cn/post/6844903870561271822