乐趣区

关于vmware:不背锅运维VMware-vSphere-API玩法

写在后面

接上篇,上篇分享了 openstack 的 api 应用套路,本篇分享 vmware 的 api 应用套路,心愿能够帮忙到有须要的盆友。

在观看本文之前,也是最重要的一点,就是请确保你曾经搭建好了用于测试的 vsphere 环境(esxi 和 vcenter)。

我的测试环境:

API 文档浏览套路 & 实战

首次浏览 vsphere api 文档的话,可能会有点懵,上面我把最须要关注的点,以及浏览套路给说分明,搞明确之后,其实也都够用了。

官网 API 文档:https://developer.vmware.com/…

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:

  • https://pkg.go.dev/github.com…
  • https://github.com/vmware/gov…

Python:

  • https://pypi.org/project/pyvm…
  • https://github.com/vmware/vsp…
  • https://github.com/vmware/pyv…

本文转载于(喜爱的盆友关注咱们):https://mp.weixin.qq.com/s/yb…

退出移动版