Merge pull request #9406 from hashicorp/fix_9084

Add usb_driver to common boot_command and use it on vsphere
pull/9429/head
Megan Marsh 6 years ago committed by GitHub
commit 004ace4340
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,54 @@
package common
import (
"context"
"fmt"
"net"
"github.com/hashicorp/packer/helper/multistep"
)
// Step to discover the http ip
// which guests use to reach the vm host
// To make sure the IP is set before boot command and http server steps
type StepHTTPIPDiscover struct {
HTTPIP string
}
func (s *StepHTTPIPDiscover) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ip, err := getHostIP(s.HTTPIP)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("http_ip", ip)
return multistep.ActionContinue
}
func (s *StepHTTPIPDiscover) Cleanup(state multistep.StateBag) {}
func getHostIP(s string) (string, error) {
if s != "" {
if net.ParseIP(s) != nil {
return s, nil
} else {
return "", fmt.Errorf("invalid IP address")
}
}
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "", fmt.Errorf("IP not found")
}

@ -0,0 +1,44 @@
package common
import (
"context"
"testing"
"github.com/hashicorp/packer/helper/multistep"
)
func TestStepHTTPIPDiscover_Run(t *testing.T) {
state := new(multistep.BasicStateBag)
step := new(StepHTTPIPDiscover)
// without setting HTTPIP
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
_, ok := state.GetOk("http_ip")
if !ok {
t.Fatal("should have http_ip")
}
// setting HTTPIP
ip := "10.0.2.2"
step = &StepHTTPIPDiscover{
HTTPIP: ip,
}
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if _, ok := state.GetOk("error"); ok {
t.Fatal("should NOT have error")
}
httpIp, ok := state.GetOk("http_ip")
if !ok {
t.Fatal("should have http_ip")
}
if httpIp != ip {
t.Fatalf("bad: Http ip is %s but was supposed to be %s", httpIp, ip)
}
}

@ -1,73 +1,29 @@
package driver
import (
"strings"
"unicode"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/mobile/event/key"
)
type KeyInput struct {
Message string
Scancode key.Code
Alt bool
Ctrl bool
Shift bool
}
var scancodeMap = make(map[rune]key.Code)
func init() {
scancodeIndex := make(map[string]key.Code)
scancodeIndex["abcdefghijklmnopqrstuvwxyz"] = key.CodeA
scancodeIndex["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] = key.CodeA
scancodeIndex["1234567890"] = key.Code1
scancodeIndex["!@#$%^&*()"] = key.Code1
scancodeIndex[" "] = key.CodeSpacebar
scancodeIndex["-=[]\\"] = key.CodeHyphenMinus
scancodeIndex["_+{}|"] = key.CodeHyphenMinus
scancodeIndex[";'`,./"] = key.CodeSemicolon
scancodeIndex[":\"~<>?"] = key.CodeSemicolon
for chars, start := range scancodeIndex {
for i, r := range chars {
scancodeMap[r] = start + key.Code(i)
}
}
}
const shiftedChars = "!@#$%^&*()_+{}|:\"~<>?"
func (vm *VirtualMachine) TypeOnKeyboard(input KeyInput) (int32, error) {
var spec types.UsbScanCodeSpec
for _, r := range input.Message {
scancode := scancodeMap[r]
shift := input.Shift || unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
// https://github.com/lamw/vghetto-scripts/blob/f74bc8ba20064f46592bcce5a873b161a7fa3d72/powershell/VMKeystrokes.ps1#L130
UsbHidCode: int32(scancode)<<16 | 7,
Modifiers: &types.UsbScanCodeSpecModifierType{
LeftControl: &input.Ctrl,
LeftAlt: &input.Alt,
LeftShift: &shift,
},
})
}
if input.Scancode != 0 {
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
UsbHidCode: int32(input.Scancode)<<16 | 7,
Modifiers: &types.UsbScanCodeSpecModifierType{
LeftControl: &input.Ctrl,
LeftAlt: &input.Alt,
LeftShift: &input.Shift,
},
})
}
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
UsbHidCode: int32(input.Scancode)<<16 | 7,
Modifiers: &types.UsbScanCodeSpecModifierType{
LeftControl: &input.Ctrl,
LeftAlt: &input.Alt,
LeftShift: &input.Shift,
},
})
req := &types.PutUsbScanCodes{
This: vm.vm.Reference(),

@ -30,6 +30,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("debug", b.config.PackerDebug)
state.Put("hook", hook)
state.Put("ui", ui)
@ -89,6 +90,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&common.StepHTTPIPDiscover{
HTTPIP: b.config.BootConfig.HTTPIP,
},
&packerCommon.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,

@ -76,7 +76,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)

