diff --git a/drivers/virtualbox/network.go b/drivers/virtualbox/network.go index 98b4fb37d1..e1a91022fb 100644 --- a/drivers/virtualbox/network.go +++ b/drivers/virtualbox/network.go @@ -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 diff --git a/drivers/virtualbox/network_test.go b/drivers/virtualbox/network_test.go index 1dec2efd25..262c343f70 100644 --- a/drivers/virtualbox/network_test.go +++ b/drivers/virtualbox/network_test.go @@ -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) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index da61b8538d..498696c717 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -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) {