github.com/mitchellh/packer@v1.3.2/builder/vmware/iso/step_create_vmx.go (about)

     1  package iso
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"strings"
    11  
    12  	vmwcommon "github.com/hashicorp/packer/builder/vmware/common"
    13  	"github.com/hashicorp/packer/helper/multistep"
    14  	"github.com/hashicorp/packer/packer"
    15  	"github.com/hashicorp/packer/template/interpolate"
    16  )
    17  
    18  type vmxTemplateData struct {
    19  	Name    string
    20  	GuestOS string
    21  	ISOPath string
    22  	Version string
    23  
    24  	HDD_BootOrder string
    25  
    26  	SCSI_Present         string
    27  	SCSI_diskAdapterType string
    28  	SATA_Present         string
    29  	NVME_Present         string
    30  
    31  	DiskName              string
    32  	DiskType              string
    33  	CDROMType             string
    34  	CDROMType_MasterSlave string
    35  
    36  	Network_Type    string
    37  	Network_Device  string
    38  	Network_Adapter string
    39  
    40  	Sound_Present string
    41  	Usb_Present   string
    42  
    43  	Serial_Present  string
    44  	Serial_Type     string
    45  	Serial_Endpoint string
    46  	Serial_Host     string
    47  	Serial_Yield    string
    48  	Serial_Filename string
    49  	Serial_Auto     string
    50  
    51  	Parallel_Present       string
    52  	Parallel_Bidirectional string
    53  	Parallel_Filename      string
    54  	Parallel_Auto          string
    55  }
    56  
    57  type additionalDiskTemplateData struct {
    58  	DiskNumber int
    59  	DiskName   string
    60  }
    61  
    62  // This step creates the VMX file for the VM.
    63  //
    64  // Uses:
    65  //   config *config
    66  //   iso_path string
    67  //   ui     packer.Ui
    68  //
    69  // Produces:
    70  //   vmx_path string - The path to the VMX file.
    71  type stepCreateVMX struct {
    72  	tempDir string
    73  }
    74  
    75  /* serial conversions */
    76  type serialConfigPipe struct {
    77  	filename string
    78  	endpoint string
    79  	host     string
    80  	yield    string
    81  }
    82  
    83  type serialConfigFile struct {
    84  	filename string
    85  	yield    string
    86  }
    87  
    88  type serialConfigDevice struct {
    89  	devicename string
    90  	yield      string
    91  }
    92  
    93  type serialConfigAuto struct {
    94  	devicename string
    95  	yield      string
    96  }
    97  
    98  type serialUnion struct {
    99  	serialType interface{}
   100  	pipe       *serialConfigPipe
   101  	file       *serialConfigFile
   102  	device     *serialConfigDevice
   103  	auto       *serialConfigAuto
   104  }
   105  
   106  func unformat_serial(config string) (*serialUnion, error) {
   107  	var defaultSerialPort string
   108  	if runtime.GOOS == "windows" {
   109  		defaultSerialPort = "COM1"
   110  	} else {
   111  		defaultSerialPort = "/dev/ttyS0"
   112  	}
   113  
   114  	input := strings.SplitN(config, ":", 2)
   115  	if len(input) < 1 {
   116  		return nil, fmt.Errorf("Unexpected format for serial port: %s", config)
   117  	}
   118  
   119  	var formatType, formatOptions string
   120  	formatType = input[0]
   121  	if len(input) == 2 {
   122  		formatOptions = input[1]
   123  	} else {
   124  		formatOptions = ""
   125  	}
   126  
   127  	switch strings.ToUpper(formatType) {
   128  	case "PIPE":
   129  		comp := strings.Split(formatOptions, ",")
   130  		if len(comp) < 3 || len(comp) > 4 {
   131  			return nil, fmt.Errorf("Unexpected format for serial port : pipe : %s", config)
   132  		}
   133  		if res := strings.ToLower(comp[1]); res != "client" && res != "server" {
   134  			return nil, fmt.Errorf("Unexpected format for serial port : pipe : endpoint : %s : %s", res, config)
   135  		}
   136  		if res := strings.ToLower(comp[2]); res != "app" && res != "vm" {
   137  			return nil, fmt.Errorf("Unexpected format for serial port : pipe : host : %s : %s", res, config)
   138  		}
   139  		res := &serialConfigPipe{
   140  			filename: comp[0],
   141  			endpoint: comp[1],
   142  			host:     map[string]string{"app": "TRUE", "vm": "FALSE"}[strings.ToLower(comp[2])],
   143  			yield:    "FALSE",
   144  		}
   145  		if len(comp) == 4 {
   146  			res.yield = strings.ToUpper(comp[3])
   147  		}
   148  		if res.yield != "TRUE" && res.yield != "FALSE" {
   149  			return nil, fmt.Errorf("Unexpected format for serial port : pipe : yield : %s : %s", res.yield, config)
   150  		}
   151  		return &serialUnion{serialType: res, pipe: res}, nil
   152  
   153  	case "FILE":
   154  		comp := strings.Split(formatOptions, ",")
   155  		if len(comp) > 2 {
   156  			return nil, fmt.Errorf("Unexpected format for serial port : file : %s", config)
   157  		}
   158  
   159  		res := &serialConfigFile{yield: "FALSE"}
   160  
   161  		res.filename = filepath.FromSlash(comp[0])
   162  
   163  		res.yield = map[bool]string{true: strings.ToUpper(comp[0]), false: "FALSE"}[len(comp) > 1]
   164  		if res.yield != "TRUE" && res.yield != "FALSE" {
   165  			return nil, fmt.Errorf("Unexpected format for serial port : file : yield : %s : %s", res.yield, config)
   166  		}
   167  
   168  		return &serialUnion{serialType: res, file: res}, nil
   169  
   170  	case "DEVICE":
   171  		comp := strings.Split(formatOptions, ",")
   172  		if len(comp) > 2 {
   173  			return nil, fmt.Errorf("Unexpected format for serial port : device : %s", config)
   174  		}
   175  
   176  		res := new(serialConfigDevice)
   177  
   178  		if len(comp) == 2 {
   179  			res.devicename = map[bool]string{true: filepath.FromSlash(comp[0]), false: defaultSerialPort}[len(comp[0]) > 0]
   180  			res.yield = strings.ToUpper(comp[1])
   181  		} else if len(comp) == 1 {
   182  			res.devicename = map[bool]string{true: filepath.FromSlash(comp[0]), false: defaultSerialPort}[len(comp[0]) > 0]
   183  			res.yield = "FALSE"
   184  		} else if len(comp) == 0 {
   185  			res.devicename = defaultSerialPort
   186  			res.yield = "FALSE"
   187  		}
   188  
   189  		if res.yield != "TRUE" && res.yield != "FALSE" {
   190  			return nil, fmt.Errorf("Unexpected format for serial port : device : yield : %s : %s", res.yield, config)
   191  		}
   192  
   193  		return &serialUnion{serialType: res, device: res}, nil
   194  
   195  	case "AUTO":
   196  		res := new(serialConfigAuto)
   197  		res.devicename = defaultSerialPort
   198  
   199  		if len(formatOptions) > 0 {
   200  			res.yield = strings.ToUpper(formatOptions)
   201  		} else {
   202  			res.yield = "FALSE"
   203  		}
   204  
   205  		if res.yield != "TRUE" && res.yield != "FALSE" {
   206  			return nil, fmt.Errorf("Unexpected format for serial port : auto : yield : %s : %s", res.yield, config)
   207  		}
   208  
   209  		return &serialUnion{serialType: res, auto: res}, nil
   210  
   211  	case "NONE":
   212  		return &serialUnion{serialType: nil}, nil
   213  
   214  	default:
   215  		return nil, fmt.Errorf("Unknown serial type : %s : %s", strings.ToUpper(formatType), config)
   216  	}
   217  }
   218  
   219  /* parallel port */
   220  type parallelUnion struct {
   221  	parallelType interface{}
   222  	file         *parallelPortFile
   223  	device       *parallelPortDevice
   224  	auto         *parallelPortAuto
   225  }
   226  type parallelPortFile struct {
   227  	filename string
   228  }
   229  type parallelPortDevice struct {
   230  	bidirectional string
   231  	devicename    string
   232  }
   233  type parallelPortAuto struct {
   234  	bidirectional string
   235  }
   236  
   237  func unformat_parallel(config string) (*parallelUnion, error) {
   238  	input := strings.SplitN(config, ":", 2)
   239  	if len(input) < 1 {
   240  		return nil, fmt.Errorf("Unexpected format for parallel port: %s", config)
   241  	}
   242  
   243  	var formatType, formatOptions string
   244  	formatType = input[0]
   245  	if len(input) == 2 {
   246  		formatOptions = input[1]
   247  	} else {
   248  		formatOptions = ""
   249  	}
   250  
   251  	switch strings.ToUpper(formatType) {
   252  	case "FILE":
   253  		res := &parallelPortFile{filename: filepath.FromSlash(formatOptions)}
   254  		return &parallelUnion{parallelType: res, file: res}, nil
   255  	case "DEVICE":
   256  		comp := strings.Split(formatOptions, ",")
   257  		if len(comp) < 1 || len(comp) > 2 {
   258  			return nil, fmt.Errorf("Unexpected format for parallel port: %s", config)
   259  		}
   260  		res := new(parallelPortDevice)
   261  		res.bidirectional = "FALSE"
   262  		res.devicename = filepath.FromSlash(comp[0])
   263  		if len(comp) > 1 {
   264  			switch strings.ToUpper(comp[1]) {
   265  			case "BI":
   266  				res.bidirectional = "TRUE"
   267  			case "UNI":
   268  				res.bidirectional = "FALSE"
   269  			default:
   270  				return nil, fmt.Errorf("Unknown parallel port direction : %s : %s", strings.ToUpper(comp[0]), config)
   271  			}
   272  		}
   273  		return &parallelUnion{parallelType: res, device: res}, nil
   274  
   275  	case "AUTO":
   276  		res := new(parallelPortAuto)
   277  		switch strings.ToUpper(formatOptions) {
   278  		case "":
   279  			fallthrough
   280  		case "UNI":
   281  			res.bidirectional = "FALSE"
   282  		case "BI":
   283  			res.bidirectional = "TRUE"
   284  		default:
   285  			return nil, fmt.Errorf("Unknown parallel port direction : %s : %s", strings.ToUpper(formatOptions), config)
   286  		}
   287  		return &parallelUnion{parallelType: res, auto: res}, nil
   288  
   289  	case "NONE":
   290  		return &parallelUnion{parallelType: nil}, nil
   291  	}
   292  
   293  	return nil, fmt.Errorf("Unexpected format for parallel port: %s", config)
   294  }
   295  
   296  /* regular steps */
   297  func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
   298  	config := state.Get("config").(*Config)
   299  	isoPath := state.Get("iso_path").(string)
   300  	ui := state.Get("ui").(packer.Ui)
   301  
   302  	// Convert the iso_path into a path relative to the .vmx file if possible
   303  	if relativeIsoPath, err := filepath.Rel(config.VMXTemplatePath, filepath.FromSlash(isoPath)); err == nil {
   304  		isoPath = relativeIsoPath
   305  	}
   306  
   307  	ui.Say("Building and writing VMX file")
   308  
   309  	vmxTemplate := DefaultVMXTemplate
   310  	if config.VMXTemplatePath != "" {
   311  		f, err := os.Open(config.VMXTemplatePath)
   312  		if err != nil {
   313  			err := fmt.Errorf("Error reading VMX template: %s", err)
   314  			state.Put("error", err)
   315  			ui.Error(err.Error())
   316  			return multistep.ActionHalt
   317  		}
   318  		defer f.Close()
   319  
   320  		rawBytes, err := ioutil.ReadAll(f)
   321  		if err != nil {
   322  			err := fmt.Errorf("Error reading VMX template: %s", err)
   323  			state.Put("error", err)
   324  			ui.Error(err.Error())
   325  			return multistep.ActionHalt
   326  		}
   327  
   328  		vmxTemplate = string(rawBytes)
   329  	}
   330  
   331  	ctx := config.ctx
   332  
   333  	if len(config.AdditionalDiskSize) > 0 {
   334  		for i := range config.AdditionalDiskSize {
   335  			ctx.Data = &additionalDiskTemplateData{
   336  				DiskNumber: i + 1,
   337  				DiskName:   config.DiskName,
   338  			}
   339  
   340  			diskTemplate := DefaultAdditionalDiskTemplate
   341  			if config.VMXDiskTemplatePath != "" {
   342  				f, err := os.Open(config.VMXDiskTemplatePath)
   343  				if err != nil {
   344  					err := fmt.Errorf("Error reading VMX disk template: %s", err)
   345  					state.Put("error", err)
   346  					ui.Error(err.Error())
   347  					return multistep.ActionHalt
   348  				}
   349  				defer f.Close()
   350  
   351  				rawBytes, err := ioutil.ReadAll(f)
   352  				if err != nil {
   353  					err := fmt.Errorf("Error reading VMX disk template: %s", err)
   354  					state.Put("error", err)
   355  					ui.Error(err.Error())
   356  					return multistep.ActionHalt
   357  				}
   358  
   359  				diskTemplate = string(rawBytes)
   360  			}
   361  
   362  			diskContents, err := interpolate.Render(diskTemplate, &ctx)
   363  			if err != nil {
   364  				err := fmt.Errorf("Error preparing VMX template for additional disk: %s", err)
   365  				state.Put("error", err)
   366  				ui.Error(err.Error())
   367  				return multistep.ActionHalt
   368  			}
   369  
   370  			vmxTemplate += diskContents
   371  		}
   372  	}
   373  
   374  	templateData := vmxTemplateData{
   375  		Name:     config.VMName,
   376  		GuestOS:  config.GuestOSType,
   377  		DiskName: config.DiskName,
   378  		Version:  config.Version,
   379  		ISOPath:  isoPath,
   380  
   381  		SCSI_Present:         "FALSE",
   382  		SCSI_diskAdapterType: "lsilogic",
   383  		SATA_Present:         "FALSE",
   384  		NVME_Present:         "FALSE",
   385  
   386  		DiskType:              "scsi",
   387  		HDD_BootOrder:         "scsi0:0",
   388  		CDROMType:             "ide",
   389  		CDROMType_MasterSlave: "0",
   390  
   391  		Network_Adapter: "e1000",
   392  
   393  		Sound_Present: map[bool]string{true: "TRUE", false: "FALSE"}[bool(config.Sound)],
   394  		Usb_Present:   map[bool]string{true: "TRUE", false: "FALSE"}[bool(config.USB)],
   395  
   396  		Serial_Present:   "FALSE",
   397  		Parallel_Present: "FALSE",
   398  	}
   399  
   400  	/// Use the disk adapter type that the user specified to tweak the .vmx
   401  	//  Also sync the cdrom adapter type according to what's common for that disk type.
   402  	//  XXX: If the cdrom type is modified, make sure to update common/step_clean_vmx.go
   403  	//       so that it will regex the correct cdrom device for removal.
   404  	diskAdapterType := strings.ToLower(config.DiskAdapterType)
   405  	switch diskAdapterType {
   406  	case "ide":
   407  		templateData.DiskType = "ide"
   408  		templateData.CDROMType = "ide"
   409  		templateData.CDROMType_MasterSlave = "1"
   410  		templateData.HDD_BootOrder = "ide0:0"
   411  	case "sata":
   412  		templateData.SATA_Present = "TRUE"
   413  		templateData.DiskType = "sata"
   414  		templateData.CDROMType = "sata"
   415  		templateData.CDROMType_MasterSlave = "1"
   416  		templateData.HDD_BootOrder = "sata0:0"
   417  	case "nvme":
   418  		templateData.NVME_Present = "TRUE"
   419  		templateData.DiskType = "nvme"
   420  		templateData.SATA_Present = "TRUE"
   421  		templateData.CDROMType = "sata"
   422  		templateData.CDROMType_MasterSlave = "0"
   423  		templateData.HDD_BootOrder = "nvme0:0"
   424  	case "scsi":
   425  		diskAdapterType = "lsilogic"
   426  		fallthrough
   427  	default:
   428  		templateData.SCSI_Present = "TRUE"
   429  		templateData.SCSI_diskAdapterType = diskAdapterType
   430  		templateData.DiskType = "scsi"
   431  		templateData.CDROMType = "ide"
   432  		templateData.CDROMType_MasterSlave = "0"
   433  		templateData.HDD_BootOrder = "scsi0:0"
   434  	}
   435  
   436  	/// Handle the cdrom adapter type. If the disk adapter type and the
   437  	//  cdrom adapter type are the same, then ensure that the cdrom is the
   438  	//  slave device on whatever bus the disk adapter is on.
   439  	cdromAdapterType := strings.ToLower(config.CdromAdapterType)
   440  	if cdromAdapterType == "" {
   441  		cdromAdapterType = templateData.CDROMType
   442  	} else if cdromAdapterType == diskAdapterType {
   443  		templateData.CDROMType_MasterSlave = "1"
   444  	} else {
   445  		templateData.CDROMType_MasterSlave = "0"
   446  	}
   447  
   448  	switch cdromAdapterType {
   449  	case "ide":
   450  		templateData.CDROMType = "ide"
   451  	case "sata":
   452  		templateData.SATA_Present = "TRUE"
   453  		templateData.CDROMType = "sata"
   454  	case "scsi":
   455  		templateData.SCSI_Present = "TRUE"
   456  		templateData.CDROMType = "scsi"
   457  	default:
   458  		err := fmt.Errorf("Error processing VMX template: %s", cdromAdapterType)
   459  		state.Put("error", err)
   460  		ui.Error(err.Error())
   461  		return multistep.ActionHalt
   462  	}
   463  
   464  	/// Assign the network adapter type into the template if one was specified.
   465  	network_adapter := strings.ToLower(config.NetworkAdapterType)
   466  	if network_adapter != "" {
   467  		templateData.Network_Adapter = network_adapter
   468  	}
   469  
   470  	/// Check the network type that the user specified
   471  	network := config.Network
   472  	driver := state.Get("driver").(vmwcommon.Driver).GetVmwareDriver()
   473  
   474  	// check to see if the driver implements a network mapper for mapping
   475  	// the network-type to its device-name.
   476  	if driver.NetworkMapper != nil {
   477  
   478  		// read network map configuration into a NetworkNameMapper.
   479  		netmap, err := driver.NetworkMapper()
   480  		if err != nil {
   481  			state.Put("error", err)
   482  			ui.Error(err.Error())
   483  			return multistep.ActionHalt
   484  		}
   485  
   486  		// try and convert the specified network to a device.
   487  		devices, err := netmap.NameIntoDevices(network)
   488  
   489  		if err == nil && len(devices) > 0 {
   490  			// If multiple devices exist, for example for network "nat", VMware chooses
   491  			// the actual device. Only type "custom" allows the exact choice of a
   492  			// specific virtual network (see below). We allow VMware to choose the device
   493  			// and for device-specific operations like GuestIP, try to go over all
   494  			// devices that match a name (e.g. "nat").
   495  			// https://pubs.vmware.com/workstation-9/index.jsp?topic=%2Fcom.vmware.ws.using.doc%2FGUID-3B504F2F-7A0B-415F-AE01-62363A95D052.html
   496  			templateData.Network_Type = network
   497  			templateData.Network_Device = ""
   498  		} else {
   499  			// otherwise, we were unable to find the type, so assume it's a custom device
   500  			templateData.Network_Type = "custom"
   501  			templateData.Network_Device = network
   502  		}
   503  
   504  		// if NetworkMapper is nil, then we're using something like ESX, so fall
   505  		// back to the previous logic of using "nat" despite it not mattering to ESX.
   506  	} else {
   507  		templateData.Network_Type = "nat"
   508  		templateData.Network_Device = network
   509  
   510  		network = "nat"
   511  	}
   512  
   513  	// store the network so that we can later figure out what ip address to bind to
   514  	state.Put("vmnetwork", network)
   515  
   516  	/// check if serial port has been configured
   517  	if config.Serial == "" {
   518  		templateData.Serial_Present = "FALSE"
   519  	} else {
   520  		serial, err := unformat_serial(config.Serial)
   521  		if err != nil {
   522  			err := fmt.Errorf("Error processing VMX template: %s", err)
   523  			state.Put("error", err)
   524  			ui.Error(err.Error())
   525  			return multistep.ActionHalt
   526  		}
   527  
   528  		templateData.Serial_Present = "TRUE"
   529  		templateData.Serial_Filename = ""
   530  		templateData.Serial_Yield = ""
   531  		templateData.Serial_Endpoint = ""
   532  		templateData.Serial_Host = ""
   533  		templateData.Serial_Auto = "FALSE"
   534  
   535  		switch serial.serialType.(type) {
   536  		case *serialConfigPipe:
   537  			templateData.Serial_Type = "pipe"
   538  			templateData.Serial_Endpoint = serial.pipe.endpoint
   539  			templateData.Serial_Host = serial.pipe.host
   540  			templateData.Serial_Yield = serial.pipe.yield
   541  			templateData.Serial_Filename = filepath.FromSlash(serial.pipe.filename)
   542  		case *serialConfigFile:
   543  			templateData.Serial_Type = "file"
   544  			templateData.Serial_Filename = filepath.FromSlash(serial.file.filename)
   545  		case *serialConfigDevice:
   546  			templateData.Serial_Type = "device"
   547  			templateData.Serial_Filename = filepath.FromSlash(serial.device.devicename)
   548  		case *serialConfigAuto:
   549  			templateData.Serial_Type = "device"
   550  			templateData.Serial_Filename = filepath.FromSlash(serial.auto.devicename)
   551  			templateData.Serial_Yield = serial.auto.yield
   552  			templateData.Serial_Auto = "TRUE"
   553  		case nil:
   554  			templateData.Serial_Present = "FALSE"
   555  			break
   556  
   557  		default:
   558  			err := fmt.Errorf("Error processing VMX template: %v", serial)
   559  			state.Put("error", err)
   560  			ui.Error(err.Error())
   561  			return multistep.ActionHalt
   562  		}
   563  	}
   564  
   565  	/// check if parallel port has been configured
   566  	if config.Parallel == "" {
   567  		templateData.Parallel_Present = "FALSE"
   568  	} else {
   569  		parallel, err := unformat_parallel(config.Parallel)
   570  		if err != nil {
   571  			err := fmt.Errorf("Error processing VMX template: %s", err)
   572  			state.Put("error", err)
   573  			ui.Error(err.Error())
   574  			return multistep.ActionHalt
   575  		}
   576  
   577  		templateData.Parallel_Auto = "FALSE"
   578  		switch parallel.parallelType.(type) {
   579  		case *parallelPortFile:
   580  			templateData.Parallel_Present = "TRUE"
   581  			templateData.Parallel_Filename = filepath.FromSlash(parallel.file.filename)
   582  		case *parallelPortDevice:
   583  			templateData.Parallel_Present = "TRUE"
   584  			templateData.Parallel_Bidirectional = parallel.device.bidirectional
   585  			templateData.Parallel_Filename = filepath.FromSlash(parallel.device.devicename)
   586  		case *parallelPortAuto:
   587  			templateData.Parallel_Present = "TRUE"
   588  			templateData.Parallel_Auto = "TRUE"
   589  			templateData.Parallel_Bidirectional = parallel.auto.bidirectional
   590  		case nil:
   591  			templateData.Parallel_Present = "FALSE"
   592  			break
   593  
   594  		default:
   595  			err := fmt.Errorf("Error processing VMX template: %v", parallel)
   596  			state.Put("error", err)
   597  			ui.Error(err.Error())
   598  			return multistep.ActionHalt
   599  		}
   600  	}
   601  
   602  	ctx.Data = &templateData
   603  
   604  	/// render the .vmx template
   605  	vmxContents, err := interpolate.Render(vmxTemplate, &ctx)
   606  	if err != nil {
   607  		err := fmt.Errorf("Error processing VMX template: %s", err)
   608  		state.Put("error", err)
   609  		ui.Error(err.Error())
   610  		return multistep.ActionHalt
   611  	}
   612  
   613  	vmxDir := config.OutputDir
   614  	if config.RemoteType != "" {
   615  		// For remote builds, we just put the VMX in a temporary
   616  		// directory since it just gets uploaded anyways.
   617  		vmxDir, err = ioutil.TempDir("", "packer-vmx")
   618  		if err != nil {
   619  			err := fmt.Errorf("Error preparing VMX template: %s", err)
   620  			state.Put("error", err)
   621  			ui.Error(err.Error())
   622  			return multistep.ActionHalt
   623  		}
   624  
   625  		// Set the tempDir so we clean it up
   626  		s.tempDir = vmxDir
   627  	}
   628  
   629  	vmxPath := filepath.Join(vmxDir, config.VMName+".vmx")
   630  	if err := vmwcommon.WriteVMX(vmxPath, vmwcommon.ParseVMX(vmxContents)); err != nil {
   631  		err := fmt.Errorf("Error creating VMX file: %s", err)
   632  		state.Put("error", err)
   633  		ui.Error(err.Error())
   634  		return multistep.ActionHalt
   635  	}
   636  
   637  	state.Put("vmx_path", vmxPath)
   638  
   639  	return multistep.ActionContinue
   640  }
   641  
   642  func (s *stepCreateVMX) Cleanup(multistep.StateBag) {
   643  	if s.tempDir != "" {
   644  		os.RemoveAll(s.tempDir)
   645  	}
   646  }
   647  
   648  // This is the default VMX template used if no other template is given.
   649  // This is hardcoded here. If you wish to use a custom template please
   650  // do so by specifying in the builder configuration.
   651  const DefaultVMXTemplate = `
   652  .encoding = "UTF-8"
   653  bios.bootOrder = "hdd,cdrom"
   654  bios.hddOrder = "{{ .HDD_BootOrder }}"
   655  checkpoint.vmState = ""
   656  cleanShutdown = "TRUE"
   657  config.version = "8"
   658  displayName = "{{ .Name }}"
   659  ehci.pciSlotNumber = "34"
   660  ehci.present = "TRUE"
   661  ethernet0.addressType = "generated"
   662  ethernet0.bsdName = "en0"
   663  ethernet0.connectionType = "{{ .Network_Type }}"
   664  ethernet0.vnet = "{{ .Network_Device }}"
   665  ethernet0.displayName = "Ethernet"
   666  ethernet0.linkStatePropagation.enable = "FALSE"
   667  ethernet0.pciSlotNumber = "33"
   668  ethernet0.present = "TRUE"
   669  ethernet0.virtualDev = "{{ .Network_Adapter }}"
   670  ethernet0.wakeOnPcktRcv = "FALSE"
   671  extendedConfigFile = "{{ .Name }}.vmxf"
   672  floppy0.present = "FALSE"
   673  guestOS = "{{ .GuestOS }}"
   674  gui.fullScreenAtPowerOn = "FALSE"
   675  gui.viewModeAtPowerOn = "windowed"
   676  hgfs.linkRootShare = "TRUE"
   677  hgfs.mapRootShare = "TRUE"
   678  
   679  scsi0.present = "{{ .SCSI_Present }}"
   680  scsi0.virtualDev = "{{ .SCSI_diskAdapterType }}"
   681  scsi0.pciSlotNumber = "16"
   682  scsi0:0.redo = ""
   683  sata0.present = "{{ .SATA_Present }}"
   684  nvme0.present = "{{ .NVME_Present }}"
   685  
   686  {{ .DiskType }}0:0.present = "TRUE"
   687  {{ .DiskType }}0:0.fileName = "{{ .DiskName }}.vmdk"
   688  
   689  {{ .CDROMType }}0:{{ .CDROMType_MasterSlave }}.present = "TRUE"
   690  {{ .CDROMType }}0:{{ .CDROMType_MasterSlave }}.fileName = "{{ .ISOPath }}"
   691  {{ .CDROMType }}0:{{ .CDROMType_MasterSlave }}.deviceType = "cdrom-image"
   692  
   693  isolation.tools.hgfs.disable = "FALSE"
   694  memsize = "512"
   695  nvram = "{{ .Name }}.nvram"
   696  pciBridge0.pciSlotNumber = "17"
   697  pciBridge0.present = "TRUE"
   698  pciBridge4.functions = "8"
   699  pciBridge4.pciSlotNumber = "21"
   700  pciBridge4.present = "TRUE"
   701  pciBridge4.virtualDev = "pcieRootPort"
   702  pciBridge5.functions = "8"
   703  pciBridge5.pciSlotNumber = "22"
   704  pciBridge5.present = "TRUE"
   705  pciBridge5.virtualDev = "pcieRootPort"
   706  pciBridge6.functions = "8"
   707  pciBridge6.pciSlotNumber = "23"
   708  pciBridge6.present = "TRUE"
   709  pciBridge6.virtualDev = "pcieRootPort"
   710  pciBridge7.functions = "8"
   711  pciBridge7.pciSlotNumber = "24"
   712  pciBridge7.present = "TRUE"
   713  pciBridge7.virtualDev = "pcieRootPort"
   714  powerType.powerOff = "soft"
   715  powerType.powerOn = "soft"
   716  powerType.reset = "soft"
   717  powerType.suspend = "soft"
   718  proxyApps.publishToHost = "FALSE"
   719  replay.filename = ""
   720  replay.supported = "FALSE"
   721  
   722  // Sound
   723  sound.startConnected = "{{ .Sound_Present }}"
   724  sound.present = "{{ .Sound_Present }}"
   725  sound.fileName = "-1"
   726  sound.autodetect = "TRUE"
   727  
   728  tools.syncTime = "TRUE"
   729  tools.upgrade.policy = "upgradeAtPowerCycle"
   730  
   731  // USB
   732  usb.pciSlotNumber = "32"
   733  usb.present = "{{ .Usb_Present }}"
   734  
   735  // Serial
   736  serial0.present = "{{ .Serial_Present }}"
   737  serial0.startConnected = "{{ .Serial_Present }}"
   738  serial0.fileName = "{{ .Serial_Filename }}"
   739  serial0.autodetect = "{{ .Serial_Auto }}"
   740  serial0.fileType = "{{ .Serial_Type }}"
   741  serial0.yieldOnMsrRead = "{{ .Serial_Yield }}"
   742  serial0.pipe.endPoint = "{{ .Serial_Endpoint }}"
   743  serial0.tryNoRxLoss = "{{ .Serial_Host }}"
   744  
   745  // Parallel
   746  parallel0.present = "{{ .Parallel_Present }}"
   747  parallel0.startConnected = "{{ .Parallel_Present }}"
   748  parallel0.fileName = "{{ .Parallel_Filename }}"
   749  parallel0.autodetect = "{{ .Parallel_Auto }}"
   750  parallel0.bidirectional = "{{ .Parallel_Bidirectional }}"
   751  
   752  virtualHW.productCompatibility = "hosted"
   753  virtualHW.version = "{{ .Version }}"
   754  vmci0.id = "1861462627"
   755  vmci0.pciSlotNumber = "35"
   756  vmci0.present = "TRUE"
   757  vmotion.checkpointFBSize = "65536000"
   758  `
   759  
   760  const DefaultAdditionalDiskTemplate = `
   761  scsi0:{{ .DiskNumber }}.fileName = "{{ .DiskName}}-{{ .DiskNumber }}.vmdk"
   762  scsi0:{{ .DiskNumber }}.present = "TRUE"
   763  scsi0:{{ .DiskNumber }}.redo = ""
   764  `