205 lines
4.9 KiB
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)
|
||
|
}
|