esxlib/client/vm.go

205 lines
4.9 KiB
Go

package client
import (
"context"
"errors"
"fmt"
"path"
"regexp"
"strings"
"esxlib/pkg/sshutil"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type PowerState string
const (
PowerStateOff = PowerState("off")
PowerStateOn = PowerState("on")
PowerStateSuspended = PowerState("suspended")
PowerStateUndefined = PowerState("")
)
type VirtualMachine struct {
c *vim25.Client
sshAuth *sshutil.Auth
mo *mo.VirtualMachine
ref types.ManagedObjectReference
}
func (v *VirtualMachine) refresh() error {
pc := property.DefaultCollector(v.c)
refs := []types.ManagedObjectReference{v.ref}
var vms []mo.VirtualMachine
err := pc.Retrieve(context.Background(), refs, nil, &vms)
if err != nil {
return err
}
if len(vms) != 1 || vms[0].Name != v.mo.Name {
return errors.New("internal error (vms don't) match")
}
v.mo = &vms[0]
return nil
}
func (v *VirtualMachine) Update() error {
return v.refresh()
}
func (v *VirtualMachine) Name() string {
return v.mo.Name
}
func (v *VirtualMachine) UUID() string {
return v.mo.Config.Uuid
}
func (v *VirtualMachine) Id() string {
return v.mo.Summary.Vm.Value
}
func (v *VirtualMachine) InternalPath() (string, string, error) {
layout, err := v.GetFiles()
if err != nil {
panic(err)
}
for _, l := range layout.File {
if strings.HasSuffix(l.Name, ".vmx") {
re := regexp.MustCompile(`\[(.*?)\] (.*)`)
matches := re.FindStringSubmatch(l.Name)
if len(matches) == 3 {
return matches[1], path.Dir(matches[2]), nil
}
}
}
return "", "", fmt.Errorf("not found")
}
func (v *VirtualMachine) GetNetworkAddressV4() []string {
addresses := make([]string, 0)
for _, x := range v.mo.Config.ExtraConfig {
kv := x.GetOptionValue()
switch kv.Key {
case "guestinfo.local-ipv4":
addresses = append(addresses, kv.Value.(string))
}
}
return addresses
}
func (v *VirtualMachine) GetFiles() (*types.VirtualMachineFileLayoutEx, error) {
if err := v.refresh(); err != nil {
return nil, err
}
return v.mo.LayoutEx, nil
}
func (v *VirtualMachine) State() PowerState {
switch v.mo.Runtime.PowerState {
case types.VirtualMachinePowerStatePoweredOff:
return PowerStateOff
case types.VirtualMachinePowerStatePoweredOn:
return PowerStateOn
case types.VirtualMachinePowerStateSuspended:
return PowerStateSuspended
default:
return PowerStateUndefined
}
}
func (v *VirtualMachine) PowerOn(ctx context.Context) error {
if v.sshAuth != nil {
cmd := fmt.Sprintf("vim-cmd vmsvc/power.on %s", v.Id())
_, err := sshutil.CombinedOutput(sshutil.Auth{
User: v.sshAuth.User,
Password: v.sshAuth.Password,
Host: v.sshAuth.Host,
}, cmd)
return err
}
return fmt.Errorf("unsupported (requires esx ssh privs)")
}
func (v *VirtualMachine) PowerOff(ctx context.Context) error {
if v.sshAuth != nil {
cmd := fmt.Sprintf("vim-cmd vmsvc/power.off %s", v.Id())
_, err := sshutil.CombinedOutput(sshutil.Auth{
User: v.sshAuth.User,
Password: v.sshAuth.Password,
Host: v.sshAuth.Host,
}, cmd)
return err
}
return fmt.Errorf("unsupported (requires esx ssh privs)")
}
func (v *VirtualMachine) Suspend(ctx context.Context) error {
// Workaround: if you are running the FREE license of ESX, the power management API is not available to you
// IE: ServerFaultCode: Current license or ESXi version prohibits execution of the requested operation
// so as an alternative you can provide the SSH auth to the server and we do it the gross way
if v.sshAuth != nil {
cmd := fmt.Sprintf("vim-cmd vmsvc/power.suspend %s", v.Id())
_, err := sshutil.CombinedOutput(sshutil.Auth{
User: v.sshAuth.User,
Password: v.sshAuth.Password,
Host: v.sshAuth.Host,
}, cmd)
return err
}
// standard API method
req := types.SuspendVM_Task{
This: v.mo.Reference(),
}
res, err := methods.SuspendVM_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := object.NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}
func (v *VirtualMachine) Resume(ctx context.Context) error {
// Workaround: if you are running the FREE license of ESX, the power management API is not available to you
// IE: ServerFaultCode: Current license or ESXi version prohibits execution of the requested operation
// so as an alternative you can provide the SSH auth to the server and we do it the gross way
if v.sshAuth != nil {
cmd := fmt.Sprintf("vim-cmd vmsvc/power.on %s", v.Id())
_, err := sshutil.CombinedOutput(sshutil.Auth{
User: v.sshAuth.User,
Password: v.sshAuth.Password,
Host: v.sshAuth.Host,
}, cmd)
return err
}
// standard API method
req := types.PowerOnVM_Task{
This: v.mo.Reference(),
}
res, err := methods.PowerOnVM_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := object.NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}