github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/network/create.go (about)

     1  package network
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	"golang.org/x/net/context"
     9  
    10  	"github.com/docker/docker/api/types"
    11  	"github.com/docker/docker/api/types/network"
    12  	"github.com/docker/docker/cli"
    13  	"github.com/docker/docker/cli/command"
    14  	"github.com/docker/docker/opts"
    15  	runconfigopts "github.com/docker/docker/runconfig/opts"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  type createOptions struct {
    20  	name       string
    21  	driver     string
    22  	driverOpts opts.MapOpts
    23  	labels     []string
    24  	internal   bool
    25  	ipv6       bool
    26  	attachable bool
    27  
    28  	ipamDriver  string
    29  	ipamSubnet  []string
    30  	ipamIPRange []string
    31  	ipamGateway []string
    32  	ipamAux     opts.MapOpts
    33  	ipamOpt     opts.MapOpts
    34  }
    35  
    36  func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
    37  	opts := createOptions{
    38  		driverOpts: *opts.NewMapOpts(nil, nil),
    39  		ipamAux:    *opts.NewMapOpts(nil, nil),
    40  		ipamOpt:    *opts.NewMapOpts(nil, nil),
    41  	}
    42  
    43  	cmd := &cobra.Command{
    44  		Use:   "create [OPTIONS] NETWORK",
    45  		Short: "Create a network",
    46  		Args:  cli.ExactArgs(1),
    47  		RunE: func(cmd *cobra.Command, args []string) error {
    48  			opts.name = args[0]
    49  			return runCreate(dockerCli, opts)
    50  		},
    51  	}
    52  
    53  	flags := cmd.Flags()
    54  	flags.StringVarP(&opts.driver, "driver", "d", "bridge", "Driver to manage the Network")
    55  	flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options")
    56  	flags.StringSliceVar(&opts.labels, "label", []string{}, "Set metadata on a network")
    57  	flags.BoolVar(&opts.internal, "internal", false, "Restrict external access to the network")
    58  	flags.BoolVar(&opts.ipv6, "ipv6", false, "Enable IPv6 networking")
    59  	flags.BoolVar(&opts.attachable, "attachable", false, "Enable manual container attachment")
    60  
    61  	flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver")
    62  	flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "Subnet in CIDR format that represents a network segment")
    63  	flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "Allocate container ip from a sub-range")
    64  	flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "IPv4 or IPv6 Gateway for the master subnet")
    65  
    66  	flags.Var(&opts.ipamAux, "aux-address", "Auxiliary IPv4 or IPv6 addresses used by Network driver")
    67  	flags.Var(&opts.ipamOpt, "ipam-opt", "Set IPAM driver specific options")
    68  
    69  	return cmd
    70  }
    71  
    72  func runCreate(dockerCli *command.DockerCli, opts createOptions) error {
    73  	client := dockerCli.Client()
    74  
    75  	ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll())
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	// Construct network create request body
    81  	nc := types.NetworkCreate{
    82  		Driver:  opts.driver,
    83  		Options: opts.driverOpts.GetAll(),
    84  		IPAM: &network.IPAM{
    85  			Driver:  opts.ipamDriver,
    86  			Config:  ipamCfg,
    87  			Options: opts.ipamOpt.GetAll(),
    88  		},
    89  		CheckDuplicate: true,
    90  		Internal:       opts.internal,
    91  		EnableIPv6:     opts.ipv6,
    92  		Attachable:     opts.attachable,
    93  		Labels:         runconfigopts.ConvertKVStringsToMap(opts.labels),
    94  	}
    95  
    96  	resp, err := client.NetworkCreate(context.Background(), opts.name, nc)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID)
   101  	return nil
   102  }
   103  
   104  // Consolidates the ipam configuration as a group from different related configurations
   105  // user can configure network with multiple non-overlapping subnets and hence it is
   106  // possible to correlate the various related parameters and consolidate them.
   107  // consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into
   108  // structured ipam data.
   109  func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
   110  	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
   111  		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
   112  	}
   113  	iData := map[string]*network.IPAMConfig{}
   114  
   115  	// Populate non-overlapping subnets into consolidation map
   116  	for _, s := range subnets {
   117  		for k := range iData {
   118  			ok1, err := subnetMatches(s, k)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  			ok2, err := subnetMatches(k, s)
   123  			if err != nil {
   124  				return nil, err
   125  			}
   126  			if ok1 || ok2 {
   127  				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
   128  			}
   129  		}
   130  		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
   131  	}
   132  
   133  	// Validate and add valid ip ranges
   134  	for _, r := range ranges {
   135  		match := false
   136  		for _, s := range subnets {
   137  			ok, err := subnetMatches(s, r)
   138  			if err != nil {
   139  				return nil, err
   140  			}
   141  			if !ok {
   142  				continue
   143  			}
   144  			if iData[s].IPRange != "" {
   145  				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
   146  			}
   147  			d := iData[s]
   148  			d.IPRange = r
   149  			match = true
   150  		}
   151  		if !match {
   152  			return nil, fmt.Errorf("no matching subnet for range %s", r)
   153  		}
   154  	}
   155  
   156  	// Validate and add valid gateways
   157  	for _, g := range gateways {
   158  		match := false
   159  		for _, s := range subnets {
   160  			ok, err := subnetMatches(s, g)
   161  			if err != nil {
   162  				return nil, err
   163  			}
   164  			if !ok {
   165  				continue
   166  			}
   167  			if iData[s].Gateway != "" {
   168  				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
   169  			}
   170  			d := iData[s]
   171  			d.Gateway = g
   172  			match = true
   173  		}
   174  		if !match {
   175  			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
   176  		}
   177  	}
   178  
   179  	// Validate and add aux-addresses
   180  	for key, aa := range auxaddrs {
   181  		match := false
   182  		for _, s := range subnets {
   183  			ok, err := subnetMatches(s, aa)
   184  			if err != nil {
   185  				return nil, err
   186  			}
   187  			if !ok {
   188  				continue
   189  			}
   190  			iData[s].AuxAddress[key] = aa
   191  			match = true
   192  		}
   193  		if !match {
   194  			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
   195  		}
   196  	}
   197  
   198  	idl := []network.IPAMConfig{}
   199  	for _, v := range iData {
   200  		idl = append(idl, *v)
   201  	}
   202  	return idl, nil
   203  }
   204  
   205  func subnetMatches(subnet, data string) (bool, error) {
   206  	var (
   207  		ip net.IP
   208  	)
   209  
   210  	_, s, err := net.ParseCIDR(subnet)
   211  	if err != nil {
   212  		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
   213  	}
   214  
   215  	if strings.Contains(data, "/") {
   216  		ip, _, err = net.ParseCIDR(data)
   217  		if err != nil {
   218  			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
   219  		}
   220  	} else {
   221  		ip = net.ParseIP(data)
   222  	}
   223  
   224  	return s.Contains(ip), nil
   225  }