vsphere golang sdk govmomi 使用指南

上一篇文章提到了如何使用 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 不只提供了這個第三方庫,同時基於這個庫還寫了 govcvcsimtoolbox 這三個命令行工具,你徹底可使用它們在命令行管理你的 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,先要了解它的功能。由於 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 查找

使用 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 查找是惟一的,查找方式和上面幾乎同樣,除非你知道虛擬機的 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 的源碼。

相關文章
相關標籤/搜索