diff --git a/builder/vmware/guest_ip.go b/builder/vmware/guest_ip.go index 96c6bfa45..e54d37d2e 100644 --- a/builder/vmware/guest_ip.go +++ b/builder/vmware/guest_ip.go @@ -1,5 +1,14 @@ package vmware +import ( + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + "time" +) + // Interface to help find the IP address of a running virtual machine. type GuestIPFinder interface { GuestIP() (string, error) @@ -9,12 +18,55 @@ type GuestIPFinder interface { // lease information from the VMware network devices. type DHCPLeaseGuestLookup struct { // Device that the guest is connected to. - Device string + Device string // MAC address of the guest. MACAddress string } func (f *DHCPLeaseGuestLookup) GuestIP() (string, error) { - return "", nil + fh, err := os.Open(fmt.Sprintf("/var/db/vmware/vmnet-dhcpd-%s.leases", f.Device)) + if err != nil { + return "", err + } + defer fh.Close() + + dhcpBytes, err := ioutil.ReadAll(fh) + if err != nil { + return "", err + } + + var lastIp string + var lastLeaseEnd time.Time + + var curIp string + var curLeaseEnd time.Time + + ipLineRe := regexp.MustCompile(`^lease (.+?) {$`) + endTimeLineRe := regexp.MustCompile(`^\s*ends \d (.+?);$`) + macLineRe := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`) + + for _, line := range strings.Split(string(dhcpBytes), "\n") { + matches := ipLineRe.FindStringSubmatch(line) + if matches != nil { + lastIp = matches[1] + continue + } + + matches = endTimeLineRe.FindStringSubmatch(line) + if matches != nil { + lastLeaseEnd, _ = time.Parse("2006/01/02 15:04:05", matches[1]) + continue + } + + // If the mac address matches and this lease ends farther in the + // future than the last match we might have, then choose it. + matches = macLineRe.FindStringSubmatch(line) + if matches != nil && matches[1] == f.MACAddress && curLeaseEnd.Before(lastLeaseEnd) { + curIp = lastIp + curLeaseEnd = lastLeaseEnd + } + } + + return curIp, nil } diff --git a/builder/vmware/step_wait_for_ssh.go b/builder/vmware/step_wait_for_ssh.go index e2232e67d..08f490c1a 100644 --- a/builder/vmware/step_wait_for_ssh.go +++ b/builder/vmware/step_wait_for_ssh.go @@ -1,8 +1,13 @@ package vmware import ( + "errors" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" + "io/ioutil" + "log" + "os" + "time" ) // This step waits for SSH to become available and establishes an SSH @@ -11,21 +16,63 @@ import ( // Uses: // config *config // ui packer.Ui +// vmx_path string // // Produces: // type stepWaitForSSH struct{} -func (stepWaitForSSH) Run(state map[string]interface{}) multistep.StepAction { +func (s *stepWaitForSSH) Run(state map[string]interface{}) multistep.StepAction { ui := state["ui"].(packer.Ui) + vmxPath := state["vmx_path"].(string) ui.Say("Waiting for SSH to become available...") - for { + time.Sleep(5 * time.Second) + + log.Println("Lookup up IP information...") // First we wait for the IP to become available... + ipLookup, err := s.dhcpLeaseLookup(vmxPath) + if err != nil { + log.Printf("Can't lookup via DHCP lease: %s", err) + } + + ip, err := ipLookup.GuestIP() + if err != nil { + log.Printf("IP lookup failed: %s", err) + continue + } + + log.Printf("Detected IP: %s", ip) } return multistep.ActionContinue } -func (stepWaitForSSH) Cleanup(map[string]interface{}) {} +func (s *stepWaitForSSH) Cleanup(map[string]interface{}) {} + +// Reads the network information for lookup via DHCP. +func (s *stepWaitForSSH) dhcpLeaseLookup(vmxPath string) (GuestIPFinder, error) { + f, err := os.Open(vmxPath) + if err != nil { + return nil, err + } + defer f.Close() + + vmxBytes, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + vmxData := ParseVMX(string(vmxBytes)) + + var ok bool + macAddress := "" + if macAddress, ok = vmxData["ethernet0.address"]; !ok || macAddress == "" { + if macAddress, ok = vmxData["ethernet0.generatedAddress"]; !ok || macAddress == "" { + return nil, errors.New("couldn't find MAC address in VMX") + } + } + + return &DHCPLeaseGuestLookup{"vmnet8", macAddress}, nil +}