package client import ( "bufio" "bytes" "context" "encoding/base64" "errors" "fmt" "text/template" "esxlib/pkg/sshutil" "github.com/vmware/govmomi/find" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/soap" ) type VirtualMachines struct { ClientRef sshAuth *sshutil.Auth } func (vms *VirtualMachines) List(ctx context.Context) ([]*VirtualMachine, error) { m := view.NewManager(vms.VIM()) v, err := m.CreateContainerView(ctx, vms.VIM().ServiceContent.RootFolder, []string{"VirtualMachine"}, true) if err != nil { return nil, 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 moList []mo.VirtualMachine err = v.Retrieve(ctx, []string{"VirtualMachine"}, nil, &moList) if err != nil { return nil, err } vmList := make([]*VirtualMachine, 0) for idx := range moList { vmList = append(vmList, &VirtualMachine{ c: vms.VIM(), sshAuth: vms.sshAuth, ref: moList[idx].Reference(), mo: &moList[idx], }) } return vmList, nil } func (vms *VirtualMachines) Get(ctx context.Context, name string) (*VirtualMachine, error) { vmList, err := vms.List(ctx) if err != nil { return nil, err } for idx, vm := range vmList { if vm.Name() == name { return vmList[idx], nil } } return nil, errors.New("vm does not exist") } func (vms *VirtualMachines) Create(ctx context.Context, name string, memSize uint, diskSize uint, initScript []byte) (*VirtualMachine, error) { // we need some into about this ESX server from someplace // DestinationPath: /vmfs/volumes/datastore1 // SourcePath container := "datastore1" path := fmt.Sprintf("%s/%s.vmx", name, name) initScriptEncoded := "" if len(initScript) != 0 { initScriptEncoded = base64.StdEncoding.EncodeToString(initScript) } tmpl, err := template.New("ubuntu-lunar.cloudinit.tmpl").ParseFiles("configs/ubuntu-lunar.cloudinit.tmpl") if err != nil { panic(err) } config := struct { Name string Hostname string MemSizeMB uint CloudInitBase64 string InitScriptBase64 string }{ Name: name, Hostname: name, MemSizeMB: memSize, InitScriptBase64: initScriptEncoded, } var b bytes.Buffer w := bufio.NewWriter(&b) err = tmpl.Execute(w, config) if err != nil { panic(err) } err = w.Flush() if err != nil { panic(err) } cloudInitEncoded := base64.StdEncoding.EncodeToString(b.Bytes()) config.CloudInitBase64 = cloudInitEncoded fmt.Printf("%s\n", string(b.Bytes())) // Now encode the vm config tmpl, err = template.New("ubuntu-lunar.esx.tmpl").ParseFiles("configs/ubuntu-lunar.esx.tmpl") if err != nil { panic(err) } var upload bytes.Buffer w = bufio.NewWriter(&upload) err = tmpl.Execute(w, config) if err != nil { panic(err) } err = w.Flush() if err != nil { panic(err) } p := soap.DefaultUpload err = vms.Mkdir(ctx, container, name) if err != nil { panic(err) } finder := find.NewFinder(vms.VIM()) ds, err := finder.Datastore(ctx, container) if err != nil { return nil, err } err = ds.Upload(ctx, &upload, path, &p) if err != nil { return nil, err } err = vms.cloneImage(ctx, "/vmfs/volumes/nas011/iso/lunar-server-cloudimg-amd64.vmdk", fmt.Sprintf("/vmfs/volumes/%s/%s/%s.vmdk", container, name, name), diskSize) if err != nil { return nil, err } err = vms.registerVM(ctx, fmt.Sprintf("/vmfs/volumes/%s/%s/%s.vmx", container, name, name)) if err != nil { return nil, err } vm, err := vms.Get(ctx, name) return vm, err } func (vms *VirtualMachines) Destroy(ctx context.Context, name string) error { vm, err := vms.Get(ctx, name) if err != nil { return err } err = vm.PowerOff(ctx) if err != nil { return err } // delete the content datastore, dir, err := vm.InternalPath() if err != nil { return err } err = vms.unregisterVM(ctx, vm.Id()) if err != nil { return err } err = vms.wipe(ctx, datastore, dir) return err } func (vms *VirtualMachines) unregisterVM(ctx context.Context, id string) error { cmd := fmt.Sprintf("vim-cmd vmsvc/unregister %s", id) _, err := sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) if err != nil { fmt.Printf("cmd: %s\n", cmd) return err } return nil } func (vms *VirtualMachines) registerVM(ctx context.Context, vmxPath string) error { cmd := fmt.Sprintf("vim-cmd solo/registervm %s", vmxPath) _, err := sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) if err != nil { fmt.Printf("cmd: %s\n", cmd) return err } return nil } func (vms *VirtualMachines) cloneImage(ctx context.Context, src, dest string, size uint) error { cmd := fmt.Sprintf("vmkfstools --clonevirtualdisk %s --diskformat thin %s", src, dest) _, err := sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) if err != nil { fmt.Printf("cmd: %s\n", cmd) return err } cmd = fmt.Sprintf("vmkfstools -X %s %s", "20G", dest) _, err = sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) if err != nil { fmt.Printf("cmd: %s\n", cmd) return err } return nil } func (vms *VirtualMachines) wipe(ctx context.Context, datastore, dir string) error { if vms.sshAuth != nil { cmd := fmt.Sprintf("rm -rf /vmfs/volumes/%s/%s", datastore, dir) _, err := sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) fmt.Printf("cmd: %s\n", cmd) return err } else { panic("no ssh config") } return nil } func (vms *VirtualMachines) Mkdir(ctx context.Context, datastore, dir string) error { // you need a ESX payed license to create directories ... thats some bullshit */ if vms.sshAuth != nil { cmd := fmt.Sprintf("mkdir -p /vmfs/volumes/%s/%s", datastore, dir) _, err := sshutil.CombinedOutput(sshutil.Auth{ User: vms.sshAuth.User, Password: vms.sshAuth.Password, Host: vms.sshAuth.Host, }, cmd) fmt.Printf("cmd: %s\n", cmd) return err } else { panic("no ssh config") } return nil }