上一篇文章提到了如何使用 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 的源碼。