github.com/alouche/packer@v0.3.7/builder/vmware/step_type_boot_command.go (about) 1 package vmware 2 3 import ( 4 "fmt" 5 "github.com/mitchellh/go-vnc" 6 "github.com/mitchellh/multistep" 7 "github.com/mitchellh/packer/packer" 8 "log" 9 "net" 10 "runtime" 11 "strings" 12 "time" 13 "unicode" 14 "unicode/utf8" 15 ) 16 17 const KeyLeftShift uint32 = 0xFFE1 18 19 type bootCommandTemplateData struct { 20 HTTPIP string 21 HTTPPort uint 22 Name string 23 } 24 25 // This step "types" the boot command into the VM over VNC. 26 // 27 // Uses: 28 // config *config 29 // http_port int 30 // ui packer.Ui 31 // vnc_port uint 32 // 33 // Produces: 34 // <nothing> 35 type stepTypeBootCommand struct{} 36 37 func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction { 38 config := state.Get("config").(*config) 39 httpPort := state.Get("http_port").(uint) 40 ui := state.Get("ui").(packer.Ui) 41 vncPort := state.Get("vnc_port").(uint) 42 43 // Connect to VNC 44 ui.Say("Connecting to VM via VNC") 45 nc, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", vncPort)) 46 if err != nil { 47 err := fmt.Errorf("Error connecting to VNC: %s", err) 48 state.Put("error", err) 49 ui.Error(err.Error()) 50 return multistep.ActionHalt 51 } 52 defer nc.Close() 53 54 c, err := vnc.Client(nc, &vnc.ClientConfig{Exclusive: true}) 55 if err != nil { 56 err := fmt.Errorf("Error handshaking with VNC: %s", err) 57 state.Put("error", err) 58 ui.Error(err.Error()) 59 return multistep.ActionHalt 60 } 61 defer c.Close() 62 63 log.Printf("Connected to VNC desktop: %s", c.DesktopName) 64 65 // Determine the host IP 66 var ipFinder HostIPFinder 67 if runtime.GOOS == "windows" { 68 ipFinder = new(VMnetNatConfIPFinder) 69 } else { 70 ipFinder = &IfconfigIPFinder{Device: "vmnet8"} 71 } 72 73 hostIp, err := ipFinder.HostIP() 74 if err != nil { 75 err := fmt.Errorf("Error detecting host IP: %s", err) 76 state.Put("error", err) 77 ui.Error(err.Error()) 78 return multistep.ActionHalt 79 } 80 81 log.Printf("Host IP for the VMware machine: %s", hostIp) 82 83 tplData := &bootCommandTemplateData{ 84 hostIp, 85 httpPort, 86 config.VMName, 87 } 88 89 ui.Say("Typing the boot command over VNC...") 90 for _, command := range config.BootCommand { 91 command, err := config.tpl.Process(command, tplData) 92 if err != nil { 93 err := fmt.Errorf("Error preparing boot command: %s", err) 94 state.Put("error", err) 95 ui.Error(err.Error()) 96 return multistep.ActionHalt 97 } 98 99 // Check for interrupts between typing things so we can cancel 100 // since this isn't the fastest thing. 101 if _, ok := state.GetOk(multistep.StateCancelled); ok { 102 return multistep.ActionHalt 103 } 104 105 vncSendString(c, command) 106 } 107 108 return multistep.ActionContinue 109 } 110 111 func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {} 112 113 func vncSendString(c *vnc.ClientConn, original string) { 114 special := make(map[string]uint32) 115 special["<bs>"] = 0xFF08 116 special["<del>"] = 0xFFFF 117 special["<enter>"] = 0xFF0D 118 special["<esc>"] = 0xFF1B 119 special["<f1>"] = 0xFFBE 120 special["<f2>"] = 0xFFBF 121 special["<f3>"] = 0xFFC0 122 special["<f4>"] = 0xFFC1 123 special["<f5>"] = 0xFFC2 124 special["<f6>"] = 0xFFC3 125 special["<f7>"] = 0xFFC4 126 special["<f8>"] = 0xFFC5 127 special["<f9>"] = 0xFFC6 128 special["<f10>"] = 0xFFC7 129 special["<f11>"] = 0xFFC8 130 special["<f12>"] = 0xFFC9 131 special["<return>"] = 0xFF0D 132 special["<tab>"] = 0xFF09 133 134 shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" 135 136 // TODO(mitchellh): Ripe for optimizations of some point, perhaps. 137 for len(original) > 0 { 138 var keyCode uint32 139 keyShift := false 140 141 if strings.HasPrefix(original, "<wait>") { 142 log.Printf("Special code '<wait>' found, sleeping one second") 143 time.Sleep(1 * time.Second) 144 original = original[len("<wait>"):] 145 continue 146 } 147 148 if strings.HasPrefix(original, "<wait5>") { 149 log.Printf("Special code '<wait5>' found, sleeping 5 seconds") 150 time.Sleep(5 * time.Second) 151 original = original[len("<wait5>"):] 152 continue 153 } 154 155 if strings.HasPrefix(original, "<wait10>") { 156 log.Printf("Special code '<wait10>' found, sleeping 10 seconds") 157 time.Sleep(10 * time.Second) 158 original = original[len("<wait10>"):] 159 continue 160 } 161 162 for specialCode, specialValue := range special { 163 if strings.HasPrefix(original, specialCode) { 164 log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue) 165 keyCode = specialValue 166 original = original[len(specialCode):] 167 break 168 } 169 } 170 171 if keyCode == 0 { 172 r, size := utf8.DecodeRuneInString(original) 173 original = original[size:] 174 keyCode = uint32(r) 175 keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) 176 177 log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift) 178 } 179 180 if keyShift { 181 c.KeyEvent(KeyLeftShift, true) 182 } 183 184 c.KeyEvent(keyCode, true) 185 c.KeyEvent(keyCode, false) 186 187 if keyShift { 188 c.KeyEvent(KeyLeftShift, false) 189 } 190 } 191 }