mirror of https://github.com/docker/docs.git
Merge pull request #1292 from ehazlett/vbox-recreate-hostonlyif
virtualbox: more flexibility with networking
This commit is contained in:
commit
3387f988b8
|
@ -1464,6 +1464,7 @@ Options:
|
||||||
- `--virtualbox-disk-size`: Size of disk for the host in MB.
|
- `--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-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-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
|
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
|
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
|
downloaded already. You could also just get an ISO straight from the Internet
|
||||||
using the `http://` form.
|
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:
|
Environment variables and default values:
|
||||||
|
|
||||||
| CLI option | Environment variable | Default |
|
| CLI option | Environment variable | Default |
|
||||||
|
@ -1491,6 +1501,7 @@ Environment variables and default values:
|
||||||
| `--virtualbox-disk-size` | `VIRTUALBOX_DISK_SIZE` | `20000` |
|
| `--virtualbox-disk-size` | `VIRTUALBOX_DISK_SIZE` | `20000` |
|
||||||
| `--virtualbox-boot2docker-url` | `VIRTUALBOX_BOOT2DOCKER_URL` | *Latest boot2docker url* |
|
| `--virtualbox-boot2docker-url` | `VIRTUALBOX_BOOT2DOCKER_URL` | *Latest boot2docker url* |
|
||||||
| `--virtualbox-import-boot2docker-vm` | - | `boot2docker-vm` |
|
| `--virtualbox-import-boot2docker-vm` | - | `boot2docker-vm` |
|
||||||
|
| `--virtualbox-hostonly-cidr` | `VIRTUALBOX_HOSTONLY_CIDR` | `192.168.99.1/24` |
|
||||||
|
|
||||||
#### VMware Fusion
|
#### VMware Fusion
|
||||||
Creates machines locally on [VMware Fusion](http://www.vmware.com/products/fusion). Requires VMware Fusion to be installed.
|
Creates machines locally on [VMware Fusion](http://www.vmware.com/products/fusion). Requires VMware Fusion to be installed.
|
||||||
|
|
|
@ -3,9 +3,11 @@ package virtualbox
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -28,6 +30,10 @@ const (
|
||||||
isoFilename = "boot2docker.iso"
|
isoFilename = "boot2docker.iso"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnableToGenerateRandomIP = errors.New("unable to generate random IP")
|
||||||
|
)
|
||||||
|
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
IPAddress string
|
IPAddress string
|
||||||
CPU int
|
CPU int
|
||||||
|
@ -44,6 +50,7 @@ type Driver struct {
|
||||||
SwarmDiscovery string
|
SwarmDiscovery string
|
||||||
storePath string
|
storePath string
|
||||||
Boot2DockerImportVM string
|
Boot2DockerImportVM string
|
||||||
|
HostOnlyCIDR string
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -86,6 +93,12 @@ func GetCreateFlags() []cli.Flag {
|
||||||
Usage: "The name of a Boot2Docker VM to import",
|
Usage: "The name of a Boot2Docker VM to import",
|
||||||
Value: "",
|
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 +163,7 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||||
d.SwarmDiscovery = flags.String("swarm-discovery")
|
d.SwarmDiscovery = flags.String("swarm-discovery")
|
||||||
d.SSHUser = "docker"
|
d.SSHUser = "docker"
|
||||||
d.Boot2DockerImportVM = flags.String("virtualbox-import-boot2docker-vm")
|
d.Boot2DockerImportVM = flags.String("virtualbox-import-boot2docker-vm")
|
||||||
|
d.HostOnlyCIDR = flags.String("virtualbox-hostonly-cidr")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -273,20 +287,7 @@ func (d *Driver) Create() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hostOnlyNetwork, err := getOrCreateHostOnlyNetwork(
|
if err := d.setupHostOnlyNetwork(d.MachineName); err != nil {
|
||||||
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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +373,11 @@ func (d *Driver) Start() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check network to re-create if needed
|
||||||
|
if err := d.setupHostOnlyNetwork(d.MachineName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
switch s {
|
switch s {
|
||||||
case state.Stopped, state.Saved:
|
case state.Stopped, state.Saved:
|
||||||
d.SSHPort, err = setPortForwarding(d.MachineName, 1, "ssh", "tcp", 22, d.SSHPort)
|
d.SSHPort, err = setPortForwarding(d.MachineName, 1, "ssh", "tcp", 22, d.SSHPort)
|
||||||
|
@ -572,6 +578,43 @@ func (d *Driver) generateDiskImage(size int) error {
|
||||||
return createDiskImage(d.diskPath(), size, raw)
|
return createDiskImage(d.diskPath(), size, raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Driver) setupHostOnlyNetwork(machineName string) error {
|
||||||
|
ip, network, err := net.ParseCIDR(d.HostOnlyCIDR)
|
||||||
|
nAddr := network.IP.To4()
|
||||||
|
|
||||||
|
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,
|
||||||
|
lowerDHCPIP,
|
||||||
|
upperDHCPIP,
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
// 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.
|
// not nil, it will be read as a raw disk image to convert from.
|
||||||
func createDiskImage(dest string, size int, r io.Reader) error {
|
func createDiskImage(dest string, size int, r io.Reader) error {
|
||||||
|
@ -678,3 +721,26 @@ func setPortForwarding(machine string, interfaceNum int, mapName, protocol strin
|
||||||
}
|
}
|
||||||
return actualHostPort, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,31 @@
|
||||||
package virtualbox
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue