Skip to content

Commit

Permalink
Merge branch 'vmware-iso-extraconfig' into release-0.10.0
Browse files Browse the repository at this point in the history
  • Loading branch information
arizvisa committed Apr 5, 2016
2 parents 86f0d3d + 7702bc7 commit 5740df3
Show file tree
Hide file tree
Showing 23 changed files with 1,644 additions and 402 deletions.
250 changes: 242 additions & 8 deletions builder/vmware/common/driver.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package common

import (
"errors"
"bytes"
"fmt"
"log"
"os"
"os/exec"
"io/ioutil"
"regexp"
"runtime"
"strconv"
"strings"
"time"
"net"

"github.com/mitchellh/multistep"
)
Expand All @@ -29,10 +34,6 @@ type Driver interface {
// Checks if the VMX file at the given path is running.
IsRunning(string) (bool, error)

// CommHost returns the host address for the VM that is being
// managed by this driver.
CommHost(multistep.StateBag) (string, error)

// Start starts a VM specified by the path to the VMX given.
Start(string, bool) error

Expand All @@ -49,14 +50,32 @@ type Driver interface {
// Attach the VMware tools ISO
ToolsInstall() error

// Get the path to the DHCP leases file for the given device.
DhcpLeasesPath(string) string

// Verify checks to make sure that this driver should function
// properly. This should check that all the files it will use
// appear to exist and so on. If everything is okay, this doesn't
// return an error. Otherwise, this returns an error.
// return an error. Otherwise, this returns an error. Each vmware
// driver should assign the VmwareMachine callback functions for locating
// paths within this function.
Verify() error

/// This is to establish a connection to the guest
CommHost(multistep.StateBag) (string, error)

/// These methods are generally implemented by the VmwareDriver
/// structure within this file. A driver implementation can
/// reimplement these, though, if it wants.

// Get the guest hw address for the vm
GuestAddress(multistep.StateBag) (string, error)

// Get the guest ip address for the vm
GuestIP(multistep.StateBag) (string, error)

// Get the host hw address for the vm
HostAddress(multistep.StateBag) (string, error)

// Get the host ip address for the vm
HostIP(multistep.StateBag) (string, error)
}

// NewDriver returns a new driver implementation for this operating
Expand Down Expand Up @@ -189,3 +208,218 @@ func compareVersions(versionFound string, versionWanted string, product string)

return nil
}

// helper functions that read configuration information from a file
func readNetmapConfig(path string) (NetworkMap,error) {
fd,err := os.Open(path)
if err != nil { return nil, err }
defer fd.Close()
return ReadNetworkMap(fd)
}

func readDhcpConfig(path string) (DhcpConfiguration,error) {
fd,err := os.Open(path)
if err != nil { return nil, err }
defer fd.Close()
return ReadDhcpConfiguration(fd)
}

// This VmwareDriver is a base class that contains default methods
// that a Driver can use or implement themselves.
type VmwareDriver struct {
/// These methods define paths that are utilized by the driver
/// A driver must overload these in order to point to the correct
/// files so that the address detection (ip and ethernet) machinery
/// works.
DhcpLeasesPath func(string) string
VmnetnatConfPath func() string
DhcpConfPath func() string
NetmapConfPath func() string
}

func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string,error) {
vmxPath := state.Get("vmx_path").(string)

log.Println("Lookup up IP information...")
f, err := os.Open(vmxPath)
if err != nil {
return "", err
}
defer f.Close()

vmxBytes, err := ioutil.ReadAll(f)
if err != nil {
return "", 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 "", errors.New("couldn't find MAC address in VMX")
}
}

res,err := net.ParseMAC(macAddress)
if err != nil { return "", err }

return res.String(),nil
}

