From fc734b6bd9575905dbab5f69980f688151336f56 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 6 May 2018 07:54:58 +1000 Subject: [PATCH 1/3] Using vmconnect to display gui for hyper-v 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. --- builder/hyperv/common/driver.go | 10 +++++++++ builder/hyperv/common/driver_mock.go | 22 +++++++++++++++++++ builder/hyperv/common/driver_ps_4.go | 12 ++++++++++ builder/hyperv/common/step_run.go | 14 +++++++++++- builder/hyperv/iso/builder.go | 6 ++++- builder/hyperv/vmcx/builder.go | 6 ++++- common/powershell/hyperv/hyperv.go | 13 +++++++++++ .../docs/builders/hyperv-iso.html.md.erb | 5 +++++ .../docs/builders/hyperv-vmcx.html.md.erb | 5 +++++ 9 files changed, 90 insertions(+), 3 deletions(-) diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index bce4802279b..187c601cda8 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -1,5 +1,9 @@ package common +import ( + "context" +) + // A driver is able to talk to HyperV and perform certain // operations with it. Some of the operations on here may seem overly // specific, but they were built specifically in mind to handle features @@ -109,4 +113,10 @@ type Driver interface { MountFloppyDrive(string, string) error UnmountFloppyDrive(string) error + + // Connect connects to a VM specified by the name given. + Connect(string) context.CancelFunc + + // Disconnect disconnects to a VM specified by the context cancel function. + Disconnect(context.CancelFunc) } diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index f33cfe29fb9..674bd5b67ef 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -1,5 +1,9 @@ package common +import ( + "context" +) + type DriverMock struct { IsRunning_Called bool IsRunning_VmName string @@ -240,6 +244,13 @@ type DriverMock struct { UnmountFloppyDrive_Called bool UnmountFloppyDrive_VmName string UnmountFloppyDrive_Err error + + Connect_Called bool + Connect_VmName string + Connect_Cancel context.CancelFunc + + Disconnect_Called bool + Disconnect_Cancel context.CancelFunc } func (d *DriverMock) IsRunning(vmName string) (bool, error) { @@ -553,3 +564,14 @@ func (d *DriverMock) UnmountFloppyDrive(vmName string) error { d.UnmountFloppyDrive_VmName = vmName return d.UnmountFloppyDrive_Err } + +func (d *DriverMock) Connect(vmName string) context.CancelFunc { + d.Connect_Called = true + d.Connect_VmName = vmName + return d.Connect_Cancel +} + +func (d *DriverMock) Disconnect(cancel context.CancelFunc) { + d.Disconnect_Called = true + d.Disconnect_Cancel = cancel +} diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index ef948867a4e..4973ea0b0e1 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -1,6 +1,7 @@ package common import ( + "context" "fmt" "log" "runtime" @@ -347,3 +348,14 @@ func (d *HypervPS4Driver) verifyHypervPermissions() error { return nil } + +// Connect connects to a VM specified by the name given. +func (d *HypervPS4Driver) Connect(vmName string) context.CancelFunc { + return hyperv.ConnectVirtualMachine(vmName) +} + +// Disconnect disconnects to a VM specified by calling the context cancel function returned +// from Connect. +func (d *HypervPS4Driver) Disconnect(cancel context.CancelFunc) { + hyperv.DisconnectVirtualMachine(cancel) +} diff --git a/builder/hyperv/common/step_run.go b/builder/hyperv/common/step_run.go index 02996fb6f35..c6cf78d2e44 100644 --- a/builder/hyperv/common/step_run.go +++ b/builder/hyperv/common/step_run.go @@ -9,7 +9,8 @@ import ( ) type StepRun struct { - vmName string + Headless bool + vmName string } func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -29,6 +30,11 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste s.vmName = vmName + if !s.Headless { + ui.Say("Connecting to vmconnect...") + cancel := driver.Connect(vmName) + state.Put("guiCancelFunc", cancel) + } return multistep.ActionContinue } @@ -39,6 +45,12 @@ func (s *StepRun) Cleanup(state multistep.StateBag) { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) + guiCancelFunc := state.Get("guiCancelFunc").(context.CancelFunc) + + if guiCancelFunc != nil { + ui.Say("Disconnecting from vmconnect...") + guiCancelFunc() + } if running, _ := driver.IsRunning(s.vmName); running { if err := driver.Stop(s.vmName); err != nil { diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 7afd40c52e5..d50e5e26a05 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -115,6 +115,8 @@ type Config struct { // 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 } @@ -427,7 +429,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchVlanId: b.config.SwitchVlanId, }, - &hypervcommon.StepRun{}, + &hypervcommon.StepRun{ + Headless: b.config.Headless, + }, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 83d911e3018..987c41a121f 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -98,6 +98,8 @@ type Config struct { SkipExport bool `mapstructure:"skip_export"` + Headless bool `mapstructure:"headless"` + ctx interpolate.Context } @@ -436,7 +438,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchVlanId: b.config.SwitchVlanId, }, - &hypervcommon.StepRun{}, + &hypervcommon.StepRun{ + Headless: b.config.Headless, + }, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.FlatBootCommand(), diff --git a/common/powershell/hyperv/hyperv.go b/common/powershell/hyperv/hyperv.go index ef85d269e59..6503b23271d 100644 --- a/common/powershell/hyperv/hyperv.go +++ b/common/powershell/hyperv/hyperv.go @@ -1,7 +1,9 @@ package hyperv import ( + "context" "errors" + "os/exec" "strconv" "strings" @@ -1244,3 +1246,14 @@ param([string]$vmName, [string]$scanCodes) err := ps.Run(script, vmName, scanCodes) return err } + +func ConnectVirtualMachine(vmName string) context.CancelFunc { + ctx, cancel := context.WithCancel(context.Background()) + cmd := exec.CommandContext(ctx, "vmconnect.exe", "localhost", vmName) + cmd.Start() + return cancel +} + +func DisconnectVirtualMachine(cancel context.CancelFunc) { + cancel() +} diff --git a/website/source/docs/builders/hyperv-iso.html.md.erb b/website/source/docs/builders/hyperv-iso.html.md.erb index 85b093acc6b..a2a97a7890c 100644 --- a/website/source/docs/builders/hyperv-iso.html.md.erb +++ b/website/source/docs/builders/hyperv-iso.html.md.erb @@ -102,6 +102,11 @@ can be configured for this builder. - `differencing_disk` (boolean) - If true enables differencing disks. Only the changes will be written to the new disk. This is especially useful if your source is a vhd/vhdx. This defaults to false. +- `headless` (boolean) - Packer defaults to building Hyper-V virtual + machines by launching a GUI that shows the console of the machine + being built. When this value is set to true, the machine will start without + a console. + - `skip_export` (boolean) - If true skips VM export. If you are interested only in the vhd/vhdx files, you can enable this option. This will create inline disks which improves the build performance. There will not be any copying of source vhds to temp directory. This defaults to false. diff --git a/website/source/docs/builders/hyperv-vmcx.html.md.erb b/website/source/docs/builders/hyperv-vmcx.html.md.erb index 3433decda27..2bb00b0b89b 100644 --- a/website/source/docs/builders/hyperv-vmcx.html.md.erb +++ b/website/source/docs/builders/hyperv-vmcx.html.md.erb @@ -139,6 +139,11 @@ can be configured for this builder. - `guest_additions_path` (string) - The path to the iso image for guest additions. +- `headless` (boolean) - Packer defaults to building Hyper-V virtual + machines by launching a GUI that shows the console of the machine + being built. When this value is set to true, the machine will start without + a console. + - `http_directory` (string) - Path to a directory to serve using an HTTP server. The files in this directory will be available over HTTP that will be requestable from the virtual machine. This is useful for hosting From 29c4b4436de4737e4480fb365112ab158d5f5697 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 10 May 2018 21:50:56 +1000 Subject: [PATCH 2/3] Changes requested in PR #6243 - Logging error if vmconnect.exe fails. - Using StepRun struct rather than StateBag for command Cancel function - Better handling in Disconnect when headless is true or vmconnect failed in Start --- builder/hyperv/common/driver.go | 2 +- builder/hyperv/common/driver_mock.go | 5 +++-- builder/hyperv/common/driver_ps_4.go | 2 +- builder/hyperv/common/step_run.go | 17 ++++++++++------- common/powershell/hyperv/hyperv.go | 10 +++++++--- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/builder/hyperv/common/driver.go b/builder/hyperv/common/driver.go index 187c601cda8..53565292330 100644 --- a/builder/hyperv/common/driver.go +++ b/builder/hyperv/common/driver.go @@ -115,7 +115,7 @@ type Driver interface { UnmountFloppyDrive(string) error // Connect connects to a VM specified by the name given. - Connect(string) context.CancelFunc + Connect(string) (context.CancelFunc, error) // Disconnect disconnects to a VM specified by the context cancel function. Disconnect(context.CancelFunc) diff --git a/builder/hyperv/common/driver_mock.go b/builder/hyperv/common/driver_mock.go index 674bd5b67ef..3fcb4632320 100644 --- a/builder/hyperv/common/driver_mock.go +++ b/builder/hyperv/common/driver_mock.go @@ -248,6 +248,7 @@ type DriverMock struct { Connect_Called bool Connect_VmName string Connect_Cancel context.CancelFunc + Connect_Err error Disconnect_Called bool Disconnect_Cancel context.CancelFunc @@ -565,10 +566,10 @@ func (d *DriverMock) UnmountFloppyDrive(vmName string) error { return d.UnmountFloppyDrive_Err } -func (d *DriverMock) Connect(vmName string) context.CancelFunc { +func (d *DriverMock) Connect(vmName string) (context.CancelFunc, error) { d.Connect_Called = true d.Connect_VmName = vmName - return d.Connect_Cancel + return d.Connect_Cancel, d.Connect_Err } func (d *DriverMock) Disconnect(cancel context.CancelFunc) { diff --git a/builder/hyperv/common/driver_ps_4.go b/builder/hyperv/common/driver_ps_4.go index 4973ea0b0e1..cadaa2ba5c5 100644 --- a/builder/hyperv/common/driver_ps_4.go +++ b/builder/hyperv/common/driver_ps_4.go @@ -350,7 +350,7 @@ func (d *HypervPS4Driver) verifyHypervPermissions() error { } // Connect connects to a VM specified by the name given. -func (d *HypervPS4Driver) Connect(vmName string) context.CancelFunc { +func (d *HypervPS4Driver) Connect(vmName string) (context.CancelFunc, error) { return hyperv.ConnectVirtualMachine(vmName) } diff --git a/builder/hyperv/common/step_run.go b/builder/hyperv/common/step_run.go index c6cf78d2e44..13bf640dfa9 100644 --- a/builder/hyperv/common/step_run.go +++ b/builder/hyperv/common/step_run.go @@ -3,14 +3,16 @@ package common import ( "context" "fmt" + "log" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) type StepRun struct { - Headless bool - vmName string + GuiCancelFunc context.CancelFunc + Headless bool + vmName string } func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { @@ -32,8 +34,10 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste if !s.Headless { ui.Say("Connecting to vmconnect...") - cancel := driver.Connect(vmName) - state.Put("guiCancelFunc", cancel) + s.GuiCancelFunc, err = driver.Connect(vmName) + if err != nil { + log.Printf(fmt.Sprintf("Non-fatal error starting vmconnect: %s. continuing...", err)) + } } return multistep.ActionContinue } @@ -45,11 +49,10 @@ func (s *StepRun) Cleanup(state multistep.StateBag) { driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) - guiCancelFunc := state.Get("guiCancelFunc").(context.CancelFunc) - if guiCancelFunc != nil { + if !s.Headless && s.GuiCancelFunc != nil { ui.Say("Disconnecting from vmconnect...") - guiCancelFunc() + s.GuiCancelFunc() } if running, _ := driver.IsRunning(s.vmName); running { diff --git a/common/powershell/hyperv/hyperv.go b/common/powershell/hyperv/hyperv.go index 6503b23271d..f1ed1e45a92 100644 --- a/common/powershell/hyperv/hyperv.go +++ b/common/powershell/hyperv/hyperv.go @@ -1247,11 +1247,15 @@ param([string]$vmName, [string]$scanCodes) return err } -func ConnectVirtualMachine(vmName string) context.CancelFunc { +func ConnectVirtualMachine(vmName string) (context.CancelFunc, error) { ctx, cancel := context.WithCancel(context.Background()) cmd := exec.CommandContext(ctx, "vmconnect.exe", "localhost", vmName) - cmd.Start() - return cancel + err := cmd.Start() + if err != nil { + // Failed to start so cancel function not required + cancel = nil + } + return cancel, err } func DisconnectVirtualMachine(cancel context.CancelFunc) { From 5710c0aca17979c519422b1e2572820c640e2782 Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 14 May 2018 20:53:51 +1000 Subject: [PATCH 3/3] Making log output clearer for hyper-v gui connection --- builder/hyperv/common/step_run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/hyperv/common/step_run.go b/builder/hyperv/common/step_run.go index 13bf640dfa9..bb415ffa149 100644 --- a/builder/hyperv/common/step_run.go +++ b/builder/hyperv/common/step_run.go @@ -33,7 +33,7 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste s.vmName = vmName if !s.Headless { - ui.Say("Connecting to vmconnect...") + ui.Say("Attempting to connect with vmconnect...") s.GuiCancelFunc, err = driver.Connect(vmName) if err != nil { log.Printf(fmt.Sprintf("Non-fatal error starting vmconnect: %s. continuing...", err))