github.com/vijayrajah/packer@v1.3.2/post-processor/vsphere-template/step_mark_as_template.go (about)

     1  package vsphere_template
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"path"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/packer/helper/multistep"
    11  	"github.com/hashicorp/packer/packer"
    12  	"github.com/hashicorp/packer/post-processor/vsphere"
    13  	"github.com/vmware/govmomi"
    14  	"github.com/vmware/govmomi/object"
    15  	"github.com/vmware/govmomi/vim25/types"
    16  )
    17  
    18  type stepMarkAsTemplate struct {
    19  	VMName       string
    20  	RemoteFolder string
    21  }
    22  
    23  func NewStepMarkAsTemplate(artifact packer.Artifact) *stepMarkAsTemplate {
    24  	remoteFolder := "Discovered virtual machine"
    25  	vmname := artifact.Id()
    26  
    27  	if artifact.BuilderId() == vsphere.BuilderId {
    28  		id := strings.Split(artifact.Id(), "::")
    29  		remoteFolder = id[1]
    30  		vmname = id[2]
    31  	}
    32  
    33  	return &stepMarkAsTemplate{
    34  		VMName:       vmname,
    35  		RemoteFolder: remoteFolder,
    36  	}
    37  }
    38  
    39  func (s *stepMarkAsTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    40  	ui := state.Get("ui").(packer.Ui)
    41  	cli := state.Get("client").(*govmomi.Client)
    42  	folder := state.Get("folder").(*object.Folder)
    43  	dcPath := state.Get("dcPath").(string)
    44  
    45  	ui.Message("Marking as a template...")
    46  
    47  	vm, err := findRuntimeVM(cli, dcPath, s.VMName, s.RemoteFolder)
    48  	if err != nil {
    49  		state.Put("error", err)
    50  		ui.Error(err.Error())
    51  		return multistep.ActionHalt
    52  	}
    53  
    54  	dsPath, err := datastorePath(vm)
    55  	if err != nil {
    56  		state.Put("error", err)
    57  		ui.Error(err.Error())
    58  		return multistep.ActionHalt
    59  	}
    60  
    61  	host, err := vm.HostSystem(context.Background())
    62  	if err != nil {
    63  		state.Put("error", err)
    64  		ui.Error(err.Error())
    65  		return multistep.ActionHalt
    66  	}
    67  
    68  	if err := vm.Unregister(context.Background()); err != nil {
    69  		state.Put("error", err)
    70  		ui.Error(err.Error())
    71  		return multistep.ActionHalt
    72  	}
    73  
    74  	if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil {
    75  		state.Put("error", err)
    76  		ui.Error(err.Error())
    77  		return multistep.ActionHalt
    78  	}
    79  
    80  	task, err := folder.RegisterVM(context.Background(), dsPath.String(), s.VMName, true, nil, host)
    81  	if err != nil {
    82  		state.Put("error", err)
    83  		ui.Error(err.Error())
    84  		return multistep.ActionHalt
    85  	}
    86  
    87  	if err = task.Wait(context.Background()); err != nil {
    88  		state.Put("error", err)
    89  		ui.Error(err.Error())
    90  		return multistep.ActionHalt
    91  	}
    92  
    93  	return multistep.ActionContinue
    94  }
    95  
    96  func datastorePath(vm *object.VirtualMachine) (*object.DatastorePath, error) {
    97  	devices, err := vm.Device(context.Background())
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	disk := ""
   103  	for _, device := range devices {
   104  		if d, ok := device.(*types.VirtualDisk); ok {
   105  			if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
   106  				disk = b.GetVirtualDeviceFileBackingInfo().FileName
   107  			}
   108  			break
   109  		}
   110  	}
   111  
   112  	if disk == "" {
   113  		return nil, fmt.Errorf("disk not found in '%v'", vm.Name())
   114  	}
   115  
   116  	re := regexp.MustCompile("\\[(.*?)\\]")
   117  
   118  	datastore := re.FindStringSubmatch(disk)[1]
   119  	vmxPath := path.Join("/", path.Dir(strings.Split(disk, " ")[1]), vm.Name()+".vmx")
   120  
   121  	return &object.DatastorePath{
   122  		Datastore: datastore,
   123  		Path:      vmxPath,
   124  	}, nil
   125  }
   126  
   127  func findRuntimeVM(cli *govmomi.Client, dcPath, name, remoteFolder string) (*object.VirtualMachine, error) {
   128  	si := object.NewSearchIndex(cli.Client)
   129  	fullPath := path.Join(dcPath, "vm", remoteFolder, name)
   130  
   131  	ref, err := si.FindByInventoryPath(context.Background(), fullPath)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	if ref == nil {
   137  		return nil, fmt.Errorf("VM at path %s not found", fullPath)
   138  	}
   139  
   140  	vm := ref.(*object.VirtualMachine)
   141  	if vm.InventoryPath == "" {
   142  		vm.SetInventoryPath(fullPath)
   143  	}
   144  
   145  	return vm, nil
   146  }
   147  
   148  // If in the target folder a virtual machine or template already exists
   149  // it will be removed to maintain consistency
   150  func unregisterPreviousVM(cli *govmomi.Client, folder *object.Folder, name string) error {
   151  	si := object.NewSearchIndex(cli.Client)
   152  	fullPath := path.Join(folder.InventoryPath, name)
   153  
   154  	ref, err := si.FindByInventoryPath(context.Background(), fullPath)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	if ref != nil {
   160  		if vm, ok := ref.(*object.VirtualMachine); ok {
   161  			return vm.Unregister(context.Background())
   162  		} else {
   163  			return fmt.Errorf("an object name '%v' already exists", name)
   164  		}
   165  
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func (s *stepMarkAsTemplate) Cleanup(multistep.StateBag) {}