@ -69,8 +69,9 @@ type FlatConfig struct {
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip" hcl:"http_ip"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"`
@ -196,8 +197,9 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"http_ip": &hcldec.AttrSpec{Name: "http_ip", Type: cty.String, Required: false},
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},

@ -3,15 +3,10 @@ package iso
import (
"context"
"fmt"
"log"
"net"
"os"
"strings"
"time"
"unicode/utf8"
"github.com/hashicorp/packer/builder/vsphere/driver"
packerCommon "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/bootcommand"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -19,9 +14,8 @@ import (
)
type BootConfig struct {
BootCommand []string `mapstructure:"boot_command"`
BootWait time.Duration `mapstructure:"boot_wait"` // example: "1m30s"; default: "10s"
HTTPIP string `mapstructure:"http_ip"`
bootcommand.BootConfig `mapstructure:",squash"`
HTTPIP string `mapstructure:"http_ip"`
}
type bootCommandTemplateData struct {
@ -30,14 +24,11 @@ type bootCommandTemplateData struct {
Name string
}
func (c *BootConfig) Prepare() []error {
var errs []error
func (c *BootConfig) Prepare(ctx *interpolate.Context) []error {
if c.BootWait == 0 {
c.BootWait = 10 * time.Second
}
return errs
return c.BootConfig.Prepare(ctx)
}
type StepBootCommand struct {
@ -46,44 +37,8 @@ type StepBootCommand struct {
Ctx interpolate.Context
}
var special = map[string]key.Code{
"<enter>": key.CodeReturnEnter,
"<esc>": key.CodeEscape,
"<bs>": key.CodeDeleteBackspace,
"<del>": key.CodeDeleteForward,
"<tab>": key.CodeTab,
"<f1>": key.CodeF1,
"<f2>": key.CodeF2,
"<f3>": key.CodeF3,
"<f4>": key.CodeF4,
"<f5>": key.CodeF5,
"<f6>": key.CodeF6,
"<f7>": key.CodeF7,
"<f8>": key.CodeF8,
"<f9>": key.CodeF9,
"<f10>": key.CodeF10,
"<f11>": key.CodeF11,
"<f12>": key.CodeF12,
"<insert>": key.CodeInsert,
"<home>": key.CodeHome,
"<end>": key.CodeEnd,
"<pageUp>": key.CodePageUp,
"<pageDown>": key.CodePageDown,
"<left>": key.CodeLeftArrow,
"<right>": key.CodeRightArrow,
"<up>": key.CodeUpArrow,
"<down>": key.CodeDownArrow,
}
var keyInterval = packerCommon.PackerKeyDefault
func init() {
if delay, err := time.ParseDuration(os.Getenv(packerCommon.PackerKeyEnv)); err == nil {
keyInterval = delay
}
}
func (s *StepBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
func (s *StepBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
debug := state.Get("debug").(bool)
ui := state.Get("ui").(packer.Ui)
vm := state.Get("vm").(*driver.VirtualMachine)
@ -91,28 +46,25 @@ func (s *StepBootCommand) Run(_ context.Context, state multistep.StateBag) multi
return multistep.ActionContinue
}
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.Config.BootWait))
wait := time.After(s.Config.BootWait)
WAITLOOP:
for {
// Wait the for the vm to boot.
if int64(s.Config.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.Config.BootWait.String()))
select {
case <-wait:
break WAITLOOP
case <-time.After(1 * time.Second):
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
case <-time.After(s.Config.BootWait):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
var pauseFn multistep.DebugPauseFn
if debug {
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
}
port := state.Get("http_port").(int)
if port > 0 {
ip, err := getHostIP(s.Config.HTTPIP)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("http_ip", ip)
ip := state.Get("http_ip").(string)
s.Ctx.Data = &bootCommandTemplateData{
ip,
port,
@ -121,136 +73,61 @@ WAITLOOP:
ui.Say(fmt.Sprintf("HTTP server is working at http://%v:%v/", ip, port))
}
ui.Say("Typing boot command...")
var keyAlt bool
var keyCtrl bool
var keyShift bool
for _, command := range s.Config.BootCommand {
message, err := interpolate.Render(command, &s.Ctx)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
for len(message) > 0 {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
if strings.HasPrefix(message, "<wait>") {
log.Printf("Waiting 1 second")
time.Sleep(1 * time.Second)
message = message[len("<wait>"):]
continue
}
if strings.HasPrefix(message, "<wait5>") {
log.Printf("Waiting 5 seconds")
time.Sleep(5 * time.Second)
message = message[len("<wait5>"):]
continue
}
if strings.HasPrefix(message, "<wait10>") {
log.Printf("Waiting 10 seconds")
time.Sleep(10 * time.Second)
message = message[len("<wait10>"):]
continue
}
sendCodes := func(code key.Code, down bool) error {
var keyAlt, keyCtrl, keyShift bool
if strings.HasPrefix(message, "<leftAltOn>") {
keyAlt = true
message = message[len("<leftAltOn>"):]
continue
}
if strings.HasPrefix(message, "<leftAltOff>") {
keyAlt = false
message = message[len("<leftAltOff>"):]
continue
}
if strings.HasPrefix(message, "<leftCtrlOn>") {
keyCtrl = true
message = message[len("<leftCtrlOn>"):]
continue
}
if strings.HasPrefix(message, "<leftCtrlOff>") {
keyCtrl = false
message = message[len("<leftCtrlOff>"):]
continue
}
if strings.HasPrefix(message, "<leftShiftOn>") {
keyShift = true
message = message[len("<leftShiftOn>"):]
continue
}
if strings.HasPrefix(message, "<leftShiftOff>") {
keyShift = false
message = message[len("<leftShiftOff>"):]
continue
}
var scancode key.Code
for specialCode, specialValue := range special {
if strings.HasPrefix(message, specialCode) {
scancode = specialValue
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
message = message[len(specialCode):]
}
}
var char rune
if scancode == 0 {
var size int
char, size = utf8.DecodeRuneInString(message)
message = message[size:]
}
switch code {
case key.CodeLeftAlt:
keyAlt = down
case key.CodeLeftControl:
keyCtrl = down
default:
keyShift = down
}
_, err := vm.TypeOnKeyboard(driver.KeyInput{
Message: string(char),
Scancode: scancode,
Ctrl: keyCtrl,
Alt: keyAlt,
Shift: keyShift,
})
if err != nil {
state.Put("error", fmt.Errorf("error typing a boot command: %v", err))
return multistep.ActionHalt
}
time.Sleep(keyInterval)
_, err := vm.TypeOnKeyboard(driver.KeyInput{
Scancode: code,
Ctrl: keyCtrl,
Alt: keyAlt,
Shift: keyShift,
})
if err != nil {
return fmt.Errorf("error typing a boot command: %v", err)
}
return nil
}
d := bootcommand.NewUSBDriver(sendCodes, s.Config.BootGroupInterval)
return multistep.ActionContinue
}
func (s *StepBootCommand) Cleanup(state multistep.StateBag) {}
func getHostIP(s string) (string, error) {
if s != "" {
if net.ParseIP(s) != nil {
return s, nil
} else {
return "", fmt.Errorf("invalid IP address")
}
ui.Say("Typing boot command...")
flatBootCommand := s.Config.FlatBootCommand()
command, err := interpolate.Render(flatBootCommand, &s.Ctx)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
addrs, err := net.InterfaceAddrs()
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
return "", err
err := fmt.Errorf("Error generating boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
if err := seq.Do(ctx, d); err != nil {
err := fmt.Errorf("Error running boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return "", fmt.Errorf("IP not found")
if pauseFn != nil {
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state)
}
return multistep.ActionContinue
}
func (s *StepBootCommand) Cleanup(_ multistep.StateBag) {}

@ -0,0 +1,136 @@
package bootcommand
import (
"fmt"
"log"
"os"
"strings"
"time"
"unicode"
"github.com/hashicorp/packer/common"
"golang.org/x/mobile/event/key"
)
// SendUsbScanCodes will be called to send codes to the VM
type SendUsbScanCodes func(k key.Code, down bool) error
type usbDriver struct {
sendImpl SendUsbScanCodes
interval time.Duration
specialMap map[string]key.Code
scancodeMap map[rune]key.Code
}
func NewUSBDriver(send SendUsbScanCodes, interval time.Duration) *usbDriver {
// We delay (default 100ms) between each key event to allow for CPU or
// network latency. See PackerKeyEnv for tuning.
keyInterval := common.PackerKeyDefault
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
keyInterval = delay
}
// override interval based on builder-specific override.
if interval > time.Duration(0) {
keyInterval = interval
}
special := map[string]key.Code{
"enter": key.CodeReturnEnter,
"return": key.CodeReturnEnter,
"esc": key.CodeEscape,
"bs": key.CodeDeleteBackspace,
"del": key.CodeDeleteForward,
"tab": key.CodeTab,
"f1": key.CodeF1,
"f2": key.CodeF2,
"f3": key.CodeF3,
"f4": key.CodeF4,
"f5": key.CodeF5,
"f6": key.CodeF6,
"f7": key.CodeF7,
"f8": key.CodeF8,
"f9": key.CodeF9,
"f10": key.CodeF10,
"f11": key.CodeF11,
"f12": key.CodeF12,
"insert": key.CodeInsert,
"home": key.CodeHome,
"end": key.CodeEnd,
"pageUp": key.CodePageUp,
"pageDown": key.CodePageDown,
"left": key.CodeLeftArrow,
"right": key.CodeRightArrow,
"up": key.CodeUpArrow,
"down": key.CodeDownArrow,
"leftalt": key.CodeLeftAlt,
"leftctrl": key.CodeLeftControl,
"leftshift": key.CodeLeftShift,
"rightalt": key.CodeRightAlt,
"rightctrl": key.CodeRightControl,
"rightshift": key.CodeRightShift,
"leftsuper": key.CodeLeftGUI,
"rightsuper": key.CodeRightGUI,
"spacebar": key.CodeSpacebar,
}
scancodeIndex := make(map[string]key.Code)
scancodeIndex["abcdefghijklmnopqrstuvwxyz"] = key.CodeA
scancodeIndex["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] = key.CodeA
scancodeIndex["1234567890"] = key.Code1
scancodeIndex["!@#$%^&*()"] = key.Code1
scancodeIndex[" "] = key.CodeSpacebar
scancodeIndex["-=[]\\"] = key.CodeHyphenMinus
scancodeIndex["_+{}|"] = key.CodeHyphenMinus
scancodeIndex[";'`,./"] = key.CodeSemicolon
scancodeIndex[":\"~<>?"] = key.CodeSemicolon
var scancodeMap = make(map[rune]key.Code)
for chars, start := range scancodeIndex {
for i, r := range chars {
scancodeMap[r] = start + key.Code(i)
}
}
return &usbDriver{
sendImpl: send,
specialMap: special,
interval: keyInterval,
scancodeMap: scancodeMap,
}
}
func (d *usbDriver) keyEvent(k key.Code, down bool) error {
if err := d.sendImpl(k, down); err != nil {
return err
}
time.Sleep(d.interval)
return nil
}
func (d *usbDriver) Flush() error {
return nil
}
func (d *usbDriver) SendKey(k rune, action KeyAction) error {
keyShift := unicode.IsUpper(k) || strings.ContainsRune(shiftedChars, k)
keyCode := d.scancodeMap[k]
log.Printf("Sending char '%c', code %s, shift %v", k, keyCode, keyShift)
return d.keyEvent(keyCode, keyShift)
}
func (d *usbDriver) SendSpecial(special string, action KeyAction) (err error) {
keyCode, ok := d.specialMap[special]
if !ok {
return fmt.Errorf("special %s not found.", special)
}
log.Printf("Special code '<%s>' found, replacing with: %s", special, keyCode)
switch action {
case KeyOn:
err = d.keyEvent(keyCode, true)
case KeyOff, KeyPress:
err = d.keyEvent(keyCode, false)
}
return err
}

