packer-cn/step_clone_vm.go

183 lines
4.3 KiB
Go

package main
import (
"github.com/vmware/govmomi"
"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"
"net/url"
)
type CloneParameters struct {
client *govmomi.Client
folder *object.Folder
resourcePool *object.ResourcePool
datastore *object.Datastore
vmSrc *object.VirtualMachine
ctx context.Context
vmName string
}
type StepCloneVM struct{
config *Config
success bool
}
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("start cloning...")
// Prepare entities: client (authentification), finder, folder, virtual machine
client, ctx, err := createClient(s.config.Url, s.config.Username, s.config.Password)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Set up finder
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, s.config.DCName)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
finder.SetDatacenter(dc)
// Get folder
folder, err := finder.FolderOrDefault(ctx, s.config.FolderName)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Get resource pool
pool, err := finder.ResourcePoolOrDefault(ctx, fmt.Sprintf("/%v/host/%v/Resources/%v", dc.Name(), s.config.Host, s.config.ResourcePool))
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// 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
}
}
// Get source VM
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
vm, err := cloneVM(&CloneParameters{
client: client,
folder: folder,
resourcePool: pool,
datastore: datastore,
vmSrc: vmSrc,
ctx: ctx,
vmName: s.config.VMName,
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("vm", vm)
state.Put("ctx", ctx)
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
poolRef := params.resourcePool.Reference()
// Creating specs for cloning
relocateSpec := types.VirtualMachineRelocateSpec{
Pool: &(poolRef),
}
if params.datastore != nil {
datastoreRef := params.datastore.Reference()
relocateSpec.Datastore = &datastoreRef
}
cloneSpec := types.VirtualMachineCloneSpec{
Location: relocateSpec,
PowerOn: false,
}
// 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.client.Client, info.Result.(types.ManagedObjectReference))
return vm, nil
}
func createClient(URL, username, password string) (*govmomi.Client, context.Context, error) {
// create context
ctx := context.TODO() // an empty, default context (for those, who is unsure)
// create a client
// (connected to the specified URL,
// logged in with the username-password)
u, err := url.Parse(URL) // create a URL object from string
if err != nil {
return nil, nil, err
}
u.User = url.UserPassword(username, password) // set username and password for automatical authentification
fmt.Println(u.String())
client, err := govmomi.NewClient(ctx, u,true) // creating a client (logs in with given uname&pswd)
if err != nil {
return nil, nil, err
}
return client, ctx, nil
}