//go:generate struct-markdown package common import ( "fmt" "path/filepath" "runtime" "strings" "github.com/hashicorp/packer/template/interpolate" ) type HWConfig struct { // The number of cpus to use when building the VM. CpuCount int `mapstructure:"cpus" required:"false"` // The amount of memory to use when building the VM in megabytes. MemorySize int `mapstructure:"memory" required:"false"` // The number of cores per socket to use when building the VM. This // corresponds to the cpuid.coresPerSocket option in the .vmx file. CoreCount int `mapstructure:"cores" required:"false"` // 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) Network string `mapstructure:"network" required:"false"` // 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 // clients. For ESXi, refer to the proper ESXi documentation. NetworkAdapterType string `mapstructure:"network_adapter_type" required:"false"` // The custom name of the network. Sets the vmx value "ethernet0.networkName" NetworkName string `mapstructure:"network_name" required:"false"` // Specify whether to enable VMware's virtual soundcard device when // building the VM. Defaults to false. Sound bool `mapstructure:"sound" required:"false"` // 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. USB bool `mapstructure:"usb" required:"false"` // 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. // // * `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`. // // * `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. // // * `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`. // // * `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. // // * `endpoint` (string) - Chooses the type of the VM-end, which can be // either a `client` or `server`. // // * `host` (string) - Chooses the type of the host-end, which can // be either `app` (application) or `vm` (another virtual-machine). // // * `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`. // // * `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. // // * `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`. // // * `NONE` - Specifies to not use a serial port. (default) // Serial string `mapstructure:"serial" required:"false"` // 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) Parallel string `mapstructure:"parallel" required:"false"` } func (c *HWConfig) Prepare(ctx *interpolate.Context) []error { var errs []error // Hardware and cpu options if c.CpuCount < 0 { errs = append(errs, fmt.Errorf("An invalid number of cpus was specified (cpus < 0): %d", c.CpuCount)) } if c.MemorySize < 0 { errs = append(errs, fmt.Errorf("An invalid amount of memory was specified (memory < 0): %d", c.MemorySize)) } // 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)) } // 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" } return errs } /* 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]) res.Yield = "FALSE" if len(comp) > 1 { res.Yield = strings.ToUpper(comp[1]) } 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) // 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]) 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) } }