@ -0,0 +1,103 @@
package bootcommand
import (
"context"
"testing"
"time"
"golang.org/x/mobile/event/key"
)
func TestUSBDriver(t *testing.T) {
tc := []struct {
command string
code key.Code
shift bool
}{
{
"<leftShift>",
key.CodeLeftShift,
false,
},
{
"<leftShiftOff>",
key.CodeLeftShift,
false,
},
{
"<leftShiftOn>",
key.CodeLeftShift,
true,
},
{
"<leftsuper>",
key.CodeLeftGUI,
false,
},
{
"<spacebar>",
key.CodeSpacebar,
false,
},
{
"<return>",
key.CodeReturnEnter,
false,
},
{
"a",
key.CodeA,
false,
},
{
"A",
key.CodeA,
true,
},
{
"_",
key.CodeHyphenMinus,
true,
},
}
for _, tt := range tc {
t.Run(tt.command, func(t *testing.T) {
var code key.Code
var shift bool
sendCodes := func(c key.Code, d bool) error {
code = c
shift = d
return nil
}
d := NewUSBDriver(sendCodes, time.Duration(0))
seq, err := GenerateExpressionSequence(tt.command)
if err != nil {
t.Fatalf("bad: not expected error: %s", err.Error())
}
err = seq.Do(context.Background(), d)
if err != nil {
t.Fatalf("bad: not expected error: %s", err.Error())
}
if code != tt.code {
t.Fatalf("bad: wrong scan code: \n expected: %s \n actual: %s", tt.code, code)
}
if shift != tt.shift {
t.Fatalf("bad: wrong shift: \n expected: %t \n actual: %t", tt.shift, shift)
}
})
}
}
func TestUSBDriver_KeyIntervalNotGiven(t *testing.T) {
d := NewUSBDriver(nil, time.Duration(0))
if d.interval != time.Duration(100)*time.Millisecond {
t.Fatal("not expected key interval")
}
}
func TestUSBDriver_KeyIntervalGiven(t *testing.T) {
d := NewUSBDriver(nil, time.Duration(5000)*time.Millisecond)
if d.interval != time.Duration(5000)*time.Millisecond {
t.Fatal("not expected key interval")
}
}

