From 3304bf8923a3ce9087dfd05defe0a9ff3615bddc Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Tue, 2 Jun 2015 23:00:13 -0400 Subject: [PATCH 1/3] virtualbox: check for hostonly iface and add if needed on start Signed-off-by: Evan Hazlett --- drivers/virtualbox/virtualbox.go | 44 ++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 7bb93d3713..efb9495e1b 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -273,20 +273,7 @@ func (d *Driver) Create() error { return err } - hostOnlyNetwork, err := getOrCreateHostOnlyNetwork( - net.ParseIP("192.168.99.1"), - net.IPv4Mask(255, 255, 255, 0), - net.ParseIP("192.168.99.2"), - net.ParseIP("192.168.99.100"), - net.ParseIP("192.168.99.254")) - if err != nil { - return err - } - if err := vbm("modifyvm", d.MachineName, - "--nic2", "hostonly", - "--nictype2", "82540EM", - "--hostonlyadapter2", hostOnlyNetwork.Name, - "--cableconnected2", "on"); err != nil { + if err := setupHostOnlyNetwork(d.MachineName); err != nil { return err } @@ -372,6 +359,11 @@ func (d *Driver) Start() error { return err } + // check network to re-create if needed + if err := setupHostOnlyNetwork(d.MachineName); err != nil { + return err + } + switch s { case state.Stopped, state.Saved: d.SSHPort, err = setPortForwarding(d.MachineName, 1, "ssh", "tcp", 22, d.SSHPort) @@ -678,3 +670,27 @@ func setPortForwarding(machine string, interfaceNum int, mapName, protocol strin } return actualHostPort, nil } + +func setupHostOnlyNetwork(machineName string) error { + hostOnlyNetwork, err := getOrCreateHostOnlyNetwork( + net.ParseIP("192.168.99.1"), + net.IPv4Mask(255, 255, 255, 0), + net.ParseIP("192.168.99.2"), + net.ParseIP("192.168.99.100"), + net.ParseIP("192.168.99.254"), + ) + + if err != nil { + return err + } + + if err := vbm("modifyvm", machineName, + "--nic2", "hostonly", + "--nictype2", "82540EM", + "--hostonlyadapter2", hostOnlyNetwork.Name, + "--cableconnected2", "on"); err != nil { + return err + } + + return nil +} From ca5f0738e26dcc13e6c55f5756c2aabb7c718ffa Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Thu, 4 Jun 2015 13:03:51 -0400 Subject: [PATCH 2/3] vbox: allow specifying host only adapter cidr Signed-off-by: Evan Hazlett --- docs/index.md | 11 +++++ drivers/virtualbox/virtualbox.go | 75 +++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/docs/index.md b/docs/index.md index 00354cf9d0..f8e10b973a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1464,6 +1464,7 @@ Options: - `--virtualbox-disk-size`: Size of disk for the host in MB. - `--virtualbox-boot2docker-url`: The URL of the boot2docker image. Defaults to the latest available version. - `--virtualbox-import-boot2docker-vm`: The name of a Boot2Docker VM to import. + - `--virtualbox-hostonly-cidr`: The CIDR of the host only adapter. The `--virtualbox-boot2docker-url` flag takes a few different forms. By default, if no value is specified for this flag, Machine will check locally for @@ -1482,6 +1483,15 @@ file://$HOME/Downloads/rc.iso` to test out a release candidate ISO that you have downloaded already. You could also just get an ISO straight from the Internet using the `http://` form. +To customize the host only adapter, you can use the `--virtualbox-hostonly-cidr` +flag. This will specify the host IP and Machine will calculate the VirtualBox +DHCP server address (a random IP on the subnet between `.1` and `.25`) so +it does not clash with the specified host IP. +Machine will also specify the DHCP lower bound to `.100` and the upper bound +to `.254`. For example, a specified CIDR of `192.168.24.1/24` would have a +DHCP server between `192.168.24.2-25`, a lower bound of `192.168.24.100` and +upper bound of `192.168.24.254`. + Environment variables and default values: | CLI option | Environment variable | Default | @@ -1491,6 +1501,7 @@ Environment variables and default values: | `--virtualbox-disk-size` | `VIRTUALBOX_DISK_SIZE` | `20000` | | `--virtualbox-boot2docker-url` | `VIRTUALBOX_BOOT2DOCKER_URL` | *Latest boot2docker url* | | `--virtualbox-import-boot2docker-vm` | - | `boot2docker-vm` | +| `--virtualbox-hostonly-cidr` | `VIRTUALBOX_HOSTONLY_CIDR` | `192.168.99.1/24` | #### VMware Fusion Creates machines locally on [VMware Fusion](http://www.vmware.com/products/fusion). Requires VMware Fusion to be installed. diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index efb9495e1b..08f8492f7f 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "net" "os" "os/exec" @@ -44,6 +45,7 @@ type Driver struct { SwarmDiscovery string storePath string Boot2DockerImportVM string + HostOnlyCIDR string } func init() { @@ -86,6 +88,12 @@ func GetCreateFlags() []cli.Flag { Usage: "The name of a Boot2Docker VM to import", Value: "", }, + cli.StringFlag{ + Name: "virtualbox-hostonly-cidr", + Usage: "Specify the Host Only CIDR", + Value: "192.168.99.1/24", + EnvVar: "VIRTUALBOX_HOSTONLY_CIDR", + }, } } @@ -150,6 +158,7 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.SwarmDiscovery = flags.String("swarm-discovery") d.SSHUser = "docker" d.Boot2DockerImportVM = flags.String("virtualbox-import-boot2docker-vm") + d.HostOnlyCIDR = flags.String("virtualbox-hostonly-cidr") return nil } @@ -273,7 +282,7 @@ func (d *Driver) Create() error { return err } - if err := setupHostOnlyNetwork(d.MachineName); err != nil { + if err := d.setupHostOnlyNetwork(d.MachineName); err != nil { return err } @@ -360,7 +369,7 @@ func (d *Driver) Start() error { } // check network to re-create if needed - if err := setupHostOnlyNetwork(d.MachineName); err != nil { + if err := d.setupHostOnlyNetwork(d.MachineName); err != nil { return err } @@ -564,6 +573,44 @@ func (d *Driver) generateDiskImage(size int) error { return createDiskImage(d.diskPath(), size, raw) } +func (d *Driver) setupHostOnlyNetwork(machineName string) error { + ip, network, err := net.ParseCIDR(d.HostOnlyCIDR) + nAddr := network.IP.To4() + + var dhcpAddr net.IP + // select pseudo-random DHCP addr; make sure not to clash with the host + for { + n := rand.Intn(25) + if byte(n) != nAddr[3] { + dhcpAddr = net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(1)) + log.Debugf("using %s for dhcp address", dhcpAddr) + break + } + } + + hostOnlyNetwork, err := getOrCreateHostOnlyNetwork( + ip, + network.Mask, + dhcpAddr, + net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(100)), + net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(254)), + ) + + if err != nil { + return err + } + + if err := vbm("modifyvm", machineName, + "--nic2", "hostonly", + "--nictype2", "82540EM", + "--hostonlyadapter2", hostOnlyNetwork.Name, + "--cableconnected2", "on"); err != nil { + return err + } + + return nil +} + // createDiskImage makes a disk image at dest with the given size in MB. If r is // not nil, it will be read as a raw disk image to convert from. func createDiskImage(dest string, size int, r io.Reader) error { @@ -670,27 +717,3 @@ func setPortForwarding(machine string, interfaceNum int, mapName, protocol strin } return actualHostPort, nil } - -func setupHostOnlyNetwork(machineName string) error { - hostOnlyNetwork, err := getOrCreateHostOnlyNetwork( - net.ParseIP("192.168.99.1"), - net.IPv4Mask(255, 255, 255, 0), - net.ParseIP("192.168.99.2"), - net.ParseIP("192.168.99.100"), - net.ParseIP("192.168.99.254"), - ) - - if err != nil { - return err - } - - if err := vbm("modifyvm", machineName, - "--nic2", "hostonly", - "--nictype2", "82540EM", - "--hostonlyadapter2", hostOnlyNetwork.Name, - "--cableconnected2", "on"); err != nil { - return err - } - - return nil -} From 2f4d8c4237c70cac7de3b0039162345e25ac82f7 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Tue, 9 Jun 2015 11:00:40 -0400 Subject: [PATCH 3/3] vbox: refactor random IP allocation to func Signed-off-by: Evan Hazlett --- drivers/virtualbox/virtualbox.go | 49 +++++++++++++++++++++------ drivers/virtualbox/virtualbox_test.go | 30 ++++++++++++++++ 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 08f8492f7f..4b0c774ffa 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -3,6 +3,7 @@ package virtualbox import ( "archive/tar" "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -29,6 +30,10 @@ const ( isoFilename = "boot2docker.iso" ) +var ( + ErrUnableToGenerateRandomIP = errors.New("unable to generate random IP") +) + type Driver struct { IPAddress string CPU int @@ -577,23 +582,22 @@ func (d *Driver) setupHostOnlyNetwork(machineName string) error { ip, network, err := net.ParseCIDR(d.HostOnlyCIDR) nAddr := network.IP.To4() - var dhcpAddr net.IP - // select pseudo-random DHCP addr; make sure not to clash with the host - for { - n := rand.Intn(25) - if byte(n) != nAddr[3] { - dhcpAddr = net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(1)) - log.Debugf("using %s for dhcp address", dhcpAddr) - break - } + dhcpAddr, err := getRandomIPinSubnet(network.IP) + if err != nil { + return err } + 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, - net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(100)), - net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(254)), + lowerDHCPIP, + upperDHCPIP, ) if err != nil { @@ -717,3 +721,26 @@ func setPortForwarding(machine string, interfaceNum int, mapName, protocol strin } return actualHostPort, nil } + +// getRandomIPinSubnet returns a pseudo-random net.IP in the same +// subnet as the IP passed +func getRandomIPinSubnet(baseIP net.IP) (net.IP, error) { + var dhcpAddr net.IP + + nAddr := baseIP.To4() + // select pseudo-random DHCP addr; make sure not to clash with the host + // only try 5 times and bail if no random received + for i := 0; i < 5; i++ { + n := rand.Intn(25) + if byte(n) != nAddr[3] { + dhcpAddr = net.IPv4(nAddr[0], nAddr[1], nAddr[2], byte(1)) + break + } + } + + if dhcpAddr == nil { + return nil, ErrUnableToGenerateRandomIP + } + + return dhcpAddr, nil +} diff --git a/drivers/virtualbox/virtualbox_test.go b/drivers/virtualbox/virtualbox_test.go index 50e40b533b..5df5e5f4cb 100644 --- a/drivers/virtualbox/virtualbox_test.go +++ b/drivers/virtualbox/virtualbox_test.go @@ -1 +1,31 @@ package virtualbox + +import ( + "net" + "testing" +) + +func TestGetRandomIPinSubnet(t *testing.T) { + // test IP 1.2.3.4 + testIP := net.IPv4(byte(1), byte(2), byte(3), byte(4)) + newIP, err := getRandomIPinSubnet(testIP) + if err != nil { + t.Fatal(err) + } + + if testIP.Equal(newIP) { + t.Fatalf("expected different IP (source %s); received %s", testIP.String(), newIP.String()) + } + + if newIP[0] != testIP[0] { + t.Fatalf("expected first octet of %d; received %d", testIP[0], newIP[0]) + } + + if newIP[1] != testIP[1] { + t.Fatalf("expected second octet of %d; received %d", testIP[1], newIP[1]) + } + + if newIP[2] != testIP[2] { + t.Fatalf("expected third octet of %d; received %d", testIP[2], newIP[2]) + } +}