Merge pull request #16910 from mavenugo/ipam

Vendoring libnetwork for the pluggable IPAM driver support
This commit is contained in:
Alexandre Beslic
2015-10-13 14:41:19 -07:00
79 changed files with 6517 additions and 2174 deletions
+156 -9
View File
@@ -5,10 +5,14 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"strings"
"text/tabwriter"
"github.com/docker/docker/api/types"
Cli "github.com/docker/docker/cli"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
)
@@ -29,15 +33,37 @@ func (cli *DockerCli) CmdNetwork(args ...string) error {
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
flIpamSubnet := opts.NewListOpts(nil)
flIpamIPRange := opts.NewListOpts(nil)
flIpamGateway := opts.NewListOpts(nil)
flIpamAux := opts.NewMapOpts(nil, nil)
cmd.Var(&flIpamSubnet, []string{"-subnet"}, "Subnet in CIDR format that represents a network segment")
cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
cmd.Var(flIpamAux, []string{"-aux-address"}, "Auxiliary ipv4 or ipv6 addresses used by network driver")
cmd.Require(flag.Exact, 1)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
if err != nil {
return err
}
// Construct network create request body
nc := types.NetworkCreate{Name: cmd.Arg(0), Driver: *flDriver, CheckDuplicate: true}
nc := types.NetworkCreate{
Name: cmd.Arg(0),
Driver: *flDriver,
IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
CheckDuplicate: true,
}
obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
if err != nil {
return err
@@ -104,12 +130,13 @@ func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
cmd := Cli.Subcmd("network ls", []string{""}, "Lists all the networks created by the user", false)
cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
noTrunc := cmd.Bool([]string{"", "-no-trunc"}, false, "Do not truncate the output")
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
cmd.Require(flag.Exact, 0)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
@@ -117,9 +144,6 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error {
if err != nil {
return err
}
if *last == -1 && *nLatest {
*last = 1
}
var networkResources []types.NetworkResource
err = json.Unmarshal(obj, &networkResources)
@@ -186,6 +210,129 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
return nil
}
// Consolidates the ipam configuration as a group from differnt related configurations
// user can configure network with multiple non-overlapping subnets and hence it is
// possible to corelate the various related parameters and consolidate them.
// consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into
// structured ipam data.
func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
}
iData := map[string]*network.IPAMConfig{}
// Populate non-overlapping subnets into consolidation map
for _, s := range subnets {
for k := range iData {
ok1, err := subnetMatches(s, k)
if err != nil {
return nil, err
}
ok2, err := subnetMatches(k, s)
if err != nil {
return nil, err
}
if ok1 || ok2 {
return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
}
}
iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
}
// Validate and add valid ip ranges
for _, r := range ranges {
match := false
for _, s := range subnets {
ok, err := subnetMatches(s, r)
if err != nil {
return nil, err
}
if !ok {
continue
}
if iData[s].IPRange != "" {
return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
}
d := iData[s]
d.IPRange = r
match = true
}
if !match {
return nil, fmt.Errorf("no matching subnet for range %s", r)
}
}
// Validate and add valid gateways
for _, g := range gateways {
match := false
for _, s := range subnets {
ok, err := subnetMatches(s, g)
if err != nil {
return nil, err
}
if !ok {
continue
}
if iData[s].Gateway != "" {
return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
}
d := iData[s]
d.Gateway = g
match = true
}
if !match {
return nil, fmt.Errorf("no matching subnet for gateway %s", g)
}
}
// Validate and add aux-addresses
for key, aa := range auxaddrs {
match := false
for _, s := range subnets {
ok, err := subnetMatches(s, aa)
if err != nil {
return nil, err
}
if !ok {
continue
}
iData[s].AuxAddress[key] = aa
match = true
}
if !match {
return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
}
}
idl := []network.IPAMConfig{}
for _, v := range iData {
idl = append(idl, *v)
}
return idl, nil
}
func subnetMatches(subnet, data string) (bool, error) {
var (
ip net.IP
)
_, s, err := net.ParseCIDR(subnet)
if err != nil {
return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
}
if strings.Contains(data, "/") {
ip, _, err = net.ParseCIDR(data)
if err != nil {
return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
}
} else {
ip = net.ParseIP(data)
}
return s.Contains(ip), nil
}
func networkUsage() string {
networkCommands := map[string]string{
"create": "Create a network",
+34 -5
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/daemon"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/pkg/parsers/filters"
"github.com/docker/libnetwork"
)
@@ -95,7 +96,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
}
nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.Options)
nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.IPAM)
if err != nil {
return err
}
@@ -179,8 +180,11 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
r.Name = nw.Name()
r.ID = nw.ID()
r.Scope = nw.Info().Scope()
r.Driver = nw.Type()
r.Containers = make(map[string]types.EndpointResource)
buildIpamResources(r, nw)
epl := nw.Endpoints()
for _, e := range epl {
sb := e.Info().Sandbox()
@@ -193,6 +197,31 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
return r
}
func buildIpamResources(r *types.NetworkResource, nw libnetwork.Network) {
id, ipv4conf, ipv6conf := nw.Info().IpamConfig()
r.IPAM.Driver = id
r.IPAM.Config = []network.IPAMConfig{}
for _, ip4 := range ipv4conf {
iData := network.IPAMConfig{}
iData.Subnet = ip4.PreferredPool
iData.IPRange = ip4.SubPool
iData.Gateway = ip4.Gateway
iData.AuxAddress = ip4.AuxAddresses
r.IPAM.Config = append(r.IPAM.Config, iData)
}
for _, ip6 := range ipv6conf {
iData := network.IPAMConfig{}
iData.Subnet = ip6.PreferredPool
iData.IPRange = ip6.SubPool
iData.Gateway = ip6.Gateway
iData.AuxAddress = ip6.AuxAddresses
r.IPAM.Config = append(r.IPAM.Config, iData)
}
}
func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
er := types.EndpointResource{}
if e == nil {
@@ -204,12 +233,12 @@ func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource {
if mac := iface.MacAddress(); mac != nil {
er.MacAddress = mac.String()
}
if ip := iface.Address(); len(ip.IP) > 0 {
er.IPv4Address = (&ip).String()
if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
er.IPv4Address = ip.String()
}
if ipv6 := iface.AddressIPv6(); len(ipv6.IP) > 0 {
er.IPv6Address = (&ipv6).String()
if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 {
er.IPv6Address = ipv6.String()
}
}
return er
+6 -5
View File
@@ -315,9 +315,10 @@ type VolumeCreateRequest struct {
type NetworkResource struct {
Name string `json:"name"`
ID string `json:"id"`
Scope string `json:"scope"`
Driver string `json:"driver"`
IPAM network.IPAM `json:"ipam"`
Containers map[string]EndpointResource `json:"containers"`
Options map[string]interface{} `json:"options,omitempty"`
}
//EndpointResource contains network resources allocated and usd for a container in a network
@@ -330,10 +331,10 @@ type EndpointResource struct {
// NetworkCreate is the expected body of the "create network" http request message
type NetworkCreate struct {
Name string `json:"name"`
CheckDuplicate bool `json:"check_duplicate"`
Driver string `json:"driver"`
Options map[string]interface{} `json:"options"`
Name string `json:"name"`
CheckDuplicate bool `json:"check_duplicate"`
Driver string `json:"driver"`
IPAM network.IPAM `json:"ipam"`
}
// NetworkCreateResponse is the response message sent by the server for network create call