写在前面
接上篇,上篇分享了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()
写在最后
本次分享就到这里,希望本文可以帮助到有需要的盆友,关于代码,下面分享一下官方的参考资料。