From 9fbd1e472adc1be00a038b59fa56dd7a775224db Mon Sep 17 00:00:00 2001 From: Taliesin Sisson Date: Sun, 7 Aug 2016 12:26:27 +0100 Subject: [PATCH] Add support for mac spoofing and dynamic memory. To enable nested virtualization, mac spoofing, no dynamic memory and at least 4gb of ram should be set for the vm. Set warning if this has not been done. Detected Virtualization Extensions are supported by the machine your are running on, as it only works for Windows 10 and Windows Server 2016 onwards. --- builder/hyperv/common/driver.go | 10 +++- builder/hyperv/common/driver_ps_4.go | 25 +++++++--- builder/hyperv/common/step_create_vm.go | 57 ++++++++++++++++----- builder/hyperv/iso/builder.go | 65 ++++++++++++++++++------ powershell/hyperv/hyperv.go | 66 ++++++++++++++++++++----- powershell/powershell.go | 17 +++++++ 6 files changed, 192 insertions(+), 48 deletions(-) diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 8cfe67582..35df38cb3 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -72,9 +72,15 @@ type Driver interface { DeleteVirtualMachine(string) error - SetVirtualMachineCpu(string, uint, bool) error + SetVirtualMachineCpuCount(string, uint) error - SetSecureBoot(string, bool) error + SetVirtualMachineMacSpoofing(string, bool) error + + SetVirtualMachineDynamicMemory(string, bool) error + + SetVirtualMachineSecureBoot(string, bool) error + + SetVirtualMachineVirtualizationExtensions(string, bool) error EnableVirtualMachineIntegrationService(string, string) error diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index df1355c59..1fa522034 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -6,12 +6,13 @@ package common import ( "fmt" - "github.com/mitchellh/packer/powershell" - "github.com/mitchellh/packer/powershell/hyperv" "log" "runtime" "strconv" "strings" + + "github.com/mitchellh/packer/powershell" + "github.com/mitchellh/packer/powershell/hyperv" ) type HypervPS4Driver struct { @@ -177,12 +178,24 @@ func (d *HypervPS4Driver) DeleteVirtualMachine(vmName string) error { return hyperv.DeleteVirtualMachine(vmName) } -func (d *HypervPS4Driver) SetVirtualMachineCpu(vmName string, cpu uint, exposeVirtualizationExtensions bool) error { - return hyperv.SetVirtualMachineCpu(vmName, cpu, exposeVirtualizationExtensions) +func (d *HypervPS4Driver) SetVirtualMachineCpuCount(vmName string, cpu uint) error { + return hyperv.SetVirtualMachineCpuCount(vmName, cpu) +} + +func (d *HypervPS4Driver) SetVirtualMachineMacSpoofing(vmName string, enable bool) error { + return hyperv.SetVirtualMachineMacSpoofing(vmName, enable) +} + +func (d *HypervPS4Driver) SetVirtualMachineDynamicMemory(vmName string, enable bool) error { + return hyperv.SetVirtualMachineDynamicMemory(vmName, enable) +} + +func (d *HypervPS4Driver) SetVirtualMachineSecureBoot(vmName string, enable bool) error { + return hyperv.SetVirtualMachineSecureBoot(vmName, enable) } -func (d *HypervPS4Driver) SetSecureBoot(vmName string, enable bool) error { - return hyperv.SetSecureBoot(vmName, enable) +func (d *HypervPS4Driver) SetVirtualMachineVirtualizationExtensions(vmName string, enable bool) error { + return hyperv.SetVirtualMachineVirtualizationExtensions(vmName, enable) } func (d *HypervPS4Driver) EnableVirtualMachineIntegrationService(vmName string, integrationServiceName string) error { diff --git a/builder/hyperv/common/step_create_vm.go b/builder/hyperv/common/step_create_vm.go index c03636f30..54a5a7103 100644 --- a/builder/hyperv/common/step_create_vm.go +++ b/builder/hyperv/common/step_create_vm.go @@ -6,6 +6,7 @@ package common import ( "fmt" + "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" ) @@ -15,13 +16,15 @@ import ( // Produces: // VMName string - The name of the VM type StepCreateVM struct { - VMName string - SwitchName string - RamSizeMB uint - DiskSize uint - Generation uint - Cpu uint - EnableSecureBoot bool + VMName string + SwitchName string + RamSizeMB uint + DiskSize uint + Generation uint + Cpu uint + EnableMacSpoofing bool + EnableDynamicMemory bool + EnableSecureBoot bool EnableVirtualizationExtensions bool } @@ -36,10 +39,7 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { ram := int64(s.RamSizeMB * 1024 * 1024) diskSize := int64(s.DiskSize * 1024 * 1024) - switchName := s.SwitchName - enableSecureBoot := s.EnableSecureBoot - - err := driver.CreateVirtualMachine(s.VMName, path, ram, diskSize, switchName, s.Generation) + err := driver.CreateVirtualMachine(s.VMName, path, ram, diskSize, s.SwitchName, s.Generation) if err != nil { err := fmt.Errorf("Error creating virtual machine: %s", err) state.Put("error", err) @@ -47,7 +47,7 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - err = driver.SetVirtualMachineCpu(s.VMName, s.Cpu, s.EnableVirtualizationExtensions) + err = driver.SetVirtualMachineCpuCount(s.VMName, s.Cpu) if err != nil { err := fmt.Errorf("Error creating setting virtual machine cpu: %s", err) state.Put("error", err) @@ -55,8 +55,28 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + if s.EnableDynamicMemory { + err = driver.SetVirtualMachineDynamicMemory(s.VMName, s.EnableDynamicMemory) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine dynamic memory: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + + if s.EnableMacSpoofing { + err = driver.SetVirtualMachineMacSpoofing(s.VMName, s.EnableMacSpoofing) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine mac spoofing: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + if s.Generation == 2 { - err = driver.SetSecureBoot(s.VMName, enableSecureBoot) + err = driver.SetVirtualMachineSecureBoot(s.VMName, s.EnableSecureBoot) if err != nil { err := fmt.Errorf("Error setting secure boot: %s", err) state.Put("error", err) @@ -65,6 +85,17 @@ func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction { } } + if s.EnableVirtualizationExtensions { + //This is only supported on Windows 10 and Windows Server 2016 onwards + err = driver.SetVirtualMachineVirtualizationExtensions(s.VMName, s.EnableVirtualizationExtensions) + if err != nil { + err := fmt.Errorf("Error creating setting virtual machine virtualization extensions: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + } + // Set the final name in the state bag so others can use it state.Put("vmName", s.VMName) diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 274dca2ea..0772e24ba 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -28,9 +28,10 @@ const ( MinDiskSize = 256 // 256MB MaxDiskSize = 64 * 1024 * 1024 // 64TB - DefaultRamSize = 1 * 1024 // 1GB - MinRamSize = 32 // 32MB - MaxRamSize = 32 * 1024 // 32GB + DefaultRamSize = 1 * 1024 // 1GB + MinRamSize = 32 // 32MB + MaxRamSize = 32 * 1024 // 32GB + MinNestedVirtualizationRamSize = 4 * 1024 // 4GB LowRam = 256 // 256MB @@ -84,12 +85,14 @@ type Config struct { // By default this is "packer-BUILDNAME", where "BUILDNAME" is the name of the build. VMName string `mapstructure:"vm_name"` - BootCommand []string `mapstructure:"boot_command"` - SwitchName string `mapstructure:"switch_name"` - Cpu uint `mapstructure:"cpu"` - Generation uint `mapstructure:"generation"` - EnableSecureBoot bool `mapstructure:"enable_secure_boot"` - EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"` + BootCommand []string `mapstructure:"boot_command"` + SwitchName string `mapstructure:"switch_name"` + 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"` + EnableVirtualizationExtensions bool `mapstructure:"enable_virtualization_extensions"` Communicator string `mapstructure:"communicator"` @@ -227,6 +230,17 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } } + 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.")) + } + } + } + // Warnings if b.config.ShutdownCommand == "" { @@ -240,6 +254,23 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { 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.RamSizeMB < 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 errs != nil && len(errs.Errors) > 0 { return warnings, errs } @@ -292,13 +323,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchName: b.config.SwitchName, }, &hypervcommon.StepCreateVM{ - VMName: b.config.VMName, - SwitchName: b.config.SwitchName, - RamSizeMB: b.config.RamSizeMB, - DiskSize: b.config.DiskSize, - Generation: b.config.Generation, - Cpu: b.config.Cpu, - EnableSecureBoot: b.config.EnableSecureBoot, + VMName: b.config.VMName, + SwitchName: b.config.SwitchName, + RamSizeMB: b.config.RamSizeMB, + DiskSize: b.config.DiskSize, + Generation: b.config.Generation, + Cpu: b.config.Cpu, + EnableMacSpoofing: b.config.EnableMacSpoofing, + EnableDynamicMemory: b.config.EnableDynamicMemory, + EnableSecureBoot: b.config.EnableSecureBoot, EnableVirtualizationExtensions: b.config.EnableVirtualizationExtensions, }, &hypervcommon.StepEnableIntegrationService{}, diff --git a/powershell/hyperv/hyperv.go b/powershell/hyperv/hyperv.go index f242dfa3c..67e26216a 100644 --- a/powershell/hyperv/hyperv.go +++ b/powershell/hyperv/hyperv.go @@ -217,23 +217,67 @@ New-VM -Name $vmName -Path $path -MemoryStartupBytes $memoryStartupBytes -NewVHD } } -func SetVirtualMachineCpu(vmName string, cpu uint, enableVirtualizationExtensions bool) error { +func SetVirtualMachineCpuCount(vmName string, cpu uint) error { var script = ` -param([string]$vmName, [int]$cpu, [string]$exposeVirtualizationExtensions) -$nested = [System.Boolean]::Parse($exposeVirtualizationExtensions) -Set-VMProcessor -VMName $vmName -Count $cpu -exposeVirtualizationExtensions $nested +param([string]$vmName, [int]$cpu) +Set-VMProcessor -VMName $vmName -Count $cpu +` + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10)) + return err +} + +func SetVirtualMachineVirtualizationExtensions(vmName string, enableVirtualizationExtensions bool) error { + + var script = ` +param([string]$vmName, [string]$exposeVirtualizationExtensionsString) +$exposeVirtualizationExtensions = [System.Boolean]::Parse($exposeVirtualizationExtensionsString) +Set-VMProcessor -VMName $vmName -ExposeVirtualizationExtensions $exposeVirtualizationExtensions ` exposeVirtualizationExtensionsString := "False" if enableVirtualizationExtensions { exposeVirtualizationExtensionsString = "True" - } + } + var ps powershell.PowerShellCmd + err := ps.Run(script, vmName, exposeVirtualizationExtensionsString) + return err +} + +func SetVirtualMachineDynamicMemory(vmName string, enableDynamicMemory bool) error { + + var script = ` +param([string]$vmName, [string]$enableDynamicMemoryString) +$enableDynamicMemory = [System.Boolean]::Parse($enableDynamicMemoryString) +Set-VMMemory -VMName $vmName -DynamicMemoryEnabled $enableDynamicMemory +` + enableDynamicMemoryString := "False" + if enableDynamicMemory { + enableDynamicMemoryString = "True" + } var ps powershell.PowerShellCmd - err := ps.Run(script, vmName, strconv.FormatInt(int64(cpu), 10), exposeVirtualizationExtensionsString) + err := ps.Run(script, vmName, enableDynamicMemoryString) + return err +} + +func SetVirtualMachineMacSpoofing(vmName string, enableMacSpoofing bool) error { + var script = ` +param([string]$vmName, $enableMacSpoofing) +Set-VMNetworkAdapter -VMName $vmName -MacAddressSpoofing $enableMacSpoofing +` + + var ps powershell.PowerShellCmd + + enableMacSpoofingString := "Off" + if enableMacSpoofing { + enableMacSpoofingString = "On" + } + + err := ps.Run(script, vmName, enableMacSpoofingString) return err } -func SetSecureBoot(vmName string, enable bool) error { +func SetVirtualMachineSecureBoot(vmName string, enableSecureBoot bool) error { var script = ` param([string]$vmName, $enableSecureBoot) Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot @@ -241,12 +285,12 @@ Set-VMFirmware -VMName $vmName -EnableSecureBoot $enableSecureBoot var ps powershell.PowerShellCmd - enableSecureBoot := "Off" - if enable { - enableSecureBoot = "On" + enableSecureBootString := "Off" + if enableSecureBoot { + enableSecureBootString = "On" } - err := ps.Run(script, vmName, enableSecureBoot) + err := ps.Run(script, vmName, enableSecureBootString) return err } diff --git a/powershell/powershell.go b/powershell/powershell.go index 044ec6384..93ba91150 100644 --- a/powershell/powershell.go +++ b/powershell/powershell.go @@ -231,6 +231,23 @@ param([string]$moduleName) return true, nil } +func HasVirtualMachineVirtualizationExtensions() (bool, error) { + + var script = ` +(GET-Command Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions" +` + + var ps PowerShellCmd + cmdOut, err := ps.Output(script) + + if err != nil { + return false, err + } + + var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True" + return hasVirtualMachineVirtualizationExtensions, err +} + func SetUnattendedProductKey(path string, productKey string) error { var script = `