@ -154,6 +154,11 @@ necessary for this build to succeed and can be found further down the page.
@include 'common/bootcommand/BootConfig.mdx'
Please note that for the Virtuabox builder, the IP address of the HTTP server
Packer launches for you to access files like the preseed file in the example
above (`{{ .HTTPIP }}`) is hardcoded to 10.0.2.2. If you change the network
of your VM you must guarantee that you can still access this HTTP server.
The boot command is sent to the VM through the `VBoxManage` utility in as few
invocations as possible. We send each character in groups of 25, with a default
delay of 100ms between groups. The delay alleviates issues with latency and CPU
@ -176,35 +181,8 @@ contention. If you notice missing keys, you can tune this delay by specifying
@include 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.mdx'
Example boot command. This is actually a working boot command used to start an
Ubuntu 12.04 installer:
```text
[
"<esc><esc><enter><wait>",
"/install/vmlinuz noapic ",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{ .Name }} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false ",
"initrd=/install/initrd.gz -- <enter>"
]
```
Please note that for the Virtuabox builder, the IP address of the HTTP server
Packer launches for you to access files like the preseed file in the example
above (`{{ .HTTPIP }}`) is hardcoded to 10.0.2.2. If you change the network
of your VM you must guarantee that you can still access this HTTP server.
For more examples of various boot commands, see the sample projects from our
[community templates page](/community-tools#templates).
## Guest Additions
Packer will automatically download the proper guest additions for the version of

@ -145,6 +145,11 @@ necessary for this build to succeed and can be found further down the page.
@include 'common/bootcommand/BootConfig.mdx'
Please note that for the Virtuabox builder, the IP address of the HTTP server
Packer launches for you to access files like the preseed file in the example
above (`{{ .HTTPIP }}`) is hardcoded to 10.0.2.2. If you change the network
of your VM you must guarantee that you can still access this HTTP server.
The boot command is sent to the VM through the `VBoxManage` utility in as few
invocations as possible. We send each character in groups of 25, with a default
delay of 100ms between groups. The delay alleviates issues with latency and CPU
@ -167,30 +172,8 @@ contention. If you notice missing keys, you can tune this delay by specifying
@include 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.mdx'
Example boot command. This is actually a working boot command used to start an
Ubuntu 12.04 installer:
```text
[
"<esc><esc><enter><wait>",
"/install/vmlinuz noapic ",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{ .Name }} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false ",
"initrd=/install/initrd.gz -- <enter>"
]
```
For more examples of various boot commands, see the sample projects from our
[community templates page](/community-tools#templates).
## Guest Additions
Packer will automatically download the proper guest additions for the version of

@ -159,6 +159,11 @@ builder.
@include 'common/bootcommand/BootConfig.mdx'
Please note that for the Virtuabox builder, the IP address of the HTTP server
Packer launches for you to access files like the preseed file in the example
above (`{{ .HTTPIP }}`) is hardcoded to 10.0.2.2. If you change the network
of your VM you must guarantee that you can still access this HTTP server.
The boot command is sent to the VM through the `VBoxManage` utility in as few
invocations as possible. We send each character in groups of 25, with a default
delay of 100ms between groups. The delay alleviates issues with latency and CPU
@ -181,34 +186,8 @@ contention. If you notice missing keys, you can tune this delay by specifying
@include 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.mdx'
Example boot command. This is actually a working boot command used to start an
Ubuntu 12.04 installer:
```text
[
"<esc><esc><enter><wait>",
"/install/vmlinuz noapic ",
"preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg ",
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us ",
"hostname={{ .Name }} ",
"fb=false debconf/frontend=noninteractive ",
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA ",
"keyboard-configuration/variant=USA console-setup/ask_detect=false ",
"initrd=/install/initrd.gz -- <enter>"
]
```
Please note that for the Virtuabox builder, the IP address of the HTTP server
Packer launches for you to access files like the preseed file in the example
above (`{{ .HTTPIP }}`) is hardcoded to 10.0.2.2. If you change the network
of your VM you must guarantee that you can still access this HTTP server.
For more examples of various boot commands, see the sample projects from our
[community templates page](/community-tools#templates).
## Guest Additions

@ -162,6 +162,31 @@ from the datastore. Example:
@include 'helper/communicator/WinRM-not-required.mdx'
## Boot Configuration
@include 'common/bootcommand/BootConfig.mdx'
We send each character to the VM with a default delay of 100ms between groups.
The delay alleviates possible issues with latency and CPU
contention. If you notice missing keys, you can tune this delay by specifying
"boot_keygroup_interval" in your Packer template, for example:
```json
{
"builders": [
{
"type": "vsphere-iso",
"boot_keygroup_interval": "500ms"
...
}
]
}
```
#### Optional:
@include 'common/bootcommand/BootConfig-not-required.mdx'
## Working with Clusters
#### Standalone Hosts

Loading…
Cancel
Save