packer-cn/step_clone.go

184 lines
4.5 KiB
Go

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"
"github.com/vmware/govmomi/vim25/mo"
"errors"
)
type CloneConfig struct {
Template string `mapstructure:"template"`
FolderName string `mapstructure:"folder"`
VMName string `mapstructure:"vm_name"`
Host string `mapstructure:"host"`
ResourcePool string `mapstructure:"resource_pool"`
Datastore string `mapstructure:"datastore"`
LinkedClone bool `mapstructure:"linked_clone"`
}
func (c *CloneConfig) Prepare() []error {
var errs []error
if c.Template == "" {
errs = append(errs, fmt.Errorf("Template name is required"))
}
if c.VMName == "" {
errs = append(errs, fmt.Errorf("Target VM name is required"))
}
if c.Host == "" {
errs = append(errs, fmt.Errorf("vSphere host is required"))
}
return errs
}
type CloneParameters struct {
ctx context.Context
vmSrc *object.VirtualMachine
vmName string
folder *object.Folder
resourcePool *object.ResourcePool
datastore *object.Datastore
linkedClone bool
}
type StepCloneVM struct {
config *CloneConfig
}
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
ctx := state.Get("ctx").(context.Context)
finder := state.Get("finder").(*find.Finder)
datacenter := state.Get("datacenter").(*object.Datacenter)
ui := state.Get("ui").(packer.Ui)
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("vmSrc", vmSrc)
ui.Say("start cloning...")
folder, err := finder.FolderOrDefault(ctx, fmt.Sprintf("/%v/vm/%v", datacenter.Name(), s.config.FolderName))
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
pool, err := finder.ResourcePoolOrDefault(ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", datacenter.Name(), s.config.Host, s.config.ResourcePool))
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
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,
vmSrc: vmSrc,
vmName: s.config.VMName,
folder: folder,
resourcePool: pool,
datastore: datastore,
linkedClone: s.config.LinkedClone,
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("vm", vm)
return multistep.ActionContinue
}
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
if vm, ok := state.GetOk("vm"); ok {
ctx := state.Get("ctx").(context.Context)
ui := state.Get("ui").(packer.Ui)
ui.Say("Destroying VM...")
task, err := vm.(*object.VirtualMachine).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
poolRef := params.resourcePool.Reference()
// Creating specs for cloning
relocateSpec := types.VirtualMachineRelocateSpec{
Pool: &(poolRef),
}
if params.datastore != nil {
datastoreRef := params.datastore.Reference()
relocateSpec.Datastore = &datastoreRef
}
if params.linkedClone == true {
relocateSpec.DiskMoveType = "createNewChildDiskBacking"
}
cloneSpec := types.VirtualMachineCloneSpec{
Location: relocateSpec,
PowerOn: false,
}
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
}
// 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
}
vm = object.NewVirtualMachine(params.vmSrc.Client(), info.Result.(types.ManagedObjectReference))
return vm, nil
}