Skip to content

Commit

Permalink
Merge pull request hashicorp#6243 from camjjack/hyper-v-gui
Browse files Browse the repository at this point in the history
Using vmconnect to display gui for hyper-v
  • Loading branch information
SwampDragons authored May 18, 2018
2 parents cbbbf55 + 5710c0a commit edb5c1f
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 3 deletions.
10 changes: 10 additions & 0 deletions builder/hyperv/common/driver.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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, error)

// Disconnect disconnects to a VM specified by the context cancel function.
Disconnect(context.CancelFunc)
}
23 changes: 23 additions & 0 deletions builder/hyperv/common/driver_mock.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package common

import (
"context"
)

type DriverMock struct {
IsRunning_Called bool
IsRunning_VmName string
Expand Down Expand Up @@ -240,6 +244,14 @@ type DriverMock struct {
UnmountFloppyDrive_Called bool
UnmountFloppyDrive_VmName string
UnmountFloppyDrive_Err error

Connect_Called bool
Connect_VmName string
Connect_Cancel context.CancelFunc
Connect_Err error

Disconnect_Called bool
Disconnect_Cancel context.CancelFunc
}

func (d *DriverMock) IsRunning(vmName string) (bool, error) {
Expand Down Expand Up @@ -553,3 +565,14 @@ func (d *DriverMock) UnmountFloppyDrive(vmName string) error {
d.UnmountFloppyDrive_VmName = vmName
return d.UnmountFloppyDrive_Err
}

func (d *DriverMock) Connect(vmName string) (context.CancelFunc, error) {
d.Connect_Called = true
d.Connect_VmName = vmName
return d.Connect_Cancel, d.Connect_Err
}

func (d *DriverMock) Disconnect(cancel context.CancelFunc) {
d.Disconnect_Called = true
d.Disconnect_Cancel = cancel
}
12 changes: 12 additions & 0 deletions builder/hyperv/common/driver_ps_4.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package common

import (
"context"
"fmt"
"log"
"runtime"
Expand Down Expand Up @@ -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, error) {
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)
}
17 changes: 16 additions & 1 deletion builder/hyperv/common/step_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package common
import (
"context"
"fmt"
"log"

"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)

type StepRun struct {
vmName string
GuiCancelFunc context.CancelFunc
Headless bool
vmName string
}

func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
Expand All @@ -29,6 +32,13 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste

s.vmName = vmName

if !s.Headless {
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))
}
}
return multistep.ActionContinue
}

Expand All @@ -40,6 +50,11 @@ func (s *StepRun) Cleanup(state multistep.StateBag) {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)

if !s.Headless && s.GuiCancelFunc != nil {
ui.Say("Disconnecting from vmconnect...")
s.GuiCancelFunc()
}

if running, _ := driver.IsRunning(s.vmName); running {
if err := driver.Stop(s.vmName); err != nil {
ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
Expand Down
6 changes: 5 additions & 1 deletion builder/hyperv/iso/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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(),
Expand Down
6 changes: 5 additions & 1 deletion builder/hyperv/vmcx/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ type Config struct {

SkipExport bool `mapstructure:"skip_export"`

Headless bool `mapstructure:"headless"`

ctx interpolate.Context
}

Expand Down Expand Up @@ -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(),
Expand Down
17 changes: 17 additions & 0 deletions common/powershell/hyperv/hyperv.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package hyperv

import (
"context"
"errors"
"os/exec"
"strconv"
"strings"

Expand Down Expand Up @@ -1244,3 +1246,18 @@ param([string]$vmName, [string]$scanCodes)
err := ps.Run(script, vmName, scanCodes)
return err
}

func ConnectVirtualMachine(vmName string) (context.CancelFunc, error) {
ctx, cancel := context.WithCancel(context.Background())
cmd := exec.CommandContext(ctx, "vmconnect.exe", "localhost", vmName)
err := cmd.Start()
if err != nil {
// Failed to start so cancel function not required
cancel = nil
}
return cancel, err
}

func DisconnectVirtualMachine(cancel context.CancelFunc) {
cancel()
}
5 changes: 5 additions & 0 deletions website/source/docs/builders/hyperv-iso.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
5 changes: 5 additions & 0 deletions website/source/docs/builders/hyperv-vmcx.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit edb5c1f

Please sign in to comment.