2019-05-31 08:27:41 -04:00
|
|
|
|
//go:generate struct-markdown
|
|
|
|
|
|
2018-11-17 02:23:28 -05:00
|
|
|
|
package common
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2020-11-11 13:21:37 -05:00
|
|
|
|
"github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate"
|
2018-11-17 02:23:28 -05:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type HWConfig struct {
|
2019-05-28 11:50:58 -04:00
|
|
|
|
// The number of cpus to use when building the VM.
|
2019-06-06 10:29:25 -04:00
|
|
|
|
CpuCount int `mapstructure:"cpus" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// The amount of memory to use when building the VM in megabytes.
|
2019-05-28 11:50:58 -04:00
|
|
|
|
MemorySize int `mapstructure:"memory" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// The number of cores per socket to use when building the VM. This
|
|
|
|
|
// corresponds to the cpuid.coresPerSocket option in the .vmx file.
|
2019-06-06 10:29:25 -04:00
|
|
|
|
CoreCount int `mapstructure:"cores" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// This is the network type that the virtual machine will be created with.
|
|
|
|
|
// This can be one of the generic values that map to a device such as
|
|
|
|
|
// hostonly, nat, or bridged. If the network is not one of these values,
|
|
|
|
|
// then it is assumed to be a VMware network device. (VMnet0..x)
|
2019-06-06 10:29:25 -04:00
|
|
|
|
Network string `mapstructure:"network" required:"false"`
|
2019-06-20 08:59:30 -04:00
|
|
|
|
// This is the ethernet adapter type the the virtual machine will be
|
|
|
|
|
// created with. By default the `e1000` network adapter type will be used
|
|
|
|
|
// by Packer. For more information, please consult [Choosing a network
|
|
|
|
|
// adapter for your virtual
|
|
|
|
|
// machine](https://kb.vmware.com/s/article/1001805) for desktop VMware
|
2019-06-06 10:29:25 -04:00
|
|
|
|
// clients. For ESXi, refer to the proper ESXi documentation.
|
2019-05-28 11:50:58 -04:00
|
|
|
|
NetworkAdapterType string `mapstructure:"network_adapter_type" required:"false"`
|
2020-08-06 07:19:55 -04:00
|
|
|
|
// The custom name of the network. Sets the vmx value "ethernet0.networkName"
|
|
|
|
|
NetworkName string `mapstructure:"network_name" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// Specify whether to enable VMware's virtual soundcard device when
|
|
|
|
|
// building the VM. Defaults to false.
|
2019-05-28 11:50:58 -04:00
|
|
|
|
Sound bool `mapstructure:"sound" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// Enable VMware's USB bus when building the guest VM. Defaults to false.
|
|
|
|
|
// To enable usage of the XHCI bus for USB 3 (5 Gbit/s), one can use the
|
|
|
|
|
// vmx_data option to enable it by specifying true for the usb_xhci.present
|
|
|
|
|
// property.
|
2019-06-06 10:29:25 -04:00
|
|
|
|
USB bool `mapstructure:"usb" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// This specifies a serial port to add to the VM. It has a format of
|
|
|
|
|
// `Type:option1,option2,...`. The field `Type` can be one of the following
|
|
|
|
|
// values: `FILE`, `DEVICE`, `PIPE`, `AUTO`, or `NONE`.
|
|
|
|
|
//
|
|
|
|
|
// * `FILE:path(,yield)` - Specifies the path to the local file to be used
|
|
|
|
|
// as the serial port.
|
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `yield` (bool) - This is an optional boolean that specifies
|
|
|
|
|
// whether the vm should yield the cpu when polling the port. By
|
|
|
|
|
// default, the builder will assume this as `FALSE`.
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
|
|
|
|
// * `DEVICE:path(,yield)` - Specifies the path to the local device to be
|
|
|
|
|
// used as the serial port. If `path` is empty, then default to the first
|
|
|
|
|
// serial port.
|
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `yield` (bool) - This is an optional boolean that specifies
|
|
|
|
|
// whether the vm should yield the cpu when polling the port. By
|
|
|
|
|
// default, the builder will assume this as `FALSE`.
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
|
|
|
|
// * `PIPE:path,endpoint,host(,yield)` - Specifies to use the named-pipe
|
|
|
|
|
// "path" as a serial port. This has a few options that determine how the
|
|
|
|
|
// VM should use the named-pipe.
|
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `endpoint` (string) - Chooses the type of the VM-end, which can be
|
|
|
|
|
// either a `client` or `server`.
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `host` (string) - Chooses the type of the host-end, which can
|
|
|
|
|
// be either `app` (application) or `vm` (another virtual-machine).
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `yield` (bool) - This is an optional boolean that specifies
|
|
|
|
|
// whether the vm should yield the cpu when polling the port. By
|
|
|
|
|
// default, the builder will assume this as `FALSE`.
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
|
|
|
|
// * `AUTO:(yield)` - Specifies to use auto-detection to determine the
|
|
|
|
|
// serial port to use. This has one option to determine how the VM should
|
|
|
|
|
// support the serial port.
|
|
|
|
|
//
|
2020-04-01 23:30:51 -04:00
|
|
|
|
// * `yield` (bool) - This is an optional boolean that specifies
|
|
|
|
|
// whether the vm should yield the cpu when polling the port. By
|
|
|
|
|
// default, the builder will assume this as `FALSE`.
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
2019-06-20 08:59:30 -04:00
|
|
|
|
// * `NONE` - Specifies to not use a serial port. (default)
|
2019-09-03 11:35:29 -04:00
|
|
|
|
//
|
2019-06-06 10:29:25 -04:00
|
|
|
|
Serial string `mapstructure:"serial" required:"false"`
|
2019-09-03 11:35:29 -04:00
|
|
|
|
// This specifies a parallel port to add to the VM. It has the format of
|
|
|
|
|
// `Type:option1,option2,...`. Type can be one of the following values:
|
|
|
|
|
// `FILE`, `DEVICE`, `AUTO`, or `NONE`.
|
|
|
|
|
//
|
|
|
|
|
// * `FILE:path` - Specifies the path to the local file to be used
|
|
|
|
|
// for the parallel port.
|
|
|
|
|
//
|
|
|
|
|
// * `DEVICE:path` - Specifies the path to the local device to be used
|
|
|
|
|
// for the parallel port.
|
|
|
|
|
//
|
|
|
|
|
// * `AUTO:direction` - Specifies to use auto-detection to determine the
|
|
|
|
|
// parallel port. Direction can be `BI` to specify bidirectional
|
|
|
|
|
// communication or `UNI` to specify unidirectional communication.
|
|
|
|
|
//
|
|
|
|
|
// * `NONE` - Specifies to not use a parallel port. (default)
|
2019-05-28 11:50:58 -04:00
|
|
|
|
Parallel string `mapstructure:"parallel" required:"false"`
|
2018-11-17 02:23:28 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *HWConfig) Prepare(ctx *interpolate.Context) []error {
|
|
|
|
|
var errs []error
|
|
|
|
|
|
|
|
|
|
// Hardware and cpu options
|
|
|
|
|
if c.CpuCount < 0 {
|
2018-11-17 07:36:02 -05:00
|
|
|
|
errs = append(errs, fmt.Errorf("An invalid number of cpus was specified (cpus < 0): %d", c.CpuCount))
|
2018-11-17 02:23:28 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.MemorySize < 0 {
|
2018-11-17 07:36:02 -05:00
|
|
|
|
errs = append(errs, fmt.Errorf("An invalid amount of memory was specified (memory < 0): %d", c.MemorySize))
|
2018-11-17 02:23:28 -05:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-11 19:58:57 -05:00
|
|
|
|
// Hardware and cpu options
|
|
|
|
|
if c.CoreCount < 0 {
|
|
|
|
|
errs = append(errs, fmt.Errorf("An invalid number of cores was specified (cores < 0): %d", c.CoreCount))
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-17 02:23:28 -05:00
|
|
|
|
// Peripherals
|
|
|
|
|
if !c.Sound {
|
|
|
|
|
c.Sound = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !c.USB {
|
|
|
|
|
c.USB = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.Parallel == "" {
|
|
|
|
|
c.Parallel = "none"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c.Serial == "" {
|
|
|
|
|
c.Serial = "none"
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-22 22:03:06 -05:00
|
|
|
|
return errs
|
2018-11-17 02:23:28 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* parallel port */
|
|
|
|
|
type ParallelUnion struct {
|
|
|
|
|
Union interface{}
|
|
|
|
|
File *ParallelPortFile
|
|
|
|
|
Device *ParallelPortDevice
|
|
|
|
|
Auto *ParallelPortAuto
|
|
|
|
|
}
|
|
|
|
|
type ParallelPortFile struct {
|
|
|
|
|
Filename string
|
|
|
|
|
}
|
|
|
|
|
type ParallelPortDevice struct {
|
|
|
|
|
Bidirectional string
|
|
|
|
|
Devicename string
|
|
|
|
|
}
|
|
|
|
|
type ParallelPortAuto struct {
|
|
|
|
|
Bidirectional string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *HWConfig) HasParallel() bool {
|
|
|
|
|
return c.Parallel != ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *HWConfig) ReadParallel() (*ParallelUnion, error) {
|
|
|
|
|
input := strings.SplitN(c.Parallel, ":", 2)
|
|
|
|
|
if len(input) < 1 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for parallel port: %s", c.Parallel)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var formatType, formatOptions string
|
|
|
|
|
formatType = input[0]
|
|
|
|
|
if len(input) == 2 {
|
|
|
|
|
formatOptions = input[1]
|
|
|
|
|
} else {
|
|
|
|
|
formatOptions = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch strings.ToUpper(formatType) {
|
|
|
|
|
case "FILE":
|
|
|
|
|
res := &ParallelPortFile{Filename: filepath.FromSlash(formatOptions)}
|
|
|
|
|
return &ParallelUnion{Union: res, File: res}, nil
|
|
|
|
|
case "DEVICE":
|
|
|
|
|
comp := strings.Split(formatOptions, ",")
|
|
|
|
|
if len(comp) < 1 || len(comp) > 2 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for parallel port: %s", c.Parallel)
|
|
|
|
|
}
|
|
|
|
|
res := new(ParallelPortDevice)
|
|
|
|
|
res.Bidirectional = "FALSE"
|
|
|
|
|
res.Devicename = filepath.FromSlash(comp[0])
|
|
|
|
|
if len(comp) > 1 {
|
|
|
|
|
switch strings.ToUpper(comp[1]) {
|
|
|
|
|
case "BI":
|
|
|
|
|
res.Bidirectional = "TRUE"
|
|
|
|
|
case "UNI":
|
|
|
|
|
res.Bidirectional = "FALSE"
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("Unknown direction %s specified for parallel port: %s", strings.ToUpper(comp[1]), c.Parallel)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return &ParallelUnion{Union: res, Device: res}, nil
|
|
|
|
|
|
|
|
|
|
case "AUTO":
|
|
|
|
|
res := new(ParallelPortAuto)
|
|
|
|
|
switch strings.ToUpper(formatOptions) {
|
|
|
|
|
case "":
|
|
|
|
|
fallthrough
|
|
|
|
|
case "UNI":
|
|
|
|
|
res.Bidirectional = "FALSE"
|
|
|
|
|
case "BI":
|
|
|
|
|
res.Bidirectional = "TRUE"
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("Unknown direction %s specified for parallel port: %s", strings.ToUpper(formatOptions), c.Parallel)
|
|
|
|
|
}
|
|
|
|
|
return &ParallelUnion{Union: res, Auto: res}, nil
|
|
|
|
|
|
|
|
|
|
case "NONE":
|
|
|
|
|
return &ParallelUnion{Union: nil}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for parallel port: %s", c.Parallel)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* serial conversions */
|
|
|
|
|
type SerialConfigPipe struct {
|
|
|
|
|
Filename string
|
|
|
|
|
Endpoint string
|
|
|
|
|
Host string
|
|
|
|
|
Yield string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SerialConfigFile struct {
|
|
|
|
|
Filename string
|
|
|
|
|
Yield string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SerialConfigDevice struct {
|
|
|
|
|
Devicename string
|
|
|
|
|
Yield string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SerialConfigAuto struct {
|
|
|
|
|
Devicename string
|
|
|
|
|
Yield string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SerialUnion struct {
|
|
|
|
|
Union interface{}
|
|
|
|
|
Pipe *SerialConfigPipe
|
|
|
|
|
File *SerialConfigFile
|
|
|
|
|
Device *SerialConfigDevice
|
|
|
|
|
Auto *SerialConfigAuto
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *HWConfig) HasSerial() bool {
|
|
|
|
|
return c.Serial != ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *HWConfig) ReadSerial() (*SerialUnion, error) {
|
|
|
|
|
var defaultSerialPort string
|
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
|
defaultSerialPort = "COM1"
|
|
|
|
|
} else {
|
|
|
|
|
defaultSerialPort = "/dev/ttyS0"
|
|
|
|
|
}
|
|
|
|
|
input := strings.SplitN(c.Serial, ":", 2)
|
|
|
|
|
if len(input) < 1 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for serial port: %s", c.Serial)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var formatType, formatOptions string
|
|
|
|
|
formatType = input[0]
|
|
|
|
|
if len(input) == 2 {
|
|
|
|
|
formatOptions = input[1]
|
|
|
|
|
} else {
|
|
|
|
|
formatOptions = ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch strings.ToUpper(formatType) {
|
|
|
|
|
case "PIPE":
|
|
|
|
|
comp := strings.Split(formatOptions, ",")
|
|
|
|
|
if len(comp) < 3 || len(comp) > 4 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for serial port pipe: %s", c.Serial)
|
|
|
|
|
}
|
|
|
|
|
if res := strings.ToLower(comp[1]); res != "client" && res != "server" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for endpoint in serial port pipe: %s -> %s", c.Serial, res)
|
|
|
|
|
}
|
|
|
|
|
if res := strings.ToLower(comp[2]); res != "app" && res != "vm" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for host in serial port pipe: %s -> %s", c.Serial, res)
|
|
|
|
|
}
|
|
|
|
|
res := &SerialConfigPipe{
|
|
|
|
|
Filename: comp[0],
|
|
|
|
|
Endpoint: comp[1],
|
|
|
|
|
Host: map[string]string{"app": "TRUE", "vm": "FALSE"}[strings.ToLower(comp[2])],
|
|
|
|
|
Yield: "FALSE",
|
|
|
|
|
}
|
|
|
|
|
if len(comp) == 4 {
|
|
|
|
|
res.Yield = strings.ToUpper(comp[3])
|
|
|
|
|
}
|
|
|
|
|
if res.Yield != "TRUE" && res.Yield != "FALSE" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for yield in serial port pipe: %s -> %s", c.Serial, res.Yield)
|
|
|
|
|
}
|
|
|
|
|
return &SerialUnion{Union: res, Pipe: res}, nil
|
|
|
|
|
|
|
|
|
|
case "FILE":
|
|
|
|
|
comp := strings.Split(formatOptions, ",")
|
|
|
|
|
if len(comp) > 2 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for serial port file: %s", c.Serial)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res := &SerialConfigFile{Yield: "FALSE"}
|
|
|
|
|
res.Filename = filepath.FromSlash(comp[0])
|
2020-07-23 03:40:57 -04:00
|
|
|
|
res.Yield = "FALSE"
|
|
|
|
|
if len(comp) > 1 {
|
|
|
|
|
res.Yield = strings.ToUpper(comp[1])
|
|
|
|
|
}
|
2018-11-17 02:23:28 -05:00
|
|
|
|
if res.Yield != "TRUE" && res.Yield != "FALSE" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for yield in serial port file: %s -> %s", c.Serial, res.Yield)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &SerialUnion{Union: res, File: res}, nil
|
|
|
|
|
|
|
|
|
|
case "DEVICE":
|
|
|
|
|
comp := strings.Split(formatOptions, ",")
|
|
|
|
|
if len(comp) > 2 {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for serial port device: %s", c.Serial)
|
|
|
|
|
}
|
|
|
|
|
res := new(SerialConfigDevice)
|
2020-07-23 03:40:57 -04:00
|
|
|
|
// set serial port defaults
|
|
|
|
|
res.Devicename = defaultSerialPort
|
|
|
|
|
res.Yield = "FALSE"
|
|
|
|
|
// Read actual values from component, if set.
|
|
|
|
|
if len(comp) == 1 {
|
|
|
|
|
filepath.FromSlash(comp[0])
|
|
|
|
|
} else if len(comp) == 2 {
|
|
|
|
|
res.Devicename = filepath.FromSlash(comp[0])
|
2018-11-17 02:23:28 -05:00
|
|
|
|
res.Yield = strings.ToUpper(comp[1])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res.Yield != "TRUE" && res.Yield != "FALSE" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for yield in serial port device: %s -> %s", c.Serial, res.Yield)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &SerialUnion{Union: res, Device: res}, nil
|
|
|
|
|
|
|
|
|
|
case "AUTO":
|
|
|
|
|
res := new(SerialConfigAuto)
|
|
|
|
|
res.Devicename = defaultSerialPort
|
|
|
|
|
|
|
|
|
|
if len(formatOptions) > 0 {
|
|
|
|
|
res.Yield = strings.ToUpper(formatOptions)
|
|
|
|
|
} else {
|
|
|
|
|
res.Yield = "FALSE"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res.Yield != "TRUE" && res.Yield != "FALSE" {
|
|
|
|
|
return nil, fmt.Errorf("Unexpected format for yield in serial port auto: %s -> %s", c.Serial, res.Yield)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &SerialUnion{Union: res, Auto: res}, nil
|
|
|
|
|
|
|
|
|
|
case "NONE":
|
|
|
|
|
return &SerialUnion{Union: nil}, nil
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return nil, fmt.Errorf("Unknown serial type %s: %s", strings.ToUpper(formatType), c.Serial)
|
|
|
|
|
}
|
|
|
|
|
}
|