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 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}

View File

@ -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
} }

View File

@ -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
} }

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 ( 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) {}

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) {}