github.phpd.cn/hashicorp/packer@v1.3.2/builder/vmware/common/step_configure_vnc.go (about) 1 package common 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "math/rand" 8 "net" 9 10 "github.com/hashicorp/packer/helper/multistep" 11 "github.com/hashicorp/packer/packer" 12 ) 13 14 // This step configures the VM to enable the VNC server. 15 // 16 // Uses: 17 // ui packer.Ui 18 // vmx_path string 19 // 20 // Produces: 21 // vnc_port uint - The port that VNC is configured to listen on. 22 type StepConfigureVNC struct { 23 Enabled bool 24 VNCBindAddress string 25 VNCPortMin uint 26 VNCPortMax uint 27 VNCDisablePassword bool 28 } 29 30 type VNCAddressFinder interface { 31 VNCAddress(string, uint, uint) (string, uint, error) 32 33 // UpdateVMX, sets driver specific VNC values to VMX data. 34 UpdateVMX(vncAddress, vncPassword string, vncPort uint, vmxData map[string]string) 35 } 36 37 func (StepConfigureVNC) VNCAddress(vncBindAddress string, portMin, portMax uint) (string, uint, error) { 38 // Find an open VNC port. Note that this can still fail later on 39 // because we have to release the port at some point. But this does its 40 // best. 41 var vncPort uint 42 portRange := int(portMax - portMin) 43 for { 44 if portRange > 0 { 45 vncPort = uint(rand.Intn(portRange)) + portMin 46 } else { 47 vncPort = portMin 48 } 49 50 log.Printf("Trying port: %d", vncPort) 51 l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", vncBindAddress, vncPort)) 52 if err == nil { 53 defer l.Close() 54 break 55 } 56 } 57 return vncBindAddress, vncPort, nil 58 } 59 60 func VNCPassword(skipPassword bool) string { 61 if skipPassword { 62 return "" 63 } 64 length := int(8) 65 66 charSet := []byte("012345689abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 67 charSetLength := len(charSet) 68 69 password := make([]byte, length) 70 71 for i := 0; i < length; i++ { 72 password[i] = charSet[rand.Intn(charSetLength)] 73 } 74 75 return string(password) 76 } 77 78 func (s *StepConfigureVNC) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { 79 if !s.Enabled { 80 log.Println("Skipping VNC configuration step...") 81 return multistep.ActionContinue 82 } 83 84 driver := state.Get("driver").(Driver) 85 ui := state.Get("ui").(packer.Ui) 86 vmxPath := state.Get("vmx_path").(string) 87 88 vmxData, err := ReadVMX(vmxPath) 89 if err != nil { 90 err := fmt.Errorf("Error reading VMX file: %s", err) 91 state.Put("error", err) 92 ui.Error(err.Error()) 93 return multistep.ActionHalt 94 } 95 96 var vncFinder VNCAddressFinder 97 if finder, ok := driver.(VNCAddressFinder); ok { 98 vncFinder = finder 99 } else { 100 vncFinder = s 101 } 102 log.Printf("Looking for available port between %d and %d", s.VNCPortMin, s.VNCPortMax) 103 vncBindAddress, vncPort, err := vncFinder.VNCAddress(s.VNCBindAddress, s.VNCPortMin, s.VNCPortMax) 104 if err != nil { 105 state.Put("error", err) 106 ui.Error(err.Error()) 107 return multistep.ActionHalt 108 } 109 110 vncPassword := VNCPassword(s.VNCDisablePassword) 111 112 log.Printf("Found available VNC port: %d", vncPort) 113 114 vncFinder.UpdateVMX(vncBindAddress, vncPassword, vncPort, vmxData) 115 116 if err := WriteVMX(vmxPath, vmxData); err != nil { 117 err := fmt.Errorf("Error writing VMX data: %s", err) 118 state.Put("error", err) 119 ui.Error(err.Error()) 120 return multistep.ActionHalt 121 } 122 123 state.Put("vnc_port", vncPort) 124 state.Put("vnc_ip", vncBindAddress) 125 state.Put("vnc_password", vncPassword) 126 127 return multistep.ActionContinue 128 } 129 130 func (StepConfigureVNC) UpdateVMX(address, password string, port uint, data map[string]string) { 131 data["remotedisplay.vnc.enabled"] = "TRUE" 132 data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) 133 data["remotedisplay.vnc.ip"] = address 134 if len(password) > 0 { 135 data["remotedisplay.vnc.password"] = password 136 } 137 } 138 139 func (StepConfigureVNC) Cleanup(multistep.StateBag) { 140 }