From 81272d1427b5ce0c30fb79d55a1f7618921a8ad4 Mon Sep 17 00:00:00 2001 From: bugbuilder Date: Mon, 14 Aug 2017 23:00:19 -0300 Subject: [PATCH] Updated mark as template to be able to use --force --- .../vsphere-template/post-processor.go | 11 +-- .../step_choose_datacenter.go | 6 +- .../vsphere-template/step_create_folder.go | 69 +++++++------ .../vsphere-template/step_fetch_vm.go | 88 ----------------- .../vsphere-template/step_mark_as_template.go | 99 ++++++++++++++++++- .../vsphere-template/step_move_template.go | 50 ---------- 6 files changed, 141 insertions(+), 182 deletions(-) delete mode 100644 post-processor/vsphere-template/step_fetch_vm.go delete mode 100644 post-processor/vsphere-template/step_move_template.go diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index d1961a491..4b299e443 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -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} diff --git a/post-processor/vsphere-template/step_choose_datacenter.go b/post-processor/vsphere-template/step_choose_datacenter.go index 2016595b6..51959b6d7 100644 --- a/post-processor/vsphere-template/step_choose_datacenter.go +++ b/post-processor/vsphere-template/step_choose_datacenter.go @@ -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 } diff --git a/post-processor/vsphere-template/step_create_folder.go b/post-processor/vsphere-template/step_create_folder.go index 57f1516e6..a0e648275 100644 --- a/post-processor/vsphere-template/step_create_folder.go +++ b/post-processor/vsphere-template/step_create_folder.go @@ -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 } diff --git a/post-processor/vsphere-template/step_fetch_vm.go b/post-processor/vsphere-template/step_fetch_vm.go deleted file mode 100644 index dd5ab7596..000000000 --- a/post-processor/vsphere-template/step_fetch_vm.go +++ /dev/null @@ -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) {} diff --git a/post-processor/vsphere-template/step_mark_as_template.go b/post-processor/vsphere-template/step_mark_as_template.go index 8948764a0..0e5465054 100644 --- a/post-processor/vsphere-template/step_mark_as_template.go +++ b/post-processor/vsphere-template/step_mark_as_template.go @@ -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) {} diff --git a/post-processor/vsphere-template/step_move_template.go b/post-processor/vsphere-template/step_move_template.go deleted file mode 100644 index e83616982..000000000 --- a/post-processor/vsphere-template/step_move_template.go +++ /dev/null @@ -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) {}