Updated mark as template to be able to use --force

This commit is contained in:
bugbuilder 2017-08-14 23:00:19 -03:00
parent 5e1d241db4
commit 81272d1427
6 changed files with 141 additions and 182 deletions

View File

@ -97,11 +97,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
break
}
}
// In some occasions when the VM is mark as template it loses its configuration if it's done immediately
// after the ESXi creates it. If vSphere is given a few seconds this behavior doesn't reappear.
// In some occasions the VM state is powered on and if we immediately try to mark as template
// (after the ESXi creates it) it will fail. If vSphere is given a few seconds this behavior doesn't reappear.
ui.Message("Waiting 10s for VMWare vSphere to start")
time.Sleep(10 * time.Second)
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
if err != nil {
return nil, false, fmt.Errorf("Error connecting to vSphere: %s", err)
@ -120,14 +119,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
&stepCreateFolder{
Folder: p.config.Folder,
},
&stepFetchVm{
&stepMarkAsTemplate{
VMName: p.config.VMName,
Source: source,
},
&stepMarkAsTemplate{},
&stepMoveTemplate{
Folder: p.config.Folder,
},
}
runner := &multistep.BasicRunner{Steps: steps}

View File

@ -18,17 +18,17 @@ func (s *stepChooseDatacenter) Run(state multistep.StateBag) multistep.StepActio
cli := state.Get("client").(*govmomi.Client)
finder := find.NewFinder(cli.Client, false)
ui.Message("Choosing datacenter...")
dc, err := finder.DatacenterOrDefault(context.Background(), s.Datacenter)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
finder.SetDatacenter(dc)
state.Put("dcPath", dc.InventoryPath)
state.Put("finder", finder)
return multistep.ActionContinue
}

View File

