Re-worked the vmware builder drivers so that they're able to check multiple ip addresses when trying to connect via the CommHost.

This commit is contained in:
Ali Rizvi-Santiago 2020-05-28 15:36:45 -05:00
parent 0609909f1a
commit 229b5d17ff
4 changed files with 94 additions and 40 deletions

View File

@ -70,7 +70,7 @@ type Driver interface {
GuestAddress(multistep.StateBag) (string, error) GuestAddress(multistep.StateBag) (string, error)
// Get the guest ip address for the vm // Get the guest ip address for the vm
GuestIP(multistep.StateBag) (string, error) PotentialGuestIP(multistep.StateBag) ([]string, error)
// Get the host hw address for the vm // Get the host hw address for the vm
HostAddress(multistep.StateBag) (string, error) HostAddress(multistep.StateBag) (string, error)
@ -327,12 +327,12 @@ func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string, error) {
return res.String(), nil return res.String(), nil
} }
func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, error) {
// grab network mapper // grab network mapper
netmap, err := d.NetworkMapper() netmap, err := d.NetworkMapper()
if err != nil { if err != nil {
return "", err return []string{}, err
} }
// convert the stashed network to a device // convert the stashed network to a device
@ -350,14 +350,14 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
vmxPath := state.Get("vmx_path").(string) vmxPath := state.Get("vmx_path").(string)
vmxData, err := readVMXConfig(vmxPath) vmxData, err := readVMXConfig(vmxPath)
if err != nil { if err != nil {
return "", err return []string{}, err
} }
var device string var device string
device, err = readCustomDeviceName(vmxData) device, err = readCustomDeviceName(vmxData)
devices = append(devices, device) devices = append(devices, device)
if err != nil { if err != nil {
return "", err return []string{}, err
} }
log.Printf("GuestIP discovered custom device matching %s: %s", network, device) log.Printf("GuestIP discovered custom device matching %s: %s", network, device)
} }
@ -365,15 +365,19 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
// figure out our MAC address for looking up the guest address // figure out our MAC address for looking up the guest address
MACAddress, err := d.GuestAddress(state) MACAddress, err := d.GuestAddress(state)
if err != nil { if err != nil {
return "", err return []string{}, err
} }
// iterate through all of the devices and collect all the dhcp lease entries
// that we possibly cacn.
var available_lease_entries []dhcpLeaseEntry
for _, device := range devices { for _, device := range devices {
// figure out the correct dhcp leases // figure out the correct dhcp leases
dhcpLeasesPath := d.DhcpLeasesPath(device) dhcpLeasesPath := d.DhcpLeasesPath(device)
log.Printf("Trying DHCP leases path: %s", dhcpLeasesPath) log.Printf("Trying DHCP leases path: %s", dhcpLeasesPath)
if dhcpLeasesPath == "" { if dhcpLeasesPath == "" {
return "", fmt.Errorf("no DHCP leases path found for device %s", device) return []string{}, fmt.Errorf("no DHCP leases path found for device %s", device)
} }
// open up the path to the dhcpd leases // open up the path to the dhcpd leases
@ -387,39 +391,69 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
// and then read its contents // and then read its contents
leaseEntries, err := ReadDhcpdLeaseEntries(fh) leaseEntries, err := ReadDhcpdLeaseEntries(fh)
if err != nil { if err != nil {
return "", err return []string{}, err
} }
// Parse our MAC address again. There's no need to check for an // Parse our MAC address again. There's no need to check for an
// error because we've already parsed this successfully. // error because we've already parsed this successfully.
hwaddr, _ := net.ParseMAC(MACAddress) hwaddr, _ := net.ParseMAC(MACAddress)
// start grepping through the file looking for fields that we care about // Go through our available lease entries and see which ones are within
var lastIp string // scope, and that match to our hardware address.
var lastLeaseEnd time.Time results := make([]dhcpLeaseEntry, 0)
var curIp string
var curLeaseEnd time.Time
for _, entry := range leaseEntries { for _, entry := range leaseEntries {
lastIp = entry.address // First check for leases that are still valid. The timestamp for
lastLeaseEnd = entry.ends // each lease should be in UTC according to the documentation at
// the top of VMWare's dhcpd.leases file.
now := time.Now().UTC()
if !(now.After(entry.starts) && now.Before(entry.ends)) {
continue
}
// If the mac address matches and this lease ends farther in the // Next check for any where the hardware address matches.
// future than the last match we might have, then choose it. if !bytes.Equal(hwaddr, entry.ether) {
if bytes.Equal(hwaddr, entry.ether) && curLeaseEnd.Before(lastLeaseEnd) { continue
curIp = lastIp }
curLeaseEnd = lastLeaseEnd
// This entry fits within our constraints, so store it so we can
// check it out later.
results = append(results, entry)
}
// If we weren't able to grab any results, then we'll do a "loose"-match
// where we only look for anything where the hardware address matches.
if len(results) == 0 {
log.Printf("Unable to find an exact match for DHCP lease. Falling back to a loose match for hw address %v", MACAddress)
for _, entry := range leaseEntries {
if bytes.Equal(hwaddr, entry.ether) {
results = append(results, entry)
}
} }
} }
if curIp != "" { // If we found something, then we need to add it to our current list
return curIp, nil // of lease entries.
if len(results) > 0 {
available_lease_entries = append(available_lease_entries, results...)
} }
// Now we need to map our results to get the address so we can return it.iterate through our results and figure out which one
// is actually up...and should be relevant.
} }
return "", fmt.Errorf("None of the found device(s) %v has a DHCP lease for MAC %s", devices, MACAddress) // Check if we found any lease entries that correspond to us. If so, then we
// need to map() them in order to extract the address field to return to the
// caller.
if len(available_lease_entries) > 0 {
addrs := make([]string, 0)
for _, entry := range available_lease_entries {
addrs = append(addrs, entry.address)
}
return addrs, nil
}
return []string{}, fmt.Errorf("None of the found device(s) %v has a DHCP lease for MAC %s", devices, MACAddress)
} }
func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) {

View File

@ -265,16 +265,16 @@ func (d *ESX5Driver) HostIP(multistep.StateBag) (string, error) {
return host, err return host, err
} }
func (d *ESX5Driver) GuestIP(multistep.StateBag) (string, error) { func (d *ESX5Driver) PotentialGuestIP(multistep.StateBag) ([]string, error) {
// GuestIP is defined by the user as d.Host..but let's validate it just to be sure // GuestIP is defined by the user as d.Host..but let's validate it just to be sure
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port)) conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port))
if err != nil { if err != nil {
return "", err return []string{}, err
} }
defer conn.Close() defer conn.Close()
host, _, err := net.SplitHostPort(conn.RemoteAddr().String()) host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
return host, err return []string{host}, err
} }
func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) { func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) {

View File

@ -52,10 +52,10 @@ type DriverMock struct {
GuestAddressResult string GuestAddressResult string
GuestAddressErr error GuestAddressErr error
GuestIPCalled bool PotentialGuestIPCalled bool
GuestIPState multistep.StateBag PotentialGuestIPState multistep.StateBag
GuestIPResult string PotentialGuestIPResult []string
GuestIPErr error PotentialGuestIPErr error
StartCalled bool StartCalled bool
StartPath string StartPath string
@ -192,10 +192,10 @@ func (d *DriverMock) GuestAddress(state multistep.StateBag) (string, error) {
return d.GuestAddressResult, d.GuestAddressErr return d.GuestAddressResult, d.GuestAddressErr
} }
func (d *DriverMock) GuestIP(state multistep.StateBag) (string, error) { func (d *DriverMock) PotentialGuestIP(state multistep.StateBag) ([]string, error) {
d.GuestIPCalled = true d.PotentialGuestIPCalled = true
d.GuestIPState = state d.PotentialGuestIPState = state
return d.GuestIPResult, d.GuestIPErr return d.PotentialGuestIPResult, d.PotentialGuestIPErr
} }
func (d *DriverMock) Start(path string, headless bool) error { func (d *DriverMock) Start(path string, headless bool) error {

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net"
"github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/helper/multistep"
) )
@ -16,18 +17,37 @@ func CommHost(config *SSHConfig) func(multistep.StateBag) (string, error) {
return config.Comm.SSHHost, nil return config.Comm.SSHHost, nil
} }
ipAddress, err := driver.GuestIP(state) ipAddrs, err := driver.PotentialGuestIP(state)
if err != nil { if err != nil {
log.Printf("IP lookup failed: %s", err) log.Printf("IP lookup failed: %s", err)
return "", fmt.Errorf("IP lookup failed: %s", err) return "", fmt.Errorf("IP lookup failed: %s", err)
} }
if ipAddress == "" { if len(ipAddrs) == 0 {
log.Println("IP is blank, no IP yet.") log.Println("IP is blank, no IP yet.")
return "", errors.New("IP is blank") return "", errors.New("IP is blank")
} }
log.Printf("Detected IP: %s", ipAddress) // Iterate through our list of addresses and dial each one. This way we
return ipAddress, nil // can dial up each one to see which lease is actually correct and has
// ssh up.
for index, ipAddress := range ipAddrs {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ipAddress, config.Comm.SSHPort))
// If we got a connection, then we should be good to go. Return the
// address to the caller and pray that things work out.
if err == nil {
conn.Close()
log.Printf("Detected IP: %s", ipAddress)
return ipAddress, nil
}
// Otherwise we need to iterate to the next entry and keep hoping.
log.Printf("Ignoring entry %d at %s:%d due to host being down.", index, ipAddress, config.Comm.SSHPort)
}
return "", errors.New("Host is not up")
} }
} }