func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string,error) {

// read netmap config
pathNetmap := d.NetmapConfPath()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap,err := readNetmapConfig(pathNetmap)
if err != nil { return "",err }

// convert the stashed network to a device
network := state.Get("vmnetwork").(string)
device,err := netmap.NameIntoDevice(network)
if err != nil { return "", err }

// figure out our MAC address for looking up the guest address
MACAddress,err := d.GuestAddress(state)
if err != nil { return "", err }

// figure out the correct dhcp leases
dhcpLeasesPath := d.DhcpLeasesPath(device)
log.Printf("DHCP leases path: %s", dhcpLeasesPath)
if dhcpLeasesPath == "" {
return "", errors.New("no DHCP leases path found.")
}

// open up the lease and read its contents
fh, err := os.Open(dhcpLeasesPath)
if err != nil {
return "", err
}
defer fh.Close()

dhcpBytes, err := ioutil.ReadAll(fh)
if err != nil {
return "", err
}

// start grepping through the file looking for fields that we care about
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") {
// Need to trim off CR character when running in windows
line = strings.TrimRight(line, "\r")

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 && strings.EqualFold(matches[1], MACAddress) && curLeaseEnd.Before(lastLeaseEnd) {
curIp = lastIp
curLeaseEnd = lastLeaseEnd
}
}
if curIp == "" {
return "", fmt.Errorf("IP not found for MAC %s in DHCP leases at %s", MACAddress, dhcpLeasesPath)
}
return curIp, nil
}

func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string,error) {

// parse network<->device mapping
pathNetmap := d.NetmapConfPath()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap,err := readNetmapConfig(pathNetmap)
if err != nil { return "",err }

// parse dhcpd configuration
pathDhcpConfig := d.DhcpConfPath()
if _, err := os.Stat(pathDhcpConfig); err != nil {
return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig)
}
config,err := readDhcpConfig(pathDhcpConfig)
if err != nil { return "",err }

// convert network to name
network := state.Get("vmnetwork").(string)
device,err := netmap.NameIntoDevice(network)
if err != nil { return "", err }

// find the entry configured in the dhcpd
interfaceConfig,err := config.HostByName(device)
if err != nil { return "", err }

// finally grab the hardware address
address,err := interfaceConfig.Hardware()
if err == nil { return address.String(), nil }

// we didn't find it, so search through our interfaces for the device name
interfaceList,err := net.Interfaces()
if err == nil { return "", err }

names := make([]string, 0)
for _,intf := range interfaceList {
if strings.HasSuffix( strings.ToLower(intf.Name), device ) {
return intf.HardwareAddr.String(),nil
}
names = append(names, intf.Name)
}
return "",fmt.Errorf("Unable to find device %s : %v", device, names)
}

func (d *VmwareDriver) HostIP(state multistep.StateBag) (string,error) {

// parse network<->device mapping
pathNetmap := d.NetmapConfPath()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap,err := readNetmapConfig(pathNetmap)
if err != nil { return "",err }

// parse dhcpd configuration
pathDhcpConfig := d.DhcpConfPath()
if _, err := os.Stat(pathDhcpConfig); err != nil {
return "", fmt.Errorf("Could not find vmnetdhcp conf file: %s", pathDhcpConfig)
}
config,err := readDhcpConfig(pathDhcpConfig)
if err != nil { return "",err }

// convert network to name
network := state.Get("vmnetwork").(string)
device,err := netmap.NameIntoDevice(network)
if err != nil { return "", err }

// find the entry configured in the dhcpd
interfaceConfig,err := config.HostByName(device)
if err != nil { return "", err }

address,err := interfaceConfig.IP4()
if err != nil { return "", err }

return address.String(),nil
}
11 changes: 7 additions & 4 deletions builder/vmware/common/driver_fusion5.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (

// Fusion5Driver is a driver that can run VMware Fusion 5.
type Fusion5Driver struct {
VmwareDriver

// This is the path to the "VMware Fusion.app"
AppPath string

Expand Down Expand Up @@ -139,6 +141,11 @@ func (d *Fusion5Driver) Verify() error {
return err
}

// default paths
d.VmwareDriver.DhcpLeasesPath = func(device string) string {
return "/var/db/vmware/vmnet-dhcpd-" + device + ".leases"
}

return nil
}

Expand All @@ -158,10 +165,6 @@ func (d *Fusion5Driver) ToolsInstall() error {
return nil
}

func (d *Fusion5Driver) DhcpLeasesPath(device string) string {
return "/var/db/vmware/vmnet-dhcpd-" + device + ".leases"
}

const fusionSuppressPlist = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
Expand Down
Loading

0 comments on commit 5740df3

Please sign in to comment.