diff --git a/builder/parallels/common/driver.go b/builder/parallels/common/driver.go index 771395d8c..bdb73719c 100644 --- a/builder/parallels/common/driver.go +++ b/builder/parallels/common/driver.go @@ -55,6 +55,7 @@ func NewDriver() (Driver, error) { var drivers map[string]Driver var prlctlPath string var supportedVersions []string + dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases" if runtime.GOOS != "darwin" { return nil, fmt.Errorf( @@ -74,11 +75,13 @@ func NewDriver() (Driver, error) { drivers = map[string]Driver{ "10": &Parallels10Driver{ Parallels9Driver: Parallels9Driver{ - PrlctlPath: prlctlPath, + PrlctlPath: prlctlPath, + dhcp_lease_file: dhcp_lease_file, }, }, "9": &Parallels9Driver{ - PrlctlPath: prlctlPath, + PrlctlPath: prlctlPath, + dhcp_lease_file: dhcp_lease_file, }, } diff --git a/builder/parallels/common/driver_9.go b/builder/parallels/common/driver_9.go index 7661a30c7..98d36cc24 100644 --- a/builder/parallels/common/driver_9.go +++ b/builder/parallels/common/driver_9.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "regexp" + "strconv" "strings" "time" @@ -18,6 +19,8 @@ import ( type Parallels9Driver struct { // This is the path to the "prlctl" application. PrlctlPath string + // The path to the parallels_dhcp_leases file + dhcp_lease_file string } func (d *Parallels9Driver) Import(name, srcPath, dstDir string, reassignMac bool) error { @@ -276,31 +279,43 @@ func (d *Parallels9Driver) Mac(vmName string) (string, error) { } // Finds the IP address of a VM connected that uses DHCP by its MAC address +// +// Parses the file /Library/Preferences/Parallels/parallels_dhcp_leases +// file contain a list of DHCP leases given by Parallels Desktop +// Example line: +// 10.211.55.181="1418921112,1800,001c42f593fb,ff42f593fb000100011c25b9ff001c42f593fb" +// IP Address ="Lease expiry, Lease time, MAC, MAC or DUID" func (d *Parallels9Driver) IpAddress(mac string) (string, error) { - var stdout bytes.Buffer - dhcp_lease_file := "/Library/Preferences/Parallels/parallels_dhcp_leases" if len(mac) != 12 { return "", fmt.Errorf("Not a valid MAC address: %s. It should be exactly 12 digits.", mac) } - cmd := exec.Command("grep", "-i", mac, dhcp_lease_file) - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { + leases, err := ioutil.ReadFile(d.dhcp_lease_file) + if err != nil { return "", err } - stdoutString := strings.TrimSpace(stdout.String()) - re := regexp.MustCompile("(.*)=.*") - ipMatch := re.FindAllStringSubmatch(stdoutString, 1) - - if len(ipMatch) != 1 { - return "", fmt.Errorf("IP lease not found for MAC address %s in: %s\n", mac, dhcp_lease_file) + re := regexp.MustCompile("(.*)=\"(.*),(.*)," + strings.ToLower(mac) + ",.*\"") + mostRecentIp := "" + mostRecentLease := uint64(0) + for _, l := range re.FindAllStringSubmatch(string(leases), -1) { + ip := l[1] + expiry, _ := strconv.ParseUint(l[2], 10, 64) + leaseTime, _ := strconv.ParseUint(l[3], 10, 32) + log.Printf("Found lease: %s for MAC: %s, expiring at %d, leased for %d s.\n", ip, mac, expiry, leaseTime) + if mostRecentLease <= expiry-leaseTime { + mostRecentIp = ip + mostRecentLease = expiry - leaseTime + } } - ip := ipMatch[0][1] - log.Printf("Found IP lease: %s for MAC address %s\n", ip, mac) - return ip, nil + if len(mostRecentIp) == 0 { + return "", fmt.Errorf("IP lease not found for MAC address %s in: %s\n", mac, d.dhcp_lease_file) + } + + log.Printf("Found IP lease: %s for MAC address %s\n", mostRecentIp, mac) + return mostRecentIp, nil } func (d *Parallels9Driver) ToolsIsoPath(k string) (string, error) { diff --git a/builder/parallels/common/driver_9_test.go b/builder/parallels/common/driver_9_test.go index a7c61b9d2..02fb4faf8 100644 --- a/builder/parallels/common/driver_9_test.go +++ b/builder/parallels/common/driver_9_test.go @@ -1,9 +1,60 @@ package common import ( + "io/ioutil" + "os" "testing" ) func TestParallels9Driver_impl(t *testing.T) { var _ Driver = new(Parallels9Driver) } + +func TestIpAddress(t *testing.T) { + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer os.Remove(tf.Name()) + + d := Parallels9Driver{ + dhcp_lease_file: tf.Name(), + } + + // No lease should be found in an empty file + ip, err := d.IpAddress("123456789012") + if err == nil { + t.Fatalf("Found IP: \"%v\". No IP should be found!\n", ip) + } + + // The most recent lease, 10.211.55.126 should be found + c := []byte(` +[vnic0] +10.211.55.125="1418288000,1800,001c4235240c,ff4235240c000100011c1c10e7001c4235240c" +10.211.55.126="1418288969,1800,001c4235240c,ff4235240c000100011c1c11ad001c4235240c" +10.211.55.254="1411712008,1800,001c42a51419,01001c42a51419" +`) + ioutil.WriteFile(tf.Name(), c, 0666) + ip, err = d.IpAddress("001C4235240c") + if err != nil { + t.Fatalf("Error: %v\n", err) + } + if ip != "10.211.55.126" { + t.Fatalf("Should have found 10.211.55.126, not %s!\n", ip) + } + + // The most recent lease, 10.211.55.124 should be found + c = []byte(`[vnic0] +10.211.55.124="1418288969,1800,001c4235240c,ff4235240c000100011c1c11ad001c4235240c" +10.211.55.125="1418288000,1800,001c4235240c,ff4235240c000100011c1c10e7001c4235240c" +10.211.55.254="1411712008,1800,001c42a51419,01001c42a51419" +`) + ioutil.WriteFile(tf.Name(), c, 0666) + ip, err = d.IpAddress("001c4235240c") + if err != nil { + t.Fatalf("Error: %v\n", err) + } + if ip != "10.211.55.124" { + t.Fatalf("Should have found 10.211.55.124, not %s!\n", ip) + } +}