builder/virtualbox: type boot command
This commit is contained in:
parent
c7071b3bd2
commit
7344d9e52c
|
@ -163,6 +163,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) packer
|
||||||
new(stepAttachISO),
|
new(stepAttachISO),
|
||||||
new(stepForwardSSH),
|
new(stepForwardSSH),
|
||||||
new(stepRun),
|
new(stepRun),
|
||||||
|
new(stepTypeBootCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the state bag
|
// Setup the state bag
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
package virtualbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const KeyLeftShift uint32 = 0xFFE1
|
||||||
|
|
||||||
|
type bootCommandTemplateData struct {
|
||||||
|
HTTPIP string
|
||||||
|
HTTPPort uint
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// This step "types" the boot command into the VM over VNC.
|
||||||
|
//
|
||||||
|
// Uses:
|
||||||
|
// config *config
|
||||||
|
// driver Driver
|
||||||
|
// http_port int
|
||||||
|
// ui packer.Ui
|
||||||
|
// vmName string
|
||||||
|
//
|
||||||
|
// Produces:
|
||||||
|
// <nothing>
|
||||||
|
type stepTypeBootCommand struct{}
|
||||||
|
|
||||||
|
func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction {
|
||||||
|
config := state["config"].(*config)
|
||||||
|
driver := state["driver"].(Driver)
|
||||||
|
httpPort := state["http_port"].(uint)
|
||||||
|
ui := state["ui"].(packer.Ui)
|
||||||
|
vmName := state["vmName"].(string)
|
||||||
|
|
||||||
|
tplData := &bootCommandTemplateData{
|
||||||
|
"10.0.2.2",
|
||||||
|
httpPort,
|
||||||
|
config.VMName,
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Typing the boot command...")
|
||||||
|
for _, command := range config.BootCommand {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
t := template.Must(template.New("boot").Parse(command))
|
||||||
|
t.Execute(&buf, tplData)
|
||||||
|
|
||||||
|
for _, code := range scancodes(buf.String()) {
|
||||||
|
if code == "wait" {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := driver.VBoxManage("controlvm", vmName, "keyboardputscancode", code); err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error sending boot command: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*stepTypeBootCommand) Cleanup(map[string]interface{}) {}
|
||||||
|
|
||||||
|
func scancodes(message string) []string {
|
||||||
|
special := make(map[string][]string)
|
||||||
|
special["<enter>"] = []string{"1c", "9c"}
|
||||||
|
special["<return>"] = []string{"1c", "9c"}
|
||||||
|
special["<esc>"] = []string{"01", "81"}
|
||||||
|
|
||||||
|
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||||
|
|
||||||
|
scancodeIndex := make(map[string]uint)
|
||||||
|
scancodeIndex["1234567890-="] = 0x02
|
||||||
|
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||||
|
scancodeIndex["qwertyuiop[]"] = 0x10
|
||||||
|
scancodeIndex["QWERTYUIOP{}"] = 0x10
|
||||||
|
scancodeIndex["asdfghjkl;'`"] = 0x1e
|
||||||
|
scancodeIndex[`ASDFGHJKL:"~`] = 0x1e
|
||||||
|
scancodeIndex[`\zxcvbnm,./`] = 0x2b
|
||||||
|
scancodeIndex["|ZXCVBNM<>?"] = 0x2b
|
||||||
|
scancodeIndex[" "] = 0x39
|
||||||
|
|
||||||
|
scancodeMap := make(map[rune]uint)
|
||||||
|
for chars, start := range scancodeIndex {
|
||||||
|
var i uint = 0
|
||||||
|
for len(chars) > 0 {
|
||||||
|
r, size := utf8.DecodeRuneInString(chars)
|
||||||
|
chars = chars[size:]
|
||||||
|
scancodeMap[r] = start + i
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, 0, len(message) * 2)
|
||||||
|
for len(message) > 0 {
|
||||||
|
var scancode []string
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<wait>") {
|
||||||
|
log.Printf("Special code <wait> found, will sleep at this point.")
|
||||||
|
scancode = []string{"wait"}
|
||||||
|
message = message[len("<wait>"):]
|
||||||
|
}
|
||||||
|
|
||||||
|
if scancode == nil {
|
||||||
|
for specialCode, specialValue := range special {
|
||||||
|
if strings.HasPrefix(message, specialCode) {
|
||||||
|
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||||
|
scancode = specialValue
|
||||||
|
message = message[len(specialCode):]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scancode == nil {
|
||||||
|
r, size := utf8.DecodeRuneInString(message)
|
||||||
|
message = message[size:]
|
||||||
|
scancodeInt := scancodeMap[r]
|
||||||
|
keyShift := unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||||
|
|
||||||
|
scancode = make([]string, 0, 4)
|
||||||
|
if keyShift {
|
||||||
|
scancode = append(scancode, "2a")
|
||||||
|
}
|
||||||
|
|
||||||
|
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt))
|
||||||
|
|
||||||
|
if keyShift {
|
||||||
|
scancode = append(scancode, "aa")
|
||||||
|
}
|
||||||
|
|
||||||
|
scancode = append(scancode, fmt.Sprintf("%02x", scancodeInt + 0x80))
|
||||||
|
log.Printf("Sending char '%c', code '%v', shift %v", r, scancode, keyShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, scancode...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in New Issue