Fix boot_command and update docs

This commit is contained in:
Moss 2020-06-12 12:42:00 +02:00
parent ba767d1663
commit 65cfb880fd
9 changed files with 91 additions and 185 deletions

View File

@ -7,14 +7,24 @@ import (
) )
type KeyInput struct { type KeyInput struct {
Message string
Scancode key.Code Scancode key.Code
Alt bool Alt bool
Ctrl bool Ctrl bool
Shift bool Shift bool
} }
func (vm *VirtualMachine) TypeOnKeyboard(spec types.UsbScanCodeSpec) (int32, error) { func (vm *VirtualMachine) TypeOnKeyboard(input KeyInput) (int32, error) {
var spec types.UsbScanCodeSpec
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{ req := &types.PutUsbScanCodes{
This: vm.vm.Reference(), This: vm.vm.Reference(),
Spec: spec, Spec: spec,

View File

@ -69,8 +69,9 @@ type FlatConfig struct {
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"` FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"` FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"` 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"` 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"` 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"` 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"` 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_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}, "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_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_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}, "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_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}, "ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},

View File

@ -8,9 +8,7 @@ import (
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/mobile/event/key" "golang.org/x/mobile/event/key"
"log"
"time" "time"
) )
@ -79,35 +77,26 @@ func (s *StepBootCommand) Run(ctx context.Context, state multistep.StateBag) mul
ui.Say(fmt.Sprintf("HTTP server is working at http://%v:%v/", ip, port)) ui.Say(fmt.Sprintf("HTTP server is working at http://%v:%v/", ip, port))
} }
sendCodes := func(codes []key.Code, downs []bool) error { sendCodes := func(code key.Code, down bool) error {
var spec types.UsbScanCodeSpec
for i, code := range codes {
var keyAlt, keyCtrl, keyShift bool var keyAlt, keyCtrl, keyShift bool
switch code { switch code {
case key.CodeLeftAlt: case key.CodeLeftAlt:
// <leftAltOn> // <leftAltOn>
keyAlt = downs[i] keyAlt = down
case key.CodeLeftControl: case key.CodeLeftControl:
// <leftCtrlOn> // <leftCtrlOn>
keyCtrl = downs[i] keyCtrl = down
default: default:
keyShift = downs[i] keyShift = down
} }
log.Printf("Sending code %s, shift %v", code, downs[i]) _, err := vm.TypeOnKeyboard(driver.KeyInput{
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{ Scancode: code,
UsbHidCode: int32(code)<<16 | 7, Ctrl: keyCtrl,
Modifiers: &types.UsbScanCodeSpecModifierType{ Alt: keyAlt,
LeftControl: &keyCtrl, Shift: keyShift,
LeftAlt: &keyAlt,
LeftShift: &keyShift,
},
}) })
}
_, err := vm.TypeOnKeyboard(spec)
if err != nil { if err != nil {
return fmt.Errorf("error typing a boot command: %v", err) return fmt.Errorf("error typing a boot command: %v", err)
} }

View File

@ -1,52 +0,0 @@
package iso
import (
"bytes"
"context"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/common/bootcommand"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"testing"
)
func TestStepBootCommand_Run(t *testing.T) {
state := new(multistep.BasicStateBag)
state.Put("ui", &packer.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
state.Put("debug", false)
state.Put("vm", new(driver.VirtualMachine))
state.Put("http_port", 2222)
state.Put("http_ip", "0.0.0.0")
step := &StepBootCommand{
Config: &BootConfig{
BootConfig: bootcommand.BootConfig{
BootCommand: []string{
"<leftShiftOn><enter><wait><f6><wait><esc><wait>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
"<bs><bs><bs>",
"/install/vmlinuz",
" initrd=/install/initrd.gz",
" priority=critical",
" locale=en_US",
" file=/media/preseed_hardcoded_ip.cfg",
" netcfg/get_ipaddress=0.0.0.0",
" netcfg/get_gateway=0.0.0.0",
"<enter>",
},
},
},
}
step.Run(context.TODO(), state)
}

View File

@ -2,18 +2,19 @@ package bootcommand
import ( import (
"fmt" "fmt"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/common"
"golang.org/x/mobile/event/key"
"log" "log"
"os" "os"
"strings" "strings"
"time" "time"
"unicode" "unicode"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/common"
"golang.org/x/mobile/event/key"
) )
// SendUsbScanCodes will be called to send codes to the VM // SendUsbScanCodes will be called to send codes to the VM
type SendUsbScanCodes func([]key.Code, []bool) error type SendUsbScanCodes func(k key.Code, down bool) error
type usbDriver struct { type usbDriver struct {
vm *driver.VirtualMachine vm *driver.VirtualMachine
@ -23,9 +24,6 @@ type usbDriver struct {
specialMap map[string]key.Code specialMap map[string]key.Code
scancodeMap map[rune]key.Code scancodeMap map[rune]key.Code
codeBuffer []key.Code
downBuffer []bool
// keyEvent can set this error which will prevent it from continuing // keyEvent can set this error which will prevent it from continuing
err error err error
} }
@ -103,25 +101,27 @@ func NewUSBDriver(send SendUsbScanCodes, interval time.Duration) *usbDriver {
} }
} }
// Flush sends codes to the vm func (d *usbDriver) keyEvent(k key.Code, down bool) error {
func (d *usbDriver) Flush() error { if d.err != nil {
defer func() { return d.err
d.codeBuffer = nil }
}() if err := d.sendImpl(k, down); err != nil {
d.err = err
if err := d.sendImpl(d.codeBuffer, d.downBuffer); err != nil {
return err return err
} }
time.Sleep(d.interval) time.Sleep(d.interval)
return nil return nil
} }
func (d *usbDriver) Flush() error {
return nil
}
func (d *usbDriver) SendKey(k rune, action KeyAction) error { func (d *usbDriver) SendKey(k rune, action KeyAction) error {
keyShift := unicode.IsUpper(k) || strings.ContainsRune(shiftedChars, k) keyShift := unicode.IsUpper(k) || strings.ContainsRune(shiftedChars, k)
keyCode := d.scancodeMap[k] keyCode := d.scancodeMap[k]
log.Printf("Sending char '%c', code %s, shift %v", k, keyCode, keyShift) log.Printf("Sending char '%c', code %s, shift %v", k, keyCode, keyShift)
d.send(keyCode, keyShift) return d.keyEvent(keyCode, keyShift)
return d.err
} }
func (d *usbDriver) SendSpecial(special string, action KeyAction) error { func (d *usbDriver) SendSpecial(special string, action KeyAction) error {
@ -133,18 +133,10 @@ func (d *usbDriver) SendSpecial(special string, action KeyAction) error {
switch action { switch action {
case KeyOn: case KeyOn:
d.send(keyCode, true) d.keyEvent(keyCode, true)
case KeyOff, KeyPress: case KeyOff, KeyPress:
d.send(keyCode, false) d.keyEvent(keyCode, false)
} }
return d.err return d.err
} }
// send stores the codes in an internal buffer. Use Flush to send them.
func (d *usbDriver) send(code key.Code, down bool) {
// slices to keep the input order
d.codeBuffer = append(d.codeBuffer, code)
d.downBuffer = append(d.downBuffer, down)
}

View File

@ -154,6 +154,11 @@ necessary for this build to succeed and can be found further down the page.
@include 'common/bootcommand/BootConfig.mdx' @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 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 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 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 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.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 ## Guest Additions
Packer will automatically download the proper guest additions for the version of Packer will automatically download the proper guest additions for the version of

View File

@ -145,6 +145,11 @@ necessary for this build to succeed and can be found further down the page.
@include 'common/bootcommand/BootConfig.mdx' @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 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 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 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 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.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 ## Guest Additions
Packer will automatically download the proper guest additions for the version of Packer will automatically download the proper guest additions for the version of

View File

@ -159,6 +159,11 @@ builder.
@include 'common/bootcommand/BootConfig.mdx' @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 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 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 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 'common/bootcommand/BootConfig-not-required.mdx'
@include 'builders/boot-command.mdx'
@include 'builders/virtualbox-ssh-key-pair.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 ## Guest Additions

View File

@ -162,6 +162,31 @@ from the datastore. Example:
@include 'helper/communicator/WinRM-not-required.mdx' @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 ## Working with Clusters
#### Standalone Hosts #### Standalone Hosts