Allow configurable VNC bind IP for QEMU

This commit allows for a user configurable VNC bind IP to be used for
QEMU.

By default this will be 127.0.0.1, alternatively, a user can use
0.0.0.0 which will bind to all interfaces.

This pull request should satisfy concerns of #3570 and #3559. It is
also in-line with the functionality provided by #3566 and #3565

Signed-off-by: Ian Duffy <ian@ianduffy.ie>
This commit is contained in:
Ian Duffy 2016-05-25 10:10:12 +01:00
parent 7759229b9d
commit fa273f3bea
5 changed files with 50 additions and 7 deletions

View File

@ -104,6 +104,7 @@ type Config struct {
ShutdownCommand string `mapstructure:"shutdown_command"` ShutdownCommand string `mapstructure:"shutdown_command"`
SSHHostPortMin uint `mapstructure:"ssh_host_port_min"` SSHHostPortMin uint `mapstructure:"ssh_host_port_min"`
SSHHostPortMax uint `mapstructure:"ssh_host_port_max"` SSHHostPortMax uint `mapstructure:"ssh_host_port_max"`
VNCBindAddress string `mapstructure:"vnc_bind_address"`
VNCPortMin uint `mapstructure:"vnc_port_min"` VNCPortMin uint `mapstructure:"vnc_port_min"`
VNCPortMax uint `mapstructure:"vnc_port_max"` VNCPortMax uint `mapstructure:"vnc_port_max"`
VMName string `mapstructure:"vm_name"` VMName string `mapstructure:"vm_name"`
@ -194,6 +195,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.SSHHostPortMax = 4444 b.config.SSHHostPortMax = 4444
} }
if b.config.VNCBindAddress == "" {
b.config.VNCBindAddress = "127.0.0.1"
}
if b.config.VNCPortMin == 0 { if b.config.VNCPortMin == 0 {
b.config.VNCPortMin = 5900 b.config.VNCPortMin = 5900
} }

View File

@ -132,6 +132,25 @@ func TestBuilderPrepare_BootWait(t *testing.T) {
} }
} }
func TestBuilderPrepare_VNCBindAddress(t *testing.T) {
var b Builder
config := testConfig()
// Test a default boot_wait
delete(config, "vnc_bind_address")
warns, err := b.Prepare(config)
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("err: %s", err)
}
if b.config.VNCBindAddress != "127.0.0.1" {
t.Fatalf("bad value: %s", b.config.VNCBindAddress)
}
}
func TestBuilderPrepare_DiskCompaction(t *testing.T) { func TestBuilderPrepare_DiskCompaction(t *testing.T) {
var b Builder var b Builder
config := testConfig() config := testConfig()

View File

@ -26,7 +26,7 @@ func (stepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction {
// Find an open VNC port. Note that this can still fail later on // Find an open VNC port. Note that this can still fail later on
// because we have to release the port at some point. But this does its // because we have to release the port at some point. But this does its
// best. // best.
msg := fmt.Sprintf("Looking for available port between %d and %d", config.VNCPortMin, config.VNCPortMax) msg := fmt.Sprintf("Looking for available port between %d and %d on %s", config.VNCPortMin, config.VNCPortMax, config.VNCBindAddress)
ui.Say(msg) ui.Say(msg)
log.Printf(msg) log.Printf(msg)
var vncPort uint var vncPort uint
@ -39,15 +39,16 @@ func (stepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction {
} }
log.Printf("Trying port: %d", vncPort) log.Printf("Trying port: %d", vncPort)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", vncPort)) l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.VNCBindAddress, vncPort))
if err == nil { if err == nil {
defer l.Close() defer l.Close()
break break
} }
} }
ui.Say(fmt.Sprintf("Found available VNC port: %d", vncPort)) log.Printf("Found available VNC port: %d on IP: %s", vncPort, config.VNCBindAddress)
state.Put("vnc_port", vncPort) state.Put("vnc_port", vncPort)
state.Put("vnc_ip", config.VNCBindAddress)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -61,12 +61,13 @@ func (s *stepRun) Cleanup(state multistep.StateBag) {
func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) { func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
config := state.Get("config").(*Config) config := state.Get("config").(*Config)
isoPath := state.Get("iso_path").(string) isoPath := state.Get("iso_path").(string)
vncIP := state.Get("vnc_ip").(string)
vncPort := state.Get("vnc_port").(uint) vncPort := state.Get("vnc_port").(uint)
sshHostPort := state.Get("sshHostPort").(uint) sshHostPort := state.Get("sshHostPort").(uint)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
vnc := fmt.Sprintf("0.0.0.0:%d", vncPort-5900) vnc := fmt.Sprintf("%s:%d", vncIP, vncPort-5900)
vmName := config.VMName vmName := config.VMName
imgPath := filepath.Join(config.OutputDir, vmName) imgPath := filepath.Join(config.OutputDir, vmName)
@ -100,9 +101,22 @@ func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error
deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice)) deviceArgs = append(deviceArgs, fmt.Sprintf("%s,netdev=user.0", config.NetDevice))
if config.Headless == true { if config.Headless == true {
ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" + vncIpRaw, vncIpOk := state.GetOk("vnc_ip")
"In headless mode, errors during the boot sequence or OS setup\n" + vncPortRaw, vncPortOk := state.GetOk("vnc_port")
"won't be easily visible. Use at your own discretion.")
if vncIpOk && vncPortOk {
vncIp := vncIpRaw.(string)
vncPort := vncPortRaw.(uint)
ui.Message(fmt.Sprintf(
"The VM will be run headless, without a GUI. If you want to\n"+
"view the screen of the VM, connect via VNC without a password to\n"+
"%s:%d", vncIp, vncPort))
} else {
ui.Message("The VM will be run headless, without a GUI, as configured.\n" +
"If the run isn't succeeding as you expect, please enable the GUI\n" +
"to inspect the progress of the build.")
}
} else { } else {
if qemuMajor >= 2 { if qemuMajor >= 2 {
defaultArgs["-display"] = "sdl" defaultArgs["-display"] = "sdl"

View File

@ -306,6 +306,10 @@ default port of `5985` or whatever value you have the service set to listen on.
`BUILDNAME` is the name of the build. Currently, no file extension will be `BUILDNAME` is the name of the build. Currently, no file extension will be
used unless it is specified in this option. used unless it is specified in this option.
- `vnc_bind_address` (string / IP address) - The IP address that should be binded
to for VNC. By default packer will use 127.0.0.1 for this. If you wish to bind
to all interfaces use 0.0.0.0
- `vnc_port_min` and `vnc_port_max` (integer) - The minimum and maximum port - `vnc_port_min` and `vnc_port_max` (integer) - The minimum and maximum port
to use for VNC access to the virtual machine. The builder uses VNC to type to use for VNC access to the virtual machine. The builder uses VNC to type
the initial `boot_command`. Because Packer generally runs in parallel, the initial `boot_command`. Because Packer generally runs in parallel,