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  }