mirror of https://github.com/docker/docs.git
Fix multiple bugs with host-only adapterss
Signed-off-by: David Gageot <david@gageot.net>
This commit is contained in:
parent
f5ba0f7420
commit
8553dd2d6c
|
|
@ -5,16 +5,20 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
)
|
||||
|
||||
const (
|
||||
buggyNetmask = "0f000000"
|
||||
dhcpPrefix = "HostInterfaceNetworking-"
|
||||
)
|
||||
|
||||
var (
|
||||
reHostonlyInterfaceCreated = regexp.MustCompile(`Interface '(.+)' was successfully created`)
|
||||
errNewHostOnlyInterfaceNotVisible = errors.New("The host-only interface we just created is not visible. This is a well known bug of VirtualBox. You might want to uninstall it and reinstall at least version 5.0.12 that is is supposed to fix this issue")
|
||||
reHostOnlyAdapterCreated = regexp.MustCompile(`Interface '(.+)' was successfully created`)
|
||||
errNewHostOnlyAdapterNotVisible = errors.New("The host-only adapter we just created is not visible. This is a well known VirtualBox bug. You might want to uninstall it and reinstall at least version 5.0.12 that is is supposed to fix this issue")
|
||||
)
|
||||
|
||||
// Host-only network.
|
||||
|
|
@ -23,7 +27,6 @@ type hostOnlyNetwork struct {
|
|||
GUID string
|
||||
DHCP bool
|
||||
IPv4 net.IPNet
|
||||
IPv6 net.IPNet
|
||||
HwAddr net.HardwareAddr
|
||||
Medium string
|
||||
Status string
|
||||
|
|
@ -32,17 +35,8 @@ type hostOnlyNetwork struct {
|
|||
|
||||
// Save changes the configuration of the host-only network.
|
||||
func (n *hostOnlyNetwork) Save(vbox VBoxManager) error {
|
||||
if n.IPv4.IP != nil && n.IPv4.Mask != nil {
|
||||
if err := vbox.vbm("hostonlyif", "ipconfig", n.Name, "--ip", n.IPv4.IP.String(), "--netmask", net.IP(n.IPv4.Mask).String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if n.IPv6.IP != nil && n.IPv6.Mask != nil {
|
||||
prefixLen, _ := n.IPv6.Mask.Size()
|
||||
if err := vbox.vbm("hostonlyif", "ipconfig", n.Name, "--ipv6", n.IPv6.IP.String(), "--netmasklengthv6", fmt.Sprintf("%d", prefixLen)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := n.SaveIPv4(vbox); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.DHCP {
|
||||
|
|
@ -52,23 +46,34 @@ func (n *hostOnlyNetwork) Save(vbox VBoxManager) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// createHostonlyNet creates a new host-only network.
|
||||
func createHostonlyNet(vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||
// SaveIPv4 changes the ipv4 configuration of the host-only network.
|
||||
func (n *hostOnlyNetwork) SaveIPv4(vbox VBoxManager) error {
|
||||
if n.IPv4.IP != nil && n.IPv4.Mask != nil {
|
||||
if err := vbox.vbm("hostonlyif", "ipconfig", n.Name, "--ip", n.IPv4.IP.String(), "--netmask", net.IP(n.IPv4.Mask).String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createHostonlyAdapter creates a new host-only network.
|
||||
func createHostonlyAdapter(vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||
out, err := vbox.vbmOut("hostonlyif", "create")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := reHostonlyInterfaceCreated.FindStringSubmatch(string(out))
|
||||
res := reHostOnlyAdapterCreated.FindStringSubmatch(string(out))
|
||||
if res == nil {
|
||||
return nil, errors.New("failed to create hostonly interface")
|
||||
return nil, errors.New("Failed to create host-only adapter")
|
||||
}
|
||||
|
||||
return &hostOnlyNetwork{Name: res[1]}, nil
|
||||
}
|
||||
|
||||
// listHostOnlyNetworks gets all host-only networks in a map keyed by HostonlyNet.NetworkName.
|
||||
func listHostOnlyNetworks(vbox VBoxManager) (map[string]*hostOnlyNetwork, error) {
|
||||
// listHostOnlyAdapters gets all host-only adapters in a map keyed by NetworkName.
|
||||
func listHostOnlyAdapters(vbox VBoxManager) (map[string]*hostOnlyNetwork, error) {
|
||||
out, err := vbox.vbmOut("list", "hostonlyifs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -90,14 +95,6 @@ func listHostOnlyNetworks(vbox VBoxManager) (map[string]*hostOnlyNetwork, error)
|
|||
n.IPv4.IP = net.ParseIP(val)
|
||||
case "NetworkMask":
|
||||
n.IPv4.Mask = parseIPv4Mask(val)
|
||||
case "IPV6Address":
|
||||
n.IPv6.IP = net.ParseIP(val)
|
||||
case "IPV6NetworkMaskPrefixLength":
|
||||
l, err := strconv.ParseUint(val, 10, 8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.IPv6.Mask = net.CIDRMask(int(l), net.IPv6len*8)
|
||||
case "HardwareAddress":
|
||||
mac, err := net.ParseMAC(val)
|
||||
if err != nil {
|
||||
|
|
@ -112,13 +109,13 @@ func listHostOnlyNetworks(vbox VBoxManager) (map[string]*hostOnlyNetwork, error)
|
|||
n.NetworkName = val
|
||||
|
||||
if _, present := byName[n.NetworkName]; present {
|
||||
return fmt.Errorf("VirtualBox is configured with multiple host-only interfaces with the same name %q. Please remove one.", n.NetworkName)
|
||||
return fmt.Errorf("VirtualBox is configured with multiple host-only adapters with the same name %q. Please remove one.", n.NetworkName)
|
||||
}
|
||||
byName[n.NetworkName] = n
|
||||
|
||||
if len(n.IPv4.IP) != 0 {
|
||||
if _, present := byIP[n.IPv4.IP.String()]; present {
|
||||
return fmt.Errorf("VirtualBox is configured with multiple host-only interfaces with the same IP %q. Please remove one.", n.IPv4.IP)
|
||||
return fmt.Errorf("VirtualBox is configured with multiple host-only adapters with the same IP %q. Please remove one.", n.IPv4.IP)
|
||||
}
|
||||
byIP[n.IPv4.IP.String()] = n
|
||||
}
|
||||
|
|
@ -135,65 +132,87 @@ func listHostOnlyNetworks(vbox VBoxManager) (map[string]*hostOnlyNetwork, error)
|
|||
return byName, nil
|
||||
}
|
||||
|
||||
func getHostOnlyNetwork(nets map[string]*hostOnlyNetwork, hostIP net.IP, netmask net.IPMask) *hostOnlyNetwork {
|
||||
func getHostOnlyAdapter(nets map[string]*hostOnlyNetwork, hostIP net.IP, netmask net.IPMask) *hostOnlyNetwork {
|
||||
for _, n := range nets {
|
||||
// Second part of this conditional handles a race where
|
||||
// VirtualBox returns us the incorrect netmask value for the
|
||||
// newly created interface.
|
||||
// newly created adapter.
|
||||
if hostIP.Equal(n.IPv4.IP) &&
|
||||
(netmask.String() == n.IPv4.Mask.String() || n.IPv4.Mask.String() == buggyNetmask) {
|
||||
log.Debugf("Found: %s", n.Name)
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("Not found")
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOrCreateHostOnlyNetwork(hostIP net.IP, netmask net.IPMask, dhcpIP net.IP, dhcpLowerIP net.IP, dhcpUpperIP net.IP, vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||
nets, err := listHostOnlyNetworks(vbox)
|
||||
func getOrCreateHostOnlyNetwork(hostIP net.IP, netmask net.IPMask, vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||
nets, err := listHostOnlyAdapters(vbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostOnlyNet := getHostOnlyNetwork(nets, hostIP, netmask)
|
||||
if hostOnlyNet != nil {
|
||||
return hostOnlyNet, nil
|
||||
hostOnlyAdapter := getHostOnlyAdapter(nets, hostIP, netmask)
|
||||
if hostOnlyAdapter != nil {
|
||||
return hostOnlyAdapter, nil
|
||||
}
|
||||
|
||||
// No existing host-only interface found. Create a new one.
|
||||
hostOnlyNet, err = createHostonlyNet(vbox)
|
||||
// No existing host-only adapter found. Create a new one.
|
||||
hostOnlyAdapter, err = createHostonlyAdapter(vbox)
|
||||
if err != nil {
|
||||
// Sometimes the host-only adapter fails to create. See https://www.virtualbox.org/ticket/14040
|
||||
// BUT, it is created in fact! So let's wait until it appears last in the list
|
||||
log.Warnf("Creating a new host-only adapter produced an error: %s", err)
|
||||
log.Warn("This is a known VirtualBox bug. Let's try to recover anyway...")
|
||||
|
||||
hostOnlyAdapter, err = waitForNewHostOnlyNetwork(nets, vbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Warnf("Found a new host-only adapter: %q", hostOnlyAdapter.Name)
|
||||
}
|
||||
|
||||
hostOnlyAdapter.IPv4.IP = hostIP
|
||||
hostOnlyAdapter.IPv4.Mask = netmask
|
||||
if err := hostOnlyAdapter.Save(vbox); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that the adapter still exists.
|
||||
// Sometimes, Vbox says it created it but then it cannot be found...
|
||||
nets, err = listHostOnlyAdapters(vbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostOnlyNet.IPv4.IP = hostIP
|
||||
hostOnlyNet.IPv4.Mask = netmask
|
||||
if err := hostOnlyNet.Save(vbox); err != nil {
|
||||
return nil, err
|
||||
found := getHostOnlyAdapter(nets, hostIP, netmask)
|
||||
if found == nil {
|
||||
return nil, errNewHostOnlyAdapterNotVisible
|
||||
}
|
||||
|
||||
dhcp := dhcpServer{}
|
||||
dhcp.IPv4.IP = dhcpIP
|
||||
dhcp.IPv4.Mask = netmask
|
||||
dhcp.LowerIP = dhcpLowerIP
|
||||
dhcp.UpperIP = dhcpUpperIP
|
||||
dhcp.Enabled = true
|
||||
if err := addHostonlyDHCP(hostOnlyNet.Name, dhcp, vbox); err != nil {
|
||||
return nil, err
|
||||
return hostOnlyAdapter, nil
|
||||
}
|
||||
|
||||
func waitForNewHostOnlyNetwork(oldNets map[string]*hostOnlyNetwork, vbox VBoxManager) (*hostOnlyNetwork, error) {
|
||||
for i := 0; i < 10; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
newNets, err := listHostOnlyAdapters(vbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for name, latestNet := range newNets {
|
||||
if _, present := oldNets[name]; !present {
|
||||
return latestNet, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the interface really exists.
|
||||
// Sometimes, Vbox says it created the interface but then it cannot be found...
|
||||
nets, err = listHostOnlyNetworks(vbox)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hostOnlyNet = getHostOnlyNetwork(nets, hostIP, netmask)
|
||||
if hostOnlyNet == nil {
|
||||
return nil, errNewHostOnlyInterfaceNotVisible
|
||||
}
|
||||
|
||||
return hostOnlyNet, nil
|
||||
return nil, errors.New("Failed to find a new host-only adapter")
|
||||
}
|
||||
|
||||
// DHCP server info.
|
||||
|
|
@ -205,22 +224,57 @@ type dhcpServer struct {
|
|||
Enabled bool
|
||||
}
|
||||
|
||||
func addDHCPServer(kind, name string, d dhcpServer, vbox VBoxManager) error {
|
||||
command := "modify"
|
||||
|
||||
// On some platforms (OSX), creating a hostonlyinterface adds a default dhcpserver
|
||||
// While on others (Windows?) it does not.
|
||||
dhcps, err := getDHCPServers(vbox)
|
||||
// removeOrphanDHCPServers removed the DHCP servers linked to no host-only adapter
|
||||
func removeOrphanDHCPServers(vbox VBoxManager) error {
|
||||
dhcps, err := listDHCPServers(vbox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := dhcps[name]; !ok {
|
||||
command = "add"
|
||||
if len(dhcps) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
nets, err := listHostOnlyAdapters(vbox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name := range dhcps {
|
||||
if strings.HasPrefix(name, dhcpPrefix) {
|
||||
if _, present := nets[name]; !present {
|
||||
if err := vbox.vbm("dhcpserver", "remove", "--netname", name); err != nil {
|
||||
log.Warnf("Unable to remove orphan dhcp server %q: %s", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addHostOnlyDHCPServer adds a DHCP server to a host-only network.
|
||||
func addHostOnlyDHCPServer(ifname string, d dhcpServer, vbox VBoxManager) error {
|
||||
name := dhcpPrefix + ifname
|
||||
|
||||
dhcps, err := listDHCPServers(vbox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// On some platforms (OSX), creating a host-only adapter adds a default dhcpserver,
|
||||
// while on others (Windows?) it does not.
|
||||
command := "add"
|
||||
if dhcp, ok := dhcps[name]; ok {
|
||||
command = "modify"
|
||||
if (dhcp.IPv4.IP.Equal(d.IPv4.IP)) && (dhcp.IPv4.Mask.String() == d.IPv4.Mask.String()) && (dhcp.LowerIP.Equal(d.LowerIP)) && (dhcp.UpperIP.Equal(d.UpperIP)) && dhcp.Enabled {
|
||||
// dhcp is up to date
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{"dhcpserver", command,
|
||||
kind, name,
|
||||
"--netname", name,
|
||||
"--ip", d.IPv4.IP.String(),
|
||||
"--netmask", net.IP(d.IPv4.Mask).String(),
|
||||
"--lowerip", d.LowerIP.String(),
|
||||
|
|
@ -235,13 +289,8 @@ func addDHCPServer(kind, name string, d dhcpServer, vbox VBoxManager) error {
|
|||
return vbox.vbm(args...)
|
||||
}
|
||||
|
||||
// addHostonlyDHCP adds a DHCP server to a host-only network.
|
||||
func addHostonlyDHCP(ifname string, d dhcpServer, vbox VBoxManager) error {
|
||||
return addDHCPServer("--netname", "HostInterfaceNetworking-"+ifname, d, vbox)
|
||||
}
|
||||
|
||||
// getDHCPServers gets all DHCP server settings in a map keyed by DHCP.NetworkName.
|
||||
func getDHCPServers(vbox VBoxManager) (map[string]*dhcpServer, error) {
|
||||
// listDHCPServers lists all DHCP server settings in a map keyed by DHCP.NetworkName.
|
||||
func listDHCPServers(vbox VBoxManager) (map[string]*dhcpServer, error) {
|
||||
out, err := vbox.vbmOut("list", "dhcpservers")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func TestGetHostOnlyNetworkHappy(t *testing.T) {
|
|||
"HostInterfaceNetworking-vboxnet0": expectedHostOnlyNetwork,
|
||||
}
|
||||
|
||||
n := getHostOnlyNetwork(vboxNets, ip, ipnet.Mask)
|
||||
n := getHostOnlyAdapter(vboxNets, ip, ipnet.Mask)
|
||||
if !reflect.DeepEqual(n, expectedHostOnlyNetwork) {
|
||||
t.Fatalf("Expected result of calling getHostOnlyNetwork to be the same as expected but it was not:\nexpected: %+v\nactual: %+v\n", expectedHostOnlyNetwork, n)
|
||||
}
|
||||
|
|
@ -107,7 +107,7 @@ func TestGetHostOnlyNetworkNotFound(t *testing.T) {
|
|||
"HostInterfaceNetworking-vboxnet0": vboxNet,
|
||||
}
|
||||
|
||||
n := getHostOnlyNetwork(vboxNets, ip, ipnet.Mask)
|
||||
n := getHostOnlyAdapter(vboxNets, ip, ipnet.Mask)
|
||||
if n != nil {
|
||||
t.Fatalf("Expected vbox net to be nil but it has a value: %+v\n", n)
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ func TestGetHostOnlyNetworkWindows10Bug(t *testing.T) {
|
|||
|
||||
// The Mask that we are passing in will be the "legitimate" mask, so it
|
||||
// must differ from the magic buggy mask.
|
||||
n := getHostOnlyNetwork(vboxNets, ip, net.IPMask(net.ParseIP("255.255.255.0").To4()))
|
||||
n := getHostOnlyAdapter(vboxNets, ip, net.IPMask(net.ParseIP("255.255.255.0").To4()))
|
||||
if !reflect.DeepEqual(n, expectedHostOnlyNetwork) {
|
||||
t.Fatalf("Expected result of calling getHostOnlyNetwork to be the same as expected but it was not:\nexpected: %+v\nactual: %+v\n", expectedHostOnlyNetwork, n)
|
||||
}
|
||||
|
|
@ -148,7 +148,7 @@ func TestListHostOnlyNetworks(t *testing.T) {
|
|||
stdOut: stdOutOneHostOnlyNetwork,
|
||||
}
|
||||
|
||||
nets, err := listHostOnlyNetworks(vbox)
|
||||
nets, err := listHostOnlyAdapters(vbox)
|
||||
|
||||
assert.Equal(t, 1, len(nets))
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -161,7 +161,6 @@ func TestListHostOnlyNetworks(t *testing.T) {
|
|||
assert.False(t, net.DHCP)
|
||||
assert.Equal(t, "192.168.99.1", net.IPv4.IP.String())
|
||||
assert.Equal(t, "ffffff00", net.IPv4.Mask.String())
|
||||
assert.Empty(t, net.IPv6.IP)
|
||||
assert.Equal(t, "0a:00:27:00:00:00", net.HwAddr.String())
|
||||
assert.Equal(t, "Ethernet", net.Medium)
|
||||
assert.Equal(t, "Up", net.Status)
|
||||
|
|
@ -174,7 +173,7 @@ func TestListTwoHostOnlyNetworks(t *testing.T) {
|
|||
stdOut: stdOutTwoHostOnlyNetwork,
|
||||
}
|
||||
|
||||
nets, err := listHostOnlyNetworks(vbox)
|
||||
nets, err := listHostOnlyAdapters(vbox)
|
||||
|
||||
assert.Equal(t, 2, len(nets))
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -187,7 +186,6 @@ func TestListTwoHostOnlyNetworks(t *testing.T) {
|
|||
assert.False(t, net.DHCP)
|
||||
assert.Equal(t, "169.254.37.187", net.IPv4.IP.String())
|
||||
assert.Equal(t, "ffffff00", net.IPv4.Mask.String())
|
||||
assert.Empty(t, net.IPv6.IP)
|
||||
assert.Equal(t, "0a:00:27:00:00:01", net.HwAddr.String())
|
||||
assert.Equal(t, "Ethernet", net.Medium)
|
||||
assert.Equal(t, "Up", net.Status)
|
||||
|
|
@ -203,7 +201,7 @@ Name: vboxnet1
|
|||
VBoxNetworkName: HostInterfaceNetworking-vboxnet1`,
|
||||
}
|
||||
|
||||
nets, err := listHostOnlyNetworks(vbox)
|
||||
nets, err := listHostOnlyAdapters(vbox)
|
||||
|
||||
assert.Equal(t, 2, len(nets))
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -223,7 +221,7 @@ func TestGetHostOnlyNetwork(t *testing.T) {
|
|||
stdOut: stdOutOneHostOnlyNetwork,
|
||||
}
|
||||
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), nil, nil, nil, vbox)
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
||||
|
||||
assert.NotNil(t, net)
|
||||
assert.Equal(t, "HostInterfaceNetworking-vboxnet0", net.NetworkName)
|
||||
|
|
@ -243,10 +241,10 @@ NetworkMask: 255.255.255.0
|
|||
VBoxNetworkName: HostInterfaceNetworking-vboxnet1`,
|
||||
}
|
||||
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), nil, nil, nil, vbox)
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
||||
|
||||
assert.Nil(t, net)
|
||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only interfaces with the same IP "192.168.99.1". Please remove one.`)
|
||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same IP "192.168.99.1". Please remove one.`)
|
||||
}
|
||||
|
||||
func TestFailIfTwoNetworksHaveSameName(t *testing.T) {
|
||||
|
|
@ -258,10 +256,10 @@ Name: vboxnet0
|
|||
VBoxNetworkName: HostInterfaceNetworking-vboxnet0`,
|
||||
}
|
||||
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), nil, nil, nil, vbox)
|
||||
net, err := getOrCreateHostOnlyNetwork(net.ParseIP("192.168.99.1"), parseIPv4Mask("255.255.255.0"), vbox)
|
||||
|
||||
assert.Nil(t, net)
|
||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only interfaces with the same name "HostInterfaceNetworking-vboxnet0". Please remove one.`)
|
||||
assert.EqualError(t, err, `VirtualBox is configured with multiple host-only adapters with the same name "HostInterfaceNetworking-vboxnet0". Please remove one.`)
|
||||
}
|
||||
|
||||
func TestGetDHCPServers(t *testing.T) {
|
||||
|
|
@ -270,7 +268,7 @@ func TestGetDHCPServers(t *testing.T) {
|
|||
stdOut: stdOutListTwoDHCPServers,
|
||||
}
|
||||
|
||||
servers, err := getDHCPServers(vbox)
|
||||
servers, err := listDHCPServers(vbox)
|
||||
|
||||
assert.Equal(t, 2, len(servers))
|
||||
assert.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ func (d *Driver) PreCreateCheck() error {
|
|||
}
|
||||
|
||||
// Check that Host-only interfaces are ok
|
||||
if _, err = listHostOnlyNetworks(d.VBoxManager); err != nil {
|
||||
if _, err = listHostOnlyAdapters(d.VBoxManager); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ func (d *Driver) Create() error {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Infof("Creating VirtualBox VM...")
|
||||
log.Info("Creating VirtualBox VM...")
|
||||
|
||||
// import b2d VM if requested
|
||||
if d.Boot2DockerImportVM != "" {
|
||||
|
|
@ -379,10 +379,6 @@ func (d *Driver) Create() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := d.setupHostOnlyNetwork(d.MachineName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.vbm("storagectl", d.MachineName,
|
||||
"--name", "SATA",
|
||||
"--add", "sata",
|
||||
|
|
@ -442,6 +438,7 @@ func (d *Driver) Create() error {
|
|||
}
|
||||
}
|
||||
|
||||
log.Info("Starting the VM...")
|
||||
return d.Start()
|
||||
}
|
||||
|
||||
|
|
@ -466,9 +463,10 @@ func (d *Driver) Start() error {
|
|||
return err
|
||||
}
|
||||
|
||||
var hostOnlyAdapter *hostOnlyNetwork
|
||||
if s == state.Stopped {
|
||||
// check network to re-create if needed
|
||||
if err := d.setupHostOnlyNetwork(d.MachineName); err != nil {
|
||||
if hostOnlyAdapter, err = d.setupHostOnlyNetwork(d.MachineName); err != nil {
|
||||
return fmt.Errorf("Error setting up host only network on machine start: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -479,8 +477,10 @@ func (d *Driver) Start() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.vbm("startvm", d.MachineName, "--type", "headless"); err != nil {
|
||||
return err
|
||||
// TODO: We could capture the last lines of the vbox log
|
||||
return fmt.Errorf("Unable to start the VM: %s", err)
|
||||
}
|
||||
case state.Paused:
|
||||
if err := d.vbm("controlvm", d.MachineName, "resume", "--type", "headless"); err != nil {
|
||||
|
|
@ -501,6 +501,55 @@ func (d *Driver) Start() error {
|
|||
return ErrMustEnableVTX
|
||||
}
|
||||
|
||||
log.Infof("Waiting for an IP...")
|
||||
if err := d.waitForIP(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hostOnlyAdapter == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check that the host-only adapter we just created can still be found
|
||||
// Sometimes it is corrupted after the VM is started.
|
||||
nets, err := listHostOnlyAdapters(d.VBoxManager)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ip, network, err := parseAndValidateCIDR(d.HostOnlyCIDR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostOnlyNet := getHostOnlyAdapter(nets, ip, network.Mask)
|
||||
if hostOnlyNet != nil {
|
||||
// OK, we found a valid host-only adapter
|
||||
return nil
|
||||
}
|
||||
|
||||
// This happens a lot on windows. The adapter has an invalid IP and the VM has the same IP
|
||||
log.Warn("The host-only adapter is corrupted. Let's stop the VM, fix the host-only adapter and restart the VM")
|
||||
if err := d.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We have to be sure the host-only adapter is not used by the VM
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
log.Debugf("Fixing %+v...", hostOnlyAdapter)
|
||||
if err := hostOnlyAdapter.SaveIPv4(d.VBoxManager); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We have to be sure the adapter is updated before starting the VM
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if err := d.vbm("startvm", d.MachineName, "--type", "headless"); err != nil {
|
||||
return fmt.Errorf("Unable to start the VM: %s", err)
|
||||
}
|
||||
|
||||
log.Infof("Waiting for an IP...")
|
||||
return d.waitForIP()
|
||||
}
|
||||
|
||||
|
|
@ -593,8 +642,7 @@ func (d *Driver) Remove() error {
|
|||
}
|
||||
|
||||
func (d *Driver) GetState() (state.State, error) {
|
||||
stdout, stderr, err := d.vbmOutErr("showvminfo", d.MachineName,
|
||||
"--machinereadable")
|
||||
stdout, stderr, err := d.vbmOutErr("showvminfo", d.MachineName, "--machinereadable")
|
||||
if err != nil {
|
||||
if reMachineNotFound.FindString(stderr) != "" {
|
||||
return state.Error, ErrMachineNotExist
|
||||
|
|
@ -671,7 +719,7 @@ func (d *Driver) generateDiskImage(size int) error {
|
|||
return createDiskImage(d.diskPath(), size, tarBuf)
|
||||
}
|
||||
|
||||
func (d *Driver) setupHostOnlyNetwork(machineName string) error {
|
||||
func (d *Driver) setupHostOnlyNetwork(machineName string) (*hostOnlyNetwork, error) {
|
||||
hostOnlyCIDR := d.HostOnlyCIDR
|
||||
|
||||
// This is to assist in migrating from version 0.2 to 0.3 format
|
||||
|
|
@ -682,38 +730,48 @@ func (d *Driver) setupHostOnlyNetwork(machineName string) error {
|
|||
|
||||
ip, network, err := parseAndValidateCIDR(hostOnlyCIDR)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Searching for hostonly interface for IPv4: %s and Mask: %s", ip, network.Mask)
|
||||
hostOnlyAdapter, err := getOrCreateHostOnlyNetwork(ip, network.Mask, d.VBoxManager)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Removing orphan DHCP servers...")
|
||||
if err := removeOrphanDHCPServers(d.VBoxManager); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dhcpAddr, err := getRandomIPinSubnet(ip)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Adding/Modifying DHCP server %q...", dhcpAddr)
|
||||
nAddr := network.IP.To4()
|
||||
lowerDHCPIP := net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(100))
|
||||
upperDHCPIP := net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(254))
|
||||
|
||||
log.Debugf("using %s for dhcp address", dhcpAddr)
|
||||
|
||||
hostOnlyNetwork, err := getOrCreateHostOnlyNetwork(
|
||||
ip,
|
||||
network.Mask,
|
||||
dhcpAddr,
|
||||
lowerDHCPIP,
|
||||
upperDHCPIP,
|
||||
d.VBoxManager,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
dhcp := dhcpServer{}
|
||||
dhcp.IPv4.IP = dhcpAddr
|
||||
dhcp.IPv4.Mask = network.Mask
|
||||
dhcp.LowerIP = net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(100))
|
||||
dhcp.UpperIP = net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(254))
|
||||
dhcp.Enabled = true
|
||||
if err := addHostOnlyDHCPServer(hostOnlyAdapter.Name, dhcp, d.VBoxManager); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.vbm("modifyvm", machineName,
|
||||
if err := d.vbm("modifyvm", machineName,
|
||||
"--nic2", "hostonly",
|
||||
"--nictype2", d.HostOnlyNicType,
|
||||
"--nicpromisc2", d.HostOnlyPromiscMode,
|
||||
"--hostonlyadapter2", hostOnlyNetwork.Name,
|
||||
"--cableconnected2", "on")
|
||||
"--hostonlyadapter2", hostOnlyAdapter.Name,
|
||||
"--cableconnected2", "on"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hostOnlyAdapter, nil
|
||||
}
|
||||
|
||||
func parseAndValidateCIDR(hostOnlyCIDR string) (net.IP, *net.IPNet, error) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue