Implement new parser for qemu boot command

pull/6129/head
Matthew Hooker 8 years ago
parent bdb1eee7d8
commit bf0a320125
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1

@ -388,7 +388,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
steps = append(steps,
new(stepConfigureVNC),
steprun,
&stepBootWait{},
&stepTypeBootCommand{},
)

@ -1,27 +0,0 @@
package qemu
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
)
// stepBootWait waits the configured time period.
type stepBootWait struct{}
func (s *stepBootWait) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
if int64(config.bootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait))
time.Sleep(config.bootWait)
}
return multistep.ActionContinue
}
func (s *stepBootWait) Cleanup(state multistep.StateBag) {}

@ -5,15 +5,10 @@ import (
"fmt"
"log"
"net"
"regexp"
"strings"
"time"
"unicode"
"unicode/utf8"
"os"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/common/boot_command"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
@ -40,13 +35,24 @@ type bootCommandTemplateData struct {
// <nothing>
type stepTypeBootCommand struct{}
func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
func (s *stepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
debug := state.Get("debug").(bool)
httpPort := state.Get("http_port").(uint)
ui := state.Get("ui").(packer.Ui)
vncPort := state.Get("vnc_port").(uint)
// Wait the for the vm to boot.
if int64(config.bootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.bootWait.String()))
select {
case <-time.After(config.bootWait):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
var pauseFn multistep.DebugPauseFn
if debug {
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
@ -76,16 +82,18 @@ func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
hostIP := "10.0.2.2"
common.SetHTTPIP(hostIP)
ctx := config.ctx
ctx.Data = &bootCommandTemplateData{
configCtx := config.ctx
configCtx.Data = &bootCommandTemplateData{
hostIP,
httpPort,
config.VMName,
}
d := bootcommand.NewVNCDriver(c)
ui.Say("Typing the boot command over VNC...")
for i, command := range config.BootCommand {
command, err := interpolate.Render(command, &ctx)
command, err := interpolate.Render(command, &configCtx)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
@ -93,259 +101,27 @@ func (s *stepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m
return multistep.ActionHalt
}
// Check for interrupts between typing things so we can cancel
// since this isn't the fastest thing.
if _, ok := state.GetOk(multistep.StateCancelled); ok {
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
err := fmt.Errorf("Error generating boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
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
}
if pauseFn != nil {
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
}
vncSendString(c, command)
}
return multistep.ActionContinue
}
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
special := make(map[string]uint32)
special["<bs>"] = 0xFF08
special["<del>"] = 0xFFFF
special["<enter>"] = 0xFF0D
special["<esc>"] = 0xFF1B
special["<f1>"] = 0xFFBE
special["<f2>"] = 0xFFBF
special["<f3>"] = 0xFFC0
special["<f4>"] = 0xFFC1
special["<f5>"] = 0xFFC2
special["<f6>"] = 0xFFC3
special["<f7>"] = 0xFFC4
special["<f8>"] = 0xFFC5
special["<f9>"] = 0xFFC6
special["<f10>"] = 0xFFC7
special["<f11>"] = 0xFFC8
special["<f12>"] = 0xFFC9
special["<return>"] = 0xFF0D
special["<tab>"] = 0xFF09
special["<up>"] = 0xFF52
special["<down>"] = 0xFF54
special["<left>"] = 0xFF51
special["<right>"] = 0xFF53
special["<spacebar>"] = 0x020
special["<insert>"] = 0xFF63
special["<home>"] = 0xFF50
special["<end>"] = 0xFF57
special["<pageUp>"] = 0xFF55
special["<pageDown>"] = 0xFF56
special["<leftAlt>"] = 0xFFE9
special["<leftCtrl>"] = 0xFFE3
special["<leftShift>"] = 0xFFE1
special["<rightAlt>"] = 0xFFEA
special["<rightCtrl>"] = 0xFFE4
special["<rightShift>"] = 0xFFE2
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
waitRe := regexp.MustCompile(`^<wait([0-9hms]+)>`)
// 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
}
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
for len(original) > 0 {
var keyCode uint32
keyShift := false
if strings.HasPrefix(original, "<leftAltOn>") {
keyCode = special["<leftAlt>"]
original = original[len("<leftAltOn>"):]
log.Printf("Special code '<leftAltOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftCtrlOn>") {
keyCode = special["<leftCtrl>"]
original = original[len("<leftCtrlOn>"):]
log.Printf("Special code '<leftCtrlOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftShiftOn>") {
keyCode = special["<leftShift>"]
original = original[len("<leftShiftOn>"):]
log.Printf("Special code '<leftShiftOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftAltOff>") {
keyCode = special["<leftAlt>"]
original = original[len("<leftAltOff>"):]
log.Printf("Special code '<leftAltOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftCtrlOff>") {
keyCode = special["<leftCtrl>"]
original = original[len("<leftCtrlOff>"):]
log.Printf("Special code '<leftCtrlOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<leftShiftOff>") {
keyCode = special["<leftShift>"]
original = original[len("<leftShiftOff>"):]
log.Printf("Special code '<leftShiftOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightAltOn>") {
keyCode = special["<rightAlt>"]
original = original[len("<rightAltOn>"):]
log.Printf("Special code '<rightAltOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightCtrlOn>") {
keyCode = special["<rightCtrl>"]
original = original[len("<rightCtrlOn>"):]
log.Printf("Special code '<rightCtrlOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightShiftOn>") {
keyCode = special["<rightShift>"]
original = original[len("<rightShiftOn>"):]
log.Printf("Special code '<rightShiftOn>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightAltOff>") {
keyCode = special["<rightAlt>"]
original = original[len("<rightAltOff>"):]
log.Printf("Special code '<rightAltOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightCtrlOff>") {
keyCode = special["<rightCtrl>"]
original = original[len("<rightCtrlOff>"):]
log.Printf("Special code '<rightCtrlOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<rightShiftOff>") {
keyCode = special["<rightShift>"]
original = original[len("<rightShiftOff>"):]
log.Printf("Special code '<rightShiftOff>' found, replacing with: %d", keyCode)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
continue
}
if strings.HasPrefix(original, "<wait>") {
log.Printf("Special code '<wait>' found, sleeping one second")
time.Sleep(1 * time.Second)
original = original[len("<wait>"):]
continue
}
if strings.HasPrefix(original, "<wait5>") {
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
time.Sleep(5 * time.Second)
original = original[len("<wait5>"):]
continue
}
if strings.HasPrefix(original, "<wait10>") {
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
time.Sleep(10 * time.Second)
original = original[len("<wait10>"):]
continue
}
waitMatch := waitRe.FindStringSubmatch(original)
if len(waitMatch) > 1 {
log.Printf("Special code %s found, sleeping", waitMatch[0])
if dt, err := time.ParseDuration(waitMatch[1]); err == nil {
time.Sleep(dt)
original = original[len(waitMatch[0]):]
continue
}
}
for specialCode, specialValue := range special {
if strings.HasPrefix(original, specialCode) {
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
keyCode = specialValue
original = original[len(specialCode):]
break
}
}
if keyCode == 0 {
r, size := utf8.DecodeRuneInString(original)
original = original[size:]
keyCode = uint32(r)
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
}
if keyShift {
c.KeyEvent(KeyLeftShift, true)
}
c.KeyEvent(keyCode, true)
time.Sleep(keyInterval)
c.KeyEvent(keyCode, false)
time.Sleep(keyInterval)
if keyShift {
c.KeyEvent(KeyLeftShift, false)
}
time.Sleep(keyInterval)
}
}

@ -129,10 +129,6 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag)
return multistep.ActionHalt
}
if pauseFn != nil {
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
}
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
err := fmt.Errorf("Error generating boot command: %s", err)
@ -147,6 +143,11 @@ func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag)
ui.Error(err.Error())
return multistep.ActionHalt
}
if pauseFn != nil {
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command[%d]: %s", i, command), state)
}
}
return multistep.ActionContinue

Loading…
Cancel
Save