@ -20,55 +20,66 @@ func (s *stepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
cli := state.Get("client").(*govmomi.Client)
dcPath := state.Get("dcPath").(string)
if s.Folder != "" {
ui.Say("Creating or checking destination folders...")
ui.Message("Creating or checking destination folders...")
base := path.Join(dcPath, "vm")
fullPath := path.Join(base, s.Folder)
si := object.NewSearchIndex(cli.Client)
base := path.Join(dcPath, "vm")
fullPath := path.Join(base, s.Folder)
si := object.NewSearchIndex(cli.Client)
var folders []string
var err error
var ref object.Reference
var folders []string
var err error
var ref object.Reference
// We iterate over the path starting with full path
// If we don't find it, we save the folder name and continue with the previous path
// The iteration ends when we find an existing path otherwise it throws error
for {
ref, err = si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
// We iterate over the path starting with full path
// If we don't find it, we save the folder name and continue with the previous path
// The iteration ends when we find an existing path otherwise it throws error
for {
ref, err = si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if ref == nil {
dir, folder := path.Split(fullPath)
fullPath = path.Clean(dir)
if fullPath == dcPath {
err = fmt.Errorf("vSphere base path %s not found", base)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if ref == nil {
dir, folder := path.Split(fullPath)
fullPath = path.Clean(dir)
if fullPath == dcPath {
err = fmt.Errorf("vSphere base path %s not found", base)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
folders = append(folders, folder)
} else {
break
}
folders = append(folders, folder)
} else {
break
}
}
root := ref.(*object.Folder)
if root, ok := ref.(*object.Folder); ok {
for i := len(folders) - 1; i >= 0; i-- {
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
root, err = root.CreateFolder(context.Background(), folders[i])
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
fullPath = path.Join(fullPath, folders[i])
}
root.SetInventoryPath(fullPath)
state.Put("folder", root)
} else {
err = fmt.Errorf("folder not found: '%v'", ref)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}

View File

@ -1,88 +0,0 @@
package vsphere_template
import (
"context"
"strings"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/vmware/govmomi/find"
)
type stepFetchVm struct {
VMName string
Source string
}
func (s *stepFetchVm) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
finder := state.Get("finder").(*find.Finder)
ui.Say("Fetching VM...")
if err := avoidOrphaned(finder, s.VMName); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
path := strings.Split(s.Source, "/vmfs/volumes/")[1]
i := strings.Index(path, "/")
storage := path[:i]
vmx := path[i:]
ds, err := finder.DatastoreOrDefault(context.Background(), storage)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
folder, err := finder.DefaultFolder(context.Background())
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
pool, err := finder.DefaultResourcePool(context.Background())
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
task, err := folder.RegisterVM(context.Background(), ds.Path(vmx), s.VMName, false, pool, nil)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err = task.Wait(context.Background()); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
vm, err := finder.VirtualMachine(context.Background(), s.VMName)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("vm", vm)
return multistep.ActionContinue
}
// When ESXi remove the VM, vSphere keep the VM as orphaned
func avoidOrphaned(f *find.Finder, vm_name string) error {
vm, err := f.VirtualMachine(context.Background(), vm_name)
if err != nil {
return err
}
return vm.Unregister(context.Background())
}
func (s *stepFetchVm) Cleanup(multistep.StateBag) {}

View File

@ -2,26 +2,117 @@ package vsphere_template
import (
"context"
"fmt"
"path"
"strings"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
)
type stepMarkAsTemplate struct{}
type stepMarkAsTemplate struct {
VMName string
Source string
}
func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*object.VirtualMachine)
cli := state.Get("client").(*govmomi.Client)
folder := state.Get("folder").(*object.Folder)
dcPath := state.Get("dcPath").(string)
ui.Say("Marking as a template...")
ui.Message("Marking as a template...")
if err := vm.MarkAsTemplate(context.Background()); err != nil {
vm, err := findRuntimeVM(cli, dcPath, s.VMName)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
host, err := vm.HostSystem(context.Background())
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err := vm.Unregister(context.Background()); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
source := strings.Split(s.Source, "/vmfs/volumes/")[1]
i := strings.Index(source, "/")
path := (&object.DatastorePath{
Datastore: source[:i],
Path: source[i:],
}).String()
if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
task, err := folder.RegisterVM(context.Background(), path, s.VMName, true, nil, host)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err = task.Wait(context.Background()); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
// We will use the virtual machine created by vmware-iso builder
func findRuntimeVM(cli *govmomi.Client, dcPath, name string) (*object.VirtualMachine, error) {
si := object.NewSearchIndex(cli.Client)
fullPath := path.Join(dcPath, "vm", "Discovered virtual machine", name)
ref, err := si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
return nil, err
}
if ref == nil {
return nil, fmt.Errorf("VM at path %s not found", fullPath)
}
return ref.(*object.VirtualMachine), nil
}
// If in the target folder a virtual machine or template already exists
// it will be removed to maintain consistency
func unregisterPreviousVM(cli *govmomi.Client, folder *object.Folder, name string) error {
si := object.NewSearchIndex(cli.Client)
fullPath := path.Join(folder.InventoryPath, name)
ref, err := si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
return err
}
if ref != nil {
if vm, ok := ref.(*object.VirtualMachine); ok {
return vm.Unregister(context.Background())
} else {
return fmt.Errorf("an object name '%v' already exists", name)
}
}
return nil
}
func (s *stepMarkAsTemplate) Cleanup(multistep.StateBag) {}

View File

@ -1,50 +0,0 @@
package vsphere_template
import (
"context"
"path"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
type stepMoveTemplate struct {
Folder string
}
func (s *stepMoveTemplate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
finder := state.Get("finder").(*find.Finder)
dcPath := state.Get("dcPath").(string)
vm := state.Get("vm").(*object.VirtualMachine)
if s.Folder != "" {
ui.Say("Moving template...")
folder, err := finder.Folder(context.Background(), path.Join(dcPath, "vm", s.Folder))
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
task, err := folder.MoveInto(context.Background(), []types.ManagedObjectReference{vm.Reference()})
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err = task.Wait(context.Background()); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *stepMoveTemplate) Cleanup(multistep.StateBag) {}