vmconnect.exe comes as part of Hyper-V and is the tool used by Hyper-V Manager to connect with a virtual machine. This commits sets behaviour the same as virtualbox and vmware to display the virtual machine connection unless headless is set in the template.
598 lines
20 KiB
Go
598 lines
20 KiB
Go
package iso
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
hypervcommon "github.com/hashicorp/packer/builder/hyperv/common"
|
|
"github.com/hashicorp/packer/common"
|
|
"github.com/hashicorp/packer/common/bootcommand"
|
|
powershell "github.com/hashicorp/packer/common/powershell"
|
|
"github.com/hashicorp/packer/common/powershell/hyperv"
|
|
"github.com/hashicorp/packer/helper/communicator"
|
|
"github.com/hashicorp/packer/helper/config"
|
|
"github.com/hashicorp/packer/helper/multistep"
|
|
"github.com/hashicorp/packer/packer"
|
|
"github.com/hashicorp/packer/template/interpolate"
|
|
)
|
|
|
|
const (
|
|
DefaultDiskSize = 40 * 1024 // ~40GB
|
|
MinDiskSize = 256 // 256MB
|
|
MaxDiskSize = 64 * 1024 * 1024 // 64TB
|
|
MaxVHDSize = 2040 * 1024 // 2040GB
|
|
|
|
DefaultDiskBlockSize = 32 // 32MB
|
|
MinDiskBlockSize = 1 // 1MB
|
|
MaxDiskBlockSize = 256 // 256MB
|
|
|
|
DefaultRamSize = 1 * 1024 // 1GB
|
|
MinRamSize = 32 // 32MB
|
|
MaxRamSize = 32 * 1024 // 32GB
|
|
MinNestedVirtualizationRamSize = 4 * 1024 // 4GB
|
|
|
|
LowRam = 256 // 256MB
|
|
|
|
DefaultUsername = ""
|
|
DefaultPassword = ""
|
|
)
|
|
|
|
// Builder implements packer.Builder and builds the actual Hyperv
|
|
// images.
|
|
type Builder struct {
|
|
config Config
|
|
runner multistep.Runner
|
|
}
|
|
|
|
type Config struct {
|
|
common.PackerConfig `mapstructure:",squash"`
|
|
common.HTTPConfig `mapstructure:",squash"`
|
|
common.ISOConfig `mapstructure:",squash"`
|
|
common.FloppyConfig `mapstructure:",squash"`
|
|
bootcommand.BootConfig `mapstructure:",squash"`
|
|
hypervcommon.OutputConfig `mapstructure:",squash"`
|
|
hypervcommon.SSHConfig `mapstructure:",squash"`
|
|
hypervcommon.ShutdownConfig `mapstructure:",squash"`
|
|
|
|
// The size, in megabytes, of the hard disk to create for the VM.
|
|
// By default, this is 130048 (about 127 GB).
|
|
DiskSize uint `mapstructure:"disk_size"`
|
|
|
|
// The size, in megabytes, of the block size used to create the hard disk.
|
|
// By default, this is 32768 (about 32 MB)
|
|
DiskBlockSize uint `mapstructure:"disk_block_size"`
|
|
|
|
// The size, in megabytes, of the computer memory in the VM.
|
|
// By default, this is 1024 (about 1 GB).
|
|
RamSize uint `mapstructure:"ram_size"`
|
|
|
|
//
|
|
SecondaryDvdImages []string `mapstructure:"secondary_iso_images"`
|
|
|
|
// Should integration services iso be mounted
|
|
GuestAdditionsMode string `mapstructure:"guest_additions_mode"`
|
|
|
|
// The path to the integration services iso
|
|
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
|
|
|
// This is the name of the new virtual machine.
|
|
// By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build.
|
|
VMName string `mapstructure:"vm_name"`
|
|
|
|
SwitchName string `mapstructure:"switch_name"`
|
|
SwitchVlanId string `mapstructure:"switch_vlan_id"`
|
|
MacAddress string `mapstructure:"mac_address"`
|
|
VlanId string `mapstructure:"vlan_id"`
|
|
Cpu uint `mapstructure:"cpu"`
|
|
Generation uint `mapstructure:"generation"`
|
|
EnableMacSpoofing bool `mapstructure:"enable_mac_spoofing"`
|
|
EnableDynamicMemory bool `mapstructure:"enable_dynamic_memory"`
|
|
EnableSecureBoot bool `mapstructure:"enable_secure_boot"`
|
|
SecureBootTemplate string `mapstructure:"secure_boot_template"`
|
|
EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"`
|
|
TempPath string `mapstructure:"temp_path"`
|
|
|
|
// A separate path can be used for storing the VM's disk image. The purpose is to enable
|
|
// reading and writing to take place on different physical disks (read from VHD temp path
|
|
// write to regular temp path while exporting the VM) to eliminate a single-disk bottleneck.
|
|
VhdTempPath string `mapstructure:"vhd_temp_path"`
|
|
|
|
Communicator string `mapstructure:"communicator"`
|
|
|
|
AdditionalDiskSize []uint `mapstructure:"disk_additional_size"`
|
|
|
|
SkipCompaction bool `mapstructure:"skip_compaction"`
|
|
|
|
SkipExport bool `mapstructure:"skip_export"`
|
|
|
|
// Use differencing disk
|
|
DifferencingDisk bool `mapstructure:"differencing_disk"`
|
|
|
|
// Create the VM with a Fixed VHD format disk instead of Dynamic VHDX
|
|
FixedVHD bool `mapstructure:"use_fixed_vhd_format"`
|
|
|
|
Headless bool `mapstructure:"headless"`
|
|
|
|
ctx interpolate.Context
|
|
}
|
|
|
|
// Prepare processes the build configuration parameters.
|
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
|
Interpolate: true,
|
|
InterpolateContext: &b.config.ctx,
|
|
InterpolateFilter: &interpolate.RenderFilter{
|
|
Exclude: []string{
|
|
"boot_command",
|
|
},
|
|
},
|
|
}, raws...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Accumulate any errors and warnings
|
|
var errs *packer.MultiError
|
|
warnings := make([]string, 0)
|
|
|
|
isoWarnings, isoErrs := b.config.ISOConfig.Prepare(&b.config.ctx)
|
|
warnings = append(warnings, isoWarnings...)
|
|
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
|
|
|
errs = packer.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.FloppyConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.HTTPConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.OutputConfig.Prepare(&b.config.ctx, &b.config.PackerConfig)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.SSHConfig.Prepare(&b.config.ctx)...)
|
|
errs = packer.MultiErrorAppend(errs, b.config.ShutdownConfig.Prepare(&b.config.ctx)...)
|
|
|
|
if len(b.config.ISOConfig.ISOUrls) < 1 || (strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhd" && strings.ToLower(filepath.Ext(b.config.ISOConfig.ISOUrls[0])) != ".vhdx") {
|
|
//We only create a new hard drive if an existing one to copy from does not exist
|
|
err = b.checkDiskSize()
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
}
|
|
|
|
err = b.checkDiskBlockSize()
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
err = b.checkRamSize()
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
if b.config.VMName == "" {
|
|
b.config.VMName = fmt.Sprintf("packer-%s", b.config.PackerBuildName)
|
|
}
|
|
|
|
log.Println(fmt.Sprintf("%s: %v", "VMName", b.config.VMName))
|
|
|
|
if b.config.SwitchName == "" {
|
|
b.config.SwitchName = b.detectSwitchName()
|
|
}
|
|
|
|
if b.config.Cpu < 1 {
|
|
b.config.Cpu = 1
|
|
}
|
|
|
|
if b.config.Generation < 1 || b.config.Generation > 2 {
|
|
b.config.Generation = 1
|
|
}
|
|
|
|
if b.config.Generation == 2 {
|
|
if len(b.config.FloppyFiles) > 0 || len(b.config.FloppyDirectories) > 0 {
|
|
err = errors.New("Generation 2 vms don't support floppy drives. Use ISO image instead.")
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
}
|
|
|
|
if len(b.config.AdditionalDiskSize) > 64 {
|
|
err = errors.New("VM's currently support a maximum of 64 additional SCSI attached disks.")
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
log.Println(fmt.Sprintf("Using switch %s", b.config.SwitchName))
|
|
log.Println(fmt.Sprintf("%s: %v", "SwitchName", b.config.SwitchName))
|
|
|
|
// Errors
|
|
|
|
if b.config.GuestAdditionsMode == "" {
|
|
if b.config.GuestAdditionsPath != "" {
|
|
b.config.GuestAdditionsMode = "attach"
|
|
} else {
|
|
b.config.GuestAdditionsPath = os.Getenv("WINDIR") + "\\system32\\vmguest.iso"
|
|
|
|
if _, err := os.Stat(b.config.GuestAdditionsPath); os.IsNotExist(err) {
|
|
if err != nil {
|
|
b.config.GuestAdditionsPath = ""
|
|
b.config.GuestAdditionsMode = "none"
|
|
} else {
|
|
b.config.GuestAdditionsMode = "attach"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if b.config.GuestAdditionsPath == "" && b.config.GuestAdditionsMode == "attach" {
|
|
b.config.GuestAdditionsPath = os.Getenv("WINDIR") + "\\system32\\vmguest.iso"
|
|
|
|
if _, err := os.Stat(b.config.GuestAdditionsPath); os.IsNotExist(err) {
|
|
if err != nil {
|
|
b.config.GuestAdditionsPath = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, isoPath := range b.config.SecondaryDvdImages {
|
|
if _, err := os.Stat(isoPath); os.IsNotExist(err) {
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(
|
|
errs, fmt.Errorf("Secondary Dvd image does not exist: %s", err))
|
|
}
|
|
}
|
|
}
|
|
|
|
numberOfIsos := len(b.config.SecondaryDvdImages)
|
|
|
|
if b.config.GuestAdditionsMode == "attach" {
|
|
if _, err := os.Stat(b.config.GuestAdditionsPath); os.IsNotExist(err) {
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(
|
|
errs, fmt.Errorf("Guest additions iso does not exist: %s", err))
|
|
}
|
|
}
|
|
|
|
numberOfIsos = numberOfIsos + 1
|
|
}
|
|
|
|
if b.config.Generation < 2 && numberOfIsos > 2 {
|
|
if b.config.GuestAdditionsMode == "attach" {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are only 2 ide controllers available, so we can't support guest additions and these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", ")))
|
|
} else {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are only 2 ide controllers available, so we can't support these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", ")))
|
|
}
|
|
} else if b.config.Generation > 1 && len(b.config.SecondaryDvdImages) > 16 {
|
|
if b.config.GuestAdditionsMode == "attach" {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are not enough drive letters available for scsi (limited to 16), so we can't support guest additions and these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", ")))
|
|
} else {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("There are not enough drive letters available for scsi (limited to 16), so we can't support these secondary dvds: %s", strings.Join(b.config.SecondaryDvdImages, ", ")))
|
|
}
|
|
}
|
|
|
|
if b.config.EnableVirtualizationExtensions {
|
|
hasVirtualMachineVirtualizationExtensions, err := powershell.HasVirtualMachineVirtualizationExtensions()
|
|
if err != nil {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed detecting virtual machine virtualization extensions support: %s", err))
|
|
} else {
|
|
if !hasVirtualMachineVirtualizationExtensions {
|
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("This version of Hyper-V does not support virtual machine virtualization extension. Please use Windows 10 or Windows Server 2016 or newer."))
|
|
}
|
|
}
|
|
}
|
|
|
|
if b.config.Generation > 1 && b.config.FixedVHD {
|
|
err = errors.New("Fixed VHD disks are only supported on Generation 1 virtual machines.")
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
if !b.config.SkipCompaction && b.config.FixedVHD {
|
|
err = errors.New("Fixed VHD disks do not support compaction.")
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
if b.config.DifferencingDisk && b.config.FixedVHD {
|
|
err = errors.New("Fixed VHD disks are not supported with differencing disks.")
|
|
errs = packer.MultiErrorAppend(errs, err)
|
|
}
|
|
|
|
// Warnings
|
|
|
|
if b.config.ShutdownCommand == "" {
|
|
warnings = append(warnings,
|
|
"A shutdown_command was not specified. Without a shutdown command, Packer\n"+
|
|
"will forcibly halt the virtual machine, which may result in data loss.")
|
|
}
|
|
|
|
warning := b.checkHostAvailableMemory()
|
|
if warning != "" {
|
|
warnings = appendWarnings(warnings, warning)
|
|
}
|
|
|
|
if b.config.EnableVirtualizationExtensions {
|
|
if b.config.EnableDynamicMemory {
|
|
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, dynamic memory should not be allowed.")
|
|
warnings = appendWarnings(warnings, warning)
|
|
}
|
|
|
|
if !b.config.EnableMacSpoofing {
|
|
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, mac spoofing should be allowed.")
|
|
warnings = appendWarnings(warnings, warning)
|
|
}
|
|
|
|
if b.config.RamSize < MinNestedVirtualizationRamSize {
|
|
warning = fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, there should be 4GB or more memory set for the vm, otherwise Hyper-V may fail to start any nested VMs.")
|
|
warnings = appendWarnings(warnings, warning)
|
|
}
|
|
}
|
|
|
|
if b.config.SwitchVlanId != "" {
|
|
if b.config.SwitchVlanId != b.config.VlanId {
|
|
warning = fmt.Sprintf("Switch network adaptor vlan should match virtual machine network adaptor vlan. The switch will not be able to see traffic from the VM.")
|
|
warnings = appendWarnings(warnings, warning)
|
|
}
|
|
}
|
|
|
|
if errs != nil && len(errs.Errors) > 0 {
|
|
return warnings, errs
|
|
}
|
|
|
|
return warnings, nil
|
|
}
|
|
|
|
// Run executes a Packer build and returns a packer.Artifact representing
|
|
// a Hyperv appliance.
|
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
|
// Create the driver that we'll use to communicate with Hyperv
|
|
driver, err := hypervcommon.NewHypervPS4Driver()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed creating Hyper-V driver: %s", err)
|
|
}
|
|
|
|
// Set up the state.
|
|
state := new(multistep.BasicStateBag)
|
|
state.Put("cache", cache)
|
|
state.Put("config", &b.config)
|
|
state.Put("debug", b.config.PackerDebug)
|
|
state.Put("driver", driver)
|
|
state.Put("hook", hook)
|
|
state.Put("ui", ui)
|
|
|
|
steps := []multistep.Step{
|
|
&hypervcommon.StepCreateTempDir{
|
|
TempPath: b.config.TempPath,
|
|
VhdTempPath: b.config.VhdTempPath,
|
|
},
|
|
&hypervcommon.StepOutputDir{
|
|
Force: b.config.PackerForce,
|
|
Path: b.config.OutputDir,
|
|
},
|
|
&common.StepDownload{
|
|
Checksum: b.config.ISOChecksum,
|
|
ChecksumType: b.config.ISOChecksumType,
|
|
Description: "ISO",
|
|
ResultKey: "iso_path",
|
|
Url: b.config.ISOUrls,
|
|
Extension: b.config.TargetExtension,
|
|
TargetPath: b.config.TargetPath,
|
|
},
|
|
&common.StepCreateFloppy{
|
|
Files: b.config.FloppyConfig.FloppyFiles,
|
|
Directories: b.config.FloppyConfig.FloppyDirectories,
|
|
},
|
|
&common.StepHTTPServer{
|
|
HTTPDir: b.config.HTTPDir,
|
|
HTTPPortMin: b.config.HTTPPortMin,
|
|
HTTPPortMax: b.config.HTTPPortMax,
|
|
},
|
|
&hypervcommon.StepCreateSwitch{
|
|
SwitchName: b.config.SwitchName,
|
|
},
|
|
&hypervcommon.StepCreateVM{
|
|
VMName: b.config.VMName,
|
|
SwitchName: b.config.SwitchName,
|
|
RamSize: b.config.RamSize,
|
|
DiskSize: b.config.DiskSize,
|
|
DiskBlockSize: b.config.DiskBlockSize,
|
|
Generation: b.config.Generation,
|
|
Cpu: b.config.Cpu,
|
|
EnableMacSpoofing: b.config.EnableMacSpoofing,
|
|
EnableDynamicMemory: b.config.EnableDynamicMemory,
|
|
EnableSecureBoot: b.config.EnableSecureBoot,
|
|
SecureBootTemplate: b.config.SecureBootTemplate,
|
|
EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions,
|
|
AdditionalDiskSize: b.config.AdditionalDiskSize,
|
|
DifferencingDisk: b.config.DifferencingDisk,
|
|
SkipExport: b.config.SkipExport,
|
|
OutputDir: b.config.OutputDir,
|
|
MacAddress: b.config.MacAddress,
|
|
FixedVHD: b.config.FixedVHD,
|
|
},
|
|
&hypervcommon.StepEnableIntegrationService{},
|
|
|
|
&hypervcommon.StepMountDvdDrive{
|
|
Generation: b.config.Generation,
|
|
},
|
|
&hypervcommon.StepMountFloppydrive{
|
|
Generation: b.config.Generation,
|
|
},
|
|
|
|
&hypervcommon.StepMountGuestAdditions{
|
|
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
|
GuestAdditionsPath: b.config.GuestAdditionsPath,
|
|
Generation: b.config.Generation,
|
|
},
|
|
|
|
&hypervcommon.StepMountSecondaryDvdImages{
|
|
IsoPaths: b.config.SecondaryDvdImages,
|
|
Generation: b.config.Generation,
|
|
},
|
|
|
|
&hypervcommon.StepConfigureVlan{
|
|
VlanId: b.config.VlanId,
|
|
SwitchVlanId: b.config.SwitchVlanId,
|
|
},
|
|
|
|
&hypervcommon.StepRun{
|
|
Headless: b.config.Headless,
|
|
},
|
|
|
|
&hypervcommon.StepTypeBootCommand{
|
|
BootCommand: b.config.FlatBootCommand(),
|
|
BootWait: b.config.BootWait,
|
|
SwitchName: b.config.SwitchName,
|
|
Ctx: b.config.ctx,
|
|
},
|
|
|
|
// configure the communicator ssh, winrm
|
|
&communicator.StepConnect{
|
|
Config: &b.config.SSHConfig.Comm,
|
|
Host: hypervcommon.CommHost,
|
|
SSHConfig: hypervcommon.SSHConfigFunc(&b.config.SSHConfig),
|
|
},
|
|
|
|
// provision requires communicator to be setup
|
|
&common.StepProvision{},
|
|
|
|
&hypervcommon.StepShutdown{
|
|
Command: b.config.ShutdownCommand,
|
|
Timeout: b.config.ShutdownTimeout,
|
|
},
|
|
|
|
// wait for the vm to be powered off
|
|
&hypervcommon.StepWaitForPowerOff{},
|
|
|
|
// remove the secondary dvd images
|
|
// after we power down
|
|
&hypervcommon.StepUnmountSecondaryDvdImages{},
|
|
&hypervcommon.StepUnmountGuestAdditions{},
|
|
&hypervcommon.StepUnmountDvdDrive{},
|
|
&hypervcommon.StepUnmountFloppyDrive{
|
|
Generation: b.config.Generation,
|
|
},
|
|
&hypervcommon.StepExportVm{
|
|
OutputDir: b.config.OutputDir,
|
|
SkipCompaction: b.config.SkipCompaction,
|
|
SkipExport: b.config.SkipExport,
|
|
},
|
|
|
|
// the clean up actions for each step will be executed reverse order
|
|
}
|
|
|
|
// Run the steps.
|
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
|
b.runner.Run(state)
|
|
|
|
// Report any errors.
|
|
if rawErr, ok := state.GetOk("error"); ok {
|
|
return nil, rawErr.(error)
|
|
}
|
|
|
|
// If we were interrupted or cancelled, then just exit.
|
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
|
return nil, errors.New("Build was cancelled.")
|
|
}
|
|
|
|
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
|
return nil, errors.New("Build was halted.")
|
|
}
|
|
|
|
return hypervcommon.NewArtifact(b.config.OutputDir)
|
|
}
|
|
|
|
// Cancel.
|
|
func (b *Builder) Cancel() {
|
|
if b.runner != nil {
|
|
log.Println("Cancelling the step runner...")
|
|
b.runner.Cancel()
|
|
}
|
|
}
|
|
|
|
func appendWarnings(slice []string, data ...string) []string {
|
|
m := len(slice)
|
|
n := m + len(data)
|
|
if n > cap(slice) { // if necessary, reallocate
|
|
// allocate double what's needed, for future growth.
|
|
newSlice := make([]string, (n+1)*2)
|
|
copy(newSlice, slice)
|
|
slice = newSlice
|
|
}
|
|
slice = slice[0:n]
|
|
copy(slice[m:n], data)
|
|
return slice
|
|
}
|
|
|
|
func (b *Builder) checkDiskSize() error {
|
|
if b.config.DiskSize == 0 {
|
|
b.config.DiskSize = DefaultDiskSize
|
|
}
|
|
|
|
log.Println(fmt.Sprintf("%s: %v", "DiskSize", b.config.DiskSize))
|
|
|
|
if b.config.DiskSize < MinDiskSize {
|
|
return fmt.Errorf("disk_size: Virtual machine requires disk space >= %v GB, but defined: %v", MinDiskSize, b.config.DiskSize/1024)
|
|
} else if b.config.DiskSize > MaxDiskSize && !b.config.FixedVHD {
|
|
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v", MaxDiskSize, b.config.DiskSize/1024)
|
|
} else if b.config.DiskSize > MaxVHDSize && b.config.FixedVHD {
|
|
return fmt.Errorf("disk_size: Virtual machine requires disk space <= %v GB, but defined: %v", MaxVHDSize/1024, b.config.DiskSize/1024)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) checkDiskBlockSize() error {
|
|
if b.config.DiskBlockSize == 0 {
|
|
b.config.DiskBlockSize = DefaultDiskBlockSize
|
|
}
|
|
|
|
log.Println(fmt.Sprintf("%s: %v", "DiskBlockSize", b.config.DiskBlockSize))
|
|
|
|
if b.config.DiskBlockSize < MinDiskBlockSize {
|
|
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size >= %v MB, but defined: %v", MinDiskBlockSize, b.config.DiskBlockSize)
|
|
} else if b.config.DiskBlockSize > MaxDiskBlockSize {
|
|
return fmt.Errorf("disk_block_size: Virtual machine requires disk block size <= %v MB, but defined: %v", MaxDiskBlockSize, b.config.DiskBlockSize)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) checkRamSize() error {
|
|
if b.config.RamSize == 0 {
|
|
b.config.RamSize = DefaultRamSize
|
|
}
|
|
|
|
log.Println(fmt.Sprintf("%s: %v", "RamSize", b.config.RamSize))
|
|
|
|
if b.config.RamSize < MinRamSize {
|
|
return fmt.Errorf("ram_size: Virtual machine requires memory size >= %v MB, but defined: %v", MinRamSize, b.config.RamSize)
|
|
} else if b.config.RamSize > MaxRamSize {
|
|
return fmt.Errorf("ram_size: Virtual machine requires memory size <= %v MB, but defined: %v", MaxRamSize, b.config.RamSize)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) checkHostAvailableMemory() string {
|
|
powershellAvailable, _, _ := powershell.IsPowershellAvailable()
|
|
|
|
if powershellAvailable {
|
|
freeMB := powershell.GetHostAvailableMemory()
|
|
|
|
if (freeMB - float64(b.config.RamSize)) < LowRam {
|
|
return fmt.Sprintf("Hyper-V might fail to create a VM if there is not enough free memory in the system.")
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func (b *Builder) detectSwitchName() string {
|
|
powershellAvailable, _, _ := powershell.IsPowershellAvailable()
|
|
|
|
if powershellAvailable {
|
|
// no switch name, try to get one attached to a online network adapter
|
|
onlineSwitchName, err := hyperv.GetExternalOnlineVirtualSwitch()
|
|
if onlineSwitchName != "" && err == nil {
|
|
return onlineSwitchName
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("packer-%s", b.config.PackerBuildName)
|
|
}
|