不背锅运维:VMware vSphere API玩法
写在前面
接上篇,上篇分享了openstack的api使用套路,本篇分享vmware的api使用套路,希望可以帮助到有需要的盆友。
在观看本文之前,也是最重要的一点,就是请确保你已经搭建好了用于测试的vsphere环境(esxi和vcenter)。
我的测试环境:
API文档阅读套路&实战
首次阅读vsphere api文档的话,可能会有点懵,下面我把最需要关注的点,以及阅读套路给说清楚,搞明白之后,其实也都够用了。
官方API文档: https://developer.vmware.com/apis
1. 关于vsphere的能力,我们更关注的是vSphere Web Services API
2. 根据vsphere版本选择相对应的API版本
3. 对象类型
如上图,All Types包含了下面几种类型,只是做了分类而已:
- Managed Object Types
- Data Object Types
- Enumerated Types
- Fault Types
Managed Object Types是最常用的,有虚拟机(VirtualMachine)、存储(Datastore)、宿主机(HostSystem)、网络(Network)等等,那平时怎么使用它呢?假设,当得到一个虚拟机实例对象时,想知道它都能获取到什么属性,那么就可以在Property进行查阅。
还有一个特别注意的地方,就是vim.VirtualMachine,它其实是某种对象类型的表示方法或者说是标识。比如,使用python的pyvmomi库,就需要指定查找的对象类型。如果是使用go,则是指定"VirtualMachine"
使用Go编码,获取虚拟机属性:
package main
import (
"context"
"flag"
"fmt"
"log"
"net/url"
"os"
"strings"
"github.com/vmware/govmomi/session/cache"
"github.com/vmware/govmomi/simulator"
"github.com/vmware/govmomi/view"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
)
func getEnvString(v string, def string) string {
r := os.Getenv(v)
if r == "" {
return def
}
return r
}
func getEnvBool(v string, def bool) bool {
r := os.Getenv(v)
if r == "" {
return def
}
switch strings.ToLower(r[0:1]) {
case "t", "y", "1":
return true
}
return false
}
const (
envURL = "GOVMOMI_URL"
envUserName = "GOVMOMI_USERNAME"
envPassword = "GOVMOMI_PASSWORD"
envInsecure = "GOVMOMI_INSECURE"
)
var urlDescription = fmt.Sprintf("ESX or vCenter URL [%s]", envURL)
var urlFlag = flag.String("url", getEnvString(envURL, ""), urlDescription)
var insecureDescription = fmt.Sprintf("Don't verify the server's certificate chain [%s]", envInsecure)
var insecureFlag = flag.Bool("insecure", getEnvBool(envInsecure, false), insecureDescription)
func processOverride(u *url.URL) {
envUsername := os.Getenv(envUserName)
envPassword := os.Getenv(envPassword)
if envUsername != "" {
var password string
var ok bool
if u.User != nil {
password, ok = u.User.Password()
}
if ok {
u.User = url.UserPassword(envUsername, password)
} else {
u.User = url.User(envUsername)
}
}
if envPassword != "" {
var username string
if u.User != nil {
username = u.User.Username()
}
u.User = url.UserPassword(username, envPassword)
}
}
func NewClient(ctx context.Context) (*vim25.Client, error) {
u, err := soap.ParseURL(*urlFlag)
if err != nil {
return nil, err
}
processOverride(u)
s := &cache.Session{
URL: u,
Insecure: *insecureFlag,
}
c := new(vim25.Client)
err = s.Login(ctx, c, nil)
if err != nil {
return nil, err
}
return c, nil
}
func Run(f func(context.Context, *vim25.Client) error) {
flag.Parse()
var err error
var c *vim25.Client
if *urlFlag == "" {
err = simulator.VPX().Run(f)
} else {
ctx := context.Background()
c, err = NewClient(ctx)
if err == nil {
err = f(ctx, c)
}
}
if err != nil {
log.Fatal(err)
}
}
const (
vimVirtualMachine = "VirtualMachine"
)
func a(ctx context.Context, c *vim25.Client) error {
m := view.NewManager(c)
v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{vimVirtualMachine}, true)
if err != nil {
return err
}
defer v.Destroy(ctx)
var vms []mo.VirtualMachine
err = v.Retrieve(ctx, []string{vimVirtualMachine}, []string{"summary"}, &vms)
if err != nil {
return err
}
for _, vm := range vms {
fmt.Println(vm.Summary.Guest.HostName, vm.Summary.Runtime.PowerState)
}
return nil
}
func main() {
Run(a)
}
设置好环境变量:
export GOVMOMI_URL="192.168.11.104"
export GOVMOMI_USERNAME="administrator@vsphere.local"
export GOVMOMI_PASSWORD="1qaz#EDC"
export GOVMOMI_INSECURE="y"
测试运行并输出结果:
[root@devhost vmware]# go run coll-vsphere.go
192.168.11.104 poweredOn
photon3-hdcs poweredOn
使用Python编码,获取虚拟机属性:
import ssl
import atexit
from pyVim.connect import SmartConnect, Disconnect
from pyVmomi import vim
def main():
si = None
ssl_context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
ssl_context.verify_mode = ssl.CERT_NONE
si = SmartConnect(
host="192.168.11.104",
user="administrator@vsphere.local",
pwd="1qaz#EDC",
port=int(443),
sslContext=ssl_context
)
atexit.register(Disconnect, si)
content = si.RetrieveContent()
container = content.rootFolder
viewType = [vim.VirtualMachine]
recursive = True
containerView = content.viewManager.CreateContainerView(
container, viewType, recursive)
children = containerView.view
for child in children:
# print(child.summary.config.name)
print(child.guest.hostName)
if __name__ == "__main__":
main()
运行并输出结果:
[root@devhost cloud-collect]# /usr/local/python2.7.13/bin/python vmcollect.py
192.168.11.104
photon3-hdcs
4. 方法
继续拿VirtualMachine对象来看看它都有哪些方法,通过文档可看到虚拟机对象支持很多方法,创建、克隆、开机、关机、添加磁盘、添加网卡等等。每一个方法都详细描述了所需要的参数。在每个具体的对象类型中,都描述了所支持的方法,这些方法也可以在“All Methods”里查到。
下面使用Python编码,从模板克隆虚拟,代码如下:
import atexit
from pyVmomi import vim
from pyVim.connect import SmartConnectNoSSL, Disconnect
vcenterhost = '192.168.11.104'
vcenteruser = 'administrator@vsphere.local'
vcenterpassword = '1qaz#EDC'
vcenterport = 443
templatename = 'centos7_ttr_temp'
vmname = 'DEMO-13'
parserCpu = 1
parserMem = 128
parserIpaddress = "192.168.11.90"
parserNetmask = "255.255.255.0"
parserGateway = "192.168.11.2"
parserDnsServer = ["8.8.8.8"]
parserDnsdomain = "local.com"
parserPortgroup = "VM Network"
parserDatacenter_name = 'Datacenter'
parserVm_folder = None
parserDatastore_name = 'datastore1'
parserCluster_name = 'DEMO环境'
parserResource_pool = None
parserPower_on = True
parserDatastorecluster_name = None
def wait_for_task(task):
print('任务创建时间 {}, 任务ID: {}'.format(task.info.queueTime, task.info.key))
while True:
if task.info.state == 'error':
print('创建过程中发生了错误或警告,消息: {}'.format(task.info.error))
print('创建进度 {}%'.format(task.info.progress)) # 如果任务状态为“正在运行”,则此属性包含进度度量,表示为从0到100的完成百分比。如果未设置此属性,则该命令不会报告进度。
if task.info.completeTime: # 判断进度条是否存在
print('创建任务进度终止,错误消息: {}'.format(task.info.error))
break
else:
if task.info.state == 'success':
print('虚拟机创建成功,VM对象: {}'.format(task.info.result))
break
def get_obj(content, vimtype, name):
"""
Return an object by name, if name is None the
first found object is returned
"""
obj = None
container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
for c in container.view:
if name:
if c.name == name:
obj = c
break
else:
obj = c
break
return obj
def clone_vm(content, template, vm_name, si, datacenter_name, vm_folder, datastore_name,
cluster_name, resource_pool, power_on, datastorecluster_name, is_cards):
"""
Clone a VM from a template/VM, datacenter_name, vm_folder, datastore_name
cluster_name, resource_pool, and power_on are all optional.
"""
# if none git the first one
datacenter = get_obj(content, [vim.Datacenter], datacenter_name)
if vm_folder:
destfolder = get_obj(content, [vim.Folder], vm_folder)
else:
destfolder = datacenter.vmFolder
if datastore_name:
datastore = get_obj(content, [vim.Datastore], datastore_name)
else:
datastore = get_obj(
content, [vim.Datastore], template.datastore[0].info.name)
# if None, get the first one
cluster = get_obj(content, [vim.ClusterComputeResource], cluster_name)
if resource_pool:
resource_pool = get_obj(content, [vim.ResourcePool], resource_pool)
else:
resource_pool = cluster.resourcePool
vmconf = vim.vm.ConfigSpec()
if datastorecluster_name:
podsel = vim.storageDrs.PodSelectionSpec()
pod = get_obj(content, [vim.StoragePod], datastorecluster_name)
podsel.storagePod = pod
storagespec = vim.storageDrs.StoragePlacementSpec()
storagespec.podSelectionSpec = podsel
storagespec.type = 'create'
storagespec.folder = destfolder
storagespec.resourcePool = resource_pool
storagespec.configSpec = vmconf
try:
rec = content.storageResourceManager.RecommendDatastores(
storageSpec=storagespec)
rec_action = rec.recommendations[0].action[0]
real_datastore_name = rec_action.destination.name
except:
real_datastore_name = template.datastore[0].info.name
datastore = get_obj(content, [vim.Datastore], real_datastore_name)
# set relospec
relospec = vim.vm.RelocateSpec()
relospec.datastore = datastore
relospec.pool = resource_pool
nic_changes = []
if is_cards:
# 编辑现有网卡连接指定的端口组
nic_prefix_label = 'Network adapter'
# nic_label = nic_prefix_label + str(1)
nic_label = 'Network adapter 1'
virtual_nic_device = None
for dev in template.config.hardware.device:
if isinstance(dev, vim.vm.device.VirtualEthernetCard) \
and dev.deviceInfo.label == nic_label:
virtual_nic_device = dev
virtual_nic_spec = vim.vm.device.VirtualDeviceSpec()
virtual_nic_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
virtual_nic_spec.device = virtual_nic_device
content = si.RetrieveContent()
network = get_obj(content, [vim.Network], parserPortgroup)
virtual_nic_spec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
virtual_nic_spec.device.backing.deviceName = parserPortgroup
virtual_nic_spec.device.backing.network = network
virtual_nic_spec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
virtual_nic_spec.device.connectable.startConnected = True
virtual_nic_spec.device.connectable.allowGuestControl = True
virtual_nic_spec.device.connectable.connected = True
virtual_nic_spec.device.connectable.status = 'untried'
nic_changes.append(virtual_nic_spec)
else:
# 添加网卡并设置连接端口组
nic_spec = vim.vm.device.VirtualDeviceSpec()
nic_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
nic_spec.device = vim.vm.device.VirtualVmxnet3()
nic_spec.device.deviceInfo = vim.Description()
nic_spec.device.deviceInfo.summary = 'vCenter API test'
content = si.RetrieveContent()
network = get_obj(content, [vim.Network], parserPortgroup)
nic_spec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
nic_spec.device.backing.deviceName = parserPortgroup
nic_spec.device.backing.network = network
nic_spec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
nic_spec.device.connectable.startConnected = True
nic_spec.device.connectable.allowGuestControl = True
nic_spec.device.connectable.connected = True
nic_spec.device.connectable.status = 'untried'
nic_spec.device.wakeOnLanEnabled = True
nic_spec.device.addressType = 'assigned'
nic_changes.append(nic_spec)
# set
vmconf = vim.vm.ConfigSpec(numCPUs=parserCpu, memoryMB=parserMem, deviceChange=nic_changes)
# Network adapter settings
adaptermap = vim.vm.customization.AdapterMapping()
globalip = vim.vm.customization.GlobalIPSettings()
adaptermap.adapter = vim.vm.customization.IPSettings()
adaptermap.adapter.ip = vim.vm.customization.FixedIp()
adaptermap.adapter.ip.ipAddress = parserIpaddress
adaptermap.adapter.subnetMask = parserNetmask
adaptermap.adapter.gateway = parserGateway
adaptermap.adapter.dnsDomain = parserDnsdomain
adaptermap.adapter.dnsServerList = parserDnsServer
# 主机名设置
ident = vim.vm.customization.LinuxPrep(domain=parserDnsdomain, hostName=vim.vm.customization.FixedName(name=vmname))
# 将所有这些部件放在一起以自定义规格
customspec = vim.vm.customization.Specification(nicSettingMap=[adaptermap], globalIPSettings=globalip,identity=ident)
clonespec = vim.vm.CloneSpec(customization=customspec, config=vmconf)
clonespec.location = relospec
clonespec.powerOn = power_on
task = template.Clone(folder=destfolder, name=vm_name, spec=clonespec)
wait_for_task(task)
def main():
si = SmartConnectNoSSL(host=vcenterhost, user=vcenteruser, pwd=vcenterpassword, port=vcenterport)
atexit.register(Disconnect, si)
content = si.RetrieveContent()
template = get_obj(content, [vim.VirtualMachine], templatename)
if template:
num_cards = template.summary.config.numEthernetCards # 获取网卡数量
if num_cards:
print('模板有网卡')
clone_vm(
content, template, vmname, si,
parserDatacenter_name, parserVm_folder,
parserDatastore_name, parserCluster_name,
parserResource_pool, parserPower_on, parserDatastorecluster_name, is_cards=True)
else:
print('模板无网卡')
clone_vm(
content, template, vmname, si,
parserDatacenter_name, parserVm_folder,
parserDatastore_name, parserCluster_name,
parserResource_pool, parserPower_on, parserDatastorecluster_name, is_cards=False)
else:
print("模板没有找到")
if __name__ == "__main__":
main()
写在最后
本次分享就到这里,希望本文可以帮助到有需要的盆友,关于代码,下面分享一下官方的参考资料。
Golang:
Python:
- https://pypi.org/project/pyvmomi/
- https://github.com/vmware/vsphere-automation-sdk-python
- https://github.com/vmware/pyvmomi-community-samples
本文转载于(喜欢的盆友关注我们):https://mp.weixin.qq.com/s/yb1CZ3NR8QjIUeMmcTM5mQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」