2017-05-09 10:23:57 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"github.com/mitchellh/multistep"
|
|
|
|
"github.com/vmware/govmomi/vim25/types"
|
|
|
|
"github.com/vmware/govmomi/object"
|
|
|
|
"github.com/hashicorp/packer/packer"
|
|
|
|
"github.com/vmware/govmomi/find"
|
|
|
|
"fmt"
|
2017-06-11 18:45:25 -04:00
|
|
|
"github.com/vmware/govmomi/vim25/mo"
|
|
|
|
"errors"
|
2017-05-09 10:23:57 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
type CloneParameters struct {
|
2017-05-31 04:38:50 -04:00
|
|
|
ctx context.Context
|
2017-06-27 23:04:25 -04:00
|
|
|
vmSrc *object.VirtualMachine
|
2017-05-31 04:38:50 -04:00
|
|
|
vmName string
|
2017-06-27 23:04:25 -04:00
|
|
|
folder *object.Folder
|
|
|
|
resourcePool *object.ResourcePool
|
|
|
|
datastore *object.Datastore
|
2017-06-11 18:45:25 -04:00
|
|
|
linkedClone bool
|
2017-05-09 10:23:57 -04:00
|
|
|
}
|
|
|
|
|
2017-06-27 23:04:25 -04:00
|
|
|
type StepCloneVM struct {
|
|
|
|
config *Config
|
2017-05-09 10:23:57 -04:00
|
|
|
success bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
|
2017-06-27 03:32:59 -04:00
|
|
|
ctx := state.Get("ctx").(context.Context)
|
|
|
|
finder := state.Get("finder").(*find.Finder)
|
|
|
|
dc := state.Get("dc").(*object.Datacenter)
|
|
|
|
vmSrc := state.Get("vmSrc").(*object.VirtualMachine)
|
2017-05-09 10:23:57 -04:00
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
ui.Say("start cloning...")
|
|
|
|
|
2017-05-31 04:38:50 -04:00
|
|
|
// Get folder
|
2017-05-09 10:23:57 -04:00
|
|
|
folder, err := finder.FolderOrDefault(ctx, s.config.FolderName)
|
|
|
|
if err != nil {
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
2017-05-31 04:38:50 -04:00
|
|
|
|
|
|
|
// Get resource pool
|
|
|
|
pool, err := finder.ResourcePoolOrDefault(ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", dc.Name(), s.config.Host, s.config.ResourcePool))
|
2017-05-09 10:23:57 -04:00
|
|
|
if err != nil {
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
2017-05-31 04:38:50 -04:00
|
|
|
// Get datastore
|
|
|
|
var datastore *object.Datastore = nil
|
|
|
|
if s.config.Datastore != "" {
|
|
|
|
datastore, err = finder.Datastore(ctx, s.config.Datastore)
|
|
|
|
if err != nil {
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vm, err := cloneVM(&CloneParameters{
|
|
|
|
ctx: ctx,
|
2017-06-27 23:04:25 -04:00
|
|
|
vmSrc: vmSrc,
|
2017-05-31 04:38:50 -04:00
|
|
|
vmName: s.config.VMName,
|
2017-06-27 23:04:25 -04:00
|
|
|
folder: folder,
|
|
|
|
resourcePool: pool,
|
|
|
|
datastore: datastore,
|
2017-06-11 18:45:25 -04:00
|
|
|
linkedClone: s.config.LinkedClone,
|
2017-05-31 04:38:50 -04:00
|
|
|
})
|
2017-05-09 10:23:57 -04:00
|
|
|
if err != nil {
|
|
|
|
state.Put("error", err)
|
|
|
|
return multistep.ActionHalt
|
|
|
|
}
|
|
|
|
|
|
|
|
state.Put("vm", vm)
|
|
|
|
s.success = true
|
|
|
|
return multistep.ActionContinue
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
|
|
|
|
if !s.success {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
|
|
|
_, halted := state.GetOk(multistep.StateHalted)
|
|
|
|
|
|
|
|
if cancelled || halted {
|
|
|
|
vm := state.Get("vm").(*object.VirtualMachine)
|
|
|
|
ctx := state.Get("ctx").(context.Context)
|
|
|
|
ui := state.Get("ui").(packer.Ui)
|
|
|
|
|
|
|
|
ui.Say("destroying VM...")
|
|
|
|
|
|
|
|
task, err := vm.Destroy(ctx)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = task.WaitForResult(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
ui.Error(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func cloneVM(params *CloneParameters) (vm *object.VirtualMachine, err error) {
|
|
|
|
vm = nil
|
|
|
|
err = nil
|
2017-05-31 04:38:50 -04:00
|
|
|
poolRef := params.resourcePool.Reference()
|
2017-05-09 10:23:57 -04:00
|
|
|
|
|
|
|
// Creating specs for cloning
|
2017-05-31 04:38:50 -04:00
|
|
|
relocateSpec := types.VirtualMachineRelocateSpec{
|
|
|
|
Pool: &(poolRef),
|
|
|
|
}
|
|
|
|
if params.datastore != nil {
|
|
|
|
datastoreRef := params.datastore.Reference()
|
|
|
|
relocateSpec.Datastore = &datastoreRef
|
|
|
|
}
|
2017-06-11 18:45:25 -04:00
|
|
|
if params.linkedClone == true {
|
|
|
|
relocateSpec.DiskMoveType = "createNewChildDiskBacking"
|
|
|
|
}
|
2017-05-31 04:38:50 -04:00
|
|
|
|
2017-05-09 10:23:57 -04:00
|
|
|
cloneSpec := types.VirtualMachineCloneSpec{
|
|
|
|
Location: relocateSpec,
|
|
|
|
PowerOn: false,
|
|
|
|
}
|
2017-06-11 18:45:25 -04:00
|
|
|
if params.linkedClone == true {
|
|
|
|
var vmImage mo.VirtualMachine
|
|
|
|
err = params.vmSrc.Properties(params.ctx, params.vmSrc.Reference(), []string{"snapshot"}, &vmImage)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Error reading base VM properties: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if vmImage.Snapshot == nil {
|
|
|
|
err = errors.New("`linked_clone=true`, but image VM has no snapshots")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cloneSpec.Snapshot = vmImage.Snapshot.CurrentSnapshot
|
|
|
|
}
|
2017-05-09 10:23:57 -04:00
|
|
|
|
|
|
|
// Cloning itself
|
|
|
|
task, err := params.vmSrc.Clone(params.ctx, params.folder, params.vmName, cloneSpec)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err := task.WaitForResult(params.ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-27 23:04:25 -04:00
|
|
|
vm = object.NewVirtualMachine(params.vmSrc.Client(), info.Result.(types.ManagedObjectReference))
|
2017-05-09 10:23:57 -04:00
|
|
|
return vm, nil
|
|
|
|
}
|