Updated mark as template to be able to use --force
This commit is contained in:
parent
5e1d241db4
commit
81272d1427
|
@ -97,11 +97,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// In some occasions when the VM is mark as template it loses its configuration if it's done immediately
|
// In some occasions the VM state is powered on and if we immediately try to mark as template
|
||||||
// after the ESXi creates it. If vSphere is given a few seconds this behavior doesn't reappear.
|
// (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")
|
ui.Message("Waiting 10s for VMWare vSphere to start")
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
||||||
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
|
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, fmt.Errorf("Error connecting to vSphere: %s", err)
|
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{
|
&stepCreateFolder{
|
||||||
Folder: p.config.Folder,
|
Folder: p.config.Folder,
|
||||||
},
|
},
|
||||||
&stepFetchVm{
|
&stepMarkAsTemplate{
|
||||||
VMName: p.config.VMName,
|
VMName: p.config.VMName,
|
||||||
Source: source,
|
Source: source,
|
||||||
},
|
},
|
||||||
&stepMarkAsTemplate{},
|
|
||||||
&stepMoveTemplate{
|
|
||||||
Folder: p.config.Folder,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runner := &multistep.BasicRunner{Steps: steps}
|
runner := &multistep.BasicRunner{Steps: steps}
|
||||||
|
|
|
@ -18,17 +18,17 @@ func (s *stepChooseDatacenter) Run(state multistep.StateBag) multistep.StepActio
|
||||||
cli := state.Get("client").(*govmomi.Client)
|
cli := state.Get("client").(*govmomi.Client)
|
||||||
finder := find.NewFinder(cli.Client, false)
|
finder := find.NewFinder(cli.Client, false)
|
||||||
|
|
||||||
|
ui.Message("Choosing datacenter...")
|
||||||
|
|
||||||
dc, err := finder.DatacenterOrDefault(context.Background(), s.Datacenter)
|
dc, err := finder.DatacenterOrDefault(context.Background(), s.Datacenter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
|
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
|
|
||||||
finder.SetDatacenter(dc)
|
|
||||||
state.Put("dcPath", dc.InventoryPath)
|
state.Put("dcPath", dc.InventoryPath)
|
||||||
state.Put("finder", finder)
|
|
||||||
return multistep.ActionContinue
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,55 +20,66 @@ func (s *stepCreateFolder) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
cli := state.Get("client").(*govmomi.Client)
|
cli := state.Get("client").(*govmomi.Client)
|
||||||
dcPath := state.Get("dcPath").(string)
|
dcPath := state.Get("dcPath").(string)
|
||||||
|
|
||||||
if s.Folder != "" {
|
ui.Message("Creating or checking destination folders...")
|
||||||
ui.Say("Creating or checking destination folders...")
|
|
||||||
|
|
||||||
base := path.Join(dcPath, "vm")
|
base := path.Join(dcPath, "vm")
|
||||||
fullPath := path.Join(base, s.Folder)
|
fullPath := path.Join(base, s.Folder)
|
||||||
si := object.NewSearchIndex(cli.Client)
|
si := object.NewSearchIndex(cli.Client)
|
||||||
|
|
||||||
var folders []string
|
var folders []string
|
||||||
var err error
|
var err error
|
||||||
var ref object.Reference
|
var ref object.Reference
|
||||||
|
|
||||||
// We iterate over the path starting with full path
|
// 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
|
// 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
|
// The iteration ends when we find an existing path otherwise it throws error
|
||||||
for {
|
for {
|
||||||
ref, err = si.FindByInventoryPath(context.Background(), fullPath)
|
ref, err = si.FindByInventoryPath(context.Background(), fullPath)
|
||||||
if err != nil {
|
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)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
return multistep.ActionHalt
|
||||||
}
|
}
|
||||||
if ref == nil {
|
|
||||||
dir, folder := path.Split(fullPath)
|
|
||||||
fullPath = path.Clean(dir)
|
|
||||||
|
|
||||||
if fullPath == dcPath {
|
folders = append(folders, folder)
|
||||||
err = fmt.Errorf("vSphere base path %s not found", base)
|
} else {
|
||||||
state.Put("error", err)
|
break
|
||||||
ui.Error(err.Error())
|
|
||||||
return multistep.ActionHalt
|
|
||||||
}
|
|
||||||
|
|
||||||
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-- {
|
for i := len(folders) - 1; i >= 0; i-- {
|
||||||
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
|
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
|
||||||
|
|
||||||
root, err = root.CreateFolder(context.Background(), folders[i])
|
root, err = root.CreateFolder(context.Background(), folders[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Put("error", err)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
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
|
return multistep.ActionContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {}
|
|
|
@ -2,26 +2,117 @@ package vsphere_template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/mitchellh/multistep"
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/vmware/govmomi"
|
||||||
"github.com/vmware/govmomi/object"
|
"github.com/vmware/govmomi/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stepMarkAsTemplate struct{}
|
type stepMarkAsTemplate struct {
|
||||||
|
VMName string
|
||||||
|
Source string
|
||||||
|
}
|
||||||
|
|
||||||
func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *stepMarkAsTemplate) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
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)
|
state.Put("error", err)
|
||||||
ui.Error(err.Error())
|
ui.Error(err.Error())
|
||||||
return multistep.ActionHalt
|
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
|
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) {}
|
func (s *stepMarkAsTemplate) Cleanup(multistep.StateBag) {}
|
||||||
|
|
|
@ -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) {}
|
|
Loading…
Reference in New Issue