上一篇文章提到了如何使用 python 和 vsphere 进行交互,我只能说我知道 govmomi 太晚了,否则怎么着都不会使用 python 的,毕竟 python 性能太差,并发用起来太蹩脚。在知道 govmomi 以后就有些蠢蠢欲动,因而抽空研究了下,并记录了下它的一些简单用法。html
govmomi 和 pyvmomi 虽然都是基于 VMware 的 api 实现的,可是因为语言的不一样,它们使用起来仍是区别很大的。除了性能上的优点以外,govmomi 不只可使用全部操纵虚拟机的功能,还支持从内容库直接部署虚拟机,这就比我以前文章使用 vsphere-automation-sdk-python + pyvmomi 这种蹩脚的用法要强不少了。不过 govmomi 既然支持内容库,没理由 pyvmomi 不支持,莫非是我孤陋寡闻了?不过这些都不是重点,重点是 govmomi。python
govmomi 其实用起来还好,可是坑必定是存在的,这个接下来会提到。咱们先安装它。git
推荐大家直接访问它的 github,上面有它的一些说明。上面也提到了安装的方式:github
go get -u github.com/vmware/govmomi
复制代码
颇有意思的是,govmomi 不只提供了这个第三方库,同时基于这个库还写了 govc
,vcsim
和 toolbox
这三个命令行工具,你彻底可使用它们在命令行管理你的 vsphere。在你安装以后,这三个工具的源码也都被你下载下来了。web
其中 govc 的源码是咱们须要密切关注的,由于 govmomi 自己能够说没有任何的示例告诉你如何使用,可是你须要的功能 govc 都提供了。因此你要实现什么功能就看 govc 对应的源码就好了。最重要的是,govc 源码的文件很是清晰明了,你要什么功能看对应的文件就行,这个接下来会讲到。vim
ok,安装安装后,咱们首先须要登陆 vsphere。api
govmomi 的登陆颇有特点,是我第一次见到的登陆类型,只能说不够直接,有些遮遮掩掩的感受,不是个人菜。bash
登陆的方式有两种,第一种是经过环境变量,它须要以下环境变量:markdown
GOVMOMI_URL
:vsphere ip;GOVMOMI_USERNAME
:用户名;GOVMOMI_PASSWORD
:密码;GOVMOMI_INSECURE
:是否进行证书校验。貌似还有其余环境变量,可是这四个是咱们登陆须要的。这种登陆方式更多的是测试性质的,没法正式使用。由于使用这种方式意味着你须要将密码写入环境变量,而且在你须要登陆多个 vsphere 时就没有办法了。固然,你能够尝试在代码内对环境变量进行修改,试他一把,但我推荐使用第二种方式。并发
第二种方式是经过 url 的方式,将用户名和密码都写入到 url 中。你们知道,http url 的定义是这样的:
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag> 复制代码
所以咱们是彻底能够将用户名和密码写入其中的。你可能担忧密码中包含 :
、/
这样的特殊字符,致使登陆失败。其实不要紧的,由于 govmomi 会将 url 解析为 url.URL,因此咱们只要本身构建一个 url.URL 就行。
url 的最终格式以下:
https://user:password@IP/sdk
复制代码
所以,咱们能够这么作:
u := &url.URL{ Scheme: "https", Host: ip, Path: "/sdk", } u.User = url.UserPassword(user, password) 复制代码
ok,url.URL 就构建完毕了,你可使用 fmt.Println(u)
直接将 url 打印出来,只不过密码颇有可能会被编码。而后使用它登陆:
ctx := context.Background() client, err := govmomi.NewClient(p.ctx, u, true) if err != nil { panic(err) } 复制代码
基本上全部 govmomi 操做都会接收 context 做为上下文,便于操做取消,所以首先须要建立一个 context 对象。
这里的 true 就是不进行证书验证,对应上面的 GOVMOMI_INSECURE
环境变量。client 就是登陆后的对象,这也是咱们下面全部操做的基础。当你有多个 vsphere 时,建立不一样的 url.URL 就能够登陆不一样的 vsphere。
完整写法:
package main import ( "context" "fmt" "github.com/vmware/govmomi" "net/url" ) const ( ip = "" user = "" password = "" ) func main() { u := &url.URL{ Scheme: "https", Host: ip, Path: "/sdk", } ctx := context.Background() u.User = url.UserPassword(user, password) client, err := govmomi.NewClient(ctx, u, true) if err != nil { panic(err) } fmt.Println(client) } 复制代码
在使用以前,咱们先使用 govc,先要了解它的功能。由于 govc 基本上实现了 govmomi 的全部功能,因此咱们彻底能够将 govc 的源码做为参考,当咱们须要实现相应的功能时,就能够直接看 govc 的源码。
首先编译 govc:
go build github.com/vmware/govmomi/govc/
复制代码
要经过定义环境变量来使用它,注意它的环境变量和上面的 govmomi 的环境变量的名称并不同(意思同样),不要搞混了。咱们首先定义环境变量:
export GOVC_URL="" export GOVC_USERNAME="" export GOVC_PASSWORD="" export GOVC_INSECURE="true" 复制代码
我只列出这四个我会用到的,更多的点击这里查看。
定义完成以后就可使用了,好比查找当前的 vsphere 上有哪些文件夹:
./govc find / -type f
复制代码
至于它有哪些功能呢?可使用 ./govc -h
列出。能够看到它的功能很是多,你想要查看对应功能的源码只须要去 govc 源码目录查找对应的文件就行,好比我要查看 host.shutdown
的源码,只须要查看 $GOPATH/src/github.com/vmware/govmomi/govc/host/shutdown.go
文件就行。
登陆成功以后,咱们通常作的就是查找虚拟机了,虚拟机的查找有多种方式,能够经过名称查、ip 查、文件夹路径查、uuid 查等。不一样的查找方式效果不一样,性能也不一样。虚拟机有两种表现形式,不一样方式查找到的虚拟机形式也不一样,不过它们能够相互进行转换。
首先进行最 low 的查找方式,这种就是遍历全部虚拟机,而后判断主机名是否相同。
// 这里的 c 就是上面登陆后 client 的 Client 属性 func findVMByName(ctx context.Context, c *vim25.Client, vmName string) { m := view.NewManager(c) v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) if err != nil { panic(err) } defer v.Destroy(ctx) // Retrieve summary property for all machines // Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.VirtualMachine.html var vms []mo.VirtualMachine err = v.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary"}, &vms) if err != nil { panic(err) } // Print summary per vm (see also: govc/vm/info.go) for _, vm := range vms { // 判断虚拟机名称是否相同,相同的话,vm 就是查找到主机 if vm.Summary.Config.Name == vmName { fmt.Printf("%s: %s\n", vm.Summary.Config.Name, vm.Summary.Config.GuestFullName) break } } } 复制代码
这样虚拟机就找到了,能够看到找到的虚拟机是 mo.VirtualMachine
类型,后续会提到如何对虚拟机进行修改。
使用 ip 查找须要虚拟机安装了 vmtools,且能够在 web 界面上可以看到它的 ip,所以虚拟机必须处于开机状态。
func findVMByIP(ctx context.Context, c *vim25.Client, ip string) { searchIndex := object.NewSearchIndex(c) // nil 是数据中心,你若是要指定的话,须要构造数据中心的结构体,这里就不指定了 // ip 是你虚拟机的 ip // true 的意思我没搞懂 reference, err := searchIndex.FindByIp(ctx, nil, ip, true) // 之因此只对 reference 进行判断而非对 err 是由于没有找到不算是 error // 也就是说 err 为 nil 并不表明就找到了,可是没找到 reference 必定为 nil if reference == nil { panic("vm not found") } // 这类的查找的对象都是 object.Reference,你须要经过对应的方法将其转换为相应的对象 // 好比虚拟机、文件夹、模板等~ vm := object.NewVirtualMachine(c, reference.Reference()) fmt.Println(vm) } 复制代码
此次找到的虚拟机类型是 *object.VirtualMachine
,而不是上面的 mo.VirtualMachine
,不过它们是能够相互转换的。
这是根据虚拟机所处的文件夹路径来查找,前提是你知道你的文件夹的路径。这个路径并非你在 web 界面上看到的,你看到的只是路径的一部分。那么如何才能知道具体的文件夹路径是啥呢?你可使用 govc 进行查看。
若是你的虚拟机在数据中心下面,没有放入任何文件夹,那么它的文件夹路径就是 /数据中心/vm
。
func findVMByPath(ctx context.Context, c *vim25.Client, folderPath, vmName string) { searchIndex := object.NewSearchIndex(c) // 经过 path.Join 将文件夹路径和虚拟机名称拼接成完整的虚拟机路径 reference, err := searchIndex.FindByInventoryPath(ctx, path.Join(folderPath, vmName)) if reference == nil { panic("vm not found") } vm := object.NewVirtualMachine(c, reference.Reference()) fmt.Println(vm) } 复制代码
虚拟机类型和上面相同,由于查找的方式相似。
uuid 查找是惟一的,查找方式和上面几乎同样,除非你知道虚拟机的 uuid,不然也没法使用。
func findVMByUuid(ctx context.Context, c *vim25.Client, uuid string) { searchIndex := object.NewSearchIndex(c) // 第一个 nil 指的是数据中心 // true 和以前的 true 意义同样,只不过我不知道是什么意思 // 最后一个 nil 我也不知道是啥意思 reference, err := searchIndex.FindByUuid(ctx, nil, uuid, true, nil) if reference == nil { panic("vm not found") } vm := object.NewVirtualMachine(c, reference.Reference()) fmt.Println(vm) } 复制代码
虚拟机类型和上面相同。固然虚拟机的查找方式确定不止这四种,这里只列出了经常使用的,有兴趣的能够了解其余的用法。
这里只是简单的列出了一些经常使用的功能,有其余需求的能够直接查看 govc 的源码,源码都还挺容易懂的。
第一种查找到的虚拟机类型和后面三种的不同,不一样的虚拟机类型有不一样的属性和方法,所以它们存在转换的需求。
转换的方式也很简单,首先是 mo.VirtualMachine
转换为 *object.VirtualMachine
:
func vmConv(c *vim25.Client, mvm mo.VirtualMachine) { vm := object.NewVirtualMachine(c, mvm.Reference()) fmt.Println(vm) } 复制代码
使用 mo.VirtualMachine
大多数是须要得到一些虚拟机相关的属性,以及配置参数。所以你想要将 *object.VirtualMachine
转化为 mo.VirtualMachine
能够这么作:
func x(ctx context.Context, vm *object.VirtualMachine, c *vim25.Client) { var mvm mo.VirtualMachine pc := property.DefaultCollector(c) // 若是想要所有属性,能够传一个空的字串切片 err := pc.RetrieveOne(ctx, vm.Reference(), []string{"runtime.host", "config.uuid"}, &mvm) } 复制代码
也能这么作:
func x(ctx context.Context, vm *object.VirtualMachine, c *vim25.Client) { var o mo.VirtualMachine if err := vm.Properties(ctx, vm.Reference(), []string{"config.uuid"}, &o); err != nil { panic(err) } } 复制代码
关机很简单,*object.VirtualMachine
对象自己就提供了关机方法。
func vmShutdown(ctx context.Context, vm *object.VirtualMachine) { // 第一个返回值是 task,我认为不必处理,若是你要处理的话能够接收后处理 _, err := vm.PowerOff(ctx) if err != nil { panic(err) } } 复制代码
删除也简单,*object.VirtualMachine
对象自己也提供了关机方法。
func vmDelete(ctx context.Context, vm *object.VirtualMachine) { // task 能够处理,也能够不处理 task, err := vm.Destroy(ctx) if err != nil { panic(err) } if task.Wait(ctx) != nil { panic(err) } } 复制代码
这方面比 pyvmomi 更简单。
func disconnectNic(ctx context.Context, vm *object.VirtualMachine) { devidelst, err := vm.Device(ctx) if err != nil { panic("获取虚拟机的设备列表失败," + err.Error()) } for _, device := range devidelst { switch device.(type) { case *types.VirtualVmxnet3: if devidelst.Disconnect(device) != nil { panic("断开网卡链接失败," + err.Error()) } if vm.EditDevice(p.ctx, device) != nil { panic("断开网卡链接失败," + err.Error()) } } } } 复制代码
将虚拟机移动到另外一个文件夹。
// 先经过目标文件夹来找到其文件夹对象,而后将虚拟机移动到这个对象中 func vmMove(ctx context.Context, destDir string, c *vim25.Client, vm *object.VirtualMachine, searchIndex *object.SearchIndex) { reference, _ := searchIndex.FindByInventoryPath(ctx, destDir) if reference == nil { panic("目标目录 " + destDir + " 不存在") } folder := object.NewFolder(c, reference.Reference()) task, err := folder.MoveInto(ctx, []types.ManagedObjectReference{vm.Reference()}) if err != nil { panic(err) } if err := task.Wait(ctx); err != nil { panic(err) } } 复制代码
就写这么多了,你们有须要的话,能够直接看 govc 的源码。