Skip to content

Commit b57c4c2

Browse files
committed
Determine host-only interface dynamically, stop assuming eth1
Signed-off-by: Travis Thieman <travis.thieman@gmail.com>
1 parent 8046e13 commit b57c4c2

File tree

2 files changed

+116
-9
lines changed

2 files changed

+116
-9
lines changed

drivers/virtualbox/virtualbox.go

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,67 @@ func (d *Driver) GetState() (state.State, error) {
652652
return state.None, nil
653653
}
654654

655+
func (d *Driver) getHostOnlyMACAddress() (string, error) {
656+
// Return the MAC address of the host-only adapter
657+
// assigned to this machine. The returned address
658+
// is lower-cased and does not contain colons.
659+
660+
stdout, stderr, err := d.vbmOutErr("showvminfo", d.MachineName, "--machinereadable")
661+
if err != nil {
662+
if reMachineNotFound.FindString(stderr) != "" {
663+
return "", ErrMachineNotExist
664+
}
665+
return "", err
666+
}
667+
668+
// First, we get the number of the host-only interface
669+
re := regexp.MustCompile(`(?m)^hostonlyadapter([\d]+)`)
670+
groups := re.FindStringSubmatch(stdout)
671+
if len(groups) < 2 {
672+
return "", errors.New("Machine does not have a host-only adapter")
673+
}
674+
675+
// Then we grab the MAC address based on that number
676+
adapterNumber := groups[1]
677+
re = regexp.MustCompile(fmt.Sprintf("(?m)^macaddress%s=\"(.*)\"", adapterNumber))
678+
groups = re.FindStringSubmatch(stdout)
679+
if len(groups) < 2 {
680+
return "", fmt.Errorf("Could not find MAC address for adapter %v", adapterNumber)
681+
}
682+
683+
return strings.ToLower(groups[1]), nil
684+
}
685+
686+
func (d *Driver) parseIPForMACFromIPAddr(ipAddrOutput string, macAddress string) (string, error) {
687+
// Given the output of "ip addr show" on the VM, return the IPv4 address
688+
// of the interface with the given MAC address.
689+
690+
lines := strings.Split(ipAddrOutput, "\n")
691+
returnNextIP := false
692+
693+
for _, line := range lines {
694+
line = strings.TrimSpace(line)
695+
696+
if strings.HasPrefix(line, "link") { // line contains MAC address
697+
vals := strings.Split(line, " ")
698+
if len(vals) >= 2 {
699+
macBlock := vals[1]
700+
macWithoutColons := strings.Replace(macBlock, ":", "", -1)
701+
if macWithoutColons == macAddress { // we are in the correct device block
702+
returnNextIP = true
703+
}
704+
}
705+
} else if strings.HasPrefix(line, "inet") && !strings.HasPrefix(line, "inet6") && returnNextIP {
706+
vals := strings.Split(line, " ")
707+
if len(vals) >= 2 {
708+
return vals[1][:strings.Index(vals[1], "/")], nil
709+
}
710+
}
711+
}
712+
713+
return "", fmt.Errorf("Could not find matching IP for MAC address %v", macAddress)
714+
}
715+
655716
func (d *Driver) GetIP() (string, error) {
656717
// DHCP is used to get the IP, so virtualbox hosts don't have IPs unless
657718
// they are running
@@ -663,23 +724,26 @@ func (d *Driver) GetIP() (string, error) {
663724
return "", drivers.ErrHostIsNotRunning
664725
}
665726

666-
output, err := drivers.RunSSHCommandFromDriver(d, "ip addr show dev eth1")
727+
macAddress, err := d.getHostOnlyMACAddress()
728+
if err != nil {
729+
return "", err
730+
}
731+
732+
log.Debugf("Host-only MAC: %s\n", macAddress)
733+
734+
output, err := drivers.RunSSHCommandFromDriver(d, "ip addr show")
667735
if err != nil {
668736
return "", err
669737
}
670738

671739
log.Debugf("SSH returned: %s\nEND SSH\n", output)
672740

673-
// parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1
674-
lines := strings.Split(output, "\n")
675-
for _, line := range lines {
676-
vals := strings.Split(strings.TrimSpace(line), " ")
677-
if len(vals) >= 2 && vals[0] == "inet" {
678-
return vals[1][:strings.Index(vals[1], "/")], nil
679-
}
741+
ipAddress, err := d.parseIPForMACFromIPAddr(output, macAddress)
742+
if err != nil {
743+
return "", err
680744
}
681745

682-
return "", fmt.Errorf("No IP address found %s", output)
746+
return ipAddress, nil
683747
}
684748

685749
func (d *Driver) publicSSHKeyPath() string {

drivers/virtualbox/virtualbox_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,49 @@ func TestGetRandomIPinSubnet(t *testing.T) {
143143
}
144144
}
145145

146+
func TestGetHolyOnlyMACAddress(t *testing.T) {
147+
driver := newTestDriver("default")
148+
driver.VBoxManager = &VBoxManagerMock{
149+
args: "showvminfo default --machinereadable",
150+
stdOut: "unrelatedfield=whatever\nhostonlyadapter2=\"vboxnet1\"\nmacaddress2=\"004488AABBCC\"\n",
151+
}
152+
153+
result, err := driver.getHostOnlyMACAddress()
154+
expected := "004488aabbcc"
155+
assert.NoError(t, err)
156+
assert.Equal(t, expected, result)
157+
}
158+
159+
func TestGetHostOnlyMACAddressWhenNoHostOnlyAdapter(t *testing.T) {
160+
driver := newTestDriver("default")
161+
driver.VBoxManager = &VBoxManagerMock{
162+
args: "showvminfo default --machinereadable",
163+
stdOut: "unrelatedfield=whatever\n",
164+
}
165+
166+
result, err := driver.getHostOnlyMACAddress()
167+
assert.Empty(t, result)
168+
assert.Equal(t, err, errors.New("Machine does not have a host-only adapter"))
169+
}
170+
171+
func TestParseIPForMACFromIPAddr(t *testing.T) {
172+
driver := newTestDriver("default")
173+
174+
ipAddrOutput := "1: eth0:\n link/ether 00:44:88:aa:bb:cc\n inet 1.2.3.4/24\n2: eth1:\n link/ether 11:55:99:dd:ee:ff\n inet 5.6.7.8/24"
175+
176+
result, err := driver.parseIPForMACFromIPAddr(ipAddrOutput, "004488aabbcc")
177+
assert.NoError(t, err)
178+
assert.Equal(t, result, "1.2.3.4")
179+
180+
result, err = driver.parseIPForMACFromIPAddr(ipAddrOutput, "115599ddeeff")
181+
assert.NoError(t, err)
182+
assert.Equal(t, result, "5.6.7.8")
183+
184+
result, err = driver.parseIPForMACFromIPAddr(ipAddrOutput, "000000000000")
185+
assert.Empty(t, result)
186+
assert.Equal(t, err, errors.New("Could not find matching IP for MAC address 000000000000"))
187+
}
188+
146189
func TestGetIPErrors(t *testing.T) {
147190
var tests = []struct {
148191
stdOut string

0 commit comments

Comments
 (0)