Commit e5f674a8 authored by Mitchell Hashimoto's avatar Mitchell Hashimoto

builder/vmware/common: Mock driver and test for DHCP Lease IP lookup

parent 458bfd18
package common
import (
"github.com/mitchellh/multistep"
)
type DriverMock struct {
CompactDiskCalled bool
CompactDiskPath string
CompactDiskErr error
CreateDiskCalled bool
CreateDiskOutput string
CreateDiskSize string
CreateDiskTypeId string
CreateDiskErr error
IsRunningCalled bool
IsRunningPath string
IsRunningResult bool
IsRunningErr error
SSHAddressCalled bool
SSHAddressState multistep.StateBag
SSHAddressResult string
SSHAddressErr error
StartCalled bool
StartPath string
StartHeadless bool
StartErr error
StopCalled bool
StopPath string
StopErr error
SuppressMessagesCalled bool
SuppressMessagesPath string
SuppressMessagesErr error
ToolsIsoPathCalled bool
ToolsIsoPathFlavor string
ToolsIsoPathResult string
DhcpLeasesPathCalled bool
DhcpLeasesPathDevice string
DhcpLeasesPathResult string
VerifyCalled bool
VerifyErr error
}
func (d *DriverMock) CompactDisk(path string) error {
d.CompactDiskCalled = true
d.CompactDiskPath = path
return d.CompactDiskErr
}
func (d *DriverMock) CreateDisk(output string, size string, typeId string) error {
d.CreateDiskCalled = true
d.CreateDiskOutput = output
d.CreateDiskSize = size
d.CreateDiskTypeId = typeId
return d.CreateDiskErr
}
func (d *DriverMock) IsRunning(path string) (bool, error) {
d.IsRunningCalled = true
d.IsRunningPath = path
return d.IsRunningResult, d.IsRunningErr
}
func (d *DriverMock) SSHAddress(state multistep.StateBag) (string, error) {
d.SSHAddressCalled = true
d.SSHAddressState = state
return d.SSHAddressResult, d.SSHAddressErr
}
func (d *DriverMock) Start(path string, headless bool) error {
d.StartCalled = true
d.StartPath = path
d.StartHeadless = headless
return d.StartErr
}
func (d *DriverMock) Stop(path string) error {
d.StopCalled = true
d.StopPath = path
return d.StopErr
}
func (d *DriverMock) SuppressMessages(path string) error {
d.SuppressMessagesCalled = true
d.SuppressMessagesPath = path
return d.SuppressMessagesErr
}
func (d *DriverMock) ToolsIsoPath(flavor string) string {
d.ToolsIsoPathCalled = true
d.ToolsIsoPathFlavor = flavor
return d.ToolsIsoPathResult
}
func (d *DriverMock) DhcpLeasesPath(device string) string {
d.DhcpLeasesPathCalled = true
d.DhcpLeasesPathDevice = device
return d.DhcpLeasesPathResult
}
func (d *DriverMock) Verify() error {
d.VerifyCalled = true
return d.VerifyErr
}
package common
import (
"testing"
)
func TestDriverMock_impl(t *testing.T) {
var _ Driver = new(DriverMock)
}
package common
import (
"io/ioutil"
"os"
"testing"
)
func TestDHCPLeaseGuestLookup_impl(t *testing.T) {
var _ GuestIPFinder = new(DHCPLeaseGuestLookup)
}
func TestDHCPLeaseGuestLookup(t *testing.T) {
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
if _, err := tf.Write([]byte(testLeaseContents)); err != nil {
t.Fatalf("err: %s", err)
}
tf.Close()
defer os.Remove(tf.Name())
driver := new(DriverMock)
driver.DhcpLeasesPathResult = tf.Name()
finder := &DHCPLeaseGuestLookup{
Driver: driver,
Device: "vmnet8",
MACAddress: "00:0c:29:59:91:02",
}
ip, err := finder.GuestIP()
if err != nil {
t.Fatalf("err: %s", err)
}
if !driver.DhcpLeasesPathCalled {
t.Fatal("should ask for DHCP leases path")
}
if driver.DhcpLeasesPathDevice != "vmnet8" {
t.Fatal("should be vmnet8")
}
if ip != "192.168.126.130" {
t.Fatalf("bad: %#v", ip)
}
}
const testLeaseContents = `
# All times in this file are in UTC (GMT), not your local timezone. This is
# not a bug, so please don't ask about it. There is no portable way to
# store leases in the local timezone, so please don't request this as a
# feature. If this is inconvenient or confusing to you, we sincerely
# apologize. Seriously, though - don't ask.
# The format of this file is documented in the dhcpd.leases(5) manual page.
lease 192.168.126.129 {
starts 0 2013/09/15 23:58:51;
ends 1 2013/09/16 00:28:51;
hardware ethernet 00:0c:29:59:91:02;
client-hostname "precise64";
}
lease 192.168.126.130 {
starts 2 2013/09/17 21:39:07;
ends 2 2013/09/17 22:09:07;
hardware ethernet 00:0c:29:59:91:02;
client-hostname "precise64";
}
lease 192.168.126.128 {
starts 0 2013/09/15 20:09:59;
ends 0 2013/09/15 20:21:58;
hardware ethernet 00:0c:29:59:91:02;
client-hostname "precise64";
}
lease 192.168.126.127 {
starts 0 2013/09/15 20:09:59;
ends 0 2013/09/15 20:21:58;
hardware ethernet 01:0c:29:59:91:02;
client-hostname "precise64";
`
package iso
import (
"errors"
"io/ioutil"
"log"
"os"
"regexp"
"strings"
"time"
vmwcommon "github.com/mitchellh/packer/builder/vmware/common"
)
// Interface to help find the IP address of a running virtual machine.
type GuestIPFinder interface {
GuestIP() (string, error)
}
// DHCPLeaseGuestLookup looks up the IP address of a guest using DHCP
// lease information from the VMware network devices.
type DHCPLeaseGuestLookup struct {
// Driver that is being used (to find leases path)
Driver vmwcommon.Driver
// Device that the guest is connected to.
Device string
// MAC address of the guest.
MACAddress string
}
func (f *DHCPLeaseGuestLookup) GuestIP() (string, error) {
dhcpLeasesPath := f.Driver.DhcpLeasesPath(f.Device)
log.Printf("DHCP leases path: %s", dhcpLeasesPath)
if dhcpLeasesPath == "" {
return "", errors.New("no DHCP leases path found.")
}
fh, err := os.Open(dhcpLeasesPath)
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") {
// 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 && matches[1] == f.MACAddress && curLeaseEnd.Before(lastLeaseEnd) {
curIp = lastIp
curLeaseEnd = lastLeaseEnd
}
}
if curIp == "" {
return "", errors.New("IP not found for MAC in DHCP leases")
}
return curIp, nil
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment