From bdb1eee7d859f49074f19fbbd10c9f79b8ea509a Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 13 Apr 2018 12:49:54 -0700 Subject: [PATCH] Implement new parser for HyperV boot command --- builder/hyperv/common/step_run.go | 19 -- .../hyperv/common/step_type_boot_command.go | 243 +++--------------- builder/hyperv/iso/builder.go | 5 +- builder/hyperv/vmcx/builder.go | 5 +- common/boot_command/boot_command_ast.go | 5 +- 5 files changed, 43 insertions(+), 234 deletions(-) diff --git a/builder/hyperv/common/step_run.go b/builder/hyperv/common/step_run.go index cd8213c5e..02996fb6f 100644 --- a/builder/hyperv/common/step_run.go +++ b/builder/hyperv/common/step_run.go @@ -3,15 +3,12 @@ package common import ( "context" "fmt" - "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) type StepRun struct { - BootWait time.Duration - vmName string } @@ -32,22 +29,6 @@ func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.Ste s.vmName = vmName - if int64(s.BootWait) > 0 { - ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait)) - wait := time.After(s.BootWait) - WAITLOOP: - for { - select { - case <-wait: - break WAITLOOP - case <-time.After(1 * time.Second): - if _, ok := state.GetOk(multistep.StateCancelled); ok { - return multistep.ActionHalt - } - } - } - } - return multistep.ActionContinue } diff --git a/builder/hyperv/common/step_type_boot_command.go b/builder/hyperv/common/step_type_boot_command.go index 43deee1fd..5772ce1b9 100644 --- a/builder/hyperv/common/step_type_boot_command.go +++ b/builder/hyperv/common/step_type_boot_command.go @@ -3,12 +3,11 @@ package common import ( "context" "fmt" - "log" "strings" - "unicode" - "unicode/utf8" + "time" "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" @@ -23,16 +22,28 @@ type bootCommandTemplateData struct { // This step "types" the boot command into the VM via the Hyper-V virtual keyboard type StepTypeBootCommand struct { BootCommand []string + BootWait time.Duration SwitchName string Ctx interpolate.Context } -func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { +func (s *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { httpPort := state.Get("http_port").(uint) ui := state.Get("ui").(packer.Ui) driver := state.Get("driver").(Driver) vmName := state.Get("vmName").(string) + // Wait the for the vm to boot. + if int64(s.BootWait) > 0 { + ui.Say(fmt.Sprintf("Waiting %s for boot...", s.BootWait.String())) + select { + case <-time.After(s.BootWait): + break + case <-ctx.Done(): + return multistep.ActionHalt + } + } + hostIp, err := driver.GetHostAdapterIpAddressForSwitch(s.SwitchName) if err != nil { @@ -52,7 +63,9 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m } ui.Say("Typing the boot command...") - scanCodesToSend := []string{} + + // Flatten command so we send it all at once + commands := []string{} for _, command := range s.BootCommand { command, err := interpolate.Render(command, &s.Ctx) @@ -64,13 +77,26 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m return multistep.ActionHalt } - scanCodesToSend = append(scanCodesToSend, scancodes(command)...) + commands = append(commands, command) } - scanCodesToSendString := strings.Join(scanCodesToSend, " ") + sendCodes := func(codes []string) error { + scanCodesToSendString := strings.Join(codes, " ") + return driver.TypeScanCodes(vmName, scanCodesToSendString) + } + d := bootcommand.NewPCATDriver(sendCodes, -1) - if err := driver.TypeScanCodes(vmName, scanCodesToSendString); err != nil { - err := fmt.Errorf("Error sending boot command: %s", err) + flatCommands := strings.Join(commands, "") + seq, err := bootcommand.GenerateExpressionSequence(flatCommands) + 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 @@ -80,202 +106,3 @@ func (s *StepTypeBootCommand) Run(_ context.Context, state multistep.StateBag) m } func (*StepTypeBootCommand) Cleanup(multistep.StateBag) {} - -func scancodes(message string) []string { - // Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html - // - // Scancodes represent raw keyboard output and are fed to the VM by using - // powershell to use Msvm_Keyboard - // - // Scancodes are recorded here in pairs. The first entry represents - // the key press and the second entry represents the key release and is - // derived from the first by the addition of 0x80. - special := make(map[string][]string) - special[""] = []string{"0e", "8e"} - special[""] = []string{"53", "d3"} - special[""] = []string{"1c", "9c"} - special[""] = []string{"01", "81"} - special[""] = []string{"3b", "bb"} - special[""] = []string{"3c", "bc"} - special[""] = []string{"3d", "bd"} - special[""] = []string{"3e", "be"} - special[""] = []string{"3f", "bf"} - special[""] = []string{"40", "c0"} - special[""] = []string{"41", "c1"} - special[""] = []string{"42", "c2"} - special[""] = []string{"43", "c3"} - special[""] = []string{"44", "c4"} - special[""] = []string{"1c", "9c"} - special[""] = []string{"0f", "8f"} - special[""] = []string{"48", "c8"} - special[""] = []string{"50", "d0"} - special[""] = []string{"4b", "cb"} - special[""] = []string{"4d", "cd"} - special[""] = []string{"39", "b9"} - special[""] = []string{"52", "d2"} - special[""] = []string{"47", "c7"} - special[""] = []string{"4f", "cf"} - special[""] = []string{"49", "c9"} - special[""] = []string{"51", "d1"} - special[""] = []string{"38", "b8"} - special[""] = []string{"1d", "9d"} - special[""] = []string{"2a", "aa"} - special[""] = []string{"e038", "e0b8"} - special[""] = []string{"e01d", "e09d"} - special[""] = []string{"36", "b6"} - - 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, "") { - scancode = []string{"38"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: 38") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"1d"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: 1d") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"2a"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: 2a") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"b8"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: b8") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"9d"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: 9d") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"aa"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: aa") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"e038"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: e038") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"e01d"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: e01d") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"36"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: 36") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"e0b8"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: e0b8") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"e09d"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: e09d") - } - - if strings.HasPrefix(message, "") { - scancode = []string{"b6"} - message = message[len(""):] - log.Printf("Special code '' found, replacing with: b6") - } - - if strings.HasPrefix(message, "") { - log.Printf("Special code found, will sleep 1 second at this point.") - scancode = []string{"wait"} - message = message[len(""):] - } - - if strings.HasPrefix(message, "") { - log.Printf("Special code found, will sleep 5 seconds at this point.") - scancode = []string{"wait5"} - message = message[len(""):] - } - - if strings.HasPrefix(message, "") { - log.Printf("Special code found, will sleep 10 seconds at this point.") - scancode = []string{"wait10"} - message = message[len(""):] - } - - 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 -} diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index feb09598f..2ad3c324a 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -404,12 +404,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchVlanId: b.config.SwitchVlanId, }, - &hypervcommon.StepRun{ - BootWait: b.config.BootWait, - }, + &hypervcommon.StepRun{}, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.BootCommand, + BootWait: b.config.BootWait, SwitchName: b.config.SwitchName, Ctx: b.config.ctx, }, diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index 8309759fb..2d7d17771 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -434,12 +434,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe SwitchVlanId: b.config.SwitchVlanId, }, - &hypervcommon.StepRun{ - BootWait: b.config.BootWait, - }, + &hypervcommon.StepRun{}, &hypervcommon.StepTypeBootCommand{ BootCommand: b.config.BootCommand, + BootWait: b.config.BootWait, SwitchName: b.config.SwitchName, Ctx: b.config.ctx, }, diff --git a/common/boot_command/boot_command_ast.go b/common/boot_command/boot_command_ast.go index f9605a67b..9a5ec1eed 100644 --- a/common/boot_command/boot_command_ast.go +++ b/common/boot_command/boot_command_ast.go @@ -14,7 +14,10 @@ TODO: * fix vbox tests * comments * lower-case specials - * check that `` works on parallels. It's different now. + * pc-at abstraction + * check that `` works. It's different now. + * parallels + * hyperv- */ // KeysAction represents what we want to do with a key press.