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,18 +35,9 @@ 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 { | ||||
| 	if err := n.SaveIPv4(vbox); 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 n.DHCP { | ||||
| 		vbox.vbm("hostonlyif", "ipconfig", n.Name, "--dhcp") // not implemented as of VirtualBox 4.3
 | ||||
|  | @ -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 | ||||
| 		} | ||||
| 
 | ||||
| 	hostOnlyNet.IPv4.IP = hostIP | ||||
| 	hostOnlyNet.IPv4.Mask = netmask | ||||
| 	if err := hostOnlyNet.Save(vbox); err != nil { | ||||
| 		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 | ||||
| 	} | ||||
| 
 | ||||
| 	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 | ||||
| 	} | ||||
| 
 | ||||
| 	// Check that the interface really exists.
 | ||||
| 	// Sometimes, Vbox says it created the interface but then it cannot be found...
 | ||||
| 	nets, err = listHostOnlyNetworks(vbox) | ||||
| 	// 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 = getHostOnlyNetwork(nets, hostIP, netmask) | ||||
| 	if hostOnlyNet == nil { | ||||
| 		return nil, errNewHostOnlyInterfaceNotVisible | ||||
| 
 | ||||
| 	found := getHostOnlyAdapter(nets, hostIP, netmask) | ||||
| 	if found == nil { | ||||
| 		return nil, errNewHostOnlyAdapterNotVisible | ||||
| 	} | ||||
| 
 | ||||
| 	return hostOnlyNet, nil | ||||
| 	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 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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